diff --git a/.changeset/goofy-laws-serve.md b/.changeset/goofy-laws-serve.md deleted file mode 100644 index 690a5f76b..000000000 --- a/.changeset/goofy-laws-serve.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -'@0xsequence/api': patch -'@0xsequence/builder': patch -'@0xsequence/guard': patch -'@0xsequence/identity-instrument': patch -'@0xsequence/indexer': patch -'@0xsequence/marketplace': patch -'@0xsequence/metadata': patch -'@0xsequence/relayer': patch -'@0xsequence/userdata': patch -'@0xsequence/abi': patch -'@0xsequence/wallet-core': patch -'@0xsequence/dapp-client': patch -'@0xsequence/wallet-primitives': patch -'@0xsequence/wallet-wdk': patch -'@repo/eslint-config': patch -'@repo/typescript-config': patch -'@repo/ui': patch ---- - -Beta release for v3 diff --git a/.changeset/open-toes-marry.md b/.changeset/open-toes-marry.md deleted file mode 100644 index ec5bf3217..000000000 --- a/.changeset/open-toes-marry.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -'@0xsequence/api': patch -'@0xsequence/builder': patch -'@0xsequence/guard': patch -'@0xsequence/identity-instrument': patch -'@0xsequence/indexer': patch -'@0xsequence/marketplace': patch -'@0xsequence/metadata': patch -'@0xsequence/relayer': patch -'@0xsequence/abi': patch -'@0xsequence/wallet-core': patch -'@0xsequence/dapp-client': patch -'@0xsequence/wallet-primitives': patch -'@0xsequence/wallet-wdk': patch -'@repo/eslint-config': patch -'@repo/typescript-config': patch -'@repo/ui': patch ---- - -3.0.0-beta.3 with fixes diff --git a/.changeset/plain-feet-stare.md b/.changeset/plain-feet-stare.md deleted file mode 100644 index c99c82026..000000000 --- a/.changeset/plain-feet-stare.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'@0xsequence/api': patch -'@0xsequence/builder': patch -'@0xsequence/guard': patch -'@0xsequence/identity-instrument': patch -'@0xsequence/indexer': patch -'@0xsequence/marketplace': patch -'@0xsequence/metadata': patch -'@0xsequence/relayer': patch -'@0xsequence/abi': patch -'@0xsequence/wallet-core': patch -'@0xsequence/dapp-client': patch -'@0xsequence/wallet-primitives': patch -'@0xsequence/wallet-wdk': patch ---- - -3.0.0-beta.2 with identity instrument updates diff --git a/.changeset/pre.json b/.changeset/pre.json deleted file mode 100644 index a92fc72b5..000000000 --- a/.changeset/pre.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "mode": "exit", - "tag": "beta", - "initialVersions": { - "docs": "0.1.0", - "web": "0.1.0", - "@0xsequence/api": "3.0.0-beta.4", - "@0xsequence/builder": "3.0.0-beta.4", - "@0xsequence/guard": "3.0.0-beta.4", - "@0xsequence/identity-instrument": "3.0.0-beta.4", - "@0xsequence/indexer": "3.0.0-beta.4", - "@0xsequence/marketplace": "3.0.0-beta.4", - "@0xsequence/metadata": "3.0.0-beta.4", - "@0xsequence/relayer": "3.0.0-beta.4", - "@0xsequence/userdata": "3.0.0-beta.4", - "@0xsequence/abi": "3.0.0-beta.4", - "@0xsequence/wallet-core": "3.0.0-beta.4", - "@0xsequence/dapp-client": "3.0.0-beta.4", - "@0xsequence/wallet-primitives": "3.0.0-beta.4", - "@0xsequence/wallet-wdk": "3.0.0-beta.4", - "@repo/eslint-config": "0.0.1-beta.0", - "@repo/typescript-config": "0.0.1-beta.0", - "@repo/ui": "0.0.1-beta.0" - }, - "changesets": ["goofy-laws-serve", "open-toes-marry", "plain-feet-stare", "wild-feet-carry", "wise-heads-buy"] -} diff --git a/.changeset/wild-feet-carry.md b/.changeset/wild-feet-carry.md deleted file mode 100644 index 962942831..000000000 --- a/.changeset/wild-feet-carry.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'@0xsequence/api': patch -'@0xsequence/builder': patch -'@0xsequence/guard': patch -'@0xsequence/identity-instrument': patch -'@0xsequence/indexer': patch -'@0xsequence/marketplace': patch -'@0xsequence/metadata': patch -'@0xsequence/relayer': patch -'@0xsequence/abi': patch -'@0xsequence/wallet-core': patch -'@0xsequence/dapp-client': patch -'@0xsequence/wallet-primitives': patch -'@0xsequence/wallet-wdk': patch ---- - -3.0.0-beta.1 diff --git a/.changeset/wise-heads-buy.md b/.changeset/wise-heads-buy.md deleted file mode 100644 index 1c35a4d35..000000000 --- a/.changeset/wise-heads-buy.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'@0xsequence/api': patch -'@0xsequence/builder': patch -'@0xsequence/guard': patch -'@0xsequence/identity-instrument': patch -'@0xsequence/indexer': patch -'@0xsequence/marketplace': patch -'@0xsequence/metadata': patch -'@0xsequence/relayer': patch -'@0xsequence/abi': patch -'@0xsequence/wallet-core': patch -'@0xsequence/dapp-client': patch -'@0xsequence/wallet-primitives': patch -'@0xsequence/wallet-wdk': patch ---- - -RC5 upgrade diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 000000000..5f540c130 --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,49 @@ +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/tests.yml b/.github/workflows/tests.yml index 3003e531e..bb22f4c72 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,6 +19,8 @@ jobs: - uses: ./.github/actions/install-dependencies - run: pnpm clean - run: pnpm build + - run: pnpm typecheck + - run: pnpm lint tests: name: Run all tests @@ -30,7 +32,7 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly + version: v1.5.0 - name: Start Anvil in background run: anvil --fork-url https://nodes.sequence.app/arbitrum & - run: pnpm build diff --git a/README.md b/README.md index 4c663ccf8..ae41ffdbd 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Sequence v3 core libraries and [wallet-contracts-v3](https://github.com/0xsequen - Run tests: `pnpm test` - > **Note:** Tests require [anvil](https://github.com/foundry-rs/foundry/tree/master/anvil) and [forge](https://github.com/foundry-rs/foundry) to be installed. You can run a local anvil instance using `pnpm run test:anvil`. + > **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 diff --git a/extras/docs/package.json b/extras/docs/package.json index abadc74fd..5b275db5b 100644 --- a/extras/docs/package.json +++ b/extras/docs/package.json @@ -12,18 +12,18 @@ "clean": "rimraf .next" }, "dependencies": { - "@repo/ui": "workspace:*", - "next": "^15.5.9", - "react": "^19.1.0", - "react-dom": "^19.1.0" + "@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": "^20.17.57", - "@types/react": "^19.2.6", + "@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.28.0", - "typescript": "^5.8.3" + "eslint": "^9.39.2", + "typescript": "^6.0.3" } } diff --git a/extras/docs/tsconfig.json b/extras/docs/tsconfig.json index c2fa4ee5d..7b9803267 100644 --- a/extras/docs/tsconfig.json +++ b/extras/docs/tsconfig.json @@ -7,6 +7,13 @@ } ] }, - "include": ["**/*.ts", "**/*.tsx", "next-env.d.ts", "next.config.js", ".next/types/**/*.ts"], + "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/package.json b/extras/web/package.json index 3b57fed2b..e042bc8e6 100644 --- a/extras/web/package.json +++ b/extras/web/package.json @@ -12,18 +12,18 @@ "clean": "rimraf .next" }, "dependencies": { - "@repo/ui": "workspace:*", - "next": "^15.5.9", - "react": "^19.1.0", - "react-dom": "^19.1.0" + "@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": "^20.17.57", - "@types/react": "^19.2.6", + "@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.28.0", - "typescript": "^5.8.3" + "eslint": "^9.39.2", + "typescript": "^6.0.3" } } diff --git a/extras/web/tsconfig.json b/extras/web/tsconfig.json index c2fa4ee5d..7b9803267 100644 --- a/extras/web/tsconfig.json +++ b/extras/web/tsconfig.json @@ -7,6 +7,13 @@ } ] }, - "include": ["**/*.ts", "**/*.tsx", "next-env.d.ts", "next.config.js", ".next/types/**/*.ts"], + "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 index 7b9926c50..5402d7dc7 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -3,6 +3,10 @@ pre-commit: 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" diff --git a/package.json b/package.json index 483cd0936..af9b64c38 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build": "pnpm build:packages", "dev": "turbo dev", "test": "turbo test --concurrency=1", - "lint": "turbo lint", + "lint": "turbo lint --continue", "format": "prettier --list-different --write \"**/*.{ts,tsx,md}\"", "typecheck": "turbo typecheck", "postinstall": "lefthook install", @@ -16,31 +16,33 @@ "reinstall": "rimraf -g ./**/node_modules && pnpm install", "test:anvil": "anvil --fork-url https://nodes.sequence.app/arbitrum", "clean": "turbo clean", - "deps:lint": "syncpack list-mismatches", - "deps:fix": "syncpack fix-mismatches" + "deps:lint": "syncpack lint --dependency-types prod,dev", + "deps:fix": "syncpack fix" }, "devDependencies": { - "@changesets/cli": "^2.29.4", - "lefthook": "^2.0.4", - "prettier": "^3.5.3", - "rimraf": "^6.1.2", - "syncpack": "^13.0.4", - "turbo": "^2.6.1", - "typescript": "^5.9.3" + "@changesets/cli": "^2.29.8", + "lefthook": "^2.1.6", + "prettier": "^3.8.3", + "rimraf": "^6.1.3", + "syncpack": "^14.3.1", + "turbo": "^2.9.14", + "typescript": "^6.0.3" }, "pnpm": { "overrides": { "ox": "^0.9.17" } }, - "packageManager": "pnpm@10.24.0", + "packageManager": "pnpm@10.33.4", "engines": { "node": ">=18" }, "syncpack": { "source": [ "package.json", - "packages/**/package.json" + "packages/**/package.json", + "extras/**/package.json", + "repo/**/package.json" ], "versionGroups": [ { diff --git a/packages/services/api/CHANGELOG.md b/packages/services/api/CHANGELOG.md index 610642b4c..12115dd1b 100644 --- a/packages/services/api/CHANGELOG.md +++ b/packages/services/api/CHANGELOG.md @@ -1,5 +1,186 @@ # @0xsequence/api +## 3.0.12 + +### Patch Changes + +- WDK fix + +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface + +## 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 diff --git a/packages/wallet/dapp-client/eslint.config.mjs b/packages/services/api/eslint.config.js similarity index 100% rename from packages/wallet/dapp-client/eslint.config.mjs rename to packages/services/api/eslint.config.js diff --git a/packages/services/api/package.json b/packages/services/api/package.json index 20fc284b0..ac1f75fcb 100644 --- a/packages/services/api/package.json +++ b/packages/services/api/package.json @@ -1,18 +1,20 @@ { "name": "@0xsequence/api", - "version": "3.0.0-beta.5", + "version": "3.0.12", "description": "api sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/api", - "author": "Sequence Platforms Inc.", + "author": "Sequence Platforms ULC", "license": "Apache-2.0", "publishConfig": { "access": "public" }, + "type": "module", "scripts": { "build": "tsc", "dev": "tsc --watch", "test": "echo", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -22,7 +24,8 @@ }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "typescript": "^5.9.3" + "@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 index bf07aa039..139fa541a 100644 --- a/packages/services/api/src/api.gen.ts +++ b/packages/services/api/src/api.gen.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -// sequence-api v0.4.0 d7026da603b2c29baf21c6aceeebc86eada372d8 +// sequence-api v0.4.0 3c15fa79614e43a5321cd2ac0c080e80af291bd1 // -- // Code generated by Webrpc-gen@v0.31.0 with typescript generator. DO NOT EDIT. // @@ -12,7 +12,7 @@ export const WebrpcVersion = 'v1' export const WebrpcSchemaVersion = 'v0.4.0' // Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = 'd7026da603b2c29baf21c6aceeebc86eada372d8' +export const WebrpcSchemaHash = '3c15fa79614e43a5321cd2ac0c080e80af291bd1' // // Client interface @@ -337,25 +337,6 @@ export interface APIClient { signal?: AbortSignal, ): Promise - /** - * - * Chain abstraction - * - */ - getIntentCallsPayloads( - req: GetIntentCallsPayloadsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - commitIntentConfig( - req: CommitIntentConfigRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - getIntentConfig(req: GetIntentConfigRequest, headers?: object, signal?: AbortSignal): Promise - /** * * Inventory, payments and management @@ -455,48 +436,6 @@ export interface APIClient { headers?: object, signal?: AbortSignal, ): Promise - - /** - * - * CCTP - * - */ - getCCTPTransfer(req: GetCCTPTransferRequest, headers?: object, signal?: AbortSignal): Promise - - queueCCTPTransfer( - req: QueueCCTPTransferRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - /** - * - * Intent Machine Worker - * - */ - queueIntentConfigExecution( - req: QueueIntentConfigExecutionRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - getIntentConfigExecutionStatus( - req: GetIntentConfigExecutionStatusRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - listIntentConfigs( - req: ListIntentConfigsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise - - queueMetaTxnReceipt( - req: QueueMetaTxnReceiptRequest, - headers?: object, - signal?: AbortSignal, - ): Promise } // @@ -632,38 +571,6 @@ export interface MetaTxn { input: string } -export interface Call { - to: string - value?: string - data?: string - gasLimit?: string - delegateCall?: boolean - onlyFallback?: boolean - behaviorOnError?: number -} - -export interface IntentCallsPayload { - chainId: string - space?: string - nonce?: string - calls: Array -} - -export interface IntentConfig { - id: number - configHash: string - originIntentAddress: string - destinationIntentAddress: string - mainSigner: string - calls: Array - preconditions: Array - executionStatus?: string - metaTxnId?: string - txnHash?: string - updatedAt?: string - createdAt?: string -} - export interface MetaTxnReceipt { metaTxID: string status: string @@ -703,30 +610,6 @@ export interface TupleComponent { value: any } -export interface AddressOverrides { - trailsLiFiSapientSignerAddress?: string - trailsRelaySapientSignerAddress?: string - trailsCCTPV2SapientSignerAddress?: string -} - -export interface TakerFee { - address: string - bps: number -} - -export interface OriginCall { - chainId: number - to: string - transactionData: string - transactionValue: string -} - -export interface IntentPrecondition { - type: string - chainId: string - data: any -} - export interface UserStorage { userAddress: string key: string @@ -866,18 +749,6 @@ export interface OffchainInventory { deletedAt?: string } -export interface CCTPTransfer { - id: string - sourceTxHash: string - sourceChainId: number - destinationChainId: number - message: string - attestation: string - status: string - createdAt: string - updatedAt: string -} - export interface OffchainPayment { id: number offchainInventoryId: number @@ -1228,33 +1099,6 @@ export interface ExecuteQuote { chainQuotes: Array } -export interface TrailsFee { - executeQuote: ExecuteQuote - crossChainFee?: CrossChainFee - takerFeeAmount?: string - takerFeeUSD?: number - trailsFixedFeeUSD: number - feeToken?: string - originTokenTotalAmount?: string - totalFeeAmount?: string - totalFeeUSD?: string - quoteProvider?: string -} - -export interface IntentQuote { - fromAmount: string - fromAmountMin: string - toAmount: string - toAmountMin: string - priceImpact: number - priceImpactUsd: string - maxSlippage: number - quoteProvider: string - quoteProviderRequestId: string - quoteProviderFeeUsd: string - feeQuotes: { [key: string]: string } -} - export interface PingRequest {} export interface PingResponse { @@ -1738,10 +1582,6 @@ export interface GetLifiTokensResponse { export interface GetLifiSwapRoutesRequest { params: GetLifiSwapRouteParams - chainId: number - toTokenAddress: string - toTokenAmount: string - walletAddress: string } export interface GetLifiSwapRoutesResponse { @@ -1756,57 +1596,6 @@ export interface GetLifiSwapQuoteResponse { quote: LifiSwapQuote } -export interface GetIntentCallsPayloadsRequest { - userAddress: string - destinationChainId: number - destinationTokenAddress: string - destinationTokenAmount: string - destinationToAddress: string - originChainId: number - originTokenAddress: string - originTokenAmount: string - destinationCallData?: string - destinationCallValue?: string - provider?: string - addressOverrides?: AddressOverrides - destinationSalt?: string - takerFee?: TakerFee - slippageTolerance?: number - tradeType?: TradeType -} - -export interface GetIntentCallsPayloadsResponse { - calls: Array - preconditions: Array - metaTxns: Array - trailsFee: TrailsFee - quote: IntentQuote - feeQuotes: { [key: string]: string } - originIntentAddress: string - destinationIntentAddress: string -} - -export interface CommitIntentConfigRequest { - originIntentAddress: string - destinationIntentAddress: string - mainSigner: string - calls: Array - preconditions: Array - addressOverrides?: AddressOverrides -} - -export interface CommitIntentConfigResponse { - config: IntentConfig -} - -export interface GetIntentConfigRequest { - intentAddress: string -} - -export interface GetIntentConfigResponse { - config: IntentConfig -} - export interface ListCurrencyGroupsRequest {} export interface ListCurrencyGroupsResponse { @@ -1975,59 +1764,6 @@ export interface FortePayGetPaymentStatusesResponse { statuses: Array } -export interface GetCCTPTransferRequest { - id: string -} - -export interface GetCCTPTransferResponse { - transfer: CCTPTransfer -} - -export interface QueueCCTPTransferRequest { - sourceTxHash?: string - metaTxHash?: string - sourceChainId: number - destinationChainId: number -} - -export interface QueueCCTPTransferResponse { - transfer: CCTPTransfer -} - -export interface QueueIntentConfigExecutionRequest { - intentConfigId: number -} - -export interface QueueIntentConfigExecutionResponse { - status: boolean -} - -export interface GetIntentConfigExecutionStatusRequest { - intentConfigId: number -} - -export interface GetIntentConfigExecutionStatusResponse { - executionStatus: string -} - -export interface ListIntentConfigsRequest { - page?: Page - executionStatus?: string -} - -export interface ListIntentConfigsResponse { - page: Page - intentConfigs: Array -} - -export interface QueueMetaTxnReceiptRequest { - metaTxID: string -} - -export interface QueueMetaTxnReceiptResponse { - status: boolean -} - // // Client // @@ -2108,9 +1844,6 @@ export class API implements APIClient { getLifiTokens: (req: GetLifiTokensRequest) => ['API', 'getLifiTokens', req] as const, getLifiSwapRoutes: (req: GetLifiSwapRoutesRequest) => ['API', 'getLifiSwapRoutes', req] as const, getLifiSwapQuote: (req: GetLifiSwapQuoteRequest) => ['API', 'getLifiSwapQuote', req] as const, - getIntentCallsPayloads: (req: GetIntentCallsPayloadsRequest) => ['API', 'getIntentCallsPayloads', req] as const, - commitIntentConfig: (req: CommitIntentConfigRequest) => ['API', 'commitIntentConfig', req] as const, - getIntentConfig: (req: GetIntentConfigRequest) => ['API', 'getIntentConfig', req] as const, listCurrencyGroups: () => ['API', 'listCurrencyGroups'] as const, addOffchainInventory: (req: AddOffchainInventoryRequest) => ['API', 'addOffchainInventory', req] as const, getOffchainInventory: (req: GetOffchainInventoryRequest) => ['API', 'getOffchainInventory', req] as const, @@ -2133,14 +1866,6 @@ export class API implements APIClient { fortePayCreateIntent: (req: FortePayCreateIntentRequest) => ['API', 'fortePayCreateIntent', req] as const, fortePayGetPaymentStatuses: (req: FortePayGetPaymentStatusesRequest) => ['API', 'fortePayGetPaymentStatuses', req] as const, - getCCTPTransfer: (req: GetCCTPTransferRequest) => ['API', 'getCCTPTransfer', req] as const, - queueCCTPTransfer: (req: QueueCCTPTransferRequest) => ['API', 'queueCCTPTransfer', req] as const, - queueIntentConfigExecution: (req: QueueIntentConfigExecutionRequest) => - ['API', 'queueIntentConfigExecution', req] as const, - getIntentConfigExecutionStatus: (req: GetIntentConfigExecutionStatusRequest) => - ['API', 'getIntentConfigExecutionStatus', req] as const, - listIntentConfigs: (req: ListIntentConfigsRequest) => ['API', 'listIntentConfigs', req] as const, - queueMetaTxnReceipt: (req: QueueMetaTxnReceiptRequest) => ['API', 'queueMetaTxnReceipt', req] as const, } ping = (headers?: object, signal?: AbortSignal): Promise => { @@ -3289,72 +3014,6 @@ export class API implements APIClient { ) } - getIntentCallsPayloads = ( - req: GetIntentCallsPayloadsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetIntentCallsPayloads'), - createHttpRequest(JsonEncode(req, 'GetIntentCallsPayloadsRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetIntentCallsPayloadsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - commitIntentConfig = ( - req: CommitIntentConfigRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('CommitIntentConfig'), - createHttpRequest(JsonEncode(req, 'CommitIntentConfigRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'CommitIntentConfigResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getIntentConfig = ( - req: GetIntentConfigRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetIntentConfig'), - createHttpRequest(JsonEncode(req, 'GetIntentConfigRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetIntentConfigResponse') - }) - }, - (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) => { @@ -3749,138 +3408,6 @@ export class API implements APIClient { }, ) } - - getCCTPTransfer = ( - req: GetCCTPTransferRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetCCTPTransfer'), - createHttpRequest(JsonEncode(req, 'GetCCTPTransferRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetCCTPTransferResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - queueCCTPTransfer = ( - req: QueueCCTPTransferRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('QueueCCTPTransfer'), - createHttpRequest(JsonEncode(req, 'QueueCCTPTransferRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'QueueCCTPTransferResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - queueIntentConfigExecution = ( - req: QueueIntentConfigExecutionRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('QueueIntentConfigExecution'), - createHttpRequest(JsonEncode(req, 'QueueIntentConfigExecutionRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'QueueIntentConfigExecutionResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - getIntentConfigExecutionStatus = ( - req: GetIntentConfigExecutionStatusRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetIntentConfigExecutionStatus'), - createHttpRequest(JsonEncode(req, 'GetIntentConfigExecutionStatusRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'GetIntentConfigExecutionStatusResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - listIntentConfigs = ( - req: ListIntentConfigsRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('ListIntentConfigs'), - createHttpRequest(JsonEncode(req, 'ListIntentConfigsRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'ListIntentConfigsResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } - - queueMetaTxnReceipt = ( - req: QueueMetaTxnReceiptRequest, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('QueueMetaTxnReceipt'), - createHttpRequest(JsonEncode(req, 'QueueMetaTxnReceiptRequest'), headers, signal), - ).then( - (res) => { - return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'QueueMetaTxnReceiptResponse') - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ - cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, - }) - }, - ) - } } const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { diff --git a/packages/services/api/src/index.ts b/packages/services/api/src/index.ts index 12656d2c0..14d83d8a1 100644 --- a/packages/services/api/src/index.ts +++ b/packages/services/api/src/index.ts @@ -1,6 +1,6 @@ -export * from './api.gen' +export * from './api.gen.js' -import { API as ApiRpc } from './api.gen' +import { API as ApiRpc } from './api.gen.js' export class SequenceAPIClient extends ApiRpc { constructor( @@ -15,7 +15,7 @@ export class SequenceAPIClient extends ApiRpc { _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: { [key: string]: any } = {} + const headers: Record = {} const jwtAuth = this.jwtAuth const projectAccessKey = this.projectAccessKey diff --git a/packages/services/builder/CHANGELOG.md b/packages/services/builder/CHANGELOG.md index 9dda5364d..106229a8f 100644 --- a/packages/services/builder/CHANGELOG.md +++ b/packages/services/builder/CHANGELOG.md @@ -1,5 +1,186 @@ # @0xsequence/builder +## 3.0.12 + +### Patch Changes + +- WDK fix + +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface + +## 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 diff --git a/packages/wallet/primitives-cli/eslint.config.mjs b/packages/services/builder/eslint.config.js similarity index 100% rename from packages/wallet/primitives-cli/eslint.config.mjs rename to packages/services/builder/eslint.config.js diff --git a/packages/services/builder/package.json b/packages/services/builder/package.json index 5f20f458b..ee4ae02f8 100644 --- a/packages/services/builder/package.json +++ b/packages/services/builder/package.json @@ -1,18 +1,20 @@ { "name": "@0xsequence/builder", - "version": "3.0.0-beta.5", + "version": "3.0.12", "description": "builder sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/builder", - "author": "Sequence Platforms Inc.", + "author": "Sequence Platforms ULC", "license": "Apache-2.0", "publishConfig": { "access": "public" }, + "type": "module", "scripts": { "build": "tsc", "dev": "tsc --watch", "test": "echo", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -21,8 +23,9 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "typescript": "^5.9.3" + "@types/node": "^25.3.0", + "typescript": "^6.0.3" } } diff --git a/packages/services/builder/src/index.ts b/packages/services/builder/src/index.ts index 7abe21855..18f13758d 100644 --- a/packages/services/builder/src/index.ts +++ b/packages/services/builder/src/index.ts @@ -1,6 +1,6 @@ -export * from './builder.gen' +export * from './builder.gen.js' -import { Builder as BuilderRpc } from './builder.gen' +import { Builder as BuilderRpc } from './builder.gen.js' export class SequenceBuilderClient extends BuilderRpc { constructor( @@ -15,7 +15,7 @@ export class SequenceBuilderClient extends BuilderRpc { _fetch = (input: RequestInfo, init?: RequestInit): Promise => { // automatically include access key auth header to requests // if its been set on the api client - const headers: { [key: string]: any } = {} + const headers: Record = {} const projectAccessKey = this.projectAccessKey if (projectAccessKey && projectAccessKey.length > 0) { diff --git a/packages/services/guard/CHANGELOG.md b/packages/services/guard/CHANGELOG.md index 9ba2ad5f4..31a84379e 100644 --- a/packages/services/guard/CHANGELOG.md +++ b/packages/services/guard/CHANGELOG.md @@ -1,5 +1,186 @@ # @0xsequence/guard +## 3.0.12 + +### Patch Changes + +- WDK fix + +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface + +## 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 diff --git a/packages/wallet/primitives/eslint.config.mjs b/packages/services/guard/eslint.config.js similarity index 100% rename from packages/wallet/primitives/eslint.config.mjs rename to packages/services/guard/eslint.config.js diff --git a/packages/services/guard/package.json b/packages/services/guard/package.json index 225211817..3c263d217 100644 --- a/packages/services/guard/package.json +++ b/packages/services/guard/package.json @@ -1,9 +1,9 @@ { "name": "@0xsequence/guard", - "version": "3.0.0-beta.5", + "version": "3.0.12", "description": "guard sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/guard", - "author": "Sequence Platforms Inc.", + "author": "Sequence Platforms ULC", "license": "Apache-2.0", "type": "module", "publishConfig": { @@ -15,7 +15,8 @@ "dev": "tsc --watch", "test": "vitest run", "test:coverage": "vitest run --coverage", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -24,10 +25,11 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "typescript": "^5.9.3", - "vitest": "^4.0.14" + "@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/local.ts b/packages/services/guard/src/local.ts index 3ef9ac787..3eeb7b8d5 100644 --- a/packages/services/guard/src/local.ts +++ b/packages/services/guard/src/local.ts @@ -1,4 +1,4 @@ -import { Address, Hex, Bytes, Secp256k1, Hash } from 'ox' +import { Address, Hex, Bytes, Secp256k1 } from 'ox' import * as Client from './client/guard.gen.js' import * as Types from './types.js' @@ -11,12 +11,13 @@ export class Guard implements Types.Guard { } async signPayload( - wallet: Address.Address, - chainId: number, - type: Client.PayloadType, + _wallet: Address.Address, + _chainId: number, + _type: Client.PayloadType, digest: Bytes.Bytes, - message: Bytes.Bytes, - signatures?: Client.Signature[], + _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 index fe1a003ce..b2acc812a 100644 --- a/packages/services/guard/src/sequence.ts +++ b/packages/services/guard/src/sequence.ts @@ -1,4 +1,4 @@ -import { Address, Hex, Signature, Bytes, Hash } from 'ox' +import { Address, Hex, Signature, Bytes } from 'ox' import * as Client from './client/guard.gen.js' import * as Types from './types.js' @@ -50,7 +50,7 @@ export class Guard implements Types.Guard { throw new Types.AuthRequiredError('PIN') } console.error(error) - throw new Error('Error signing with guard') + throw new Error('Error signing with guard', { cause: error }) } } } diff --git a/packages/services/guard/test/sequence.test.ts b/packages/services/guard/test/sequence.test.ts index 5ac92a378..9c66cc433 100644 --- a/packages/services/guard/test/sequence.test.ts +++ b/packages/services/guard/test/sequence.test.ts @@ -1,11 +1,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Guard } from '../src/sequence' -import { PayloadType } from '../src/client/guard.gen' +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() -global.fetch = mockFetch +globalThis.fetch = mockFetch describe('Sequence', () => { describe('GuardSigner', () => { @@ -86,10 +86,10 @@ describe('Sequence', () => { ok: true, }) - await guard.signPayload(testWallet, 1, PayloadType.ConfigUpdate, testMessageDigest, testMessage) + 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) + expect(requestBody.request.chainId).toBe(customChainId) }) it('Should throw error when guard service fails', async () => { @@ -116,6 +116,21 @@ describe('Sequence', () => { ).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) diff --git a/packages/services/identity-instrument/CHANGELOG.md b/packages/services/identity-instrument/CHANGELOG.md index 3ecd8814c..fc966ce94 100644 --- a/packages/services/identity-instrument/CHANGELOG.md +++ b/packages/services/identity-instrument/CHANGELOG.md @@ -1,5 +1,186 @@ # @0xsequence/identity-instrument +## 3.0.12 + +### Patch Changes + +- WDK fix + +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface + +## 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 diff --git a/packages/services/identity-instrument/eslint.config.js b/packages/services/identity-instrument/eslint.config.js new file mode 100644 index 000000000..cecf89b03 --- /dev/null +++ b/packages/services/identity-instrument/eslint.config.js @@ -0,0 +1,4 @@ +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 index b025b75c8..f6d9038b6 100644 --- a/packages/services/identity-instrument/package.json +++ b/packages/services/identity-instrument/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/identity-instrument", - "version": "3.0.0-beta.5", + "version": "3.0.12", "license": "Apache-2.0", "type": "module", "publishConfig": { @@ -10,7 +10,9 @@ "scripts": { "build": "tsc", "dev": "tsc --watch", - "test": "vitest run" + "test": "vitest run", + "lint": "eslint . --max-warnings 0", + "typecheck": "tsc --noEmit" }, "exports": { ".": { @@ -19,10 +21,11 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "typescript": "^5.9.3", - "vitest": "^4.0.14" + "@types/node": "^25.3.0", + "typescript": "^6.0.3", + "vitest": "^4.0.18" }, "dependencies": { "json-canonicalize": "^2.0.0", diff --git a/packages/services/indexer/CHANGELOG.md b/packages/services/indexer/CHANGELOG.md index 3978b95ac..c6a1e0d33 100644 --- a/packages/services/indexer/CHANGELOG.md +++ b/packages/services/indexer/CHANGELOG.md @@ -1,5 +1,186 @@ # @0xsequence/indexer +## 3.0.12 + +### Patch Changes + +- WDK fix + +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface + +## 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 diff --git a/packages/services/indexer/eslint.config.js b/packages/services/indexer/eslint.config.js new file mode 100644 index 000000000..cecf89b03 --- /dev/null +++ b/packages/services/indexer/eslint.config.js @@ -0,0 +1,4 @@ +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 index 03ec99a79..9aa8ed4f4 100644 --- a/packages/services/indexer/package.json +++ b/packages/services/indexer/package.json @@ -1,18 +1,20 @@ { "name": "@0xsequence/indexer", - "version": "3.0.0-beta.5", + "version": "3.0.12", "description": "indexer sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/indexer", - "author": "Sequence Platforms Inc.", + "author": "Sequence Platforms ULC", "license": "Apache-2.0", "publishConfig": { "access": "public" }, + "type": "module", "scripts": { "build": "tsc", "dev": "tsc --watch", "test": "echo", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -21,8 +23,9 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "typescript": "^5.9.3" + "@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 index a4588c71e..15f28c2cb 100644 --- a/packages/services/indexer/src/index.ts +++ b/packages/services/indexer/src/index.ts @@ -1,8 +1,8 @@ -export * from './indexer.gen' -export * as IndexerGateway from './indexergw.gen' +export * from './indexer.gen.js' +export * as IndexerGateway from './indexergw.gen.js' -import { Indexer as IndexerRpc } from './indexer.gen' -import { IndexerGateway as IndexerGatewayRpc } from './indexergw.gen' +import { Indexer as IndexerRpc } from './indexer.gen.js' +import { IndexerGateway as IndexerGatewayRpc } from './indexergw.gen.js' export class SequenceIndexer extends IndexerRpc { constructor( @@ -17,7 +17,7 @@ export class SequenceIndexer extends IndexerRpc { _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: { [key: string]: any } = {} + const headers: Record = {} const jwtAuth = this.jwtAuth const projectAccessKey = this.projectAccessKey @@ -50,7 +50,7 @@ export class SequenceIndexerGateway extends IndexerGatewayRpc { _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: { [key: string]: any } = {} + const headers: Record = {} const jwtAuth = this.jwtAuth const projectAccessKey = this.projectAccessKey diff --git a/packages/services/indexer/src/indexer.gen.ts b/packages/services/indexer/src/indexer.gen.ts index 89de72566..8dba62d16 100644 --- a/packages/services/indexer/src/indexer.gen.ts +++ b/packages/services/indexer/src/indexer.gen.ts @@ -1,79 +1,420 @@ /* eslint-disable */ -// sequence-indexer v0.4.0 546b527de7002f409ffa602ad35b5a3abe979088 +// sequence-indexer v0.4.0 2bca559c4c590bce7d70c33df115a58399efec82 // -- -// Code generated by webrpc-gen@v0.21.1 with typescript generator. DO NOT EDIT. +// Code generated by Webrpc-gen@v0.31.2 with typescript generator. DO NOT EDIT. // -// webrpc-gen -schema=indexer.ridl -service=Indexer -target=typescript -client -out=./clients/indexer.gen.ts +// webrpc-gen -schema=merged.gen.json -service=Indexer -target=typescript -client -out=./clients/indexer.gen.ts -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.21.1;gen-typescript@v0.15.1;sequence-indexer@v0.4.0' - -// WebRPC description and code-gen version -export const WebRPCVersion = 'v1' +// Webrpc description and code-gen version +export const WebrpcVersion = 'v1' // Schema version of your RIDL schema -export const WebRPCSchemaVersion = 'v0.4.0' +export const WebrpcSchemaVersion = 'v0.4.0' // Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = '546b527de7002f409ffa602ad35b5a3abe979088' +export const WebrpcSchemaHash = '2bca559c4c590bce7d70c33df115a58399efec82' -type WebrpcGenVersions = { - webrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} +// +// Client interface +// -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } +export interface IndexerClient { + addWebhookListener( + req: AddWebhookListenerRequest, + headers?: object, + signal?: AbortSignal, + ): Promise - return parseWebrpcGenVersions(headerValue) -} + /** + * Fetches a single receipt and then will stop the subscription + */ + fetchTransactionReceipt( + req: FetchTransactionReceiptRequest, + headers?: object, + signal?: AbortSignal, + ): Promise -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } + /** + * Fetches a single receipt with filter and then will stop the subscription + */ + fetchTransactionReceiptWithFilter( + req: FetchTransactionReceiptWithFilterRequest, + headers?: object, + signal?: AbortSignal, + ): Promise - const [_, webrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') + /** + * Webhooks + */ + getAllWebhookListeners( + req: GetAllWebhookListenersRequest, + headers?: object, + signal?: AbortSignal, + ): Promise - return { - webrpcGenVersion: webrpcGenVersion!, - codeGenName: codeGenName!, - codeGenVersion: codeGenVersion!, - schemaName: schemaName!, - schemaVersion: schemaVersion!, - } + /** + * 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 } // -// Types +// Schema types // -export enum ResourceStatus { - NOT_AVAILABLE = 'NOT_AVAILABLE', - REFRESHING = 'REFRESHING', - AVAILABLE = 'AVAILABLE', +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 { @@ -93,56 +434,29 @@ export interface ContractInfo { status: ResourceStatus } -export interface ContractInfoExtensions { - link: string - description: string - categories: Array - 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 TokenMetadata { - 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 ContractInfoExtensionIndexingInfo { + useOnChainBalance: boolean } -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 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 { @@ -156,72 +470,73 @@ export enum ContractType { ERC721_BRIDGE = 'ERC721_BRIDGE', ERC1155_BRIDGE = 'ERC1155_BRIDGE', SEQ_MARKETPLACE = 'SEQ_MARKETPLACE', + ERC6909 = 'ERC6909', } -export enum EventLogType { - UNKNOWN = 'UNKNOWN', - BLOCK_ADDED = 'BLOCK_ADDED', - BLOCK_REMOVED = 'BLOCK_REMOVED', -} - -export enum EventLogDataType { - EVENT = 'EVENT', - TOKEN_TRANSFER = 'TOKEN_TRANSFER', - NATIVE_TOKEN_TRANSFER = 'NATIVE_TOKEN_TRANSFER', - SEQUENCE_TXN = 'SEQUENCE_TXN', -} - -export enum OrderStatus { - OPEN = 'OPEN', - CLOSED = 'CLOSED', - CANCELLED = 'CANCELLED', +export enum ContractVerificationStatus { + VERIFIED = 'VERIFIED', + UNVERIFIED = 'UNVERIFIED', + ALL = 'ALL', } -export enum TxnTransferType { - UNKNOWN = 'UNKNOWN', - SEND = 'SEND', - RECEIVE = 'RECEIVE', +export interface DiskUsage { + humanReadable: string + used: number + size: number + percent: number + dirs: { [key: string]: string } } -export enum TransactionStatus { - FAILED = 'FAILED', - SUCCESSFUL = 'SUCCESSFUL', +export interface EtherBalance { + accountAddress: string + balanceWei: string } -export enum TransactionType { - LegacyTxnType = 'LegacyTxnType', - AccessListTxnType = 'AccessListTxnType', - DynamicFeeTxnType = 'DynamicFeeTxnType', +export interface EventDecoded { + topicHash: string + eventSig: string + types: Array + names: Array + values: Array } -export enum SortOrder { - DESC = 'DESC', - ASC = 'ASC', +export interface EventFilter { + events?: Array + contractAddresses?: Array + accounts?: Array + tokenIDs?: Array } -export enum ContractVerificationStatus { - VERIFIED = 'VERIFIED', - UNVERIFIED = 'UNVERIFIED', - ALL = 'ALL', +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 interface Version { - webrpcVersion: string - schemaVersion: string - schemaHash: string - appVersion: string +export enum EventLogDataType { + EVENT = 'EVENT', + TOKEN_TRANSFER = 'TOKEN_TRANSFER', + NATIVE_TOKEN_TRANSFER = 'NATIVE_TOKEN_TRANSFER', + SEQUENCE_TXN = 'SEQUENCE_TXN', } -export interface RuntimeStatus { - healthOK: boolean - indexerEnabled: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - chainID: number - checks: RuntimeChecks +export enum EventLogType { + UNKNOWN = 'UNKNOWN', + BLOCK_ADDED = 'BLOCK_ADDED', + BLOCK_REMOVED = 'BLOCK_REMOVED', } export interface GatewayBackendResponseTime { @@ -235,6 +550,30 @@ export interface GatewayBackendRuntimeStatus { 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 @@ -245,43 +584,115 @@ export interface GatewayRuntimeStatus { backends: Array } -export interface WALWriterRuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string +export interface GatewayTokenBalance { + chainId: number + errorReason?: string + results: Array +} + +export interface GatewayTokenPriceQuery { chainID: number - percentWALWritten: 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 RuntimeChecks { - running: boolean - runnables: any - cgoEnabled: boolean - quotaControlEnabled: boolean - syncMode: string - percentIndexed: number - lastBlockNum: number - lastBlockNumWithState: number - bloomStatus: BloomStatus - bond: Bond - diskUsage: DiskUsage +export interface NativeTokenBalance { + accountAddress: string + chainId: number + name: string + symbol: string + balance: string + balanceUSD: string + priceUSD: string + priceUpdatedAt?: string + errorReason?: string } -export interface DiskUsage { - humanReadable: string - used: number - size: number - percent: number - dirs: { [key: string]: string } +export enum NetworkType { + MAINNETS = 'MAINNETS', + TESTNETS = 'TESTNETS', + ALL = 'ALL', } -export interface Bond { - pebble: PebbleMetrics - estimatedDiskUsagePerTable: any - estimatedDiskUsageTotal: string +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 { @@ -292,66 +703,54 @@ export interface PebbleMetrics { compactionMarkedFiles: number } -export interface BloomStatus { - enabled: boolean - initialized: boolean - bloomInitElapsedTime: string -} - -export interface EtherBalance { - accountAddress: string - balanceWei: string +export interface Price { + contractAddress: string + tokenID?: string + priceUSD: string + updatedAt?: string } -export interface NativeTokenBalance { - accountAddress: string - chainId: number - balance: string - error: string +export enum ResourceStatus { + NOT_AVAILABLE = 'NOT_AVAILABLE', + REFRESHING = 'REFRESHING', + AVAILABLE = 'AVAILABLE', } -export interface IndexState { - chainId: string +export interface RuntimeChecks { + running: boolean + runnables: any + cgoEnabled: boolean + quotaControlEnabled: boolean + syncMode: string + percentIndexed: number lastBlockNum: number - lastBlockHash: string -} - -export interface IndexedBlock { - blockNumber: number - blockShortHash: string + lastBlockNumWithState: number + bloomStatus: BloomStatus + bond: Bond + diskUsage: DiskUsage + metrics: IndexerMetrics } -export interface TxnInfo { - from: string - to: string - value: string +export interface RuntimeStatus { + healthOK: boolean + indexerEnabled: boolean + startTime: string + uptime: number + ver: string + branch: string + commitHash: string + chainID: number + checks: RuntimeChecks } -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 interface SortBy { + column: string + order: SortOrder } -export interface EventDecoded { - topicHash: string - eventSig: string - types: Array - names: Array - values: Array +export enum SortOrder { + DESC = 'DESC', + ASC = 'ASC', } export interface TokenBalance { @@ -360,6 +759,9 @@ export interface TokenBalance { accountAddress: string tokenID?: string balance: string + balanceUSD: string + priceUSD: string + priceUpdatedAt?: string blockHash: string blockNumber: number chainId: number @@ -369,39 +771,32 @@ export interface TokenBalance { tokenMetadata?: TokenMetadata } -export interface OrderbookOrder { - 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 TokenBalanceFilter { + contractAddress: string + sinceBlockNumber: number } -export interface OrderbookOrderFilter { - isListing?: boolean - userAddresses?: Array - tokenIds: Array - excludeUserAddresses?: Array - afterBlockNumber: number - afterCreatedAt: number - beforeExpiry: number - userAddress?: string - excludeUserAddress?: string +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 - accountAddress: string contractAddress: string contractType: ContractType fromAddress: string @@ -409,12 +804,45 @@ export interface TokenHistory { txnHash: string txnIndex: number txnLogIndex: number - logData: string tokenIDs: string - Amounts: 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 @@ -433,17 +861,12 @@ export interface Transaction { timestamp: string } -export interface TxnTransfer { - transferType: TxnTransferType - contractAddress: string - contractType: ContractType - from: string - to: string - tokenIds?: Array - amounts: Array - logIndex: number - contractInfo?: ContractInfo - tokenMetadata?: { [key: string]: TokenMetadata } +export interface TransactionFilter { + txnHash?: string + from?: string + to?: string + contractAddress?: string + event?: string } export interface TransactionHistoryFilter { @@ -456,14 +879,14 @@ export interface TransactionHistoryFilter { fromBlock?: number toBlock?: number tokenID?: string + omitPrices?: boolean } -export interface TransactionFilter { - txnHash?: string - from?: string - to?: string - contractAddress?: string - event?: string +export interface TransactionLog { + contractAddress: string + topics: Array + data: string + index: number } export interface TransactionReceipt { @@ -482,31 +905,60 @@ export interface TransactionReceipt { reorged: boolean } -export interface TransactionLog { +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 - topics: Array - data: string - index: number + contractType: ContractType + from: string + to: string + tokenIds?: Array + amounts: Array + logIndex: number + amountsUSD?: Array + pricesUSD?: Array + contractInfo?: ContractInfo + tokenMetadata?: { [key: string]: TokenMetadata } } -export interface TokenIDRange { - start: string - end: string +export enum TxnTransferType { + UNKNOWN = 'UNKNOWN', + SEND = 'SEND', + RECEIVE = 'RECEIVE', } -export interface Page { - page?: number - column?: string - before?: any - after?: any - sort?: Array - pageSize?: number - more?: boolean +export interface Version { + webrpcVersion: string + schemaVersion: string + schemaHash: string + appVersion: string } -export interface SortBy { - column: string - order: SortOrder +export interface WALWriterRuntimeStatus { + healthOK: boolean + startTime: string + uptime: number + ver: string + branch: string + commitHash: string + chainID: number + percentWALWritten: number } export interface WebhookListener { @@ -519,469 +971,345 @@ export interface WebhookListener { active: boolean } -export interface EventFilter { - events?: Array - contractAddresses?: Array - accounts?: Array - tokenIDs?: Array +export interface AddWebhookListenerRequest { + url: string + filters: EventFilter + projectId?: number } -export interface TokenBalanceFilter { - contractAddress: string - sinceBlockNumber: number +export interface AddWebhookListenerResponse { + status: boolean + listener: WebhookListener } -export interface MetadataOptions { - verifiedOnly?: boolean - unverifiedOnly?: boolean - includeContracts?: Array +export interface FetchTransactionReceiptRequest { + txnHash: string + maxBlockWait?: number } -export interface TokenBalancesFilter { - accountAddresses: Array - contractStatus?: ContractVerificationStatus - contractTypes?: Array - contractWhitelist?: Array - contractBlacklist?: Array - omitNativeBalances: boolean +export interface FetchTransactionReceiptResponse { + receipt: TransactionReceipt } -export interface TokenBalancesByContractFilter { - contractAddresses: Array - accountAddresses?: Array - contractStatus?: ContractVerificationStatus +export interface FetchTransactionReceiptWithFilterRequest { + filter: TransactionFilter + maxBlockWait?: number } -export interface GatewayEtherBalance { - chainId: number - error: string - result: EtherBalance +export interface FetchTransactionReceiptWithFilterResponse { + receipt: TransactionReceipt } -export interface GatewayNativeTokenBalance { - chainId: number - error: string - result: NativeTokenBalance +export interface GetAllWebhookListenersRequest { + projectId?: number } -export interface GatewayNativeTokenBalances { - chainId: number - error: string - results: Array +export interface GetAllWebhookListenersResponse { + listeners: Array } -export interface GatewayTokenBalance { - chainId: number - error: string - results: Array +export interface GetBalanceUpdatesRequest { + contractAddress: string + lastBlockNumber: number + lastBlockHash?: string + page?: Page } -export interface Indexer { - ping(headers?: object, signal?: AbortSignal): Promise - version(headers?: object, signal?: AbortSignal): Promise - runtimeStatus(headers?: object, signal?: AbortSignal): Promise - getChainID(headers?: object, signal?: AbortSignal): Promise - getEtherBalance(args: GetEtherBalanceArgs, headers?: object, signal?: AbortSignal): Promise - getNativeTokenBalance( - args: GetNativeTokenBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getTokenBalancesSummary( - args: GetTokenBalancesSummaryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getTokenBalancesDetails( - args: GetTokenBalancesDetailsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getTokenBalancesByContract( - args: GetTokenBalancesByContractArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getTokenBalances(args: GetTokenBalancesArgs, headers?: object, signal?: AbortSignal): Promise - getTokenSupplies(args: GetTokenSuppliesArgs, headers?: object, signal?: AbortSignal): Promise - getTokenSuppliesMap( - args: GetTokenSuppliesMapArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getTokenIDs(args: GetTokenIDsArgs, headers?: object, signal?: AbortSignal): Promise - getTokenIDRanges(args: GetTokenIDRangesArgs, headers?: object, signal?: AbortSignal): Promise - getBalanceUpdates( - args: GetBalanceUpdatesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getTransactionHistory( - args: GetTransactionHistoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - fetchTransactionReceipt( - args: FetchTransactionReceiptArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - fetchTransactionReceiptWithFilter( - args: FetchTransactionReceiptWithFilterArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - subscribeReceipts(args: SubscribeReceiptsArgs, options: WebrpcStreamOptions): Promise - subscribeEvents(args: SubscribeEventsArgs, options: WebrpcStreamOptions): Promise - subscribeBalanceUpdates( - args: SubscribeBalanceUpdatesArgs, - options: WebrpcStreamOptions, - ): Promise - syncBalance(args: SyncBalanceArgs, headers?: object, signal?: AbortSignal): Promise - getAllWebhookListeners( - args: GetAllWebhookListenersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getWebhookListener( - args: GetWebhookListenerArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - addWebhookListener( - args: AddWebhookListenerArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - updateWebhookListener( - args: UpdateWebhookListenerArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - removeWebhookListener( - args: RemoveWebhookListenerArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - removeAllWebhookListeners( - args: RemoveAllWebhookListenersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - toggleWebhookListener( - args: ToggleWebhookListenerArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - pauseAllWebhookListeners( - args: PauseAllWebhookListenersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - resumeAllWebhookListeners( - args: ResumeAllWebhookListenersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getOrderbookOrders( - args: GetOrderbookOrdersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getTopOrders(args: GetTopOrdersArgs, headers?: object, signal?: AbortSignal): Promise +export interface GetBalanceUpdatesResponse { + page: Page + balances: Array } -export interface PingArgs {} +export interface GetChainIDRequest {} -export interface PingReturn { - status: boolean +export interface GetChainIDResponse { + chainID: number } -export interface VersionArgs {} -export interface VersionReturn { - version: Version +export interface GetEtherBalanceRequest { + accountAddress?: string } -export interface RuntimeStatusArgs {} -export interface RuntimeStatusReturn { - status: RuntimeStatus +export interface GetEtherBalanceResponse { + balance: EtherBalance } -export interface GetChainIDArgs {} -export interface GetChainIDReturn { - chainID: number +export interface GetMarketplaceOrdersRequest { + marketplaceContractAddress: string + collectionAddress: string + filter?: MarketplaceOrderFilter + page?: Page } -export interface GetEtherBalanceArgs { - accountAddress?: string + +export interface GetMarketplaceOrdersResponse { + page?: Page + orders: Array } -export interface GetEtherBalanceReturn { - balance: EtherBalance +export interface GetMarketplaceTopOrdersRequest { + marketplaceContractAddress: string + collectionAddress: string + filter: MarketplaceTopOrdersFilter +} + +export interface GetMarketplaceTopOrdersResponse { + orders: Array } -export interface GetNativeTokenBalanceArgs { + +export interface GetNativeTokenBalanceRequest { accountAddress?: string + omitPrices?: boolean } -export interface GetNativeTokenBalanceReturn { +export interface GetNativeTokenBalanceResponse { balance: NativeTokenBalance } -export interface GetTokenBalancesSummaryArgs { - filter: TokenBalancesFilter - omitMetadata?: boolean + +export interface GetTokenBalancesRequest { + accountAddress?: string + contractAddress?: string + tokenID?: string + includeMetadata?: boolean + metadataOptions?: MetadataOptions + includeCollectionTokens?: boolean page?: Page } -export interface GetTokenBalancesSummaryReturn { +export interface GetTokenBalancesResponse { page: Page - nativeBalances: Array balances: Array } -export interface GetTokenBalancesDetailsArgs { - filter: TokenBalancesFilter + +export interface GetTokenBalancesByContractRequest { + filter: TokenBalancesByContractFilter omitMetadata?: boolean page?: Page } -export interface GetTokenBalancesDetailsReturn { +export interface GetTokenBalancesByContractResponse { page: Page - nativeBalances: Array balances: Array } -export interface GetTokenBalancesByContractArgs { - filter: TokenBalancesByContractFilter + +export interface GetTokenBalancesDetailsRequest { + filter: TokenBalancesFilter omitMetadata?: boolean page?: Page } -export interface GetTokenBalancesByContractReturn { +export interface GetTokenBalancesDetailsResponse { page: Page + nativeBalances: Array balances: Array } -export interface GetTokenBalancesArgs { - accountAddress?: string - contractAddress?: string - tokenID?: string - includeMetadata?: boolean - metadataOptions?: MetadataOptions - includeCollectionTokens?: boolean + +export interface GetTokenBalancesSummaryRequest { + filter: TokenBalancesFilter + omitMetadata?: boolean page?: Page } -export interface GetTokenBalancesReturn { +export interface GetTokenBalancesSummaryResponse { page: Page + nativeBalances: Array balances: Array } -export interface GetTokenSuppliesArgs { + +export interface GetTokenIDRangesRequest { contractAddress: string - includeMetadata?: boolean - metadataOptions?: MetadataOptions - page?: Page + lastTokenID?: string } -export interface GetTokenSuppliesReturn { - page: Page +export interface GetTokenIDRangesResponse { contractType: ContractType - tokenIDs: Array -} -export interface GetTokenSuppliesMapArgs { - tokenMap: { [key: string]: Array } - includeMetadata?: boolean - metadataOptions?: MetadataOptions + tokenIDRanges: Array + moreRanges: boolean } -export interface GetTokenSuppliesMapReturn { - supplies: { [key: string]: Array } -} -export interface GetTokenIDsArgs { +export interface GetTokenIDsRequest { contractAddress: string page?: Page } -export interface GetTokenIDsReturn { +export interface GetTokenIDsResponse { page: Page contractType: ContractType tokenIDs: Array } -export interface GetTokenIDRangesArgs { - contractAddress: string + +export interface GetTokenPriceRequest { + q: TokenPriceQuery } -export interface GetTokenIDRangesReturn { - contractType: ContractType - tokenIDRanges: Array - moreRanges: boolean +export interface GetTokenPriceResponse { + price: Price +} + +export interface GetTokenPricesRequest { + q: Array +} + +export interface GetTokenPricesResponse { + prices: Array } -export interface GetBalanceUpdatesArgs { + +export interface GetTokenSuppliesRequest { contractAddress: string - lastBlockNumber: number - lastBlockHash?: string + includeMetadata?: boolean page?: Page } -export interface GetBalanceUpdatesReturn { +export interface GetTokenSuppliesResponse { page: Page - balances: Array + contractType: ContractType + tokenIDs: Array +} + +export interface GetTokenSuppliesMapRequest { + tokenMap: { [key: string]: Array } + includeMetadata?: boolean +} + +export interface GetTokenSuppliesMapResponse { + supplies: { [key: string]: Array } } -export interface GetTransactionHistoryArgs { + +export interface GetTransactionHistoryRequest { filter: TransactionHistoryFilter page?: Page includeMetadata?: boolean metadataOptions?: MetadataOptions } -export interface GetTransactionHistoryReturn { +export interface GetTransactionHistoryResponse { page: Page transactions: Array } -export interface FetchTransactionReceiptArgs { - txnHash: string - maxBlockWait?: number -} -export interface FetchTransactionReceiptReturn { - receipt: TransactionReceipt -} -export interface FetchTransactionReceiptWithFilterArgs { - filter: TransactionFilter - maxBlockWait?: number +export interface GetWebhookListenerRequest { + id: number + projectId?: number } -export interface FetchTransactionReceiptWithFilterReturn { - receipt: TransactionReceipt -} -export interface SubscribeReceiptsArgs { - filter: TransactionFilter +export interface GetWebhookListenerResponse { + listener: WebhookListener } -export interface SubscribeReceiptsReturn { - receipt: TransactionReceipt -} -export interface SubscribeEventsArgs { - filter: EventFilter +export interface ListTokenPricesRequest { + page?: Page } -export interface SubscribeEventsReturn { - log: EventLog +export interface ListTokenPricesResponse { + page: Page + prices: Array } -export interface SubscribeBalanceUpdatesArgs { - contractAddress: string + +export interface PauseAllWebhookListenersRequest { + projectId?: number } -export interface SubscribeBalanceUpdatesReturn { - balance: TokenBalance +export interface PauseAllWebhookListenersResponse { + status: boolean } -export interface SyncBalanceArgs { - accountAddress: string - contractAddress: string - tokenID?: string + +export interface PingRequest {} + +export interface PingResponse { + status: boolean } -export interface SyncBalanceReturn {} -export interface GetAllWebhookListenersArgs { +export interface RemoveAllWebhookListenersRequest { projectId?: number } -export interface GetAllWebhookListenersReturn { - listeners: Array +export interface RemoveAllWebhookListenersResponse { + status: boolean } -export interface GetWebhookListenerArgs { + +export interface RemoveWebhookListenerRequest { id: number projectId?: number } -export interface GetWebhookListenerReturn { - listener: WebhookListener +export interface RemoveWebhookListenerResponse { + status: boolean } -export interface AddWebhookListenerArgs { - url: string - filters: EventFilter + +export interface ResumeAllWebhookListenersRequest { projectId?: number } -export interface AddWebhookListenerReturn { +export interface ResumeAllWebhookListenersResponse { status: boolean - listener: WebhookListener } -export interface UpdateWebhookListenerArgs { - listener: WebhookListener - projectId?: number + +export interface RuntimeStatusRequest {} + +export interface RuntimeStatusResponse { + status: RuntimeStatus } -export interface UpdateWebhookListenerReturn { - status: boolean +export interface SubscribeBalanceUpdatesRequest { + contractAddress: string } -export interface RemoveWebhookListenerArgs { - id: number - projectId?: number + +export interface SubscribeBalanceUpdatesResponse { + balance: TokenBalance } -export interface RemoveWebhookListenerReturn { - status: boolean +export interface SubscribeEventsRequest { + filter: EventFilter } -export interface RemoveAllWebhookListenersArgs { - projectId?: number + +export interface SubscribeEventsResponse { + log: EventLog } -export interface RemoveAllWebhookListenersReturn { - status: boolean +export interface SubscribeReceiptsRequest { + filter: TransactionFilter } -export interface ToggleWebhookListenerArgs { - id: number - projectId?: number + +export interface SubscribeReceiptsResponse { + receipt: TransactionReceipt } -export interface ToggleWebhookListenerReturn { - webhookListener: WebhookListener +export interface SyncBalanceRequest { + accountAddress: string + contractAddress: string + tokenID?: string } -export interface PauseAllWebhookListenersArgs { + +export interface SyncBalanceResponse {} + +export interface ToggleWebhookListenerRequest { + id: number projectId?: number } -export interface PauseAllWebhookListenersReturn { - status: boolean +export interface ToggleWebhookListenerResponse { + webhookListener: WebhookListener } -export interface ResumeAllWebhookListenersArgs { + +export interface UpdateWebhookListenerRequest { + listener: WebhookListener projectId?: number } -export interface ResumeAllWebhookListenersReturn { +export interface UpdateWebhookListenerResponse { status: boolean } -export interface GetOrderbookOrdersArgs { - page?: Page - orderbookContractAddress: string - collectionAddress: string - currencyAddresses: Array - filter: OrderbookOrderFilter - orderStatuses: Array - filters: Array - beforeExpiryTimestamp: number - blockNumberAfter: number - createdAtAfter: number -} -export interface GetOrderbookOrdersReturn { - page?: Page - orders: Array -} -export interface GetTopOrdersArgs { - orderbookContractAddress: string - collectionAddress: string - currencyAddresses: Array - tokenIDs: Array - isListing: boolean - priceSort: SortOrder - excludeUser?: string -} +export interface VersionRequest {} -export interface GetTopOrdersReturn { - orders: Array +export interface VersionResponse { + version: Version } // // Client // -export class Indexer implements Indexer { + +export class Indexer implements IndexerClient { protected hostname: string protected fetch: Fetch protected path = '/rpc/Indexer/' @@ -995,600 +1323,750 @@ export class Indexer implements Indexer { return this.hostname + this.path + name } - ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Ping'), createHTTPRequest({}, headers, signal)).then( + 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 { - status: _data.status, - } + return JsonDecode(_data, 'AddWebhookListenerResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + 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( + 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 { - version: _data.version, - } + return JsonDecode(_data, 'FetchTransactionReceiptResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + 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( + 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 { - status: _data.status, - } + return JsonDecode( + _data, + 'FetchTransactionReceiptWithFilterResponse', + ) }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + 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( + 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 { - chainID: _data.chainID, - } + return JsonDecode(_data, 'GetAllWebhookListenersResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getEtherBalance = ( - args: GetEtherBalanceArgs, + getBalanceUpdates = ( + req: GetBalanceUpdatesRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetEtherBalance'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetBalanceUpdates'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - balance: _data.balance, - } + return JsonDecode(_data, 'GetBalanceUpdatesResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getNativeTokenBalance = ( - args: GetNativeTokenBalanceArgs, + 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('GetNativeTokenBalance'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetEtherBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - balance: _data.balance, - } + return JsonDecode(_data, 'GetEtherBalanceResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTokenBalancesSummary = ( - args: GetTokenBalancesSummaryArgs, + getMarketplaceOrders = ( + req: GetMarketplaceOrdersRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesSummary'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetMarketplaceOrders'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - nativeBalances: >_data.nativeBalances, - balances: >_data.balances, - } + return JsonDecode(_data, 'GetMarketplaceOrdersResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTokenBalancesDetails = ( - args: GetTokenBalancesDetailsArgs, + getMarketplaceTopOrders = ( + req: GetMarketplaceTopOrdersRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesDetails'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetMarketplaceTopOrders'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - nativeBalances: >_data.nativeBalances, - balances: >_data.balances, - } + return JsonDecode(_data, 'GetMarketplaceTopOrdersResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTokenBalancesByContract = ( - args: GetTokenBalancesByContractArgs, + getNativeTokenBalance = ( + req: GetNativeTokenBalanceRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesByContract'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetNativeTokenBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - balances: >_data.balances, - } + return JsonDecode(_data, 'GetNativeTokenBalanceResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getTokenBalances = ( - args: GetTokenBalancesArgs, + req: GetTokenBalancesRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalances'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenBalances'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - balances: >_data.balances, - } + return JsonDecode(_data, 'GetTokenBalancesResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTokenSupplies = ( - args: GetTokenSuppliesArgs, + getTokenBalancesByContract = ( + req: GetTokenBalancesByContractRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenSupplies'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenBalancesByContract'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - contractType: _data.contractType, - tokenIDs: >_data.tokenIDs, - } + return JsonDecode(_data, 'GetTokenBalancesByContractResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTokenSuppliesMap = ( - args: GetTokenSuppliesMapArgs, + getTokenBalancesDetails = ( + req: GetTokenBalancesDetailsRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenSuppliesMap'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenBalancesDetails'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - supplies: <{ [key: string]: Array }>_data.supplies, - } + return JsonDecode(_data, 'GetTokenBalancesDetailsResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTokenIDs = (args: GetTokenIDsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetTokenIDs'), createHTTPRequest(args, headers, signal)).then( + 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 { - page: _data.page, - contractType: _data.contractType, - tokenIDs: >_data.tokenIDs, - } + return JsonDecode(_data, 'GetTokenBalancesSummaryResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getTokenIDRanges = ( - args: GetTokenIDRangesArgs, + req: GetTokenIDRangesRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenIDRanges'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenIDRanges'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractType: _data.contractType, - tokenIDRanges: >_data.tokenIDRanges, - moreRanges: _data.moreRanges, - } + return JsonDecode(_data, 'GetTokenIDRangesResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getBalanceUpdates = ( - args: GetBalanceUpdatesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetBalanceUpdates'), createHTTPRequest(args, headers, signal)).then( + 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 { - page: _data.page, - balances: >_data.balances, - } + return JsonDecode(_data, 'GetTokenIDsResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTransactionHistory = ( - args: GetTransactionHistoryArgs, + getTokenPrice = ( + req: GetTokenPriceRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTransactionHistory'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenPrice'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - transactions: >_data.transactions, - } + return JsonDecode(_data, 'GetTokenPriceResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - fetchTransactionReceipt = ( - args: FetchTransactionReceiptArgs, + getTokenPrices = ( + req: GetTokenPricesRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('FetchTransactionReceipt'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenPrices'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - receipt: _data.receipt, - } + return JsonDecode(_data, 'GetTokenPricesResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - fetchTransactionReceiptWithFilter = ( - args: FetchTransactionReceiptWithFilterArgs, + getTokenSupplies = ( + req: GetTokenSuppliesRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('FetchTransactionReceiptWithFilter'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenSupplies'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - receipt: _data.receipt, - } + return JsonDecode(_data, 'GetTokenSuppliesResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - subscribeReceipts = ( - args: SubscribeReceiptsArgs, - options: WebrpcStreamOptions, - ): Promise => { - const _fetch = () => - this.fetch(this.url('SubscribeReceipts'), createHTTPRequest(args, options.headers, options.signal)).then( - async (res) => { - await sseResponse(res, options, _fetch) - }, - (error) => { - options.onError(error, _fetch) - }, - ) - return _fetch() - } - subscribeEvents = (args: SubscribeEventsArgs, options: WebrpcStreamOptions): Promise => { - const _fetch = () => - this.fetch(this.url('SubscribeEvents'), createHTTPRequest(args, options.headers, options.signal)).then( - async (res) => { - await sseResponse(res, options, _fetch) - }, - (error) => { - options.onError(error, _fetch) - }, - ) - return _fetch() - } - subscribeBalanceUpdates = ( - args: SubscribeBalanceUpdatesArgs, - options: WebrpcStreamOptions, - ): Promise => { - const _fetch = () => - this.fetch(this.url('SubscribeBalanceUpdates'), createHTTPRequest(args, options.headers, options.signal)).then( - async (res) => { - await sseResponse(res, options, _fetch) - }, - (error) => { - options.onError(error, _fetch) - }, - ) - return _fetch() - } - syncBalance = (args: SyncBalanceArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('SyncBalance'), createHTTPRequest(args, headers, signal)).then( + 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 {} + return JsonDecode(_data, 'GetTokenSuppliesMapResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getAllWebhookListeners = ( - args: GetAllWebhookListenersArgs, + getTransactionHistory = ( + req: GetTransactionHistoryRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetAllWebhookListeners'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTransactionHistory'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - listeners: >_data.listeners, - } + return JsonDecode(_data, 'GetTransactionHistoryResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getWebhookListener = ( - args: GetWebhookListenerArgs, + req: GetWebhookListenerRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetWebhookListener'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - listener: _data.listener, - } + return JsonDecode(_data, 'GetWebhookListenerResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - addWebhookListener = ( - args: AddWebhookListenerArgs, + listTokenPrices = ( + req: ListTokenPricesRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('AddWebhookListener'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('ListTokenPrices'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - listener: _data.listener, - } + return JsonDecode(_data, 'ListTokenPricesResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - updateWebhookListener = ( - args: UpdateWebhookListenerArgs, + pauseAllWebhookListeners = ( + req: PauseAllWebhookListenersRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('UpdateWebhookListener'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('PauseAllWebhookListeners'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - } + return JsonDecode(_data, 'PauseAllWebhookListenersResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - removeWebhookListener = ( - args: RemoveWebhookListenerArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('RemoveWebhookListener'), createHTTPRequest(args, headers, signal)).then( + 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, - } + return JsonDecode(_data, 'PingResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } removeAllWebhookListeners = ( - args: RemoveAllWebhookListenersArgs, + req: RemoveAllWebhookListenersRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('RemoveAllWebhookListeners'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('RemoveAllWebhookListeners'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - } + return JsonDecode(_data, 'RemoveAllWebhookListenersResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - toggleWebhookListener = ( - args: ToggleWebhookListenerArgs, + removeWebhookListener = ( + req: RemoveWebhookListenerRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ToggleWebhookListener'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('RemoveWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - webhookListener: _data.webhookListener, - } + return JsonDecode(_data, 'RemoveWebhookListenerResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - pauseAllWebhookListeners = ( - args: PauseAllWebhookListenersArgs, + resumeAllWebhookListeners = ( + req: ResumeAllWebhookListenersRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('PauseAllWebhookListeners'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('ResumeAllWebhookListeners'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - } + return JsonDecode(_data, 'ResumeAllWebhookListenersResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - resumeAllWebhookListeners = ( - args: ResumeAllWebhookListenersArgs, + 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('ResumeAllWebhookListeners'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('ToggleWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - } + return JsonDecode(_data, 'ToggleWebhookListenerResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getOrderbookOrders = ( - args: GetOrderbookOrdersArgs, + updateWebhookListener = ( + req: UpdateWebhookListenerRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetOrderbookOrders'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('UpdateWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - orders: >_data.orders, - } + return JsonDecode(_data, 'UpdateWebhookListenerResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTopOrders = (args: GetTopOrdersArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetTopOrders'), createHTTPRequest(args, headers, signal)).then( + version = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - orders: >_data.orders, - } + return JsonDecode(_data, 'VersionResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1643,16 +2121,11 @@ const sseResponse = async (res: Response, options: WebrpcStreamOptions, ret lastReadTime = Date.now() buffer += decoder.decode(value, { stream: true }) } catch (error) { - let message = '' - if (error instanceof Error) { - message = error.message - } - if (error instanceof DOMException && error.name === 'AbortError') { onError( - WebrpcRequestFailedError.new({ + WebrpcClientAbortedError.new({ message: 'AbortError', - cause: `AbortError: ${message}`, + cause: `AbortError: ${error instanceof Error ? error.message : String(error)}`, }), () => { throw new Error('Abort signal cannot be used to reconnect') @@ -1661,7 +2134,7 @@ const sseResponse = async (res: Response, options: WebrpcStreamOptions, ret } else { onError( WebrpcStreamLostError.new({ - cause: `reader.read(): ${message}`, + cause: `reader.read(): ${error instanceof Error ? error.message : String(error)}`, }), retryFetch, ) @@ -1671,12 +2144,13 @@ const sseResponse = async (res: Response, options: WebrpcStreamOptions, ret let lines = buffer.split('\n') for (let i = 0; i < lines.length - 1; i++) { - if (lines[i]!.length == 0) { + const line = lines[i] + if (line?.length === 0) { continue } let data: any try { - data = JSON.parse(lines[i]!) + data = JSON.parse(line) if (data.hasOwnProperty('webrpcError')) { const error = data.webrpcError const code: number = typeof error.code === 'number' ? error.code : 0 @@ -1690,8 +2164,7 @@ const sseResponse = async (res: Response, options: WebrpcStreamOptions, ret onError( WebrpcBadResponseError.new({ status: res.status, - // @ts-ignore - cause: `JSON.parse(): ${error.message}`, + cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}`, }), retryFetch, ) @@ -1700,7 +2173,8 @@ const sseResponse = async (res: Response, options: WebrpcStreamOptions, ret } if (!done) { - buffer = lines[lines.length - 1]! + const lastLine = lines[lines.length - 1] + buffer = lastLine || '' continue } @@ -1709,16 +2183,13 @@ const sseResponse = async (res: Response, options: WebrpcStreamOptions, ret } } -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 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 => { @@ -1727,13 +2198,9 @@ const buildResponse = (res: Response): Promise => { 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}`, + cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, }) } if (!res.ok) { @@ -1744,518 +2211,511 @@ const buildResponse = (res: Response): Promise => { }) } +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 { - 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 + + 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(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause) + return new this({ message: payload.message, code: payload.code, status: payload.status, cause: 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) + 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( - name: string = 'WebrpcRequestFailed', - code: number = -1, - message: string = 'request failed', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcBadRoute', - code: number = -2, - message: string = 'bad route', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcBadMethod', - code: number = -3, - message: string = 'bad method', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcBadRequest', - code: number = -4, - message: string = 'bad request', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcBadResponse', - code: number = -5, - message: string = 'bad response', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcServerPanic', - code: number = -6, - message: string = 'server panic', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcInternalError', - code: number = -7, - message: string = 'internal error', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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 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 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( - name: string = 'WebrpcStreamLost', - code: number = -9, - message: string = 'stream lost', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcStreamFinished', - code: number = -10, - message: string = 'stream finished', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 ProjectNotFoundError extends WebrpcError { - constructor( - name: string = 'ProjectNotFound', - code: number = 1100, - message: string = 'Project not found', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ProjectNotFoundError.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 AccessKeyNotFoundError extends WebrpcError { - constructor( - name: string = 'AccessKeyNotFound', - code: number = 1101, - message: string = 'Access key not found', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AccessKeyNotFoundError.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 AccessKeyMismatchError extends WebrpcError { - constructor( - name: string = 'AccessKeyMismatch', - code: number = 1102, - message: string = 'Access key mismatch', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AccessKeyMismatchError.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 InvalidOriginError extends WebrpcError { - constructor( - name: string = 'InvalidOrigin', - code: number = 1103, - message: string = 'Invalid origin for Access Key', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidOriginError.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 InvalidServiceError extends WebrpcError { - constructor( - name: string = 'InvalidService', - code: number = 1104, - message: string = 'Service not enabled for Access key', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidServiceError.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 UnauthorizedUserError extends WebrpcError { - constructor( - name: string = 'UnauthorizedUser', - code: number = 1105, - message: string = 'Unauthorized user', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnauthorizedUserError.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 QuotaExceededError extends WebrpcError { - constructor( - name: string = 'QuotaExceeded', - code: number = 1200, - message: string = 'Quota exceeded', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, QuotaExceededError.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 RateLimitError extends WebrpcError { - constructor( - name: string = 'RateLimit', - code: number = 1201, - message: string = 'Rate limit exceeded', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RateLimitError.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 NoDefaultKeyError extends WebrpcError { - constructor( - name: string = 'NoDefaultKey', - code: number = 1300, - message: string = 'No default access key found', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NoDefaultKeyError.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 MaxAccessKeysError extends WebrpcError { - constructor( - name: string = 'MaxAccessKeys', - code: number = 1301, - message: string = 'Access keys limit reached', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MaxAccessKeysError.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 AtLeastOneKeyError extends WebrpcError { - constructor( - name: string = 'AtLeastOneKey', - code: number = 1302, - message: string = 'You need at least one Access Key', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AtLeastOneKeyError.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 TimeoutError extends WebrpcError { - constructor( - name: string = 'Timeout', - code: number = 1900, - message: string = 'Request timed out', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, TimeoutError.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 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 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 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 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 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 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 ResourceExhaustedError extends WebrpcError { - constructor( - name: string = 'ResourceExhausted', - code: number = 2004, - message: string = 'Resource exhausted', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ResourceExhaustedError.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 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 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 MetadataCallFailedError extends WebrpcError { - constructor( - name: string = 'MetadataCallFailed', - code: number = 3003, - message: string = 'Metadata service call failed', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MetadataCallFailedError.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) } } @@ -2268,38 +2728,78 @@ export enum errors { WebrpcBadResponse = 'WebrpcBadResponse', WebrpcServerPanic = 'WebrpcServerPanic', WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientDisconnected = 'WebrpcClientDisconnected', + 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', + AccessKeyNotFound = 'AccessKeyNotFound', + AtLeastOneKey = 'AtLeastOneKey', + Geoblocked = 'Geoblocked', + InvalidArgument = 'InvalidArgument', InvalidOrigin = 'InvalidOrigin', InvalidService = 'InvalidService', - UnauthorizedUser = 'UnauthorizedUser', + MaxAccessKeys = 'MaxAccessKeys', + MetadataCallFailed = 'MetadataCallFailed', + MethodNotFound = 'MethodNotFound', + NoDefaultKey = 'NoDefaultKey', + NotFound = 'NotFound', + PermissionDenied = 'PermissionDenied', + ProjectNotFound = 'ProjectNotFound', + QueryFailed = 'QueryFailed', QuotaExceeded = 'QuotaExceeded', RateLimit = 'RateLimit', - NoDefaultKey = 'NoDefaultKey', - MaxAccessKeys = 'MaxAccessKeys', - AtLeastOneKey = 'AtLeastOneKey', + RateLimited = 'RateLimited', + RequestConflict = 'RequestConflict', + ResourceExhausted = 'ResourceExhausted', + SessionExpired = 'SessionExpired', Timeout = 'Timeout', - InvalidArgument = 'InvalidArgument', + Unauthorized = 'Unauthorized', + UnauthorizedUser = 'UnauthorizedUser', Unavailable = 'Unavailable', - QueryFailed = 'QueryFailed', - ResourceExhausted = 'ResourceExhausted', - NotFound = 'NotFound', - MetadataCallFailed = 'MetadataCallFailed', } -const webrpcErrorByCode: { [code: number]: any } = { +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, @@ -2308,46 +2808,89 @@ const webrpcErrorByCode: { [code: number]: any } = { [-5]: WebrpcBadResponseError, [-6]: WebrpcServerPanicError, [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientDisconnectedError, + [-8]: WebrpcClientAbortedError, [-9]: WebrpcStreamLostError, [-10]: WebrpcStreamFinishedError, - [1000]: UnauthorizedError, - [1001]: PermissionDeniedError, - [1002]: SessionExpiredError, - [1003]: MethodNotFoundError, - [1004]: RequestConflictError, [1005]: AbortedError, - [1006]: GeoblockedError, - [1007]: RateLimitedError, - [1100]: ProjectNotFoundError, - [1101]: AccessKeyNotFoundError, [1102]: AccessKeyMismatchError, + [1101]: AccessKeyNotFoundError, + [1302]: AtLeastOneKeyError, + [1006]: GeoblockedError, + [2001]: InvalidArgumentError, [1103]: InvalidOriginError, [1104]: InvalidServiceError, - [1105]: UnauthorizedUserError, + [1301]: MaxAccessKeysError, + [3003]: MetadataCallFailedError, + [1003]: MethodNotFoundError, + [1300]: NoDefaultKeyError, + [3000]: NotFoundError, + [1001]: PermissionDeniedError, + [1100]: ProjectNotFoundError, + [2003]: QueryFailedError, [1200]: QuotaExceededError, [1201]: RateLimitError, - [1300]: NoDefaultKeyError, - [1301]: MaxAccessKeysError, - [1302]: AtLeastOneKeyError, + [1007]: RateLimitedError, + [1004]: RequestConflictError, + [2004]: ResourceExhaustedError, + [1002]: SessionExpiredError, [1900]: TimeoutError, - [2001]: InvalidArgumentError, + [1000]: UnauthorizedError, + [1105]: UnauthorizedUserError, [2002]: UnavailableError, - [2003]: QueryFailedError, - [2004]: ResourceExhaustedError, - [3000]: NotFoundError, - [3003]: MetadataCallFailedError, } -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise +// +// Webrpc +// -export interface WebrpcStreamOptions extends WebrpcOptions { - onMessage: (message: T) => void - onError: (error: WebrpcError, reconnect: () => void) => void - onOpen?: () => void - onClose?: () => void +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 interface WebrpcOptions { - headers?: HeadersInit - signal?: AbortSignal + +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 index 92f85b2c8..d22bc5b56 100644 --- a/packages/services/indexer/src/indexergw.gen.ts +++ b/packages/services/indexer/src/indexergw.gen.ts @@ -1,79 +1,174 @@ /* eslint-disable */ -// sequence-indexer v0.4.0 5be4a3e78d9c7e0cc378c675ec01c518e83772e3 +// sequence-indexer v0.4.0 212120aad9a46e88ead9a2183c5717e9902d8c2b // -- -// Code generated by webrpc-gen@v0.21.1 with typescript generator. DO NOT EDIT. +// Code generated by Webrpc-gen@v0.31.2 with typescript generator. DO NOT EDIT. // -// webrpc-gen -schema=indexer.ridl -service=IndexerGateway -target=typescript -client -out=./clients/indexergw.gen.ts +// webrpc-gen -schema=merged.gen.json -service=IndexerGateway -target=typescript -client -out=./clients/indexergw.gen.ts -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.21.1;gen-typescript@v0.15.1;sequence-indexer@v0.4.0' - -// WebRPC description and code-gen version -export const WebRPCVersion = 'v1' +// Webrpc description and code-gen version +export const WebrpcVersion = 'v1' // Schema version of your RIDL schema -export const WebRPCSchemaVersion = 'v0.4.0' +export const WebrpcSchemaVersion = 'v0.4.0' // Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = '5be4a3e78d9c7e0cc378c675ec01c518e83772e3' +export const WebrpcSchemaHash = '212120aad9a46e88ead9a2183c5717e9902d8c2b' -type WebrpcGenVersions = { - webrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} +// +// Client interface +// -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } +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 - return parseWebrpcGenVersions(headerValue) -} + /** + * GetChains returns a list of chains with their ID and name + */ + getChains(req: GetChainsRequest, headers?: object, signal?: AbortSignal): Promise -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } + /** + * 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 - const [_, webrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') + /** + * 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 - return { - webrpcGenVersion: webrpcGenVersion!, - codeGenName: codeGenName!, - codeGenVersion: codeGenVersion!, - schemaName: schemaName!, - schemaVersion: schemaVersion!, - } + /** + * 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 } // -// Types +// Schema types // -export enum ResourceStatus { - NOT_AVAILABLE = 'NOT_AVAILABLE', - REFRESHING = 'REFRESHING', - AVAILABLE = 'AVAILABLE', +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 { @@ -93,56 +188,29 @@ export interface ContractInfo { status: ResourceStatus } -export interface ContractInfoExtensions { - link: string - description: string - categories: Array - 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 TokenMetadata { - 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 ContractInfoExtensionIndexingInfo { + useOnChainBalance: boolean } -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 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 { @@ -156,72 +224,73 @@ export enum ContractType { ERC721_BRIDGE = 'ERC721_BRIDGE', ERC1155_BRIDGE = 'ERC1155_BRIDGE', SEQ_MARKETPLACE = 'SEQ_MARKETPLACE', + ERC6909 = 'ERC6909', } -export enum EventLogType { - UNKNOWN = 'UNKNOWN', - BLOCK_ADDED = 'BLOCK_ADDED', - BLOCK_REMOVED = 'BLOCK_REMOVED', -} - -export enum EventLogDataType { - EVENT = 'EVENT', - TOKEN_TRANSFER = 'TOKEN_TRANSFER', - NATIVE_TOKEN_TRANSFER = 'NATIVE_TOKEN_TRANSFER', - SEQUENCE_TXN = 'SEQUENCE_TXN', -} - -export enum OrderStatus { - OPEN = 'OPEN', - CLOSED = 'CLOSED', - CANCELLED = 'CANCELLED', +export enum ContractVerificationStatus { + VERIFIED = 'VERIFIED', + UNVERIFIED = 'UNVERIFIED', + ALL = 'ALL', } -export enum TxnTransferType { - UNKNOWN = 'UNKNOWN', - SEND = 'SEND', - RECEIVE = 'RECEIVE', +export interface DiskUsage { + humanReadable: string + used: number + size: number + percent: number + dirs: { [key: string]: string } } -export enum TransactionStatus { - FAILED = 'FAILED', - SUCCESSFUL = 'SUCCESSFUL', +export interface EtherBalance { + accountAddress: string + balanceWei: string } -export enum TransactionType { - LegacyTxnType = 'LegacyTxnType', - AccessListTxnType = 'AccessListTxnType', - DynamicFeeTxnType = 'DynamicFeeTxnType', +export interface EventDecoded { + topicHash: string + eventSig: string + types: Array + names: Array + values: Array } -export enum SortOrder { - DESC = 'DESC', - ASC = 'ASC', +export interface EventFilter { + events?: Array + contractAddresses?: Array + accounts?: Array + tokenIDs?: Array } -export enum ContractVerificationStatus { - VERIFIED = 'VERIFIED', - UNVERIFIED = 'UNVERIFIED', - ALL = 'ALL', +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 interface Version { - webrpcVersion: string - schemaVersion: string - schemaHash: string - appVersion: string +export enum EventLogDataType { + EVENT = 'EVENT', + TOKEN_TRANSFER = 'TOKEN_TRANSFER', + NATIVE_TOKEN_TRANSFER = 'NATIVE_TOKEN_TRANSFER', + SEQUENCE_TXN = 'SEQUENCE_TXN', } -export interface RuntimeStatus { - healthOK: boolean - indexerEnabled: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - chainID: number - checks: RuntimeChecks +export enum EventLogType { + UNKNOWN = 'UNKNOWN', + BLOCK_ADDED = 'BLOCK_ADDED', + BLOCK_REMOVED = 'BLOCK_REMOVED', } export interface GatewayBackendResponseTime { @@ -235,6 +304,30 @@ export interface GatewayBackendRuntimeStatus { 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 @@ -245,113 +338,173 @@ export interface GatewayRuntimeStatus { backends: Array } -export interface WALWriterRuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string +export interface GatewayTokenBalance { + chainId: number + errorReason?: string + results: Array +} + +export interface GatewayTokenPriceQuery { chainID: number - percentWALWritten: number + queries: Array } -export interface RuntimeChecks { - running: boolean - runnables: any - cgoEnabled: boolean - quotaControlEnabled: boolean - syncMode: string - percentIndexed: number +export interface GatewayTransaction { + chainId: number + errorReason?: string + results: Array +} + +export interface IndexState { + chainId: string lastBlockNum: number - lastBlockNumWithState: number - bloomStatus: BloomStatus - bond: Bond - diskUsage: DiskUsage + lastBlockHash: string } -export interface DiskUsage { - humanReadable: string - used: number - size: number - percent: number - dirs: { [key: string]: string } +export interface IndexedBlock { + blockNumber: number + blockShortHash: string } -export interface Bond { - pebble: PebbleMetrics - estimatedDiskUsagePerTable: any - estimatedDiskUsageTotal: string +export interface IndexerMetrics { + blocksPerSecond: number + eventsPerSecond: number } -export interface PebbleMetrics { - compactionCount: number - compactionEstimatedDebt: number - compactionInProgressBytes: number - compactionNumInProgress: number - compactionMarkedFiles: 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 BloomStatus { - enabled: boolean - initialized: boolean - bloomInitElapsedTime: string +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 EtherBalance { - accountAddress: string - balanceWei: string +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 - error: string + balanceUSD: string + priceUSD: string + priceUpdatedAt?: string + errorReason?: string } -export interface IndexState { - chainId: string - lastBlockNum: number - lastBlockHash: string +export enum NetworkType { + MAINNETS = 'MAINNETS', + TESTNETS = 'TESTNETS', + ALL = 'ALL', } -export interface IndexedBlock { - blockNumber: number - blockShortHash: string +export enum OrderStatus { + OPEN = 'OPEN', + CLOSED = 'CLOSED', + CANCELLED = 'CANCELLED', } -export interface TxnInfo { - from: string - to: string - value: string +export interface Page { + page?: number + column?: string + before?: any + after?: any + sort?: Array + pageSize?: number + more?: boolean } -export interface EventLog { - id: number - uid: string - type: EventLogType - blockNumber: number - blockHash: string - parentBlockHash: string +export interface PebbleMetrics { + compactionCount: number + compactionEstimatedDebt: number + compactionInProgressBytes: number + compactionNumInProgress: number + compactionMarkedFiles: number +} + +export interface Price { contractAddress: string - contractType: ContractType - txnHash: string - txnIndex: number - txnLogIndex: number - logDataType: EventLogDataType - ts: string - txnInfo?: TxnInfo - rawLog?: { [key: string]: any } - event?: EventDecoded + tokenID?: string + priceUSD: string + updatedAt?: string } -export interface EventDecoded { - topicHash: string - eventSig: string - types: Array - names: Array - values: Array +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 { @@ -360,6 +513,9 @@ export interface TokenBalance { accountAddress: string tokenID?: string balance: string + balanceUSD: string + priceUSD: string + priceUpdatedAt?: string blockHash: string blockNumber: number chainId: number @@ -369,50 +525,76 @@ export interface TokenBalance { tokenMetadata?: TokenMetadata } -export interface OrderbookOrder { - orderId: string - tokenContract: string - tokenId: string - isListing: boolean - quantity: string - quantityRemaining: string - currencyAddress: string - pricePerToken: string - expiry: string - orderStatus: OrderStatus - createdBy: string +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 - orderbookContractAddress: string - createdAt: 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 OrderbookOrderFilter { - isListing?: boolean - userAddresses?: Array - tokenIds: Array - excludeUserAddresses?: Array - afterBlockNumber: number - afterCreatedAt: number - beforeExpiry: number - userAddress?: string - excludeUserAddress?: 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 TokenHistory { - blockNumber: number - blockHash: string - accountAddress: string +export interface TokenPriceQuery { contractAddress: string - contractType: ContractType - fromAddress: string - toAddress: string - txnHash: string - txnIndex: number - txnLogIndex: number - logData: string - tokenIDs: string - Amounts: string - ts: string + tokenID?: string } export interface TokenSupply { @@ -433,17 +615,12 @@ export interface Transaction { timestamp: string } -export interface TxnTransfer { - transferType: TxnTransferType - contractAddress: string - contractType: ContractType - from: string - to: string - tokenIds?: Array - amounts: Array - logIndex: number - contractInfo?: ContractInfo - tokenMetadata?: { [key: string]: TokenMetadata } +export interface TransactionFilter { + txnHash?: string + from?: string + to?: string + contractAddress?: string + event?: string } export interface TransactionHistoryFilter { @@ -456,14 +633,14 @@ export interface TransactionHistoryFilter { fromBlock?: number toBlock?: number tokenID?: string + omitPrices?: boolean } -export interface TransactionFilter { - txnHash?: string - from?: string - to?: string - contractAddress?: string - event?: string +export interface TransactionLog { + contractAddress: string + topics: Array + data: string + index: number } export interface TransactionReceipt { @@ -482,31 +659,60 @@ export interface TransactionReceipt { reorged: boolean } -export interface TransactionLog { +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 - topics: Array - data: string - index: number + contractType: ContractType + from: string + to: string + tokenIds?: Array + amounts: Array + logIndex: number + amountsUSD?: Array + pricesUSD?: Array + contractInfo?: ContractInfo + tokenMetadata?: { [key: string]: TokenMetadata } } -export interface TokenIDRange { - start: string - end: string +export enum TxnTransferType { + UNKNOWN = 'UNKNOWN', + SEND = 'SEND', + RECEIVE = 'RECEIVE', } -export interface Page { - page?: number - column?: string - before?: any - after?: any - sort?: Array - pageSize?: number - more?: boolean +export interface Version { + webrpcVersion: string + schemaVersion: string + schemaHash: string + appVersion: string } -export interface SortBy { - column: string - order: SortOrder +export interface WALWriterRuntimeStatus { + healthOK: boolean + startTime: string + uptime: number + ver: string + branch: string + commitHash: string + chainID: number + percentWALWritten: number } export interface WebhookListener { @@ -519,109 +725,45 @@ export interface WebhookListener { active: boolean } -export interface EventFilter { - events?: Array - contractAddresses?: Array - accounts?: Array - tokenIDs?: Array -} - -export interface TokenBalanceFilter { +export interface GetBalanceUpdatesRequest { + chainIds?: Array + networks?: Array + networkType?: NetworkType contractAddress: string - sinceBlockNumber: number -} - -export interface MetadataOptions { - verifiedOnly?: boolean - unverifiedOnly?: boolean - includeContracts?: Array -} - -export interface TokenBalancesFilter { - accountAddresses: Array - contractStatus?: ContractVerificationStatus - contractTypes?: Array - contractWhitelist?: Array - contractBlacklist?: Array - omitNativeBalances: boolean -} - -export interface TokenBalancesByContractFilter { - contractAddresses: Array - accountAddresses?: Array - contractStatus?: ContractVerificationStatus -} - -export interface GatewayEtherBalance { - chainId: number - error: string - result: EtherBalance -} - -export interface GatewayNativeTokenBalance { - chainId: number - error: string - result: NativeTokenBalance + lastBlockNumber: number + lastBlockHash?: string + page?: Page } -export interface GatewayNativeTokenBalances { - chainId: number - error: string - results: Array +export interface GetBalanceUpdatesResponse { + page: Page + balances: Array } -export interface GatewayTokenBalance { - chainId: number - error: string - results: Array +export interface GetChainsRequest { + networkType?: NetworkType } -export interface IndexerGateway { - getNativeTokenBalance( - args: GetNativeTokenBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getTokenBalances(args: GetTokenBalancesArgs, headers?: object, signal?: AbortSignal): Promise - getTokenBalancesSummary( - args: GetTokenBalancesSummaryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getTokenBalancesDetails( - args: GetTokenBalancesDetailsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getTokenBalancesByContract( - args: GetTokenBalancesByContractArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getBalanceUpdates( - args: GetBalanceUpdatesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - ping(headers?: object, signal?: AbortSignal): Promise - version(headers?: object, signal?: AbortSignal): Promise - runtimeStatus(headers?: object, signal?: AbortSignal): Promise +export interface GetChainsResponse { + chains: Array } -export interface GetNativeTokenBalanceArgs { +export interface GetNativeTokenBalanceRequest { chainIds?: Array networks?: Array - testnets?: boolean + networkType?: NetworkType accountAddress?: string + omitPrices?: boolean } -export interface GetNativeTokenBalanceReturn { +export interface GetNativeTokenBalanceResponse { balances: Array } -export interface GetTokenBalancesArgs { + +export interface GetTokenBalancesRequest { chainIds?: Array networks?: Array - testnets?: boolean + networkType?: NetworkType accountAddress?: string contractAddress?: string tokenID?: string @@ -631,85 +773,109 @@ export interface GetTokenBalancesArgs { page?: Page } -export interface GetTokenBalancesReturn { +export interface GetTokenBalancesResponse { page: Page balances: Array } -export interface GetTokenBalancesSummaryArgs { + +export interface GetTokenBalancesByContractRequest { chainIds?: Array networks?: Array - testnets?: boolean - filter: TokenBalancesFilter + networkType?: NetworkType + filter: TokenBalancesByContractFilter omitMetadata?: boolean page?: Page } -export interface GetTokenBalancesSummaryReturn { +export interface GetTokenBalancesByContractResponse { page: Page - nativeBalances: Array balances: Array } -export interface GetTokenBalancesDetailsArgs { + +export interface GetTokenBalancesDetailsRequest { chainIds?: Array networks?: Array - testnets?: boolean + networkType?: NetworkType filter: TokenBalancesFilter omitMetadata?: boolean page?: Page } -export interface GetTokenBalancesDetailsReturn { +export interface GetTokenBalancesDetailsResponse { page: Page nativeBalances: Array balances: Array } -export interface GetTokenBalancesByContractArgs { + +export interface GetTokenBalancesSummaryRequest { chainIds?: Array networks?: Array - testnets?: boolean - filter: TokenBalancesByContractFilter + networkType?: NetworkType + filter: TokenBalancesFilter omitMetadata?: boolean page?: Page } -export interface GetTokenBalancesByContractReturn { +export interface GetTokenBalancesSummaryResponse { page: Page + nativeBalances: Array balances: Array } -export interface GetBalanceUpdatesArgs { + +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 - testnets?: boolean - contractAddress: string - lastBlockNumber: number - lastBlockHash?: string + networkType?: NetworkType + filter: TransactionHistoryFilter + includeMetadata?: boolean + metadataOptions?: MetadataOptions page?: Page } -export interface GetBalanceUpdatesReturn { +export interface GetTransactionHistoryResponse { page: Page - balances: Array + transactions: Array } -export interface PingArgs {} -export interface PingReturn { +export interface PingRequest {} + +export interface PingResponse { status: boolean } -export interface VersionArgs {} -export interface VersionReturn { - version: Version -} -export interface RuntimeStatusArgs {} +export interface RuntimeStatusRequest {} -export interface RuntimeStatusReturn { +export interface RuntimeStatusResponse { status: GatewayRuntimeStatus } +export interface VersionRequest {} + +export interface VersionResponse { + version: Version +} + // // Client // -export class IndexerGateway implements IndexerGateway { + +export class IndexerGateway implements IndexerGatewayClient { protected hostname: string protected fetch: Fetch protected path = '/rpc/IndexerGateway/' @@ -723,183 +889,266 @@ export class IndexerGateway implements IndexerGateway { return this.hostname + this.path + name } - getNativeTokenBalance = ( - args: GetNativeTokenBalanceArgs, + 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('GetNativeTokenBalance'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenBalancesByContract'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - balances: >_data.balances, - } + return JsonDecode(_data, 'GetTokenBalancesByContractResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTokenBalances = ( - args: GetTokenBalancesArgs, + getTokenBalancesDetails = ( + req: GetTokenBalancesDetailsRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalances'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenBalancesDetails'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - balances: >_data.balances, - } + return JsonDecode(_data, 'GetTokenBalancesDetailsResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getTokenBalancesSummary = ( - args: GetTokenBalancesSummaryArgs, + req: GetTokenBalancesSummaryRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesSummary'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenBalancesSummary'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - nativeBalances: >_data.nativeBalances, - balances: >_data.balances, - } + return JsonDecode(_data, 'GetTokenBalancesSummaryResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTokenBalancesDetails = ( - args: GetTokenBalancesDetailsArgs, + getTokenPrice = ( + req: GetTokenPriceRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesDetails'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenPrice'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - nativeBalances: >_data.nativeBalances, - balances: >_data.balances, - } + return JsonDecode(_data, 'GetTokenPriceResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTokenBalancesByContract = ( - args: GetTokenBalancesByContractArgs, + getTokenPrices = ( + req: GetTokenPricesRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTokenBalancesByContract'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTokenPrices'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - balances: >_data.balances, - } + return JsonDecode(_data, 'GetTokenPricesResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getBalanceUpdates = ( - args: GetBalanceUpdatesArgs, + getTransactionHistory = ( + req: GetTransactionHistoryRequest, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetBalanceUpdates'), createHTTPRequest(args, headers, signal)).then( + ): Promise => { + return this.fetch(this.url('GetTransactionHistory'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - balances: >_data.balances, - } + return JsonDecode(_data, 'GetTransactionHistoryResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + 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( + 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, - } + return JsonDecode(_data, 'PingResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + 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( + runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('RuntimeStatus'), createHttpRequest('{}', headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - version: _data.version, - } + return JsonDecode(_data, 'RuntimeStatusResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + 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( + version = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - } + return JsonDecode(_data, 'VersionResponse') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + 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 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 => { @@ -908,13 +1157,9 @@ const buildResponse = (res: Response): Promise => { 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}`, + cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, }) } if (!res.ok) { @@ -925,518 +1170,494 @@ const buildResponse = (res: Response): Promise => { }) } +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 { - 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 + + 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(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause) + return new this({ message: payload.message, code: payload.code, status: payload.status, cause: 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) + 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( - name: string = 'WebrpcRequestFailed', - code: number = -1, - message: string = 'request failed', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcBadRoute', - code: number = -2, - message: string = 'bad route', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcBadMethod', - code: number = -3, - message: string = 'bad method', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcBadRequest', - code: number = -4, - message: string = 'bad request', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcBadResponse', - code: number = -5, - message: string = 'bad response', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcServerPanic', - code: number = -6, - message: string = 'server panic', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcInternalError', - code: number = -7, - message: string = 'internal error', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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 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 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( - name: string = 'WebrpcStreamLost', - code: number = -9, - message: string = 'stream lost', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - name: string = 'WebrpcStreamFinished', - code: number = -10, - message: string = 'stream finished', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + 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( - 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 ProjectNotFoundError extends WebrpcError { - constructor( - name: string = 'ProjectNotFound', - code: number = 1100, - message: string = 'Project not found', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ProjectNotFoundError.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 AccessKeyNotFoundError extends WebrpcError { - constructor( - name: string = 'AccessKeyNotFound', - code: number = 1101, - message: string = 'Access key not found', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AccessKeyNotFoundError.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 AccessKeyMismatchError extends WebrpcError { - constructor( - name: string = 'AccessKeyMismatch', - code: number = 1102, - message: string = 'Access key mismatch', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AccessKeyMismatchError.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 InvalidOriginError extends WebrpcError { - constructor( - name: string = 'InvalidOrigin', - code: number = 1103, - message: string = 'Invalid origin for Access Key', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidOriginError.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 InvalidServiceError extends WebrpcError { - constructor( - name: string = 'InvalidService', - code: number = 1104, - message: string = 'Service not enabled for Access key', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidServiceError.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 UnauthorizedUserError extends WebrpcError { - constructor( - name: string = 'UnauthorizedUser', - code: number = 1105, - message: string = 'Unauthorized user', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnauthorizedUserError.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 QuotaExceededError extends WebrpcError { - constructor( - name: string = 'QuotaExceeded', - code: number = 1200, - message: string = 'Quota exceeded', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, QuotaExceededError.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 RateLimitError extends WebrpcError { - constructor( - name: string = 'RateLimit', - code: number = 1201, - message: string = 'Rate limit exceeded', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RateLimitError.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 NoDefaultKeyError extends WebrpcError { - constructor( - name: string = 'NoDefaultKey', - code: number = 1300, - message: string = 'No default access key found', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NoDefaultKeyError.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 MaxAccessKeysError extends WebrpcError { - constructor( - name: string = 'MaxAccessKeys', - code: number = 1301, - message: string = 'Access keys limit reached', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MaxAccessKeysError.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 AtLeastOneKeyError extends WebrpcError { - constructor( - name: string = 'AtLeastOneKey', - code: number = 1302, - message: string = 'You need at least one Access Key', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AtLeastOneKeyError.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 TimeoutError extends WebrpcError { - constructor( - name: string = 'Timeout', - code: number = 1900, - message: string = 'Request timed out', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, TimeoutError.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 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 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 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 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 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 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 ResourceExhaustedError extends WebrpcError { - constructor( - name: string = 'ResourceExhausted', - code: number = 2004, - message: string = 'Resource exhausted', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ResourceExhaustedError.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 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 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 MetadataCallFailedError extends WebrpcError { - constructor( - name: string = 'MetadataCallFailed', - code: number = 3003, - message: string = 'Metadata service call failed', - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MetadataCallFailedError.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) } } @@ -1449,38 +1670,78 @@ export enum errors { WebrpcBadResponse = 'WebrpcBadResponse', WebrpcServerPanic = 'WebrpcServerPanic', WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientDisconnected = 'WebrpcClientDisconnected', + 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', + AccessKeyNotFound = 'AccessKeyNotFound', + AtLeastOneKey = 'AtLeastOneKey', + Geoblocked = 'Geoblocked', + InvalidArgument = 'InvalidArgument', InvalidOrigin = 'InvalidOrigin', InvalidService = 'InvalidService', - UnauthorizedUser = 'UnauthorizedUser', + MaxAccessKeys = 'MaxAccessKeys', + MetadataCallFailed = 'MetadataCallFailed', + MethodNotFound = 'MethodNotFound', + NoDefaultKey = 'NoDefaultKey', + NotFound = 'NotFound', + PermissionDenied = 'PermissionDenied', + ProjectNotFound = 'ProjectNotFound', + QueryFailed = 'QueryFailed', QuotaExceeded = 'QuotaExceeded', RateLimit = 'RateLimit', - NoDefaultKey = 'NoDefaultKey', - MaxAccessKeys = 'MaxAccessKeys', - AtLeastOneKey = 'AtLeastOneKey', + RateLimited = 'RateLimited', + RequestConflict = 'RequestConflict', + ResourceExhausted = 'ResourceExhausted', + SessionExpired = 'SessionExpired', Timeout = 'Timeout', - InvalidArgument = 'InvalidArgument', + Unauthorized = 'Unauthorized', + UnauthorizedUser = 'UnauthorizedUser', Unavailable = 'Unavailable', - QueryFailed = 'QueryFailed', - ResourceExhausted = 'ResourceExhausted', - NotFound = 'NotFound', - MetadataCallFailed = 'MetadataCallFailed', } -const webrpcErrorByCode: { [code: number]: any } = { +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, @@ -1489,35 +1750,89 @@ const webrpcErrorByCode: { [code: number]: any } = { [-5]: WebrpcBadResponseError, [-6]: WebrpcServerPanicError, [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientDisconnectedError, + [-8]: WebrpcClientAbortedError, [-9]: WebrpcStreamLostError, [-10]: WebrpcStreamFinishedError, - [1000]: UnauthorizedError, - [1001]: PermissionDeniedError, - [1002]: SessionExpiredError, - [1003]: MethodNotFoundError, - [1004]: RequestConflictError, [1005]: AbortedError, - [1006]: GeoblockedError, - [1007]: RateLimitedError, - [1100]: ProjectNotFoundError, - [1101]: AccessKeyNotFoundError, [1102]: AccessKeyMismatchError, + [1101]: AccessKeyNotFoundError, + [1302]: AtLeastOneKeyError, + [1006]: GeoblockedError, + [2001]: InvalidArgumentError, [1103]: InvalidOriginError, [1104]: InvalidServiceError, - [1105]: UnauthorizedUserError, + [1301]: MaxAccessKeysError, + [3003]: MetadataCallFailedError, + [1003]: MethodNotFoundError, + [1300]: NoDefaultKeyError, + [3000]: NotFoundError, + [1001]: PermissionDeniedError, + [1100]: ProjectNotFoundError, + [2003]: QueryFailedError, [1200]: QuotaExceededError, [1201]: RateLimitError, - [1300]: NoDefaultKeyError, - [1301]: MaxAccessKeysError, - [1302]: AtLeastOneKeyError, + [1007]: RateLimitedError, + [1004]: RequestConflictError, + [2004]: ResourceExhaustedError, + [1002]: SessionExpiredError, [1900]: TimeoutError, - [2001]: InvalidArgumentError, + [1000]: UnauthorizedError, + [1105]: UnauthorizedUserError, [2002]: UnavailableError, - [2003]: QueryFailedError, - [2004]: ResourceExhaustedError, - [3000]: NotFoundError, - [3003]: MetadataCallFailedError, } -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise +// +// 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 index fed9c77b4..8623be1c0 100644 --- a/packages/services/indexer/tsconfig.json +++ b/packages/services/indexer/tsconfig.json @@ -3,7 +3,9 @@ "compilerOptions": { "rootDir": "src", "outDir": "dist", - "types": ["node"] + "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 index 2b450ab82..e7c04d933 100644 --- a/packages/services/marketplace/CHANGELOG.md +++ b/packages/services/marketplace/CHANGELOG.md @@ -1,5 +1,186 @@ # @0xsequence/marketplace +## 3.0.12 + +### Patch Changes + +- WDK fix + +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface + +## 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 diff --git a/packages/services/marketplace/eslint.config.js b/packages/services/marketplace/eslint.config.js new file mode 100644 index 000000000..cecf89b03 --- /dev/null +++ b/packages/services/marketplace/eslint.config.js @@ -0,0 +1,4 @@ +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 index b454b44ae..117539eca 100644 --- a/packages/services/marketplace/package.json +++ b/packages/services/marketplace/package.json @@ -1,18 +1,20 @@ { "name": "@0xsequence/marketplace", - "version": "3.0.0-beta.5", + "version": "3.0.12", "description": "marketplace sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/marketplace", - "author": "Sequence Platforms Inc.", + "author": "Sequence Platforms ULC", "license": "Apache-2.0", "publishConfig": { "access": "public" }, + "type": "module", "scripts": { "build": "tsc", "dev": "tsc --watch", "test": "echo", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -21,8 +23,9 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "typescript": "^5.9.3" + "@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 index f7ec2b0d3..3b67f6f2d 100644 --- a/packages/services/marketplace/src/index.ts +++ b/packages/services/marketplace/src/index.ts @@ -1,6 +1,6 @@ -export * from './marketplace.gen' +export * from './marketplace.gen.js' -import { Marketplace as MarketplaceRpc } from './marketplace.gen' +import { Marketplace as MarketplaceRpc } from './marketplace.gen.js' export class MarketplaceIndexer extends MarketplaceRpc { constructor( @@ -15,7 +15,7 @@ export class MarketplaceIndexer extends MarketplaceRpc { _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: { [key: string]: any } = {} + const headers: Record = {} const jwtAuth = this.jwtAuth const projectAccessKey = this.projectAccessKey diff --git a/packages/services/metadata/CHANGELOG.md b/packages/services/metadata/CHANGELOG.md index f783d7ba3..e2b877a57 100644 --- a/packages/services/metadata/CHANGELOG.md +++ b/packages/services/metadata/CHANGELOG.md @@ -1,5 +1,186 @@ # @0xsequence/metadata +## 3.0.12 + +### Patch Changes + +- WDK fix + +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface + +## 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 diff --git a/packages/services/metadata/eslint.config.js b/packages/services/metadata/eslint.config.js new file mode 100644 index 000000000..cecf89b03 --- /dev/null +++ b/packages/services/metadata/eslint.config.js @@ -0,0 +1,4 @@ +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 index b31905d3a..d28f855fa 100644 --- a/packages/services/metadata/package.json +++ b/packages/services/metadata/package.json @@ -1,18 +1,20 @@ { "name": "@0xsequence/metadata", - "version": "3.0.0-beta.5", + "version": "3.0.12", "publishConfig": { "access": "public" }, "description": "metadata sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/metadata", - "author": "Sequence Platforms Inc.", + "author": "Sequence Platforms ULC", "license": "Apache-2.0", + "type": "module", "scripts": { "build": "tsc", "dev": "tsc --watch", "test": "echo", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -21,8 +23,9 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "typescript": "^5.9.3" + "@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 index 8c620b424..f9a1a600c 100644 --- a/packages/services/metadata/src/index.ts +++ b/packages/services/metadata/src/index.ts @@ -1,6 +1,6 @@ -export * from './metadata.gen' +export * from './metadata.gen.js' -import { Metadata as MetadataRpc, Collections as CollectionsRpc } from './metadata.gen' +import { Metadata as MetadataRpc, Collections as CollectionsRpc } from './metadata.gen.js' export class SequenceMetadata extends MetadataRpc { constructor( @@ -15,7 +15,7 @@ export class SequenceMetadata extends MetadataRpc { _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: { [key: string]: any } = {} + const headers: Record = {} const jwtAuth = this.jwtAuth const projectAccessKey = this.projectAccessKey @@ -47,7 +47,7 @@ export class SequenceCollections extends CollectionsRpc { _fetch = (input: RequestInfo, init?: RequestInit): Promise => { // automatically include jwt auth header to requests // if its been set on the client - const headers: { [key: string]: any } = {} + const headers: Record = {} const jwtAuth = this.jwtAuth diff --git a/packages/services/relayer/CHANGELOG.md b/packages/services/relayer/CHANGELOG.md index c4e639bfd..321204d54 100644 --- a/packages/services/relayer/CHANGELOG.md +++ b/packages/services/relayer/CHANGELOG.md @@ -1,5 +1,277 @@ # @0xsequence/relayer +## 3.1.1 + +### Patch Changes + +- WDK fix +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.12 + +## 3.1.0 + +### Minor Changes + +- 55121af: Surface explicit sponsorship signal on `feeOptions` and an error marker on + `feeOptions` / `feeTokens`. + - `RpcRelayer.feeOptions` now returns `sponsored: boolean`, forwarded from the + server's `FeeOptionsReturn.sponsored`. The `Relayer` interface and all + bundled implementations (`RpcRelayer`, `SequenceRelayer`, `LocalRelayer`, + `EIP6963Relayer`, `PkRelayer`) carry the new field. + - When `feeOptions` swallows a transport / server error it now returns + `{ options: [], sponsored: false, failed: true }` (was `{ options: [] }`). + - When `feeTokens` swallows an error it now returns + `{ isFeeRequired: false, failed: true }` (was `{ isFeeRequired: false }`). + + These changes are additive — existing consumers that ignore the new fields are + unaffected. Consumers that classified sponsorship by "no fee option attached" + should migrate to `sponsored === true` to distinguish a real subsidy from a + swallowed `/FeeOptions` error. + +### Patch Changes + +- Fix for relayer sponsored fees +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.11 + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.10 + +## 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 diff --git a/packages/services/relayer/eslint.config.js b/packages/services/relayer/eslint.config.js new file mode 100644 index 000000000..cecf89b03 --- /dev/null +++ b/packages/services/relayer/eslint.config.js @@ -0,0 +1,4 @@ +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 index 9778d1e8e..e1be3b5da 100644 --- a/packages/services/relayer/package.json +++ b/packages/services/relayer/package.json @@ -1,13 +1,13 @@ { "name": "@0xsequence/relayer", - "version": "3.0.0-beta.5", + "version": "3.1.1", "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 Inc.", + "author": "Sequence Platforms ULC", "license": "Apache-2.0", "scripts": { "build": "tsc", @@ -17,7 +17,8 @@ "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" + "typecheck": "tsc --noEmit", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -26,10 +27,11 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "typescript": "^5.9.3", - "vitest": "^4.0.14" + "@types/node": "^25.3.0", + "typescript": "^6.0.3", + "vitest": "^4.0.18" }, "dependencies": { "@0xsequence/wallet-primitives": "workspace:^", diff --git a/packages/services/relayer/src/preconditions/codec.ts b/packages/services/relayer/src/preconditions/codec.ts index 74f83154b..b59fae6d6 100644 --- a/packages/services/relayer/src/preconditions/codec.ts +++ b/packages/services/relayer/src/preconditions/codec.ts @@ -36,6 +36,11 @@ export function decodePrecondition(p: TransactionPrecondition): Precondition | u 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 { @@ -118,73 +123,92 @@ export function decodePrecondition(p: TransactionPrecondition): Precondition | u } export function encodePrecondition(p: Precondition): string { - const data: any = {} - switch (p.type()) { case 'native-balance': { const native = p as NativeBalancePrecondition - data.address = native.address.toString() - if (native.min !== undefined) data.min = native.min.toString() - if (native.max !== undefined) data.max = native.max.toString() - break + 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 - data.address = erc20.address.toString() - data.token = erc20.token.toString() - if (erc20.min !== undefined) data.min = erc20.min.toString() - if (erc20.max !== undefined) data.max = erc20.max.toString() - break + 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 - data.address = erc20.address.toString() - data.token = erc20.token.toString() - data.operator = erc20.operator.toString() - data.min = erc20.min.toString() - break + 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 - data.address = erc721.address.toString() - data.token = erc721.token.toString() - data.tokenId = erc721.tokenId.toString() - if (erc721.owned !== undefined) data.owned = erc721.owned - break + 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 - data.address = erc721.address.toString() - data.token = erc721.token.toString() - data.tokenId = erc721.tokenId.toString() - data.operator = erc721.operator.toString() - break + 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 - data.address = erc1155.address.toString() - data.token = erc1155.token.toString() - data.tokenId = erc1155.tokenId.toString() - if (erc1155.min !== undefined) data.min = erc1155.min.toString() - if (erc1155.max !== undefined) data.max = erc1155.max.toString() - break + 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 - data.address = erc1155.address.toString() - data.token = erc1155.token.toString() - data.tokenId = erc1155.tokenId.toString() - data.operator = erc1155.operator.toString() - data.min = erc1155.min.toString() - break + 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(data) + return JSON.stringify({}) } diff --git a/packages/services/relayer/src/relayer/relayer.ts b/packages/services/relayer/src/relayer/relayer.ts index 3ed5a6962..d0c558082 100644 --- a/packages/services/relayer/src/relayer/relayer.ts +++ b/packages/services/relayer/src/relayer/relayer.ts @@ -11,13 +11,20 @@ export interface Relayer { isAvailable(wallet: Address.Address, chainId: number): Promise - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> + feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: FeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> feeOptions( wallet: Address.Address, chainId: number, + to: Address.Address, calls: Payload.Call[], - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> + data?: Hex.Hex, + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> relay(to: Address.Address, data: Hex.Hex, chainId: number, quote?: FeeQuote): Promise<{ opHash: Hex.Hex }> @@ -26,8 +33,10 @@ export interface Relayer { checkPrecondition(precondition: Precondition.Precondition): Promise } -export function isRelayer(relayer: any): relayer is Relayer { +export function isRelayer(relayer: unknown): relayer is Relayer { return ( + typeof relayer === 'object' && + relayer !== null && 'isAvailable' in relayer && 'feeOptions' in relayer && 'relay' in relayer && diff --git a/packages/services/relayer/src/relayer/rpc-relayer/index.ts b/packages/services/relayer/src/relayer/rpc-relayer/index.ts index 04db6aa40..05ba8be76 100644 --- a/packages/services/relayer/src/relayer/rpc-relayer/index.ts +++ b/packages/services/relayer/src/relayer/rpc-relayer/index.ts @@ -7,10 +7,19 @@ import { TransactionPrecondition, ETHTxnStatus, } from './relayer.gen.js' -import { Address, Hex, Bytes, AbiFunction } from 'ox' +import { Address, Hex, AbiFunction } from 'ox' import { Constants, Payload, Network } from '@0xsequence/wallet-primitives' import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' -import { decodePrecondition } from '../../preconditions/index.js' +import { + decodePrecondition, + Erc1155ApprovalPrecondition, + Erc1155BalancePrecondition, + Erc20ApprovalPrecondition, + Erc20BalancePrecondition, + Erc721ApprovalPrecondition, + Erc721OwnershipPrecondition, + NativeBalancePrecondition, +} from '../../preconditions/index.js' import { erc20BalanceOf, erc20Allowance, @@ -49,12 +58,14 @@ const networkToChain = (network: Network.Network): Chain => { }, } : undefined, - contracts: network.ensAddress - ? { - ensUniversalResolver: { - address: network.ensAddress as `0x${string}`, + contracts: network.contracts + ? Object.entries(network.contracts).reduce( + (acc, [name, address]) => { + acc[name] = { address } + return acc }, - } + {} as Record, + ) : undefined, } as Chain } @@ -67,7 +78,9 @@ export const getChain = (chainId: number): Chain => { } // Fall back to viem's built-in chains - const viemChain = Object.values(chains).find((c: any) => typeof c === 'object' && 'id' in c && c.id === chainId) + 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 } @@ -76,7 +89,7 @@ export const getChain = (chainId: number): Chain => { } export class RpcRelayer implements Relayer { - public readonly kind: 'relayer' = 'relayer' + public readonly kind = 'relayer' public readonly type = 'rpc' public readonly id: string public readonly chainId: number @@ -110,7 +123,12 @@ export class RpcRelayer implements Relayer { return Promise.resolve(this.chainId === chainId) } - async feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: RpcFeeToken[]; paymentAddress?: Address.Address }> { + async feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: RpcFeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> { try { const { isFeeRequired, tokens, paymentAddress } = await this.client.feeTokens() if (isFeeRequired) { @@ -127,24 +145,33 @@ export class RpcRelayer implements Relayer { } } catch (e) { console.warn('RpcRelayer.feeTokens failed:', e) - return { isFeeRequired: false } + return { isFeeRequired: false, failed: true } } } async feeOptions( wallet: Address.Address, chainId: number, + to: Address.Address, calls: Payload.Call[], - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { - const callsStruct: Payload.Calls = { type: 'call', space: 0n, nonce: 0n, calls: calls } - const data = Payload.encode(callsStruct) + data?: Hex.Hex, + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> { + // 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: wallet, - to: wallet, - data: Bytes.toHex(data), + wallet, + to: feeOptionsTo, + data: feeOptionsData, }, { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) }, ) @@ -160,10 +187,10 @@ export class RpcRelayer implements Relayer { gasLimit: option.gasLimit, })) - return { options, quote } + return { options, quote, sponsored: result.sponsored } } catch (e) { console.warn('RpcRelayer.feeOptions failed:', e) - return { options: [] } + return { options: [], sponsored: false, failed: true } } } @@ -230,7 +257,7 @@ export class RpcRelayer implements Relayer { return { opHash: `0x${result.txnHash}` } } - async status(opHash: Hex.Hex, chainId: number): Promise { + 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 }) @@ -282,9 +309,9 @@ export class RpcRelayer implements Relayer { switch (decoded.type()) { case 'native-balance': { - const native = decoded as any + const native = decoded as NativeBalancePrecondition try { - const balance = await this.provider.getBalance({ address: native.address.toString() as `0x${string}` }) + 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 @@ -307,9 +334,9 @@ export class RpcRelayer implements Relayer { } case 'erc20-balance': { - const erc20 = decoded as any + const erc20 = decoded as Erc20BalancePrecondition try { - const data = AbiFunction.encodeData(erc20BalanceOf, [erc20.address.toString()]) + 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}`, @@ -336,9 +363,9 @@ export class RpcRelayer implements Relayer { } case 'erc20-approval': { - const erc20 = decoded as any + const erc20 = decoded as Erc20ApprovalPrecondition try { - const data = AbiFunction.encodeData(erc20Allowance, [erc20.address.toString(), erc20.operator.toString()]) + 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}`, @@ -353,12 +380,12 @@ export class RpcRelayer implements Relayer { } case 'erc721-ownership': { - const erc721 = decoded as any + const erc721 = decoded as Erc721OwnershipPrecondition try { const data = AbiFunction.encodeData(erc721OwnerOf, [erc721.tokenId]) const result = await this.provider.call({ - to: erc721.token.toString() as `0x${string}`, - data: data as `0x${string}`, + to: erc721.token, + data: data, }) const resultHex = result.toString() as `0x${string}` const owner = resultHex.slice(-40) @@ -372,7 +399,7 @@ export class RpcRelayer implements Relayer { } case 'erc721-approval': { - const erc721 = decoded as any + const erc721 = decoded as Erc721ApprovalPrecondition try { const data = AbiFunction.encodeData(erc721GetApproved, [erc721.tokenId]) const result = await this.provider.call({ @@ -389,9 +416,9 @@ export class RpcRelayer implements Relayer { } case 'erc1155-balance': { - const erc1155 = decoded as any + const erc1155 = decoded as Erc1155BalancePrecondition try { - const data = AbiFunction.encodeData(erc1155BalanceOf, [erc1155.address.toString(), erc1155.tokenId]) + 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}`, @@ -418,15 +445,12 @@ export class RpcRelayer implements Relayer { } case 'erc1155-approval': { - const erc1155 = decoded as any + const erc1155 = decoded as Erc1155ApprovalPrecondition try { - const data = AbiFunction.encodeData(erc1155IsApprovedForAll, [ - erc1155.address.toString(), - erc1155.operator.toString(), - ]) + const data = AbiFunction.encodeData(erc1155IsApprovedForAll, [erc1155.address, erc1155.operator]) const result = await this.provider.call({ - to: erc1155.token.toString() as `0x${string}`, - data: data as `0x${string}`, + to: erc1155.token, + data: data, }) return BigInt(result.toString()) === 1n } catch (error) { diff --git a/packages/services/relayer/src/relayer/rpc-relayer/relayer.gen.ts b/packages/services/relayer/src/relayer/rpc-relayer/relayer.gen.ts index ca5dbe9c8..dd3630069 100644 --- a/packages/services/relayer/src/relayer/rpc-relayer/relayer.gen.ts +++ b/packages/services/relayer/src/relayer/rpc-relayer/relayer.gen.ts @@ -1,7 +1,7 @@ /* eslint-disable */ -// sequence-relayer v0.4.1 7f8a4b83b00e0b6849c76c2ff0e23931e26b3d9f +// sequence-relayer v0.4.1 43ed1a26ed167b22a0b0827a8725a0df6fbe6ab4 // -- -// Code generated by Webrpc-gen@v0.30.2 with typescript generator. DO NOT EDIT. +// 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 @@ -12,7 +12,7 @@ export const WebrpcVersion = 'v1' export const WebrpcSchemaVersion = 'v0.4.1' // Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = '7f8a4b83b00e0b6849c76c2ff0e23931e26b3d9f' +export const WebrpcSchemaHash = '43ed1a26ed167b22a0b0827a8725a0df6fbe6ab4' // // Client interface @@ -72,6 +72,23 @@ export interface RelayerClient { 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 */ @@ -81,6 +98,17 @@ export interface RelayerClient { 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, @@ -93,21 +121,6 @@ export interface RelayerClient { signal?: AbortSignal, ): Promise - /** - * Sent transactions from an account. If filter is omitted then it will return all transactions. - */ - sentTransactions(req: SentTransactionsArgs, headers?: object, signal?: AbortSignal): Promise - - /** - * Pending transactions waiting to be mined for an account. This endpoint is just a sugar of `SentTransactions` - * with the filter set to pending: true. - */ - pendingTransactions( - req: PendingTransactionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** * Legacy Gas Tank */ @@ -145,7 +158,7 @@ export interface RelayerClient { ): Promise /** - * Gas Sponsorship + * Project-Level Gas Sponsorship */ listGasSponsors(req: ListGasSponsorsArgs, headers?: object, signal?: AbortSignal): Promise @@ -157,6 +170,39 @@ export interface RelayerClient { 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 */ @@ -186,6 +232,12 @@ export interface RelayerClient { // Schema types // +export enum RepairOperation { + SKIP = 'SKIP', + REQUEUE = 'REQUEUE', + DROP = 'DROP', +} + export enum ETHTxnStatus { UNKNOWN = 'UNKNOWN', DROPPED = 'DROPPED', @@ -195,6 +247,7 @@ export enum ETHTxnStatus { PARTIALLY_FAILED = 'PARTIALLY_FAILED', FAILED = 'FAILED', PENDING_PRECONDITION = 'PENDING_PRECONDITION', + MINED = 'MINED', } export enum TransferType { @@ -221,7 +274,7 @@ export enum FeeTokenType { ERC1155_TOKEN = 'ERC1155_TOKEN', } -export enum SortOrder { +export enum Order { DESC = 'DESC', ASC = 'ASC', } @@ -250,7 +303,31 @@ 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 {} @@ -287,6 +364,7 @@ export interface GasSponsor { id: number gasTankId: number projectId: number + ecosystemId: number chainId: number address: string name: string @@ -312,6 +390,14 @@ export interface MetaTxn { walletAddress: string contract: string input: string + authorization?: EIP7702Authorization +} + +export interface EIP7702Authorization { + chainId: number + implementation: string + nonce: number + signature: string } export interface MetaTxnLog { @@ -329,6 +415,7 @@ export interface MetaTxnLog { minedAt: string target: string input: string + bridgeGas?: string txnArgs: { [key: string]: any } txnReceipt?: { [key: string]: any } walletAddress: string @@ -402,11 +489,6 @@ export interface TxnLogTransfer { amounts: Array } -export interface SentTransactionsFilter { - pending?: boolean - failed?: boolean -} - export interface SimulateResult { executed: boolean succeeded: boolean @@ -443,19 +525,16 @@ export interface FeeToken { } export interface Page { - pageSize?: number - page?: number - more?: boolean - totalRecords?: number - column?: string - before?: any - after?: any - sort?: Array + pageSize: number + page: number + more: boolean + column: string + sort: Array } -export interface SortBy { +export interface Sort { column: string - order: SortOrder + order: Order } export interface PingArgs {} @@ -515,6 +594,7 @@ export interface GetMetaTxnReceiptArgs { export interface GetMetaTxnReceiptReturn { receipt: MetaTxnReceipt + metaTxnEnqueued: boolean } export interface SimulateArgs { @@ -558,6 +638,7 @@ export interface FeeOptionsArgs { to: string data: string simulate?: boolean + authorization?: EIP7702Authorization } export interface FeeOptionsReturn { @@ -566,6 +647,34 @@ export interface FeeOptionsReturn { 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 + authorization?: EIP7702Authorization +} + +export interface FeeOptionsWithBridgeGasReturn { + options: Array + sponsored: boolean + quote?: string +} + export interface GetMetaTxnNetworkFeeOptionsArgs { walletConfig: any payload: string @@ -575,9 +684,30 @@ 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 + includeNonSponsored?: boolean } export interface GetMetaTransactionsReturn { @@ -595,25 +725,6 @@ export interface GetTransactionCostReturn { cost: number } -export interface SentTransactionsArgs { - filter?: SentTransactionsFilter - page?: Page -} - -export interface SentTransactionsReturn { - page: Page - transactions: Array -} - -export interface PendingTransactionsArgs { - page?: Page -} - -export interface PendingTransactionsReturn { - page: Page - transactions: Array -} - export interface GetGasTankArgs { id: number } @@ -735,6 +846,58 @@ 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 @@ -795,12 +958,16 @@ export class Relayer implements RelayerClient { 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, - sentTransactions: (req: SentTransactionsArgs) => ['Relayer', 'sentTransactions', req] as const, - pendingTransactions: (req: PendingTransactionsArgs) => ['Relayer', 'pendingTransactions', 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, @@ -816,6 +983,14 @@ export class Relayer implements RelayerClient { 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, @@ -897,10 +1072,7 @@ export class Relayer implements RelayerClient { } sendMetaTxn = (req: SendMetaTxnArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SendMetaTxn'), - createHttpRequest(JsonEncode(req, 'SendMetaTxnArgs'), headers, signal), - ).then( + return this.fetch(this.url('SendMetaTxn'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'SendMetaTxnReturn') @@ -919,10 +1091,7 @@ export class Relayer implements RelayerClient { headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch( - this.url('GetMetaTxnNonce'), - createHttpRequest(JsonEncode(req, 'GetMetaTxnNonceArgs'), headers, signal), - ).then( + return this.fetch(this.url('GetMetaTxnNonce'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'GetMetaTxnNonceReturn') @@ -941,10 +1110,7 @@ export class Relayer implements RelayerClient { headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch( - this.url('GetMetaTxnReceipt'), - createHttpRequest(JsonEncode(req, 'GetMetaTxnReceiptArgs'), headers, signal), - ).then( + return this.fetch(this.url('GetMetaTxnReceipt'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'GetMetaTxnReceiptReturn') @@ -959,7 +1125,7 @@ export class Relayer implements RelayerClient { } simulate = (req: SimulateArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Simulate'), createHttpRequest(JsonEncode(req, 'SimulateArgs'), headers, signal)).then( + return this.fetch(this.url('Simulate'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'SimulateReturn') @@ -974,10 +1140,7 @@ export class Relayer implements RelayerClient { } simulateV3 = (req: SimulateV3Args, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SimulateV3'), - createHttpRequest(JsonEncode(req, 'SimulateV3Args'), headers, signal), - ).then( + return this.fetch(this.url('SimulateV3'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'SimulateV3Return') @@ -996,10 +1159,7 @@ export class Relayer implements RelayerClient { headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch( - this.url('UpdateMetaTxnGasLimits'), - createHttpRequest(JsonEncode(req, 'UpdateMetaTxnGasLimitsArgs'), headers, signal), - ).then( + return this.fetch(this.url('UpdateMetaTxnGasLimits'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'UpdateMetaTxnGasLimitsReturn') @@ -1029,10 +1189,7 @@ export class Relayer implements RelayerClient { } feeOptions = (req: FeeOptionsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('FeeOptions'), - createHttpRequest(JsonEncode(req, 'FeeOptionsArgs'), headers, signal), - ).then( + return this.fetch(this.url('FeeOptions'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'FeeOptionsReturn') @@ -1046,6 +1203,44 @@ export class Relayer implements RelayerClient { ) } + 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, @@ -1053,7 +1248,7 @@ export class Relayer implements RelayerClient { ): Promise => { return this.fetch( this.url('GetMetaTxnNetworkFeeOptions'), - createHttpRequest(JsonEncode(req, 'GetMetaTxnNetworkFeeOptionsArgs'), headers, signal), + createHttpRequest(JsonEncode(req), headers, signal), ).then( (res) => { return buildResponse(res).then((_data) => { @@ -1068,18 +1263,11 @@ export class Relayer implements RelayerClient { ) } - getMetaTransactions = ( - req: GetMetaTransactionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetMetaTransactions'), - createHttpRequest(JsonEncode(req, 'GetMetaTransactionsArgs'), headers, signal), - ).then( + 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, 'GetMetaTransactionsReturn') + return JsonDecode(_data, 'StartSenderReturn') }) }, (error) => { @@ -1090,18 +1278,26 @@ export class Relayer implements RelayerClient { ) } - getTransactionCost = ( - req: GetTransactionCostArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('GetTransactionCost'), - createHttpRequest(JsonEncode(req, 'GetTransactionCostArgs'), headers, signal), - ).then( + 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, 'GetTransactionCostReturn') + 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) => { @@ -1112,18 +1308,15 @@ export class Relayer implements RelayerClient { ) } - sentTransactions = ( - req: SentTransactionsArgs, + getMetaTransactions = ( + req: GetMetaTransactionsArgs, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('SentTransactions'), - createHttpRequest(JsonEncode(req, 'SentTransactionsArgs'), headers, signal), - ).then( + ): Promise => { + return this.fetch(this.url('GetMetaTransactions'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'SentTransactionsReturn') + return JsonDecode(_data, 'GetMetaTransactionsReturn') }) }, (error) => { @@ -1134,18 +1327,15 @@ export class Relayer implements RelayerClient { ) } - pendingTransactions = ( - req: PendingTransactionsArgs, + getTransactionCost = ( + req: GetTransactionCostArgs, headers?: object, signal?: AbortSignal, - ): Promise => { - return this.fetch( - this.url('PendingTransactions'), - createHttpRequest(JsonEncode(req, 'PendingTransactionsArgs'), headers, signal), - ).then( + ): Promise => { + return this.fetch(this.url('GetTransactionCost'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return JsonDecode(_data, 'PendingTransactionsReturn') + return JsonDecode(_data, 'GetTransactionCostReturn') }) }, (error) => { @@ -1157,10 +1347,7 @@ export class Relayer implements RelayerClient { } getGasTank = (req: GetGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetGasTank'), - createHttpRequest(JsonEncode(req, 'GetGasTankArgs'), headers, signal), - ).then( + return this.fetch(this.url('GetGasTank'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'GetGasTankReturn') @@ -1175,10 +1362,7 @@ export class Relayer implements RelayerClient { } addGasTank = (req: AddGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('AddGasTank'), - createHttpRequest(JsonEncode(req, 'AddGasTankArgs'), headers, signal), - ).then( + return this.fetch(this.url('AddGasTank'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'AddGasTankReturn') @@ -1193,10 +1377,7 @@ export class Relayer implements RelayerClient { } updateGasTank = (req: UpdateGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('UpdateGasTank'), - createHttpRequest(JsonEncode(req, 'UpdateGasTankArgs'), headers, signal), - ).then( + return this.fetch(this.url('UpdateGasTank'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'UpdateGasTankReturn') @@ -1217,7 +1398,7 @@ export class Relayer implements RelayerClient { ): Promise => { return this.fetch( this.url('NextGasTankBalanceAdjustmentNonce'), - createHttpRequest(JsonEncode(req, 'NextGasTankBalanceAdjustmentNonceArgs'), headers, signal), + createHttpRequest(JsonEncode(req), headers, signal), ).then( (res) => { return buildResponse(res).then((_data) => { @@ -1237,10 +1418,7 @@ export class Relayer implements RelayerClient { headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch( - this.url('AdjustGasTankBalance'), - createHttpRequest(JsonEncode(req, 'AdjustGasTankBalanceArgs'), headers, signal), - ).then( + return this.fetch(this.url('AdjustGasTankBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'AdjustGasTankBalanceReturn') @@ -1261,7 +1439,7 @@ export class Relayer implements RelayerClient { ): Promise => { return this.fetch( this.url('GetGasTankBalanceAdjustment'), - createHttpRequest(JsonEncode(req, 'GetGasTankBalanceAdjustmentArgs'), headers, signal), + createHttpRequest(JsonEncode(req), headers, signal), ).then( (res) => { return buildResponse(res).then((_data) => { @@ -1283,7 +1461,7 @@ export class Relayer implements RelayerClient { ): Promise => { return this.fetch( this.url('ListGasTankBalanceAdjustments'), - createHttpRequest(JsonEncode(req, 'ListGasTankBalanceAdjustmentsArgs'), headers, signal), + createHttpRequest(JsonEncode(req), headers, signal), ).then( (res) => { return buildResponse(res).then((_data) => { @@ -1303,10 +1481,7 @@ export class Relayer implements RelayerClient { headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch( - this.url('ListGasSponsors'), - createHttpRequest(JsonEncode(req, 'ListGasSponsorsArgs'), headers, signal), - ).then( + return this.fetch(this.url('ListGasSponsors'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'ListGasSponsorsReturn') @@ -1321,10 +1496,7 @@ export class Relayer implements RelayerClient { } getGasSponsor = (req: GetGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetGasSponsor'), - createHttpRequest(JsonEncode(req, 'GetGasSponsorArgs'), headers, signal), - ).then( + return this.fetch(this.url('GetGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'GetGasSponsorReturn') @@ -1339,10 +1511,7 @@ export class Relayer implements RelayerClient { } addGasSponsor = (req: AddGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('AddGasSponsor'), - createHttpRequest(JsonEncode(req, 'AddGasSponsorArgs'), headers, signal), - ).then( + return this.fetch(this.url('AddGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'AddGasSponsorReturn') @@ -1361,10 +1530,7 @@ export class Relayer implements RelayerClient { headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch( - this.url('UpdateGasSponsor'), - createHttpRequest(JsonEncode(req, 'UpdateGasSponsorArgs'), headers, signal), - ).then( + return this.fetch(this.url('UpdateGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'UpdateGasSponsorReturn') @@ -1383,10 +1549,7 @@ export class Relayer implements RelayerClient { headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch( - this.url('RemoveGasSponsor'), - createHttpRequest(JsonEncode(req, 'RemoveGasSponsorArgs'), headers, signal), - ).then( + return this.fetch(this.url('RemoveGasSponsor'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'RemoveGasSponsorReturn') @@ -1400,15 +1563,107 @@ export class Relayer implements RelayerClient { ) } + 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, 'AddressGasSponsorsArgs'), headers, signal), - ).then( + return this.fetch(this.url('AddressGasSponsors'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'AddressGasSponsorsReturn') @@ -1427,10 +1682,7 @@ export class Relayer implements RelayerClient { headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch( - this.url('GetProjectBalance'), - createHttpRequest(JsonEncode(req, 'GetProjectBalanceArgs'), headers, signal), - ).then( + return this.fetch(this.url('GetProjectBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'GetProjectBalanceReturn') @@ -1449,10 +1701,7 @@ export class Relayer implements RelayerClient { headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch( - this.url('AdjustProjectBalance'), - createHttpRequest(JsonEncode(req, 'AdjustProjectBalanceArgs'), headers, signal), - ).then( + return this.fetch(this.url('AdjustProjectBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return JsonDecode(_data, 'AdjustProjectBalanceReturn') @@ -1468,7 +1717,11 @@ export class Relayer implements RelayerClient { } const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { ...headers, 'Content-Type': 'application/json' } + const reqHeaders: { [key: string]: string } = { + ...headers, + 'Content-Type': 'application/json', + [WebrpcHeader]: WebrpcHeaderValue, + } return { method: 'POST', headers: reqHeaders, body, signal } } @@ -1499,44 +1752,11 @@ export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise(obj: T, typ: string = ''): string => { - return JSON.stringify(encodeType(typ, 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. @@ -1890,7 +2119,7 @@ export class AccessKeyMismatchError extends WebrpcError { 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 + this.status = typeof error.status === 'number' ? error.status : 403 if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) } @@ -1932,6 +2161,18 @@ export class UnauthorizedUserError extends WebrpcError { } } +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) @@ -2114,6 +2355,7 @@ export enum errors { InvalidOrigin = 'InvalidOrigin', InvalidService = 'InvalidService', UnauthorizedUser = 'UnauthorizedUser', + InvalidChain = 'InvalidChain', QuotaExceeded = 'QuotaExceeded', QuotaRateLimit = 'QuotaRateLimit', NoDefaultKey = 'NoDefaultKey', @@ -2155,6 +2397,7 @@ export enum WebrpcErrorCodes { InvalidOrigin = 1103, InvalidService = 1104, UnauthorizedUser = 1105, + InvalidChain = 1106, QuotaExceeded = 1200, QuotaRateLimit = 1201, NoDefaultKey = 1300, @@ -2196,6 +2439,7 @@ export const webrpcErrorByCode: { [code: number]: any } = { [1103]: InvalidOriginError, [1104]: InvalidServiceError, [1105]: UnauthorizedUserError, + [1106]: InvalidChainError, [1200]: QuotaExceededError, [1201]: QuotaRateLimitError, [1300]: NoDefaultKeyError, @@ -2217,7 +2461,7 @@ export const webrpcErrorByCode: { [code: number]: any } = { export const WebrpcHeader = 'Webrpc' -export const WebrpcHeaderValue = 'webrpc@v0.30.2;gen-typescript@v0.22.2;sequence-relayer@v0.4.1' +export const WebrpcHeaderValue = 'webrpc@v0.32.2;gen-typescript@v0.23.1;sequence-relayer@v0.4.1' type WebrpcGenVersions = { WebrpcGenVersion: string diff --git a/packages/services/relayer/src/relayer/standard/eip6963.ts b/packages/services/relayer/src/relayer/standard/eip6963.ts index 9d4861363..957b13c44 100644 --- a/packages/services/relayer/src/relayer/standard/eip6963.ts +++ b/packages/services/relayer/src/relayer/standard/eip6963.ts @@ -6,7 +6,7 @@ import { Payload } from '@0xsequence/wallet-primitives' import { FeeToken, TransactionPrecondition } from '../rpc-relayer/relayer.gen.js' export class EIP6963Relayer implements Relayer { - public readonly kind: 'relayer' = 'relayer' + public readonly kind = 'relayer' public readonly type = 'eip6963' public readonly id: string public readonly info: EIP6963ProviderInfo @@ -23,16 +23,22 @@ export class EIP6963Relayer implements Relayer { return this.relayer.isAvailable(wallet, chainId) } - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: FeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> { 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, calls) + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> { + return this.relayer.feeOptions(wallet, chainId, to, calls) } async relay(to: Address.Address, data: Hex.Hex, chainId: number, _?: FeeQuote): Promise<{ opHash: Hex.Hex }> { @@ -58,7 +64,7 @@ export function getEIP6963Store() { return store } -let relayers: Map = new Map() +const relayers: Map = new Map() export function getRelayers(): EIP6963Relayer[] { const store = getEIP6963Store() diff --git a/packages/services/relayer/src/relayer/standard/local.ts b/packages/services/relayer/src/relayer/standard/local.ts index 14d697aa2..e8066674c 100644 --- a/packages/services/relayer/src/relayer/standard/local.ts +++ b/packages/services/relayer/src/relayer/standard/local.ts @@ -1,9 +1,18 @@ -import { Constants, Payload } from '@0xsequence/wallet-primitives' +import { Payload } from '@0xsequence/wallet-primitives' import { EIP1193Provider } from 'mipd' -import { AbiFunction, Address, Bytes, Hex, TransactionReceipt } from 'ox' +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 } from '../../preconditions/index.js' +import { + decodePrecondition, + Erc1155ApprovalPrecondition, + Erc1155BalancePrecondition, + Erc20ApprovalPrecondition, + Erc20BalancePrecondition, + Erc721ApprovalPrecondition, + Erc721OwnershipPrecondition, + NativeBalancePrecondition, +} from '../../preconditions/index.js' import { erc20BalanceOf, erc20Allowance, @@ -23,7 +32,7 @@ export interface GenericProvider { } export class LocalRelayer implements Relayer { - public readonly kind: 'relayer' = 'relayer' + public readonly kind = 'relayer' public readonly type = 'local' public readonly id = 'local' @@ -34,7 +43,7 @@ export class LocalRelayer implements Relayer { } static createFromWindow(window: Window): LocalRelayer | undefined { - const eth = (window as any).ethereum + const eth = (window as { ethereum?: EIP1193Provider }).ethereum if (!eth) { console.warn('Window.ethereum not found, skipping local relayer') return undefined @@ -47,32 +56,24 @@ export class LocalRelayer implements Relayer { return new LocalRelayer(new EIP1193ProviderAdapter(provider)) } - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: FeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> { return Promise.resolve({ isFeeRequired: false, }) } feeOptions( - wallet: Address.Address, - chainId: number, - calls: Payload.Call[], - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { - return Promise.resolve({ options: [] }) - } - - private decodeCalls(data: Hex.Hex): Payload.Calls { - const executeSelector = AbiFunction.getSelector(Constants.EXECUTE) - - let packedPayload - if (data.startsWith(executeSelector)) { - const decode = AbiFunction.decodeData(Constants.EXECUTE, data) - packedPayload = decode[0] - } else { - packedPayload = data - } - - return Payload.decode(Bytes.fromHex(packedPayload)) + _wallet: Address.Address, + _chainId: number, + _to: Address.Address, + _calls: Payload.Call[], + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> { + return Promise.resolve({ options: [], sponsored: false }) } async relay( @@ -177,8 +178,8 @@ export class LocalRelayer implements Relayer { switch (decoded.type()) { case 'native-balance': { - const native = decoded as any - const balance = await this.provider.getBalance(native.address.toString()) + const native = decoded as NativeBalancePrecondition + const balance = await this.provider.getBalance(native.address) if (native.min !== undefined && balance < native.min) { return false } @@ -189,10 +190,10 @@ export class LocalRelayer implements Relayer { } case 'erc20-balance': { - const erc20 = decoded as any - const data = AbiFunction.encodeData(erc20BalanceOf, [erc20.address.toString()]) + const erc20 = decoded as Erc20BalancePrecondition + const data = AbiFunction.encodeData(erc20BalanceOf, [erc20.address]) const result = await this.provider.call({ - to: erc20.token.toString(), + to: erc20.token, data, }) const balance = BigInt(result) @@ -206,10 +207,10 @@ export class LocalRelayer implements Relayer { } case 'erc20-approval': { - const erc20 = decoded as any - const data = AbiFunction.encodeData(erc20Allowance, [erc20.address.toString(), erc20.operator.toString()]) + const erc20 = decoded as Erc20ApprovalPrecondition + const data = AbiFunction.encodeData(erc20Allowance, [erc20.address, erc20.operator]) const result = await this.provider.call({ - to: erc20.token.toString(), + to: erc20.token, data, }) const allowance = BigInt(result) @@ -217,10 +218,10 @@ export class LocalRelayer implements Relayer { } case 'erc721-ownership': { - const erc721 = decoded as any + const erc721 = decoded as Erc721OwnershipPrecondition const data = AbiFunction.encodeData(erc721OwnerOf, [erc721.tokenId]) const result = await this.provider.call({ - to: erc721.token.toString(), + to: erc721.token, data, }) const owner = '0x' + result.slice(26) @@ -229,10 +230,10 @@ export class LocalRelayer implements Relayer { } case 'erc721-approval': { - const erc721 = decoded as any + const erc721 = decoded as Erc721ApprovalPrecondition const data = AbiFunction.encodeData(erc721GetApproved, [erc721.tokenId]) const result = await this.provider.call({ - to: erc721.token.toString(), + to: erc721.token, data, }) const approved = '0x' + result.slice(26) @@ -240,10 +241,10 @@ export class LocalRelayer implements Relayer { } case 'erc1155-balance': { - const erc1155 = decoded as any - const data = AbiFunction.encodeData(erc1155BalanceOf, [erc1155.address.toString(), erc1155.tokenId]) + const erc1155 = decoded as Erc1155BalancePrecondition + const data = AbiFunction.encodeData(erc1155BalanceOf, [erc1155.address, erc1155.tokenId]) const result = await this.provider.call({ - to: erc1155.token.toString(), + to: erc1155.token, data, }) const balance = BigInt(result) @@ -257,13 +258,10 @@ export class LocalRelayer implements Relayer { } case 'erc1155-approval': { - const erc1155 = decoded as any - const data = AbiFunction.encodeData(erc1155IsApprovedForAll, [ - erc1155.address.toString(), - erc1155.operator.toString(), - ]) + const erc1155 = decoded as Erc1155ApprovalPrecondition + const data = AbiFunction.encodeData(erc1155IsApprovedForAll, [erc1155.address, erc1155.operator]) const result = await this.provider.call({ - to: erc1155.token.toString(), + to: erc1155.token, data, }) return BigInt(result) === 1n @@ -340,7 +338,7 @@ export class EIP1193ProviderAdapter implements GenericProvider { const rpcReceipt = await this.provider.request({ method: 'eth_getTransactionReceipt', params: [txHash] }) if (rpcReceipt) { - const receipt = TransactionReceipt.fromRpc(rpcReceipt as any) + const receipt = TransactionReceipt.fromRpc(rpcReceipt as Parameters[0]) if (receipt?.status === 'success') { return 'success' } else if (receipt?.status === 'reverted') { diff --git a/packages/services/relayer/src/relayer/standard/pk-relayer.ts b/packages/services/relayer/src/relayer/standard/pk-relayer.ts index 37b2e5a08..28ea0850a 100644 --- a/packages/services/relayer/src/relayer/standard/pk-relayer.ts +++ b/packages/services/relayer/src/relayer/standard/pk-relayer.ts @@ -5,7 +5,7 @@ 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' = 'relayer' + public readonly kind = 'relayer' public readonly type = 'pk' public readonly id = 'pk' private readonly relayer: LocalRelayer @@ -107,16 +107,22 @@ export class PkRelayer implements Relayer { return providerChainId === chainId } - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: FeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> { 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, calls) + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> { + return this.relayer.feeOptions(wallet, chainId, to, calls) } async relay(to: Address.Address, data: Hex.Hex, chainId: number, _?: FeeQuote): Promise<{ opHash: Hex.Hex }> { @@ -131,7 +137,7 @@ export class PkRelayer implements Relayer { return this.relayer.status(opHash, chainId) } - async checkPrecondition(precondition: Precondition.Precondition): Promise { + 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 index 5c0bd1663..8519c618a 100644 --- a/packages/services/relayer/src/relayer/standard/sequence.ts +++ b/packages/services/relayer/src/relayer/standard/sequence.ts @@ -3,7 +3,7 @@ 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' = 'relayer' + public readonly kind = 'relayer' public readonly type = 'sequence' readonly id = 'sequence' @@ -17,7 +17,12 @@ export class SequenceRelayer implements Relayer { return true } - async feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + async feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: FeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> { const { isFeeRequired, tokens, paymentAddress } = await this.service.feeTokens() if (isFeeRequired) { Address.assert(paymentAddress) @@ -36,23 +41,25 @@ export class SequenceRelayer implements Relayer { async feeOptions( wallet: Address.Address, _chainId: number, + to: Address.Address, calls: Payload.Call[], - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { - const to = wallet // TODO: this might be the guest module + transactionData?: Hex.Hex, + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> { 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 = AbiFunction.encodeData(execute, [Bytes.toHex(payload), signature]) + const data = transactionData ?? AbiFunction.encodeData(execute, [Bytes.toHex(payload), signature]) - const { options, quote } = await this.service.feeOptions({ wallet, to, data }) + const { options, quote, sponsored } = await this.service.feeOptions({ wallet, to, data }) return { options, quote: quote ? { _tag: 'FeeQuote', _quote: quote } : undefined, + sponsored, } } - async checkPrecondition(precondition: TransactionPrecondition): Promise { + async checkPrecondition(_precondition: TransactionPrecondition): Promise { // TODO: implement return false } diff --git a/packages/services/relayer/test/preconditions/codec.test.ts b/packages/services/relayer/test/preconditions/codec.test.ts index 88d442510..a5ba279d3 100644 --- a/packages/services/relayer/test/preconditions/codec.test.ts +++ b/packages/services/relayer/test/preconditions/codec.test.ts @@ -36,8 +36,8 @@ describe('Preconditions Codec', () => { describe('decodePrecondition', () => { it('should return undefined for null/undefined input', () => { - expect(decodePrecondition(null as any)).toBeUndefined() - expect(decodePrecondition(undefined as any)).toBeUndefined() + 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', () => { @@ -231,8 +231,8 @@ describe('Preconditions Codec', () => { it('should handle malformed addresses gracefully', () => { const intent: TransactionPrecondition = { type: 'native-balance', - ownerAddress: 'invalid-address' as any, - tokenAddress: NATIVE_TOKEN_ADDRESS, + ownerAddress: 'invalid-address', + tokenAddress: NATIVE_TOKEN_ADDRESS.toString(), chainId: ARBITRUM_CHAIN_ID, minAmount: BigInt('1000000000000000000'), } @@ -243,13 +243,13 @@ describe('Preconditions Codec', () => { }) it('should handle malformed BigInt values gracefully', () => { - const intent: TransactionPrecondition = { + const intent = { type: 'native-balance', - ownerAddress: TEST_ADDRESS, - tokenAddress: NATIVE_TOKEN_ADDRESS, + ownerAddress: TEST_ADDRESS.toString(), + tokenAddress: NATIVE_TOKEN_ADDRESS.toString(), chainId: ARBITRUM_CHAIN_ID, - minAmount: 'not-a-number' as any, - } + minAmount: 'not-a-number', + } as unknown as TransactionPrecondition const result = decodePrecondition(intent) expect(result).toBeUndefined() @@ -316,8 +316,8 @@ describe('Preconditions Codec', () => { }, { type: 'native-balance', - ownerAddress: 'invalid-address' as any, - tokenAddress: NATIVE_TOKEN_ADDRESS, + ownerAddress: 'invalid-address', + tokenAddress: NATIVE_TOKEN_ADDRESS.toString(), chainId: ARBITRUM_CHAIN_ID, minAmount: BigInt('1000000000000000000'), }, diff --git a/packages/services/relayer/test/preconditions/preconditions.test.ts b/packages/services/relayer/test/preconditions/preconditions.test.ts index e4975daaf..2eee47cba 100644 --- a/packages/services/relayer/test/preconditions/preconditions.test.ts +++ b/packages/services/relayer/test/preconditions/preconditions.test.ts @@ -1,4 +1,4 @@ -import { Address, Provider, RpcTransport, Secp256k1 } from 'ox' +import { Address, Hex, Secp256k1 } from 'ox' import { describe, expect, it, vi } from 'vitest' import { Erc1155ApprovalPrecondition, @@ -9,49 +9,47 @@ import { Erc721OwnershipPrecondition, NativeBalancePrecondition, } from '../../src/preconditions/types.js' -import { LocalRelayer } from '../../src/standard/local.js' -import { CAN_RUN_LIVE, RPC_URL } from '../../../../wallet/core/test/constants' +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: Provider.Provider; chainId: number }> => { - let provider: Provider.Provider - let chainId: number = Network.ChainId.MAINNET + const getProvider = async (): Promise<{ provider: GenericProvider; chainId: number }> => { + const chainId = Network.ChainId.MAINNET if (CAN_RUN_LIVE) { - provider = Provider.from(RpcTransport.fromHttp(RPC_URL!!)) - chainId = Number(await provider.request({ method: 'eth_chainId' })) - } else { - provider = { - request: vi.fn(), - on: vi.fn(), - removeListener: vi.fn(), - call: vi.fn(), - sendTransaction: vi.fn(), - getBalance: vi.fn(), - } as unknown as Provider.Provider + throw new Error('Live tests not configured: set up RPC and GenericProvider adapter') } - - return { provider: provider!, chainId } + const provider = createMockProvider() + return { provider, chainId } } const testWalletAddress = randomAddress() - const requireContractDeployed = async (provider: Provider.Provider, contract: Address.Address) => { - const code = await provider.request({ method: 'eth_getCode', params: [contract, 'latest'] }) - if (code === '0x') { - throw new Error(`Contract ${contract} not deployed`) + 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 as any) + const relayer = new LocalRelayer(provider) const precondition = new NativeBalancePrecondition( testWalletAddress, @@ -59,225 +57,182 @@ describe('Preconditions', () => { 2000000000000000000n, // 2 ETH max ) - const intentPrecondition = { + const transactionPrecondition = { type: precondition.type(), - chainId: chainId.toString(), - data: JSON.stringify({ - address: precondition.address.toString(), - min: precondition.min?.toString(), - max: precondition.max?.toString(), - }), + chainId, + ownerAddress: precondition.address.toString(), + tokenAddress: ZERO_ADDRESS, + minAmount: precondition.min ?? 0n, } - if (!CAN_RUN_LIVE) { - // Mock the balance check - ;(provider as any).request.mockResolvedValue('0x16345785d8a0000') // 1.5 ETH in hex - } + vi.mocked(provider.getBalance).mockResolvedValue(1500000000000000000n) // 1.5 ETH - const isValid = await relayer.checkPrecondition(intentPrecondition) + 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 as any) - await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT) + const relayer = new LocalRelayer(provider) + await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) const precondition = new Erc20BalancePrecondition( testWalletAddress, - ERC20_IMPLICIT_MINT_CONTRACT, + Address.from(ERC20_IMPLICIT_MINT_CONTRACT), 1000000n, // 1 token min 2000000n, // 2 tokens max ) - const intentPrecondition = { + const transactionPrecondition = { type: precondition.type(), - chainId: chainId.toString(), - data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), - min: precondition.min?.toString(), - max: precondition.max?.toString(), - }), + chainId, + ownerAddress: precondition.address.toString(), + tokenAddress: precondition.token.toString(), + minAmount: precondition.min ?? 0n, } - if (!CAN_RUN_LIVE) { - // Mock the balanceOf call - ;(provider as any).call.mockResolvedValue('0x1e8480') // 1.5 tokens in hex - } + vi.mocked(provider.call).mockResolvedValue('0x1e8480' as Hex.Hex) // 1.5 tokens in hex - const isValid = await relayer.checkPrecondition(intentPrecondition) + 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 as any) - await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT) + const relayer = new LocalRelayer(provider) + await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) const operator = randomAddress() const precondition = new Erc20ApprovalPrecondition( testWalletAddress, - ERC20_IMPLICIT_MINT_CONTRACT, + Address.from(ERC20_IMPLICIT_MINT_CONTRACT), operator, 1000000n, // 1 token min approval ) - const intentPrecondition = { + const transactionPrecondition = { type: precondition.type(), - chainId: chainId.toString(), - data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), - operator: precondition.operator.toString(), - min: precondition.min.toString(), - }), + chainId, + ownerAddress: precondition.address.toString(), + tokenAddress: precondition.token.toString(), + minAmount: precondition.min, } - if (!CAN_RUN_LIVE) { - // Mock the allowance call - ;(provider as any).call.mockResolvedValue('0x1e8480') // 1.5 tokens in hex - } + vi.mocked(provider.call).mockResolvedValue('0x1e8480' as Hex.Hex) // 1.5 tokens in hex - const isValid = await relayer.checkPrecondition(intentPrecondition) + 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 as any) - await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT) + const relayer = new LocalRelayer(provider) + await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) const precondition = new Erc721OwnershipPrecondition( testWalletAddress, - ERC20_IMPLICIT_MINT_CONTRACT, + Address.from(ERC20_IMPLICIT_MINT_CONTRACT), 1n, // tokenId true, // must own ) - const intentPrecondition = { + const transactionPrecondition = { type: precondition.type(), - chainId: chainId.toString(), - data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), - tokenId: precondition.tokenId.toString(), - owned: precondition.owned, - }), + chainId, + ownerAddress: precondition.address.toString(), + tokenAddress: precondition.token.toString(), + minAmount: 0n, } - if (!CAN_RUN_LIVE) { - // Mock the ownerOf call - ;(provider as any).call.mockResolvedValue( - '0x000000000000000000000000' + testWalletAddress.toString().slice(2).toLowerCase(), - ) - } + vi.mocked(provider.call).mockResolvedValue( + ('0x000000000000000000000000' + testWalletAddress.toString().slice(2).toLowerCase()) as Hex.Hex, + ) - const isValid = await relayer.checkPrecondition(intentPrecondition) + 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 as any) - await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT) + const relayer = new LocalRelayer(provider) + await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) const operator = randomAddress() const precondition = new Erc721ApprovalPrecondition( testWalletAddress, - ERC20_IMPLICIT_MINT_CONTRACT, + Address.from(ERC20_IMPLICIT_MINT_CONTRACT), 1n, // tokenId operator, ) - const intentPrecondition = { + const transactionPrecondition = { type: precondition.type(), - chainId: chainId.toString(), - data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), - tokenId: precondition.tokenId.toString(), - operator: precondition.operator.toString(), - }), + chainId, + ownerAddress: precondition.address.toString(), + tokenAddress: precondition.token.toString(), + minAmount: 0n, } - if (!CAN_RUN_LIVE) { - // Mock the getApproved call - ;(provider as any).call.mockResolvedValue( - '0x000000000000000000000000' + operator.toString().slice(2).toLowerCase(), - ) - } + // 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(intentPrecondition) + 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 as any) - await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT) + const relayer = new LocalRelayer(provider) + await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) const precondition = new Erc1155BalancePrecondition( testWalletAddress, - ERC20_IMPLICIT_MINT_CONTRACT, + Address.from(ERC20_IMPLICIT_MINT_CONTRACT), 1n, // tokenId 1000000n, // 1 token min 2000000n, // 2 tokens max ) - const intentPrecondition = { + const transactionPrecondition = { type: precondition.type(), - chainId: chainId.toString(), - data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), - tokenId: precondition.tokenId.toString(), - min: precondition.min?.toString(), - max: precondition.max?.toString(), - }), + chainId, + ownerAddress: precondition.address.toString(), + tokenAddress: precondition.token.toString(), + minAmount: precondition.min ?? 0n, } - if (!CAN_RUN_LIVE) { - // Mock the balanceOf call - ;(provider as any).call.mockResolvedValue('0x1e8480') // 1.5 tokens in hex - } + vi.mocked(provider.call).mockResolvedValue('0x1e8480' as Hex.Hex) // 1.5 tokens in hex - const isValid = await relayer.checkPrecondition(intentPrecondition) + 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 as any) - await requireContractDeployed(provider, ERC20_IMPLICIT_MINT_CONTRACT) + const relayer = new LocalRelayer(provider) + await requireContractDeployed(provider, Address.from(ERC20_IMPLICIT_MINT_CONTRACT)) const operator = randomAddress() const precondition = new Erc1155ApprovalPrecondition( testWalletAddress, - ERC20_IMPLICIT_MINT_CONTRACT, + Address.from(ERC20_IMPLICIT_MINT_CONTRACT), 1n, // tokenId operator, 1000000n, // 1 token min approval ) - const intentPrecondition = { + const transactionPrecondition = { type: precondition.type(), - chainId: chainId.toString(), - data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), - tokenId: precondition.tokenId.toString(), - operator: precondition.operator.toString(), - min: precondition.min.toString(), - }), + chainId, + ownerAddress: precondition.address.toString(), + tokenAddress: precondition.token.toString(), + minAmount: precondition.min, } - if (!CAN_RUN_LIVE) { - // Mock the isApprovedForAll call - ;(provider as any).call.mockResolvedValue('0x1') // true - } + vi.mocked(provider.call).mockResolvedValue('0x1' as Hex.Hex) // true - const isValid = await relayer.checkPrecondition(intentPrecondition) + 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 index 7fdc008ad..706642c23 100644 --- a/packages/services/relayer/test/preconditions/selectors.test.ts +++ b/packages/services/relayer/test/preconditions/selectors.test.ts @@ -15,104 +15,76 @@ import { } from '../../src/preconditions/types.js' import { Network } from '@0xsequence/wallet-primitives' -// Test addresses -const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TOKEN_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') +// 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: TransactionPrecondition = { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - chainID: '1', - min: '1000000000000000000', - }), - } - + 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: TransactionPrecondition = { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - chainID: '42161', // Arbitrum chainID - }), - } - + 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: TransactionPrecondition = { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - }), - } - - const chainId = extractChainID(precondition) - expect(chainId).toBeUndefined() - }) - - it('should return undefined when chainID is falsy', () => { - const precondition: TransactionPrecondition = { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - chainID: '', - min: '1000000000000000000', - }), - } - - const chainId = extractChainID(precondition) - expect(chainId).toBeUndefined() - }) - - it('should return undefined when chainID is null', () => { - const precondition: TransactionPrecondition = { + const precondition = { type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - chainID: null, - min: '1000000000000000000', - }), - } - + 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 any)).toBeUndefined() - expect(extractChainID(undefined as any)).toBeUndefined() - }) - - it('should return undefined for invalid JSON', () => { - const precondition: TransactionPrecondition = { - type: 'native-balance', - data: 'invalid json', - } - - const chainId = extractChainID(precondition) - expect(chainId).toBeUndefined() + 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: TransactionPrecondition = { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - chainID: '0', - }), - } - + const precondition = nativePrecondition({ chainId: 0 }) const chainId = extractChainID(precondition) expect(chainId).toBe(0) }) @@ -120,23 +92,7 @@ describe('Preconditions Selectors', () => { describe('extractSupportedPreconditions', () => { it('should extract valid preconditions', () => { - const intents: TransactionPrecondition[] = [ - { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - }), - }, - { - type: 'erc20-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - min: '1000000', - }), - }, - ] + const intents: TransactionPrecondition[] = [nativePrecondition(), erc20Precondition()] const results = extractSupportedPreconditions(intents) expect(results).toHaveLength(2) @@ -146,21 +102,15 @@ describe('Preconditions Selectors', () => { it('should filter out invalid preconditions', () => { const intents: TransactionPrecondition[] = [ - { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - }), - }, + nativePrecondition(), { type: 'unknown-type', - data: JSON.stringify({ address: TEST_ADDRESS }), - }, - { - type: 'native-balance', - data: 'invalid json', - }, + chainId: 1, + ownerAddress: TEST_ADDRESS, + tokenAddress: ZERO_ADDRESS, + minAmount: 0n, + } as TransactionPrecondition, + nativePrecondition({ ownerAddress: '' }), ] const results = extractSupportedPreconditions(intents) @@ -169,8 +119,8 @@ describe('Preconditions Selectors', () => { }) it('should return empty array for null/undefined input', () => { - expect(extractSupportedPreconditions(null as any)).toEqual([]) - expect(extractSupportedPreconditions(undefined as any)).toEqual([]) + expect(extractSupportedPreconditions(null as unknown as TransactionPrecondition[])).toEqual([]) + expect(extractSupportedPreconditions(undefined as unknown as TransactionPrecondition[])).toEqual([]) }) it('should return empty array for empty input', () => { @@ -180,25 +130,15 @@ describe('Preconditions Selectors', () => { it('should handle mixed valid and invalid preconditions', () => { const intents: TransactionPrecondition[] = [ - { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - }), - }, - { - type: 'erc721-ownership', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - tokenId: '123', - }), - }, + nativePrecondition(), + erc721OwnershipPrecondition(), { type: 'invalid-type', - data: JSON.stringify({ address: TEST_ADDRESS }), - }, + chainId: 1, + ownerAddress: TEST_ADDRESS, + tokenAddress: ZERO_ADDRESS, + minAmount: 0n, + } as TransactionPrecondition, ] const results = extractSupportedPreconditions(intents) @@ -211,67 +151,29 @@ describe('Preconditions Selectors', () => { describe('extractNativeBalancePreconditions', () => { it('should extract only native balance preconditions', () => { const intents: TransactionPrecondition[] = [ - { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - }), - }, - { - type: 'erc20-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - min: '1000000', - }), - }, - { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - max: '2000000000000000000', - }), - }, + 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) - - // Verify the specific properties expect(results[0].min).toBe(1000000000000000000n) - expect(results[1].max).toBe(2000000000000000000n) + expect(results[1].min).toBe(2000000000000000000n) }) it('should return empty array when no native balance preconditions exist', () => { - const intents: TransactionPrecondition[] = [ - { - type: 'erc20-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - min: '1000000', - }), - }, - { - type: 'erc721-ownership', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - tokenId: '123', - }), - }, - ] + 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 any)).toEqual([]) - expect(extractNativeBalancePreconditions(undefined as any)).toEqual([]) + expect(extractNativeBalancePreconditions(null as unknown as TransactionPrecondition[])).toEqual([]) + expect(extractNativeBalancePreconditions(undefined as unknown as TransactionPrecondition[])).toEqual([]) }) it('should return empty array for empty input', () => { @@ -281,24 +183,8 @@ describe('Preconditions Selectors', () => { it('should filter out invalid native balance preconditions', () => { const intents: TransactionPrecondition[] = [ - { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - }), - }, - { - type: 'native-balance', - data: 'invalid json', // This will be filtered out - }, - { - type: 'native-balance', - data: JSON.stringify({ - // Missing address - this will be filtered out - min: '1000000000000000000', - }), - }, + nativePrecondition({ minAmount: 1000000000000000000n }), + nativePrecondition({ ownerAddress: '' }), ] const results = extractNativeBalancePreconditions(intents) @@ -311,69 +197,31 @@ describe('Preconditions Selectors', () => { describe('extractERC20BalancePreconditions', () => { it('should extract only ERC20 balance preconditions', () => { const intents: TransactionPrecondition[] = [ - { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - }), - }, - { - type: 'erc20-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - min: '1000000', - }), - }, - { - type: 'erc20-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - max: '2000000', - }), - }, + 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) - - // Verify the specific properties expect(results[0].min).toBe(1000000n) - expect(results[1].max).toBe(2000000n) - expect(results[0].token).toBe(TOKEN_ADDRESS) - expect(results[1].token).toBe(TOKEN_ADDRESS) + 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[] = [ - { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - }), - }, - { - type: 'erc721-ownership', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - tokenId: '123', - }), - }, - ] + 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 any)).toEqual([]) - expect(extractERC20BalancePreconditions(undefined as any)).toEqual([]) + expect(extractERC20BalancePreconditions(null as unknown as TransactionPrecondition[])).toEqual([]) + expect(extractERC20BalancePreconditions(undefined as unknown as TransactionPrecondition[])).toEqual([]) }) it('should return empty array for empty input', () => { @@ -383,33 +231,15 @@ describe('Preconditions Selectors', () => { it('should filter out invalid ERC20 balance preconditions', () => { const intents: TransactionPrecondition[] = [ - { - type: 'erc20-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - min: '1000000', - }), - }, - { - type: 'erc20-balance', - data: 'invalid json', // This will be filtered out - }, - { - type: 'erc20-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - // Missing token address - this will be filtered out - min: '1000000', - }), - }, + 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).toBe(TOKEN_ADDRESS) + 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 index a4ecda1d9..a479676b5 100644 --- a/packages/services/relayer/test/preconditions/types.test.ts +++ b/packages/services/relayer/test/preconditions/types.test.ts @@ -179,7 +179,7 @@ describe('Preconditions Types', () => { TEST_ADDRESS, TOKEN_ADDRESS, OPERATOR_ADDRESS, - undefined as any, + undefined as unknown as bigint, ) const error = precondition.isValid() @@ -224,7 +224,7 @@ describe('Preconditions Types', () => { }) it('should validate tokenId is required', () => { - const precondition = new Erc721OwnershipPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, undefined as any) + const precondition = new Erc721OwnershipPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, undefined as unknown as bigint) const error = precondition.isValid() expect(error).toBeInstanceOf(Error) @@ -271,7 +271,7 @@ describe('Preconditions Types', () => { const precondition = new Erc721ApprovalPrecondition( TEST_ADDRESS, TOKEN_ADDRESS, - undefined as any, + undefined as unknown as bigint, OPERATOR_ADDRESS, ) @@ -319,7 +319,7 @@ describe('Preconditions Types', () => { }) it('should validate tokenId is required', () => { - const precondition = new Erc1155BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, undefined as any) + const precondition = new Erc1155BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, undefined as unknown as bigint) const error = precondition.isValid() expect(error).toBeInstanceOf(Error) @@ -402,7 +402,7 @@ describe('Preconditions Types', () => { const precondition = new Erc1155ApprovalPrecondition( TEST_ADDRESS, TOKEN_ADDRESS, - undefined as any, + undefined as unknown as bigint, OPERATOR_ADDRESS, 1000000n, ) @@ -432,7 +432,7 @@ describe('Preconditions Types', () => { TOKEN_ADDRESS, 123n, OPERATOR_ADDRESS, - undefined as any, + undefined as unknown as bigint, ) const error = precondition.isValid() diff --git a/packages/services/relayer/test/relayer/relayer.test.ts b/packages/services/relayer/test/relayer/relayer.test.ts index adbadd236..cd8c096a3 100644 --- a/packages/services/relayer/test/relayer/relayer.test.ts +++ b/packages/services/relayer/test/relayer/relayer.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' import { Address, Hex } from 'ox' import { Network, Payload } from '@0xsequence/wallet-primitives' -import { Relayer, RelayerGen } from '@0xsequence/relayer' +import { Relayer, RpcRelayerGen } from '@0xsequence/relayer' // Test addresses and data const TEST_WALLET_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') @@ -91,13 +91,11 @@ describe('Relayer', () => { }) it('should return false for non-objects', () => { - // These will throw due to the 'in' operator, so we need to test the actual behavior - expect(() => Relayer.isRelayer(null)).toThrow() - expect(() => Relayer.isRelayer(undefined)).toThrow() - expect(() => Relayer.isRelayer('string')).toThrow() - expect(() => Relayer.isRelayer(123)).toThrow() - expect(() => Relayer.isRelayer(true)).toThrow() - // Arrays and objects should not throw, but should return false + 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) }) @@ -127,7 +125,7 @@ describe('Relayer', () => { symbol: 'ETH', decimals: 18, logoURL: 'https://example.com/eth.png', - type: 'NATIVE' as RelayerGen.FeeTokenType, + type: 'NATIVE' as RpcRelayerGen.FeeTokenType, contractAddress: undefined, }, to: TEST_TO_ADDRESS, @@ -296,6 +294,7 @@ describe('Relayer', () => { vi.mocked(mockRelayer.feeOptions).mockResolvedValue({ options: [], quote: undefined, + sponsored: false, }) vi.mocked(mockRelayer.relay).mockResolvedValue({ opHash: TEST_OP_HASH, @@ -310,7 +309,7 @@ describe('Relayer', () => { 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, []) + 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) @@ -319,11 +318,120 @@ describe('Relayer', () => { const statusResult = await mockRelayer.status(TEST_OP_HASH, TEST_CHAIN_ID) expect(statusResult.status).toBe('confirmed') - const preconditionResult = await mockRelayer.checkPrecondition({} as any) + 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, + }) + }) + + it('should propagate sponsored:true from the server', async () => { + const fetchImpl = vi.fn( + async () => new Response(JSON.stringify({ options: [], sponsored: true }), { status: 200 }), + ) + const relayer = new Relayer.RpcRelayer('https://relayer.test', TEST_CHAIN_ID, 'https://rpc.test', fetchImpl) + + const result = await relayer.feeOptions(TEST_WALLET_ADDRESS, TEST_CHAIN_ID, TEST_TO_ADDRESS, [mockCall]) + + expect(result.sponsored).toBe(true) + expect(result.options).toEqual([]) + expect(result.failed).toBeUndefined() + }) + + it('should propagate sponsored:false from the server', async () => { + const fetchImpl = vi.fn( + async () => new Response(JSON.stringify({ options: [], sponsored: false }), { status: 200 }), + ) + const relayer = new Relayer.RpcRelayer('https://relayer.test', TEST_CHAIN_ID, 'https://rpc.test', fetchImpl) + + const result = await relayer.feeOptions(TEST_WALLET_ADDRESS, TEST_CHAIN_ID, TEST_TO_ADDRESS, [mockCall]) + + expect(result.sponsored).toBe(false) + expect(result.failed).toBeUndefined() + }) + + it('should return sponsored:false and failed:true when the server errors', async () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) + const fetchImpl = vi.fn( + async () => + new Response(JSON.stringify({ error: 'Aborted', code: 1005, msg: 'simulation failed' }), { status: 400 }), + ) + const relayer = new Relayer.RpcRelayer('https://relayer.test', TEST_CHAIN_ID, 'https://rpc.test', fetchImpl) + + const result = await relayer.feeOptions(TEST_WALLET_ADDRESS, TEST_CHAIN_ID, TEST_TO_ADDRESS, [mockCall]) + + expect(result).toEqual({ options: [], sponsored: false, failed: true }) + expect(warn).toHaveBeenCalled() + warn.mockRestore() + }) + }) + + describe('RpcRelayer.feeTokens', () => { + it('should return failed:true when the server errors', async () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) + const fetchImpl = vi.fn(async () => new Response('boom', { status: 500 })) + const relayer = new Relayer.RpcRelayer('https://relayer.test', TEST_CHAIN_ID, 'https://rpc.test', fetchImpl) + + const result = await relayer.feeTokens() + + expect(result).toEqual({ isFeeRequired: false, failed: true }) + expect(warn).toHaveBeenCalled() + warn.mockRestore() + }) + }) + describe('Type compatibility', () => { it('should work with Address and Hex types from ox', () => { // Test that the interfaces work correctly with ox types diff --git a/packages/services/userdata/CHANGELOG.md b/packages/services/userdata/CHANGELOG.md index 0079ca325..0d372c8e2 100644 --- a/packages/services/userdata/CHANGELOG.md +++ b/packages/services/userdata/CHANGELOG.md @@ -1,5 +1,182 @@ # @0xsequence/userdata +## 3.0.12 + +### Patch Changes + +- WDK fix + +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface + +## 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 diff --git a/packages/services/userdata/eslint.config.js b/packages/services/userdata/eslint.config.js new file mode 100644 index 000000000..cecf89b03 --- /dev/null +++ b/packages/services/userdata/eslint.config.js @@ -0,0 +1,4 @@ +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 index dedb4e953..bdbb2d056 100644 --- a/packages/services/userdata/package.json +++ b/packages/services/userdata/package.json @@ -1,18 +1,20 @@ { "name": "@0xsequence/userdata", - "version": "3.0.0-beta.5", + "version": "3.0.12", "description": "userdata sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/userdata", - "author": "Sequence Platforms Inc.", + "author": "Sequence Platforms ULC", "license": "Apache-2.0", "publishConfig": { "access": "public" }, + "type": "module", "scripts": { "build": "tsc", "dev": "tsc --watch", "test": "echo", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -21,8 +23,9 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "typescript": "^5.9.3" + "@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 index af76930fc..63b855967 100644 --- a/packages/services/userdata/src/index.ts +++ b/packages/services/userdata/src/index.ts @@ -1,6 +1,6 @@ -export * from './userdata.gen' +export * from './userdata.gen.js' -import { UserData as UserdataRpc } from './userdata.gen' +import { UserData as UserdataRpc } from './userdata.gen.js' export class SequenceUserdataClient extends UserdataRpc { constructor( @@ -15,7 +15,7 @@ export class SequenceUserdataClient extends UserdataRpc { _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: { [key: string]: any } = {} + const headers: Record = {} const jwtAuth = this.jwtAuth const projectAccessKey = this.projectAccessKey diff --git a/packages/services/userdata/src/userdata.gen.ts b/packages/services/userdata/src/userdata.gen.ts index a26fdb995..5a4882250 100644 --- a/packages/services/userdata/src/userdata.gen.ts +++ b/packages/services/userdata/src/userdata.gen.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -// userdata v0.1.0 99a19ff0218eda6f5e544642d0fd72f66736bdaf +// userdata v0.1.0 1c37d1866dada9f3770a8ca37e790c7bcdb6424a // -- // Code generated by Webrpc-gen@v0.30.2 with typescript generator. DO NOT EDIT. // @@ -12,7 +12,7 @@ export const WebrpcVersion = 'v1' export const WebrpcSchemaVersion = 'v0.1.0' // Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = '99a19ff0218eda6f5e544642d0fd72f66736bdaf' +export const WebrpcSchemaHash = '1c37d1866dada9f3770a8ca37e790c7bcdb6424a' // // Client interface @@ -28,26 +28,189 @@ export interface UserDataClient { 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 + ecosystem?: number + preferences: WalletPreferences + updatedAt: string + createdAt: string } -export interface Signer { - address: string - kind: string - email?: 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 { @@ -60,11 +223,114 @@ export interface Session { 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 { @@ -90,6 +356,264 @@ 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 // @@ -112,6 +636,36 @@ export class UserData implements UserDataClient { 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 => { @@ -172,6 +726,628 @@ export class UserData implements UserDataClient { }, ) } + + 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 => { diff --git a/packages/utils/abi/CHANGELOG.md b/packages/utils/abi/CHANGELOG.md index 447d70ed1..dcdd1a635 100644 --- a/packages/utils/abi/CHANGELOG.md +++ b/packages/utils/abi/CHANGELOG.md @@ -1,5 +1,186 @@ # @0xsequence/abi +## 3.0.12 + +### Patch Changes + +- WDK fix + +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface + +## 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 diff --git a/packages/utils/abi/eslint.config.js b/packages/utils/abi/eslint.config.js new file mode 100644 index 000000000..cecf89b03 --- /dev/null +++ b/packages/utils/abi/eslint.config.js @@ -0,0 +1,4 @@ +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 index ac07e0f0a..9e9e8fb33 100644 --- a/packages/utils/abi/package.json +++ b/packages/utils/abi/package.json @@ -1,18 +1,20 @@ { "name": "@0xsequence/abi", - "version": "3.0.0-beta.5", + "version": "3.0.12", "description": "abi sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/utils/abi", - "author": "Sequence Platforms Inc.", + "author": "Sequence Platforms ULC", "license": "Apache-2.0", "publishConfig": { "access": "public" }, + "type": "module", "scripts": { "build": "tsc", "dev": "tsc --watch", "test": "echo", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -21,8 +23,9 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "typescript": "^5.9.3" + "@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 index db6dfa292..39336c543 100644 --- a/packages/utils/abi/src/index.ts +++ b/packages/utils/abi/src/index.ts @@ -1,22 +1,22 @@ -export { abi as erc5719Abi } from './wallet/erc5719' -export { abi as erc1271Abi } from './wallet/erc1271' -export { abi as erc6492Abi } from './wallet/erc6492' -export { abi as factoryAbi } from './wallet/factory' -export { abi as mainModuleAbi } from './wallet/mainModule' -export { abi as mainModuleUpgradableAbi } from './wallet/mainModuleUpgradable' -export { abi as moduleHooksAbi } from './wallet/moduleHooks' -export { abi as sequenceUtilsAbi } from './wallet/sequenceUtils' -export { abi as requireFreshSignerAbi } from './wallet/libs/requireFreshSigners' -export { abi as walletProxyHookAbi } from './wallet/walletProxyHook' +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' +export { walletContracts } from './wallet/index.js' -export { ERC1155_ABI } from './tokens/erc1155' -export { ERC1155_ITEMS_ABI } from './tokens/erc1155Items' -export { ERC20_ABI } from './tokens/erc20' -export { ERC6909_ABI } from './tokens/erc6909' -export { ERC721_ABI } from './tokens/erc721' -export { ERC721_ITEMS_ABI } from './tokens/erc721Items' +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' -export { ERC721_SALE_ABI } from './sale/erc721Sale' +export { ERC1155_SALE_ABI } from './sale/erc1155Sale.js' +export { ERC721_SALE_ABI } from './sale/erc721Sale.js' diff --git a/packages/utils/abi/src/wallet/index.ts b/packages/utils/abi/src/wallet/index.ts index 0ceae0a35..50b567e71 100644 --- a/packages/utils/abi/src/wallet/index.ts +++ b/packages/utils/abi/src/wallet/index.ts @@ -1,13 +1,13 @@ -import * as erc5719 from './erc5719' -import * as erc1271 from './erc1271' -import * as erc6492 from './erc6492' -import * as factory from './factory' -import * as mainModule from './mainModule' -import * as mainModuleUpgradable from './mainModuleUpgradable' -import * as moduleHooks from './moduleHooks' -import * as sequenceUtils from './sequenceUtils' -import * as requireFreshSigner from './libs/requireFreshSigners' -import * as walletProxyHook from './walletProxyHook' +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" diff --git a/packages/wallet/core/CHANGELOG.md b/packages/wallet/core/CHANGELOG.md index 940b7e413..24e429424 100644 --- a/packages/wallet/core/CHANGELOG.md +++ b/packages/wallet/core/CHANGELOG.md @@ -1,5 +1,314 @@ # @0xsequence/wallet-core +## 3.0.12 + +### Patch Changes + +- WDK fix +- Updated dependencies + - @0xsequence/guard@3.0.12 + - @0xsequence/relayer@3.1.1 + - @0xsequence/wallet-primitives@3.0.12 + +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees +- Updated dependencies +- Updated dependencies [55121af] + - @0xsequence/guard@3.0.11 + - @0xsequence/relayer@3.1.0 + - @0xsequence/wallet-primitives@3.0.11 + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface +- Updated dependencies + - @0xsequence/guard@3.0.10 + - @0xsequence/relayer@3.0.10 + - @0xsequence/wallet-primitives@3.0.10 + +## 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 diff --git a/packages/wallet/core/eslint.config.js b/packages/wallet/core/eslint.config.js new file mode 100644 index 000000000..d10bbd1e9 --- /dev/null +++ b/packages/wallet/core/eslint.config.js @@ -0,0 +1,12 @@ +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 index 682ed0d06..96c286e65 100644 --- a/packages/wallet/core/package.json +++ b/packages/wallet/core/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-core", - "version": "3.0.0-beta.5", + "version": "3.0.12", "license": "Apache-2.0", "type": "module", "publishConfig": { @@ -13,7 +13,8 @@ "test": "vitest run", "test:coverage": "vitest run --coverage", "typecheck": "tsc --noEmit", - "clean": "rimraf dist" + "clean": "rimraf dist", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -22,13 +23,14 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "@vitest/coverage-v8": "^4.0.14", - "dotenv": "^17.2.3", + "@types/node": "^25.3.0", + "@vitest/coverage-v8": "^4.0.18", + "dotenv": "^17.3.1", "fake-indexeddb": "^6.2.5", - "typescript": "^5.9.3", - "vitest": "^4.0.14" + "typescript": "^6.0.3", + "vitest": "^4.0.18" }, "dependencies": { "@0xsequence/guard": "workspace:^", diff --git a/packages/wallet/core/src/bundler/bundlers/pimlico.ts b/packages/wallet/core/src/bundler/bundlers/pimlico.ts index e2d95ec33..4837babee 100644 --- a/packages/wallet/core/src/bundler/bundlers/pimlico.ts +++ b/packages/wallet/core/src/bundler/bundlers/pimlico.ts @@ -16,16 +16,22 @@ type PimlicoGasPrice = { } export class PimlicoBundler implements Bundler { - public readonly kind: 'bundler' = 'bundler' + public readonly kind = '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) { + constructor(bundlerRpcUrl: string, provider: Provider.Provider | string, fetcher?: typeof fetch) { 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 { @@ -113,7 +119,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 */ } @@ -165,7 +171,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 fetch(this.bundlerRpcUrl, { + const res = await this.fetcher(this.bundlerRpcUrl, { method: 'POST', headers: { 'content-type': 'application/json' }, body, diff --git a/packages/wallet/core/src/env.ts b/packages/wallet/core/src/env.ts new file mode 100644 index 000000000..0a463dd1d --- /dev/null +++ b/packages/wallet/core/src/env.ts @@ -0,0 +1,68 @@ +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/index.ts b/packages/wallet/core/src/index.ts index b36e917ca..27c54b8f5 100644 --- a/packages/wallet/core/src/index.ts +++ b/packages/wallet/core/src/index.ts @@ -5,6 +5,7 @@ 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/index.ts b/packages/wallet/core/src/signers/index.ts index 80ccc07f1..28a8815b2 100644 --- a/packages/wallet/core/src/signers/index.ts +++ b/packages/wallet/core/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/packages/wallet/core/src/signers/passkey.ts index 0cc7cacab..770b6b181 100644 --- a/packages/wallet/core/src/signers/passkey.ts +++ b/packages/wallet/core/src/signers/passkey.ts @@ -5,12 +5,15 @@ 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 = { @@ -18,6 +21,11 @@ export type CreatePasskeyOptions = { requireUserVerification?: boolean credentialName?: string embedMetadata?: boolean + webauthn?: WebAuthnLike +} + +export type FindPasskeyOptions = { + webauthn?: WebAuthnLike } export type WitnessMessage = { @@ -45,6 +53,7 @@ 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 @@ -53,6 +62,7 @@ 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( @@ -60,6 +70,7 @@ 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) @@ -90,13 +101,15 @@ 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 WebAuthnP256.createCredential({ + const credential = await webauthn.createCredential({ user: { name, }, @@ -120,6 +133,7 @@ export class Passkey implements SapientSigner, Witnessable { }, embedMetadata: options?.embedMetadata, metadata, + webauthn, }) if (options?.stateProvider) { @@ -132,8 +146,10 @@ export class Passkey implements SapientSigner, Witnessable { static async find( stateReader: State.Reader, extensions: Pick, + options?: FindPasskeyOptions, ): Promise { - const response = await WebAuthnP256.sign({ challenge: Hex.random(32) }) + const webauthn = options?.webauthn ?? WebAuthnP256 + const response = await webauthn.sign({ challenge: Hex.random(32) }) if (!response.raw) throw new Error('No credential returned') const authenticatorDataBytes = Bytes.fromHex(response.metadata.authenticatorData) @@ -218,7 +234,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) + return Passkey.loadFromWitness(stateReader, extensions, flattened[0]!.wallet, flattened[0]!.imageHash, options) } async signSapient( @@ -234,7 +250,7 @@ export class Passkey implements SapientSigner, Witnessable { const challenge = Hex.fromBytes(Payload.hash(wallet, chainId, payload)) - const response = await WebAuthnP256.sign({ + const response = await this.webauthn.sign({ challenge, credentialId: this.credentialId, userVerification: this.publicKey.requireUserVerification ? 'required' : 'discouraged', @@ -260,7 +276,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/packages/wallet/core/src/signers/pk/encrypted.ts index c75bc57f9..dce0eb3cc 100644 --- a/packages/wallet/core/src/signers/pk/encrypted.ts +++ b/packages/wallet/core/src/signers/pk/encrypted.ts @@ -1,4 +1,5 @@ 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 { @@ -17,6 +18,7 @@ export class EncryptedPksDb { constructor( private readonly localStorageKeyPrefix: string = 'e_pk_key_', tableName: string = 'e_pk', + private readonly env?: CoreEnv, ) { this.tableName = tableName } @@ -25,9 +27,59 @@ 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 = indexedDB.open(this.dbName, this.dbVersion) + const request = this.getIndexedDB().open(this.dbName, this.dbVersion) request.onupgradeneeded = () => { const db = request.result if (!db.objectStoreNames.contains(this.tableName)) { @@ -73,7 +125,11 @@ export class EncryptedPksDb { } async generateAndStore(): Promise { - const encryptionKey = await window.crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [ + const crypto = this.getCrypto() + const storage = this.getStorage() + const TextEncoderCtor = this.getTextEncoderCtor() + + const encryptionKey = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [ 'encrypt', 'decrypt', ]) @@ -84,13 +140,13 @@ export class EncryptedPksDb { 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 exportedKey = await crypto.subtle.exportKey('jwk', encryptionKey) + storage.setItem(keyPointer, JSON.stringify(exportedKey)) - const encoder = new TextEncoder() + const encoder = new TextEncoderCtor() 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 iv = crypto.getRandomValues(new Uint8Array(12)) + const encryptedBuffer = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, encryptionKey, encodedPk) const encrypted: EncryptedData = { iv, @@ -113,7 +169,7 @@ export class EncryptedPksDb { async getEncryptedPkStore(address: Address.Address): Promise { const entry = await this.getEncryptedEntry(address) if (!entry) return - return new EncryptedPkStore(entry) + return new EncryptedPkStore(entry, this.env) } async listAddresses(): Promise { @@ -125,12 +181,41 @@ export class EncryptedPksDb { const dbKey = this.computeDbKey(address) await this.putData(dbKey, undefined) const keyPointer = this.localStorageKeyPrefix + address - window.localStorage.removeItem(keyPointer) + this.getStorage().removeItem(keyPointer) } } export class EncryptedPkStore implements PkStore { - constructor(private readonly encrypted: EncryptedData) {} + 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 + } address(): Address.Address { return this.encrypted.address @@ -141,16 +226,20 @@ export class EncryptedPkStore implements PkStore { } async signDigest(digest: Bytes.Bytes): Promise<{ r: bigint; s: bigint; yParity: number }> { - const keyJson = window.localStorage.getItem(this.encrypted.keyPointer) + const storage = this.getStorage() + const crypto = this.getCrypto() + const TextDecoderCtor = this.getTextDecoderCtor() + + const keyJson = storage.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( + const encryptionKey = await crypto.subtle.importKey('jwk', jwk, { name: 'AES-GCM' }, false, ['decrypt']) + const decryptedBuffer = await crypto.subtle.decrypt( { name: 'AES-GCM', iv: this.encrypted.iv }, encryptionKey, this.encrypted.data, ) - const decoder = new TextDecoder() + const decoder = new TextDecoderCtor() 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/packages/wallet/core/src/signers/pk/index.ts index 5c26b1dcb..b15fb3ecb 100644 --- a/packages/wallet/core/src/signers/pk/index.ts +++ b/packages/wallet/core/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/packages/wallet/core/src/signers/session-manager.ts index ef3d81b3a..6b23cb7a6 100644 --- a/packages/wallet/core/src/signers/session-manager.ts +++ b/packages/wallet/core/src/signers/session-manager.ts @@ -18,6 +18,7 @@ import { SessionSigner, SessionSignerInvalidReason, isImplicitSessionSigner, + isIncrementCall, UsageLimit, } from './session/index.js' @@ -130,21 +131,16 @@ export class SessionManager implements SapientSigner { })) } - 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 + /** + * 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 { const signers: SessionSigner[] = [] for (const call of calls) { let supported = false @@ -173,9 +169,67 @@ 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}, `, - ) + 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++ } } return signers @@ -191,20 +245,23 @@ export class SessionManager implements SapientSigner { } const signers = await this.findSignersForCalls(wallet, chainId, calls) - // Create a map of signers to their associated calls - const signerToCalls = new Map() + // Map each signer to only their non-increment calls + const signerToNonIncrementCalls = new Map() signers.forEach((signer, index) => { const call = calls[index]! - const existingCalls = signerToCalls.get(signer) || [] - signerToCalls.set(signer, [...existingCalls, call]) + if (isIncrementCall(call, this.address)) { + return + } + const existing = signerToNonIncrementCalls.get(signer) || [] + signerToNonIncrementCalls.set(signer, [...existing, call]) }) - // Prepare increments for each explicit signer with their associated calls + // Prepare increments for each explicit signer from their non-increment calls only const increments: UsageLimit[] = ( await Promise.all( - Array.from(signerToCalls.entries()).map(async ([signer, associatedCalls]) => { + Array.from(signerToNonIncrementCalls.entries()).map(async ([signer, nonIncrementCalls]) => { if (isExplicitSessionSigner(signer)) { - return signer.prepareIncrements(wallet, chainId, associatedCalls, this.address, this._provider!) + return signer.prepareIncrements(wallet, chainId, nonIncrementCalls, this.address, this._provider!) } return [] }), diff --git a/packages/wallet/core/src/signers/session/explicit.ts b/packages/wallet/core/src/signers/session/explicit.ts index cd72b2256..ef78c554a 100644 --- a/packages/wallet/core/src/signers/session/explicit.ts +++ b/packages/wallet/core/src/signers/session/explicit.ts @@ -1,14 +1,7 @@ -import { - Constants, - Extensions, - Payload, - Permission, - SessionConfig, - SessionSignature, -} from '@0xsequence/wallet-primitives' +import { Constants, 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, SessionSignerValidity, UsageLimit } from './session.js' +import { ExplicitSessionSigner, isIncrementCall, SessionSignerValidity, UsageLimit } from './session.js' export type ExplicitParams = Omit @@ -215,11 +208,7 @@ export class Explicit implements ExplicitSessionSigner { sessionManagerAddress: Address.Address, provider?: Provider.Provider, ): Promise { - 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)) - ) { + if (isIncrementCall(call, sessionManagerAddress)) { // Can sign increment usage calls return true } @@ -241,11 +230,7 @@ export class Explicit implements ExplicitSessionSigner { ): Promise { const call = payload.calls[callIdx]! let permissionIndex: number - 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)) - ) { + if (isIncrementCall(call, sessionManagerAddress)) { // Permission check not required. Use the first permission permissionIndex = 0 } else { @@ -323,7 +308,7 @@ export class Explicit implements ExplicitSessionSigner { Bytes.fromHex(call.data).slice(Number(rule.offset), Number(rule.offset) + 32), 32, ) - let value: Bytes.Bytes = callDataValue.map((b, i) => b & rule.mask[i]!) + const 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/packages/wallet/core/src/signers/session/implicit.ts index 71b112865..8eb765808 100644 --- a/packages/wallet/core/src/signers/session/implicit.ts +++ b/packages/wallet/core/src/signers/session/implicit.ts @@ -1,6 +1,5 @@ import { Attestation, - Extensions, Payload, Signature as SequenceSignature, SessionConfig, @@ -96,7 +95,7 @@ export class Implicit implements ImplicitSessionSigner { ) const expectedResult = Bytes.toHex(Attestation.generateImplicitRequestMagic(this._attestation, wallet)) return acceptImplicitRequest === expectedResult - } catch (error) { + } catch { // console.log('implicit signer unsupported call', call, error) return false } diff --git a/packages/wallet/core/src/signers/session/session.ts b/packages/wallet/core/src/signers/session/session.ts index 4bcc5bb77..08b2ade81 100644 --- a/packages/wallet/core/src/signers/session/session.ts +++ b/packages/wallet/core/src/signers/session/session.ts @@ -1,5 +1,5 @@ -import { Payload, SessionConfig, SessionSignature } from '@0xsequence/wallet-primitives' -import { Address, Hex, Provider } from 'ox' +import { Constants, Payload, SessionConfig, SessionSignature } from '@0xsequence/wallet-primitives' +import { AbiFunction, Address, Hex, Provider } from 'ox' export type SessionSignerInvalidReason = | 'Expired' @@ -68,3 +68,11 @@ 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/arweave/arweave.ts b/packages/wallet/core/src/state/arweave/arweave.ts new file mode 100644 index 000000000..b3cadb734 --- /dev/null +++ b/packages/wallet/core/src/state/arweave/arweave.ts @@ -0,0 +1,115 @@ +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 new file mode 100644 index 000000000..6d7aa7009 --- /dev/null +++ b/packages/wallet/core/src/state/arweave/index.ts @@ -0,0 +1,1006 @@ +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 (record['Context-Factory']) { + 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 new file mode 100644 index 000000000..059579cb0 --- /dev/null +++ b/packages/wallet/core/src/state/arweave/schema.ts @@ -0,0 +1,369 @@ +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/src/state/index.ts b/packages/wallet/core/src/state/index.ts index 53e169908..94faee061 100644 --- a/packages/wallet/core/src/state/index.ts +++ b/packages/wallet/core/src/state/index.ts @@ -79,9 +79,10 @@ export interface Writer { export type MaybePromise = T | Promise -export * as Local from './local/index.js' +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 * 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/packages/wallet/core/src/state/local/index.ts index b3200c844..282de20e0 100644 --- a/packages/wallet/core/src/state/local/index.ts +++ b/packages/wallet/core/src/state/local/index.ts @@ -203,7 +203,7 @@ export class Provider implements ProviderInterface { fromImageHash: Hex.Hex, options?: { allUpdates?: boolean }, ): Promise<{ imageHash: Hex.Hex; signature: Signature.RawSignature }[]> { - let fromConfig = await this.store.loadConfig(fromImageHash) + const 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,6 +397,8 @@ 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/packages/wallet/core/src/state/local/indexed-db.ts index 98a43743c..eeb5cd346 100644 --- a/packages/wallet/core/src/state/local/indexed-db.ts +++ b/packages/wallet/core/src/state/local/indexed-db.ts @@ -1,5 +1,6 @@ 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 @@ -16,15 +17,27 @@ export class IndexedDbStore implements Store { private _db: IDBDatabase | null = null private dbName: string - constructor(dbName: string = 'sequence-indexeddb') { + constructor( + dbName: string = 'sequence-indexeddb', + private readonly env?: CoreEnv, + ) { 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 = indexedDB.open(this.dbName, DB_VERSION) + const request = this.getIndexedDB().open(this.dbName, DB_VERSION) request.onupgradeneeded = () => { const db = request.result diff --git a/packages/wallet/core/src/state/remote/dev-http.ts b/packages/wallet/core/src/state/remote/dev-http.ts index d7fe0f492..7c288e050 100644 --- a/packages/wallet/core/src/state/remote/dev-http.ts +++ b/packages/wallet/core/src/state/remote/dev-http.ts @@ -4,10 +4,16 @@ import { Provider } from '../index.js' export class DevHttpProvider implements Provider { private readonly baseUrl: string + private readonly fetcher: typeof fetch - constructor(baseUrl: string) { + constructor(baseUrl: string, fetcher?: typeof fetch) { // 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 { @@ -24,7 +30,7 @@ export class DevHttpProvider implements Provider { let response: Response try { - response = await fetch(url, options) + response = await this.fetcher(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) @@ -38,12 +44,12 @@ export class DevHttpProvider implements Provider { const errorText = await response.text() const errorJson = await Utils.fromJSON(errorText) errorPayload = { ...errorPayload, ...errorJson } - } catch (e) { + } catch { try { // If JSON parsing fails, try getting text for better error message const errorText = await response.text() errorPayload.body = errorText - } catch (textErr) { + } catch { // Ignore if reading text also fails } } @@ -99,7 +105,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 (readError) { + } catch { 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/sequence/index.ts b/packages/wallet/core/src/state/sequence/index.ts index 3712f2aa5..084211153 100644 --- a/packages/wallet/core/src/state/sequence/index.ts +++ b/packages/wallet/core/src/state/sequence/index.ts @@ -9,13 +9,17 @@ import { TransactionRequest, } from 'ox' import { normalizeAddressKeys, Provider as ProviderInterface } from '../index.js' -import { Sessions, SignatureType } from './sessions.gen.js' +import { Sessions, SignatureType, type Fetch } from './sessions.gen.js' export class Provider implements ProviderInterface { private readonly service: Sessions - constructor(host = 'https://keymachine.sequence.app') { - this.service = new Sessions(host, fetch) + 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) } async getConfiguration(imageHash: Hex.Hex): Promise { @@ -186,7 +190,9 @@ export class Provider implements ProviderInterface { case SignatureType.SapientCompact: throw new Error(`unexpected compact sapient signature by ${signer}`) } - } catch {} + } catch { + // ignore + } } async getWitnessForSapient( @@ -221,7 +227,9 @@ export class Provider implements ProviderInterface { signature: { type: 'sapient_compact', address: signer, data: witness.signature }, } } - } catch {} + } catch { + // ignore + } } async getConfigurationUpdates( @@ -380,7 +388,7 @@ const recoverSapientSignatureCompactFunction = AbiFunction.from(recoverSapientSi class PasskeySignatureValidator implements oxProvider.Provider { request: oxProvider.Provider['request'] = (async (request) => { switch (request.method) { - case 'eth_call': + case 'eth_call': { if (!request.params || !Array.isArray(request.params) || request.params.length === 0) { throw new Error('eth_call requires transaction parameters') } @@ -406,6 +414,7 @@ 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`) diff --git a/packages/wallet/core/src/wallet.ts b/packages/wallet/core/src/wallet.ts index 05a02da09..66b0eb52b 100644 --- a/packages/wallet/core/src/wallet.ts +++ b/packages/wallet/core/src/wallet.ts @@ -25,6 +25,51 @@ 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 @@ -342,7 +387,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)) { + if (Address.isEqual(call.to, this.address) && call.data !== '0x') { throw new Error('calls to the wallet contract itself are not allowed in safe mode') } } @@ -355,7 +400,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 @@ -455,7 +500,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)) { + if (Address.isEqual(call.to, this.address) && call.data !== '0x') { throw new Error('calls to the wallet contract itself are not allowed in safe mode') } } @@ -494,6 +539,56 @@ 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) @@ -570,7 +665,7 @@ export class Wallet { if (typeof message !== 'string') { encodedMessage = TypedData.encode(message) } else { - let hexMessage = Hex.validate(message) ? message : Hex.fromString(message) + const 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/packages/wallet/core/test/constants.ts index 493bf6bf2..42b51d49b 100644 --- a/packages/wallet/core/test/constants.ts +++ b/packages/wallet/core/test/constants.ts @@ -1,9 +1,12 @@ 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 @@ -16,4 +19,5 @@ 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' diff --git a/packages/wallet/core/test/envelope.test.ts b/packages/wallet/core/test/envelope.test.ts index 8ecc0e282..c92cf900e 100644 --- a/packages/wallet/core/test/envelope.test.ts +++ b/packages/wallet/core/test/envelope.test.ts @@ -6,7 +6,6 @@ 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/session-manager.test.ts b/packages/wallet/core/test/session-manager.test.ts index 93a69ed89..7ba342ebc 100644 --- a/packages/wallet/core/test/session-manager.test.ts +++ b/packages/wallet/core/test/session-manager.test.ts @@ -1,4 +1,4 @@ -import { Extensions } from '@0xsequence/wallet-primitives' +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' @@ -731,7 +731,7 @@ for (const extension of ALL_EXTENSIONS) { ) it( - 'signs a payload using an explicit session', + 'signs a payload using an explicit session with allowAll and value limit', async () => { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) @@ -804,7 +804,7 @@ for (const extension of ALL_EXTENSIONS) { ) it( - 'signs a payload using an explicit session', + 'signs using explicit session with onlyOnce, consumes usage and rejects second call', async () => { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) @@ -1357,5 +1357,347 @@ 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/signers-pk.test.ts b/packages/wallet/core/test/signers-pk.test.ts index 4121ffb68..7a696cf6c 100644 --- a/packages/wallet/core/test/signers-pk.test.ts +++ b/packages/wallet/core/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-implicit.test.ts b/packages/wallet/core/test/signers-session-implicit.test.ts index ed66a50af..5b0a37082 100644 --- a/packages/wallet/core/test/signers-session-implicit.test.ts +++ b/packages/wallet/core/test/signers-session-implicit.test.ts @@ -236,7 +236,6 @@ describe('Implicit Session', () => { }, } const identitySignature = createValidIdentitySignature(attestation) - const topology = createValidTopology() // This should throw an error during construction due to future issued time expect(() => { @@ -250,7 +249,6 @@ 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/arweave/arweave.test.ts b/packages/wallet/core/test/state/arweave/arweave.test.ts new file mode 100644 index 000000000..8c7aa5cd2 --- /dev/null +++ b/packages/wallet/core/test/state/arweave/arweave.test.ts @@ -0,0 +1,230 @@ +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 new file mode 100644 index 000000000..f40e18f2e --- /dev/null +++ b/packages/wallet/core/test/state/arweave/recording @@ -0,0 +1,2455 @@ +[ + { + "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/state/debug.test.ts b/packages/wallet/core/test/state/debug.test.ts index 4824297ab..6882f45a2 100644 --- a/packages/wallet/core/test/state/debug.test.ts +++ b/packages/wallet/core/test/state/debug.test.ts @@ -5,7 +5,6 @@ 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', () => { @@ -241,13 +240,13 @@ describe('State Debug', () => { } const complexRef: ComplexInterface = { - async complexMethod(data) { + async complexMethod() { return 'complex-ref' }, } const complexCand: ComplexInterface = { - async complexMethod(data) { + async complexMethod() { return 'complex-cand' }, } diff --git a/packages/wallet/core/test/wallet-fee-options.test.ts b/packages/wallet/core/test/wallet-fee-options.test.ts new file mode 100644 index 000000000..633f47672 --- /dev/null +++ b/packages/wallet/core/test/wallet-fee-options.test.ts @@ -0,0 +1,106 @@ +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/dapp-client/CHANGELOG.md b/packages/wallet/dapp-client/CHANGELOG.md index 3c4d46618..7087e36b9 100644 --- a/packages/wallet/dapp-client/CHANGELOG.md +++ b/packages/wallet/dapp-client/CHANGELOG.md @@ -1,5 +1,359 @@ # @0xsequence/dapp-client +## 3.1.1 + +### Patch Changes + +- WDK fix +- Updated dependencies + - @0xsequence/guard@3.0.12 + - @0xsequence/relayer@3.1.1 + - @0xsequence/wallet-core@3.0.12 + - @0xsequence/wallet-primitives@3.0.12 + +## 3.1.0 + +### Minor Changes + +- 55121af: Add `isSponsored` for explicit sponsorship checks. + + `DappClient.isSponsored(chainId, transactions)` and + `ChainSessionManager.isSponsored(calls)` return `true` only when the relayer's + `/FeeOptions` endpoint explicitly reports sponsorship. Any error, network + failure, or absence of sponsorship returns `false`, so a `true` result is + always safe to surface as "free gas" in UI. + + Prefer this over inferring sponsorship from an empty `getFeeOptions` array — a + swallowed `/FeeOptions` error also produces an empty array, so the inference + can misclassify a failed quote as a real subsidy. The new method uses the + positive `sponsored: boolean` signal from `@0xsequence/relayer`'s widened + `feeOptions` return. + + `getFeeOptions` is unchanged. + +### Patch Changes + +- Fix for relayer sponsored fees +- Updated dependencies +- Updated dependencies [55121af] + - @0xsequence/guard@3.0.11 + - @0xsequence/relayer@3.1.0 + - @0xsequence/wallet-core@3.0.11 + - @0xsequence/wallet-primitives@3.0.11 + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface +- Updated dependencies + - @0xsequence/guard@3.0.10 + - @0xsequence/relayer@3.0.10 + - @0xsequence/wallet-core@3.0.10 + - @0xsequence/wallet-primitives@3.0.10 + +## 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 diff --git a/packages/wallet/dapp-client/eslint.config.js b/packages/wallet/dapp-client/eslint.config.js new file mode 100644 index 000000000..cecf89b03 --- /dev/null +++ b/packages/wallet/dapp-client/eslint.config.js @@ -0,0 +1,4 @@ +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 index d67d1ea7d..59ecad054 100644 --- a/packages/wallet/dapp-client/package.json +++ b/packages/wallet/dapp-client/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/dapp-client", - "version": "3.0.0-beta.5", + "version": "3.1.1", "license": "Apache-2.0", "type": "module", "publishConfig": { @@ -11,7 +11,8 @@ "build": "tsc", "dev": "tsc --watch", "typecheck": "tsc --noEmit", - "clean": "rimraf dist" + "clean": "rimraf dist", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -20,14 +21,15 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "@vitest/coverage-v8": "^4.0.14", - "dotenv": "^17.2.3", + "@types/node": "^25.3.0", + "@vitest/coverage-v8": "^4.0.18", + "dotenv": "^17.3.1", "fake-indexeddb": "^6.2.5", - "happy-dom": "^20.0.10", - "typescript": "^5.9.3", - "vitest": "^4.0.14" + "happy-dom": "^20.8.9", + "typescript": "^6.0.3", + "vitest": "^4.0.18" }, "dependencies": { "@0xsequence/guard": "workspace:^", diff --git a/packages/wallet/dapp-client/src/ChainSessionManager.ts b/packages/wallet/dapp-client/src/ChainSessionManager.ts index cd67c6b60..d2ebd1a8e 100644 --- a/packages/wallet/dapp-client/src/ChainSessionManager.ts +++ b/packages/wallet/dapp-client/src/ChainSessionManager.ts @@ -37,12 +37,14 @@ import { 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' @@ -84,6 +86,11 @@ export class ChainSessionManager { 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. @@ -151,11 +158,11 @@ export class ChainSessionManager { listener: ChainSessionManagerEventMap[K], ): () => void { if (!this.eventListeners[event]) { - this.eventListeners[event] = new Set() as any + this.eventListeners[event] = new Set() } - ;(this.eventListeners[event] as any).add(listener) + this.eventListeners[event].add(listener) return () => { - ;(this.eventListeners[event] as any)?.delete(listener) + this.eventListeners[event]?.delete(listener) } } @@ -280,6 +287,7 @@ export class ChainSessionManager { preferredLoginMethod?: LoginMethod email?: string includeImplicitSession?: boolean + ethAuth?: EthAuthSettings } = {}, ): Promise { if (this.isInitialized) { @@ -306,6 +314,7 @@ export class ChainSessionManager { origin, session: completeSession as ExplicitSession | undefined, includeImplicitSession: options.includeImplicitSession ?? false, + ethAuth: options.ethAuth, preferredLoginMethod: options.preferredLoginMethod, email: options.preferredLoginMethod === 'email' ? options.email : undefined, } @@ -372,6 +381,10 @@ export class ChainSessionManager { this.guard = guard } + if (payload.ethAuth) { + await this._saveEthAuthProofIfProvided(connectResponse.ethAuthProof) + } + if (this.transport.mode === TransportMode.POPUP) { this.transport.closeWallet() } @@ -591,6 +604,10 @@ export class ChainSessionManager { 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.') @@ -811,19 +828,6 @@ export class ChainSessionManager { await this.sessionManager.findSignersForCalls(this.wallet.address, this.chainId, calls) return true } catch (error) { - if (error instanceof Error && error.message.includes('Signer supporting call is expired')) { - // Extract the expired signer address from the message with address regex - const expiredSignerAddress = error.message.match(/(0x[0-9a-fA-F]{40})/)?.[1] - if (expiredSignerAddress) { - // Refresh the session - await this._refreshExplicitSession(Address.from(expiredSignerAddress)) - // Retry the permission check - return this.hasPermission(transactions) - } else { - // Could not parse error message. Rethrow as this shouldn't happen. - throw error - } - } // An error from findSignersForCalls indicates a permission failure. console.warn( `Permission check failed for chain ${this.chainId}:`, @@ -851,13 +855,77 @@ export class ChainSessionManager { })) try { const signedCall = await this._buildAndSignCalls(callsToSend) - const feeOptions = await this.relayer.feeOptions(signedCall.to, this.chainId, 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)}`) } } + /** + * Checks whether the given transactions would be sponsored by an active + * relayer policy on this chain. + * + * Returns `true` only when the relayer's `/FeeOptions` endpoint explicitly + * reports sponsorship. A failed quote, network error, or absence of + * sponsorship all return `false`, so a `true` result is always safe to + * surface as "free gas" in UI. + */ + async isSponsored(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.sponsored === true && !feeOptions.failed + } catch (err) { + console.warn( + `isSponsored check failed for chain ${this.chainId}:`, + err instanceof Error ? err.message : String(err), + ) + return false + } + } + /** * Builds, signs, and sends a batch of transactions. * @param transactions The transactions to be sent. @@ -907,7 +975,7 @@ export class ChainSessionManager { callsToSend.unshift(transferCall) } } - const signedCalls = await this._buildAndSignCalls(callsToSend) + 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') { @@ -1101,4 +1169,49 @@ export class ChainSessionManager { 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 index 3c5a29cba..cc6bcba4d 100644 --- a/packages/wallet/dapp-client/src/DappClient.ts +++ b/packages/wallet/dapp-client/src/DappClient.ts @@ -14,8 +14,10 @@ import { GetFeeTokensResponse, GuardConfig, LoginMethod, + EthAuthSettings, RandomPrivateKeyFn, RequestActionType, + ETHAuthProof, SendWalletTransactionPayload, SequenceSessionStorage, SignMessagePayload, @@ -30,7 +32,7 @@ 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?: any) => void +export type DappClientEventListener = (data?: unknown) => void interface DappClientEventMap { sessionsUpdated: () => void @@ -180,11 +182,12 @@ export class DappClient { */ public on(event: K, listener: DappClientEventMap[K]): () => void { if (!this.eventListeners[event]) { - this.eventListeners[event] = new Set() as any + // @ts-expect-error - indexing into evenListeners will improperly create a union of all the possible types + this.eventListeners[event] = new Set() } - ;(this.eventListeners[event] as any).add(listener) + this.eventListeners[event].add(listener) return () => { - ;(this.eventListeners[event] as any)?.delete(listener) + this.eventListeners[event]?.delete(listener) } } @@ -407,6 +410,13 @@ export class DappClient { } } + /** + * 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. @@ -559,6 +569,7 @@ export class DappClient { preferredLoginMethod?: LoginMethod email?: string includeImplicitSession?: boolean + ethAuth?: EthAuthSettings } = {}, ): Promise { if (this.isInitialized) { @@ -614,6 +625,7 @@ export class DappClient { preferredLoginMethod?: LoginMethod email?: string includeImplicitSession?: boolean + ethAuth?: EthAuthSettings } = {}, ): Promise { if (!this.isInitialized || !this.hasSessionlessConnection || !this.walletAddress) { @@ -794,6 +806,30 @@ export class DappClient { return await chainSessionManager.getFeeOptions(transactions) } + /** + * Checks whether the given transactions would be sponsored on `chainId`. + * + * Returns `true` only when the relayer's `/FeeOptions` endpoint explicitly + * reports sponsorship. A failed quote, network error, or absence of + * sponsorship all return `false`, so a `true` result is always safe to + * surface as "free gas" in UI. + * + * Prefer this over inferring sponsorship from an empty `getFeeOptions` + * array — a swallowed `/FeeOptions` error also produces an empty array. + * + * @example + * if (await dappClient.isSponsored(1, transactions)) { + * // safe to show "Free gas, sponsored by app" + * } else { + * const feeOptions = await dappClient.getFeeOptions(1, transactions) + * // present feeOptions[0..n] to the user as payment choices + * } + */ + async isSponsored(chainId: number, transactions: Transaction[]): Promise { + const chainSessionManager = await this.getOrInitializeChainManager(chainId) + return await chainSessionManager.isSponsored(transactions) + } + /** * Fetches fee tokens for a chain. * @returns A promise that resolves with the fee tokens response. {@link GetFeeTokensResponse} @@ -965,8 +1001,6 @@ export class DappClient { async disconnect(options?: { keepSessionlessConnection?: boolean }): Promise { const keepSessionlessConnection = options?.keepSessionlessConnection ?? true - const transportMode = this.transportMode - if (this.transport) { this.transport.destroy() } diff --git a/packages/wallet/dapp-client/src/DappTransport.ts b/packages/wallet/dapp-client/src/DappTransport.ts index 502c238bd..090b1070b 100644 --- a/packages/wallet/dapp-client/src/DappTransport.ts +++ b/packages/wallet/dapp-client/src/DappTransport.ts @@ -378,10 +378,7 @@ export class DappTransport { return } - const isPotentiallyValidSource = - this.walletWindow && (event.source === this.walletWindow || !this.walletWindow.closed) - - if (!isPotentiallyValidSource && event.data?.type !== MessageType.WALLET_OPENED) { + if (!this.walletWindow || event.source !== this.walletWindow) { return } diff --git a/packages/wallet/dapp-client/src/index.ts b/packages/wallet/dapp-client/src/index.ts index d16662c37..ce976c13d 100644 --- a/packages/wallet/dapp-client/src/index.ts +++ b/packages/wallet/dapp-client/src/index.ts @@ -23,8 +23,11 @@ export type { GetFeeTokensResponse, FeeToken, FeeOption, + TransportMessage, + EthAuthSettings, + ETHAuthProof, } from './types/index.js' -export { RequestActionType, TransportMode } from './types/index.js' +export { RequestActionType, TransportMode, MessageType } from './types/index.js' export { FeeOptionError, TransactionError, @@ -34,7 +37,17 @@ export { SigningError, ModifyExplicitSessionError, } from './utils/errors.js' -export { getExplorerUrl, jsonReplacers, jsonRevivers } from './utils/index.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, @@ -45,6 +58,14 @@ export type { } from './utils/storage.js' export { WebStorage } from './utils/storage.js' -export { Attestation, Permission, Extensions, SessionConfig, Constants, Payload } from '@0xsequence/wallet-primitives' +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 index 72e3dbe11..0f023c2bb 100644 --- a/packages/wallet/dapp-client/src/types/index.ts +++ b/packages/wallet/dapp-client/src/types/index.ts @@ -21,19 +21,39 @@ export const RequestActionType = { SEND_WALLET_TRANSACTION: 'sendWalletTransaction', } as const -export type LoginMethod = 'google' | 'apple' | 'email' | 'passkey' | 'mnemonic' +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 } @@ -81,6 +101,7 @@ export interface CreateNewSessionResponse { userEmail?: string loginMethod?: LoginMethod guard?: GuardConfig + ethAuthProof?: ETHAuthProof } export interface SignatureResponse { diff --git a/packages/wallet/dapp-client/src/utils/index.ts b/packages/wallet/dapp-client/src/utils/index.ts index cdb2c4e69..12bf312c3 100644 --- a/packages/wallet/dapp-client/src/utils/index.ts +++ b/packages/wallet/dapp-client/src/utils/index.ts @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Network } from '@0xsequence/wallet-primitives' -import { Bytes, Hex } from 'ox' +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 @@ -124,6 +126,50 @@ const uint8ArrayReviver: JsonReviver = (key, 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. * diff --git a/packages/wallet/dapp-client/src/utils/storage.ts b/packages/wallet/dapp-client/src/utils/storage.ts index 8928d354e..8a56015ba 100644 --- a/packages/wallet/dapp-client/src/utils/storage.ts +++ b/packages/wallet/dapp-client/src/utils/storage.ts @@ -5,6 +5,7 @@ import { SignMessagePayload, SignTypedDataPayload, GuardConfig, + ETHAuthProof, SendWalletTransactionPayload, ModifyExplicitSessionPayload, CreateNewSessionPayload, @@ -81,6 +82,10 @@ export interface SequenceStorage { getSessionlessConnection(): Promise clearSessionlessConnection(): Promise + saveEthAuthProof(proof: ETHAuthProof): Promise + getEthAuthProof(): Promise + clearEthAuthProof(): Promise + saveSessionlessConnectionSnapshot?(sessionData: SessionlessConnectionData): Promise getSessionlessConnectionSnapshot?(): Promise clearSessionlessConnectionSnapshot?(): Promise @@ -94,6 +99,7 @@ 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' @@ -305,6 +311,15 @@ export class WebStorage implements SequenceStorage { } } + 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 @@ -314,6 +329,15 @@ export class WebStorage implements SequenceStorage { } } + 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) @@ -323,6 +347,15 @@ export class WebStorage implements SequenceStorage { } } + 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) @@ -363,6 +396,7 @@ export class WebStorage implements SequenceStorage { 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) diff --git a/packages/wallet/dapp-client/test/ethauth-proof.test.ts b/packages/wallet/dapp-client/test/ethauth-proof.test.ts new file mode 100644 index 000000000..93273f0d6 --- /dev/null +++ b/packages/wallet/dapp-client/test/ethauth-proof.test.ts @@ -0,0 +1,207 @@ +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/primitives-cli/eslint.config.js b/packages/wallet/primitives-cli/eslint.config.js new file mode 100644 index 000000000..cecf89b03 --- /dev/null +++ b/packages/wallet/primitives-cli/eslint.config.js @@ -0,0 +1,4 @@ +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 index ee00f2a2a..b91c0a885 100644 --- a/packages/wallet/primitives-cli/package.json +++ b/packages/wallet/primitives-cli/package.json @@ -21,14 +21,14 @@ } }, "devDependencies": { - "@repo/eslint-config": "workspace:*", + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", + "@types/node": "^25.3.0", "@types/yargs": "^17.0.35", "concurrently": "^9.2.1", - "esbuild": "^0.27.0", - "nodemon": "^3.1.11", - "typescript": "^5.9.3" + "esbuild": "^0.27.3", + "nodemon": "^3.1.14", + "typescript": "^6.0.3" }, "dependencies": { "@0xsequence/wallet-primitives": "workspace:^", diff --git a/packages/wallet/primitives-cli/tsconfig.json b/packages/wallet/primitives-cli/tsconfig.json index 1e325a596..39eeeb094 100644 --- a/packages/wallet/primitives-cli/tsconfig.json +++ b/packages/wallet/primitives-cli/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "rootDir": "src", "outDir": "dist", - "sourceMap": true + "sourceMap": true, + "types": ["node"] }, "include": ["src"], "exclude": ["node_modules", "dist"] diff --git a/packages/wallet/primitives/CHANGELOG.md b/packages/wallet/primitives/CHANGELOG.md index 5e9e2c183..657ffa65c 100644 --- a/packages/wallet/primitives/CHANGELOG.md +++ b/packages/wallet/primitives/CHANGELOG.md @@ -1,5 +1,186 @@ # @0xsequence/wallet-primitives +## 3.0.12 + +### Patch Changes + +- WDK fix + +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface + +## 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 diff --git a/packages/wallet/primitives/eslint.config.js b/packages/wallet/primitives/eslint.config.js new file mode 100644 index 000000000..d10bbd1e9 --- /dev/null +++ b/packages/wallet/primitives/eslint.config.js @@ -0,0 +1,12 @@ +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 index 83f97b33c..14e64c1b5 100644 --- a/packages/wallet/primitives/package.json +++ b/packages/wallet/primitives/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-primitives", - "version": "3.0.0-beta.5", + "version": "3.0.12", "license": "Apache-2.0", "type": "module", "publishConfig": { @@ -13,7 +13,8 @@ "test": "vitest run", "test:coverage": "vitest run --coverage", "typecheck": "tsc --noEmit", - "clean": "rimraf dist" + "clean": "rimraf dist", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -22,10 +23,11 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@vitest/coverage-v8": "^4.0.14", - "typescript": "^5.9.3", - "vitest": "^4.0.14" + "@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/attestation.ts b/packages/wallet/primitives/src/attestation.ts index 78795862e..4f576f7a0 100644 --- a/packages/wallet/primitives/src/attestation.ts +++ b/packages/wallet/primitives/src/attestation.ts @@ -1,4 +1,4 @@ -import { Address, Bytes, Hash } from 'ox' +import { Address, Bytes, Hash, Hex } from 'ox' export type Attestation = { approvedSigner: Address.Address @@ -14,6 +14,18 @@ export type AuthData = { 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 { @@ -76,9 +88,9 @@ export function toJson(attestation: Attestation, indent?: number): string { return JSON.stringify(encodeForJson(attestation), null, indent) } -export function encodeForJson(attestation: Attestation): any { +export function encodeForJson(attestation: Attestation): EncodedAttestation { return { - approvedSigner: attestation.approvedSigner.toString(), + approvedSigner: attestation.approvedSigner, identityType: Bytes.toHex(attestation.identityType), issuerHash: Bytes.toHex(attestation.issuerHash), audienceHash: Bytes.toHex(attestation.audienceHash), @@ -94,7 +106,7 @@ export function fromJson(json: string): Attestation { return fromParsed(JSON.parse(json)) } -export function fromParsed(parsed: any): Attestation { +export function fromParsed(parsed: EncodedAttestation): Attestation { return { approvedSigner: Address.from(parsed.approvedSigner), identityType: Bytes.fromHex(parsed.identityType), diff --git a/packages/wallet/primitives/src/config.ts b/packages/wallet/primitives/src/config.ts index 27bb7f032..20c54dc49 100644 --- a/packages/wallet/primitives/src/config.ts +++ b/packages/wallet/primitives/src/config.ts @@ -2,7 +2,6 @@ import { Address, Bytes, Hash, Hex } from 'ox' import { isRawConfig, isRawNestedLeaf, - isRawNode, isRawSignerLeaf, isSignedSapientSignerLeaf, isSignedSignerLeaf, @@ -55,6 +54,49 @@ export type Leaf = SignerLeaf | SapientSignerLeaf | SubdigestLeaf | AnyAddressSu 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 @@ -62,39 +104,39 @@ export type Config = { checkpointer?: Address.Address } -export function isSignerLeaf(cand: any): cand is SignerLeaf { - return typeof cand === 'object' && cand !== null && cand.type === 'signer' +export function isSignerLeaf(cand: unknown): cand is SignerLeaf { + return typeof cand === 'object' && cand !== null && 'type' in cand && cand.type === 'signer' } -export function isSapientSignerLeaf(cand: any): cand is SapientSignerLeaf { - return typeof cand === 'object' && cand !== null && cand.type === 'sapient-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: any): cand is SubdigestLeaf { - return typeof cand === 'object' && cand !== null && cand.type === 'subdigest' +export function isSubdigestLeaf(cand: unknown): cand is SubdigestLeaf { + return typeof cand === 'object' && cand !== null && 'type' in cand && cand.type === 'subdigest' } -export function isAnyAddressSubdigestLeaf(cand: any): cand is AnyAddressSubdigestLeaf { - return typeof cand === 'object' && cand !== null && cand.type === 'any-address-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: any): cand is NodeLeaf { - return Hex.validate(cand) && cand.length === 66 +export function isNodeLeaf(cand: unknown): cand is NodeLeaf { + return typeof cand === 'string' && Hex.validate(cand) && cand.length === 66 } -export function isNestedLeaf(cand: any): cand is NestedLeaf { - return typeof cand === 'object' && cand !== null && cand.type === 'nested' +export function isNestedLeaf(cand: unknown): cand is NestedLeaf { + return typeof cand === 'object' && cand !== null && 'type' in cand && cand.type === 'nested' } -export function isNode(cand: any): cand is Node { +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: any): cand is Config { - return typeof cand === 'object' && 'threshold' in cand && 'checkpoint' in cand && 'topology' in cand +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: Topology): cand is Leaf { +export function isLeaf(cand: unknown): cand is Leaf { return ( isSignerLeaf(cand) || isSapientSignerLeaf(cand) || @@ -105,7 +147,7 @@ export function isLeaf(cand: Topology): cand is Leaf { ) } -export function isTopology(cand: any): cand is Topology { +export function isTopology(cand: unknown): cand is Topology { return isNode(cand) || isLeaf(cand) } @@ -310,7 +352,7 @@ export function configFromJson(json: string): Config { } } -function encodeTopology(top: Topology): any { +function encodeTopology(top: Topology): EncodedTopology { if (isNode(top)) { return [encodeTopology(top[0]), encodeTopology(top[1])] } else if (isSignerLeaf(top)) { @@ -350,7 +392,7 @@ function encodeTopology(top: Topology): any { throw new Error('Invalid topology') } -function decodeTopology(obj: any): Topology { +function decodeTopology(obj: EncodedTopology): Topology { if (Array.isArray(obj)) { if (obj.length !== 2) { throw new Error('Invalid node structure in JSON') diff --git a/packages/wallet/primitives/src/extensions/recovery.ts b/packages/wallet/primitives/src/extensions/recovery.ts index 7073272e5..43cd05b9c 100644 --- a/packages/wallet/primitives/src/extensions/recovery.ts +++ b/packages/wallet/primitives/src/extensions/recovery.ts @@ -1,7 +1,6 @@ import { Abi, AbiFunction, Address, Bytes, Hex, Provider } from 'ox' import * as GenericTree from '../generic-tree.js' import { Signature } from '../index.js' -import * as Network from '../network.js' import * as Payload from '../payload.js' import { packRSY } from '../utils.js' @@ -56,21 +55,21 @@ export type Tree = Branch | GenericTree.Node | RecoveryLeaf /** * Type guard to check if a value is a RecoveryLeaf */ -export function isRecoveryLeaf(cand: any): cand is RecoveryLeaf { - return typeof cand === 'object' && cand !== null && cand.type === 'leaf' +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: any): cand is Branch { +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: any): cand is Tree { +export function isTree(cand: unknown): cand is Tree { return isRecoveryLeaf(cand) || GenericTree.isNode(cand) || isBranch(cand) } @@ -203,7 +202,7 @@ export function parseBranch(encoded: Bytes.Bytes): { nodes: Tree[]; leftover: By */ export function trimTopology(topology: Tree, signer: Address.Address): Tree { if (isRecoveryLeaf(topology)) { - if (topology.signer === signer) { + if (Address.isEqual(topology.signer, signer)) { return topology } else { return hashConfiguration(topology) @@ -437,10 +436,10 @@ export function fromGenericTree(tree: GenericTree.Tree): Tree { */ export function encodeCalldata( wallet: Address.Address, - payload: Payload.Recovery, + payload: Payload.Recovery, signer: Address.Address, signature: Signature.SignatureOfSignerLeaf, -) { +): Hex.Hex { let signatureBytes: Hex.Hex if (signature.type === 'erc1271') { diff --git a/packages/wallet/primitives/src/network.ts b/packages/wallet/primitives/src/network.ts index b0ef2bbde..498c7a793 100644 --- a/packages/wallet/primitives/src/network.ts +++ b/packages/wallet/primitives/src/network.ts @@ -1,3 +1,8 @@ +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', @@ -21,8 +26,11 @@ export interface Network { name: string decimals: number } - ensAddress?: string deprecated?: true + contracts?: { + multicall3?: Address.Address + ensUniversalResolver?: Address.Address + } } export const ChainId = { @@ -34,7 +42,6 @@ export const ChainId = { // Polygon POLYGON: 137, - POLYGON_ZKEVM: 1101, POLYGON_AMOY: 80002, // BSC @@ -67,10 +74,6 @@ export const ChainId = { HOMEVERSE_TESTNET: 40875, HOMEVERSE: 19011, - // Xai - XAI: 660279, - XAI_SEPOLIA: 37714555429, - // TELOS TELOS: 40, TELOS_TESTNET: 41, @@ -83,10 +86,6 @@ export const ChainId = { APECHAIN: 33139, APECHAIN_TESTNET: 33111, - // Blast - BLAST: 81457, - BLAST_SEPOLIA: 168587773, - // SKALE Nebula SKALE_NEBULA: 1482601649, SKALE_NEBULA_TESTNET: 37084624, @@ -95,16 +94,13 @@ export const ChainId = { 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, @@ -112,23 +108,25 @@ export const ChainId = { // MONAD MONAD: 143, - MONAD_TESTNET: 10143, // SOMNIA SOMNIA_TESTNET: 50312, SOMNIA: 5031, - // INCENTIV - 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] @@ -150,7 +148,10 @@ export const ALL: Network[] = [ name: 'Ether', decimals: 18, }, - ensAddress: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + ensUniversalResolver: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + }, }, { chainId: ChainId.SEPOLIA, @@ -168,6 +169,9 @@ export const ALL: Network[] = [ name: 'Sepolia Ether', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.POLYGON, @@ -185,6 +189,9 @@ export const ALL: Network[] = [ name: 'POL', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.POLYGON_AMOY, @@ -202,22 +209,8 @@ export const ALL: Network[] = [ name: 'Amoy POL', decimals: 18, }, - }, - { - 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, }, }, { @@ -236,6 +229,9 @@ export const ALL: Network[] = [ name: 'BNB', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.BSC_TESTNET, @@ -253,6 +249,9 @@ export const ALL: Network[] = [ name: 'Testnet BNB', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.OPTIMISM, @@ -270,6 +269,9 @@ export const ALL: Network[] = [ name: 'Ether', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.OPTIMISM_SEPOLIA, @@ -287,6 +289,9 @@ export const ALL: Network[] = [ name: 'Sepolia Ether', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.ARBITRUM, @@ -304,6 +309,9 @@ export const ALL: Network[] = [ name: 'Ether', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.ARBITRUM_SEPOLIA, @@ -321,6 +329,9 @@ export const ALL: Network[] = [ name: 'Sepolia Ether', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.ARBITRUM_NOVA, @@ -338,6 +349,9 @@ export const ALL: Network[] = [ name: 'Ether', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.AVALANCHE, @@ -355,6 +369,9 @@ export const ALL: Network[] = [ name: 'AVAX', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.AVALANCHE_TESTNET, @@ -372,6 +389,9 @@ export const ALL: Network[] = [ name: 'Testnet AVAX', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.GNOSIS, @@ -389,12 +409,15 @@ export const ALL: Network[] = [ name: 'XDAI', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.BASE, type: NetworkType.MAINNET, name: 'base', - title: 'Base (Coinbase)', + title: 'Base', rpcUrl: getRpcUrl('base'), logoUrl: getLogoUrl(ChainId.BASE), blockExplorer: { @@ -406,6 +429,9 @@ export const ALL: Network[] = [ name: 'Ether', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.BASE_SEPOLIA, @@ -423,6 +449,9 @@ export const ALL: Network[] = [ name: 'Sepolia Ether', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.HOMEVERSE, @@ -440,6 +469,9 @@ export const ALL: Network[] = [ name: 'OAS', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.HOMEVERSE_TESTNET, @@ -457,39 +489,8 @@ export const ALL: Network[] = [ name: 'Testnet OAS', decimals: 18, }, - }, - { - 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, - }, - }, - { - 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, }, }, { @@ -508,6 +509,9 @@ export const ALL: Network[] = [ name: 'Ether', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.B3_SEPOLIA, @@ -525,6 +529,9 @@ export const ALL: Network[] = [ name: 'Ether', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.APECHAIN, @@ -542,6 +549,9 @@ export const ALL: Network[] = [ name: 'ApeCoin', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.APECHAIN_TESTNET, @@ -559,39 +569,8 @@ export const ALL: Network[] = [ name: 'ApeCoin', decimals: 18, }, - }, - { - 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, - }, - }, - { - 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, }, }, { @@ -610,6 +589,9 @@ export const ALL: Network[] = [ name: 'TLOS', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.TELOS_TESTNET, @@ -627,6 +609,9 @@ export const ALL: Network[] = [ name: 'TLOS', decimals: 18, }, + contracts: { + multicall3: SEQUENCE_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.SKALE_NEBULA, @@ -644,6 +629,9 @@ export const ALL: Network[] = [ name: 'SKALE Fuel', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.SKALE_NEBULA_TESTNET, @@ -661,6 +649,9 @@ export const ALL: Network[] = [ name: 'SKALE Fuel', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.SONEIUM, @@ -678,6 +669,9 @@ export const ALL: Network[] = [ name: 'Ether', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.SONEIUM_MINATO, @@ -695,22 +689,8 @@ export const ALL: Network[] = [ name: 'Ether', decimals: 18, }, - }, - { - 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, }, }, { @@ -729,6 +709,9 @@ export const ALL: Network[] = [ name: 'IMX', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.IMMUTABLE_ZKEVM_TESTNET, @@ -746,6 +729,9 @@ export const ALL: Network[] = [ name: 'IMX', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.MOONBEAM, @@ -763,6 +749,9 @@ export const ALL: Network[] = [ name: 'GLMR', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.MOONBASE_ALPHA, @@ -780,6 +769,9 @@ export const ALL: Network[] = [ name: 'GLMR', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { chainId: ChainId.ETHERLINK, @@ -797,23 +789,29 @@ export const ALL: Network[] = [ name: 'Tez', decimals: 18, }, + contracts: { + multicall3: DEFAULT_MULTICALL3_ADDRESS, + }, }, { - chainId: ChainId.ETHERLINK_TESTNET, + chainId: ChainId.ETHERLINK_SHADOWNET_TESTNET, type: NetworkType.TESTNET, - name: 'etherlink-testnet', - title: 'Etherlink Testnet', - rpcUrl: getRpcUrl('etherlink-testnet'), - logoUrl: getLogoUrl(ChainId.ETHERLINK_TESTNET), + name: 'etherlink-shadownet-testnet', + title: 'Etherlink Shadownet Testnet', + rpcUrl: getRpcUrl('etherlink-shadownet-testnet'), + logoUrl: getLogoUrl(ChainId.ETHERLINK_SHADOWNET_TESTNET), blockExplorer: { - name: 'Etherlink Testnet Explorer', - url: 'https://testnet.explorer.etherlink.com/', + 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, @@ -831,25 +829,10 @@ export const ALL: Network[] = [ name: 'MON', decimals: 18, }, - }, - { - 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, @@ -866,6 +849,9 @@ export const ALL: Network[] = [ name: 'SOMI', decimals: 18, }, + contracts: { + multicall3: SEQUENCE_MULTICALL3_ADDRESS, + }, }, { @@ -884,23 +870,8 @@ export const ALL: Network[] = [ name: 'STT', decimals: 18, }, - }, - - { - 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: SEQUENCE_MULTICALL3_ADDRESS, }, }, @@ -920,23 +891,8 @@ export const ALL: Network[] = [ name: 'ETH', decimals: 18, }, - }, - - { - 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, }, }, @@ -956,6 +912,63 @@ export const ALL: Network[] = [ 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, + }, }, ] diff --git a/packages/wallet/primitives/src/payload.ts b/packages/wallet/primitives/src/payload.ts index 1359abdbe..c933a6118 100644 --- a/packages/wallet/primitives/src/payload.ts +++ b/packages/wallet/primitives/src/payload.ts @@ -1,7 +1,7 @@ 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, Network } from './index.js' +import { Attestation } from './index.js' import { minBytesFor } from './utils.js' import { UserOperation } from 'ox/erc4337' diff --git a/packages/wallet/primitives/src/signature.ts b/packages/wallet/primitives/src/signature.ts index 2e5d3212c..6ba93f9c1 100644 --- a/packages/wallet/primitives/src/signature.ts +++ b/packages/wallet/primitives/src/signature.ts @@ -1,4 +1,4 @@ -import { AbiFunction, AbiParameters, Address, Bytes, Hash, Hex, Provider, Secp256k1, Signature } from 'ox' +import { AbiFunction, AbiParameters, Address, Bytes, Hash, Hex, Provider, Secp256k1 } from 'ox' import { Config, Leaf, @@ -22,7 +22,7 @@ import { RECOVER_SAPIENT_SIGNATURE, RECOVER_SAPIENT_SIGNATURE_COMPACT, IS_VALID_ import { wrap, decode } from './erc-6492.js' import { fromConfigUpdate, hash, Parented } from './payload.js' import { minBytesFor, packRSY, unpackRSY } from './utils.js' -import { Constants, Network } from './index.js' +import { Constants } from './index.js' export const FLAG_SIGNATURE_HASH = 0 export const FLAG_ADDRESS = 1 @@ -618,7 +618,7 @@ export function fillLeaves( export function encodeChainedSignature(signatures: RawSignature[]): Uint8Array { let flag = 0x01 - let sigForCheckpointer = signatures[signatures.length - 1] + const sigForCheckpointer = signatures[signatures.length - 1] if (sigForCheckpointer?.configuration.checkpointer) { flag |= 0x40 @@ -723,7 +723,7 @@ export function encodeTopology( const isBranching = isNode(topology[1]!) || isRawNode(topology[1]!) if (isBranching) { - let encoded1Size = minBytesFor(BigInt(encoded1.length)) + const encoded1Size = minBytesFor(BigInt(encoded1.length)) if (encoded1Size > 15) { throw new Error('Branch too large') } @@ -799,7 +799,7 @@ export function encodeTopology( } else if (topology.signature.type === 'erc1271') { let flag = FLAG_SIGNATURE_ERC1271 << 4 - let bytesForSignatureSize = minBytesFor(BigInt(topology.signature.data.length)) + const bytesForSignatureSize = minBytesFor(BigInt(topology.signature.data.length)) if (bytesForSignatureSize > 3) { throw new Error('Signature too large') } @@ -826,7 +826,7 @@ export function encodeTopology( let flag = (topology.signature.type === 'sapient' ? FLAG_SIGNATURE_SAPIENT : FLAG_SIGNATURE_SAPIENT_COMPACT) << 4 const signatureBytes = Bytes.fromHex(topology.signature.data) - let bytesForSignatureSize = minBytesFor(BigInt(signatureBytes.length)) + const bytesForSignatureSize = minBytesFor(BigInt(signatureBytes.length)) if (bytesForSignatureSize > 3) { throw new Error('Signature too large') } @@ -1218,7 +1218,7 @@ async function recoverTopology( weight: topology.weight, } - default: + default: { const provider = 'provider' in options!.provider ? options!.provider.provider : options!.provider const block = 'block' in options!.provider ? options!.provider.block : undefined @@ -1254,6 +1254,7 @@ async function recoverTopology( } } } + } } case 'sapient': @@ -1264,7 +1265,7 @@ async function recoverTopology( case 'assume-valid': throw new Error(`unable to validate sapient signer ${topology.signature.address} signature`) - default: + default: { const provider = 'provider' in options!.provider ? options!.provider.provider : options!.provider const block = 'block' in options!.provider ? options!.provider.block : undefined @@ -1298,6 +1299,7 @@ async function recoverTopology( }, weight: topology.weight, } + } } } } else if (isRawNestedLeaf(topology)) { diff --git a/packages/wallet/primitives/src/utils.ts b/packages/wallet/primitives/src/utils.ts index 7a70fb087..3a2b28d46 100644 --- a/packages/wallet/primitives/src/utils.ts +++ b/packages/wallet/primitives/src/utils.ts @@ -1,4 +1,4 @@ -import { AbiParameters, Bytes, Hash, Hex } from 'ox' +import { Bytes } from 'ox' export function minBytesFor(val: bigint): number { return Math.ceil(val.toString(16).length / 2) @@ -7,7 +7,7 @@ export function minBytesFor(val: bigint): number { // ERC-2098 export function packRSY({ r, s, yParity }: { r: bigint; s: bigint; yParity: number }): Bytes.Bytes { const rBytes = Bytes.padLeft(Bytes.fromNumber(r), 32) - let sBytes = Bytes.padLeft(Bytes.fromNumber(s), 32) + const sBytes = Bytes.padLeft(Bytes.fromNumber(s), 32) if (yParity % 2 === 1) { sBytes[0]! |= 0x80 } diff --git a/packages/wallet/primitives/test/address.test.ts b/packages/wallet/primitives/test/address.test.ts index 38ac16ccc..b004dc96d 100644 --- a/packages/wallet/primitives/test/address.test.ts +++ b/packages/wallet/primitives/test/address.test.ts @@ -88,7 +88,7 @@ describe('Address', () => { }) it('should work with Dev1 context', () => { - const { stage2, ...dev1Context } = Dev1 + const { stage2: _, ...dev1Context } = Dev1 const address = from(sampleConfig, dev1Context) expect(() => Address.assert(address)).not.toThrow() @@ -96,53 +96,53 @@ describe('Address', () => { }) it('should work with Dev2 context', () => { - const { stage2, ...dev2Context } = Dev2 + 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: _, ...dev1Context } = 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, ...rc3Context } = Rc3 + 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: _, ...dev2Context } = 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, ...rc4Context } = Rc4 + 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: _, ...dev2Context } = 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, ...rc5Context } = Rc5 + 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: _, ...dev2Context } = Dev2 + const { stage2: _stage2_2, ...dev2Context } = Dev2 const dev2Address = from(sampleConfig, dev2Context) expect(address).not.toBe(dev2Address) }) diff --git a/packages/wallet/primitives/test/attestation.test.ts b/packages/wallet/primitives/test/attestation.test.ts index 41a75da07..3708132a7 100644 --- a/packages/wallet/primitives/test/attestation.test.ts +++ b/packages/wallet/primitives/test/attestation.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { Address, Bytes, Hash, Hex } from 'ox' +import { Bytes, Hash } from 'ox' import { Attestation, diff --git a/packages/wallet/primitives/test/config.test.ts b/packages/wallet/primitives/test/config.test.ts index 4dbaa0f84..b4ff8e6d9 100644 --- a/packages/wallet/primitives/test/config.test.ts +++ b/packages/wallet/primitives/test/config.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { Address, Bytes, Hash, Hex } from 'ox' +import { Bytes } from 'ox' import { Config, diff --git a/packages/wallet/primitives/test/passkeys.test.ts b/packages/wallet/primitives/test/passkeys.test.ts index b6aa0990f..ddfd59b9e 100644 --- a/packages/wallet/primitives/test/passkeys.test.ts +++ b/packages/wallet/primitives/test/passkeys.test.ts @@ -104,13 +104,6 @@ describe('Passkeys', () => { y: testPublicKeyY, } - const samplePublicKeyWithHashMetadata: PublicKey = { - requireUserVerification: true, - x: testPublicKeyX, - y: testPublicKeyY, - metadata: testMetadataHash, - } - // Realistic authenticator data based on WebAuthn spec and ox patterns // This represents actual WebAuthn authenticator data structure const sampleAuthenticatorData = Bytes.fromHex( @@ -196,7 +189,7 @@ describe('Passkeys', () => { { name: 'special chars', credentialId: '!@#$%^&*()_+{}|:"<>?[]\\;\',./' }, ] - testCases.forEach(({ name, credentialId }) => { + testCases.forEach(({ credentialId }) => { const metadata: PasskeyMetadata = { credentialId } const tree = metadataTree(metadata) expect(GenericTree.isLeaf(tree)).toBe(true) @@ -709,7 +702,7 @@ describe('Passkeys', () => { }, ] - specialCharTests.forEach(({ name, credentialId, clientData }) => { + specialCharTests.forEach(({ credentialId, clientData }) => { if (credentialId) { const unicodeMetadata: PasskeyMetadata = { credentialId } const tree = metadataTree(unicodeMetadata) @@ -787,7 +780,7 @@ describe('Passkeys', () => { { userVerification: true, metadata: true, description: 'User verification with metadata' }, ] - testCombinations.forEach(({ userVerification, metadata, description }) => { + testCombinations.forEach(({ userVerification, metadata }) => { const pubKey = createValidPublicKey({ requireUserVerification: userVerification, ...(metadata && { metadata: samplePasskeyMetadata }), diff --git a/packages/wallet/primitives/test/payload.test.ts b/packages/wallet/primitives/test/payload.test.ts index 72b831d22..33884dbdb 100644 --- a/packages/wallet/primitives/test/payload.test.ts +++ b/packages/wallet/primitives/test/payload.test.ts @@ -1,6 +1,5 @@ -import { describe, expect, it, vi } from 'vitest' -import { Address, Bytes, Hash, Hex } from 'ox' -import { UserOperation } from 'ox/erc4337' +import { describe, expect, it } from 'vitest' +import { Address, Bytes, Hex } from 'ox' import { KIND_TRANSACTIONS, @@ -17,11 +16,7 @@ import { Digest, SessionImplicitAuthorize, Calls4337_07, - Recovery, - MayRecoveryPayload, - Payload, Parented, - TypedDataToSign, SolidityDecoded, fromMessage, fromConfigUpdate, diff --git a/packages/wallet/primitives/test/precondition.test.ts b/packages/wallet/primitives/test/precondition.test.ts index c994e8a52..84f919d62 100644 --- a/packages/wallet/primitives/test/precondition.test.ts +++ b/packages/wallet/primitives/test/precondition.test.ts @@ -1,7 +1,6 @@ import { describe, expect, it } from 'vitest' import { - Precondition, NativeBalancePrecondition, Erc20BalancePrecondition, Erc20ApprovalPrecondition, @@ -10,7 +9,6 @@ import { Erc1155BalancePrecondition, Erc1155ApprovalPrecondition, AnyPrecondition, - IntentPrecondition, isValidPreconditionType, createPrecondition, createIntentPrecondition, @@ -225,13 +223,13 @@ describe('Precondition', () => { }) it('should throw for null precondition', () => { - expect(() => createPrecondition(null as any)).toThrow( + 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 any)).toThrow( + expect(() => createPrecondition(undefined as unknown as AnyPrecondition)).toThrow( "Invalid precondition object: missing or invalid 'type' property.", ) }) @@ -240,7 +238,7 @@ describe('Precondition', () => { const invalidPrecondition = { address: testAddress, min: testMinAmount, - } as any + } as unknown as AnyPrecondition expect(() => createPrecondition(invalidPrecondition)).toThrow( "Invalid precondition object: missing or invalid 'type' property.", ) @@ -250,7 +248,7 @@ describe('Precondition', () => { const invalidPrecondition = { type: 'invalid-type', address: testAddress, - } as any + } as unknown as AnyPrecondition expect(() => createPrecondition(invalidPrecondition)).toThrow( "Invalid precondition object: missing or invalid 'type' property.", ) @@ -260,7 +258,7 @@ describe('Precondition', () => { const invalidPrecondition = { type: 123, address: testAddress, - } as any + } as unknown as AnyPrecondition expect(() => createPrecondition(invalidPrecondition)).toThrow( "Invalid precondition object: missing or invalid 'type' property.", ) @@ -386,7 +384,7 @@ describe('Precondition', () => { const invalidPrecondition = { type: 'invalid-type', address: testAddress, - } as any + } as unknown as AnyPrecondition expect(() => createIntentPrecondition(invalidPrecondition)).toThrow('Invalid precondition type: invalid-type') }) diff --git a/packages/wallet/primitives/test/signature.test.ts b/packages/wallet/primitives/test/signature.test.ts index 8738b9bcf..e3261948a 100644 --- a/packages/wallet/primitives/test/signature.test.ts +++ b/packages/wallet/primitives/test/signature.test.ts @@ -14,7 +14,6 @@ import { FLAG_SIGNATURE_SAPIENT, FLAG_SIGNATURE_SAPIENT_COMPACT, RSY, - SignatureOfSignerLeafEthSign, SignatureOfSignerLeafHash, SignatureOfSignerLeafErc1271, SignatureOfSapientSignerLeaf, @@ -42,7 +41,7 @@ import { recover, } from '../src/signature.js' import { packRSY } from '../src/utils.js' -import { Config, SignerLeaf, SapientSignerLeaf } from '../src/config.js' +import { SignerLeaf, SapientSignerLeaf, SubdigestLeaf, Topology, NestedLeaf } from '../src/config.js' import * as Payload from '../src/payload.js' import { ChainId } from '../src/network.js' @@ -63,11 +62,6 @@ describe('Signature', () => { ...sampleRSY, } - const sampleEthSignSignature: SignatureOfSignerLeafEthSign = { - type: 'eth_sign', - ...sampleRSY, - } - const sampleErc1271Signature: SignatureOfSignerLeafErc1271 = { type: 'erc1271', address: testAddress, @@ -377,7 +371,7 @@ describe('Signature', () => { expect(result.nodes).toHaveLength(1) expect(result.leftover).toHaveLength(0) - const node = result.nodes[0] as any + const node = result.nodes[0] as SubdigestLeaf expect(node.type).toBe('subdigest') expect(node.digest).toBe(digest) }) @@ -517,7 +511,7 @@ describe('Signature', () => { }) it('should throw for invalid topology', () => { - expect(() => encodeTopology({} as any)).toThrow('Invalid topology') + expect(() => encodeTopology({} as Topology)).toThrow('Invalid topology') }) }) @@ -696,9 +690,9 @@ describe('Signature', () => { const signatureProvider = () => sampleHashSignature - const result = fillLeaves(nestedTopology, signatureProvider) - expect((result as any).type).toBe('nested') - expect((result as any).tree).toHaveProperty('signature') + const result = fillLeaves(nestedTopology, signatureProvider) as NestedLeaf + expect(result.type).toBe('nested') + expect(result.tree).toHaveProperty('signature') }) it('should handle topology without signatures', () => { @@ -751,12 +745,12 @@ describe('Signature', () => { const result = fillLeaves(binaryTree, signatureProvider) expect(Array.isArray(result)).toBe(true) - expect((result as any)[0]).toHaveProperty('signature') - expect((result as any)[1]).toHaveProperty('signature') + expect(result[0]).toHaveProperty('signature') + expect(result[1]).toHaveProperty('signature') }) it('should throw for invalid topology', () => { - expect(() => fillLeaves({} as any, () => undefined)).toThrow('Invalid topology') + expect(() => fillLeaves({} as Topology, () => undefined)).toThrow('Invalid topology') }) }) }) @@ -917,7 +911,7 @@ describe('Signature', () => { }, ] - signatures.forEach(({ topology, name }) => { + signatures.forEach(({ topology }) => { const signature = { noChainId: false, configuration: { @@ -1760,7 +1754,7 @@ describe('Signature', () => { }, ] - for (const { name, payload } of payloadTypes) { + for (const { payload } of payloadTypes) { const sapientSignature: RawSignature = { noChainId: false, configuration: { diff --git a/packages/wallet/primitives/test/utils.test.ts b/packages/wallet/primitives/test/utils.test.ts index 76ba462f0..dc8392c32 100644 --- a/packages/wallet/primitives/test/utils.test.ts +++ b/packages/wallet/primitives/test/utils.test.ts @@ -248,7 +248,7 @@ describe('Utils', () => { it('should revive Uint8Array values', () => { const reviver = createJSONReviver() - const result = reviver('test', { __uint8array: [1, 2, 3, 255] }) + const result = reviver('test', { __uint8array: [1, 2, 3, 255] }) as Uint8Array expect(result).toBeInstanceOf(Uint8Array) expect(Array.from(result)).toEqual([1, 2, 3, 255]) @@ -314,7 +314,7 @@ describe('Utils', () => { it('should handle empty Uint8Array', () => { const reviver = createJSONReviver() - const result = reviver('test', { __uint8array: [] }) + const result = reviver('test', { __uint8array: [] }) as Uint8Array expect(result).toBeInstanceOf(Uint8Array) expect(result.length).toBe(0) @@ -401,7 +401,7 @@ describe('Utils', () => { it('should deserialize objects with BigInt', () => { const json = '{"value":{"__bigint":"0x75bcd15"},"name":"test"}' - const result = fromJSON(json) + const result = fromJSON(json) as { value: bigint; name: string } expect(result.value).toBe(123456789n) expect(result.name).toBe('test') @@ -409,7 +409,7 @@ describe('Utils', () => { it('should deserialize objects with Uint8Array', () => { const json = '{"data":{"__uint8array":[1,2,3]},"name":"test"}' - const result = fromJSON(json) + const result = fromJSON(json) as { data: Uint8Array; name: string } expect(result.data).toBeInstanceOf(Uint8Array) expect(Array.from(result.data)).toEqual([1, 2, 3]) diff --git a/packages/wallet/wdk/CHANGELOG.md b/packages/wallet/wdk/CHANGELOG.md index ea802f9c4..6793c3f98 100644 --- a/packages/wallet/wdk/CHANGELOG.md +++ b/packages/wallet/wdk/CHANGELOG.md @@ -1,5 +1,389 @@ # @0xsequence/wallet-wdk +## 3.1.1 + +### Patch Changes + +- WDK fix +- Updated dependencies + - @0xsequence/guard@3.0.12 + - @0xsequence/identity-instrument@3.0.12 + - @0xsequence/relayer@3.1.1 + - @0xsequence/wallet-core@3.0.12 + - @0xsequence/wallet-primitives@3.0.12 + +## 3.1.0 + +### Minor Changes + +- 55121af: Carry `sponsored` / `failed` through `StandardRelayerOption`. + + `StandardRelayerOption` now exposes two optional fields: + - `sponsored?: boolean` — populated from the relayer SDK's new `feeOptions` + return field. `true` means the server confirmed an active sponsorship policy + match; `false` means it did not (or the quote failed). + - `failed?: boolean` — `true` when the relayer's `feeOptions` call was swallowed + due to a transport or server error. + + Both fields are populated on the empty-options construction branch and the + per-option mapping branch in `transactions.ts`. The new `isStandardRelayerOption` + and `isERC4337RelayerOption` runtime helpers are now re-exported from the + package root for consumers that need to narrow `RelayerOption` before reading + the new fields. + + UI / wallet consumers that previously classified sponsorship by "no `feeOption` + attached" should switch to `sponsored === true` so a real subsidy is no longer + indistinguishable from a swallowed `/FeeOptions` error. + +### Patch Changes + +- Fix for relayer sponsored fees +- Updated dependencies +- Updated dependencies [55121af] + - @0xsequence/guard@3.0.11 + - @0xsequence/identity-instrument@3.0.11 + - @0xsequence/relayer@3.1.0 + - @0xsequence/wallet-core@3.0.11 + - @0xsequence/wallet-primitives@3.0.11 + +## 3.0.10 + +### Patch Changes + +- Minor network updates, relayer interface +- Updated dependencies + - @0xsequence/guard@3.0.10 + - @0xsequence/identity-instrument@3.0.10 + - @0xsequence/relayer@3.0.10 + - @0xsequence/wallet-core@3.0.10 + - @0xsequence/wallet-primitives@3.0.10 + +## 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 diff --git a/packages/wallet/wdk/eslint.config.js b/packages/wallet/wdk/eslint.config.js new file mode 100644 index 000000000..d10bbd1e9 --- /dev/null +++ b/packages/wallet/wdk/eslint.config.js @@ -0,0 +1,12 @@ +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 index 87d1a45a5..875f24c81 100644 --- a/packages/wallet/wdk/package.json +++ b/packages/wallet/wdk/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-wdk", - "version": "3.0.0-beta.5", + "version": "3.1.1", "license": "Apache-2.0", "type": "module", "publishConfig": { @@ -12,9 +12,10 @@ "dev": "tsc --watch", "test": "vitest run && npm run test:ssr", "test:coverage": "vitest run --coverage", - "test:ssr": "node test/test-ssr-safety.mjs", + "test:ssr": "node test/test-ssr-safety.js", "typecheck": "tsc --noEmit", - "clean": "rimraf dist" + "clean": "rimraf dist", + "lint": "eslint . --max-warnings 0" }, "exports": { ".": { @@ -23,14 +24,15 @@ } }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^24.10.1", - "@vitest/coverage-v8": "^4.0.14", - "dotenv": "^17.2.3", + "@types/node": "^25.3.0", + "@vitest/coverage-v8": "^4.0.18", + "dotenv": "^17.3.1", "fake-indexeddb": "^6.2.5", - "happy-dom": "^20.0.10", - "typescript": "^5.9.3", - "vitest": "^4.0.14" + "happy-dom": "^20.8.9", + "typescript": "^6.0.3", + "vitest": "^4.0.18" }, "dependencies": { "@0xsequence/guard": "workspace:^", @@ -42,6 +44,6 @@ "idb": "^8.0.3", "jwt-decode": "^4.0.0", "ox": "^0.9.17", - "uuid": "^13.0.0" + "uuid": "^14.0.0" } } diff --git a/packages/wallet/wdk/src/dbs/auth-commitments.ts b/packages/wallet/wdk/src/dbs/auth-commitments.ts index a3f360639..613d4bd57 100644 --- a/packages/wallet/wdk/src/dbs/auth-commitments.ts +++ b/packages/wallet/wdk/src/dbs/auth-commitments.ts @@ -1,8 +1,13 @@ -import { Generic, Migration } from './generic.js' +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}` @@ -10,9 +15,7 @@ export type AuthCommitment = { verifier?: string challenge?: string target: string - isSignUp: boolean - signer?: string -} +} & ({ type: 'auth' } | { type: 'reauth'; signer: string } | { type: 'add-signer'; wallet: string }) export class AuthCommitments extends Generic { constructor(dbName: string = 'sequence-auth-commitments') { diff --git a/packages/wallet/wdk/src/dbs/auth-keys.ts b/packages/wallet/wdk/src/dbs/auth-keys.ts index 56cf1ddf9..690d18cd9 100644 --- a/packages/wallet/wdk/src/dbs/auth-keys.ts +++ b/packages/wallet/wdk/src/dbs/auth-keys.ts @@ -1,5 +1,6 @@ -import { Generic, Migration } from './generic.js' +import { Generic } from './generic.js' import { IDBPDatabase, IDBPTransaction } from 'idb' +import type { WdkEnv } from '../env.js' const TABLE_NAME = 'auth-keys' @@ -11,9 +12,12 @@ export type AuthKey = { } export class AuthKeys extends Generic { - private expirationTimers = new Map() + private expirationTimers = new Map>() - constructor(dbName: string = 'sequence-auth-keys') { + constructor( + dbName: string = 'sequence-auth-keys', + private readonly env?: WdkEnv, + ) { super(dbName, TABLE_NAME, 'address', [ ( db: IDBPDatabase, @@ -64,7 +68,10 @@ export class AuthKeys extends Generic { if (result !== undefined) { return result } else if (attempt < 2) { - await new Promise((resolve) => setTimeout(resolve, 50)) + 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 { @@ -108,7 +115,11 @@ export class AuthKeys extends Generic { await this.del(authKey.address.toLowerCase()) return } - const timer = window.setTimeout(() => { + 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) @@ -118,7 +129,10 @@ export class AuthKeys extends Generic { private clearExpiration(address: string): void { const timer = this.expirationTimers.get(address.toLowerCase()) if (timer) { - window.clearTimeout(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/index.ts b/packages/wallet/wdk/src/dbs/index.ts index 797997b94..1cf7f30f0 100644 --- a/packages/wallet/wdk/src/dbs/index.ts +++ b/packages/wallet/wdk/src/dbs/index.ts @@ -1,4 +1,4 @@ -export type { AuthCommitment } from './auth-commitments.js' +export type { AuthCommitment, CommitAuthArgs } from './auth-commitments.js' export { AuthCommitments } from './auth-commitments.js' export type { AuthKey } from './auth-keys.js' diff --git a/packages/wallet/wdk/src/dbs/messages.ts b/packages/wallet/wdk/src/dbs/messages.ts index 8a076ea05..54459f06f 100644 --- a/packages/wallet/wdk/src/dbs/messages.ts +++ b/packages/wallet/wdk/src/dbs/messages.ts @@ -1,5 +1,5 @@ import { Message } from '../sequence/types/message-request.js' -import { Generic, Migration } from './generic.js' +import { Generic } from './generic.js' import { IDBPDatabase, IDBPTransaction } from 'idb' const TABLE_NAME = 'messages' diff --git a/packages/wallet/wdk/src/dbs/passkey-credentials.ts b/packages/wallet/wdk/src/dbs/passkey-credentials.ts index 9fdb2abc4..5e9e6bc31 100644 --- a/packages/wallet/wdk/src/dbs/passkey-credentials.ts +++ b/packages/wallet/wdk/src/dbs/passkey-credentials.ts @@ -1,4 +1,4 @@ -import { Generic, Migration } from './generic.js' +import { Generic } from './generic.js' import { IDBPDatabase, IDBPTransaction } from 'idb' import { Address } from 'ox' import { Extensions } from '@0xsequence/wallet-primitives' diff --git a/packages/wallet/wdk/src/dbs/recovery.ts b/packages/wallet/wdk/src/dbs/recovery.ts index 164c7082b..e035825f3 100644 --- a/packages/wallet/wdk/src/dbs/recovery.ts +++ b/packages/wallet/wdk/src/dbs/recovery.ts @@ -1,4 +1,4 @@ -import { Generic, Migration } from './generic.js' +import { Generic } from './generic.js' import { QueuedRecoveryPayload } from '../sequence/types/recovery.js' import { IDBPDatabase, IDBPTransaction } from 'idb' diff --git a/packages/wallet/wdk/src/dbs/signatures.ts b/packages/wallet/wdk/src/dbs/signatures.ts index a108d113c..3f7328187 100644 --- a/packages/wallet/wdk/src/dbs/signatures.ts +++ b/packages/wallet/wdk/src/dbs/signatures.ts @@ -1,5 +1,5 @@ import { BaseSignatureRequest } from '../sequence/index.js' -import { Generic, Migration } from './generic.js' +import { Generic } from './generic.js' import { IDBPDatabase, IDBPTransaction } from 'idb' const TABLE_NAME = 'envelopes' diff --git a/packages/wallet/wdk/src/dbs/transactions.ts b/packages/wallet/wdk/src/dbs/transactions.ts index d9019702d..48f7680c8 100644 --- a/packages/wallet/wdk/src/dbs/transactions.ts +++ b/packages/wallet/wdk/src/dbs/transactions.ts @@ -1,5 +1,5 @@ import { Transaction } from '../sequence/types/transaction-request.js' -import { Generic, Migration } from './generic.js' +import { Generic } from './generic.js' import { IDBPDatabase, IDBPTransaction } from 'idb' const TABLE_NAME = 'transactions' diff --git a/packages/wallet/wdk/src/dbs/wallets.ts b/packages/wallet/wdk/src/dbs/wallets.ts index bd9829eb5..4b1777610 100644 --- a/packages/wallet/wdk/src/dbs/wallets.ts +++ b/packages/wallet/wdk/src/dbs/wallets.ts @@ -1,4 +1,4 @@ -import { Generic, Migration } from './generic.js' +import { Generic } from './generic.js' import { Wallet } from '../sequence/types/wallet.js' import { IDBPDatabase, IDBPTransaction } from 'idb' diff --git a/packages/wallet/wdk/src/env.ts b/packages/wallet/wdk/src/env.ts new file mode 100644 index 000000000..4e3cecb1a --- /dev/null +++ b/packages/wallet/wdk/src/env.ts @@ -0,0 +1,58 @@ +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 index fdc53ca41..0cb74bbbd 100644 --- a/packages/wallet/wdk/src/identity/signer.ts +++ b/packages/wallet/wdk/src/identity/signer.ts @@ -1,17 +1,22 @@ -import { Address, Signature, Hex, Bytes, PersonalMessage } from 'ox' -import { Signers, State } from '@0xsequence/wallet-core' -import { IdentityInstrument, KeyType } from '@0xsequence/identity-instrument' +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): Identity.AuthKey { +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 window.crypto.subtle.sign( + const authKeySignature = await resolvedCrypto.subtle.sign( { name: 'ECDSA', hash: 'SHA-256', @@ -28,6 +33,7 @@ export class IdentitySigner implements Signers.Signer { constructor( readonly identityInstrument: IdentityInstrument, readonly authKey: AuthKey, + private readonly crypto?: CryptoLike, ) {} get address(): Address.Address { @@ -47,7 +53,7 @@ export class IdentitySigner implements Signers.Signer { } async signDigest(digest: Bytes.Bytes): Promise { - const sigHex = await this.identityInstrument.sign(toIdentityAuthKey(this.authKey), digest) + const sigHex = await this.identityInstrument.sign(toIdentityAuthKey(this.authKey, this.crypto), digest) const sig = Signature.fromHex(sigHex) return { type: 'hash', @@ -55,7 +61,7 @@ export class IdentitySigner implements Signers.Signer { } } - 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/wdk/src/index.ts b/packages/wallet/wdk/src/index.ts index 973ec785a..003abdd72 100644 --- a/packages/wallet/wdk/src/index.ts +++ b/packages/wallet/wdk/src/index.ts @@ -1,2 +1,3 @@ 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 index a1640a7cf..f95117109 100644 --- a/packages/wallet/wdk/src/sequence/cron.ts +++ b/packages/wallet/wdk/src/sequence/cron.ts @@ -17,12 +17,14 @@ export class Cron { 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() } @@ -33,7 +35,11 @@ export class Cron { private start() { if (this.isStopping) return this.executeCheckJobsChain() - this.checkInterval = setInterval(() => this.executeCheckJobsChain(), 60 * 1000) + const setIntervalFn = this.env.timers?.setInterval ?? (globalThis as any).setInterval + if (!setIntervalFn) { + return + } + this.checkInterval = setIntervalFn(() => this.executeCheckJobsChain(), 60 * 1000) } /** @@ -58,7 +64,10 @@ export class Cron { this.isStopping = true if (this.checkInterval) { - clearInterval(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.') } @@ -104,48 +113,22 @@ export class Cron { } try { - await navigator.locks.request('sequence-cron-jobs', async (lock: Lock | null) => { - if (this.isStopping) { - return - } - if (!lock) { - return - } - - const now = Date.now() - const storage = await this.getStorageState() - - for (const [id, job] of this.jobs) { + 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) { - break + return } - - 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 (error instanceof DOMException && error.name === 'AbortError') { - this.shared.modules.logger.log(`Cron: Job ${id} was aborted.`) - } else { - console.error(`Cron job ${id} failed:`, error) - } - } + if (!lock) { + return } - } - - if (!this.isStopping) { - await this.syncWithStorage() - } - }) + await this.runJobs() + }) + } else { + await this.runJobs() + } } catch (error) { - if (error instanceof DOMException && error.name === 'AbortError') { + 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) @@ -153,13 +136,51 @@ export class Cron { } } + 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 state = localStorage.getItem(this.STORAGE_KEY) + const storage = this.env.storage + if (!storage) { + return new Map() + } + const state = storage.getItem(this.STORAGE_KEY) return new Map(state ? JSON.parse(state) : []) } @@ -168,7 +189,19 @@ export class Cron { */ 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 }]) - localStorage.setItem(this.STORAGE_KEY, JSON.stringify(state)) + 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/handlers/authcode-pkce.ts b/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts index 0b4706c0e..308e2809f 100644 --- a/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts +++ b/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts @@ -1,10 +1,12 @@ -import { Hex, Address, Bytes } from 'ox' +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( @@ -16,31 +18,37 @@ export class AuthCodePkceHandler extends AuthCodeHandler implements Handler { signatures: Signatures, commitments: Db.AuthCommitments, authKeys: Db.AuthKeys, + env?: WdkEnv, ) { - super(signupKind, issuer, oauthUrl, audience, nitro, signatures, commitments, authKeys) + super(signupKind, issuer, oauthUrl, audience, nitro, signatures, commitments, authKeys, env) } - public async commitAuth(target: string, isSignUp: boolean, state?: string, signer?: string) { + public async commitAuth(target: string, args: CommitAuthArgs) { let challenge = new Identity.AuthCodePkceChallenge(this.issuer, this.audience, this.redirectUri) - if (signer) { - challenge = challenge.withSigner({ address: signer, keyType: Identity.KeyType.Ethereum_Secp256k1 }) + if (args.type === 'reauth') { + challenge = challenge.withSigner({ address: args.signer, keyType: Identity.KeyType.Ethereum_Secp256k1 }) } const { verifier, loginHint, challenge: codeChallenge } = await this.nitroCommitVerifier(challenge) - if (!state) { - state = Hex.fromBytes(Bytes.random(32)) - } + const state = args.state ?? Hex.fromBytes(Bytes.random(32)) - await this.commitments.set({ + const base = { id: state, - kind: this.signupKind, + kind: this.signupKind as Db.AuthCommitment['kind'], verifier, challenge: codeChallenge, target, metadata: {}, - isSignUp, - }) + } + + 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 = new URLSearchParams({ + const searchParams = this.serializeQuery({ code_challenge: codeChallenge, code_challenge_method: 'S256', client_id: this.audience, @@ -51,7 +59,7 @@ export class AuthCodePkceHandler extends AuthCodeHandler implements Handler { state, }) - return `${this.oauthUrl}?${searchParams.toString()}` + return `${this.oauthUrl}?${searchParams}` } public async completeAuth( diff --git a/packages/wallet/wdk/src/sequence/handlers/authcode.ts b/packages/wallet/wdk/src/sequence/handlers/authcode.ts index f73f9ec5d..4b5d5922b 100644 --- a/packages/wallet/wdk/src/sequence/handlers/authcode.ts +++ b/packages/wallet/wdk/src/sequence/handlers/authcode.ts @@ -6,6 +6,9 @@ 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 = '' @@ -19,11 +22,17 @@ export class AuthCodeHandler extends IdentityHandler implements Handler { signatures: Signatures, protected readonly commitments: Db.AuthCommitments, authKeys: Db.AuthKeys, + env?: WdkEnv, ) { - super(nitro, authKeys, signatures, Identity.IdentityType.OIDC) + 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 } @@ -31,29 +40,33 @@ export class AuthCodeHandler extends IdentityHandler implements Handler { this.redirectUri = redirectUri } - public async commitAuth(target: string, isSignUp: boolean, state?: string, signer?: string) { - if (!state) { - state = Hex.fromBytes(Bytes.random(32)) - } + public async commitAuth(target: string, args: CommitAuthArgs) { + const state = args.state ?? Hex.fromBytes(Bytes.random(32)) - await this.commitments.set({ + const base = { id: state, - kind: this.signupKind, - signer, + kind: this.signupKind as Db.AuthCommitment['kind'], target, metadata: {}, - isSignUp, - }) + } + + 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 = new URLSearchParams({ + const searchParams = this.serializeQuery({ client_id: this.audience, redirect_uri: this.redirectUri, response_type: 'code', - scope: 'openid profile email', state, + ...(this.signupKind === 'apple' ? {} : { scope: 'openid profile email' }), }) - return `${this.oauthUrl}?${searchParams.toString()}` + return `${this.oauthUrl}?${searchParams}` } public async completeAuth( @@ -61,7 +74,7 @@ export class AuthCodeHandler extends IdentityHandler implements Handler { code: string, ): Promise<[IdentitySigner, { [key: string]: string }]> { let challenge = new Identity.AuthCodeChallenge(this.issuer, this.audience, this.redirectUri, code) - if (commitment.signer) { + if (commitment.type === 'reauth') { challenge = challenge.withSigner({ address: commitment.signer, keyType: Identity.KeyType.Ethereum_Secp256k1 }) } await this.nitroCommitVerifier(challenge) @@ -94,10 +107,33 @@ export class AuthCodeHandler extends IdentityHandler implements Handler { status: 'actionable', message: 'request-redirect', handle: async () => { - const url = await this.commitAuth(window.location.pathname, false, request.id, address) - window.location.href = url + 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 index 9da0f8971..4d525d37e 100644 --- a/packages/wallet/wdk/src/sequence/handlers/devices.ts +++ b/packages/wallet/wdk/src/sequence/handlers/devices.ts @@ -13,7 +13,7 @@ export class DevicesHandler implements Handler { private readonly devices: Devices, ) {} - onStatusChange(cb: () => void): () => void { + onStatusChange(_cb: () => void): () => void { return () => {} } diff --git a/packages/wallet/wdk/src/sequence/handlers/guard.ts b/packages/wallet/wdk/src/sequence/handlers/guard.ts index bbe8c2698..b495c95d8 100644 --- a/packages/wallet/wdk/src/sequence/handlers/guard.ts +++ b/packages/wallet/wdk/src/sequence/handlers/guard.ts @@ -35,7 +35,7 @@ export class GuardHandler implements Handler { this.onPromptCode = undefined } - onStatusChange(cb: () => void): () => void { + onStatusChange(_cb: () => void): () => void { return () => {} } @@ -87,8 +87,9 @@ export class GuardHandler implements Handler { address, handler: this, status: 'ready', - handle: () => - new Promise(async (resolve, reject) => { + 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) @@ -106,7 +107,8 @@ export class GuardHandler implements Handler { reject(e) } } - }), + }) + }, } } } diff --git a/packages/wallet/wdk/src/sequence/handlers/identity.ts b/packages/wallet/wdk/src/sequence/handlers/identity.ts index f18245222..a991e8863 100644 --- a/packages/wallet/wdk/src/sequence/handlers/identity.ts +++ b/packages/wallet/wdk/src/sequence/handlers/identity.ts @@ -1,9 +1,10 @@ -import { Hex, Bytes } from 'ox' +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 @@ -19,12 +20,17 @@ export const identityTypeToHex = (identityType?: Identity.IdentityType): Hex.Hex } 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) @@ -37,7 +43,7 @@ export class IdentityHandler { throw new Error('no-auth-key') } - const res = await this.nitro.commitVerifier(toIdentityAuthKey(authKey), challenge) + const res = await this.nitro.commitVerifier(toIdentityAuthKey(authKey, this.env.crypto), challenge) return res } @@ -47,7 +53,7 @@ export class IdentityHandler { throw new Error('no-auth-key') } - const res = await this.nitro.completeAuth(toIdentityAuthKey(authKey), challenge) + 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 @@ -55,7 +61,7 @@ export class IdentityHandler { await this.authKeys.delBySigner(authKey.identitySigner) await this.authKeys.set(authKey) - const signer = new IdentitySigner(this.nitro, authKey) + const signer = new IdentitySigner(this.nitro, authKey, this.env.crypto) return { signer, email: res.identity.email } } @@ -72,13 +78,21 @@ export class IdentityHandler { if (!authKey) { return undefined } - return new IdentitySigner(this.nitro, authKey) + 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 keyPair = await window.crypto.subtle.generateKey( + 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', @@ -86,7 +100,7 @@ export class IdentityHandler { false, ['sign', 'verify'], ) - const publicKey = await window.crypto.subtle.exportKey('raw', keyPair.publicKey) + const publicKey = await crypto.subtle.exportKey('raw', keyPair.publicKey) authKey = { address: Hex.fromBytes(new Uint8Array(publicKey)), identitySigner: '', diff --git a/packages/wallet/wdk/src/sequence/handlers/idtoken.ts b/packages/wallet/wdk/src/sequence/handlers/idtoken.ts new file mode 100644 index 000000000..58d5b5df9 --- /dev/null +++ b/packages/wallet/wdk/src/sequence/handlers/idtoken.ts @@ -0,0 +1,146 @@ +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 index 0cac943b9..9c1b1f0f5 100644 --- a/packages/wallet/wdk/src/sequence/handlers/index.ts +++ b/packages/wallet/wdk/src/sequence/handlers/index.ts @@ -3,4 +3,5 @@ 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 index 143edc0cd..3d932a533 100644 --- a/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts +++ b/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts @@ -93,8 +93,9 @@ export class MnemonicHandler implements Handler { handler: this, status: 'actionable', message: 'enter-mnemonic', - handle: () => - new Promise(async (resolve, reject) => { + 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) { @@ -117,7 +118,8 @@ export class MnemonicHandler implements Handler { 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 index f037189cb..42bf85c9e 100644 --- a/packages/wallet/wdk/src/sequence/handlers/otp.ts +++ b/packages/wallet/wdk/src/sequence/handlers/otp.ts @@ -8,6 +8,7 @@ import { SignerUnavailable, SignerReady, SignerActionable, BaseSignatureRequest 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 @@ -18,8 +19,8 @@ export class OtpHandler extends IdentityHandler implements Handler { private onPromptOtp: undefined | PromptOtpHandler - constructor(nitro: Identity.IdentityInstrument, signatures: Signatures, authKeys: Db.AuthKeys) { - super(nitro, authKeys, signatures, Identity.IdentityType.Email) + constructor(nitro: Identity.IdentityInstrument, signatures: Signatures, authKeys: Db.AuthKeys, env?: WdkEnv) { + super(nitro, authKeys, signatures, Identity.IdentityType.Email, env) } public registerUI(onPromptOtp: PromptOtpHandler) { @@ -84,7 +85,7 @@ export class OtpHandler extends IdentityHandler implements Handler { try { await this.handleAuth(challenge, onPromptOtp) return true - } catch (e) { + } catch { return false } }, @@ -95,6 +96,7 @@ export class OtpHandler extends IdentityHandler implements Handler { 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) diff --git a/packages/wallet/wdk/src/sequence/handlers/passkeys.ts b/packages/wallet/wdk/src/sequence/handlers/passkeys.ts index da3db7ff3..17c3196ca 100644 --- a/packages/wallet/wdk/src/sequence/handlers/passkeys.ts +++ b/packages/wallet/wdk/src/sequence/handlers/passkeys.ts @@ -1,33 +1,35 @@ -import { Signers, State } from '@0xsequence/wallet-core' +import { State } from '@0xsequence/wallet-core' import { Address, Hex } from 'ox' import { Kinds } from '../types/signer.js' import { Signatures } from '../signatures.js' -import { Extensions } from '@0xsequence/wallet-primitives' +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() + 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 { + onStatusChange(_cb: () => void): () => void { return () => {} } - public addReadySigner(signer: Signers.Passkey.Passkey) { + 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 { + private async loadPasskey(wallet: Address.Address, imageHash: Hex.Hex): Promise { try { - return await Signers.Passkey.Passkey.loadFromWitness(this.stateReader, this.extensions, wallet, imageHash) + return await this.passkeyProvider.loadFromWitness(this.stateReader, this.extensions, wallet, imageHash) } catch (e) { console.warn('Failed to load passkey:', e) return undefined @@ -55,7 +57,7 @@ export class PasskeysHandler implements Handler { } // First check if we have a ready signer that matches the imageHash - let passkey: Signers.Passkey.Passkey | undefined + let passkey: PasskeySigner | undefined // Look for a ready signer with matching imageHash for (const readySigner of this.readySigners.values()) { @@ -91,12 +93,10 @@ export class PasskeysHandler implements Handler { message: 'request-interaction-with-passkey', imageHash: imageHash, handle: async () => { - const signature = await passkey.signSapient( - request.envelope.wallet, - request.envelope.chainId, - request.envelope.payload, - imageHash, + 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, diff --git a/packages/wallet/wdk/src/sequence/handlers/recovery.ts b/packages/wallet/wdk/src/sequence/handlers/recovery.ts index 57921de72..9c0ca654d 100644 --- a/packages/wallet/wdk/src/sequence/handlers/recovery.ts +++ b/packages/wallet/wdk/src/sequence/handlers/recovery.ts @@ -76,7 +76,7 @@ export class RecoveryHandler implements Handler { return true }, } - } catch (e) { + } catch { return { address, handler: this, diff --git a/packages/wallet/wdk/src/sequence/index.ts b/packages/wallet/wdk/src/sequence/index.ts index 37729a477..b3081a2a9 100644 --- a/packages/wallet/wdk/src/sequence/index.ts +++ b/packages/wallet/wdk/src/sequence/index.ts @@ -3,16 +3,23 @@ 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, + CompleteRedirectWithMetadataArgs, + CompleteRedirectMetadataResult, SignupArgs, + AddLoginSignerArgs, + RemoveLoginSignerArgs, LoginToWalletArgs, LoginToMnemonicArgs, LoginToPasskeyArgs, diff --git a/packages/wallet/wdk/src/sequence/manager.ts b/packages/wallet/wdk/src/sequence/manager.ts index 9fe04d13c..c7eecf329 100644 --- a/packages/wallet/wdk/src/sequence/manager.ts +++ b/packages/wallet/wdk/src/sequence/manager.ts @@ -5,6 +5,7 @@ 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' @@ -13,6 +14,7 @@ import { AuthCodePkceHandler, DevicesHandler, Handler, + IdTokenHandler, MnemonicHandler, OtpHandler, PasskeysHandler, @@ -30,13 +32,31 @@ 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 @@ -51,6 +71,9 @@ export type ManagerOptions = { dbPruningInterval?: number + env?: WdkEnv + passkeyProvider?: PasskeyProvider + stateProvider?: State.Provider networks?: Network.Network[] relayers?: Relayer.Relayer[] | (() => Relayer.Relayer[]) @@ -58,6 +81,8 @@ export type ManagerOptions = { 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 @@ -67,7 +92,7 @@ export type ManagerOptions = { identity?: { url?: string - fetch?: typeof window.fetch + fetch?: typeof fetch verifyAttestation?: boolean expectedPcr0?: string[] scope?: string @@ -77,21 +102,79 @@ export type ManagerOptions = { google?: { enabled: boolean clientId: string + authMethod?: 'authcode-pkce' | 'id-token' } apple?: { enabled: boolean clientId: string + authMethod?: 'authcode' | 'id-token' } - customProviders?: { - kind: `custom-${string}` - authMethod: 'id-token' | 'authcode' | 'authcode-pkce' - issuer: string - oauthUrl: string - clientId: string - }[] + 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, @@ -112,7 +195,9 @@ export const ManagerOptionsDefaults = { dbPruningInterval: 1000 * 60 * 60 * 24, // 24 hours - stateProvider: new State.Sequence.Provider(), + passkeyProvider: defaultPasskeyProvider, + + stateProvider: typeof fetch !== 'undefined' ? new State.Sequence.Provider(undefined, fetch) : undefined, networks: Network.ALL, relayers: () => { if (typeof window !== 'undefined') { @@ -122,6 +207,8 @@ export const ManagerOptionsDefaults = { }, bundlers: [], + nonWitnessableSigners: [] as Address.Address[], + guardUrl: 'https://guard.sequence.app', guardAddresses: { wallet: '0x26f3D30F41FA897309Ae804A2AFf15CEb1dA5742', @@ -155,6 +242,7 @@ export const ManagerOptionsDefaults = { defaultRecoverySettings: { requiredDeltaTime: 2592000n, // 30 days (in seconds) minTimestamp: 0n, + includeTestnets: false, }, multiInjectedProviderDiscovery: true, @@ -169,10 +257,12 @@ export const ManagerOptionsDefaults = { google: { enabled: false, clientId: '', + authMethod: 'authcode-pkce' as const, }, apple: { enabled: false, clientId: '', + authMethod: 'authcode' as const, }, }, } @@ -181,17 +271,117 @@ export const CreateWalletOptionsDefaults = { useGuard: false, } -export function applyManagerOptionsDefaults(options?: ManagerOptions) { +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 { - ...ManagerOptionsDefaults, - ...options, - identity: { ...ManagerOptionsDefaults.identity, ...options?.identity }, + 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 = { @@ -220,6 +410,8 @@ export type Sequence = { readonly relayers: Relayer.Relayer[] readonly bundlers: Bundler.Bundler[] + readonly nonWitnessableSigners: ReadonlySet + readonly defaultGuardTopology: Config.Topology readonly defaultRecoverySettings: RecoverySettings @@ -246,6 +438,8 @@ export type Shared = { readonly sequence: Sequence readonly databases: Databases + readonly env: WdkEnv + readonly passkeyProvider: PasskeyProvider readonly handlers: Map @@ -378,7 +572,7 @@ export class Manager { const ops = applyManagerOptionsDefaults(options) // Build relayers list - let relayers: Relayer.Relayer[] = [] + const relayers: Relayer.Relayer[] = [] // Add EIP-6963 relayers if enabled if (ops.multiInjectedProviderDiscovery) { @@ -407,6 +601,10 @@ export class Manager { relayers, bundlers: ops.bundlers, + nonWitnessableSigners: new Set( + (ops.nonWitnessableSigners ?? []).map((address) => address.toLowerCase() as Address.Address), + ), + defaultGuardTopology: ops.defaultGuardTopology, defaultRecoverySettings: ops.defaultRecoverySettings, @@ -428,6 +626,9 @@ export class Manager { pruningInterval: ops.dbPruningInterval, }, + env: ops.env, + passkeyProvider: ops.passkeyProvider, + modules: {} as any, handlers: new Map(), } @@ -460,6 +661,7 @@ export class Manager { modules.signatures, shared.sequence.extensions, shared.sequence.stateProvider, + shared.passkeyProvider, ) shared.handlers.set(Kinds.LoginPasskey, this.passkeysHandler) @@ -482,44 +684,88 @@ export class Manager { 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) + this.otpHandler = new OtpHandler(identityInstrument, modules.signatures, shared.databases.authKeys, shared.env) shared.handlers.set(Kinds.LoginEmailOtp, this.otpHandler) } if (ops.identity.google?.enabled) { - shared.handlers.set( - Kinds.LoginGooglePkce, - 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, - ), - ) + 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) { - 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, - ), - ) + 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': - throw new Error('id-token is not supported yet') + 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, @@ -532,6 +778,7 @@ export class Manager { modules.signatures, shared.databases.authCommitments, shared.databases.authKeys, + shared.env, ), ) break @@ -547,6 +794,7 @@ export class Manager { modules.signatures, shared.databases.authCommitments, shared.databases.authKeys, + shared.env, ), ) break @@ -575,6 +823,20 @@ export class Manager { 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) || (() => {}) } diff --git a/packages/wallet/wdk/src/sequence/messages.ts b/packages/wallet/wdk/src/sequence/messages.ts index 131aae3b1..d8fe480c2 100644 --- a/packages/wallet/wdk/src/sequence/messages.ts +++ b/packages/wallet/wdk/src/sequence/messages.ts @@ -242,7 +242,7 @@ export class Messages implements MessagesInterface { const message = await this.getByMessageOrSignatureId(messageOrSignatureId) await this.shared.databases.signatures.del(message.signatureId) await this.shared.databases.messages.del(message.id) - } catch (error) { + } catch { // Ignore } } diff --git a/packages/wallet/wdk/src/sequence/passkeys-provider.ts b/packages/wallet/wdk/src/sequence/passkeys-provider.ts new file mode 100644 index 000000000..3ed2068fe --- /dev/null +++ b/packages/wallet/wdk/src/sequence/passkeys-provider.ts @@ -0,0 +1,55 @@ +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 index 600fb7674..2e6074364 100644 --- a/packages/wallet/wdk/src/sequence/recovery.ts +++ b/packages/wallet/wdk/src/sequence/recovery.ts @@ -1,12 +1,16 @@ import { Envelope } from '@0xsequence/wallet-core' import { Config, Constants, Extensions, GenericTree, Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex, Provider, RpcTransport } from 'ox' +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. @@ -284,7 +288,7 @@ export class Recovery implements RecoveryInterface { } await this.updateRecoveryModule(modules, (leaves) => { - const next = leaves.filter((l) => l.signer !== address) + const next = leaves.filter((l) => !Address.isEqual(l.signer, address)) if (next.length === 0) { return [ { @@ -514,9 +518,16 @@ export class Recovery implements RecoveryInterface { 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 : true)) + .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)), })) @@ -526,69 +537,167 @@ export class Recovery implements RecoveryInterface { return [] } + const recoveryExtension = this.shared.sequence.extensions.recovery const payloads: QueuedRecoveryPayload[] = [] - for (const signer of signers) { - for (const { chainId, provider } of providers) { - const totalPayloads = await Extensions.Recovery.totalQueuedPayloads( - provider, - this.shared.sequence.extensions.recovery, - wallet, - signer.address, - ) - - for (let i = 0n; i < totalPayloads; i++) { - const payloadHash = await Extensions.Recovery.queuedPayloadHashOf( - provider, - this.shared.sequence.extensions.recovery, - wallet, - signer.address, - i, - ) - - const timestamp = await Extensions.Recovery.timestampForQueuedPayload( - provider, - this.shared.sequence.extensions.recovery, - 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 + 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, + ) } - // The id is the index + signer address + chainId + wallet address - const id = `${i}-${signer.address}-${chainId}-${wallet}` - - // Create a new payload - const payloadEntry: QueuedRecoveryPayload = { - id, - index: i, - recoveryModule: this.shared.sequence.extensions.recovery, - wallet: wallet, - signer: signer.address, - chainId, - startTimestamp: timestamp, - endTimestamp: timestamp + signer.requiredDeltaTime, - payloadHash, - payload: payload?.payload, + 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) + } } - - 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 payloads + return result } async encodeRecoverySignature(imageHash: Hex.Hex, signer: Address.Address) { diff --git a/packages/wallet/wdk/src/sequence/sessions.ts b/packages/wallet/wdk/src/sequence/sessions.ts index 70c2d85ed..198258279 100644 --- a/packages/wallet/wdk/src/sequence/sessions.ts +++ b/packages/wallet/wdk/src/sequence/sessions.ts @@ -9,7 +9,9 @@ import { 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' @@ -362,7 +364,11 @@ export class Sessions implements SessionsInterface { let audienceHash: Hex.Hex = '0x' if (handler instanceof IdentityHandler) { identityType = handler.identityType - if (handler instanceof AuthCodePkceHandler) { + if ( + handler instanceof AuthCodeHandler || + handler instanceof AuthCodePkceHandler || + handler instanceof IdTokenHandler + ) { issuerHash = Hash.keccak256(Hex.fromString(handler.issuer)) audienceHash = Hash.keccak256(Hex.fromString(handler.audience)) } diff --git a/packages/wallet/wdk/src/sequence/signers.ts b/packages/wallet/wdk/src/sequence/signers.ts index cf79c43c7..ecb43cab7 100644 --- a/packages/wallet/wdk/src/sequence/signers.ts +++ b/packages/wallet/wdk/src/sequence/signers.ts @@ -12,6 +12,11 @@ function toKnownKind(kind: string): Kind { 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 } @@ -48,6 +53,19 @@ export class Signers { 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 @@ -68,7 +86,9 @@ export class Signers { if (isWitnessExtraSignerKind(message)) { return toKnownKind(message.signerKind) } - } catch {} + } catch { + // ignore + } return undefined } diff --git a/packages/wallet/wdk/src/sequence/transactions.ts b/packages/wallet/wdk/src/sequence/transactions.ts index 824cb00f6..c5549f28d 100644 --- a/packages/wallet/wdk/src/sequence/transactions.ts +++ b/packages/wallet/wdk/src/sequence/transactions.ts @@ -310,22 +310,28 @@ export class Transactions implements TransactionsInterface { 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) { - tx.envelope.payload.nonce = changes.nonce + payload.nonce = changes.nonce } if (changes?.space) { - tx.envelope.payload.space = changes.space + payload.space = changes.space } if (changes?.calls) { - if (changes.calls.length !== tx.envelope.payload.calls.length) { + 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++) { - tx.envelope.payload.calls[i]!.gasLimit = changes.calls[i]!.gasLimit + payload.calls[i]!.gasLimit = changes.calls[i]!.gasLimit } } @@ -335,6 +341,7 @@ export class Transactions implements TransactionsInterface { 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([ @@ -347,7 +354,13 @@ export class Transactions implements TransactionsInterface { return [] } - const feeOptions = await relayer.feeOptions(tx.wallet, tx.envelope.chainId, tx.envelope.payload.calls) + 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 : {} @@ -358,6 +371,8 @@ export class Transactions implements TransactionsInterface { id: uuidv7(), relayerType: relayer.type, relayerId: relayer.id, + sponsored: feeOptions.sponsored, + failed: feeOptions.failed, name, icon, } as StandardRelayerOption, @@ -370,6 +385,8 @@ export class Transactions implements TransactionsInterface { feeOption, relayerType: relayer.type, relayerId: relayer.id, + sponsored: feeOptions.sponsored, + failed: feeOptions.failed, quote: feeOptions.quote, })) }), @@ -388,8 +405,8 @@ export class Transactions implements TransactionsInterface { } try { - const erc4337Op = await wallet.prepare4337Transaction(provider, tx.envelope.payload.calls, { - space: tx.envelope.payload.space, + const erc4337Op = await wallet.prepare4337Transaction(provider, payload.calls, { + space: payload.space, }) const erc4337OpsWithEstimatedLimits = await bundler.estimateLimits(tx.wallet, erc4337Op.payload) @@ -492,7 +509,7 @@ export class Transactions implements TransactionsInterface { let tx: Transaction | undefined try { tx = await this.get(transactionOrSignatureId) - } catch (e) { + } catch { // If not found, it might be a signature ID const signature = await this.shared.modules.signatures.get(transactionOrSignatureId) if (!signature) { diff --git a/packages/wallet/wdk/src/sequence/types/index.ts b/packages/wallet/wdk/src/sequence/types/index.ts index 066e8a071..946ee9bf3 100644 --- a/packages/wallet/wdk/src/sequence/types/index.ts +++ b/packages/wallet/wdk/src/sequence/types/index.ts @@ -27,5 +27,6 @@ export type { TransactionRequest, TransactionRequested, } from './transaction-request.js' +export { isERC4337RelayerOption, isStandardRelayerOption } 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/signature-request.ts b/packages/wallet/wdk/src/sequence/types/signature-request.ts index cbce933da..e3cb1b787 100644 --- a/packages/wallet/wdk/src/sequence/types/signature-request.ts +++ b/packages/wallet/wdk/src/sequence/types/signature-request.ts @@ -13,6 +13,8 @@ export type ActionToPayload = { [Actions.Recovery]: Payload.Recovery [Actions.AddRecoverySigner]: Payload.ConfigUpdate [Actions.RemoveRecoverySigner]: Payload.ConfigUpdate + [Actions.AddLoginSigner]: Payload.ConfigUpdate + [Actions.RemoveLoginSigner]: Payload.ConfigUpdate [Actions.SessionImplicitAuthorize]: Payload.SessionImplicitAuthorize } @@ -26,6 +28,8 @@ export const Actions = { Recovery: 'recovery', AddRecoverySigner: 'add-recovery-signer', RemoveRecoverySigner: 'remove-recovery-signer', + AddLoginSigner: 'add-login-signer', + RemoveLoginSigner: 'remove-login-signer', SessionImplicitAuthorize: 'session-implicit-authorize', } as const diff --git a/packages/wallet/wdk/src/sequence/types/signer.ts b/packages/wallet/wdk/src/sequence/types/signer.ts index 30a2a5073..eb46db52f 100644 --- a/packages/wallet/wdk/src/sequence/types/signer.ts +++ b/packages/wallet/wdk/src/sequence/types/signer.ts @@ -5,7 +5,7 @@ export const Kinds = { LoginPasskey: 'login-passkey', LoginMnemonic: 'login-mnemonic', // Todo: do not name it login-mnemonic, just mnemonic LoginEmailOtp: 'login-email-otp', - LoginGooglePkce: 'login-google-pkce', + LoginGoogle: 'login-google', LoginApple: 'login-apple', Recovery: 'recovery-extension', Guard: 'guard-extension', diff --git a/packages/wallet/wdk/src/sequence/types/transaction-request.ts b/packages/wallet/wdk/src/sequence/types/transaction-request.ts index 51160a049..5511bc61b 100644 --- a/packages/wallet/wdk/src/sequence/types/transaction-request.ts +++ b/packages/wallet/wdk/src/sequence/types/transaction-request.ts @@ -21,6 +21,8 @@ export type StandardRelayerOption = BaseRelayerOption & { kind: 'standard' feeOption?: Relayer.FeeOption quote?: Relayer.FeeQuote + sponsored?: boolean + failed?: boolean name?: string icon?: string } diff --git a/packages/wallet/wdk/src/sequence/types/wallet.ts b/packages/wallet/wdk/src/sequence/types/wallet.ts index dd754a05b..c1e12a990 100644 --- a/packages/wallet/wdk/src/sequence/types/wallet.ts +++ b/packages/wallet/wdk/src/sequence/types/wallet.ts @@ -36,7 +36,7 @@ export interface Wallet { /** * A string identifier for the authentication method used for this session. - * Examples: 'login-mnemonic', 'login-passkey', 'login-google-pkce'. + * Examples: 'login-mnemonic', 'login-passkey', 'login-google'. * @property */ loginType: string diff --git a/packages/wallet/wdk/src/sequence/wallets.ts b/packages/wallet/wdk/src/sequence/wallets.ts index fd0320cc4..2d3af321b 100644 --- a/packages/wallet/wdk/src/sequence/wallets.ts +++ b/packages/wallet/wdk/src/sequence/wallets.ts @@ -1,17 +1,55 @@ import { Wallet as CoreWallet, Envelope, Signers, State } from '@0xsequence/wallet-core' -import { Config, Constants, GenericTree, Payload, SessionConfig } from '@0xsequence/wallet-primitives' +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 { ManagerOptionsDefaults, Shared } from './manager.js' +import { Shared } from './manager.js' import { Device } from './types/device.js' -import { Action, Module } from './types/index.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 { GuardRole } from './guards.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}` @@ -19,6 +57,12 @@ export type StartSignUpWithRedirectArgs = { 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 } @@ -49,9 +93,31 @@ export type EmailOtpSignupArgs = CommonSignupArgs & { 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 + includeMetadata?: false +} + +export type CompleteRedirectWithMetadataArgs = Omit & { + includeMetadata: true +} + +export type CompleteRedirectMetadataResult = { + target: string + addedLoginSigner?: { + wallet: Address.Address + signer: { + address: Address.Address + kind: string + email?: string + } + } } export type AuthCodeSignupArgs = CommonSignupArgs & { @@ -62,7 +128,25 @@ export type AuthCodeSignupArgs = CommonSignupArgs & { isRedirect: boolean } -export type SignupArgs = PasskeySignupArgs | MnemonicSignupArgs | EmailOtpSignupArgs | AuthCodeSignupArgs +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 @@ -180,6 +264,7 @@ export interface WalletsInterface { * - `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. @@ -210,6 +295,7 @@ export interface WalletsInterface { * @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: CompleteRedirectWithMetadataArgs): Promise completeRedirect(args: CompleteRedirectArgs): Promise /** @@ -243,6 +329,66 @@ export interface WalletsInterface { */ 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. * @@ -361,7 +507,25 @@ export function isLoginToPasskeyArgs(args: LoginArgs): args is LoginToPasskeyArg } export function isAuthCodeArgs(args: SignupArgs): args is AuthCodeSignupArgs { - return 'kind' in args && (args.kind === 'google-pkce' || args.kind === 'apple') + 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 { @@ -417,17 +581,17 @@ function buildCappedTree(members: { address: Address.Address; imageHash?: Hex.He } 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 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, @@ -625,8 +789,8 @@ export class Wallets implements WalletsInterface { loginEmail?: string }> { switch (args.kind) { - case 'passkey': - const passkeySigner = await Signers.Passkey.Passkey.create(this.shared.sequence.extensions, { + case 'passkey': { + const passkeySigner = await this.shared.passkeyProvider.create(this.shared.sequence.extensions, { stateProvider: this.shared.sequence.stateProvider, credentialName: args.name, }) @@ -638,8 +802,9 @@ export class Wallets implements WalletsInterface { signerKind: Kinds.LoginPasskey, }, } + } - case 'mnemonic': + case 'mnemonic': { const mnemonicSigner = MnemonicHandler.toSigner(args.mnemonic) if (!mnemonicSigner) { throw new Error('invalid-mnemonic') @@ -653,6 +818,7 @@ export class Wallets implements WalletsInterface { signerKind: Kinds.LoginMnemonic, }, } + } case 'email-otp': { const handler = this.shared.handlers.get(Kinds.LoginEmailOtp) as OtpHandler @@ -672,9 +838,28 @@ export class Wallets implements WalletsInterface { } } + 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('login-' + args.kind) as AuthCodeHandler + const handler = this.shared.handlers.get(getSignupHandlerKey(args.kind)) as AuthCodeHandler if (!handler) { throw new Error('handler-not-registered') } @@ -686,7 +871,7 @@ export class Wallets implements WalletsInterface { return { signer, extra: { - signerKind: 'login-' + args.kind, + signerKind: getSignerKindForSignup(args.kind), }, loginEmail, } @@ -694,7 +879,18 @@ export class Wallets implements WalletsInterface { } if (args.kind.startsWith('custom-')) { - // TODO: support other custom auth methods (e.g. id-token) + 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') @@ -714,45 +910,139 @@ export class Wallets implements WalletsInterface { } async startSignUpWithRedirect(args: StartSignUpWithRedirectArgs) { - const kind = args.kind.startsWith('custom-') ? args.kind : 'login-' + args.kind - const handler = this.shared.handlers.get(kind) as AuthCodeHandler + 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') } - return handler.commitAuth(args.target, true) + 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 { + async completeRedirect(args: CompleteRedirectWithMetadataArgs): Promise + async completeRedirect(args: CompleteRedirectArgs): Promise + async completeRedirect( + args: CompleteRedirectArgs | CompleteRedirectWithMetadataArgs, + ): Promise { const commitment = await this.shared.databases.authCommitments.get(args.state) if (!commitment) { throw new Error('invalid-state') } - // commitment.isSignUp and signUp also mean 'signIn' from wallet's perspective - if (commitment.isSignUp) { - await this.signUp({ - kind: commitment.kind, - commitment, - code: args.code, - noGuard: args.noGuard, - target: commitment.target, - isRedirect: true, - use4337: args.use4337, - }) - } else { - const kind = commitment.kind.startsWith('custom-') ? commitment.kind : 'login-' + commitment.kind - const handler = this.shared.handlers.get(kind) as AuthCodeHandler - if (!handler) { - throw new Error('handler-not-registered') + let addedLoginSigner: CompleteRedirectMetadataResult['addedLoginSigner'] + + 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, metadata] = await handler.completeAuth(commitment, args.code) + const signerKind = getSignerKindForSignup(commitment.kind) + const signerAddress = await signer.address + + await this.addLoginSignerFromPrepared(walletAddress, { + signer, + extra: { signerKind }, + }) + + const addedSigner: { + address: Address.Address + kind: string + email?: string + } = { + address: signerAddress, + kind: signerKind, + } + if (metadata?.email !== undefined) { + addedSigner.email = metadata.email + } + + addedLoginSigner = { + wallet: walletAddress, + signer: addedSigner, + } + break } - await handler.completeAuth(commitment, args.code) + 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') } + if (args.includeMetadata) { + const result: CompleteRedirectMetadataResult = { + target: commitment.target, + } + if (addedLoginSigner) { + result.addedLoginSigner = addedLoginSigner + } + return result + } + return commitment.target } @@ -834,7 +1124,7 @@ export class Wallets implements WalletsInterface { const sessionsGuardTopology = args.noGuard ? undefined : this.shared.modules.guards.topology('sessions') // Add modules - let modules: Module[] = [] + const modules: Module[] = [] if (!args.noSessionManager) { const identitySigners = [device.address] @@ -891,7 +1181,7 @@ export class Wallets implements WalletsInterface { } // Store passkey credential ID mapping if this is a passkey signup - if (args.kind === 'passkey' && loginSigner.signer instanceof Signers.Passkey.Passkey) { + if (args.kind === 'passkey' && this.isPasskeySigner(loginSigner.signer)) { try { await this.shared.databases.passkeyCredentials.saveCredential( loginSigner.signer.credentialId, @@ -1043,8 +1333,6 @@ export class Wallets implements WalletsInterface { }) return requestId - } catch (error) { - throw error } finally { this.pendingMnemonicOrPasskeyLogin = undefined } @@ -1075,7 +1363,7 @@ export class Wallets implements WalletsInterface { } if (isLoginToPasskeyArgs(args)) { - let passkeySigner: Signers.Passkey.Passkey + let passkeySigner: PasskeySigner if (args.credentialId) { // Application-controlled login: use the provided credentialId @@ -1087,7 +1375,7 @@ export class Wallets implements WalletsInterface { } // Create passkey signer from stored credential - passkeySigner = new Signers.Passkey.Passkey({ + passkeySigner = this.shared.passkeyProvider.fromCredential({ credentialId: credential.credentialId, publicKey: credential.publicKey, extensions: this.shared.sequence.extensions, @@ -1098,7 +1386,7 @@ export class Wallets implements WalletsInterface { // Default discovery behavior: use WebAuthn discovery this.shared.modules.logger.log('No credentialId provided, using discovery method') - const foundPasskeySigner = await Signers.Passkey.Passkey.find( + const foundPasskeySigner = await this.shared.passkeyProvider.find( this.shared.sequence.stateProvider, this.shared.sequence.extensions, ) @@ -1153,6 +1441,20 @@ export class Wallets implements WalletsInterface { 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) @@ -1170,6 +1472,87 @@ export class Wallets implements WalletsInterface { }) } + 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, @@ -1220,7 +1603,7 @@ export class Wallets implements WalletsInterface { return requestId } - async completeLogout(requestId: string, options?: { skipValidateSave?: boolean }) { + 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) { @@ -1400,4 +1783,54 @@ export class Wallets implements WalletsInterface { 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 index 2e62b8afd..d9b46bd87 100644 --- a/packages/wallet/wdk/test/authcode-pkce.test.ts +++ b/packages/wallet/wdk/test/authcode-pkce.test.ts @@ -1,10 +1,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Address, Hex, Bytes } from 'ox' import * as Identity from '@0xsequence/identity-instrument' -import { AuthCodePkceHandler } from '../src/sequence/handlers/authcode-pkce' -import { Signatures } from '../src/sequence/signatures' -import * as Db from '../src/dbs' -import { IdentitySigner } from '../src/identity/signer' +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 @@ -68,7 +67,7 @@ describe('AuthCodePkceHandler', () => { handler.setRedirectUri('https://example.com/auth/callback') // Mock inherited methods - vi.spyOn(handler as any, 'nitroCommitVerifier').mockImplementation(async (challenge) => { + vi.spyOn(handler as any, 'nitroCommitVerifier').mockImplementation(async () => { return { verifier: 'mock-verifier-code', loginHint: 'user@example.com', @@ -76,7 +75,7 @@ describe('AuthCodePkceHandler', () => { } }) - vi.spyOn(handler as any, 'nitroCompleteAuth').mockImplementation(async (challenge) => { + vi.spyOn(handler as any, 'nitroCompleteAuth').mockImplementation(async () => { return { signer: mockIdentitySigner, email: 'user@example.com', @@ -91,9 +90,8 @@ describe('AuthCodePkceHandler', () => { describe('commitAuth', () => { it('Should create Google PKCE auth commitment and return OAuth URL', async () => { const target = 'https://example.com/success' - const isSignUp = true - const result = await handler.commitAuth(target, isSignUp) + const result = await handler.commitAuth(target, { type: 'auth' }) // Verify nitroCommitVerifier was called with correct challenge expect(handler['nitroCommitVerifier']).toHaveBeenCalledWith( @@ -111,7 +109,7 @@ describe('AuthCodePkceHandler', () => { challenge: 'mock-challenge-hash', target, metadata: {}, - isSignUp, + type: 'auth', }) // Verify OAuth URL is constructed correctly @@ -128,10 +126,13 @@ describe('AuthCodePkceHandler', () => { it('Should use provided state instead of generating random one', async () => { const target = 'https://example.com/success' - const isSignUp = false const customState = 'custom-state-123' - const result = await handler.commitAuth(target, isSignUp, customState) + const result = await handler.commitAuth(target, { + type: 'reauth', + state: customState, + signer: '0x1234567890123456789012345678901234567890', + }) // Verify commitment was saved with custom state expect(mockCommitments.set).toHaveBeenCalledWith({ @@ -141,7 +142,8 @@ describe('AuthCodePkceHandler', () => { challenge: 'mock-challenge-hash', target, metadata: {}, - isSignUp, + type: 'reauth', + signer: '0x1234567890123456789012345678901234567890', }) // Verify URL contains custom state @@ -150,10 +152,9 @@ describe('AuthCodePkceHandler', () => { it('Should include signer in challenge when provided', async () => { const target = 'https://example.com/success' - const isSignUp = true const signer = '0x9876543210987654321098765432109876543210' - await handler.commitAuth(target, isSignUp, undefined, signer) + await handler.commitAuth(target, { type: 'reauth', state: 'test-state', signer }) // Verify nitroCommitVerifier was called with signer in challenge expect(handler['nitroCommitVerifier']).toHaveBeenCalledWith( @@ -165,9 +166,8 @@ describe('AuthCodePkceHandler', () => { it('Should generate random state when not provided', async () => { const target = 'https://example.com/success' - const isSignUp = true - const result = await handler.commitAuth(target, isSignUp) + 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]+/) @@ -182,18 +182,22 @@ describe('AuthCodePkceHandler', () => { const target = 'https://example.com/success' // Test signup - await handler.commitAuth(target, true) + await handler.commitAuth(target, { type: 'auth' }) expect(mockCommitments.set).toHaveBeenLastCalledWith( expect.objectContaining({ - isSignUp: true, + type: 'auth', }), ) // Test login - await handler.commitAuth(target, false) + await handler.commitAuth(target, { + type: 'reauth', + state: 'test-state', + signer: '0x1234567890123456789012345678901234567890', + }) expect(mockCommitments.set).toHaveBeenLastCalledWith( expect.objectContaining({ - isSignUp: false, + type: 'reauth', }), ) }) @@ -201,13 +205,17 @@ describe('AuthCodePkceHandler', () => { 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', true)).rejects.toThrow('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', true)).rejects.toThrow('Database write failed') + await expect(handler.commitAuth('https://example.com/success', { type: 'auth' })).rejects.toThrow( + 'Database write failed', + ) }) }) @@ -222,7 +230,7 @@ describe('AuthCodePkceHandler', () => { challenge: 'test-challenge-hash', target: 'https://example.com/success', metadata: { scope: 'openid profile email' }, - isSignUp: true, + type: 'auth', } }) @@ -327,14 +335,14 @@ describe('AuthCodePkceHandler', () => { describe('Integration and Edge Cases', () => { it('Should have correct kind property', () => { - expect(handler.kind).toBe('login-google-pkce') + 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', true).then((result) => { + return handler.commitAuth('https://example.com/success', { type: 'auth' }).then((result) => { expect(result).toContain(`redirect_uri=${encodeURIComponent(newRedirectUri)}`) }) }) diff --git a/packages/wallet/wdk/test/authcode.test.ts b/packages/wallet/wdk/test/authcode.test.ts index 121a03348..f673f8d42 100644 --- a/packages/wallet/wdk/test/authcode.test.ts +++ b/packages/wallet/wdk/test/authcode.test.ts @@ -1,12 +1,12 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Address, Hex, Bytes } from 'ox' +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' -import { Signatures } from '../src/sequence/signatures' -import * as Db from '../src/dbs' -import { IdentitySigner } from '../src/identity/signer' -import { BaseSignatureRequest } from '../src/sequence/types/signature-request' +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 = { @@ -45,8 +45,8 @@ class MockURLSearchParams { } } -// @ts-ignore - Override global URLSearchParams for testing -global.URLSearchParams = MockURLSearchParams as any +// Override global URLSearchParams for testing +globalThis.URLSearchParams = MockURLSearchParams as any // Mock dependencies with proper vi.fn() types const mockCommitVerifier = vi.fn() @@ -113,7 +113,7 @@ describe('AuthCodeHandler', () => { kind: 'google-pkce', metadata: {}, target: '/test-target', - isSignUp: false, + type: 'reauth', signer: testWallet, } @@ -188,7 +188,7 @@ describe('AuthCodeHandler', () => { // === KIND GETTER === describe('kind getter', () => { - it('Should return login-google-pkce for Google PKCE handler', () => { + it('Should return login-google for Google PKCE handler', () => { const googleHandler = new AuthCodeHandler( 'google-pkce', 'https://accounts.google.com', @@ -200,7 +200,7 @@ describe('AuthCodeHandler', () => { mockAuthKeys, ) - expect(googleHandler.kind).toBe('login-google-pkce') + expect(googleHandler.kind).toBe('login-google') }) it('Should return login-apple for Apple handler', () => { @@ -247,20 +247,17 @@ describe('AuthCodeHandler', () => { it('Should create auth commitment and return OAuth URL', async () => { const target = '/test-target' - const isSignUp = true - const signer = testWallet - const result = await authCodeHandler.commitAuth(target, isSignUp, undefined, signer) + const result = await authCodeHandler.commitAuth(target, { type: 'auth' }) // Verify commitment was saved expect(mockAuthCommitmentsSet).toHaveBeenCalledOnce() - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0][0] + const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! expect(commitmentCall.kind).toBe('google-pkce') - expect(commitmentCall.signer).toBe(signer) expect(commitmentCall.target).toBe(target) expect(commitmentCall.metadata).toEqual({}) - expect(commitmentCall.isSignUp).toBe(isSignUp) + expect(commitmentCall.type).toBe('auth') expect(commitmentCall.id).toBeDefined() expect(typeof commitmentCall.id).toBe('string') @@ -276,18 +273,21 @@ describe('AuthCodeHandler', () => { it('Should use provided state parameter', async () => { const customState = 'custom-state-123' - const result = await authCodeHandler.commitAuth('/target', false, customState) + const result = await authCodeHandler.commitAuth('/target', { + type: 'reauth', + state: customState, + signer: testWallet, + }) // Verify commitment uses custom state - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0][0] + 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 () => { - const result = await authCodeHandler.commitAuth('/target', false) - - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0][0] + 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) @@ -307,18 +307,19 @@ describe('AuthCodeHandler', () => { ) appleHandler.setRedirectUri('https://example.com/callback') - const result = await appleHandler.commitAuth('/target', false) + 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 () => { - const result = await authCodeHandler.commitAuth('/target', true) - - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0][0] + await authCodeHandler.commitAuth('/target', { type: 'auth' }) + const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! expect(commitmentCall.signer).toBeUndefined() - expect(commitmentCall.isSignUp).toBe(true) + expect(commitmentCall.type).toBe('auth') }) }) @@ -327,7 +328,7 @@ describe('AuthCodeHandler', () => { describe('completeAuth()', () => { it('Should complete auth flow with code and return signer', async () => { const authCode = 'test-auth-code-123' - const mockSigner = {} as IdentitySigner + // const mockSigner = {} as IdentitySigner const mockEmail = 'test@example.com' mockCommitVerifier.mockResolvedValueOnce(undefined) @@ -348,12 +349,12 @@ describe('AuthCodeHandler', () => { // Verify commitVerifier was called expect(mockCommitVerifier).toHaveBeenCalledOnce() - const commitVerifierCall = mockCommitVerifier.mock.calls[0] + const commitVerifierCall = mockCommitVerifier.mock.calls[0]! expect(commitVerifierCall[1]).toBeInstanceOf(AuthCodeChallenge) // Verify completeAuth was called expect(mockCompleteAuth).toHaveBeenCalledOnce() - const completeAuthCall = mockCompleteAuth.mock.calls[0] + const completeAuthCall = mockCompleteAuth.mock.calls[0]! expect(completeAuthCall[1]).toBeInstanceOf(AuthCodeChallenge) // Verify results @@ -490,9 +491,9 @@ describe('AuthCodeHandler', () => { expect(window.location.href).toContain('https://accounts.google.com/o/oauth2/v2/auth') expect(mockAuthCommitmentsSet).toHaveBeenCalledOnce() - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0][0] + const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! expect(commitmentCall.target).toBe(window.location.pathname) - expect(commitmentCall.isSignUp).toBe(false) + expect(commitmentCall.type).toBe('reauth') expect(commitmentCall.signer).toBe(testWallet) }) }) @@ -654,7 +655,7 @@ describe('AuthCodeHandler', () => { it('Should handle auth commitments database errors', async () => { mockAuthCommitmentsSet.mockRejectedValueOnce(new Error('Database error')) - await expect(authCodeHandler.commitAuth('/target', false)).rejects.toThrow('Database error') + await expect(authCodeHandler.commitAuth('/target', { type: 'auth' })).rejects.toThrow('Database error') }) it('Should handle auth keys database errors', async () => { @@ -671,7 +672,11 @@ describe('AuthCodeHandler', () => { authCodeHandler.setRedirectUri('https://example.com/callback') // Step 1: Commit auth - const commitUrl = await authCodeHandler.commitAuth('/test-target', false, 'test-state', testWallet) + const commitUrl = await authCodeHandler.commitAuth('/test-target', { + type: 'reauth', + state: 'test-state', + signer: testWallet, + }) expect(commitUrl).toContain('state=test-state') expect(mockAuthCommitmentsSet).toHaveBeenCalledWith( @@ -679,7 +684,7 @@ describe('AuthCodeHandler', () => { id: 'test-state', kind: 'google-pkce', target: '/test-target', - isSignUp: false, + type: 'reauth', signer: testWallet, }), ) @@ -709,17 +714,17 @@ describe('AuthCodeHandler', () => { authCodeHandler.setRedirectUri('https://example.com/callback') // Test signup flow - await authCodeHandler.commitAuth('/signup-target', true, 'signup-state') + await authCodeHandler.commitAuth('/signup-target', { type: 'auth', state: 'signup-state' }) - const signupCall = mockAuthCommitmentsSet.mock.calls[0][0] - expect(signupCall.isSignUp).toBe(true) + 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', false, 'login-state') + await authCodeHandler.commitAuth('/login-target', { type: 'reauth', state: 'login-state', signer: testWallet }) - const loginCall = mockAuthCommitmentsSet.mock.calls[1][0] - expect(loginCall.isSignUp).toBe(false) + 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 index 14b099614..855884c8b 100644 --- a/packages/wallet/wdk/test/constants.ts +++ b/packages/wallet/wdk/test/constants.ts @@ -1,11 +1,13 @@ import { config as dotenvConfig } from 'dotenv' import { Abi, Address, Provider, RpcTransport } from 'ox' -import { Manager, ManagerOptions, ManagerOptionsDefaults } from '../src/sequence' -import { mockEthereum } from './setup' -import { Signers as CoreSigners, State, Relayer } from '@0xsequence/wallet-core' -import * as Db from '../src/dbs' +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 }) @@ -13,6 +15,7 @@ export const EMITTER_ADDRESS: Address.Address = '0xb7bE532959236170064cf099e1a33 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 @@ -80,17 +83,17 @@ export function newRemoteManager( ? `_${remoteManagerOptions.tag}_testrun_${testIdCounter}` : `_testrun_${testIdCounter}` - let relayers: Relayer.Relayer[] = [] - let bundlers: Relayer.Bundler[] = [] + 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.Standard.PkRelayer(remoteManagerOptions.network.relayerPk as `0x${string}`, provider)) + relayers.push(new Relayer.PkRelayer(remoteManagerOptions.network.relayerPk as `0x${string}`, provider)) } if (remoteManagerOptions.network.bundlerUrl) { bundlers.push( - new Relayer.Bundlers.PimlicoBundler( + new Bundler.Bundlers.PimlicoBundler( remoteManagerOptions.network.bundlerUrl, Provider.from(RpcTransport.fromHttp(remoteManagerOptions.network.rpcUrl)), ), diff --git a/packages/wallet/wdk/test/guard.test.ts b/packages/wallet/wdk/test/guard.test.ts index 44e7e2cb5..ffb117c3f 100644 --- a/packages/wallet/wdk/test/guard.test.ts +++ b/packages/wallet/wdk/test/guard.test.ts @@ -1,11 +1,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Manager } from '../src/sequence' -import { GuardHandler } from '../src/sequence/handlers/guard' +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' -import { newManager } from './constants' -import { GuardRole, Guards } from '../src/sequence/guards' +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() @@ -16,8 +16,8 @@ describe('GuardHandler', () => { let guards: Guards let testWallet: Address.Address let testPayload: Payload.Payload - let testMessageDigest: Bytes.Bytes - let testMessage: Hex.Hex + let _testMessageDigest: Bytes.Bytes + let _testMessage: Hex.Hex beforeEach(async () => { vi.clearAllMocks() @@ -28,8 +28,8 @@ describe('GuardHandler', () => { 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) + _testMessage = TypedData.encode(Payload.toTyped(testWallet, Network.ChainId.ARBITRUM, testPayload)) + _testMessageDigest = Payload.hash(testWallet, Network.ChainId.ARBITRUM, testPayload) }) afterEach(async () => { @@ -163,7 +163,7 @@ describe('GuardHandler', () => { expect(result).toBe(true) expect(mockAddSignature).toHaveBeenCalledOnce() - const [requestId, signatureData] = mockAddSignature.mock.calls[0] + 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() @@ -247,7 +247,7 @@ describe('GuardHandler', () => { expect(mockCallback).toHaveBeenCalledOnce() expect(mockAddSignature).toHaveBeenCalledOnce() - const [requestId, signatureData] = mockAddSignature.mock.calls[0] + 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() @@ -300,7 +300,7 @@ describe('GuardHandler', () => { signatures: [], }) - expect(mockFetch.mock.calls[0][0]).toContain(customGuardUrl) + expect(mockFetch.mock.calls[0]![0]).toContain(customGuardUrl) 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 index 43f6e0b5b..bba408ef3 100644 --- a/packages/wallet/wdk/test/identity-auth-dbs.test.ts +++ b/packages/wallet/wdk/test/identity-auth-dbs.test.ts @@ -1,9 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Manager } from '../src/sequence' -import { Address, Hex, Bytes } from 'ox' -import { IdentityInstrument } from '@0xsequence/identity-instrument' -import * as Db from '../src/dbs' -import { LOCAL_RPC_URL } from './constants' +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' @@ -36,7 +34,7 @@ describe('Identity Authentication Databases', () => { verifier: 'test-verifier-code', challenge: 'test-challenge-hash', target: 'test-target-url', - isSignUp: true, + type: 'reauth', signer: '0x1234567890123456789012345678901234567890', } @@ -68,7 +66,7 @@ describe('Identity Authentication Databases', () => { response_mode: 'form_post', }, target: 'apple-redirect-url', - isSignUp: false, + type: 'auth', } await authCommitmentsDb.set(appleCommitment) @@ -76,7 +74,7 @@ describe('Identity Authentication Databases', () => { expect(retrieved).toBeDefined() expect(retrieved!.kind).toBe('apple') - expect(retrieved!.isSignUp).toBe(false) + expect(retrieved!.type).toBe('auth') expect(retrieved!.metadata.response_type).toBe('code id_token') }) @@ -87,21 +85,22 @@ describe('Identity Authentication Databases', () => { kind: 'google-pkce', metadata: {}, target: 'target-1', - isSignUp: true, + type: 'auth', }, { id: 'commit-2', kind: 'apple', metadata: {}, target: 'target-2', - isSignUp: false, + type: 'reauth', + signer: '0x1234567890123456789012345678901234567890', }, { id: 'commit-3', kind: 'google-pkce', metadata: {}, target: 'target-3', - isSignUp: true, + type: 'auth', }, ] @@ -131,7 +130,7 @@ describe('Identity Authentication Databases', () => { kind: 'google-pkce', metadata: {}, target: 'init-target', - isSignUp: true, + type: 'auth', } await freshDb.set(testCommitment) @@ -353,9 +352,84 @@ describe('Identity Authentication Databases', () => { }, }) - // Verify that Google handler is registered and uses our databases + // 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-pkce')).toBe(true) + 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 () => { @@ -426,5 +500,50 @@ describe('Identity Authentication 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 index 5eb9c42b4..526177aa8 100644 --- a/packages/wallet/wdk/test/identity-signer.test.ts +++ b/packages/wallet/wdk/test/identity-signer.test.ts @@ -1,10 +1,10 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Address, Hex, Bytes } from 'ox' +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' -import { AuthKey } from '../src/dbs/auth-keys' +import { IdentitySigner, toIdentityAuthKey } from '../src/identity/signer.js' +import { AuthKey } from '../src/dbs/auth-keys.js' // Mock the global crypto API const mockCryptoSubtle = { @@ -13,7 +13,7 @@ const mockCryptoSubtle = { exportKey: vi.fn(), } -Object.defineProperty(global, 'window', { +Object.defineProperty(globalThis, 'window', { value: { crypto: { subtle: mockCryptoSubtle, @@ -31,7 +31,7 @@ describe('Identity Signer', () => { let testAuthKey: AuthKey let testWallet: Address.Address let mockStateWriter: State.Writer - let mockSignFn: ReturnType + let mockSignFn: Mock beforeEach(() => { vi.clearAllMocks() @@ -179,7 +179,7 @@ describe('Identity Signer', () => { // Verify that identityInstrument.sign was called with correct parameters expect(mockSignFn).toHaveBeenCalledOnce() - const [authKeyArg, digestArg] = mockSignFn.mock.calls[0] + const [authKeyArg, digestArg] = mockSignFn.mock.calls[0]! expect(authKeyArg.address).toBe(testAuthKey.address) expect(authKeyArg.signer).toBe(testAuthKey.identitySigner) expect(digestArg).toBeDefined() @@ -196,7 +196,7 @@ describe('Identity Signer', () => { expect(mockSignFn).toHaveBeenCalledOnce() // The digest should be different for different chainIds - const [, digestArg] = mockSignFn.mock.calls[0] + const [, digestArg] = mockSignFn.mock.calls[0]! expect(digestArg).toBeDefined() }) @@ -255,7 +255,7 @@ describe('Identity Signer', () => { } expect(mockSignFn).toHaveBeenCalledOnce() - const [authKeyArg, digestArg] = mockSignFn.mock.calls[0] + const [authKeyArg, digestArg] = mockSignFn.mock.calls[0]! expect(authKeyArg.address).toBe(testAuthKey.address) expect(digestArg).toBe(digest) }) @@ -295,7 +295,7 @@ describe('Identity Signer', () => { it('Should handle malformed signature from identity instrument', async () => { const digest = Hex.toBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') - mockSignFn.mockResolvedValueOnce('invalid-signature') + mockSignFn.mockResolvedValueOnce('invalid-signature' as any) await expect(identitySigner.signDigest(digest)).rejects.toThrow() // Should throw when Signature.fromHex fails }) @@ -317,7 +317,7 @@ describe('Identity Signer', () => { // Verify witness was saved expect(mockSaveWitnesses).toHaveBeenCalledOnce() - const [wallet, chainId, payload, witness] = mockSaveWitnesses.mock.calls[0] + const [wallet, chainId, payload, witness] = mockSaveWitnesses.mock.calls[0]! expect(wallet).toBe(testWallet) expect(chainId).toBe(0) // Witness signatures use chainId 0 @@ -338,7 +338,7 @@ describe('Identity Signer', () => { await identitySigner.witness(mockStateWriter, testWallet) // Extract the payload that was signed - const [, , payload] = mockSaveWitnesses.mock.calls[0] + const [, , payload] = mockSaveWitnesses.mock.calls[0]! // Parse the message content to verify consent structure const messageHex = payload.message @@ -367,7 +367,7 @@ describe('Identity Signer', () => { await identitySigner.witness(mockStateWriter, testWallet, extraData) // Extract and verify extra data was included - const [, , payload] = mockSaveWitnesses.mock.calls[0] + const [, , payload] = mockSaveWitnesses.mock.calls[0]! const messageString = Hex.toString(payload.message) const consentData = JSON.parse(messageString) @@ -435,7 +435,7 @@ describe('Identity Signer', () => { expect(mockSaveWitnesses).toHaveBeenCalledOnce() // Verify witness payload includes extra context - const [, , witnessPayload] = mockSaveWitnesses.mock.calls[0] + 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') @@ -469,8 +469,8 @@ describe('Identity Signer', () => { expect(mockSignFn).toHaveBeenCalledTimes(2) // Verify different payloads produce different hashes - const [, messageDigest] = mockSignFn.mock.calls[0] - const [, transactionDigest] = mockSignFn.mock.calls[1] + const [, messageDigest] = mockSignFn.mock.calls[0]! + const [, transactionDigest] = mockSignFn.mock.calls[1]! expect(messageDigest).not.toEqual(transactionDigest) }) }) @@ -500,7 +500,7 @@ describe('Identity Signer', () => { it('Should handle malformed hex signatures', async () => { const digest = Hex.toBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') - mockSignFn.mockResolvedValueOnce('not-a-hex-string') + mockSignFn.mockResolvedValueOnce('not-a-hex-string' as any) await expect(identitySigner.signDigest(digest)).rejects.toThrow() }) diff --git a/packages/wallet/wdk/test/idtoken.test.ts b/packages/wallet/wdk/test/idtoken.test.ts new file mode 100644 index 000000000..7badcb89a --- /dev/null +++ b/packages/wallet/wdk/test/idtoken.test.ts @@ -0,0 +1,343 @@ +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 index 9420ca000..32d68ffe5 100644 --- a/packages/wallet/wdk/test/messages.test.ts +++ b/packages/wallet/wdk/test/messages.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest' -import { Manager, SignerActionable } from '../src/sequence' +import { Manager, SignerActionable } from '../src/sequence/index.js' import { Mnemonic } from 'ox' -import { newManager } from './constants' +import { newManager } from './constants.js' import { Network } from '@0xsequence/wallet-primitives' describe('Messages', () => { @@ -41,12 +41,13 @@ describe('Messages', () => { // Verify message appears in list const messages = await manager.messages.list() expect(messages.length).toBe(1) - expect(messages[0].wallet).toBe(wallet) - expect(messages[0].message).toBe(testMessage) - expect(messages[0].status).toBe('requested') - expect(messages[0].signatureId).toBe(signatureId) - expect(messages[0].source).toBe('unknown') - expect(messages[0].id).toBeDefined() + 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 () => { @@ -63,8 +64,11 @@ describe('Messages', () => { const messages = await manager.messages.list() expect(messages.length).toBe(1) - expect(messages[0].source).toBe(customSource) - expect(messages[0].message).toBe(testMessage) + + const message = messages[0]! + + expect(message.source).toBe(customSource) + expect(message.message).toBe(testMessage) }) it('Should get message by ID', async () => { @@ -79,7 +83,7 @@ describe('Messages', () => { const messages = await manager.messages.list() expect(messages.length).toBe(1) - const messageId = messages[0].id + const messageId = messages[0]!.id // Get by message ID const retrievedMessage = await manager.messages.get(messageId) @@ -269,7 +273,7 @@ describe('Messages', () => { const signatureId = await manager.messages.request(wallet!, testMessage) const messages = await manager.messages.list() - const messageId = messages[0].id + const messageId = messages[0]!.id let updateCallCount = 0 let lastMessage: any @@ -315,7 +319,7 @@ describe('Messages', () => { await manager.messages.request(wallet!, testMessage) const messages = await manager.messages.list() - const messageId = messages[0].id + const messageId = messages[0]!.id let callCount = 0 let receivedMessage: any diff --git a/packages/wallet/wdk/test/otp.test.ts b/packages/wallet/wdk/test/otp.test.ts index 1ad450227..8229dd761 100644 --- a/packages/wallet/wdk/test/otp.test.ts +++ b/packages/wallet/wdk/test/otp.test.ts @@ -1,13 +1,13 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +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, OtpChallenge } from '@0xsequence/identity-instrument' -import { OtpHandler } from '../src/sequence/handlers/otp' -import { Signatures } from '../src/sequence/signatures' -import * as Db from '../src/dbs' -import { IdentitySigner } from '../src/identity/signer' -import { BaseSignatureRequest } from '../src/sequence/types/signature-request' -import { Kinds } from '../src/sequence/types/signer' +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 = { @@ -69,7 +69,7 @@ describe('OtpHandler', () => { let otpHandler: OtpHandler let testWallet: Address.Address let testRequest: BaseSignatureRequest - let mockPromptOtp: ReturnType + let mockPromptOtp: Mock beforeEach(() => { vi.clearAllMocks() diff --git a/packages/wallet/wdk/test/passkeys.test.ts b/packages/wallet/wdk/test/passkeys.test.ts index 857258c23..e73d0f4e3 100644 --- a/packages/wallet/wdk/test/passkeys.test.ts +++ b/packages/wallet/wdk/test/passkeys.test.ts @@ -1,12 +1,12 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { Address, Hex } from 'ox' import { Network, Payload } from '@0xsequence/wallet-primitives' -import { Signers, State } from '@0xsequence/wallet-core' +import { State } from '@0xsequence/wallet-core' import { Extensions } from '@0xsequence/wallet-primitives' -import { PasskeysHandler } from '../src/sequence/handlers/passkeys' -import { Signatures } from '../src/sequence/signatures' -import { BaseSignatureRequest } from '../src/sequence/types/signature-request' -import { Kinds } from '../src/sequence/types/signer' +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() diff --git a/packages/wallet/wdk/test/recovery.test.ts b/packages/wallet/wdk/test/recovery.test.ts index 9401260ab..ebaab9d3a 100644 --- a/packages/wallet/wdk/test/recovery.test.ts +++ b/packages/wallet/wdk/test/recovery.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest' -import { Manager, QueuedRecoveryPayload, SignerReady, TransactionDefined } from '../src/sequence' +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' +import { LOCAL_RPC_URL, newManager } from './constants.js' describe('Recovery', () => { it('Should execute a recovery', async () => { @@ -159,7 +159,7 @@ describe('Recovery', () => { expect(tx.status).toBe('defined') expect((tx as TransactionDefined).relayerOptions.length).toBe(1) - const localRelayer = (tx as TransactionDefined).relayerOptions[0] + const localRelayer = (tx as TransactionDefined).relayerOptions[0]! expect(localRelayer).toBeDefined() expect(localRelayer.relayerId).toBe('local') @@ -174,7 +174,7 @@ describe('Recovery', () => { const recoverySigner = request4.signers.find((s) => s.handler?.kind === 'recovery-extension') expect(recoverySigner).toBeDefined() expect(recoverySigner!.status).toBe('ready') - 1 + // Handle the recovery signer const result4 = await (recoverySigner as SignerReady).handle() expect(result4).toBeDefined() @@ -303,9 +303,9 @@ describe('Recovery', () => { }) // Set up mnemonic handler and sign the payload - let handledMnemonic2 = 0 + let _handledMnemonic2 = 0 const unregisterHandler = manager.registerMnemonicUI(async (respond) => { - handledMnemonic2++ + _handledMnemonic2++ await respond(mnemonic2) }) @@ -332,7 +332,7 @@ describe('Recovery', () => { expect(Array.isArray(fetchedPayloads)).toBeTruthy() expect(fetchedPayloads.length).toBe(1) - const fetchedPayload = fetchedPayloads[0] + const fetchedPayload = fetchedPayloads[0]! expect(fetchedPayload).toBeDefined() expect(fetchedPayload.wallet).toBe(wallet) expect(fetchedPayload.chainId).toBe(Network.ChainId.ARBITRUM) @@ -390,8 +390,8 @@ describe('Recovery', () => { expect(updatedPayloads.length).toBe(fetchedPayloads2.length) if (updatedPayloads.length > 0 && fetchedPayloads.length > 0) { - const updated = updatedPayloads[0] - const fetched = fetchedPayloads[0] + const updated = updatedPayloads[0]! + const fetched = fetchedPayloads[0]! expect(updated.id).toBe(fetched.id) expect(updated.wallet).toBe(fetched.wallet) diff --git a/packages/wallet/wdk/test/sessions-idtoken.test.ts b/packages/wallet/wdk/test/sessions-idtoken.test.ts new file mode 100644 index 000000000..7c080af5c --- /dev/null +++ b/packages/wallet/wdk/test/sessions-idtoken.test.ts @@ -0,0 +1,98 @@ +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 index 4e3c9a2ca..aa6ad05b8 100644 --- a/packages/wallet/wdk/test/sessions.test.ts +++ b/packages/wallet/wdk/test/sessions.test.ts @@ -4,7 +4,7 @@ import { Signers as CoreSigners, Wallet as CoreWallet, Envelope, State } from '. 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' +import { EMITTER_ABI, EMITTER_ADDRESS, LOCAL_RPC_URL } from './constants.js' const ALL_EXTENSIONS: { name: string @@ -358,7 +358,7 @@ for (const extension of ALL_EXTENSIONS) { throw new Error('Failed to create pk store') } // Create the initial permissions - let explicitSession: ExplicitSession = { + const explicitSession: ExplicitSession = { type: 'explicit', sessionAddress: e.address, chainId, @@ -402,7 +402,7 @@ for (const extension of ALL_EXTENSIONS) { // 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' + explicitSession.permissions[0]!.target = '0x0000000000000000000000000000000000000000' await setupExplicitSession(explicitSession, true) diff --git a/packages/wallet/wdk/test/setup.ts b/packages/wallet/wdk/test/setup.ts index 70482040c..4aa336a55 100644 --- a/packages/wallet/wdk/test/setup.ts +++ b/packages/wallet/wdk/test/setup.ts @@ -14,7 +14,7 @@ import { } from 'fake-indexeddb' import { Provider, RpcTransport } from 'ox' import { vi } from 'vitest' -import { LOCAL_RPC_URL } from './constants' +import { LOCAL_RPC_URL } from './constants.js' // Add IndexedDB support to the test environment using fake-indexeddb global.indexedDB = indexedDB diff --git a/packages/wallet/wdk/test/signers-kindof.test.ts b/packages/wallet/wdk/test/signers-kindof.test.ts new file mode 100644 index 000000000..06b01cb78 --- /dev/null +++ b/packages/wallet/wdk/test/signers-kindof.test.ts @@ -0,0 +1,62 @@ +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.mjs b/packages/wallet/wdk/test/test-ssr-safety.js similarity index 50% rename from packages/wallet/wdk/test/test-ssr-safety.mjs rename to packages/wallet/wdk/test/test-ssr-safety.js index 55ef7723d..71eb36170 100644 --- a/packages/wallet/wdk/test/test-ssr-safety.mjs +++ b/packages/wallet/wdk/test/test-ssr-safety.js @@ -1,12 +1,13 @@ #!/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 */ @@ -52,24 +53,25 @@ 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')) { + 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:') @@ -81,7 +83,7 @@ try { 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 @@ -98,83 +100,84 @@ 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)) { + 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 propValue = accessed[prop] - if (typeof propValue === 'object' && propValue !== null && depth < 2) { - await testExports(propValue, `${currentPath}.${prop}`, depth + 1) + 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}.${prop}: ${err.message}`) + errors.push(`${currentPath}[${i}]: ${err.message}`) } } } - } - } catch (err) { - if (err.message.includes('window') || err.message.includes('document')) { - errors.push(`${currentPath}: ${err.message}`) + } else { + // Test object properties recursively + await testExports(accessed, currentPath, depth + 1) } } - } 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') || + } 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}`) + 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...') @@ -191,43 +194,46 @@ if (!wdk) { } 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}`) + 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}`) + }) + } + } 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) { + // 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}`) } @@ -244,37 +250,37 @@ if (!wdk) { } 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}`) + 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}`) } @@ -290,7 +296,7 @@ if (errors.length === 0) { 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}`)) + warnings.slice(0, 5).forEach((warn) => console.log(` - ${warn}`)) if (warnings.length > 5) { console.log(` ... and ${warnings.length - 5} more`) } @@ -298,11 +304,11 @@ if (errors.length === 0) { process.exit(0) } else { console.log('\n❌ ERRORS FOUND:') - errors.forEach(err => console.log(` - ${err}`)) + 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}`)) + 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 index 91bffa56a..8d494347d 100644 --- a/packages/wallet/wdk/test/transactions.test.ts +++ b/packages/wallet/wdk/test/transactions.test.ts @@ -1,7 +1,14 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { Manager, SignerActionable, Transaction, TransactionDefined, TransactionRelayed } from '../src/sequence' +import { afterEach, describe, expect, it } from 'vitest' +import { + isStandardRelayerOption, + 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' +import { LOCAL_RPC_URL, newManager } from './constants.js' import { Payload, Network } from '@0xsequence/wallet-primitives' describe('Transactions', () => { @@ -48,9 +55,17 @@ describe('Transactions', () => { } expect(tx.relayerOptions.length).toBe(1) - expect(tx.relayerOptions[0].id).toBeDefined() + expect(tx.relayerOptions[0]!.id).toBeDefined() + + // PkRelayer/LocalRelayer never report sponsorship, so sponsored should be + // explicitly false (not undefined) and failed should remain undefined. + const firstOption = tx.relayerOptions[0]! + if (isStandardRelayerOption(firstOption)) { + expect(firstOption.sponsored).toBe(false) + expect(firstOption.failed).toBeUndefined() + } - const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0].id) + const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0]!.id) expect(sigId).toBeDefined() tx = await manager.transactions.get(txId!) @@ -161,9 +176,17 @@ describe('Transactions', () => { } expect(tx.relayerOptions.length).toBe(1) - expect(tx.relayerOptions[0].id).toBeDefined() + expect(tx.relayerOptions[0]!.id).toBeDefined() + + // PkRelayer/LocalRelayer never report sponsorship, so sponsored should be + // explicitly false (not undefined) and failed should remain undefined. + const firstOption = tx.relayerOptions[0]! + if (isStandardRelayerOption(firstOption)) { + expect(firstOption.sponsored).toBe(false) + expect(firstOption.failed).toBeUndefined() + } - const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0].id) + const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0]!.id) expect(sigId).toBeDefined() tx = await manager.transactions.get(txId!) @@ -230,11 +253,12 @@ describe('Transactions', () => { expect(calledTimes).toBe(1) expect(transactions.length).toBe(1) - expect(transactions[0].status).toBe('requested') - expect(transactions[0].wallet).toBe(wallet!) - expect(transactions[0].requests.length).toBe(1) - expect(transactions[0].requests[0].to).toEqual(to) - expect(transactions[0].requests[0].value).toEqual(9n) + 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 () => { @@ -273,12 +297,12 @@ describe('Transactions', () => { 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() + 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) + const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0]!.id) expect(sigId).toBeDefined() while (calledTimes < 2) { @@ -374,7 +398,7 @@ describe('Transactions', () => { expect(tx).toBeDefined() expect(tx!.status).toBe('defined') - const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0].id) + const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0]!.id) expect(sigId).toBeDefined() await manager.transactions.delete(txId!) @@ -433,12 +457,12 @@ describe('Transactions', () => { // 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] + 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) + const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0]!.id) expect(sigId).toBeDefined() tx = await manager.transactions.get(txId!) @@ -474,12 +498,31 @@ describe('Transactions', () => { 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({ @@ -515,12 +558,12 @@ describe('Transactions', () => { }) let transactionsList: Transaction[] = [] - let updateCount = 0 + let _updateCount = 0 // Use onTransactionsUpdate to verify list functionality const unsubscribe = manager.transactions.onTransactionsUpdate((txs) => { transactionsList = txs - updateCount++ + _updateCount++ }) // Initially should be empty @@ -540,9 +583,10 @@ describe('Transactions', () => { // Should now have one transaction expect(transactionsList.length).toBe(1) - expect(transactionsList[0].id).toBe(txId) - expect(transactionsList[0].status).toBe('requested') - expect(transactionsList[0].wallet).toBe(wallet) + const tx = transactionsList[0]! + expect(tx.id).toBe(txId) + expect(tx.status).toBe('requested') + expect(tx.wallet).toBe(wallet) unsubscribe() }) @@ -577,7 +621,7 @@ describe('Transactions', () => { expect(callCount).toBe(1) expect(receivedTransactions.length).toBe(1) - expect(receivedTransactions[0].id).toBe(txId) + expect(receivedTransactions[0]!.id).toBe(txId) unsubscribe() }) @@ -697,8 +741,8 @@ describe('Transactions', () => { 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) + 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 () => { @@ -780,8 +824,8 @@ describe('Transactions', () => { 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) + expect(tx.requests[0]!.data).toBe('0x1234') + expect(tx.requests[0]!.gasLimit).toBe(21000n) }) it('Should throw error for unknown network', async () => { @@ -820,12 +864,12 @@ describe('Transactions', () => { 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') + 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 () => { @@ -856,7 +900,7 @@ describe('Transactions', () => { throw new Error('Transaction not defined') } - const sigId = await manager.transactions.selectRelayer(txId, tx.relayerOptions[0].id) + const sigId = await manager.transactions.selectRelayer(txId, tx.relayerOptions[0]!.id) // Sign the transaction const sigRequest = await manager.signatures.get(sigId) diff --git a/packages/wallet/wdk/test/wallets.test.ts b/packages/wallet/wdk/test/wallets.test.ts index ec3265b50..d2d1a6ee8 100644 --- a/packages/wallet/wdk/test/wallets.test.ts +++ b/packages/wallet/wdk/test/wallets.test.ts @@ -1,8 +1,13 @@ -import { afterEach, describe, expect, it } from 'vitest' -import { Manager, SignerActionable, SignerReady } from '../src/sequence' +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' +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 @@ -24,6 +29,305 @@ describe('Wallets', () => { 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 expose added login signer metadata from redirect when requested', async () => { + manager = newManager({ + identity: { + google: { + enabled: true, + clientId: 'test-google-client-id', + }, + }, + }) + + const wallet = await manager.wallets.signUp({ + mnemonic: Mnemonic.random(Mnemonic.english), + kind: 'mnemonic', + noGuard: true, + }) + expect(wallet).toBeDefined() + + const handler = (manager as any).shared.handlers.get(Kinds.LoginGoogle) as AuthCodePkceHandler + const addedSigner = MnemonicHandler.toSigner(Mnemonic.random(Mnemonic.english)) + if (!addedSigner) { + throw new Error('Failed to create added login signer for test') + } + + const completeAuthSpy = vi + .spyOn(handler, 'completeAuth') + .mockResolvedValue([addedSigner as unknown as IdentitySigner, { email: 'secondary-google-user@example.com' }]) + + const state = 'add-signer-state-with-metadata' + await (manager as any).shared.databases.authCommitments.set({ + id: state, + kind: 'google-pkce', + metadata: {}, + target: '/account/signers', + type: 'add-signer', + wallet: wallet!, + }) + + const result = await manager.wallets.completeRedirect({ + state, + code: 'auth-code', + includeMetadata: true, + }) + + expect(completeAuthSpy).toHaveBeenCalledWith(expect.objectContaining({ id: state }), 'auth-code') + expect(result).toEqual({ + target: '/account/signers', + addedLoginSigner: { + wallet, + signer: { + address: await addedSigner.address, + kind: Kinds.LoginGoogle, + email: 'secondary-google-user@example.com', + }, + }, + }) + }) + + it('Should keep returning the redirect target string when metadata is not requested', async () => { + manager = newManager({ + identity: { + google: { + enabled: true, + clientId: 'test-google-client-id', + }, + }, + }) + + const wallet = await manager.wallets.signUp({ + mnemonic: Mnemonic.random(Mnemonic.english), + kind: 'mnemonic', + noGuard: true, + }) + expect(wallet).toBeDefined() + + const handler = (manager as any).shared.handlers.get(Kinds.LoginGoogle) as AuthCodePkceHandler + const addedSigner = MnemonicHandler.toSigner(Mnemonic.random(Mnemonic.english)) + if (!addedSigner) { + throw new Error('Failed to create added login signer for test') + } + + vi.spyOn(handler, 'completeAuth').mockResolvedValue([ + addedSigner as unknown as IdentitySigner, + { email: 'secondary-google-user@example.com' }, + ]) + + const state = 'add-signer-state-without-metadata' + await (manager as any).shared.databases.authCommitments.set({ + id: state, + kind: 'google-pkce', + metadata: {}, + target: '/account/signers', + type: 'add-signer', + wallet: wallet!, + }) + + const result = await manager.wallets.completeRedirect({ + state, + code: 'auth-code', + }) + + expect(result).toBe('/account/signers') + }) + + 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) @@ -65,7 +369,7 @@ describe('Wallets', () => { const walletsAfterFirst = await manager.wallets.list() expect(walletsAfterFirst.length).toBe(1) - expect(walletsAfterFirst[0].address).toBe(wallet1) + expect(walletsAfterFirst[0]!.address).toBe(wallet1) }) // === WALLET SELECTOR REGISTRATION === @@ -73,9 +377,9 @@ describe('Wallets', () => { it('Should register and unregister wallet selector', async () => { manager = newManager() - let selectorCalls = 0 + let _selectorCalls = 0 const mockSelector = async () => { - selectorCalls++ + _selectorCalls++ return 'create-new' as const } @@ -160,7 +464,7 @@ describe('Wallets', () => { const mnemonic = Mnemonic.random(Mnemonic.english) // Create initial wallet - const firstWallet = await manager!.wallets.signUp({ + const _firstWallet = await manager!.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true, @@ -283,14 +587,14 @@ describe('Wallets', () => { 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.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.login[0]!.kind).toBe('login-mnemonic') - expect(config.guard).not.toBeDefined() // No guard for noGuard: true + expect(config.walletGuard).not.toBeDefined() // No guard for noGuard: true expect(config.raw).toBeDefined() expect(config.raw.loginTopology).toBeDefined() @@ -319,7 +623,9 @@ describe('Wallets', () => { Config.findSignerLeaf(config.raw.guardTopology!, Constants.PlaceholderAddress as Address.Address), ).toBeUndefined() - const sessionsModule = config.raw.modules.find((m) => Address.isEqual(m.sapientLeaf.address, sessionsModuleAddress)) + 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( @@ -360,7 +666,9 @@ describe('Wallets', () => { ).toBeUndefined() const sessionsModuleAddress = (manager as any).shared.sequence.extensions.sessions - const sessionsModule = config.raw.modules.find((m) => Address.isEqual(m.sapientLeaf.address, sessionsModuleAddress)) + 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() }) @@ -406,7 +714,7 @@ describe('Wallets', () => { 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 }) + 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) @@ -426,7 +734,7 @@ describe('Wallets', () => { const wallets = await manager.wallets.list() expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) + expect(wallets[0]!.address).toBe(wallet!) const requestId = await manager.wallets.logout(wallet!) expect(requestId).toBeDefined() @@ -466,16 +774,16 @@ describe('Wallets', () => { const wallets = await manager.wallets.list() expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) - expect(wallets[0].status).toBe('ready') + 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') + expect(wallets2[0]!.address).toBe(wallet!) + expect(wallets2[0]!.status).toBe('logging-out') const request = await manager.signatures.get(requestId) expect(request).toBeDefined() @@ -511,8 +819,8 @@ describe('Wallets', () => { const wallets = await manager.wallets.list() expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) - expect(wallets[0].status).toBe('logging-in') + expect(wallets[0]!.address).toBe(wallet!) + expect(wallets[0]!.status).toBe('logging-in') let signRequests = 0 const unregistedUI = manager.registerMnemonicUI(async (respond) => { @@ -539,8 +847,8 @@ describe('Wallets', () => { 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') + 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!) @@ -560,7 +868,7 @@ describe('Wallets', () => { const wallets = await manager.wallets.list() expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) + expect(wallets[0]!.address).toBe(wallet!) const requestId = await manager.wallets.logout(wallet!) expect(requestId).toBeDefined() @@ -609,7 +917,7 @@ describe('Wallets', () => { 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!) + 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!) @@ -618,10 +926,10 @@ describe('Wallets', () => { expect(recovery?.length).toBe(1) // The kind of the device key should be 'local-device' - expect(config.devices[0].kind).toBe('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') + expect(recovery?.[0]!.kind).toBe('local-device') }) it('Should fail to logout from a non-existent wallet', async () => { @@ -672,8 +980,8 @@ describe('Wallets', () => { const wallets = await manager.wallets.list() expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) - expect(wallets[0].status).toBe('logging-in') + expect(wallets[0]!.address).toBe(wallet!) + expect(wallets[0]!.status).toBe('logging-in') const request = await manager.signatures.get(requestId!) expect(request).toBeDefined() @@ -695,14 +1003,14 @@ describe('Wallets', () => { 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') + 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() - - let wallet: any | undefined + // eslint-disable-next-line + let wallet: Address.Address | undefined let callbackCalls = 0 let unregisterCallback: (() => void) | undefined @@ -711,8 +1019,8 @@ describe('Wallets', () => { unregisterCallback = manager.wallets.onWalletsUpdate((wallets) => { callbackCalls++ expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) - expect(wallets[0].status).toBe('ready') + expect(wallets[0]!.address).toBe(wallet!) + expect(wallets[0]!.status).toBe('ready') resolve() }) }) @@ -773,8 +1081,8 @@ describe('Wallets', () => { unregisterCallback = manager.wallets.onWalletsUpdate((wallets) => { callbackCalls++ expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) - expect(wallets[0].status).toBe('logging-out') + expect(wallets[0]!.address).toBe(wallet!) + expect(wallets[0]!.status).toBe('logging-out') resolve() }) }) @@ -796,9 +1104,9 @@ describe('Wallets', () => { const devices = await manager.wallets.listDevices(wallet!) expect(devices.length).toBe(1) - expect(devices[0].address).not.toBe(wallet) - expect(devices[0].isLocal).toBe(true) 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 () => { @@ -816,8 +1124,8 @@ describe('Wallets', () => { // 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 + expect(devices1[0]!.isLocal).toBe(true) + const device1Address = devices1[0]!.address // Wallet logs in on device 2 const managerDevice2 = newManager(undefined, undefined, 'device-2') @@ -934,8 +1242,8 @@ describe('Wallets', () => { 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) + expect(finalDevices[0]!.isLocal).toBe(true) + expect(finalDevices[0]!.address).not.toBe(device2Address) await managerDevice1.stop() await managerDevice2.stop() @@ -952,7 +1260,7 @@ describe('Wallets', () => { const devices = await manager.wallets.listDevices(wallet!) expect(devices.length).toBe(1) - const localDeviceAddress = devices[0].address + const localDeviceAddress = devices[0]!.address const remoteLogoutPromise = manager.wallets.remoteLogout(wallet!, localDeviceAddress) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5a389f7d..406d256af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,143 +12,152 @@ importers: .: devDependencies: '@changesets/cli': - specifier: ^2.29.4 - version: 2.29.7(@types/node@24.10.1) + specifier: ^2.29.8 + version: 2.29.8(@types/node@25.3.0) lefthook: - specifier: ^2.0.4 - version: 2.0.4 + specifier: ^2.1.6 + version: 2.1.6 prettier: - specifier: ^3.5.3 - version: 3.6.2 + specifier: ^3.8.3 + version: 3.8.3 rimraf: - specifier: ^6.1.2 - version: 6.1.2 + specifier: ^6.1.3 + version: 6.1.3 syncpack: - specifier: ^13.0.4 - version: 13.0.4(typescript@5.9.3) + specifier: ^14.3.1 + version: 14.3.1 turbo: - specifier: ^2.6.1 - version: 2.6.1 + specifier: ^2.9.14 + version: 2.9.14 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 extras/docs: dependencies: '@repo/ui': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/ui next: - specifier: ^15.5.9 - version: 15.5.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + specifier: ^15.5.16 + version: 15.5.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: - specifier: ^19.1.0 - version: 19.2.0 + specifier: ^19.2.3 + version: 19.2.3 react-dom: - specifier: ^19.1.0 - version: 19.2.0(react@19.2.0) + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) devDependencies: '@repo/eslint-config': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/eslint-config '@repo/typescript-config': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/typescript-config '@types/node': - specifier: ^20.17.57 - version: 20.19.21 + specifier: ^25.3.0 + version: 25.3.0 '@types/react': - specifier: ^19.2.6 - version: 19.2.6 + specifier: ^19.2.7 + version: 19.2.7 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.6) + version: 19.2.3(@types/react@19.2.7) eslint: - specifier: ^9.28.0 - version: 9.37.0 + specifier: ^9.39.2 + version: 9.39.2 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^6.0.3 + version: 6.0.3 extras/web: dependencies: '@repo/ui': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/ui next: - specifier: ^15.5.9 - version: 15.5.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + specifier: ^15.5.16 + version: 15.5.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: - specifier: ^19.1.0 - version: 19.2.0 + specifier: ^19.2.3 + version: 19.2.3 react-dom: - specifier: ^19.1.0 - version: 19.2.0(react@19.2.0) + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) devDependencies: '@repo/eslint-config': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/eslint-config '@repo/typescript-config': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/typescript-config '@types/node': - specifier: ^20.17.57 - version: 20.19.21 + specifier: ^25.3.0 + version: 25.3.0 '@types/react': - specifier: ^19.2.6 - version: 19.2.6 + specifier: ^19.2.7 + version: 19.2.7 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.6) + version: 19.2.3(@types/react@19.2.7) eslint: - specifier: ^9.28.0 - version: 9.37.0 + specifier: ^9.39.2 + version: 9.39.2 typescript: - specifier: ^5.8.3 - version: 5.8.3 + 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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + 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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 packages/services/guard: dependencies: ox: specifier: ^0.9.17 - version: 0.9.17(typescript@5.9.3) + 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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.0.14 - version: 4.0.14(@types/node@24.10.1)(happy-dom@20.0.10) + specifier: ^4.0.18 + version: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) packages/services/identity-instrument: dependencies: @@ -160,56 +169,68 @@ importers: version: 4.0.0 ox: specifier: ^0.9.17 - version: 0.9.17(typescript@5.9.3) + 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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.0.14 - version: 4.0.14(@types/node@24.10.1)(happy-dom@20.0.10) + 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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + 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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + 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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 packages/services/relayer: dependencies: @@ -218,50 +239,59 @@ importers: version: link:../../wallet/primitives mipd: specifier: ^0.0.7 - version: 0.0.7(typescript@5.9.3) + version: 0.0.7(typescript@6.0.3) ox: specifier: ^0.9.17 - version: 0.9.17(typescript@5.9.3) + version: 0.9.17(typescript@6.0.3)(zod@4.2.0) viem: specifier: ^2.40.3 - version: 2.40.3(typescript@5.9.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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.0.14 - version: 4.0.14(@types/node@24.10.1)(happy-dom@20.0.10) + 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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + 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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 packages/wallet/core: dependencies: @@ -276,35 +306,38 @@ importers: version: link:../primitives mipd: specifier: ^0.0.7 - version: 0.0.7(typescript@5.9.3) + version: 0.0.7(typescript@6.0.3) ox: specifier: ^0.9.17 - version: 0.9.17(typescript@5.9.3) + version: 0.9.17(typescript@6.0.3)(zod@4.2.0) viem: specifier: ^2.40.3 - version: 2.40.3(typescript@5.9.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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 '@vitest/coverage-v8': - specifier: ^4.0.14 - version: 4.0.14(vitest@4.0.14(@types/node@24.10.1)(happy-dom@20.0.10)) + 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.2.3 - version: 17.2.3 + specifier: ^17.3.1 + version: 17.3.1 fake-indexeddb: specifier: ^6.2.5 version: 6.2.5 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.0.14 - version: 4.0.14(@types/node@24.10.1)(happy-dom@20.0.10) + specifier: ^4.0.18 + version: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) packages/wallet/dapp-client: dependencies: @@ -322,51 +355,57 @@ importers: version: link:../primitives ox: specifier: ^0.9.17 - version: 0.9.17(typescript@5.9.3) + 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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 '@vitest/coverage-v8': - specifier: ^4.0.14 - version: 4.0.14(vitest@4.0.14(@types/node@24.10.1)(happy-dom@20.0.10)) + 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.2.3 - version: 17.2.3 + specifier: ^17.3.1 + version: 17.3.1 fake-indexeddb: specifier: ^6.2.5 version: 6.2.5 happy-dom: - specifier: ^20.0.10 - version: 20.0.10 + specifier: ^20.8.9 + version: 20.8.9 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.0.14 - version: 4.0.14(@types/node@24.10.1)(happy-dom@20.0.10) + 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@5.9.3) + 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.14 - version: 4.0.14(vitest@4.0.14(@types/node@24.10.1)(happy-dom@20.0.10)) + specifier: ^4.0.18 + version: 4.0.18(vitest@4.0.18(@types/node@25.3.0)(happy-dom@20.8.9)) typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.0.14 - version: 4.0.14(@types/node@24.10.1)(happy-dom@20.0.10) + specifier: ^4.0.18 + version: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) packages/wallet/primitives-cli: dependencies: @@ -375,20 +414,20 @@ importers: version: link:../primitives ox: specifier: ^0.9.17 - version: 0.9.17(typescript@5.9.3) + 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:* + specifier: workspace:^ version: link:../../../repo/eslint-config '@repo/typescript-config': specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 '@types/yargs': specifier: ^17.0.35 version: 17.0.35 @@ -396,14 +435,14 @@ importers: specifier: ^9.2.1 version: 9.2.1 esbuild: - specifier: ^0.27.0 - version: 0.27.0 + specifier: ^0.27.3 + version: 0.27.3 nodemon: - specifier: ^3.1.11 - version: 3.1.11 + specifier: ^3.1.14 + version: 3.1.14 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 packages/wallet/wdk: dependencies: @@ -433,104 +472,107 @@ importers: version: 4.0.0 ox: specifier: ^0.9.17 - version: 0.9.17(typescript@5.9.3) + version: 0.9.17(typescript@6.0.3)(zod@4.2.0) uuid: - specifier: ^13.0.0 - version: 13.0.0 + 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: ^24.10.1 - version: 24.10.1 + specifier: ^25.3.0 + version: 25.3.0 '@vitest/coverage-v8': - specifier: ^4.0.14 - version: 4.0.14(vitest@4.0.14(@types/node@24.10.1)(happy-dom@20.0.10)) + 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.2.3 - version: 17.2.3 + specifier: ^17.3.1 + version: 17.3.1 fake-indexeddb: specifier: ^6.2.5 version: 6.2.5 happy-dom: - specifier: ^20.0.10 - version: 20.0.10 + specifier: ^20.8.9 + version: 20.8.9 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vitest: - specifier: ^4.0.14 - version: 4.0.14(@types/node@24.10.1)(happy-dom@20.0.10) + 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.28.0 - version: 9.37.0 + specifier: ^9.39.2 + version: 9.39.2 '@next/eslint-plugin-next': - specifier: ^15.3.3 - version: 15.5.5 + specifier: ^15.5.9 + version: 15.5.9 eslint: - specifier: ^9.28.0 - version: 9.37.0 + specifier: ^9.39.2 + version: 9.39.2 eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.2(eslint@9.37.0) + 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.37.0) + version: 7.37.5(eslint@9.39.2) eslint-plugin-react-hooks: - specifier: ^5.2.0 - version: 5.2.0(eslint@9.37.0) + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.2) eslint-plugin-turbo: - specifier: ^2.5.4 - version: 2.5.8(eslint@9.37.0)(turbo@2.6.3) + specifier: ^2.6.3 + version: 2.6.3(eslint@9.39.2)(turbo@2.9.14) globals: - specifier: ^15.15.0 - version: 15.15.0 + specifier: ^16.5.0 + version: 16.5.0 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^6.0.3 + version: 6.0.3 typescript-eslint: - specifier: ^8.33.1 - version: 8.46.1(eslint@9.37.0)(typescript@5.8.3) + 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.1.0 - version: 19.2.0 + specifier: ^19.2.3 + version: 19.2.3 react-dom: - specifier: ^19.1.0 - version: 19.2.0(react@19.2.0) + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) devDependencies: '@repo/eslint-config': - specifier: workspace:* + specifier: workspace:^ version: link:../eslint-config '@repo/typescript-config': - specifier: workspace:* + specifier: workspace:^ version: link:../typescript-config '@turbo/gen': specifier: ^1.13.4 - version: 1.13.4(@types/node@20.19.21)(typescript@5.8.3) + version: 1.13.4(@types/node@25.3.0)(typescript@6.0.3) '@types/node': - specifier: ^20.17.57 - version: 20.19.21 + specifier: ^25.3.0 + version: 25.3.0 '@types/react': - specifier: ^19.2.6 - version: 19.2.6 + specifier: ^19.2.7 + version: 19.2.7 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.6) + version: 19.2.3(@types/react@19.2.7) typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^6.0.3 + version: 6.0.3 packages: @@ -544,6 +586,36 @@ packages: 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'} @@ -552,6 +624,14 @@ packages: 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'} @@ -565,6 +645,14 @@ packages: 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'} @@ -573,8 +661,8 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@changesets/apply-release-plan@7.0.13': - resolution: {integrity: sha512-BIW7bofD2yAWoE8H4V40FikC+1nNFEKBisMECccS16W1rt6qqhNTBDmIw5HaqmMgtLNz9e7oiALiEUuKrQ4oHg==} + '@changesets/apply-release-plan@7.0.14': + resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==} '@changesets/assemble-release-plan@6.0.9': resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} @@ -582,12 +670,12 @@ packages: '@changesets/changelog-git@0.2.1': resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - '@changesets/cli@2.29.7': - resolution: {integrity: sha512-R7RqWoaksyyKXbKXBTbT4REdy22yH81mcFK6sWtqSanxUCbUi9Uf+6aqxZtDQouIqPdem2W56CdxXgsxdq7FLQ==} + '@changesets/cli@2.29.8': + resolution: {integrity: sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA==} hasBin: true - '@changesets/config@3.1.1': - resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==} + '@changesets/config@3.1.2': + resolution: {integrity: sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog==} '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} @@ -595,8 +683,8 @@ packages: '@changesets/get-dependents-graph@2.1.3': resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} - '@changesets/get-release-plan@4.0.13': - resolution: {integrity: sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg==} + '@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==} @@ -607,14 +695,14 @@ packages: '@changesets/logger@0.1.1': resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - '@changesets/parse@0.4.1': - resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==} + '@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.5': - resolution: {integrity: sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==} + '@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==} @@ -632,317 +720,161 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@emnapi/runtime@1.7.1': - resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} - - '@esbuild/aix-ppc64@0.25.11': - resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@esbuild/aix-ppc64@0.27.0': - resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==} + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.11': - resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.0': - resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.25.11': - resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.27.0': - resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==} + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.11': - resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.0': - resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.25.11': - resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.0': - resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.11': - resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.27.0': - resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==} + '@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.25.11': - resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.0': - resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.11': - resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.27.0': - resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==} + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.11': - resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.0': - resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.25.11': - resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.0': - resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.25.11': - resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.27.0': - resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==} + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.11': - resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.0': - resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.25.11': - resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.0': - resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.25.11': - resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.0': - resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.11': - resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.0': - resolution: {integrity: sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.25.11': - resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.27.0': - resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==} + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.11': - resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.0': - resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.11': - resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.0': - resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.11': - resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.27.0': - resolution: {integrity: sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==} + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.11': - resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.0': - resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.11': - resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.27.0': - resolution: {integrity: sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==} + '@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.25.11': - resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.27.0': - resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.25.11': - resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.0': - resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.25.11': - resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.0': - resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.25.11': - resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.0': - resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.25.11': - resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.27.0': - resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==} + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -953,36 +885,36 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + '@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.0': - resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + '@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.0': - resolution: {integrity: sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==} + '@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.16.0': - resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} + '@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.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + '@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.37.0': - resolution: {integrity: sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==} + '@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.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + '@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.0': - resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} + '@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': @@ -1001,8 +933,8 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@img/colour@1.0.0': - resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} '@img/sharp-darwin-arm64@0.34.5': @@ -1031,89 +963,105 @@ packages: 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==} @@ -1138,8 +1086,8 @@ packages: cpu: [x64] os: [win32] - '@inquirer/external-editor@1.0.2': - resolution: {integrity: sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==} + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -1147,13 +1095,11 @@ packages: '@types/node': optional: true - '@isaacs/balanced-match@4.0.1': - resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} - engines: {node: 20 || >=22} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - '@isaacs/brace-expansion@5.0.0': - resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} - engines: {node: 20 || >=22} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} @@ -1174,56 +1120,60 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@next/env@15.5.9': - resolution: {integrity: sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==} + '@next/env@15.5.18': + resolution: {integrity: sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g==} - '@next/eslint-plugin-next@15.5.5': - resolution: {integrity: sha512-FMzm412l9oFB8zdRD+K6HQ1VzlS+sNNsdg0MfvTg0i8lfCyTgP/RFxiu/pGJqZ/IQnzn9xSiLkjOVI7Iv4nbdQ==} + '@next/eslint-plugin-next@15.5.9': + resolution: {integrity: sha512-kUzXx0iFiXw27cQAViE1yKWnz/nF8JzRmwgMRTMh8qMY90crNsdXJRh2e+R0vBpFR3kk1yvAR7wev7+fCCb79Q==} - '@next/swc-darwin-arm64@15.5.7': - resolution: {integrity: sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==} + '@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.7': - resolution: {integrity: sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==} + '@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.7': - resolution: {integrity: sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==} + '@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.7': - resolution: {integrity: sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==} + '@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.7': - resolution: {integrity: sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==} + '@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.7': - resolution: {integrity: sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==} + '@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.7': - resolution: {integrity: sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==} + '@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.7': - resolution: {integrity: sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==} + '@next/swc-win32-x64-msvc@15.5.18': + resolution: {integrity: sha512-LIu5me6QTANCd25E7I5uIEfvgQ06RK7tvHAbYo3zCb3VpxQEPvMcSpd87NwUABDT6MbGPdEGR5VRiK4PPTJhQg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1236,10 +1186,6 @@ packages: resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} engines: {node: ^14.21.3 || >=16} - '@noble/curves@1.9.7': - resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} - engines: {node: ^14.21.3 || >=16} - '@noble/hashes@1.4.0': resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} engines: {node: '>= 16'} @@ -1260,113 +1206,124 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@rollup/rollup-android-arm-eabi@4.52.4': - resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} + '@rollup/rollup-android-arm-eabi@4.53.4': + resolution: {integrity: sha512-PWU3Y92H4DD0bOqorEPp1Y0tbzwAurFmIYpjcObv5axGVOtcTlB0b2UKMd2echo08MgN7jO8WQZSSysvfisFSQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.4': - resolution: {integrity: sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==} + '@rollup/rollup-android-arm64@4.53.4': + resolution: {integrity: sha512-Gw0/DuVm3rGsqhMGYkSOXXIx20cC3kTlivZeuaGt4gEgILivykNyBWxeUV5Cf2tDA2nPLah26vq3emlRrWVbng==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.4': - resolution: {integrity: sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==} + '@rollup/rollup-darwin-arm64@4.53.4': + resolution: {integrity: sha512-+w06QvXsgzKwdVg5qRLZpTHh1bigHZIqoIUPtiqh05ZiJVUQ6ymOxaPkXTvRPRLH88575ZCRSRM3PwIoNma01Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.4': - resolution: {integrity: sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==} + '@rollup/rollup-darwin-x64@4.53.4': + resolution: {integrity: sha512-EB4Na9G2GsrRNRNFPuxfwvDRDUwQEzJPpiK1vo2zMVhEeufZ1k7J1bKnT0JYDfnPC7RNZ2H5YNQhW6/p2QKATw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.4': - resolution: {integrity: sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==} + '@rollup/rollup-freebsd-arm64@4.53.4': + resolution: {integrity: sha512-bldA8XEqPcs6OYdknoTMaGhjytnwQ0NClSPpWpmufOuGPN5dDmvIa32FygC2gneKK4A1oSx86V1l55hyUWUYFQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.4': - resolution: {integrity: sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==} + '@rollup/rollup-freebsd-x64@4.53.4': + resolution: {integrity: sha512-3T8GPjH6mixCd0YPn0bXtcuSXi1Lj+15Ujw2CEb7dd24j9thcKscCf88IV7n76WaAdorOzAgSSbuVRg4C8V8Qw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': - resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==} + '@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.52.4': - resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==} + '@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.52.4': - resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==} + '@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.52.4': - resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==} + '@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.52.4': - resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==} + '@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.52.4': - resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==} + '@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.52.4': - resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==} + '@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.52.4': - resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==} + '@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.52.4': - resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==} + '@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.52.4': - resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==} + '@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.52.4': - resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==} + '@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.52.4': - resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==} + '@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.52.4': - resolution: {integrity: sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==} + '@rollup/rollup-win32-arm64-msvc@4.53.4': + resolution: {integrity: sha512-WvUpUAWmUxZKtRnQWpRKnLW2DEO8HB/l8z6oFFMNuHndMzFTJEXzaYJ5ZAmzNw0L21QQJZsUQFt2oPf3ykAD/w==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.4': - resolution: {integrity: sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==} + '@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.52.4': - resolution: {integrity: sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==} + '@rollup/rollup-win32-x64-gnu@4.53.4': + resolution: {integrity: sha512-zuuC7AyxLWLubP+mlUwEyR8M1ixW1ERNPHJfXm8x7eQNP4Pzkd7hS3qBuKBR70VRiQ04Kw8FNfRMF5TNxuZq2g==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.4': - resolution: {integrity: sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==} + '@rollup/rollup-win32-x64-msvc@4.53.4': + resolution: {integrity: sha512-Sbx45u/Lbb5RyptSbX7/3deP+/lzEmZ0BTSHxwxN/IMOZDZf8S0AGo0hJD5n/LQssxb5Z3B4og4P2X6Dd8acCA==} cpu: [x64] os: [win32] @@ -1379,10 +1336,6 @@ packages: '@scure/bip39@1.6.0': resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} - '@sindresorhus/merge-streams@2.3.0': - resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} - engines: {node: '>=18'} - '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -1392,8 +1345,8 @@ packages: '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} '@tsconfig/node12@1.0.11': resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} @@ -1404,16 +1357,46 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@turbo/darwin-64@2.9.14': + resolution: {integrity: sha512-t7QiPflaEyBE4oayeZtSmu4mEfjgIrcNlNNl1z1dmIVPqEdtA7+CfTf8d7KXsOGPh6aNgWjKxyvQg9uGfDQF+A==} + cpu: [x64] + os: [darwin] + + '@turbo/darwin-arm64@2.9.14': + resolution: {integrity: sha512-d23147mC9BsCPA9mJ0h/ubcpbRgcJBXbcG3+Vq7YLhjz3IXuvQsJ1UXH8f4MD76ZjJ4m/E4aRdJV+MW88CDfbw==} + cpu: [arm64] + os: [darwin] + '@turbo/gen@1.13.4': resolution: {integrity: sha512-PK38N1fHhDUyjLi0mUjv0RbX0xXGwDLQeRSGsIlLcVpP1B5fwodSIwIYXc9vJok26Yne94BX5AGjueYsUT3uUw==} hasBin: true + '@turbo/linux-64@2.9.14': + resolution: {integrity: sha512-P3ZKB5tuUDdDQWuAsACGUR1qv9W7BNWxdxqVJ0kZNuNNPRaVYTPPikLcp79+GiEcW3npsR+KyP38lnQiBc5aSA==} + cpu: [x64] + os: [linux] + + '@turbo/linux-arm64@2.9.14': + resolution: {integrity: sha512-ZRTlzcUMrrPv9ZuDzRF9n60Ym13bKeG9jDB8WjxyLhWNzV+AJQN+zdpIk3NJYf2zQsGUm1mNar2P0elRzLw25g==} + cpu: [arm64] + os: [linux] + + '@turbo/windows-64@2.9.14': + resolution: {integrity: sha512-exanwN6sIduZwykYeiTQj8kCmOhazP5WOz3bvXMcYtjhL6Z3iRWLewKrXCBq0bqwSP3iBMb/AerRCnHI4lx46A==} + cpu: [x64] + os: [win32] + + '@turbo/windows-arm64@2.9.14': + resolution: {integrity: sha512-fVdCsnmYoKICsycbWuuGp6Jvi51/3G/UluFWuAUCvR8PIW5IJkAk5BM9UF8PSm0Q2IphWHFZjYEgjHsh3B9y/g==} + cpu: [arm64] + os: [win32] + '@turbo/workspaces@1.13.4': resolution: {integrity: sha512-3uYg2b5TWCiupetbDFMbBFMHl33xQTvp5DNg0fZSYal73Z9AlFH9yWabHWMYw6ywmwM1evkYRpTVA2n7GgqT5A==} hasBin: true - '@types/chai@5.2.2': - resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -1437,19 +1420,16 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@20.19.21': - resolution: {integrity: sha512-CsGG2P3I5y48RPMfprQGfy4JPRZ6csfC3ltBZSRItG3ngggmNY/qs2uZKp4p9VbrpqNNSMzUZNFZKzgOGnd/VA==} - - '@types/node@24.10.1': - resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + '@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.6': - resolution: {integrity: sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==} + '@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==} @@ -1460,85 +1440,88 @@ packages: '@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.46.1': - resolution: {integrity: sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ==} + '@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.46.1 + '@typescript-eslint/parser': ^8.50.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.46.1': - resolution: {integrity: sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==} + '@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.46.1': - resolution: {integrity: sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg==} + '@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.46.1': - resolution: {integrity: sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A==} + '@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.46.1': - resolution: {integrity: sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g==} + '@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.46.1': - resolution: {integrity: sha512-+BlmiHIiqufBxkVnOtFwjah/vrkF4MtKKvpXrKSPLCkCtAp8H01/VV43sfqA98Od7nJpDcFnkwgyfQbOG0AMvw==} + '@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.46.1': - resolution: {integrity: sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ==} + '@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.46.1': - resolution: {integrity: sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg==} + '@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.46.1': - resolution: {integrity: sha512-vkYUy6LdZS7q1v/Gxb2Zs7zziuXN0wxqsetJdeZdRe/f5dwJFglmuvZBfTUivCtjH725C1jWCDfpadadD95EDQ==} + '@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.46.1': - resolution: {integrity: sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==} + '@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.14': - resolution: {integrity: sha512-EYHLqN/BY6b47qHH7gtMxAg++saoGmsjWmAq9MlXxAz4M0NcHh9iOyKhBZyU4yxZqOd8Xnqp80/5saeitz4Cng==} + '@vitest/coverage-v8@4.0.18': + resolution: {integrity: sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==} peerDependencies: - '@vitest/browser': 4.0.14 - vitest: 4.0.14 + '@vitest/browser': 4.0.18 + vitest: 4.0.18 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.0.14': - resolution: {integrity: sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw==} + '@vitest/expect@4.0.18': + resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} - '@vitest/mocker@4.0.14': - resolution: {integrity: sha512-RzS5NujlCzeRPF1MK7MXLiEFpkIXeMdQ+rN3Kk3tDI9j0mtbr7Nmuq67tpkOJQpgyClbOltCXMjLZicJHsH5Cg==} + '@vitest/mocker@4.0.18': + resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0-0 @@ -1548,20 +1531,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.0.14': - resolution: {integrity: sha512-SOYPgujB6TITcJxgd3wmsLl+wZv+fy3av2PpiPpsWPZ6J1ySUYfScfpIt2Yv56ShJXR2MOA6q2KjKHN4EpdyRQ==} + '@vitest/pretty-format@4.0.18': + resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} - '@vitest/runner@4.0.14': - resolution: {integrity: sha512-BsAIk3FAqxICqREbX8SetIteT8PiaUL/tgJjmhxJhCsigmzzH8xeadtp7LRnTpCVzvf0ib9BgAfKJHuhNllKLw==} + '@vitest/runner@4.0.18': + resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} - '@vitest/snapshot@4.0.14': - resolution: {integrity: sha512-aQVBfT1PMzDSA16Y3Fp45a0q8nKexx6N5Amw3MX55BeTeZpoC08fGqEZqVmPcqN0ueZsuUQ9rriPMhZ3Mu19Ag==} + '@vitest/snapshot@4.0.18': + resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} - '@vitest/spy@4.0.14': - resolution: {integrity: sha512-JmAZT1UtZooO0tpY3GRyiC/8W7dCs05UOq9rfsUUgEZEdq+DuHLmWhPsrTt0TiW7WYeL/hXpaE07AZ2RCk44hg==} + '@vitest/spy@4.0.18': + resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} - '@vitest/utils@4.0.14': - resolution: {integrity: sha512-hLqXZKAWNg8pI+SQXyXxWCTOpA3MvsqcbVeNgSi8x/CSN2wi26dSzn1wrOhmCmFjEvN9p8/kLFRHa6PI8jHazw==} + '@vitest/utils@4.0.18': + resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} abitype@1.1.0: resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} @@ -1574,8 +1557,8 @@ packages: zod: optional: true - abitype@1.1.1: - resolution: {integrity: sha512-Loe5/6tAgsBukY95eGaPSDmQHIjRZYQq8PB1MpsNccDIK8WiV+Uw6WzaIXipvaxTEL2yEB0OpEaQv3gs8pkS9Q==} + abitype@1.2.2: + resolution: {integrity: sha512-4DOIMWscIB3j8hboLAUjLZCE8TMLdgecBpHFumfU4PdO/C1SBCVx4Nu1wPYXaL2iK8B0Jk3tiwnDLCpUtm3fZg==} peerDependencies: typescript: '>=5.0.4' zod: ^3.22.0 || ^4.0.0 @@ -1683,16 +1666,20 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} - asn1js@3.0.6: - resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==} + 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.8: - resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==} + ast-v8-to-istanbul@0.3.11: + resolution: {integrity: sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw==} async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} @@ -1705,12 +1692,21 @@ packages: 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==} @@ -1729,10 +1725,19 @@ packages: 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==} @@ -1759,8 +1764,8 @@ packages: camel-case@3.0.0: resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} - caniuse-lite@1.0.30001760: - resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} + 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==} @@ -1770,10 +1775,6 @@ packages: resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} engines: {node: '>=18'} - chalk-template@1.1.2: - resolution: {integrity: sha512-2bxTP2yUH7AJj/VAXfcA+4IcWGdQ87HwBANLt5XxGTeomo8yG0y95N1um9i5StvhT/Bl0/2cARA5v1PpPXUxUA==} - engines: {node: '>=14.16'} - chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1786,18 +1787,14 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - 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.0: - resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} @@ -1815,10 +1812,6 @@ packages: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} - cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} - engines: {node: '>=18'} - cli-spinners@2.9.2: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} @@ -1859,10 +1852,6 @@ packages: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} - commander@13.1.0: - resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} - engines: {node: '>=18'} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1874,17 +1863,11 @@ packages: constant-case@2.0.0: resolution: {integrity: sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==} - core-js-pure@3.46.0: - resolution: {integrity: sha512-NMCW30bHNofuhwLhYPt66OLOKTMbOhgTTatKVbaQC3KRHpTCiRIBYvtshr+NBYSnBxwAFhjW/RfJ0XbIjS16rw==} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cosmiconfig@9.0.0: - resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true + core-js-pure@3.47.0: + resolution: {integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==} create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -1974,16 +1957,16 @@ packages: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} - dotenv@17.2.3: - resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} + 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'} - effect@3.19.6: - resolution: {integrity: sha512-Eh1E/CI+xCAcMSDC5DtyE29yWJINC0zwBbwHappQPorjKyS69rCA8qzpsHpfhKnPDYgxdg8zkknii8mZ+6YMQA==} + 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==} @@ -1995,15 +1978,12 @@ packages: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - - es-abstract@1.24.0: - resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -2014,8 +1994,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + es-iterator-helpers@1.2.2: + resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} engines: {node: '>= 0.4'} es-module-lexer@1.7.0: @@ -2037,13 +2017,8 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild@0.25.11: - resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} - engines: {node: '>=18'} - hasBin: true - - esbuild@0.27.0: - resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} hasBin: true @@ -2064,8 +2039,8 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-config-prettier@9.1.2: - resolution: {integrity: sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==} + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true peerDependencies: eslint: '>=7.0.0' @@ -2074,9 +2049,9 @@ packages: resolution: {integrity: sha512-2tktqUAT+Q3hCAU0iSf4xAN1k9zOpjK5WO8104mB0rT/dGhOa09582HN5HlbxNbPRZ0THV7nLGvzugcNOSjzfA==} engines: {node: '>=6'} - eslint-plugin-react-hooks@5.2.0: - resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} - engines: {node: '>=10'} + 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 @@ -2086,8 +2061,8 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-plugin-turbo@2.5.8: - resolution: {integrity: sha512-bVjx4vTH0oTKIyQ7EGFAXnuhZMrKIfu17qlex/dps7eScPnGQLJ3r1/nFq80l8xA+8oYjsSirSQ2tXOKbz3kEw==} + 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' @@ -2104,8 +2079,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.37.0: - resolution: {integrity: sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==} + 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: @@ -2149,8 +2124,8 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - expect-type@1.2.2: - resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} extendable-error@0.1.7: @@ -2164,10 +2139,6 @@ packages: resolution: {integrity: sha512-CGnyrvbhPlWYMngksqrSSUT1BAVP49dZocrHuK0SvtR0D5TMs5wP0o3j7jexDJW01KSadjBp1M/71o/KR3nD1w==} engines: {node: '>=18'} - fast-check@3.23.2: - resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} - engines: {node: '>=8.0.0'} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2262,6 +2233,10 @@ packages: 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.*} @@ -2298,20 +2273,20 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@13.0.0: - resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} - engines: {node: 20 || >=22} + 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: Glob versions prior to v9 are no longer supported + 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@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} globalthis@1.0.4: @@ -2326,10 +2301,6 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} - globby@14.1.0: - resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} - engines: {node: '>=18'} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -2341,16 +2312,13 @@ packages: resolution: {integrity: sha512-rEDCuqUQ4tbD78TpzsMtt5OIf0cBCSDWSJtUDaF6JsAh+k0v9r++NzxNEG87oDZx9ZwGhD8DaezR2L/yrw0Jdw==} engines: {node: '>=10'} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} hasBin: true - happy-dom@20.0.10: - resolution: {integrity: sha512-6umCCHcjQrhP5oXhrHQQvLB0bwb1UzHAHdsXy+FjtKoYjUhmNZsQL8NivwM1vDvNEChJabVrUYxUnp/ZdYmy2g==} + happy-dom@20.8.9: + resolution: {integrity: sha512-Tz23LR9T9jOGVZm2x1EPdXqwA37G/owYMxRwU0E4miurAtFsPMQ1d2Jc2okUaSjZqAFz2oEn3FLXC5a0a+siyA==} engines: {node: '>=20.0.0'} has-bigints@1.1.0: @@ -2387,9 +2355,11 @@ packages: header-case@1.0.1: resolution: {integrity: sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==} - hosted-git-info@8.1.0: - resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} - engines: {node: ^18.17.0 || >=20.5.0} + 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==} @@ -2402,8 +2372,8 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} - human-id@4.1.2: - resolution: {integrity: sha512-v/J+4Z/1eIJovEBdlV5TYj1IR+ZiohcYGRY+qN/oC9dAfKzVT023N/Bgw37hrKCoVRBvk3bqyzpr2PP5YeTMSg==} + human-id@4.1.3: + resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true human-signals@2.1.0: @@ -2414,8 +2384,8 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.0: - resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + iconv-lite@0.7.1: + resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} engines: {node: '>=0.10.0'} idb@8.0.3: @@ -2469,17 +2439,14 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - ip-address@10.0.1: - resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + 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-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} @@ -2536,10 +2503,6 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} - is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} - is-lower-case@1.1.3: resolution: {integrity: sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==} @@ -2603,14 +2566,6 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - - is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} - engines: {node: '>=18'} - is-upper-case@1.1.2: resolution: {integrity: sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==} @@ -2653,10 +2608,6 @@ packages: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} - istanbul-lib-source-maps@5.0.6: - resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} - engines: {node: '>=10'} - istanbul-reports@3.2.0: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} @@ -2665,18 +2616,23 @@ packages: 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-tokens@9.0.1: - resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} hasBin: true json-buffer@3.0.1: @@ -2685,17 +2641,16 @@ packages: json-canonicalize@2.0.0: resolution: {integrity: sha512-yyrnK/mEm6Na3ChbJUWueXdapueW0p380RUyTW87XGb1ww8l8hU0pRrGC3vSWHe9CxrbPHX2fGUOZpNiHR0IIg==} - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - 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==} - jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + 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==} @@ -2714,71 +2669,64 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - - lefthook-darwin-arm64@2.0.4: - resolution: {integrity: sha512-AR63/O5UkM7Sc6x5PhP4vTuztTYRBeBroXApeWGM/8e5uZyoQug/7KTh7xhbCMDf8WJv6vdFeXAQCPSmDyPU3Q==} + lefthook-darwin-arm64@2.1.6: + resolution: {integrity: sha512-hyB7eeiX78BS66f70byTJacDLC/xV1vgMv9n+idFUsrM7J3Udd/ag9Ag5NP3t0eN0EqQqAtrNnt35EH01lxnRQ==} cpu: [arm64] os: [darwin] - lefthook-darwin-x64@2.0.4: - resolution: {integrity: sha512-618DVUttSzV9egQiqTQoxGfnR240JoPWYmqRVHhiegnQKZ2lp5XJ+7NMxeRk/ih93VVOLzFO5ky3PbpxTmJgjQ==} + lefthook-darwin-x64@2.1.6: + resolution: {integrity: sha512-5Ka6cFxiH83krt+OMRQtmS6zqoZR5SLXSudLjTbZA1c3ZqF0+dqkeb4XcB6plx6WR0GFizabuc6Bi3iXPIe1eQ==} cpu: [x64] os: [darwin] - lefthook-freebsd-arm64@2.0.4: - resolution: {integrity: sha512-mTAQym1BK38fKglHBQ/0GXPznVC4LoStHO5lAI3ZxaEC0FQetqGHYFzhWbIH5sde9JhztE2rL/aBzMHDoAtzSw==} + lefthook-freebsd-arm64@2.1.6: + resolution: {integrity: sha512-VswyOg5CVN3rMaOJ2HtnkltiMKgFHW/wouWxXsV8RxSa4tgWOKxM0EmSXi8qc2jX+LRga6B0uOY6toXS01zWxA==} cpu: [arm64] os: [freebsd] - lefthook-freebsd-x64@2.0.4: - resolution: {integrity: sha512-sy02aSxd8UMd6XmiPFVl/Em0b78jdZcDSsLwg+bweJQQk0l+vJhOfqFiG11mbnpo+EBIZmRe6OH5LkxeSU36+w==} + lefthook-freebsd-x64@2.1.6: + resolution: {integrity: sha512-vXsCUFYuVwrVWwcypB7Zt2Hf+5pl1V1la7ZfvGYZaTRURu0zF/XUnMF/nOz/PebGv0f4x/iOWXWwP7E42xRWsg==} cpu: [x64] os: [freebsd] - lefthook-linux-arm64@2.0.4: - resolution: {integrity: sha512-W0Nlr/Cz2QTH9n4k5zNrk3LSsg1C4wHiJi8hrAiQVTaAV/N1XrKqd0DevqQuouuapG6pw/6B1xCgiNPebv9oyw==} + lefthook-linux-arm64@2.1.6: + resolution: {integrity: sha512-WDJiQhJdZOvKORZd+kF/ms2l6NSsXzdA9ahflyr65V90AC4jES223W8VtEMbGPUtHuGWMEZ/v/XvwlWv0Ioz9g==} cpu: [arm64] os: [linux] - lefthook-linux-x64@2.0.4: - resolution: {integrity: sha512-N6ySVCtB/DrOZ1ZgPL8WBZTgtoVHvcPKI+LV5wbcGrvA/dzDZFvniadrbDWZg7Tm705efiQzyENjwhhqNkwiww==} + lefthook-linux-x64@2.1.6: + resolution: {integrity: sha512-C18nCd7nTX1AVL4TcvwMmLAO1VI1OuGluIOTjiPkBQ746Ls1HhL5rl//jMPACmT28YmxIQJ2ZcLPNmhvEVBZvw==} cpu: [x64] os: [linux] - lefthook-openbsd-arm64@2.0.4: - resolution: {integrity: sha512-VmOhJO3pYzZ/1C2WFXtL/n5pq4/eYOroqJJpwTJfmCHyw4ceLACu8MDyU5AMJhGMkbL8mPxGInJKxg5xhYgGRw==} + lefthook-openbsd-arm64@2.1.6: + resolution: {integrity: sha512-mZOMxM8HiPxVFXDO3PtCUbH4GB8rkveXhsgXF27oAZTYVzQ3gO9vT6r/pxit6msqRXz3fvcwimLVJgb8eRsa8A==} cpu: [arm64] os: [openbsd] - lefthook-openbsd-x64@2.0.4: - resolution: {integrity: sha512-U8MZz1xlHUdflkQQ2hkMQsei6fSZbs8tuE4EjCIHWnNdnAF4V8sZ6n1KbxsJcoZXPyBZqxZSMu1o/Ye8IAMVKg==} + lefthook-openbsd-x64@2.1.6: + resolution: {integrity: sha512-sG9ALLZSnnMOfXu+B7SmxFhJhuoAh4bqi5En5aaHJET48TqrLOcWWZuH+7ArFM6gr/U5KfSUvdmHFmY8WqCcIg==} cpu: [x64] os: [openbsd] - lefthook-windows-arm64@2.0.4: - resolution: {integrity: sha512-543H3y2JAwNdvwUQ6nlNBG7rdKgoOUgzAa6pYcl6EoqicCRrjRmGhkJu7vUudkkrD2Wjm7tr9hU9poP2g5fRFQ==} + lefthook-windows-arm64@2.1.6: + resolution: {integrity: sha512-lD8yFWY4Csuljd0Rqs7EQaySC0VvDf7V3rN1FhRMUISTRDHutebIom1Loc8ckQPvKYGC6mftT9k0GvipsS+Brw==} cpu: [arm64] os: [win32] - lefthook-windows-x64@2.0.4: - resolution: {integrity: sha512-UDEPK9RWKm60xsNOdS/DQOdFba0SFa4w3tpFMXK1AJzmRHhosoKrorXGhtTr6kcM0MGKOtYi8GHsm++ArZ9wvQ==} + lefthook-windows-x64@2.1.6: + resolution: {integrity: sha512-q4z2n3xucLscoWiyMwFViEj3N8MDSkPulMwcJYuCYFHoPhP1h+icqNu7QRLGYj6AnVrCQweiUJY3Tb2X+GbD/A==} cpu: [x64] os: [win32] - lefthook@2.0.4: - resolution: {integrity: sha512-GNCU2vQWM/UWjiEF23601aILi1aMbPke6viortH7wIO/oVGOCW0H6FdLez4XZDyqnHL9XkTnd0BBVrBbYVMLpA==} + 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'} - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2808,10 +2756,6 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} - log-symbols@6.0.0: - resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} - engines: {node: '>=18'} - loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2822,13 +2766,13 @@ packages: lower-case@1.1.4: resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@11.2.2: - resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + 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'} @@ -2865,13 +2809,9 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} - engines: {node: '>=18'} - - minimatch@10.1.1: - resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} - engines: {node: 20 || >=22} + 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==} @@ -2883,8 +2823,8 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} mipd@0.0.7: @@ -2909,8 +2849,8 @@ packages: mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -2924,8 +2864,8 @@ packages: resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} engines: {node: '>= 0.4.0'} - next@15.5.9: - resolution: {integrity: sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==} + 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: @@ -2952,8 +2892,11 @@ packages: resolution: {integrity: sha512-Cov028YhBZ5aB7MdMWJEmwyBig43aGL5WT4vdoB28Oitau1zZAcHUn8Sgfk9HM33TqhtLJ9PlM/O0Mv+QpV/4Q==} engines: {node: '>=8.9.4'} - nodemon@3.1.11: - resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==} + 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 @@ -2961,10 +2904,6 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - npm-package-arg@12.0.2: - resolution: {integrity: sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==} - engines: {node: ^18.17.0 || >=20.5.0} - npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -3007,10 +2946,6 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} - engines: {node: '>=18'} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -3023,10 +2958,6 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} - ora@8.2.0: - resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} - engines: {node: '>=18'} - os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -3099,10 +3030,6 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - pascal-case@2.0.1: resolution: {integrity: sha512-qjS4s8rBOJa2Xm0jmxXiyh1+OFf6ekCWOvUaRgAQSktzlTbMotS0nmG9gyYAybCWBcuP4fsBeRCKNwGBnMe2OQ==} @@ -3124,18 +3051,14 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@2.0.0: - resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} - engines: {node: 20 || >=22} + 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'} - path-type@6.0.0: - resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} - engines: {node: '>=18'} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -3154,9 +3077,9 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - pkijs@3.3.0: - resolution: {integrity: sha512-SVUpr7uqRNuR6w417k0aM8YrHWWhvMh4P0paIA+wWdOxPOLd7PCDZgt3/nooAyrrO1cjwPP2I1hd3h2P6hUQZQ==} - engines: {node: '>=12.0.0'} + 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==} @@ -3166,8 +3089,8 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + postcss@8.5.14: + resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -3179,19 +3102,11 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.8.3: + resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} engines: {node: '>=14'} hasBin: true - proc-log@5.0.0: - resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} - engines: {node: ^18.17.0 || >=20.5.0} - - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -3209,15 +3124,12 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - pvtsutils@1.3.6: resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} - pvutils@1.1.3: - resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} - engines: {node: '>=6.0.0'} + 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==} @@ -3229,26 +3141,22 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-dom@19.2.0: - resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: - react: ^19.2.0 + react: ^19.2.3 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react@19.2.0: - resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} + 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'} - read-yaml-file@2.1.0: - resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} - engines: {node: '>=10.13'} - readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -3284,8 +3192,8 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} hasBin: true @@ -3297,10 +3205,6 @@ packages: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} - restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} - engines: {node: '>=18'} - reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -3310,13 +3214,13 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rimraf@6.1.2: - resolution: {integrity: sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==} + rimraf@6.1.3: + resolution: {integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==} engines: {node: 20 || >=22} hasBin: true - rollup@4.52.4: - resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==} + rollup@4.53.4: + resolution: {integrity: sha512-YpXaaArg0MvrnJpvduEDYIp7uGOqKXbH9NsHGQ6SxKCOsNAjZF018MmxefFUulVP2KLtiGw1UvZbr+/ekjvlDg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3364,6 +3268,11 @@ packages: 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==} @@ -3425,17 +3334,10 @@ packages: resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} engines: {node: '>=10'} - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - slash@5.1.0: - resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} - engines: {node: '>=14.16'} - smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -3471,10 +3373,6 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - stdin-discarder@0.2.2: - resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} - engines: {node: '>=18'} - stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} @@ -3521,10 +3419,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -3569,9 +3463,49 @@ packages: swap-case@1.1.2: resolution: {integrity: sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==} - syncpack@13.0.4: - resolution: {integrity: sha512-kJ9VlRxNCsBD5pJAE29oXeBYbPLhEySQmK4HdpsLv81I6fcDDW17xeJqMwiU3H7/woAVsbgq25DJNS8BeiN5+w==} - engines: {node: '>=18.18.0'} + 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: @@ -3581,18 +3515,15 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - tightrope@0.2.0: - resolution: {integrity: sha512-Kw36UHxJEELq2VUqdaSGR2/8cAsPgMtvX8uGVU6Jk26O66PhXec0A5ZnRYs47btbtwPDpXXF66+Fo3vimCM9aQ==} - engines: {node: '>=16'} - 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@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} @@ -3644,81 +3575,14 @@ packages: '@swc/wasm': optional: true - ts-toolbelt@9.6.0: - resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} - tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - turbo-darwin-64@2.6.1: - resolution: {integrity: sha512-Dm0HwhyZF4J0uLqkhUyCVJvKM9Rw7M03v3J9A7drHDQW0qAbIGBrUijQ8g4Q9Cciw/BXRRd8Uzkc3oue+qn+ZQ==} - cpu: [x64] - os: [darwin] - - turbo-darwin-64@2.6.3: - resolution: {integrity: sha512-BlJJDc1CQ7SK5Y5qnl7AzpkvKSnpkfPmnA+HeU/sgny3oHZckPV2776ebO2M33CYDSor7+8HQwaodY++IINhYg==} - cpu: [x64] - os: [darwin] - - turbo-darwin-arm64@2.6.1: - resolution: {integrity: sha512-U0PIPTPyxdLsrC3jN7jaJUwgzX5sVUBsKLO7+6AL+OASaa1NbT1pPdiZoTkblBAALLP76FM0LlnsVQOnmjYhyw==} - cpu: [arm64] - os: [darwin] - - turbo-darwin-arm64@2.6.3: - resolution: {integrity: sha512-MwVt7rBKiOK7zdYerenfCRTypefw4kZCue35IJga9CH1+S50+KTiCkT6LBqo0hHeoH2iKuI0ldTF2a0aB72z3w==} - cpu: [arm64] - os: [darwin] - - turbo-linux-64@2.6.1: - resolution: {integrity: sha512-eM1uLWgzv89bxlK29qwQEr9xYWBhmO/EGiH22UGfq+uXr+QW1OvNKKMogSN65Ry8lElMH4LZh0aX2DEc7eC0Mw==} - cpu: [x64] - os: [linux] - - turbo-linux-64@2.6.3: - resolution: {integrity: sha512-cqpcw+dXxbnPtNnzeeSyWprjmuFVpHJqKcs7Jym5oXlu/ZcovEASUIUZVN3OGEM6Y/OTyyw0z09tOHNt5yBAVg==} - cpu: [x64] - os: [linux] - - turbo-linux-arm64@2.6.1: - resolution: {integrity: sha512-MFFh7AxAQAycXKuZDrbeutfWM5Ep0CEZ9u7zs4Hn2FvOViTCzIfEhmuJou3/a5+q5VX1zTxQrKGy+4Lf5cdpsA==} - cpu: [arm64] - os: [linux] - - turbo-linux-arm64@2.6.3: - resolution: {integrity: sha512-MterpZQmjXyr4uM7zOgFSFL3oRdNKeflY7nsjxJb2TklsYqiu3Z9pQ4zRVFFH8n0mLGna7MbQMZuKoWqqHb45w==} - cpu: [arm64] - os: [linux] - - turbo-windows-64@2.6.1: - resolution: {integrity: sha512-buq7/VAN7KOjMYi4tSZT5m+jpqyhbRU2EUTTvp6V0Ii8dAkY2tAAjQN1q5q2ByflYWKecbQNTqxmVploE0LVwQ==} - cpu: [x64] - os: [win32] - - turbo-windows-64@2.6.3: - resolution: {integrity: sha512-biDU70v9dLwnBdLf+daoDlNJVvqOOP8YEjqNipBHzgclbQlXbsi6Gqqelp5er81Qo3BiRgmTNx79oaZQTPb07Q==} - cpu: [x64] - os: [win32] - - turbo-windows-arm64@2.6.1: - resolution: {integrity: sha512-7w+AD5vJp3R+FB0YOj1YJcNcOOvBior7bcHTodqp90S3x3bLgpr7tE6xOea1e8JkP7GK6ciKVUpQvV7psiwU5Q==} - cpu: [arm64] - os: [win32] - - turbo-windows-arm64@2.6.3: - resolution: {integrity: sha512-dDHVKpSeukah3VsI/xMEKeTnV9V9cjlpFSUs4bmsUiLu3Yv2ENlgVEZv65wxbeE0bh0jjpmElDT+P1KaCxArQQ==} - cpu: [arm64] - os: [win32] - - turbo@2.6.1: - resolution: {integrity: sha512-qBwXXuDT3rA53kbNafGbT5r++BrhRgx3sAo0cHoDAeG9g1ItTmUMgltz3Hy7Hazy1ODqNpR+C7QwqL6DYB52yA==} - hasBin: true - - turbo@2.6.3: - resolution: {integrity: sha512-bf6YKUv11l5Xfcmg76PyWoy/e2vbkkxFNBGJSnfdSXQC33ZiUfutYh6IXidc5MhsnrFkWfdNNLyaRk+kHMLlwA==} + turbo@2.9.14: + resolution: {integrity: sha512-BQqXRr4UoWI3UPFrtznCLykYHxwxWh53iCB57x092jPMjIlW1wnm3N895g5irpiXmnxUhREBB0n6+y8BHhs4nw==} hasBin: true type-check@0.4.0: @@ -3745,20 +3609,15 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.46.1: - resolution: {integrity: sha512-VHgijW803JafdSsDO8I761r3SHrgk4T00IdyQ+/UsthtgPRsBWQLqoSxOolxTpxRKi1kGXK0bSz4CoAc9ObqJA==} + 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@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} - engines: {node: '>=14.17'} - hasBin: true - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true @@ -3774,15 +3633,8 @@ packages: undefsafe@2.0.5: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - - unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} - engines: {node: '>=18'} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} @@ -3792,6 +3644,12 @@ packages: 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==} @@ -3807,8 +3665,8 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@13.0.0: - resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} + uuid@14.0.0: + resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} hasBin: true v8-compile-cache-lib@3.0.1: @@ -3818,20 +3676,16 @@ packages: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - validate-npm-package-name@6.0.2: - resolution: {integrity: sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==} - engines: {node: ^18.17.0 || >=20.5.0} - - viem@2.40.3: - resolution: {integrity: sha512-feYfEpbgjRkZYQpwcgxqkWzjxHI5LSDAjcGetHHwDRuX9BRQHUdV8ohrCosCYpdEhus/RknD3/bOd4qLYVPPuA==} + 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.1.10: - resolution: {integrity: sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==} + vite@7.3.0: + resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3870,18 +3724,18 @@ packages: yaml: optional: true - vitest@4.0.14: - resolution: {integrity: sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==} + 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.14 - '@vitest/browser-preview': 4.0.14 - '@vitest/browser-webdriverio': 4.0.14 - '@vitest/ui': 4.0.14 + '@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: @@ -3971,10 +3825,25 @@ packages: utf-8-validate: optional: true + ws@8.20.0: + resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + 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'} @@ -3999,12 +3868,21 @@ packages: 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.0 + pkijs: 3.3.3 '@adraffy/ens-normalize@1.11.1': {} @@ -4014,20 +3892,101 @@ snapshots: 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.46.0 + 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 @@ -4035,9 +3994,9 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@changesets/apply-release-plan@7.0.13': + '@changesets/apply-release-plan@7.0.14': dependencies: - '@changesets/config': 3.1.1 + '@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 @@ -4064,23 +4023,23 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.29.7(@types/node@24.10.1)': + '@changesets/cli@2.29.8(@types/node@25.3.0)': dependencies: - '@changesets/apply-release-plan': 7.0.13 + '@changesets/apply-release-plan': 7.0.14 '@changesets/assemble-release-plan': 6.0.9 '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.1 + '@changesets/config': 3.1.2 '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.3 - '@changesets/get-release-plan': 4.0.13 + '@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.5 + '@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.2(@types/node@24.10.1) + '@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 @@ -4097,7 +4056,7 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@changesets/config@3.1.1': + '@changesets/config@3.1.2': dependencies: '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.3 @@ -4118,12 +4077,12 @@ snapshots: picocolors: 1.1.1 semver: 7.7.3 - '@changesets/get-release-plan@4.0.13': + '@changesets/get-release-plan@4.0.14': dependencies: '@changesets/assemble-release-plan': 6.0.9 - '@changesets/config': 3.1.1 + '@changesets/config': 3.1.2 '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.5 + '@changesets/read': 0.6.6 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 @@ -4141,10 +4100,10 @@ snapshots: dependencies: picocolors: 1.1.1 - '@changesets/parse@0.4.1': + '@changesets/parse@0.4.2': dependencies: '@changesets/types': 6.1.0 - js-yaml: 3.14.1 + js-yaml: 4.1.1 '@changesets/pre@2.0.2': dependencies: @@ -4153,11 +4112,11 @@ snapshots: '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - '@changesets/read@0.6.5': + '@changesets/read@0.6.6': dependencies: '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.1 + '@changesets/parse': 0.4.2 '@changesets/types': 6.1.0 fs-extra: 7.0.1 p-filter: 2.1.0 @@ -4176,198 +4135,120 @@ snapshots: dependencies: '@changesets/types': 6.1.0 fs-extra: 7.0.1 - human-id: 4.1.2 + 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.7.1': + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.25.11': - optional: true - - '@esbuild/aix-ppc64@0.27.0': - optional: true - - '@esbuild/android-arm64@0.25.11': - optional: true - - '@esbuild/android-arm64@0.27.0': - optional: true - - '@esbuild/android-arm@0.25.11': - optional: true - - '@esbuild/android-arm@0.27.0': - optional: true - - '@esbuild/android-x64@0.25.11': - optional: true - - '@esbuild/android-x64@0.27.0': - optional: true - - '@esbuild/darwin-arm64@0.25.11': - optional: true - - '@esbuild/darwin-arm64@0.27.0': - optional: true - - '@esbuild/darwin-x64@0.25.11': - optional: true - - '@esbuild/darwin-x64@0.27.0': - optional: true - - '@esbuild/freebsd-arm64@0.25.11': - optional: true - - '@esbuild/freebsd-arm64@0.27.0': - optional: true - - '@esbuild/freebsd-x64@0.25.11': - optional: true - - '@esbuild/freebsd-x64@0.27.0': - optional: true - - '@esbuild/linux-arm64@0.25.11': - optional: true - - '@esbuild/linux-arm64@0.27.0': - optional: true - - '@esbuild/linux-arm@0.25.11': - optional: true - - '@esbuild/linux-arm@0.27.0': - optional: true - - '@esbuild/linux-ia32@0.25.11': - optional: true - - '@esbuild/linux-ia32@0.27.0': - optional: true - - '@esbuild/linux-loong64@0.25.11': - optional: true - - '@esbuild/linux-loong64@0.27.0': - optional: true - - '@esbuild/linux-mips64el@0.25.11': - optional: true - - '@esbuild/linux-mips64el@0.27.0': - optional: true - - '@esbuild/linux-ppc64@0.25.11': + '@esbuild/aix-ppc64@0.27.3': optional: true - '@esbuild/linux-ppc64@0.27.0': + '@esbuild/android-arm64@0.27.3': optional: true - '@esbuild/linux-riscv64@0.25.11': + '@esbuild/android-arm@0.27.3': optional: true - '@esbuild/linux-riscv64@0.27.0': + '@esbuild/android-x64@0.27.3': optional: true - '@esbuild/linux-s390x@0.25.11': + '@esbuild/darwin-arm64@0.27.3': optional: true - '@esbuild/linux-s390x@0.27.0': + '@esbuild/darwin-x64@0.27.3': optional: true - '@esbuild/linux-x64@0.25.11': + '@esbuild/freebsd-arm64@0.27.3': optional: true - '@esbuild/linux-x64@0.27.0': + '@esbuild/freebsd-x64@0.27.3': optional: true - '@esbuild/netbsd-arm64@0.25.11': + '@esbuild/linux-arm64@0.27.3': optional: true - '@esbuild/netbsd-arm64@0.27.0': + '@esbuild/linux-arm@0.27.3': optional: true - '@esbuild/netbsd-x64@0.25.11': + '@esbuild/linux-ia32@0.27.3': optional: true - '@esbuild/netbsd-x64@0.27.0': + '@esbuild/linux-loong64@0.27.3': optional: true - '@esbuild/openbsd-arm64@0.25.11': + '@esbuild/linux-mips64el@0.27.3': optional: true - '@esbuild/openbsd-arm64@0.27.0': + '@esbuild/linux-ppc64@0.27.3': optional: true - '@esbuild/openbsd-x64@0.25.11': + '@esbuild/linux-riscv64@0.27.3': optional: true - '@esbuild/openbsd-x64@0.27.0': + '@esbuild/linux-s390x@0.27.3': optional: true - '@esbuild/openharmony-arm64@0.25.11': + '@esbuild/linux-x64@0.27.3': optional: true - '@esbuild/openharmony-arm64@0.27.0': + '@esbuild/netbsd-arm64@0.27.3': optional: true - '@esbuild/sunos-x64@0.25.11': + '@esbuild/netbsd-x64@0.27.3': optional: true - '@esbuild/sunos-x64@0.27.0': + '@esbuild/openbsd-arm64@0.27.3': optional: true - '@esbuild/win32-arm64@0.25.11': + '@esbuild/openbsd-x64@0.27.3': optional: true - '@esbuild/win32-arm64@0.27.0': + '@esbuild/openharmony-arm64@0.27.3': optional: true - '@esbuild/win32-ia32@0.25.11': + '@esbuild/sunos-x64@0.27.3': optional: true - '@esbuild/win32-ia32@0.27.0': + '@esbuild/win32-arm64@0.27.3': optional: true - '@esbuild/win32-x64@0.25.11': + '@esbuild/win32-ia32@0.27.3': optional: true - '@esbuild/win32-x64@0.27.0': + '@esbuild/win32-x64@0.27.3': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.37.0)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2)': dependencies: - eslint: 9.37.0 + eslint: 9.39.2 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.1': {} + '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.0': + '@eslint/config-array@0.21.1': dependencies: - '@eslint/object-schema': 2.1.6 + '@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.0': + '@eslint/config-helpers@0.4.2': dependencies: - '@eslint/core': 0.16.0 + '@eslint/core': 0.17.0 - '@eslint/core@0.16.0': + '@eslint/core@0.17.0': dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.1': + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 debug: 4.4.3(supports-color@5.5.0) @@ -4375,19 +4256,19 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.0 + js-yaml: 4.1.1 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.37.0': {} + '@eslint/js@9.39.2': {} - '@eslint/object-schema@2.1.6': {} + '@eslint/object-schema@2.1.7': {} - '@eslint/plugin-kit@0.4.0': + '@eslint/plugin-kit@0.4.1': dependencies: - '@eslint/core': 0.16.0 + '@eslint/core': 0.17.0 levn: 0.4.1 '@humanfs/core@0.19.1': {} @@ -4401,7 +4282,7 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@img/colour@1.0.0': + '@img/colour@1.1.0': optional: true '@img/sharp-darwin-arm64@0.34.5': @@ -4486,7 +4367,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.7.1 + '@emnapi/runtime': 1.10.0 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -4498,25 +4379,22 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@inquirer/external-editor@1.0.2(@types/node@20.19.21)': + '@inquirer/external-editor@1.0.3(@types/node@25.3.0)': dependencies: - chardet: 2.1.0 - iconv-lite: 0.7.0 + chardet: 2.1.1 + iconv-lite: 0.7.1 optionalDependencies: - '@types/node': 20.19.21 + '@types/node': 25.3.0 - '@inquirer/external-editor@1.0.2(@types/node@24.10.1)': + '@jridgewell/gen-mapping@0.3.13': dependencies: - chardet: 2.1.0 - iconv-lite: 0.7.0 - optionalDependencies: - '@types/node': 24.10.1 - - '@isaacs/balanced-match@4.0.1': {} + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 - '@isaacs/brace-expansion@5.0.0': + '@jridgewell/remapping@2.3.5': dependencies: - '@isaacs/balanced-match': 4.0.1 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} @@ -4548,34 +4426,34 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 - '@next/env@15.5.9': {} + '@next/env@15.5.18': {} - '@next/eslint-plugin-next@15.5.5': + '@next/eslint-plugin-next@15.5.9': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@15.5.7': + '@next/swc-darwin-arm64@15.5.18': optional: true - '@next/swc-darwin-x64@15.5.7': + '@next/swc-darwin-x64@15.5.18': optional: true - '@next/swc-linux-arm64-gnu@15.5.7': + '@next/swc-linux-arm64-gnu@15.5.18': optional: true - '@next/swc-linux-arm64-musl@15.5.7': + '@next/swc-linux-arm64-musl@15.5.18': optional: true - '@next/swc-linux-x64-gnu@15.5.7': + '@next/swc-linux-x64-gnu@15.5.18': optional: true - '@next/swc-linux-x64-musl@15.5.7': + '@next/swc-linux-x64-musl@15.5.18': optional: true - '@next/swc-win32-arm64-msvc@15.5.7': + '@next/swc-win32-arm64-msvc@15.5.18': optional: true - '@next/swc-win32-x64-msvc@15.5.7': + '@next/swc-win32-x64-msvc@15.5.18': optional: true '@noble/ciphers@1.3.0': {} @@ -4584,10 +4462,6 @@ snapshots: dependencies: '@noble/hashes': 1.8.0 - '@noble/curves@1.9.7': - dependencies: - '@noble/hashes': 1.8.0 - '@noble/hashes@1.4.0': {} '@noble/hashes@1.8.0': {} @@ -4604,77 +4478,77 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@rollup/rollup-android-arm-eabi@4.52.4': + '@rollup/rollup-android-arm-eabi@4.53.4': optional: true - '@rollup/rollup-android-arm64@4.52.4': + '@rollup/rollup-android-arm64@4.53.4': optional: true - '@rollup/rollup-darwin-arm64@4.52.4': + '@rollup/rollup-darwin-arm64@4.53.4': optional: true - '@rollup/rollup-darwin-x64@4.52.4': + '@rollup/rollup-darwin-x64@4.53.4': optional: true - '@rollup/rollup-freebsd-arm64@4.52.4': + '@rollup/rollup-freebsd-arm64@4.53.4': optional: true - '@rollup/rollup-freebsd-x64@4.52.4': + '@rollup/rollup-freebsd-x64@4.53.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': + '@rollup/rollup-linux-arm-gnueabihf@4.53.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.4': + '@rollup/rollup-linux-arm-musleabihf@4.53.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.4': + '@rollup/rollup-linux-arm64-gnu@4.53.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.4': + '@rollup/rollup-linux-arm64-musl@4.53.4': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.4': + '@rollup/rollup-linux-loong64-gnu@4.53.4': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.4': + '@rollup/rollup-linux-ppc64-gnu@4.53.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.4': + '@rollup/rollup-linux-riscv64-gnu@4.53.4': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.4': + '@rollup/rollup-linux-riscv64-musl@4.53.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.4': + '@rollup/rollup-linux-s390x-gnu@4.53.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.4': + '@rollup/rollup-linux-x64-gnu@4.53.4': optional: true - '@rollup/rollup-linux-x64-musl@4.52.4': + '@rollup/rollup-linux-x64-musl@4.53.4': optional: true - '@rollup/rollup-openharmony-arm64@4.52.4': + '@rollup/rollup-openharmony-arm64@4.53.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.4': + '@rollup/rollup-win32-arm64-msvc@4.53.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.4': + '@rollup/rollup-win32-ia32-msvc@4.53.4': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.4': + '@rollup/rollup-win32-x64-gnu@4.53.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.4': + '@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.7 + '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 @@ -4683,8 +4557,6 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 - '@sindresorhus/merge-streams@2.3.0': {} - '@standard-schema/spec@1.0.0': {} '@swc/helpers@0.5.15': @@ -4693,7 +4565,7 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} - '@tsconfig/node10@1.0.11': {} + '@tsconfig/node10@1.0.12': {} '@tsconfig/node12@1.0.11': {} @@ -4701,17 +4573,23 @@ snapshots: '@tsconfig/node16@1.0.4': {} - '@turbo/gen@1.13.4(@types/node@20.19.21)(typescript@5.8.3)': + '@turbo/darwin-64@2.9.14': + optional: true + + '@turbo/darwin-arm64@2.9.14': + optional: true + + '@turbo/gen@1.13.4(@types/node@25.3.0)(typescript@6.0.3)': dependencies: - '@turbo/workspaces': 1.13.4(@types/node@20.19.21) + '@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@20.19.21) + 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@20.19.21)(typescript@5.8.3) + 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: @@ -4721,7 +4599,19 @@ snapshots: - supports-color - typescript - '@turbo/workspaces@1.13.4(@types/node@20.19.21)': + '@turbo/linux-64@2.9.14': + optional: true + + '@turbo/linux-arm64@2.9.14': + optional: true + + '@turbo/windows-64@2.9.14': + optional: true + + '@turbo/windows-arm64@2.9.14': + optional: true + + '@turbo/workspaces@1.13.4(@types/node@25.3.0)': dependencies: chalk: 2.4.2 commander: 10.0.1 @@ -4729,8 +4619,8 @@ snapshots: fast-glob: 3.3.3 fs-extra: 10.1.0 gradient-string: 2.0.2 - inquirer: 8.2.7(@types/node@20.19.21) - js-yaml: 4.1.0 + 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 @@ -4738,9 +4628,10 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@types/chai@5.2.2': + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 '@types/deep-eql@4.0.2': {} @@ -4749,7 +4640,7 @@ snapshots: '@types/glob@7.2.0': dependencies: '@types/minimatch': 6.0.0 - '@types/node': 24.10.1 + '@types/node': 25.3.0 '@types/inquirer@6.5.0': dependencies: @@ -4760,196 +4651,193 @@ snapshots: '@types/minimatch@6.0.0': dependencies: - minimatch: 10.1.1 + minimatch: 9.0.5 '@types/node@12.20.55': {} - '@types/node@20.19.21': + '@types/node@25.3.0': dependencies: - undici-types: 6.21.0 + undici-types: 7.18.2 - '@types/node@24.10.1': + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: - undici-types: 7.16.0 + '@types/react': 19.2.7 - '@types/react-dom@19.2.3(@types/react@19.2.6)': - dependencies: - '@types/react': 19.2.6 - - '@types/react@19.2.6': + '@types/react@19.2.7': dependencies: csstype: 3.2.3 '@types/through@0.0.33': dependencies: - '@types/node': 24.10.1 + '@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.46.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0)(typescript@5.8.3))(eslint@9.37.0)(typescript@5.8.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.1 - '@typescript-eslint/parser': 8.46.1(eslint@9.37.0)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.46.1 - '@typescript-eslint/type-utils': 8.46.1(eslint@9.37.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.46.1(eslint@9.37.0)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.46.1 - eslint: 9.37.0 - graphemer: 1.4.0 + '@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@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.46.1(eslint@9.37.0)(typescript@5.8.3)': + '@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.46.1 - '@typescript-eslint/types': 8.46.1 - '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.46.1 + '@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.37.0 - typescript: 5.8.3 + eslint: 9.39.2 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.46.1(typescript@5.8.3)': + '@typescript-eslint/project-service@8.50.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.46.1(typescript@5.8.3) - '@typescript-eslint/types': 8.46.1 + '@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: 5.8.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.46.1': + '@typescript-eslint/scope-manager@8.50.0': dependencies: - '@typescript-eslint/types': 8.46.1 - '@typescript-eslint/visitor-keys': 8.46.1 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 - '@typescript-eslint/tsconfig-utils@8.46.1(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.50.0(typescript@6.0.3)': dependencies: - typescript: 5.8.3 + typescript: 6.0.3 - '@typescript-eslint/type-utils@8.46.1(eslint@9.37.0)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2)(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.46.1 - '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.46.1(eslint@9.37.0)(typescript@5.8.3) + '@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.37.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + 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.46.1': {} + '@typescript-eslint/types@8.50.0': {} - '@typescript-eslint/typescript-estree@8.46.1(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.50.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.46.1(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.46.1(typescript@5.8.3) - '@typescript-eslint/types': 8.46.1 - '@typescript-eslint/visitor-keys': 8.46.1 + '@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) - fast-glob: 3.3.3 - is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.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.46.1(eslint@9.37.0)(typescript@5.8.3)': + '@typescript-eslint/utils@8.50.0(eslint@9.39.2)(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0) - '@typescript-eslint/scope-manager': 8.46.1 - '@typescript-eslint/types': 8.46.1 - '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.8.3) - eslint: 9.37.0 - typescript: 5.8.3 + '@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.46.1': + '@typescript-eslint/visitor-keys@8.50.0': dependencies: - '@typescript-eslint/types': 8.46.1 + '@typescript-eslint/types': 8.50.0 eslint-visitor-keys: 4.2.1 - '@vitest/coverage-v8@4.0.14(vitest@4.0.14(@types/node@24.10.1)(happy-dom@20.0.10))': + '@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.14 - ast-v8-to-istanbul: 0.3.8 + '@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-lib-source-maps: 5.0.6 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.14(@types/node@24.10.1)(happy-dom@20.0.10) - transitivePeerDependencies: - - supports-color + vitest: 4.0.18(@types/node@25.3.0)(happy-dom@20.8.9) - '@vitest/expect@4.0.14': + '@vitest/expect@4.0.18': dependencies: '@standard-schema/spec': 1.0.0 - '@types/chai': 5.2.2 - '@vitest/spy': 4.0.14 - '@vitest/utils': 4.0.14 + '@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.14(vite@7.1.10(@types/node@24.10.1))': + '@vitest/mocker@4.0.18(vite@7.3.0(@types/node@25.3.0))': dependencies: - '@vitest/spy': 4.0.14 + '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.1.10(@types/node@24.10.1) + vite: 7.3.0(@types/node@25.3.0) - '@vitest/pretty-format@4.0.14': + '@vitest/pretty-format@4.0.18': dependencies: tinyrainbow: 3.0.3 - '@vitest/runner@4.0.14': + '@vitest/runner@4.0.18': dependencies: - '@vitest/utils': 4.0.14 + '@vitest/utils': 4.0.18 pathe: 2.0.3 - '@vitest/snapshot@4.0.14': + '@vitest/snapshot@4.0.18': dependencies: - '@vitest/pretty-format': 4.0.14 + '@vitest/pretty-format': 4.0.18 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.14': {} + '@vitest/spy@4.0.18': {} - '@vitest/utils@4.0.14': + '@vitest/utils@4.0.18': dependencies: - '@vitest/pretty-format': 4.0.14 + '@vitest/pretty-format': 4.0.18 tinyrainbow: 3.0.3 - abitype@1.1.0(typescript@5.9.3): + abitype@1.1.0(typescript@6.0.3)(zod@4.2.0): optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 + zod: 4.2.0 - abitype@1.1.1(typescript@5.9.3): + abitype@1.2.2(typescript@6.0.3)(zod@4.2.0): optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 + zod: 4.2.0 acorn-jsx@5.3.2(acorn@8.15.0): dependencies: @@ -5018,7 +4906,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 is-string: 1.1.1 @@ -5030,7 +4918,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -5039,21 +4927,21 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + 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.0 + 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.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-shim-unscopables: 1.1.0 @@ -5062,26 +4950,28 @@ snapshots: array-buffer-byte-length: 1.0.2 call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 - asn1js@3.0.6: + asn1js@3.0.7: dependencies: pvtsutils: 1.3.6 - pvutils: 1.1.3 + 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.8: + ast-v8-to-istanbul@0.3.11: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 - js-tokens: 9.0.1 + js-tokens: 10.0.0 async-function@1.0.0: {} @@ -5091,8 +4981,12 @@ snapshots: 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: @@ -5116,10 +5010,22 @@ snapshots: 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 @@ -5151,16 +5057,12 @@ snapshots: no-case: 2.3.2 upper-case: 1.1.3 - caniuse-lite@1.0.30001760: {} + caniuse-lite@1.0.30001791: {} cbor2@1.12.0: {} chai@6.2.1: {} - chalk-template@1.1.2: - dependencies: - chalk: 5.6.2 - chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -5177,8 +5079,6 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.6.2: {} - change-case@3.1.0: dependencies: camel-case: 3.0.0 @@ -5202,7 +5102,7 @@ snapshots: chardet@0.7.0: {} - chardet@2.1.0: {} + chardet@2.1.1: {} chokidar@3.6.0: dependencies: @@ -5224,10 +5124,6 @@ snapshots: dependencies: restore-cursor: 3.1.0 - cli-cursor@5.0.0: - dependencies: - restore-cursor: 5.1.0 - cli-spinners@2.9.2: {} cli-width@3.0.0: {} @@ -5262,8 +5158,6 @@ snapshots: commander@10.0.1: {} - commander@13.1.0: {} - concat-map@0.0.1: {} concurrently@9.2.1: @@ -5280,16 +5174,9 @@ snapshots: snake-case: 2.1.0 upper-case: 1.1.3 - core-js-pure@3.46.0: {} + convert-source-map@2.0.0: {} - cosmiconfig@9.0.0(typescript@5.9.3): - dependencies: - env-paths: 2.2.1 - import-fresh: 3.3.1 - js-yaml: 4.1.0 - parse-json: 5.2.0 - optionalDependencies: - typescript: 5.9.3 + core-js-pure@3.47.0: {} create-require@1.1.1: {} @@ -5385,7 +5272,7 @@ snapshots: dotenv@16.0.3: {} - dotenv@17.2.3: {} + dotenv@17.3.1: {} dunder-proto@1.0.1: dependencies: @@ -5393,10 +5280,7 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - effect@3.19.6: - dependencies: - '@standard-schema/spec': 1.0.0 - fast-check: 3.23.2 + electron-to-chromium@1.5.267: {} emoji-regex@10.6.0: {} @@ -5407,13 +5291,9 @@ snapshots: ansi-colors: 4.1.3 strip-ansi: 6.0.1 - env-paths@2.2.1: {} - - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 + entities@7.0.1: {} - es-abstract@1.24.0: + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 @@ -5474,12 +5354,12 @@ snapshots: es-errors@1.3.0: {} - es-iterator-helpers@1.2.1: + 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.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-set-tostringtag: 2.1.0 function-bind: 1.1.2 @@ -5516,63 +5396,34 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.25.11: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.11 - '@esbuild/android-arm': 0.25.11 - '@esbuild/android-arm64': 0.25.11 - '@esbuild/android-x64': 0.25.11 - '@esbuild/darwin-arm64': 0.25.11 - '@esbuild/darwin-x64': 0.25.11 - '@esbuild/freebsd-arm64': 0.25.11 - '@esbuild/freebsd-x64': 0.25.11 - '@esbuild/linux-arm': 0.25.11 - '@esbuild/linux-arm64': 0.25.11 - '@esbuild/linux-ia32': 0.25.11 - '@esbuild/linux-loong64': 0.25.11 - '@esbuild/linux-mips64el': 0.25.11 - '@esbuild/linux-ppc64': 0.25.11 - '@esbuild/linux-riscv64': 0.25.11 - '@esbuild/linux-s390x': 0.25.11 - '@esbuild/linux-x64': 0.25.11 - '@esbuild/netbsd-arm64': 0.25.11 - '@esbuild/netbsd-x64': 0.25.11 - '@esbuild/openbsd-arm64': 0.25.11 - '@esbuild/openbsd-x64': 0.25.11 - '@esbuild/openharmony-arm64': 0.25.11 - '@esbuild/sunos-x64': 0.25.11 - '@esbuild/win32-arm64': 0.25.11 - '@esbuild/win32-ia32': 0.25.11 - '@esbuild/win32-x64': 0.25.11 - - esbuild@0.27.0: + esbuild@0.27.3: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.0 - '@esbuild/android-arm': 0.27.0 - '@esbuild/android-arm64': 0.27.0 - '@esbuild/android-x64': 0.27.0 - '@esbuild/darwin-arm64': 0.27.0 - '@esbuild/darwin-x64': 0.27.0 - '@esbuild/freebsd-arm64': 0.27.0 - '@esbuild/freebsd-x64': 0.27.0 - '@esbuild/linux-arm': 0.27.0 - '@esbuild/linux-arm64': 0.27.0 - '@esbuild/linux-ia32': 0.27.0 - '@esbuild/linux-loong64': 0.27.0 - '@esbuild/linux-mips64el': 0.27.0 - '@esbuild/linux-ppc64': 0.27.0 - '@esbuild/linux-riscv64': 0.27.0 - '@esbuild/linux-s390x': 0.27.0 - '@esbuild/linux-x64': 0.27.0 - '@esbuild/netbsd-arm64': 0.27.0 - '@esbuild/netbsd-x64': 0.27.0 - '@esbuild/openbsd-arm64': 0.27.0 - '@esbuild/openbsd-x64': 0.27.0 - '@esbuild/openharmony-arm64': 0.27.0 - '@esbuild/sunos-x64': 0.27.0 - '@esbuild/win32-arm64': 0.27.0 - '@esbuild/win32-ia32': 0.27.0 - '@esbuild/win32-x64': 0.27.0 + '@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: {} @@ -5588,25 +5439,32 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@9.1.2(eslint@9.37.0): + eslint-config-prettier@10.1.8(eslint@9.39.2): dependencies: - eslint: 9.37.0 + eslint: 9.39.2 eslint-plugin-only-warn@1.1.0: {} - eslint-plugin-react-hooks@5.2.0(eslint@9.37.0): + eslint-plugin-react-hooks@7.0.1(eslint@9.39.2): dependencies: - eslint: 9.37.0 + '@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.37.0): + 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.1 - eslint: 9.37.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 @@ -5620,11 +5478,11 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-turbo@2.5.8(eslint@9.37.0)(turbo@2.6.3): + eslint-plugin-turbo@2.6.3(eslint@9.39.2)(turbo@2.9.14): dependencies: dotenv: 16.0.3 - eslint: 9.37.0 - turbo: 2.6.3 + eslint: 9.39.2 + turbo: 2.9.14 eslint-scope@8.4.0: dependencies: @@ -5635,21 +5493,20 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.37.0: + eslint@9.39.2: dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.4.0 - '@eslint/core': 0.16.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.37.0 - '@eslint/plugin-kit': 0.4.0 + '@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 - '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 @@ -5713,7 +5570,7 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - expect-type@1.2.2: {} + expect-type@1.3.0: {} extendable-error@0.1.7: {} @@ -5725,10 +5582,6 @@ snapshots: fake-indexeddb@6.2.5: {} - fast-check@3.23.2: - dependencies: - pure-rand: 6.1.0 - fast-deep-equal@3.1.3: {} fast-glob@3.3.1: @@ -5830,6 +5683,8 @@ snapshots: generator-function@2.0.1: {} + gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} get-east-asian-width@1.4.0: {} @@ -5876,11 +5731,11 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@13.0.0: + glob@13.0.6: dependencies: - minimatch: 10.1.1 - minipass: 7.1.2 - path-scurry: 2.0.0 + minimatch: 10.2.2 + minipass: 7.1.3 + path-scurry: 2.0.2 glob@7.2.3: dependencies: @@ -5893,7 +5748,7 @@ snapshots: globals@14.0.0: {} - globals@15.15.0: {} + globals@16.5.0: {} globalthis@1.0.4: dependencies: @@ -5920,15 +5775,6 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 - globby@14.1.0: - dependencies: - '@sindresorhus/merge-streams': 2.3.0 - fast-glob: 3.3.3 - ignore: 7.0.5 - path-type: 6.0.0 - slash: 5.1.0 - unicorn-magic: 0.3.0 - gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -5938,8 +5784,6 @@ snapshots: chalk: 4.1.2 tinygradient: 1.1.5 - graphemer@1.4.0: {} - handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -5949,11 +5793,17 @@ snapshots: optionalDependencies: uglify-js: 3.19.3 - happy-dom@20.0.10: + happy-dom@20.8.9: dependencies: - '@types/node': 20.19.21 + '@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: {} @@ -5984,9 +5834,11 @@ snapshots: no-case: 2.3.2 upper-case: 1.1.3 - hosted-git-info@8.1.0: + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: dependencies: - lru-cache: 10.4.3 + hermes-estree: 0.25.1 html-escaper@2.0.2: {} @@ -6004,7 +5856,7 @@ snapshots: transitivePeerDependencies: - supports-color - human-id@4.1.2: {} + human-id@4.1.3: {} human-signals@2.1.0: {} @@ -6012,7 +5864,7 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.0: + iconv-lite@0.7.1: dependencies: safer-buffer: 2.1.2 @@ -6060,9 +5912,9 @@ snapshots: strip-ansi: 6.0.1 through: 2.3.8 - inquirer@8.2.7(@types/node@20.19.21): + inquirer@8.2.7(@types/node@25.3.0): dependencies: - '@inquirer/external-editor': 1.0.2(@types/node@20.19.21) + '@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 @@ -6086,7 +5938,7 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 - ip-address@10.0.1: {} + ip-address@10.1.0: {} is-array-buffer@3.0.5: dependencies: @@ -6094,8 +5946,6 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 - is-arrayish@0.2.1: {} - is-async-function@2.1.1: dependencies: async-function: 1.0.0 @@ -6156,8 +6006,6 @@ snapshots: is-interactive@1.0.0: {} - is-interactive@2.0.0: {} - is-lower-case@1.1.3: dependencies: lower-case: 1.1.4 @@ -6213,10 +6061,6 @@ snapshots: is-unicode-supported@0.1.0: {} - is-unicode-supported@1.3.0: {} - - is-unicode-supported@2.1.0: {} - is-upper-case@1.1.2: dependencies: upper-case: 1.1.3 @@ -6252,14 +6096,6 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@5.0.6: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - debug: 4.4.3(supports-color@5.5.0) - istanbul-lib-coverage: 3.2.2 - transitivePeerDependencies: - - supports-color - istanbul-reports@3.2.0: dependencies: html-escaper: 2.0.2 @@ -6274,30 +6110,30 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 - js-tokens@4.0.0: {} + js-tokens@10.0.0: {} - js-tokens@9.0.1: {} + js-tokens@4.0.0: {} - js-yaml@3.14.1: + js-yaml@3.14.2: dependencies: argparse: 1.0.10 esprima: 4.0.1 - js-yaml@4.1.0: + 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-parse-even-better-errors@2.3.1: {} - json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} - jsonc-parser@3.3.1: {} + json5@2.2.3: {} jsonfile@4.0.0: optionalDependencies: @@ -6322,58 +6158,54 @@ snapshots: dependencies: json-buffer: 3.0.1 - kleur@3.0.3: {} - - lefthook-darwin-arm64@2.0.4: + lefthook-darwin-arm64@2.1.6: optional: true - lefthook-darwin-x64@2.0.4: + lefthook-darwin-x64@2.1.6: optional: true - lefthook-freebsd-arm64@2.0.4: + lefthook-freebsd-arm64@2.1.6: optional: true - lefthook-freebsd-x64@2.0.4: + lefthook-freebsd-x64@2.1.6: optional: true - lefthook-linux-arm64@2.0.4: + lefthook-linux-arm64@2.1.6: optional: true - lefthook-linux-x64@2.0.4: + lefthook-linux-x64@2.1.6: optional: true - lefthook-openbsd-arm64@2.0.4: + lefthook-openbsd-arm64@2.1.6: optional: true - lefthook-openbsd-x64@2.0.4: + lefthook-openbsd-x64@2.1.6: optional: true - lefthook-windows-arm64@2.0.4: + lefthook-windows-arm64@2.1.6: optional: true - lefthook-windows-x64@2.0.4: + lefthook-windows-x64@2.1.6: optional: true - lefthook@2.0.4: + lefthook@2.1.6: optionalDependencies: - lefthook-darwin-arm64: 2.0.4 - lefthook-darwin-x64: 2.0.4 - lefthook-freebsd-arm64: 2.0.4 - lefthook-freebsd-x64: 2.0.4 - lefthook-linux-arm64: 2.0.4 - lefthook-linux-x64: 2.0.4 - lefthook-openbsd-arm64: 2.0.4 - lefthook-openbsd-x64: 2.0.4 - lefthook-windows-arm64: 2.0.4 - lefthook-windows-x64: 2.0.4 + 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 - lines-and-columns@1.2.4: {} - locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -6399,11 +6231,6 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 - log-symbols@6.0.0: - dependencies: - chalk: 5.6.2 - is-unicode-supported: 1.3.0 - loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -6414,9 +6241,11 @@ snapshots: lower-case@1.1.4: {} - lru-cache@10.4.3: {} + lru-cache@11.2.4: {} - lru-cache@11.2.2: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 lru-cache@7.18.3: {} @@ -6432,7 +6261,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 make-error@1.3.6: {} @@ -6449,11 +6278,9 @@ snapshots: mimic-fn@2.1.0: {} - mimic-function@5.0.1: {} - - minimatch@10.1.1: + minimatch@10.2.2: dependencies: - '@isaacs/brace-expansion': 5.0.0 + brace-expansion: 5.0.3 minimatch@3.1.2: dependencies: @@ -6465,11 +6292,11 @@ snapshots: minimist@1.2.8: {} - minipass@7.1.2: {} + minipass@7.1.3: {} - mipd@0.0.7(typescript@5.9.3): + mipd@0.0.7(typescript@6.0.3): optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 mkdirp@0.5.6: dependencies: @@ -6481,7 +6308,7 @@ snapshots: mute-stream@0.0.8: {} - nanoid@3.3.11: {} + nanoid@3.3.12: {} natural-compare@1.4.0: {} @@ -6489,24 +6316,24 @@ snapshots: netmask@2.0.2: {} - next@15.5.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + next@15.5.18(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - '@next/env': 15.5.9 + '@next/env': 15.5.18 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001760 + caniuse-lite: 1.0.30001791 postcss: 8.4.31 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - styled-jsx: 5.1.6(react@19.2.0) + 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.7 - '@next/swc-darwin-x64': 15.5.7 - '@next/swc-linux-arm64-gnu': 15.5.7 - '@next/swc-linux-arm64-musl': 15.5.7 - '@next/swc-linux-x64-gnu': 15.5.7 - '@next/swc-linux-x64-musl': 15.5.7 - '@next/swc-win32-arm64-msvc': 15.5.7 - '@next/swc-win32-x64-msvc': 15.5.7 + '@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' @@ -6528,16 +6355,18 @@ snapshots: isbinaryfile: 4.0.10 lodash.get: 4.4.2 mkdirp: 0.5.6 - resolve: 1.22.10 + resolve: 1.22.11 - nodemon@3.1.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: 3.1.2 + minimatch: 10.2.2 pstree.remy: 1.1.8 - semver: 7.7.3 + semver: 7.7.4 simple-update-notifier: 2.0.0 supports-color: 5.5.0 touch: 3.1.1 @@ -6545,13 +6374,6 @@ snapshots: normalize-path@3.0.0: {} - npm-package-arg@12.0.2: - dependencies: - hosted-git-info: 8.1.0 - proc-log: 5.0.0 - semver: 7.7.3 - validate-npm-package-name: 6.0.2 - npm-run-path@4.0.1: dependencies: path-key: 3.1.1 @@ -6582,7 +6404,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 object.values@1.2.1: @@ -6602,10 +6424,6 @@ snapshots: dependencies: mimic-fn: 2.1.0 - onetime@7.0.0: - dependencies: - mimic-function: 5.0.1 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -6638,18 +6456,6 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 - ora@8.2.0: - dependencies: - chalk: 5.6.2 - cli-cursor: 5.0.0 - cli-spinners: 2.9.2 - is-interactive: 2.0.0 - is-unicode-supported: 2.1.0 - log-symbols: 6.0.0 - stdin-discarder: 0.2.2 - string-width: 7.2.0 - strip-ansi: 7.1.2 - os-tmpdir@1.0.2: {} outdent@0.5.0: {} @@ -6660,7 +6466,7 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 - ox@0.9.17(typescript@5.9.3): + ox@0.9.17(typescript@6.0.3)(zod@4.2.0): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -6668,10 +6474,10 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.1(typescript@5.9.3) + abitype: 1.2.2(typescript@6.0.3)(zod@4.2.0) eventemitter3: 5.0.1 optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - zod @@ -6735,13 +6541,6 @@ snapshots: dependencies: callsites: 3.1.0 - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.27.1 - error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - pascal-case@2.0.1: dependencies: camel-case: 3.0.0 @@ -6759,15 +6558,13 @@ snapshots: path-parse@1.0.7: {} - path-scurry@2.0.0: + path-scurry@2.0.2: dependencies: - lru-cache: 11.2.2 - minipass: 7.1.2 + lru-cache: 11.2.4 + minipass: 7.1.3 path-type@4.0.0: {} - path-type@6.0.0: {} - pathe@2.0.3: {} picocolors@1.1.1: {} @@ -6778,26 +6575,26 @@ snapshots: pify@4.0.1: {} - pkijs@3.3.0: + pkijs@3.3.3: dependencies: '@noble/hashes': 1.4.0 - asn1js: 3.0.6 + asn1js: 3.0.7 bytestreamjs: 2.0.1 pvtsutils: 1.3.6 - pvutils: 1.1.3 + pvutils: 1.1.5 tslib: 2.8.1 possible-typed-array-names@1.1.0: {} postcss@8.4.31: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.6: + postcss@8.5.14: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -6805,14 +6602,7 @@ snapshots: prettier@2.8.8: {} - prettier@3.6.2: {} - - proc-log@5.0.0: {} - - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 + prettier@3.8.3: {} prop-types@15.8.1: dependencies: @@ -6839,13 +6629,11 @@ snapshots: punycode@2.3.1: {} - pure-rand@6.1.0: {} - pvtsutils@1.3.6: dependencies: tslib: 2.8.1 - pvutils@1.1.3: {} + pvutils@1.1.5: {} quansync@0.2.11: {} @@ -6858,27 +6646,22 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-dom@19.2.0(react@19.2.0): + react-dom@19.2.3(react@19.2.3): dependencies: - react: 19.2.0 + react: 19.2.3 scheduler: 0.27.0 react-is@16.13.1: {} - react@19.2.0: {} + react@19.2.3: {} read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 - js-yaml: 3.14.1 + js-yaml: 3.14.2 pify: 4.0.1 strip-bom: 3.0.0 - read-yaml-file@2.1.0: - dependencies: - js-yaml: 4.1.0 - strip-bom: 4.0.0 - readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -6893,7 +6676,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -6924,7 +6707,7 @@ snapshots: resolve-from@5.0.0: {} - resolve@1.22.10: + resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 @@ -6941,48 +6724,43 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 - restore-cursor@5.1.0: - dependencies: - onetime: 7.0.0 - signal-exit: 4.1.0 - reusify@1.1.0: {} rimraf@3.0.2: dependencies: glob: 7.2.3 - rimraf@6.1.2: + rimraf@6.1.3: dependencies: - glob: 13.0.0 + glob: 13.0.6 package-json-from-dist: 1.0.1 - rollup@4.52.4: + rollup@4.53.4: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.4 - '@rollup/rollup-android-arm64': 4.52.4 - '@rollup/rollup-darwin-arm64': 4.52.4 - '@rollup/rollup-darwin-x64': 4.52.4 - '@rollup/rollup-freebsd-arm64': 4.52.4 - '@rollup/rollup-freebsd-x64': 4.52.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.4 - '@rollup/rollup-linux-arm-musleabihf': 4.52.4 - '@rollup/rollup-linux-arm64-gnu': 4.52.4 - '@rollup/rollup-linux-arm64-musl': 4.52.4 - '@rollup/rollup-linux-loong64-gnu': 4.52.4 - '@rollup/rollup-linux-ppc64-gnu': 4.52.4 - '@rollup/rollup-linux-riscv64-gnu': 4.52.4 - '@rollup/rollup-linux-riscv64-musl': 4.52.4 - '@rollup/rollup-linux-s390x-gnu': 4.52.4 - '@rollup/rollup-linux-x64-gnu': 4.52.4 - '@rollup/rollup-linux-x64-musl': 4.52.4 - '@rollup/rollup-openharmony-arm64': 4.52.4 - '@rollup/rollup-win32-arm64-msvc': 4.52.4 - '@rollup/rollup-win32-ia32-msvc': 4.52.4 - '@rollup/rollup-win32-x64-gnu': 4.52.4 - '@rollup/rollup-win32-x64-msvc': 4.52.4 + '@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: {} @@ -7028,6 +6806,8 @@ snapshots: semver@7.7.3: {} + semver@7.7.4: {} + sentence-case@2.1.1: dependencies: no-case: 2.3.2 @@ -7057,9 +6837,9 @@ snapshots: sharp@0.34.5: dependencies: - '@img/colour': 1.0.0 + '@img/colour': 1.1.0 detect-libc: 2.1.2 - semver: 7.7.3 + semver: 7.7.4 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -7131,14 +6911,10 @@ snapshots: simple-update-notifier@2.0.0: dependencies: - semver: 7.7.3 - - sisteransi@1.0.5: {} + semver: 7.7.4 slash@3.0.0: {} - slash@5.1.0: {} - smart-buffer@4.2.0: {} snake-case@2.1.0: @@ -7155,7 +6931,7 @@ snapshots: socks@2.8.7: dependencies: - ip-address: 10.0.1 + ip-address: 10.1.0 smart-buffer: 4.2.0 source-map-js@1.2.1: {} @@ -7173,8 +6949,6 @@ snapshots: std-env@3.10.0: {} - stdin-discarder@0.2.2: {} - stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 @@ -7197,7 +6971,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -7211,7 +6985,7 @@ snapshots: string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 string.prototype.trim@1.2.10: dependencies: @@ -7219,7 +6993,7 @@ snapshots: call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 @@ -7250,18 +7024,16 @@ snapshots: strip-bom@3.0.0: {} - strip-bom@4.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.0): + styled-jsx@5.1.6(react@19.2.3): dependencies: client-only: 0.0.1 - react: 19.2.0 + react: 19.2.3 supports-color@5.5.0: dependencies: @@ -7282,39 +7054,50 @@ snapshots: lower-case: 1.1.4 upper-case: 1.1.3 - syncpack@13.0.4(typescript@5.9.3): - dependencies: - chalk: 5.6.2 - chalk-template: 1.1.2 - commander: 13.1.0 - cosmiconfig: 9.0.0(typescript@5.9.3) - effect: 3.19.6 - enquirer: 2.4.1 - fast-check: 3.23.2 - globby: 14.1.0 - jsonc-parser: 3.3.1 - minimatch: 9.0.5 - npm-package-arg: 12.0.2 - ora: 8.2.0 - prompts: 2.4.2 - read-yaml-file: 2.1.0 - semver: 7.7.3 - tightrope: 0.2.0 - ts-toolbelt: 9.6.0 - transitivePeerDependencies: - - typescript + 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: {} - tightrope@0.2.0: {} - tinybench@2.9.0: {} tinycolor2@1.6.0: {} - tinyexec@0.3.2: {} + tinyexec@1.0.2: {} tinyglobby@0.2.15: dependencies: @@ -7345,87 +7128,40 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@2.1.0(typescript@5.8.3): + ts-api-utils@2.1.0(typescript@6.0.3): dependencies: - typescript: 5.8.3 + typescript: 6.0.3 - ts-node@10.9.2(@types/node@20.19.21)(typescript@5.8.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.11 + '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.19.21 + '@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: 5.8.3 + typescript: 6.0.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - ts-toolbelt@9.6.0: {} - tslib@1.14.1: {} tslib@2.8.1: {} - turbo-darwin-64@2.6.1: - optional: true - - turbo-darwin-64@2.6.3: - optional: true - - turbo-darwin-arm64@2.6.1: - optional: true - - turbo-darwin-arm64@2.6.3: - optional: true - - turbo-linux-64@2.6.1: - optional: true - - turbo-linux-64@2.6.3: - optional: true - - turbo-linux-arm64@2.6.1: - optional: true - - turbo-linux-arm64@2.6.3: - optional: true - - turbo-windows-64@2.6.1: - optional: true - - turbo-windows-64@2.6.3: - optional: true - - turbo-windows-arm64@2.6.1: - optional: true - - turbo-windows-arm64@2.6.3: - optional: true - - turbo@2.6.1: + turbo@2.9.14: optionalDependencies: - turbo-darwin-64: 2.6.1 - turbo-darwin-arm64: 2.6.1 - turbo-linux-64: 2.6.1 - turbo-linux-arm64: 2.6.1 - turbo-windows-64: 2.6.1 - turbo-windows-arm64: 2.6.1 - - turbo@2.6.3: - optionalDependencies: - turbo-darwin-64: 2.6.3 - turbo-darwin-arm64: 2.6.3 - turbo-linux-64: 2.6.3 - turbo-linux-arm64: 2.6.3 - turbo-windows-64: 2.6.3 - turbo-windows-arm64: 2.6.3 + '@turbo/darwin-64': 2.9.14 + '@turbo/darwin-arm64': 2.9.14 + '@turbo/linux-64': 2.9.14 + '@turbo/linux-arm64': 2.9.14 + '@turbo/windows-64': 2.9.14 + '@turbo/windows-arm64': 2.9.14 type-check@0.4.0: dependencies: @@ -7466,20 +7202,18 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.46.1(eslint@9.37.0)(typescript@5.8.3): + typescript-eslint@8.50.0(eslint@9.39.2)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.46.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0)(typescript@5.8.3))(eslint@9.37.0)(typescript@5.8.3) - '@typescript-eslint/parser': 8.46.1(eslint@9.37.0)(typescript@5.8.3) - '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.46.1(eslint@9.37.0)(typescript@5.8.3) - eslint: 9.37.0 - typescript: 5.8.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) + '@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@5.8.3: {} - - typescript@5.9.3: {} + typescript@6.0.3: {} uglify-js@3.19.3: optional: true @@ -7493,16 +7227,18 @@ snapshots: undefsafe@2.0.5: {} - undici-types@6.21.0: {} - - undici-types@7.16.0: {} - - unicorn-magic@0.3.0: {} + 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 @@ -7520,68 +7256,66 @@ snapshots: util-deprecate@1.0.2: {} - uuid@13.0.0: {} + uuid@14.0.0: {} v8-compile-cache-lib@3.0.1: {} validate-npm-package-name@5.0.1: {} - validate-npm-package-name@6.0.2: {} - - viem@2.40.3(typescript@5.9.3): + 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@5.9.3) + 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@5.9.3) + ox: 0.9.17(typescript@6.0.3)(zod@4.2.0) ws: 8.18.3 optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - bufferutil - utf-8-validate - zod - vite@7.1.10(@types/node@24.10.1): + vite@7.3.0(@types/node@25.3.0): dependencies: - esbuild: 0.25.11 + esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.52.4 + postcss: 8.5.14 + rollup: 4.53.4 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 25.3.0 fsevents: 2.3.3 - vitest@4.0.14(@types/node@24.10.1)(happy-dom@20.0.10): + vitest@4.0.18(@types/node@25.3.0)(happy-dom@20.8.9): dependencies: - '@vitest/expect': 4.0.14 - '@vitest/mocker': 4.0.14(vite@7.1.10(@types/node@24.10.1)) - '@vitest/pretty-format': 4.0.14 - '@vitest/runner': 4.0.14 - '@vitest/snapshot': 4.0.14 - '@vitest/spy': 4.0.14 - '@vitest/utils': 4.0.14 + '@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.2.2 + 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: 0.3.2 + tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.1.10(@types/node@24.10.1) + vite: 7.3.0(@types/node@25.3.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.10.1 - happy-dom: 20.0.10 + '@types/node': 25.3.0 + happy-dom: 20.8.9 transitivePeerDependencies: - jiti - less @@ -7677,8 +7411,12 @@ snapshots: 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: {} @@ -7705,3 +7443,9 @@ snapshots: 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 index 59a78ba9a..bd308b020 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,7 @@ +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/* diff --git a/repo/eslint-config/CHANGELOG.md b/repo/eslint-config/CHANGELOG.md index ddc6085a8..2df72f2e7 100644 --- a/repo/eslint-config/CHANGELOG.md +++ b/repo/eslint-config/CHANGELOG.md @@ -1,5 +1,12 @@ # @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 diff --git a/repo/eslint-config/package.json b/repo/eslint-config/package.json index 7fa53be38..4b4d2db7f 100644 --- a/repo/eslint-config/package.json +++ b/repo/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@repo/eslint-config", - "version": "0.0.1-beta.1", + "version": "0.0.1", "type": "module", "private": true, "exports": { @@ -9,16 +9,16 @@ "./react-internal": "./react-internal.js" }, "devDependencies": { - "@eslint/js": "^9.28.0", - "@next/eslint-plugin-next": "^15.3.3", - "eslint": "^9.28.0", - "eslint-config-prettier": "^9.1.0", + "@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": "^5.2.0", - "eslint-plugin-turbo": "^2.5.4", - "globals": "^15.15.0", - "typescript": "^5.8.3", - "typescript-eslint": "^8.33.1" + "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/typescript-config/CHANGELOG.md b/repo/typescript-config/CHANGELOG.md index 611dc70b2..3e5ecbabf 100644 --- a/repo/typescript-config/CHANGELOG.md +++ b/repo/typescript-config/CHANGELOG.md @@ -1,5 +1,12 @@ # @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 diff --git a/repo/typescript-config/next-css-side-effect.d.ts b/repo/typescript-config/next-css-side-effect.d.ts new file mode 100644 index 000000000..6a748979a --- /dev/null +++ b/repo/typescript-config/next-css-side-effect.d.ts @@ -0,0 +1,2 @@ +/** 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/package.json b/repo/typescript-config/package.json index cb34e9260..ed931bce6 100644 --- a/repo/typescript-config/package.json +++ b/repo/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@repo/typescript-config", - "version": "0.0.1-beta.1", + "version": "0.0.1", "private": true, "license": "MIT", "publishConfig": { diff --git a/repo/ui/CHANGELOG.md b/repo/ui/CHANGELOG.md index 8994e0294..232f9accb 100644 --- a/repo/ui/CHANGELOG.md +++ b/repo/ui/CHANGELOG.md @@ -1,5 +1,12 @@ # @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 diff --git a/repo/ui/eslint.config.mjs b/repo/ui/eslint.config.js similarity index 100% rename from repo/ui/eslint.config.mjs rename to repo/ui/eslint.config.js diff --git a/repo/ui/package.json b/repo/ui/package.json index 226eed0d9..4c3634444 100644 --- a/repo/ui/package.json +++ b/repo/ui/package.json @@ -1,7 +1,8 @@ { "name": "@repo/ui", - "version": "0.0.1-beta.1", + "version": "0.0.1", "private": true, + "type": "module", "exports": { "./button": "./src/button.tsx", "./card": "./src/card.tsx", @@ -13,16 +14,16 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", - "@repo/typescript-config": "workspace:*", + "@repo/eslint-config": "workspace:^", + "@repo/typescript-config": "workspace:^", "@turbo/gen": "^1.13.4", - "@types/node": "^20.17.57", - "@types/react": "^19.2.6", + "@types/node": "^25.3.0", + "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", - "typescript": "^5.8.3" + "typescript": "^6.0.3" }, "dependencies": { - "react": "^19.1.0", - "react-dom": "^19.1.0" + "react": "^19.2.3", + "react-dom": "^19.2.3" } } diff --git a/turbo.json b/turbo.json index fcf046270..139d2ea57 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,5 @@ { - "$schema": "https://turbo.build/schema.json", + "$schema": "https://v2-8-10.turborepo.dev/schema.json", "ui": "tui", "tasks": { "build": { @@ -26,6 +26,7 @@ "repo/**/*.tsx", "test/**/*.ts", "test/**/*.tsx", + "test/**/*.js", "test/**/*.mjs" ], "outputs": []