diff --git a/.changeset/align-mcp-zod.md b/.changeset/align-mcp-zod.md new file mode 100644 index 00000000..92e04a84 --- /dev/null +++ b/.changeset/align-mcp-zod.md @@ -0,0 +1,5 @@ +--- +'@sei-js/mcp-server': patch +--- + +Align `zod` with `@modelcontextprotocol/sdk` so both resolve the same schema types. diff --git a/.changeset/fix-global-wallet-release-blockers.md b/.changeset/fix-global-wallet-release-blockers.md new file mode 100644 index 00000000..f94865bd --- /dev/null +++ b/.changeset/fix-global-wallet-release-blockers.md @@ -0,0 +1,18 @@ +--- +'@sei-js/sei-global-wallet': minor +--- + +Harden Sei Global Wallet browser, EIP-6963, and packaging behavior without changing what existing installs resolve. + +New exports: `registerEIP6963Provider`, `unregisterEIP6963Provider`, `eip6963ProviderInfo`, and `registerSolanaStandard`. + +- Raise `@dynamic-labs/global-wallet-client` to `^4.96.3`, so applications inherit Dynamic's transitive fixes without waiting for a release here. +- Initialize Dynamic's required `global` and `process` aliases before loading its modules in browsers and edge-like SSR runtimes, without consumer bundler configuration and without replacing consumer-defined values. `process.env.NODE_ENV` defaults to `production` so libraries loaded afterwards do not take a development branch inside a production bundle. +- Dispatch the initial EIP-6963 announcement, re-announce on every provider request, expose cleanup helpers, use stable UUIDv4 provider metadata, and replace the non-square wordmark with the unmodified square black mark from the official Sei brand kit. +- Keep the root, `./eip6963`, and `./ethereum` entrypoints resolvable with no optional peer installed, including for types. +- Verify real npm and Bun consumers, all five entrypoints, EIP-6963 and Solana registration, ZeroDev resolution, esbuild and Vite browser runtimes, SSR imports, types, audits, and package contents. + +Every optional peer range stays a superset of the 1.4.1 contract, and `@wallet-standard/wallet` stays a direct dependency, so no existing install changes how it resolves. The `events` dependency is present so bundlers can resolve the bare `events` specifier that `@zerodev/sdk` imports; browser builds of the `./zerodev` path fail without it. Dynamic 4.96.3's own peer contract is the version set this release is verified against; it is documented under [Optional peer versions](https://github.com/sei-protocol/sei-js/blob/main/packages/sei-global-wallet/README.md#optional-peer-versions) as guidance rather than enforced through narrowed ranges. + +> [!WARNING] +> This release carries a temporary consumer security waiver: Dynamic transitively pins vulnerable `axios` and `uuid`, and dependency overrides in a library do not propagate to applications. See [Required consumer overrides](https://github.com/sei-protocol/sei-js/blob/main/packages/sei-global-wallet/README.md#required-consumer-overrides) for the exact override blocks and the accepted advisory set. diff --git a/.github/workflows/sei-global-wallet-smoke.yml b/.github/workflows/sei-global-wallet-smoke.yml new file mode 100644 index 00000000..ff7169dd --- /dev/null +++ b/.github/workflows/sei-global-wallet-smoke.yml @@ -0,0 +1,67 @@ +name: Sei Global Wallet Consumer Smoke + +on: + pull_request: + paths: + - '.changeset/**' + - '.github/workflows/sei-global-wallet-smoke.yml' + - 'bun.lock' + - 'package.json' + - 'packages/sei-global-wallet/**' + - 'scripts/check-sei-global-wallet.ts' + - 'scripts/dynamic-package-lock*' + - 'tsconfig.base.json' + - 'tsconfig.test.json' + # The publishing commit is the merge of the generated "Version Packages" pull + # request, so a pull_request-only trigger never gates the release itself. + push: + branches: [main] + paths: + - '.changeset/**' + - '.github/workflows/sei-global-wallet-smoke.yml' + - 'bun.lock' + - 'package.json' + - 'packages/sei-global-wallet/**' + - 'scripts/check-sei-global-wallet.ts' + - 'scripts/dynamic-package-lock*' + - 'tsconfig.base.json' + - 'tsconfig.test.json' + # Consumer installs resolve live registry and advisory data, which drifts + # without any change in this repository. + schedule: + - cron: '17 6 * * *' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +jobs: + consumers: + name: npm, Bun, edge, and browser consumers + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + registry-url: 'https://registry.npmjs.org' + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: '1.3.14' + + - name: Install workspace dependencies + run: bun install --frozen-lockfile + + - name: Verify npm and waived Bun consumers + run: bun run test:sei-global-wallet-release diff --git a/bun.lock b/bun.lock index e6537aed..a79e83c6 100644 --- a/bun.lock +++ b/bun.lock @@ -42,7 +42,7 @@ "dotenv": "^16.5.0", "express": "^4.21.2", "viem": "2.55.16", - "zod": "^3.24.2", + "zod": "^4.0.5", }, "devDependencies": { "@types/cors": "^2.8.17", @@ -76,13 +76,21 @@ "name": "@sei-js/sei-global-wallet", "version": "1.4.1", "dependencies": { - "@dynamic-labs/global-wallet-client": "^4.60.1", + "@dynamic-labs/global-wallet-client": "^4.96.3", "@wallet-standard/wallet": "^1.1.0", + "events": "3.3.0", + "process": "0.11.10", }, "devDependencies": { "@solana/wallet-standard-features": "^1.2.0", - "@solana/web3.js": "^1.92.1", + "@solana/web3.js": "1.98.1", + "@wallet-standard/base": "^1.0.1", + "@wallet-standard/features": "^1.0.3", + "@wallet-standard/wallet": "^1.1.0", + "@zerodev/sdk": "5.5.7", "typescript": "^5.7.3", + "viem": "2.45.3", + "zksync-sso": "0.2.0", }, "peerDependencies": { "@dynamic-labs/ethereum-aa": "^4.15.0", @@ -91,7 +99,7 @@ "@wallet-standard/base": "^1.0.1", "@wallet-standard/features": "^1.0.3", "@wallet-standard/wallet": "^1.1.0", - "@zerodev/sdk": "5.4.36", + "@zerodev/sdk": "^5.4.36", "viem": "^2.7.12", }, "optionalPeers": [ @@ -179,9 +187,9 @@ "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], - "@dynamic-labs-sdk/assert-package-version": ["@dynamic-labs-sdk/assert-package-version@1.20.0", "", {}, "sha512-mde7Cc4P/fSDlvFMqD9D0uTToJeQzRxAq4N0ph94QSGVppAO4hL/tlE8StySVIXZlN2Ku1L+7DKDurMaHnzmSg=="], + "@dynamic-labs-sdk/assert-package-version": ["@dynamic-labs-sdk/assert-package-version@1.27.1", "", {}, "sha512-eO5RCFOJ9L8QkJAuo5IJBam7QWvljr6dqThBZt1zPxu5BWmxGgDg8BxN0D3GqbI22kjLhMJRkkPLUTIUPVraVg=="], - "@dynamic-labs-sdk/client": ["@dynamic-labs-sdk/client@1.20.0", "", { "dependencies": { "@dynamic-labs-sdk/assert-package-version": "1.20.0", "@dynamic-labs-wallet/browser-wallet-client": "1.0.70", "@dynamic-labs-wallet/forward-mpc-client": "1.0.1", "@dynamic-labs/sdk-api-core": "0.0.1074", "@simplewebauthn/browser": "13.1.0", "ably": "2.17.1", "buffer": "6.0.3", "eventemitter3": "5.0.1", "zod": "4.0.5" }, "peerDependencies": { "@react-native-async-storage/async-storage": "^2.2.0", "react-native": ">=0.73.6", "react-native-inappbrowser-reborn": "^3.7.0", "react-native-keychain": "^10.0.0", "react-native-passkey": ">=3.3.2" }, "optionalPeers": ["@react-native-async-storage/async-storage", "react-native", "react-native-inappbrowser-reborn", "react-native-keychain", "react-native-passkey"] }, "sha512-D5yEBgY5NaBQdz9OnpWd2Kxu8QnXDTAqvxoRorIdF35KIZje3ily+tqEZUYP44cirWKKxyakIcyu6AO3y3chMg=="], + "@dynamic-labs-sdk/client": ["@dynamic-labs-sdk/client@1.27.1", "", { "dependencies": { "@dynamic-labs-sdk/assert-package-version": "1.27.1", "@dynamic-labs-wallet/browser-wallet-client": "1.0.99", "@dynamic-labs-wallet/forward-mpc-client": "1.0.1", "@dynamic-labs/sdk-api-core": "0.0.1125", "@simplewebauthn/browser": "13.1.0", "ably": "2.17.1", "buffer": "6.0.3", "eventemitter3": "5.0.1", "zod": "4.0.5" }, "peerDependencies": { "@react-native-async-storage/async-storage": "^2.2.0", "react-native": ">=0.73.6", "react-native-inappbrowser-reborn": "^3.7.0", "react-native-keychain": "^10.0.0", "react-native-passkey": ">=3.3.2" }, "optionalPeers": ["@react-native-async-storage/async-storage", "react-native", "react-native-inappbrowser-reborn", "react-native-keychain", "react-native-passkey"] }, "sha512-9ahR6VgUEbPCc7TTBvCG4AmfCQtO0vGRt+X3/pcQN3IjogPQ+nmxjjR/2Iqcp1aE2p9B+nnYJUHWWRSaAkpG8w=="], "@dynamic-labs-wallet/browser-wallet-client": ["@dynamic-labs-wallet/browser-wallet-client@1.0.92", "", { "dependencies": { "@dynamic-labs-wallet/core": "1.0.92", "@dynamic-labs/logger": "^4.81.0", "@dynamic-labs/message-transport": "^4.81.0" } }, "sha512-9jKkTl5PnQ482lAf642PTC+hI10Zyq2yJyjFkSOjfekVNYLXLhmBk5mZn3x3QlLW+jmk6bVAejQ5xpZJU4lJAQ=="], @@ -191,37 +199,37 @@ "@dynamic-labs-wallet/forward-mpc-shared": ["@dynamic-labs-wallet/forward-mpc-shared@0.7.1", "", { "dependencies": { "@noble/ciphers": "^0.4.1", "@noble/hashes": "^2.0.0", "@noble/post-quantum": "^0.5.1", "fp-ts": "^2.16.11", "io-ts": "^2.2.22" }, "peerDependencies": { "@dynamic-labs-wallet/primitives": ">=0.0.336 || 0.0.1" } }, "sha512-GPTYQ55z0nj18bkEAJEgvodH0l8phE03hY3VDOz1J4U91bFL+BKh6tjJQU6uycAhP/DouZggPYr0qKi/RBBdbA=="], - "@dynamic-labs-wallet/primitives": ["@dynamic-labs-wallet/primitives@1.0.92", "", {}, "sha512-/v2ZNA4rxoMf9hoo2CVzTG9BceS/UVfkKPNmXUqsjqEkl/KQAA0p0fTCeba8FGLjUtub/U1UmmU9SpNlCU8/+Q=="], + "@dynamic-labs-wallet/primitives": ["@dynamic-labs-wallet/primitives@1.0.99", "", {}, "sha512-ASfmh3Gjl3+IX56Yh4eeZL+zJpWqqCIYp5gzRGLvFRzQCxhmKgmOCfuzUxbVmFAGGzrnM4LO5yFQGQRxBIkxyQ=="], - "@dynamic-labs/assert-package-version": ["@dynamic-labs/assert-package-version@4.96.1", "", { "dependencies": { "@dynamic-labs/logger": "4.96.1" } }, "sha512-h3MGktUfvVCPs6T5x35jiORRTEX7/1TZjYGlgg+gH4FvTItzdksujQc7vUOMkwGVwxMSiDo8vPzqk5OKX8BREQ=="], + "@dynamic-labs/assert-package-version": ["@dynamic-labs/assert-package-version@4.96.3", "", { "dependencies": { "@dynamic-labs/logger": "4.96.3" } }, "sha512-x+UGpnia+yO8g6E00iVi1V0Pp6W6too+WZUpIqH1+lL5SjH5HcjSwpSPOo6viciRwBF4ZbmZooBG2RYmFgQ2Yg=="], - "@dynamic-labs/ethereum-aa-core": ["@dynamic-labs/ethereum-aa-core@4.96.1", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/ethereum-core": "4.96.1", "@dynamic-labs/sdk-api-core": "0.0.1093", "@dynamic-labs/types": "4.96.1", "@dynamic-labs/utils": "4.96.1", "@dynamic-labs/wallet-book": "4.96.1", "@dynamic-labs/wallet-connector-core": "4.96.1" }, "peerDependencies": { "viem": "^2.45.3" } }, "sha512-nj1zkPKdY7IE9g1UxCGwsUaT3A+ZDojW+AW9MSl+KW9uvOkqJTXXR9NoZvXKWjDn/+XzP0+V9coFz5vwxPmzMg=="], + "@dynamic-labs/ethereum-aa-core": ["@dynamic-labs/ethereum-aa-core@4.96.3", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/ethereum-core": "4.96.3", "@dynamic-labs/sdk-api-core": "0.12.0", "@dynamic-labs/types": "4.96.3", "@dynamic-labs/utils": "4.96.3", "@dynamic-labs/wallet-book": "4.96.3", "@dynamic-labs/wallet-connector-core": "4.96.3" }, "peerDependencies": { "viem": "^2.45.3" } }, "sha512-B4RBIpbUI4dc6xv6h6sU++4z+wjUVc86jS5zK2TNilfYNg2HfxNbVBY67ecIYAJQQSLh73NKTeMWKkpDVzdaew=="], - "@dynamic-labs/ethereum-aa-zksync": ["@dynamic-labs/ethereum-aa-zksync@4.96.1", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/ethereum-aa-core": "4.96.1", "@dynamic-labs/ethereum-core": "4.96.1", "@dynamic-labs/sdk-api-core": "0.0.1093", "@dynamic-labs/types": "4.96.1", "@dynamic-labs/utils": "4.96.1", "@dynamic-labs/wallet-book": "4.96.1", "@dynamic-labs/wallet-connector-core": "4.96.1", "zksync-sso": "0.2.0" }, "peerDependencies": { "viem": "^2.45.3" } }, "sha512-P5rtWY4nxyLTBufwjfdevApBA0w+3zuJxkjZamFqHWRovfKi2E/JBhpOssVzW7PkbQTCjAHDARWAR9czARUs5w=="], + "@dynamic-labs/ethereum-aa-zksync": ["@dynamic-labs/ethereum-aa-zksync@4.96.3", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/ethereum-aa-core": "4.96.3", "@dynamic-labs/ethereum-core": "4.96.3", "@dynamic-labs/sdk-api-core": "0.12.0", "@dynamic-labs/types": "4.96.3", "@dynamic-labs/utils": "4.96.3", "@dynamic-labs/wallet-book": "4.96.3", "@dynamic-labs/wallet-connector-core": "4.96.3", "zksync-sso": "0.2.0" }, "peerDependencies": { "viem": "^2.45.3" } }, "sha512-cv8BFyHK3KvCo4vphbFPnBX/RP6ndCxte/RNqSlxZ1bgw9g0A5AJZ6e821Z0CKv3NjixJ4QBcGM2IBIBl0PteA=="], - "@dynamic-labs/ethereum-core": ["@dynamic-labs/ethereum-core@4.96.1", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/logger": "4.96.1", "@dynamic-labs/rpc-providers": "4.96.1", "@dynamic-labs/sdk-api-core": "0.0.1093", "@dynamic-labs/types": "4.96.1", "@dynamic-labs/utils": "4.96.1", "@dynamic-labs/wallet-book": "4.96.1", "@dynamic-labs/wallet-connector-core": "4.96.1" }, "peerDependencies": { "viem": "^2.45.3" } }, "sha512-fXjWHi/COfkYl8nr5cAVj02qsGredC7vYW2YOoR83qnFWwXEQipK4/AxxIxrmCLSeeBER9sUzjfXESKcYnmoDA=="], + "@dynamic-labs/ethereum-core": ["@dynamic-labs/ethereum-core@4.96.3", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/logger": "4.96.3", "@dynamic-labs/rpc-providers": "4.96.3", "@dynamic-labs/sdk-api-core": "0.12.0", "@dynamic-labs/types": "4.96.3", "@dynamic-labs/utils": "4.96.3", "@dynamic-labs/wallet-book": "4.96.3", "@dynamic-labs/wallet-connector-core": "4.96.3" }, "peerDependencies": { "viem": "^2.45.3" } }, "sha512-wx1JqIeVbc38ghODYk8F21jHvPTdTKd7OMp0CZu5+4JETbFij8wl/FS5UrbrkBUi5hbrpLVyGqxIkfV3b+GIcg=="], - "@dynamic-labs/global-wallet-client": ["@dynamic-labs/global-wallet-client@4.96.1", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/ethereum-aa-zksync": "4.96.1", "@dynamic-labs/logger": "4.96.1", "@dynamic-labs/message-transport": "4.96.1", "@dynamic-labs/store": "4.96.1", "@dynamic-labs/types": "4.96.1", "@dynamic-labs/utils": "4.96.1", "eventemitter3": "5.0.1" }, "peerDependencies": { "@dynamic-labs/ethereum-aa": "4.96.1", "@solana/wallet-standard-features": "^1.2.0", "@solana/web3.js": "1.98.1", "@wallet-standard/base": "^1.0.1", "@wallet-standard/features": "^1.0.3", "@wallet-standard/wallet": "^1.1.0", "@zerodev/sdk": "5.5.7", "viem": "^2.45.3", "zksync-sso": "0.2.0" }, "optionalPeers": ["@dynamic-labs/ethereum-aa", "@solana/wallet-standard-features", "@solana/web3.js", "@wallet-standard/base", "@wallet-standard/features", "@wallet-standard/wallet", "@zerodev/sdk", "viem", "zksync-sso"] }, "sha512-vWM5ohYSRXXwJZLqC3Vv8VABNlRomMkfug08f6P8pJ5V8PPYFVx/WBie79WogSRp8zLi2dQTAEFDJuSxsJVVTw=="], + "@dynamic-labs/global-wallet-client": ["@dynamic-labs/global-wallet-client@4.96.3", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/ethereum-aa-zksync": "4.96.3", "@dynamic-labs/logger": "4.96.3", "@dynamic-labs/message-transport": "4.96.3", "@dynamic-labs/store": "4.96.3", "@dynamic-labs/types": "4.96.3", "@dynamic-labs/utils": "4.96.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "@dynamic-labs/ethereum-aa": "4.96.3", "@solana/wallet-standard-features": "^1.2.0", "@solana/web3.js": "1.98.1", "@wallet-standard/base": "^1.0.1", "@wallet-standard/features": "^1.0.3", "@wallet-standard/wallet": "^1.1.0", "@zerodev/sdk": "5.5.7", "viem": "^2.45.3", "zksync-sso": "0.2.0" }, "optionalPeers": ["@dynamic-labs/ethereum-aa", "@solana/wallet-standard-features", "@solana/web3.js", "@wallet-standard/base", "@wallet-standard/features", "@wallet-standard/wallet", "@zerodev/sdk", "viem", "zksync-sso"] }, "sha512-DUdGRbJXzrlgwVIYudSJGZSOkBhcFG1GRCnGd9/KlRI5v3BssXkl5Stg27lPmwdq7a497kM5fNSCoX6gm/bJQQ=="], - "@dynamic-labs/iconic": ["@dynamic-labs/iconic@4.96.1", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/logger": "4.96.1", "sharp": "0.35.0", "url": "0.11.0" }, "peerDependencies": { "react": ">=18.0.0 <20.0.0", "react-dom": ">=18.0.0 <20.0.0" } }, "sha512-eGspUTCASO+MgGo+8UImKHJBBbaSby2hUTLi1Bqy9s6H/El9PhNek9TeYWrez8VrGgKZLOAvcq9YEEH3FxAYLA=="], + "@dynamic-labs/iconic": ["@dynamic-labs/iconic@4.96.3", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/logger": "4.96.3", "sharp": "0.35.0", "url": "0.11.0" }, "peerDependencies": { "react": ">=18.0.0 <20.0.0", "react-dom": ">=18.0.0 <20.0.0" } }, "sha512-j0/9Ai+nvvkoBtp4vRax0HT+KnL/qZC85bIxwAUXpfBwKmNQDtWqJXfcpkXDgou0kLsZhJzAbJQbSTJfecNmbA=="], - "@dynamic-labs/logger": ["@dynamic-labs/logger@4.96.1", "", { "dependencies": { "eventemitter3": "5.0.1" } }, "sha512-O/9VUliqsiWWB79VZKsbA+mUI/5oppdCOYzEyB/wZ4EF5HamIqY98NBdEl/isUdCCVbOL7DA/bK7Hf0ThBocmg=="], + "@dynamic-labs/logger": ["@dynamic-labs/logger@4.96.3", "", { "dependencies": { "eventemitter3": "5.0.1" } }, "sha512-fI6BFQbAQZrAD8mCw8DQXiyt8BeRIKGktAxGaoFFlsgw0pHcIv0rBW7BKW/I9LH4UPUZnpaV+y9S5C2jv0lrZA=="], - "@dynamic-labs/message-transport": ["@dynamic-labs/message-transport@4.96.1", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/logger": "4.96.1", "@dynamic-labs/utils": "4.96.1", "@vue/reactivity": "^3.4.21", "eventemitter3": "5.0.1" } }, "sha512-fFwbrzfTZGKrz+F+8g0Hsaf3fWxvaUopqqyVNHZ17COnS0N87O9ZfRKgKNy1oXQ7pwRnq4HLdrOawpWRFNcRqw=="], + "@dynamic-labs/message-transport": ["@dynamic-labs/message-transport@4.96.3", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/logger": "4.96.3", "@dynamic-labs/utils": "4.96.3", "@vue/reactivity": "^3.4.21", "eventemitter3": "5.0.1" } }, "sha512-0zg9G/Hot8/sV9Q/WqpK1C3GJY3k/o9BWwhRLE2qRX1WwfGC6ghfaO5TP5oJKJ/+vXuMY2l8GN5Jah1ausT6ew=="], - "@dynamic-labs/rpc-providers": ["@dynamic-labs/rpc-providers@4.96.1", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/types": "4.96.1" } }, "sha512-5smNTPDUGqNQmePkUlbNpGxhYqITL8fRktpCe1kuZQ+K5qh4zMwF0BQS81ReUkXDfQajnpMGuhV+bqUlTqtoAw=="], + "@dynamic-labs/rpc-providers": ["@dynamic-labs/rpc-providers@4.96.3", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/types": "4.96.3" } }, "sha512-bAGV7JkZ55TUYjIkpLVDcZIJJqa7CLwMomeek0uSi7lGkJJTqrQDuYYF7M2sHOGFigQEJf9TQSSKEk2r+FFDBA=="], - "@dynamic-labs/sdk-api-core": ["@dynamic-labs/sdk-api-core@0.0.1093", "", {}, "sha512-NdtIGe5XlgY4ohvy//RHjokUiMJ/1BHA+IV12awIDwPfeR9+dp0gMKurlTIbnOqn0P95pbUQKdpbtxZmtCWDRw=="], + "@dynamic-labs/sdk-api-core": ["@dynamic-labs/sdk-api-core@0.12.0", "", {}, "sha512-3e7gCCljkGc3NHxg2sy/DhSlAAqxA6cJBX2eaNhqL26OQi/bzivadwtEaVWSJ430+gtO1+Aa779BCJ40/WO5vw=="], - "@dynamic-labs/store": ["@dynamic-labs/store@4.96.1", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/logger": "4.96.1" } }, "sha512-MiRhvov++gA1jZ7RG/WzgYFoL/0dVNbbAe8Y8moEvyUOOyt3RQLpYAOadPoZH3WdvE/VDCnFDOAMNebMlDnYQQ=="], + "@dynamic-labs/store": ["@dynamic-labs/store@4.96.3", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/logger": "4.96.3" } }, "sha512-N/xmCvDRGjz6y6mvEVOxA26T5fvW33yqo8e9cVRUGllBsI6BK9bV3Da/mP+ZxSbFXnc+X+yAf+K/YPtfg+yqVg=="], - "@dynamic-labs/types": ["@dynamic-labs/types@4.96.1", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/sdk-api-core": "0.0.1093" } }, "sha512-Vio1KRMVsPy0hiUorQoJ+tDlKJIkPi+zXd4gZBZABVMjTGiVuOdEZRap6SxACwfSjKh7amsyn6Aw2Md1fORwUw=="], + "@dynamic-labs/types": ["@dynamic-labs/types@4.96.3", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/sdk-api-core": "0.12.0" } }, "sha512-gqqkV4go3yQyVFHkYE/GsB515DvfAbsUngLkx/OKolSd5D+UkscTSNOrjBJlsc/yEbsdsO8/TyWUxdpl/aVKnQ=="], - "@dynamic-labs/utils": ["@dynamic-labs/utils@4.96.1", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/logger": "4.96.1", "@dynamic-labs/sdk-api-core": "0.0.1093", "@dynamic-labs/types": "4.96.1", "buffer": "6.0.3", "eventemitter3": "5.0.1", "tldts": "6.0.16" } }, "sha512-yCqhsyF2Ob9CpB2nHo7ThWggOHeC9xK53v+XKZ1EJ3pHauoLu9DpS3OySEZWz53vfHoFF4U+QYehYjWk840w1g=="], + "@dynamic-labs/utils": ["@dynamic-labs/utils@4.96.3", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/logger": "4.96.3", "@dynamic-labs/sdk-api-core": "0.12.0", "@dynamic-labs/types": "4.96.3", "buffer": "6.0.3", "eventemitter3": "5.0.1", "tldts": "6.0.16" } }, "sha512-du5wkV2N2IjJDZcoZPpCLb7uloDpdTHmxvW16pEwSfW2STbXEj4TE3cMpMGJQ1O/VQ9+8XjJLYeN2v9RHbJTnw=="], - "@dynamic-labs/wallet-book": ["@dynamic-labs/wallet-book@4.96.1", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/iconic": "4.96.1", "@dynamic-labs/logger": "4.96.1", "@dynamic-labs/utils": "4.96.1", "eventemitter3": "5.0.1", "util": "0.12.5", "zod": "4.0.5" }, "peerDependencies": { "react": ">=18.0.0 <20.0.0", "react-dom": ">=18.0.0 <20.0.0" } }, "sha512-EWwqA+WDEfOyuMPzGjlujA1Twl0ucnxOvQTNpeHj8YONU09m/KkprFZq5DqHpCFFGe6xCA64DU+GEcJjKgX1Rg=="], + "@dynamic-labs/wallet-book": ["@dynamic-labs/wallet-book@4.96.3", "", { "dependencies": { "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/iconic": "4.96.3", "@dynamic-labs/logger": "4.96.3", "@dynamic-labs/utils": "4.96.3", "eventemitter3": "5.0.1", "util": "0.12.5", "zod": "4.0.5" }, "peerDependencies": { "react": ">=18.0.0 <20.0.0", "react-dom": ">=18.0.0 <20.0.0" } }, "sha512-Y76B/X1WFwn+7/77wTfZ5DG78Ag3hUG4NenZDAH5fTCgyCF75kAsQXI2Ar4nL7DBBr7S34mqiH0BcmGh8ObDKg=="], - "@dynamic-labs/wallet-connector-core": ["@dynamic-labs/wallet-connector-core@4.96.1", "", { "dependencies": { "@dynamic-labs-sdk/client": "1.20.0", "@dynamic-labs-wallet/browser-wallet-client": "1.0.92", "@dynamic-labs-wallet/forward-mpc-client": "1.0.1", "@dynamic-labs/assert-package-version": "4.96.1", "@dynamic-labs/logger": "4.96.1", "@dynamic-labs/rpc-providers": "4.96.1", "@dynamic-labs/sdk-api-core": "0.0.1093", "@dynamic-labs/types": "4.96.1", "@dynamic-labs/utils": "4.96.1", "@dynamic-labs/wallet-book": "4.96.1", "eventemitter3": "5.0.1" } }, "sha512-w6J6xCE49PE6qPNQX6spcCRnGIWy9FTrbMNC/9awe2WjjlGELiqfFpSjh0euyDEfcHMUvKJwZ5DoWGyGi4xU8Q=="], + "@dynamic-labs/wallet-connector-core": ["@dynamic-labs/wallet-connector-core@4.96.3", "", { "dependencies": { "@dynamic-labs-sdk/client": "1.27.1", "@dynamic-labs-wallet/browser-wallet-client": "1.0.92", "@dynamic-labs-wallet/forward-mpc-client": "1.0.1", "@dynamic-labs/assert-package-version": "4.96.3", "@dynamic-labs/logger": "4.96.3", "@dynamic-labs/rpc-providers": "4.96.3", "@dynamic-labs/sdk-api-core": "0.12.0", "@dynamic-labs/types": "4.96.3", "@dynamic-labs/utils": "4.96.3", "@dynamic-labs/wallet-book": "4.96.3", "eventemitter3": "5.0.1" } }, "sha512-DmdwelVqaTHDinznFfE5mPAsTWPSt7Ppq96ayh4uJTGjm81/xQY6y/h6ZTVHw+4kh5g6B8XUrEZZlB4daVLR4g=="], "@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], @@ -281,7 +289,7 @@ "@hexagon/base64": ["@hexagon/base64@1.1.28", "", {}, "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="], - "@hono/node-server": ["@hono/node-server@2.1.0", "", { "peerDependencies": { "hono": "^4" } }, "sha512-XovyyCCnBzW+zKu+z/zq8hwNs4KOR5rEMAOxo2f40Q5xoOI37IMm6MIg2COOUtUApo0i6850MTBKH2u4QLGIqg=="], + "@hono/node-server": ["@hono/node-server@2.1.1", "", { "peerDependencies": { "hono": "^4" } }, "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg=="], "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], @@ -367,33 +375,33 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@peculiar/asn1-android": ["@peculiar/asn1-android@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-skLbS+IOGv1lUgDqtChr8xvtvEr3HMse/JGBaL2r1J1o/n7a8wqOrovMtlRq/UXLhxvmLaONP67hwtshgzwfzA=="], + "@peculiar/asn1-android": ["@peculiar/asn1-android@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-SYHm4SoWSI0nRCoos6jpGusIqhPH9bbGBqv7ohlZ+H6BunrDzzQPk2ePgDuEUzV82OdvbLgtW4twUDwhU9P3YQ=="], - "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "@peculiar/asn1-x509-attr": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA=="], + "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "@peculiar/asn1-x509-attr": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-cben7oxmQsUGZqotus7yt0srYdncOT6RNWcTQ77T2RFOXejYVYkXadrfePdRcrVpO9K95IRLKKglG2k38jKXuw=="], - "@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg=="], + "@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-xd4YN4vpRjkDAQWVfZZkeu12IEND7DOpkqaHSIHxZl1uggUNa9Ju0QxY2jHvDAS9pP0zhRBytg8ifsnGo3V0jw=="], - "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ=="], + "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-JJXefFshRAuVAjWQo/39bkg1ywc1VaiO44S8RRC+Ykvf/u2KDmYffoDb0ZBPCR5uJy4AGKQhl8mX+Q8ShcWaXQ=="], - "@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.8.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.8.0", "@peculiar/asn1-pkcs8": "^2.8.0", "@peculiar/asn1-rsa": "^2.8.0", "@peculiar/asn1-schema": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg=="], + "@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.9.4", "", { "dependencies": { "@peculiar/asn1-cms": "^2.9.4", "@peculiar/asn1-pkcs8": "^2.9.4", "@peculiar/asn1-rsa": "^2.9.4", "@peculiar/asn1-schema": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-khuGzHTzNzk4GDlIBEILyIs6Lce0yn0ZBdoI9v93kmNncfZRhD+AQ5ODFqdhvoE8cMJF/JMTQ8yA+t1D14kqCw=="], - "@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA=="], + "@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-duRdotlUx9eDZe6QrQpQKl61RbWykCHBCkKayP8V8XdEFwlKHZ8qGGDMyS6Pye7OX7nLFttTTpRkJeet78ckwQ=="], - "@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.8.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.8.0", "@peculiar/asn1-pfx": "^2.8.0", "@peculiar/asn1-pkcs8": "^2.8.0", "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "@peculiar/asn1-x509-attr": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ=="], + "@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.9.4", "", { "dependencies": { "@peculiar/asn1-cms": "^2.9.4", "@peculiar/asn1-pfx": "^2.9.4", "@peculiar/asn1-pkcs8": "^2.9.4", "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "@peculiar/asn1-x509-attr": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-kaL4cNxBpdQE2dKlyZBqz4ygCrwffO+8wfoxTEqM1Z8RadvCeELBRzcv0dzM8aY9azHMwODO5nxU65zXmhToOQ=="], - "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg=="], + "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-pZ96eD1PptovcWQ/GSmuNFXd/7EQJNlKfDaNCyE2rx3W0v6QFelkzquVqRSRyyDXXCYD69ZXJDzZ8GhIiQzKoA=="], - "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.8.0", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q=="], + "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.9.4", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-GjzePcT9Iw8NzeOPf73iNS9xM+TBhd/FilAfP+RQGkTMQJTVWtytN3JHJACCjf/ABNau5S7mS3g+DcuxmRgYEg=="], - "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg=="], + "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-CxhBo/RdEbMMob7T31ZdQjGuoyRFLVwrDzTn25bihzBasRg9kRm/0IxIPvhgQtcK/9dNcO1XQL2fuPugwELL0Q=="], - "@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA=="], + "@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.9.4", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.4", "@peculiar/asn1-x509": "^2.9.4", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-ehQXbpQaQYycgu8OrvigwSPTFfVRcu0ECNYCWw+yzBp02Lw5paRqzzhUpfOgO2K38+WfFZuEz/0RPtam5g0OMg=="], "@peculiar/utils": ["@peculiar/utils@2.0.3", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ=="], "@peculiar/x509": ["@peculiar/x509@1.14.3", "", { "dependencies": { "@peculiar/asn1-cms": "^2.6.0", "@peculiar/asn1-csr": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.0", "@peculiar/asn1-pkcs9": "^2.6.0", "@peculiar/asn1-rsa": "^2.6.0", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "pvtsutils": "^1.3.6", "reflect-metadata": "^0.2.2", "tslib": "^2.8.1", "tsyringe": "^4.10.0" } }, "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA=="], - "@publint/pack": ["@publint/pack@0.1.6", "", { "dependencies": { "tinyexec": "^1.2.4" } }, "sha512-3uVNyGcVplhPZSLVyeIpL7+cIRn1YCSNHLG/rUIlBQMVH8YuN9++YF+5+UDIIO9RW98dujiUoTltO7RDB5bFJA=="], + "@publint/pack": ["@publint/pack@0.1.7", "", { "dependencies": { "tinyexec": "^1.3.0" } }, "sha512-4EDEmvxWtgsCnnVeBvtFIFZtUhPPt1+bA9JrSwU4Sa//6oKtzCSlGGXYJr44OD9aGISymbieJ4mCKHUygUDU+g=="], "@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="], @@ -427,7 +435,7 @@ "@solana/wallet-standard-features": ["@solana/wallet-standard-features@1.4.0", "", { "dependencies": { "@wallet-standard/base": "^1.1.0", "@wallet-standard/features": "^1.1.0" } }, "sha512-f0tAdqwM2aL6CiFbIgt9h5zKFp+mgY/iNGwoxPMTj9VSTeQj7d1GGSmWhZw0XWoZ4N/1tnKTKmYFq+Dyq08jRw=="], - "@solana/web3.js": ["@solana/web3.js@1.98.4", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw=="], + "@solana/web3.js": ["@solana/web3.js@1.98.1", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-gRAq1YPbfSDAbmho4kY7P/8iLIjMWXAzBJdP9iENFR+dFQSBSueHzjK/ou8fxhqHP9j+J4Msl4p/oDemFcIjlg=="], "@swc/helpers": ["@swc/helpers@0.5.23", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw=="], @@ -479,6 +487,8 @@ "@wallet-standard/wallet": ["@wallet-standard/wallet@1.1.1", "", { "dependencies": { "@wallet-standard/base": "^1.1.1" } }, "sha512-8WiRPaKk/wNNRZhB2eVhpR/JW7/aqTCMoZhgVUCujuzDmxxmGvsosMxdCG4NAdYkoyozAHCX8/xLtlWUn5mNdQ=="], + "@zerodev/sdk": ["@zerodev/sdk@5.5.7", "", { "dependencies": { "semver": "^7.6.0" }, "peerDependencies": { "viem": "^2.28.0" } }, "sha512-Sf4G13yi131H8ujun64obvXIpk1UWn64GiGJjfvGx8aIKg+OWTRz9AZHgGKK+bE/evAmqIg4nchuSvKPhOau1w=="], + "abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="], "ably": ["ably@2.17.1", "", { "dependencies": { "@ably/msgpack-js": "^0.4.0", "dequal": "^2.0.3", "fastestsmallesttextencoderdecoder": "^1.0.22", "got": "^11.8.5", "ulid": "^2.3.0", "ws": "^8.17.1" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-70yfXHoM7JtJD/8FCtPD1gkWW0f+AJqbJp0PsqDAqiyxFB8cPFY+FuKHgNTYb8eRHKXq8hT1xiDphUcY0+GHnA=="], @@ -683,6 +693,8 @@ "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], "eventsource-parser": ["eventsource-parser@3.1.1", "", {}, "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ=="], @@ -763,7 +775,7 @@ "highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="], - "hono": ["hono@4.13.2", "", {}, "sha512-JydRilDRkYBQMt9qR9U92mXxmbGqsqSn/IKOrh4e7/gEbn+0zSr8igTu0obwJoNGN4sez28DIql7FBHWydoJpA=="], + "hono": ["hono@4.13.3", "", {}, "sha512-r8AO2mYHoLxSHkgafNeC/BXyb2vWRxD3jem4Ts+ptav8oTG5FIRifAjuJEmZI4bSvvc2ns0GxmIYiZnHqN3mMw=="], "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], @@ -773,7 +785,7 @@ "https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], - "human-id": ["human-id@4.2.0", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA=="], + "human-id": ["human-id@4.2.1", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw=="], "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], @@ -829,7 +841,7 @@ "jayson": ["jayson@4.3.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-AauzHcUcqs8OBnCHOkJY280VaTiCm57AbuO7lqzcw7JapGj50BisE3xhksye4zlTSR1+1tAz67wLTl8tEH1obQ=="], - "jose": ["jose@6.2.8", "", {}, "sha512-Bsdjwm3Qsd/P0jR+BHDe3LytDfY7WBq2HmCCLIwuVRHMuEC9ae7/R474GIUdF1NgCyZjzVo/A9DOiOBtXq8ZoQ=="], + "jose": ["jose@6.2.10", "", {}, "sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g=="], "js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], @@ -887,7 +899,7 @@ "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], - "ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "mute-stream": ["mute-stream@1.0.0", "", {}, "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA=="], @@ -959,6 +971,8 @@ "prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], "proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="], @@ -1173,7 +1187,7 @@ "zksync-sso": ["zksync-sso@0.2.0", "", { "dependencies": { "@peculiar/asn1-ecc": "^2.3.13", "@peculiar/asn1-schema": "^2.3.13", "abitype": "^1.0.6", "bigint-conversion": "^2.4.3", "buffer": "^6.0.3", "ms": "^2.1.3" }, "peerDependencies": { "@simplewebauthn/browser": "13.x", "@simplewebauthn/server": "13.x", "@wagmi/core": "2.x", "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-JyxmYx2KnreTEQANyihkhzQGqA0Opa0j1qT6BLBmjP8WOwsYEiOMolbwxNK7X/KETXI77IZhGxWl8ZMKQgYl8A=="], - "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "zod": ["zod@4.0.5", "", {}, "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA=="], "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], @@ -1183,11 +1197,13 @@ "@arethetypeswrong/core/typescript": ["typescript@5.6.1-rc", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ=="], - "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client": ["@dynamic-labs-wallet/browser-wallet-client@1.0.70", "", { "dependencies": { "@dynamic-labs-wallet/core": "1.0.70", "@dynamic-labs/logger": "^4.81.0", "@dynamic-labs/message-transport": "^4.81.0" } }, "sha512-n0iKSn1rj9rGrfDDvgqeW3rHc09TAp7k1S481NsyNbsdWkSkACXLD8YYggVIvvXVq7w/5MfOqp6b/ZjoF4bXKg=="], + "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client": ["@dynamic-labs-wallet/browser-wallet-client@1.0.99", "", { "dependencies": { "@dynamic-labs-wallet/core": "1.0.99", "@dynamic-labs/logger": "^5.0.0", "@dynamic-labs/message-transport": "^5.0.0" } }, "sha512-dyvqU6yAF5OvLpcLI8064qsLNniWE15mcLWf61KjimPsVryoon0vPU9xxmgxrvKtixx4I2LeQxDtlv7C1iyHMQ=="], + + "@dynamic-labs-sdk/client/@dynamic-labs/sdk-api-core": ["@dynamic-labs/sdk-api-core@0.0.1125", "", {}, "sha512-DZiEWOJD9ibwJRknhp3sed2YALaE7d8DGyM0zXIaFNzQ0GpYABDlXSLRCAFnYOof1yK7qMsOlYq9C5WD5tf7Hg=="], - "@dynamic-labs-sdk/client/@dynamic-labs/sdk-api-core": ["@dynamic-labs/sdk-api-core@0.0.1074", "", {}, "sha512-bUkwDnREU6vSiQk5fv4NBWKCNxJOYHXgbOgh92J5gWH/M3ey1gaINRBXgQUMFFi81wsxGdyVto7266JY7QnnWw=="], + "@dynamic-labs-wallet/core/@dynamic-labs-wallet/primitives": ["@dynamic-labs-wallet/primitives@1.0.92", "", {}, "sha512-/v2ZNA4rxoMf9hoo2CVzTG9BceS/UVfkKPNmXUqsjqEkl/KQAA0p0fTCeba8FGLjUtub/U1UmmU9SpNlCU8/+Q=="], - "@dynamic-labs-sdk/client/zod": ["zod@4.0.5", "", {}, "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA=="], + "@dynamic-labs-wallet/core/@dynamic-labs/sdk-api-core": ["@dynamic-labs/sdk-api-core@0.0.1093", "", {}, "sha512-NdtIGe5XlgY4ohvy//RHjokUiMJ/1BHA+IV12awIDwPfeR9+dp0gMKurlTIbnOqn0P95pbUQKdpbtxZmtCWDRw=="], "@dynamic-labs-wallet/forward-mpc-client/@noble/hashes": ["@noble/hashes@2.3.0", "", {}, "sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ=="], @@ -1195,8 +1211,6 @@ "@dynamic-labs-wallet/forward-mpc-shared/@noble/hashes": ["@noble/hashes@2.3.0", "", {}, "sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ=="], - "@dynamic-labs/wallet-book/zod": ["zod@4.0.5", "", {}, "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA=="], - "@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], @@ -1243,6 +1257,8 @@ "@sei-js/mcp-server/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], + "@sei-js/sei-global-wallet/viem": ["viem@2.45.3", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.12.1", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-axOD7rIbGiDHHA1MHKmpqqTz3CMCw7YpE/FVypddQMXL5i364VkNZh9JeEJH17NO484LaZUOMueo35IXyL76Mw=="], + "@solana/errors/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "@solana/errors/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], @@ -1277,6 +1293,8 @@ "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], + "debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "ethers/@noble/curves": ["@noble/curves@1.2.0", "", { "dependencies": { "@noble/hashes": "1.3.2" } }, "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw=="], "ethers/@noble/hashes": ["@noble/hashes@1.3.2", "", {}, "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="], @@ -1289,8 +1307,6 @@ "https-proxy-agent/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - "humanize-ms/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "inquirer/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "inquirer/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], @@ -1321,7 +1337,7 @@ "rpc-websockets/@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "rxjs/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], @@ -1337,9 +1353,11 @@ "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "zksync-sso/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs-wallet/core": ["@dynamic-labs-wallet/core@1.0.99", "", { "dependencies": { "@dynamic-labs-wallet/primitives": "1.0.99", "@dynamic-labs/sdk-api-core": "^0.0.1093", "axios": "1.16.0", "uuid": "11.1.0" }, "peerDependencies": { "@dynamic-labs-wallet/forward-mpc-client": "1.0.1" } }, "sha512-5/X3c0kAnRrpF0+U090ukoah7iSHw+/n3ct+Ul6+FtK7gBxlETWwczvHJC3OD1UVL8nK1MJLbrA/rpNGFnIVbQ=="], - "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs-wallet/core": ["@dynamic-labs-wallet/core@1.0.70", "", { "dependencies": { "@dynamic-labs-wallet/primitives": "1.0.70", "@dynamic-labs/sdk-api-core": "^0.0.984", "axios": "1.16.0", "uuid": "11.1.0" }, "peerDependencies": { "@dynamic-labs-wallet/forward-mpc-client": "1.0.1" } }, "sha512-brTbTiAwS2TEhB6G2lL43dFx9Bj19wB5GPngi49yiX6veQJkQP9kkqqcxv97zYSEIUbBaPP/GndkPSewXPXYew=="], + "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs/logger": ["@dynamic-labs/logger@5.3.2", "", { "dependencies": { "eventemitter3": "5.0.1" } }, "sha512-BafajCDxq2tTg465Z1cJYMDnnQHUtEiuSUKkRd6HEZtbW1OQdY3yQFhkffZxxwdruqWC7/yXUUgqs4ngyTWOSw=="], + + "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs/message-transport": ["@dynamic-labs/message-transport@5.3.2", "", { "dependencies": { "@dynamic-labs/assert-package-version": "5.3.2", "@dynamic-labs/logger": "5.3.2", "@dynamic-labs/utils": "5.3.2", "@vue/reactivity": "^3.4.21", "eventemitter3": "5.0.1" } }, "sha512-bBx6+DM9U6bFGVkW8lgfCeZAWQQ29qTfVMhqsFzRSiy+C3ZV0EYQ9QnYZeIO6V3QHOLOuUZMnMEMpQfcc3gN/g=="], "@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], @@ -1363,9 +1381,11 @@ "@modelcontextprotocol/sdk/express/type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], - "accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "@sei-js/sei-global-wallet/viem/ox": ["ox@0.12.1", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-uU0llpthaaw4UJoXlseCyBHmQ3bLrQmz9rRLIAUHqv46uHuae9SE+ukYBRIPVCnlEnHKuWjDUcDFHWx9gbGNoA=="], + + "@sei-js/sei-global-wallet/viem/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], - "agent-base/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -1375,36 +1395,34 @@ "ethers/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="], - "express-rate-limit/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "https-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "inquirer/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - "router/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs-wallet/core/@dynamic-labs-wallet/primitives": ["@dynamic-labs-wallet/primitives@1.0.70", "", {}, "sha512-epxiBEyu5YVZPbj3jre4AkAht+yuf/9IfDzQiY5uUP4BoNGtn8ezveRAEFgTfhytyuyl1GiI78t6K7xW8Zi5LA=="], - - "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs-wallet/core/@dynamic-labs/sdk-api-core": ["@dynamic-labs/sdk-api-core@0.0.984", "", {}, "sha512-smSL1nUDZ753Ldeb848GJufOzEMzkGUcDdxUVcfmfHnA8kEdmKO+c/4nfQEUeDNvaFxd9ueB9ZKTYkLC2t/uXg=="], + "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs-wallet/core/@dynamic-labs/sdk-api-core": ["@dynamic-labs/sdk-api-core@0.0.1093", "", {}, "sha512-NdtIGe5XlgY4ohvy//RHjokUiMJ/1BHA+IV12awIDwPfeR9+dp0gMKurlTIbnOqn0P95pbUQKdpbtxZmtCWDRw=="], - "@modelcontextprotocol/sdk/express/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs/message-transport/@dynamic-labs/assert-package-version": ["@dynamic-labs/assert-package-version@5.3.2", "", { "dependencies": { "@dynamic-labs/logger": "5.3.2" } }, "sha512-Ic5RWXoIJlVAhysclN9QCnHZ6I9YDzbZXUBo0aOtyrDq6QRGP+qImDj1+m7eiixyXA9BeVGeiQc4jDUlzJrQkg=="], - "@modelcontextprotocol/sdk/express/body-parser/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], + "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs/message-transport/@dynamic-labs/utils": ["@dynamic-labs/utils@5.3.2", "", { "dependencies": { "@dynamic-labs/assert-package-version": "5.3.2", "@dynamic-labs/logger": "5.3.2", "@dynamic-labs/sdk-api-core": "0.12.0", "@dynamic-labs/types": "5.3.2", "buffer": "6.0.3", "eventemitter3": "5.0.1", "tldts": "6.0.16" } }, "sha512-x0k2bQSFnNAlD2OxTWMiQrFlJdRGIUfCSBvOc/J5TDJ60RoEcwHF0ucMvZmpvhiUAuZkgzCuYrMkgjRQA1b4gA=="], - "@modelcontextprotocol/sdk/express/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "@modelcontextprotocol/sdk/express/accepts/negotiator": ["negotiator@1.1.0", "", { "dependencies": { "content-type": "^2.1.0" } }, "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg=="], - "@modelcontextprotocol/sdk/express/send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "@modelcontextprotocol/sdk/express/body-parser/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], - "@modelcontextprotocol/sdk/express/type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], + "@modelcontextprotocol/sdk/express/type-is/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], "@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.1", "", {}, "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ=="], + + "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs/message-transport/@dynamic-labs/utils/@dynamic-labs/sdk-api-core": ["@dynamic-labs/sdk-api-core@0.12.0", "", {}, "sha512-3e7gCCljkGc3NHxg2sy/DhSlAAqxA6cJBX2eaNhqL26OQi/bzivadwtEaVWSJ430+gtO1+Aa779BCJ40/WO5vw=="], + + "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs/message-transport/@dynamic-labs/utils/@dynamic-labs/types": ["@dynamic-labs/types@5.3.2", "", { "dependencies": { "@dynamic-labs/assert-package-version": "5.3.2", "@dynamic-labs/sdk-api-core": "0.12.0" } }, "sha512-oHPrly4hXFehh5tFH20CS/5DPjKr6KLpQ9V05pEd36nFj3VrQCsTrW77VKSTRi8Wphnqqq0xUcX7dVv4g6QiZQ=="], + + "@modelcontextprotocol/sdk/express/accepts/negotiator/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], } } diff --git a/package.json b/package.json index 30e25c71..b44c0b48 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "test:all": "bun run test", "test:coverage": "bun test --isolate --coverage --coverage-reporter=lcov packages", "test:create-sei-release": "bun run --cwd packages/create-sei test:release", + "test:sei-global-wallet-release": "bun scripts/check-sei-global-wallet.ts", "test:scripts": "bun test --isolate scripts", "typecheck": "tsc --project tsconfig.test.json" }, diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index d3846b3f..34d2569b 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -40,7 +40,7 @@ "dotenv": "^16.5.0", "express": "^4.21.2", "viem": "2.55.16", - "zod": "^3.24.2" + "zod": "^4.0.5" }, "keywords": [ "mcp", diff --git a/packages/sei-global-wallet/README.md b/packages/sei-global-wallet/README.md index aed7d162..474286a6 100644 --- a/packages/sei-global-wallet/README.md +++ b/packages/sei-global-wallet/README.md @@ -13,16 +13,158 @@ -## 🚀 Quick Start +> [!WARNING] +> **Temporary consumer security waiver:** Dynamic Global Wallet Client transitively pins vulnerable `axios` and `uuid` as of 4.96.3, the current floor. +> Dependency overrides are root-only in both npm and Bun; this package cannot propagate them to your application. Add the overrides below before installing, and drop them once your install resolves a Dynamic release that corrects those pins. + +## Required consumer overrides + +Dependency overrides are root-only; this package cannot propagate them to an application. npm and Bun require different policies. + +Complete npm root overrides when the optional AA path is not enabled: +```json +{ + "overrides": { + "axios": "1.18.0", + "uuid": "11.1.1", + "viem": { + "ws": "8.21.0" + } + } +} +``` + +Complete Bun root overrides when the optional AA path is not enabled: +```json +{ + "overrides": { + "axios": "1.18.0", + "uuid": "11.1.1" + } +} +``` + +The Axios and UUID overrides are temporary until Dynamic updates its exact transitive pins: + +- The high-severity Axios issue is in the Node HTTP adapter and requires a prerequisite prototype-pollution/interceptor pattern. Browser wallet paths do not use that adapter, which reduces exploitability but does not make the vulnerable install acceptable. +- The UUID issue affects the v3, v5, and v6 buffer APIs. Dynamic's observed call sites use UUID v4, which reduces exploitability but does not clear the audit finding. + +Applications using the optional `./zerodev` / Dynamic account-abstraction path must also replace the vulnerable exact `bn.js@4.11.6` copies used by `ethjs-unit` and `number-to-bn`. The complete npm root override block is: +```json +{ + "overrides": { + "axios": "1.18.0", + "ethjs-unit": { + "bn.js": "4.12.5" + }, + "number-to-bn": { + "bn.js": "4.12.5" + }, + "uuid": "11.1.1", + "viem": { + "ws": "8.21.0" + } + } +} +``` + +Bun 1.3.14 does not support nested overrides. Do **not** globally override `bn.js` or `ws`: Solana/borsh require bn5 while Jayson requires ws7. Bun AA consumers use the same complete safe root block shown above for Bun: +```json +{ + "overrides": { + "axios": "1.18.0", + "uuid": "11.1.1" + } +} +``` + +For npm, scoped `bn.js@4.12.5` stays on the legacy dependencies' expected major while Solana resolves `bn.js@5.2.5`. Scoped `ws@8.21.0` patches Viem's ws8 subtree while Jayson resolves `ws@7.5.13` from its `^7.5.10` range. The result is audit-clean. + +The verifier builds its browser consumer against `viem@2.45.3`, because the previously tested 2.55.19 pulls Ox Tempo's `node:worker_threads` path into Vite resolution while 2.45.3 predates it. That is a property of the verifier's own bundle, not a constraint on applications: the published `viem` peer range stays `^2.7.12`. If your bundler externalizes `node:worker_threads` on a newer Viem, configure it in your application rather than downgrading. + +For Bun, scoped overrides are unavailable. The selected waiver therefore accepts exactly these unresolved optional-AA advisories while preserving compatible majors: + +- `GHSA-378v-28hj-76wf` on `bn.js@4.11.6` — moderate, CVSS 5.3. +- `GHSA-58qx-3vcg-4xpx` on Viem's `ws@8.18.3` — moderate, CVSS 4.4. +- `GHSA-96hv-2xvq-fx4p` on Viem's `ws@8.18.3` — high, CVSS 7.5. + +Jayson remains on `ws@7.5.13`; globally forcing ws8 would violate that major contract. The verifier fails on any advisory outside this set, and reports rather than fails when one of them stops being reported, so an upstream fix or a withdrawn advisory never turns an unrelated pull request red. + +## Quick start ```bash npm install @sei-js/sei-global-wallet ``` -Then import to register with EIP-6963 standard: +Then import the side-effect entrypoint to register with EIP-6963: ```javascript import '@sei-js/sei-global-wallet/eip6963'; ``` This single import enables Sei Global Wallet across all compatible wallet libraries (RainbowKit, ConnectKit, Web3-React, Wagmi, etc.). + +The entrypoint dispatches the required initial `eip6963:announceProvider` event and re-announces for every `eip6963:requestProvider` event. It is safe to import during SSR; registration occurs only in a browser. + +## Entrypoints + +- `@sei-js/sei-global-wallet` exports the configured wallet client and Dynamic wallet features. +- `@sei-js/sei-global-wallet/eip6963` registers the EIP-6963 provider and exports registration, cleanup, and stable provider-info helpers. +- `@sei-js/sei-global-wallet/ethereum` exports `createEIP1193Provider`. +- `@sei-js/sei-global-wallet/solana` registers the wallet-standard provider and exports `createSolanaWallet` and `registerSolanaStandard`. +- `@sei-js/sei-global-wallet/zerodev` exports `createKernelClient`. + +All entrypoints are ESM-only. Consumers do not need Vite, esbuild, or other bundler shims. + +### Globals installed on import + +Dynamic 4.x reads Node-style globals, so importing any entrypoint of this package defines them on `globalThis` when they are absent: + +- `globalThis.global`, aliased to `globalThis`. +- `globalThis.process`, set to the `process/browser.js` shim with `env.NODE_ENV` set to `production`. + +Both are `configurable` and `writable`, and neither is installed when the consumer or runtime already defines it. `NODE_ENV` is set because the browser shim ships an empty `env`, and libraries that branch on `process.env.NODE_ENV !== 'production'` would otherwise take their development path inside a production bundle. These are true globals, so every library loaded afterwards observes them; if your application needs different values, define `global` and `process` before importing this package and they will be left alone. + +## Optional peer versions + +Install only the peers needed by the subpaths your application uses. The declared ranges stay deliberately wide, because an optional peer still fails `npm install` with `ERESOLVE` once your application has the package at a version outside the range. The **verified** column is Dynamic 4.96.3's own peer contract, which is the set this package's release checks run against. + +| Peer | Declared range | Verified against | Needed by | +| --- | --- | --- | --- | +| `viem` | `^2.7.12` | `2.45.3` | `./zerodev`, Dynamic AA | +| `@dynamic-labs/ethereum-aa` | `^4.15.0` | `4.96.3` | `./zerodev` | +| `@zerodev/sdk` | `^5.4.36` | `5.5.7` | `./zerodev` | +| `@solana/web3.js` | `^1.92.1` | `1.98.1` | `./solana` | +| `@solana/wallet-standard-features` | `^1.2.0` | `^1.2.0` | `./solana` | +| `@wallet-standard/base` | `^1.0.1` | `^1.0.1` | `./solana` | +| `@wallet-standard/features` | `^1.0.3` | `^1.0.3` | `./solana` | +| `@wallet-standard/wallet` | `^1.1.0` | `^1.1.0` | `./solana` | + +> [!NOTE] +> Dynamic 4.96.3 pins several of these exactly for itself, so a version outside the verified column can still be rejected by Dynamic's own peer contract during install, and is not covered by this package's checks. Prefer the verified versions; the wide ranges exist so that upgrading this package never breaks an install on its own. +> +> Dynamic also declares an optional `zksync-sso@0.2.0` peer for its zkSync path. This package does not redeclare it, so npm surfaces that requirement from Dynamic directly. + +Two dependencies exist for transitive resolution rather than for this package's own source, so neither is removable despite nothing here importing them: + +- `@wallet-standard/wallet`, because Dynamic's `./solana` module imports it, so `@sei-js/sei-global-wallet/solana` needs it present at runtime. +- `events`, because `@zerodev/sdk` imports the bare `events` specifier. Bundling the `./zerodev` path for the browser fails with `Could not resolve "events"` unless that polyfill is in the tree. + +The root, `./eip6963`, and `./ethereum` entrypoints need no optional peer at all, including for type resolution. The release verifier typechecks them with `skipLibCheck: false` in a consumer that installs nothing but this package, so a published declaration that referenced a type from an uninstalled peer would fail the check. + +`@dynamic-labs/global-wallet-client` is a `^4.96.3` dependency rather than an exact pin, so applications inherit Dynamic's transitive fixes without waiting for a release here. This repository's lockfile pins the exact version it tests. + +A full Bun lockfile regeneration showed that the previous isolated Dynamic 4.96.1 subtree beneath `@dynamic-labs-wallet/browser-wallet-client@1.0.92` was stale: its `^4.81.0` ranges resolve compatibly without it. The verifier now asserts that no `@dynamic-labs/*` package resolves to more than one version, in npm and Bun graphs and in browser bundles, which stays meaningful across Dynamic upgrades. + +## Brand asset + +The EIP-6963 and wallet-standard icon is an exact, unrecolored data-URI encoding of the square black +[official Sei mark](https://github.com/sei-protocol/sei-docs/blob/8710d6078058bdf67ec704b346f75ee9883e25ea/assets/brand/logo/sei-mark.svg) +from the [Sei brand kit](https://brand.sei.io/). Its proportions, paths, and `#000000` fill are unchanged. +The pinned source includes its terminal newline and has SHA-256 `e288cd08b510afbc19f1ea85c990397de2cad2077459a6833d64e26f86b761fa`. + +## Release verification + +The dedicated `Sei Global Wallet Consumer Smoke` workflow runs on wallet-related paths for pull requests and for pushes to `main` (so the publishing commit is gated too), daily on a schedule to catch registry and advisory drift, and on demand with `workflow_dispatch`. It executes `bun run test:sei-global-wallet-release`, including an audit-clean scoped npm consumer, a waiver-aware Bun consumer, declarations that resolve with no optional peer installed, native and bundled edge-like SSR, real local ZeroDev provider operations in esbuild/Vite, dependency graphs, and package contents. Regular package tests remain deterministic and do not perform clean consumer installs. + +`SEI_GLOBAL_WALLET_FAST_CHECK=1` shortens the local loop by skipping the two clean npm consumers and the entire Bun consumer path, so a green run under that flag covers neither the audit waiver nor Bun. Release verification must run without it, which is what CI does. diff --git a/packages/sei-global-wallet/package.json b/packages/sei-global-wallet/package.json index 7c09f948..19d43758 100644 --- a/packages/sei-global-wallet/package.json +++ b/packages/sei-global-wallet/package.json @@ -12,6 +12,7 @@ "module": "./dist/index.js", "types": "./dist/index.d.ts", "license": "ISC", + "sideEffects": true, "files": [ "dist" ], @@ -23,13 +24,21 @@ "test": "bun test --isolate src" }, "dependencies": { - "@dynamic-labs/global-wallet-client": "^4.60.1", - "@wallet-standard/wallet": "^1.1.0" + "@dynamic-labs/global-wallet-client": "^4.96.3", + "@wallet-standard/wallet": "^1.1.0", + "events": "3.3.0", + "process": "0.11.10" }, "devDependencies": { "@solana/wallet-standard-features": "^1.2.0", - "@solana/web3.js": "^1.92.1", - "typescript": "^5.7.3" + "@solana/web3.js": "1.98.1", + "@wallet-standard/base": "^1.0.1", + "@wallet-standard/features": "^1.0.3", + "@wallet-standard/wallet": "^1.1.0", + "@zerodev/sdk": "5.5.7", + "typescript": "^5.7.3", + "viem": "2.45.3", + "zksync-sso": "0.2.0" }, "peerDependencies": { "@dynamic-labs/ethereum-aa": "^4.15.0", @@ -38,7 +47,7 @@ "@wallet-standard/base": "^1.0.1", "@wallet-standard/features": "^1.0.3", "@wallet-standard/wallet": "^1.1.0", - "@zerodev/sdk": "5.4.36", + "@zerodev/sdk": "^5.4.36", "viem": "^2.7.12" }, "peerDependenciesMeta": { diff --git a/packages/sei-global-wallet/src/eip6963.ts b/packages/sei-global-wallet/src/eip6963.ts index 612b03ed..eb09aeb4 100644 --- a/packages/sei-global-wallet/src/eip6963.ts +++ b/packages/sei-global-wallet/src/eip6963.ts @@ -1,3 +1,12 @@ -import { EIP6963Emitter } from './lib/EIP6963Emitter.js'; +import { registerEIP6963Provider } from './lib/EIP6963Emitter.js'; -EIP6963Emitter(); +registerEIP6963Provider(); + +export { + EIP6963Emitter, + type EIP6963ProviderDetail, + type EIP6963ProviderInfo, + eip6963ProviderInfo, + registerEIP6963Provider, + unregisterEIP6963Provider +} from './lib/EIP6963Emitter.js'; diff --git a/packages/sei-global-wallet/src/ethereum.ts b/packages/sei-global-wallet/src/ethereum.ts index 0e0e6cf5..f1faee11 100644 --- a/packages/sei-global-wallet/src/ethereum.ts +++ b/packages/sei-global-wallet/src/ethereum.ts @@ -1 +1 @@ -export { createEIP1193Provider } from '@dynamic-labs/global-wallet-client/ethereum'; +export { createEIP1193Provider } from './lib/dynamicEthereum.js'; diff --git a/packages/sei-global-wallet/src/index.ts b/packages/sei-global-wallet/src/index.ts index 4d52d312..b2b388e2 100644 --- a/packages/sei-global-wallet/src/index.ts +++ b/packages/sei-global-wallet/src/index.ts @@ -1,5 +1,6 @@ import Wallet from './lib/wallet.js'; -export * from '@dynamic-labs/global-wallet-client/features'; +export * from './lib/dynamicFeatures.js'; +export { Wallet }; export default Wallet; diff --git a/packages/sei-global-wallet/src/lib/EIP6963Emitter.ts b/packages/sei-global-wallet/src/lib/EIP6963Emitter.ts index 4457d4b1..1f0a67f8 100644 --- a/packages/sei-global-wallet/src/lib/EIP6963Emitter.ts +++ b/packages/sei-global-wallet/src/lib/EIP6963Emitter.ts @@ -1,16 +1,66 @@ -import type { DataURIImage } from '@dynamic-labs/global-wallet-client'; -import { announceEip6963Provider, createEIP1193Provider } from '@dynamic-labs/global-wallet-client/ethereum'; import { config } from './config.js'; +import type { DataURIImage } from './dynamicClient.js'; +import { createEIP1193Provider } from './dynamicEthereum.js'; import Wallet from './wallet.js'; -export const EIP6963Emitter = () => { - announceEip6963Provider({ - info: { - icon: config.walletIcon as DataURIImage, - uuid: config.environmentId, - name: config.walletName, - rdns: config.eip6963.rdns - }, +export interface EIP6963ProviderInfo { + icon: DataURIImage; + name: string; + rdns: string; + uuid: string; +} + +export interface EIP6963ProviderDetail { + info: Readonly; + provider: ReturnType; +} + +export const eip6963ProviderInfo: Readonly = Object.freeze({ + icon: config.walletIcon, + name: config.walletName, + rdns: config.eip6963.rdns, + uuid: config.eip6963.uuid +}); + +const noCleanup = () => {}; +let activeCleanup: (() => void) | undefined; + +export const registerEIP6963Provider = (): (() => void) => { + if (typeof window === 'undefined' || typeof CustomEvent === 'undefined') return noCleanup; + if (activeCleanup) return activeCleanup; + + const browserWindow = window; + const detail: EIP6963ProviderDetail = Object.freeze({ + info: eip6963ProviderInfo, provider: createEIP1193Provider(Wallet) }); + const announceProvider = () => { + browserWindow.dispatchEvent( + new CustomEvent('eip6963:announceProvider', { + detail + }) + ); + }; + + browserWindow.addEventListener('eip6963:requestProvider', announceProvider); + + let active = true; + const cleanup = () => { + if (!active) return; + active = false; + browserWindow.removeEventListener('eip6963:requestProvider', announceProvider); + if (activeCleanup === cleanup) activeCleanup = undefined; + }; + + activeCleanup = cleanup; + announceProvider(); + + return cleanup; }; + +export const unregisterEIP6963Provider = () => { + activeCleanup?.(); +}; + +// Kept for compatibility with the original internal helper name. +export const EIP6963Emitter = registerEIP6963Provider; diff --git a/packages/sei-global-wallet/src/lib/__tests__/EIP6963Emitter.spec.ts b/packages/sei-global-wallet/src/lib/__tests__/EIP6963Emitter.spec.ts index ce826747..1cc0ee7f 100644 --- a/packages/sei-global-wallet/src/lib/__tests__/EIP6963Emitter.spec.ts +++ b/packages/sei-global-wallet/src/lib/__tests__/EIP6963Emitter.spec.ts @@ -1,40 +1,98 @@ -import { announceEip6963Provider, createEIP1193Provider } from '@dynamic-labs/global-wallet-client/ethereum'; -import { EIP6963Emitter } from '../EIP6963Emitter'; +import { afterEach, beforeEach, describe, expect, it, jest } from 'bun:test'; +import { createEIP1193Provider } from '../dynamicEthereum'; +import { type EIP6963ProviderDetail, eip6963ProviderInfo, registerEIP6963Provider, unregisterEIP6963Provider } from '../EIP6963Emitter'; import Wallet from '../wallet'; -jest.mock('@dynamic-labs/global-wallet-client/ethereum', () => ({ - announceEip6963Provider: jest.fn(), +jest.mock('../dynamicEthereum', () => ({ createEIP1193Provider: jest.fn() })); jest.mock('../wallet', () => ({})); -jest.mock('../config', () => ({ - config: { - walletIcon: 'icon-url', - environmentId: 'env-id', - walletName: 'SEI Wallet', - eip6963: { - rdns: 'com.sei.wallet' - } - } -})); describe('EIP6963Emitter', () => { - it('should announce the provider with correct config and provider instance', () => { - const mockProvider = { foo: 'bar' }; + const originalWindow = globalThis.window; + + beforeEach(() => { + unregisterEIP6963Provider(); + Object.defineProperty(globalThis, 'window', { + configurable: true, + value: new EventTarget() + }); + jest.clearAllMocks(); + }); + + afterEach(() => { + unregisterEIP6963Provider(); + Object.defineProperty(globalThis, 'window', { + configurable: true, + value: originalWindow + }); + }); + + it('announces immediately and again for every request', () => { + const mockProvider = { foo: 'bar' } as unknown as EIP6963ProviderDetail['provider']; (createEIP1193Provider as unknown as jest.Mock).mockReturnValue(mockProvider); + const announcements: EIP6963ProviderDetail[] = []; + window.addEventListener('eip6963:announceProvider', (event) => { + announcements.push((event as CustomEvent).detail); + }); - EIP6963Emitter(); + const cleanup = registerEIP6963Provider(); expect(createEIP1193Provider).toHaveBeenCalledWith(Wallet); - expect(announceEip6963Provider).toHaveBeenCalledWith({ - info: { - icon: 'icon-url', - uuid: 'env-id', - name: 'SEI Wallet', - rdns: 'com.sei.wallet' - }, - provider: mockProvider + expect(announcements).toEqual([ + { + info: eip6963ProviderInfo, + provider: mockProvider + } + ]); + + window.dispatchEvent(new Event('eip6963:requestProvider')); + window.dispatchEvent(new Event('eip6963:requestProvider')); + + expect(announcements).toHaveLength(3); + expect(announcements[1]).toBe(announcements[0]); + expect(announcements[2]).toBe(announcements[0]); + + cleanup(); + window.dispatchEvent(new Event('eip6963:requestProvider')); + expect(announcements).toHaveLength(3); + }); + + it('keeps one active request listener per registration', () => { + const mockProvider = { foo: 'bar' } as unknown as EIP6963ProviderDetail['provider']; + (createEIP1193Provider as unknown as jest.Mock).mockReturnValue(mockProvider); + const announcements: EIP6963ProviderDetail[] = []; + window.addEventListener('eip6963:announceProvider', (event) => { + announcements.push((event as CustomEvent).detail); + }); + + const firstCleanup = registerEIP6963Provider(); + const secondCleanup = registerEIP6963Provider(); + + expect(secondCleanup).toBe(firstCleanup); + expect(createEIP1193Provider).toHaveBeenCalledTimes(1); + + window.dispatchEvent(new Event('eip6963:requestProvider')); + expect(announcements).toHaveLength(2); + }); + + it('does nothing during SSR', () => { + Object.defineProperty(globalThis, 'window', { + configurable: true, + value: undefined }); + + const cleanup = registerEIP6963Provider(); + + expect(createEIP1193Provider).not.toHaveBeenCalled(); + expect(cleanup()).toBeUndefined(); + }); + + it('exports stable provider info', () => { + expect(eip6963ProviderInfo.name).toBe('Sei Global Wallet'); + expect(eip6963ProviderInfo.rdns).toBe('io.sei.global-wallet'); + expect(eip6963ProviderInfo.uuid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i); + expect(Object.isFrozen(eip6963ProviderInfo)).toBe(true); }); }); diff --git a/packages/sei-global-wallet/src/lib/__tests__/browserGlobal.preserve.spec.ts b/packages/sei-global-wallet/src/lib/__tests__/browserGlobal.preserve.spec.ts new file mode 100644 index 00000000..0a87764d --- /dev/null +++ b/packages/sei-global-wallet/src/lib/__tests__/browserGlobal.preserve.spec.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from 'bun:test'; + +// Separate file so `bun test --isolate` gives this the only import of the shim +// in its registry, with both globals already present. +describe('browser global shim with consumer values present', () => { + it('leaves consumer-defined global and process untouched', async () => { + const originalGlobal = Object.getOwnPropertyDescriptor(globalThis, 'global'); + const originalProcess = Object.getOwnPropertyDescriptor(globalThis, 'process'); + const consumerGlobal = { sentinel: 'consumer-global' }; + const consumerProcess = { env: { NODE_ENV: 'development' }, sentinel: 'consumer-process' }; + Object.defineProperty(globalThis, 'global', { configurable: true, value: consumerGlobal, writable: true }); + Object.defineProperty(globalThis, 'process', { configurable: true, value: consumerProcess, writable: true }); + + try { + await import('../browserGlobal.js'); + + // Read through Reflect so the ambient Node global typings do not narrow + // the comparison against these sentinels. + const installedGlobal: unknown = Reflect.get(globalThis, 'global'); + const installedProcess: unknown = Reflect.get(globalThis, 'process'); + + expect(installedGlobal).toBe(consumerGlobal); + expect(installedProcess).toBe(consumerProcess); + // The shim must not rewrite an environment the consumer chose. + expect(consumerProcess.env.NODE_ENV).toBe('development'); + } finally { + if (originalGlobal) Object.defineProperty(globalThis, 'global', originalGlobal); + else Reflect.deleteProperty(globalThis, 'global'); + + if (originalProcess) Object.defineProperty(globalThis, 'process', originalProcess); + else Reflect.deleteProperty(globalThis, 'process'); + } + }); +}); diff --git a/packages/sei-global-wallet/src/lib/__tests__/browserGlobal.spec.ts b/packages/sei-global-wallet/src/lib/__tests__/browserGlobal.spec.ts new file mode 100644 index 00000000..ae30ae3c --- /dev/null +++ b/packages/sei-global-wallet/src/lib/__tests__/browserGlobal.spec.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from 'bun:test'; + +// Runs in its own module registry (`bun test --isolate`), so the shim's +// import-time side effect is observed exactly once against absent globals. +describe('browser global shim with nothing defined', () => { + it('installs global and a process shim that reports a production NODE_ENV', async () => { + const originalGlobal = Object.getOwnPropertyDescriptor(globalThis, 'global'); + const originalProcess = Object.getOwnPropertyDescriptor(globalThis, 'process'); + Reflect.deleteProperty(globalThis, 'global'); + Reflect.deleteProperty(globalThis, 'process'); + + try { + await import('../browserGlobal.js'); + + const runtime = globalThis as typeof globalThis & { + global?: unknown; + process?: { browser?: boolean; env?: Record; nextTick?: unknown }; + }; + + expect(runtime.global).toBe(globalThis); + expect(runtime.process?.browser).toBe(true); + expect(typeof runtime.process?.nextTick).toBe('function'); + // Without this, libraries gating on NODE_ENV take their development + // branch inside a production browser bundle. + expect(runtime.process?.env?.NODE_ENV).toBe('production'); + } finally { + if (originalGlobal) Object.defineProperty(globalThis, 'global', originalGlobal); + if (originalProcess) Object.defineProperty(globalThis, 'process', originalProcess); + } + }); +}); diff --git a/packages/sei-global-wallet/src/lib/__tests__/browserIntegration.spec.ts b/packages/sei-global-wallet/src/lib/__tests__/browserIntegration.spec.ts new file mode 100644 index 00000000..609dd619 --- /dev/null +++ b/packages/sei-global-wallet/src/lib/__tests__/browserIntegration.spec.ts @@ -0,0 +1,102 @@ +import { afterAll, beforeAll, describe, expect, it } from 'bun:test'; + +class MemoryStorage implements Storage { + readonly #values = new Map(); + + get length() { + return this.#values.size; + } + + clear() { + this.#values.clear(); + } + + getItem(key: string) { + return this.#values.get(key) ?? null; + } + + key(index: number) { + return [...this.#values.keys()][index] ?? null; + } + + removeItem(key: string) { + this.#values.delete(key); + } + + setItem(key: string, value: string) { + this.#values.set(key, value); + } +} + +describe('browser registrations', () => { + const originalWindow = Object.getOwnPropertyDescriptor(globalThis, 'window'); + const originalStorage = Object.getOwnPropertyDescriptor(globalThis, 'localStorage'); + const browserWindow = new EventTarget(); + + beforeAll(() => { + Object.defineProperty(globalThis, 'window', { + configurable: true, + value: browserWindow + }); + Object.defineProperty(globalThis, 'localStorage', { + configurable: true, + value: new MemoryStorage() + }); + }); + + afterAll(() => { + if (originalWindow) Object.defineProperty(globalThis, 'window', originalWindow); + else delete (globalThis as { window?: unknown }).window; + + if (originalStorage) Object.defineProperty(globalThis, 'localStorage', originalStorage); + else delete (globalThis as { localStorage?: unknown }).localStorage; + }); + + it('announces EIP-6963 immediately and on every request', async () => { + const announcements: CustomEvent[] = []; + browserWindow.addEventListener('eip6963:announceProvider', (event) => { + announcements.push(event as CustomEvent); + }); + + const eip6963 = await import('../../eip6963'); + + expect(announcements).toHaveLength(1); + expect(announcements[0].detail.info).toEqual(eip6963.eip6963ProviderInfo); + expect(typeof announcements[0].detail.provider.request).toBe('function'); + + browserWindow.dispatchEvent(new Event('eip6963:requestProvider')); + browserWindow.dispatchEvent(new Event('eip6963:requestProvider')); + expect(announcements).toHaveLength(3); + expect(announcements[1].detail).toBe(announcements[0].detail); + expect(announcements[2].detail).toBe(announcements[0].detail); + + eip6963.unregisterEIP6963Provider(); + browserWindow.dispatchEvent(new Event('eip6963:requestProvider')); + expect(announcements).toHaveLength(3); + }); + + it('registers a real Solana wallet-standard provider', async () => { + const wallets: Array<{ icon: string; name: string }> = []; + browserWindow.addEventListener('wallet-standard:register-wallet', (event) => { + (event as CustomEvent).detail({ + register: (wallet: { icon: string; name: string }) => wallets.push(wallet) + }); + }); + + await import('../../solana'); + + expect(wallets).toHaveLength(1); + expect(wallets[0].name).toBe('Sei Global Wallet'); + expect(wallets[0].icon).toMatch(/^data:image\/svg\+xml;base64,/); + + browserWindow.dispatchEvent( + new CustomEvent('wallet-standard:app-ready', { + detail: { + register: (wallet: { icon: string; name: string }) => wallets.push(wallet) + } + }) + ); + expect(wallets).toHaveLength(2); + expect(wallets[1]).toBe(wallets[0]); + }); +}); diff --git a/packages/sei-global-wallet/src/lib/__tests__/config.spec.ts b/packages/sei-global-wallet/src/lib/__tests__/config.spec.ts index e99caa42..7c048ebc 100644 --- a/packages/sei-global-wallet/src/lib/__tests__/config.spec.ts +++ b/packages/sei-global-wallet/src/lib/__tests__/config.spec.ts @@ -1,3 +1,5 @@ +import { describe, expect, it } from 'bun:test'; +import { createHash } from 'node:crypto'; import { config } from '../config'; describe('sei-global-wallet config', () => { @@ -7,10 +9,20 @@ describe('sei-global-wallet config', () => { expect(config.walletUrl).toBe('https://global-wallet.sei.io'); expect(config.environmentId).toBe('36b63d10-7ba6-49a3-9614-22f471b9283c'); expect(config.eip6963.rdns).toBe('io.sei.global-wallet'); + expect(config.eip6963.uuid).toBe('36b63d10-7ba6-49a3-9614-22f471b9283c'); + expect(config.eip6963.uuid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i); }); - it('walletIcon is an SVG data URI', () => { + it('uses the unmodified square official Sei mark', () => { expect(typeof config.walletIcon).toBe('string'); expect(config.walletIcon).toMatch(/^data:image\/svg\+xml;base64,.+/); + + const bytes = Buffer.from(config.walletIcon.slice(config.walletIcon.indexOf(',') + 1), 'base64'); + const svg = bytes.toString('utf8'); + expect(svg.startsWith('')).toBe(true); + expect(svg).toContain('Sei mark'); + expect(svg.endsWith('\n')).toBe(true); + expect(svg).not.toContain('#9E1F19'); + expect(createHash('sha256').update(bytes).digest('hex')).toBe('e288cd08b510afbc19f1ea85c990397de2cad2077459a6833d64e26f86b761fa'); }); }); diff --git a/packages/sei-global-wallet/src/lib/__tests__/entrypoints.spec.ts b/packages/sei-global-wallet/src/lib/__tests__/entrypoints.spec.ts new file mode 100644 index 00000000..ceee5d19 --- /dev/null +++ b/packages/sei-global-wallet/src/lib/__tests__/entrypoints.spec.ts @@ -0,0 +1,57 @@ +import { describe, expect, it } from 'bun:test'; +import packageJson from '../../../package.json'; + +describe('public entrypoints', () => { + it('imports the root and every non-AA subpath during SSR', async () => { + const [root, eip6963, ethereum, solana] = await Promise.all([ + import('../../index'), + import('../../eip6963'), + import('../../ethereum'), + import('../../solana') + ]); + + expect(root.default).toBe(root.Wallet); + expect(typeof root.connect).toBe('function'); + expect(typeof eip6963.registerEIP6963Provider).toBe('function'); + expect(eip6963.eip6963ProviderInfo.name).toBe('Sei Global Wallet'); + expect(typeof ethereum.createEIP1193Provider).toBe('function'); + expect(typeof solana.createSolanaWallet).toBe('function'); + expect(typeof solana.registerSolanaStandard).toBe('function'); + }); + + it('keeps the optional zerodev export for consumers that install Dynamic AA', () => { + expect(packageJson.exports['./zerodev']).toEqual({ + types: './dist/zerodev.d.ts', + import: './dist/zerodev.js', + default: './dist/zerodev.js' + }); + expect(packageJson.peerDependenciesMeta['@dynamic-labs/ethereum-aa']).toEqual({ optional: true }); + }); + + // Optional peers only stop erroring when absent; once an application has one, + // a narrower range than 1.4.1 published fails its install with ERESOLVE. Any + // change here must stay a superset of these ranges. + it('keeps every optional peer range compatible with the 1.4.1 contract', () => { + expect(packageJson.peerDependencies).toEqual({ + '@dynamic-labs/ethereum-aa': '^4.15.0', + '@solana/wallet-standard-features': '^1.2.0', + '@solana/web3.js': '^1.92.1', + '@wallet-standard/base': '^1.0.1', + '@wallet-standard/features': '^1.0.3', + '@wallet-standard/wallet': '^1.1.0', + '@zerodev/sdk': '^5.4.36', + viem: '^2.7.12' + }); + }); + + // Dynamic's ./solana module imports @wallet-standard/wallet directly, so the + // solana entrypoint needs it installed rather than merely declared as a peer. + it('keeps @wallet-standard/wallet installed for the solana entrypoint', () => { + expect(packageJson.dependencies['@wallet-standard/wallet']).toBe('^1.1.0'); + }); + + // An exact pin would make every Dynamic transitive fix wait on a release here. + it('declares the Dynamic dependency as a range', () => { + expect(packageJson.dependencies['@dynamic-labs/global-wallet-client']).toBe('^4.96.3'); + }); +}); diff --git a/packages/sei-global-wallet/src/lib/__tests__/registerSolanaStandard.spec.ts b/packages/sei-global-wallet/src/lib/__tests__/registerSolanaStandard.spec.ts index a61960c8..c9c06ea9 100644 --- a/packages/sei-global-wallet/src/lib/__tests__/registerSolanaStandard.spec.ts +++ b/packages/sei-global-wallet/src/lib/__tests__/registerSolanaStandard.spec.ts @@ -1,8 +1,9 @@ -import { createSolanaWallet, registerWallet } from '@dynamic-labs/global-wallet-client/solana'; +import { afterAll, beforeAll, describe, expect, it, jest } from 'bun:test'; +import { createSolanaWallet, registerWallet } from '../dynamicSolana'; import { registerSolanaStandard } from '../registerSolanaStandard'; import Wallet from '../wallet'; -jest.mock('@dynamic-labs/global-wallet-client/solana', () => ({ +jest.mock('../dynamicSolana', () => ({ createSolanaWallet: jest.fn(), registerWallet: jest.fn() })); @@ -16,11 +17,27 @@ jest.mock('../config', () => ({ })); describe('registerSolanaStandard', () => { - it('calls createSolanaWallet and registers it', () => { - const mockWalletObject = { id: 'sei' }; + const originalWindow = globalThis.window; + + beforeAll(() => { + Object.defineProperty(globalThis, 'window', { + configurable: true, + value: {} + }); + }); + + afterAll(() => { + Object.defineProperty(globalThis, 'window', { + configurable: true, + value: originalWindow + }); + }); + + it('creates and registers one wallet-standard provider', () => { + const mockWalletObject = { id: 'sei' } as unknown as ReturnType; (createSolanaWallet as unknown as jest.Mock).mockReturnValue(mockWalletObject); - registerSolanaStandard(); + const wallet = registerSolanaStandard(); expect(createSolanaWallet).toHaveBeenCalledWith( { @@ -31,5 +48,9 @@ describe('registerSolanaStandard', () => { ); expect(registerWallet).toHaveBeenCalledWith(mockWalletObject); + expect(wallet).toBe(mockWalletObject); + expect(registerSolanaStandard()).toBe(mockWalletObject); + expect(createSolanaWallet).toHaveBeenCalledTimes(1); + expect(registerWallet).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/sei-global-wallet/src/lib/browserGlobal.ts b/packages/sei-global-wallet/src/lib/browserGlobal.ts new file mode 100644 index 00000000..6f2cac90 --- /dev/null +++ b/packages/sei-global-wallet/src/lib/browserGlobal.ts @@ -0,0 +1,33 @@ +import processShim from 'process/browser.js'; + +type BrowserGlobal = typeof globalThis & { + global?: typeof globalThis; + process?: typeof processShim; +}; + +const runtime = globalThis as BrowserGlobal; + +const install = (key: 'global' | 'process', value: unknown) => { + Object.defineProperty(runtime, key, { + configurable: true, + enumerable: false, + value, + writable: true + }); +}; + +// Dynamic 4.x reads Node-style globals in browser and edge-like runtimes, but +// its own polyfills can execute later after bundling. Install only missing +// values and preserve anything the consumer already set. +if (typeof runtime.global === 'undefined') { + install('global', runtime); +} + +if (typeof runtime.process === 'undefined') { + // Every library loaded after this point shares the shim, and + // `process/browser.js` ships an empty `env`. Libraries that branch on + // `process.env.NODE_ENV !== 'production'` would otherwise take their + // development path inside a production bundle. + processShim.env.NODE_ENV ??= 'production'; + install('process', processShim); +} diff --git a/packages/sei-global-wallet/src/lib/config.ts b/packages/sei-global-wallet/src/lib/config.ts index 20ddc1b4..69f2ddd4 100644 --- a/packages/sei-global-wallet/src/lib/config.ts +++ b/packages/sei-global-wallet/src/lib/config.ts @@ -1,22 +1,28 @@ -import type { DataURIImage } from '@dynamic-labs/global-wallet-client'; -import type { WalletIcon } from '@wallet-standard/base'; +import type { DataURIImage } from './dynamicClient.js'; -interface EIP6963Config { +// Deliberately free of `@wallet-standard/base`: config.d.ts is reachable from +// the root and eip6963 entrypoints, which must typecheck without any optional +// peer installed. The Solana path narrows this to `WalletIcon` at its boundary. +interface WalletConfig { walletName: string; - walletIcon: DataURIImage | WalletIcon; + walletIcon: DataURIImage; walletUrl: string; environmentId: string; eip6963: { rdns: string; + uuid: string; }; } -export const config: EIP6963Config = { +export const config: WalletConfig = { // Wallet name will be seen as the Wallet name walletName: 'Sei Global Wallet', // Wallet icon will be seen as the Wallet icon + // Exact, unrecolored encoding of the official square Sei mark, including the + // source file's terminal newline. config.spec.ts pins the decoded SHA-256: + // https://github.com/sei-protocol/sei-docs/blob/8710d6078058bdf67ec704b346f75ee9883e25ea/assets/brand/logo/sei-mark.svg walletIcon: - 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjU2IiBoZWlnaHQ9Ijk2IiB2aWV3Qm94PSIwIDAgMjU2IDk2IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDBfMzE5MV83MzI1KSI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNDcuNDYzNyA5NS4wODU4QzYxLjU5OTkgOTUuMDg1OCA3NC4zMDAyIDg4Ljk1ODMgODMuMDM1OCA3OS4yMjMxQzc4Ljk3MjIgNzUuNzAzNSA3Mi44NDgyIDc1LjQ4OTcgNjguNTIyIDc4Ljk2ODVMNjcuNjk1NSA3OS42MzMyQzU5Ljc3NDQgODYuMDAyOSA0OC4yMzI0IDg1LjA2MjIgNDEuNDU1IDc3LjQ5NDZDMzcuNzU4NiA3My4zNjcxIDMxLjQyODEgNzIuOTQ1OCAyNy4yMTM2IDc2LjU0NjZMMTcuNjg0NSA4NC42ODc5QzI1Ljg0NjEgOTEuMTk0OSAzNi4xOTg5IDk1LjA4NTggNDcuNDYzNyA5NS4wODU4Wk02Mi45MzE0IDcyLjA2MzlDNzAuNDc2MSA2NS45OTY3IDgxLjA4MDkgNjYuMjE0NCA4OC4zMzcyIDcyLjA3NDJDOTIuNjc0NSA2NC45MTIzIDk1LjE2OTkgNTYuNTE4MSA5NS4xNjk5IDQ3LjU0MjhDOTUuMTY5OSAzNy41Njk5IDkyLjA4ODkgMjguMzE0MyA4Ni44MjMzIDIwLjY2OTdDODMuNDI5IDE5Ljk0OTMgNzkuNzQ5NyAyMC43OTA3IDc2Ljk1NjggMjMuMjU3Mkw3Ni4xNjE5IDIzLjk1OTJDNjguNTQ0MSAzMC42ODY2IDU2Ljk3MSAzMC4yNzc3IDQ5Ljg1MDMgMjMuMDI5OUM0NS45NjY2IDE5LjA3NjkgMzkuNjIzNCAxOC45NDcxIDM1LjU4MDIgMjIuNzM3OUwyNC40OTEyIDMzLjEzNDZMMTguMzkwOCAyNi42NzI3TDI5LjQ4IDE2LjI3NTlDMzcuMDUzOCA5LjE3NDk0IDQ4LjkzNjUgOS40MTgxIDU2LjIxMTUgMTYuODIzQzYwLjAxMjcgMjAuNjkyMiA2Ni4xOTA5IDIwLjkxMDQgNzAuMjU3NSAxNy4zMTkxTDcxLjA1MjMgMTYuNjE3MUM3My41MjA3IDE0LjQzNzEgNzYuMzc2OCAxMi45Njc1IDc5LjM2NyAxMi4xOTQ2QzcwLjkxNTIgNC42MTM2MiA1OS43MzAxIDAgNDcuNDYzNyAwQzIzLjI0NjEgMCAzLjI0MzMyIDE3Ljk4MzQgMC4xNjc1MzYgNDEuMjgxMUM3LjUyMzM3IDM3Ljc2MzQgMTYuNTk2NyAzOS4xMTcgMjIuNTk0NCA0NS4yMzc4QzI2LjM4MDcgNDkuMTAxOSAzMi40NzczIDQ5LjUwOTcgMzYuNzQ3OSA0Ni4xODQ3TDQyLjk0ODEgNDEuMzU3NUM1MC43NCAzNS4yOTA4IDYxLjc1NTEgMzUuNTg0NCA2OS4yMTA1IDQyLjA1NzlMODEuMjgxNSA1Mi41Mzg0TDc1LjQzMzMgNTkuMjI3OEw2My4zNjI1IDQ4Ljc0NzFDNTkuMTIzMSA0NS4wNjYzIDUyLjg1OTggNDQuODk5MiA0OC40Mjg3IDQ4LjM0OUw0Mi4yMjg4IDUzLjE3NjFDMzQuMzgyNSA1OS4yODUgMjMuMTgxMyA1OC41MzU3IDE2LjIyNSA1MS40MzYzQzEyLjE3OTggNDcuMzA4MSA1LjU2Mzg4IDQ3LjE2NCAxLjM0MTY5IDUxLjExMjRMMCA1Mi4zNjY5QzEuMDA0MDkgNjIuMjk3IDUuMDc0MzMgNzEuMzI1MSAxMS4yNTI5IDc4LjQ5NjhMMjEuNDE5MiA2OS44MTA5QzI5LjMxMzkgNjMuMDY1OCA0MS4xNzI2IDYzLjg1NTMgNDguMDk3IDcxLjU4NjlDNTEuNzE1IDc1LjYyNjggNTcuODc2MyA3Ni4xMjg5IDYyLjEwNDkgNzIuNzI4NUw2Mi45MzE0IDcyLjA2MzlaIiBmaWxsPSIjOUUxRjE5Ii8+CjxwYXRoIGQ9Ik0xMjkuNTMzIDYyLjc5MDlDMTI5LjUzMyA3Mi43Mzg4IDEzNy4zNjkgNzkuMjYzOSAxNDkuNzEyIDc5LjI2MzlDMTYyLjE2NCA3OS4yNjM5IDE3MC43NSA3Mi45NTI4IDE3MC43NSA2My4wMDQ3QzE3MC43NSA1NS4xOTU5IDE2Ni4zNDkgNTEuMTMxMiAxNTcuNDQxIDQ4Ljk5MThMMTQ4LjIxIDQ2Ljc0NTNDMTQzLjA1NyA0NS40NjE4IDE0MC40ODIgNDMuMTA4NSAxNDAuNDgyIDM5LjU3ODZDMTQwLjQ4MiAzNC43NjQ5IDE0NC4wMjMgMzEuOTgzOCAxNTAuMzU2IDMxLjk4MzhDMTU2LjQ3NSAzMS45ODM4IDE2MC4wMTcgMzUuMjk5NyAxNjAuMjMyIDQwLjU0MTJIMTY5Ljg5M0MxNjkuNjc3IDMwLjU5MzEgMTYyLjE2NCAyNC4xNzUgMTUwLjY3OCAyNC4xNzVDMTM5LjA4NSAyNC4xNzUgMTMwLjkyOSAzMC4yNzIxIDEzMC45MjkgMzkuODk5M0MxMzAuOTI5IDQ3LjYwMTIgMTM1LjMzIDUyLjIwMDkgMTQ0LjY2NyA1NC40NDcxTDE1My44OTkgNTYuNTg2NUMxNTkuMzczIDU3Ljk3NzIgMTYxLjA5IDYwLjExNjUgMTYxLjA5IDYzLjY0NjZDMTYxLjA5IDY4LjQ2MDEgMTU3LjAxMSA3MS4zNDgzIDE0OS45MjcgNzEuMzQ4M0MxNDMuMjcyIDcxLjM0ODMgMTM5LjE5NCA2OC4xMzkyIDEzOS4xOTQgNjIuNzkwOUgxMjkuNTMzWiIgZmlsbD0iIzlFMUYxOSIvPgo8cGF0aCBkPSJNMjA2LjczMiA3OS4yNjM5QzIxOS43MjEgNzkuMjYzOSAyMjguNTIyIDcyLjk1MjggMjMxLjA5OCA2MS43MjEySDIyMS42NTNDMjE5LjkzNiA2Ny43MTEyIDIxNC43ODQgNzAuOTIwMyAyMDYuOTQ3IDcwLjkyMDNDMTk2Ljk2NCA3MC45MjAzIDE5MS4xNjkgNjQuODIzIDE5MC40MTggNTMuODA1NEwyMzAuOTkxIDUzLjY5ODNWNDkuOTU0NUMyMzAuOTkxIDM0LjQ0NCAyMjEuMjI0IDI0LjE3NSAyMDYuMzA0IDI0LjE3NUMxOTEuMDYyIDI0LjE3NSAxODAuNjUgMzUuNTEzNyAxODAuNjUgNTEuODhDMTgwLjY1IDY4LjEzOTIgMTkxLjI3NyA3OS4yNjM5IDIwNi43MzIgNzkuMjYzOVpNMjA2LjMwNCAzMi42MjU1QzIxNS4yMTMgMzIuNjI1NSAyMjAuOTAyIDM4LjQwMTkgMjIwLjkwMiA0Ny4wNjYzSDE5MC42MzNDMTkxLjkyMSAzNy44NjcgMTk3LjUwMiAzMi42MjU1IDIwNi4zMDQgMzIuNjI1NVoiIGZpbGw9IiM5RTFGMTkiLz4KPHBhdGggZD0iTTI0OS4zNDUgMTMuMjY0MkMyNTIuOTk0IDEzLjI2NDIgMjU2IDEwLjI2OSAyNTYgNi42MzIwN0MyNTYgMi44ODgxNyAyNTIuOTk0IDAgMjQ5LjM0NSAwQzI0NS42OTUgMCAyNDIuNjg5IDIuODg4MTcgMjQyLjY4OSA2LjYzMjA3QzI0Mi42ODkgMTAuMjY5IDI0NS42OTUgMTMuMjY0MiAyNDkuMzQ1IDEzLjI2NDJaTTI0NC40MDggNzcuOTgwM0gyNTQuNDk3VjI1LjY3MjVIMjQ0LjQwOFY3Ny45ODAzWiIgZmlsbD0iIzlFMUYxOSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzMxOTFfNzMyNSI+CjxyZWN0IHdpZHRoPSIyNTYiIGhlaWdodD0iOTYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==', + 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNDAiIGhlaWdodD0iMjQwIiB2aWV3Qm94PSIwIDAgMjMuOTk2NSAyMy45OTY1IiBmaWxsPSIjMDAwMDAwIj48dGl0bGU+U2VpIG1hcms8L3RpdGxlPjxwYXRoIGQ9Ik0xMS45OTY5IDIzLjk5NjVIMTEuOTk2NUwxMS45OTY3IDIzLjk5NjVMMTEuOTk2OSAyMy45OTY1WiIvPjxwYXRoIGQ9Ik0xNi4wODUgMTkuNTI1NkMxNi44MDYxIDE4Ljg2ODkgMTcuOTA5MiAxOC44NjkgMTguNjMwMyAxOS41MjU4TDE4LjYzMDUgMTkuNTI1OUwyMC4wOTEgMjAuODU1OEMxNy45NTcgMjIuODA2NSAxNS4xMTU4IDIzLjk5NjUgMTEuOTk2NyAyMy45OTY1QzguODg4MjQgMjMuOTk2NCA2LjA1NTkxIDIyLjgxNDcgMy45MjQ0OCAyMC44NzZMNS40MDcyIDE5LjUyNTlMNS40MDczNCAxOS41MjU4QzYuMTI4NDQgMTguODY5IDcuMjMxNDcgMTguODY4OSA3Ljk1MjY1IDE5LjUyNTZMOS4yNTA4OCAyMC43MDg3TDkuMjUxMDkgMjAuNzA4OUMxMC44MTk1IDIyLjEzNzQgMTMuMjE4MiAyMi4xMzc0IDE0Ljc4NjYgMjAuNzA4OUwxNC43ODY4IDIwLjcwODdMMTYuMDg1IDE5LjUyNTZMMTYuMDg1IDE5LjUyNTZaIi8+PHBhdGggZD0iTTExLjk5NjUgMEMxNS4xMjE0IDAgMTcuOTY3MiAxLjE5NDIxIDIwLjEwMjUgMy4xNTEwNUMyMC4wNDEyIDMuMjAwNzUgMTkuOTgxIDMuMjUyNDUgMTkuOTIyIDMuMzA2MTlMMTkuOTIxOCAzLjMwNjM2TDE4LjYyMzYgNC40ODk0NEMxNy45MDI1IDUuMTQ2MDUgMTYuNzk5NiA1LjE0NjA1IDE2LjA3ODUgNC40ODk0NEwxNC43ODAzIDMuMzA2MzZMMTQuNzggMy4zMDYxOUwxNC43NDMxIDMuMjcyOTZDMTMuMTc0MiAxLjg3NzcyIDEwLjgwMDcgMS44ODg4MyA5LjI0NDU0IDMuMzA2MjJMNS45MjU2NiA2LjMyODMyTDcuNDIwMzggNy45NjkzOUwxMC43Mzk0IDQuOTQ3MTVMMTAuNzM5NSA0Ljk0NzA0QzExLjQ2MDcgNC4yOTAyNSAxMi41NjM3IDQuMjkwMTYgMTMuMjg0OSA0Ljk0NjgzTDE0LjU4MzEgNi4xMjk5MUwxNC41ODMzIDYuMTMwMTJDMTYuMTUxNyA3LjU1ODY2IDE4LjU1MDQgNy41NTg2NiAyMC4xMTg4IDYuMTMwMTJMMjAuMTE5IDYuMTI5OTRMMjEuNDE3MiA0Ljk0Njg3TDIxLjQxNzIgNC45NDY4M0MyMS40NzUzIDQuODkzOTEgMjEuNTM1OSA0Ljg0NTMgMjEuNTk4NiA0LjgwMDkxQzIzLjEwNDMgNi44MDU5MiAyMy45OTY1IDkuMjk3OSAyMy45OTY1IDExLjk5ODNDMjMuOTk2NSAxNC43MDY5IDIzLjA5ODggMTcuMjA1OCAyMS41ODQ3IDE5LjIxMzlMMjAuMTI1MyAxNy44ODVMMjAuMTI1MyAxNy44ODQ5QzE4LjU1NjkgMTYuNDU2NCAxNi4xNTgyIDE2LjQ1NjQgMTQuNTg5OCAxNy44ODQ5TDE0LjU4OTYgMTcuODg1MUwxMy4yOTE0IDE5LjA2ODJDMTIuNTcwMyAxOS43MjQ4IDExLjQ2NzQgMTkuNzI0OCAxMC43NDYzIDE5LjA2ODJMOS40NDgwNSAxNy44ODUxTDkuNDQ3ODQgMTcuODg0OUw5LjQxMDkyIDE3Ljg1MTdDNy44NDIgMTYuNDU2NSA1LjQ2ODQ5IDE2LjQ2NzYgMy45MTIzMyAxNy44ODVMMi40MjY2NCAxOS4yMzc4QzEuMzgxMjcgMTcuODU4NCAwLjYyNzM2NSAxNi4yNDYxIDAuMjU2NTM2IDE0LjQ5MjRMMi43MzQ1OCAxMi4yMzZMMi43MzQ3MiAxMi4yMzU5QzMuNDU1ODYgMTEuNTc5IDQuNTU4NjEgMTEuNTc5IDUuMjc5NzggMTIuMjM1OEw2LjU3ODM3IDEzLjQxODhMNi41Nzg0NyAxMy40MTg5QzguMTQ2ODEgMTQuODQ3NCAxMC41NDU1IDE0Ljg0NzYgMTIuMTEzNyAxMy40MTg4TDEzLjQxMjIgMTIuMjM1OEMxNC4xMzM0IDExLjU3OSAxNS4yMzY1IDExLjU3OSAxNS45NTc3IDEyLjIzNTlMMTUuOTU3OCAxMi4yMzZMMTkuMjc2OSAxNS4yNTgyTDIwLjc3MTYgMTMuNjE3MUwxNy40NTI3IDEwLjU5NUwxNy40NTI3IDEwLjU5NUMxNS44ODQzIDkuMTY2NDYgMTMuNDg1NiA5LjE2NjQ2IDExLjkxNzIgMTAuNTk1TDExLjkxNzEgMTAuNTk1MUwxMC42MTg1IDExLjc3ODJMMTAuNjE4NSAxMS43NzgyQzkuODk3NzkgMTIuNDM0OCA4Ljc5NDc2IDEyLjQzNSA4LjA3MzUyIDExLjc3ODJMNi43NzQ5MyAxMC41OTUxTDYuNzc0ODYgMTAuNTk1TDYuNzM3OTUgMTAuNTYxOEM1LjE2OTAzIDkuMTY2NTQgMi43OTU4NyA5LjE3NzY1IDEuMjM5NzEgMTAuNTk1TDAgMTEuNzIzOUMwLjE0NTg3NiA1LjIyNDM4IDUuNDYwNzEgNy4wNzkxM2UtMDYgMTEuOTk2NSAwWiIvPjwvc3ZnPgo=', // URL of your wallet domain (e.g. https://dynamic.example.com) walletUrl: 'https://global-wallet.sei.io', // Environment ID of your wallet (e.g. 1234567890) @@ -24,6 +30,8 @@ export const config: EIP6963Config = { // EIP6963 configuration eip6963: { // RDNS of your wallet (e.g. com.example.wallet) - rdns: 'io.sei.global-wallet' + rdns: 'io.sei.global-wallet', + // Stable UUIDv4 for the EIP-6963 provider identity. + uuid: '36b63d10-7ba6-49a3-9614-22f471b9283c' } }; diff --git a/packages/sei-global-wallet/src/lib/dynamicClient.ts b/packages/sei-global-wallet/src/lib/dynamicClient.ts new file mode 100644 index 00000000..e5d122cc --- /dev/null +++ b/packages/sei-global-wallet/src/lib/dynamicClient.ts @@ -0,0 +1,7 @@ +import './browserGlobal.js'; + +export { + createGlobalWalletClient, + type DataURIImage, + type GlobalWalletClient +} from '@dynamic-labs/global-wallet-client'; diff --git a/packages/sei-global-wallet/src/lib/dynamicEthereum.ts b/packages/sei-global-wallet/src/lib/dynamicEthereum.ts new file mode 100644 index 00000000..11a624da --- /dev/null +++ b/packages/sei-global-wallet/src/lib/dynamicEthereum.ts @@ -0,0 +1,3 @@ +import './browserGlobal.js'; + +export { createEIP1193Provider } from '@dynamic-labs/global-wallet-client/ethereum'; diff --git a/packages/sei-global-wallet/src/lib/dynamicFeatures.ts b/packages/sei-global-wallet/src/lib/dynamicFeatures.ts new file mode 100644 index 00000000..e95c98f2 --- /dev/null +++ b/packages/sei-global-wallet/src/lib/dynamicFeatures.ts @@ -0,0 +1,3 @@ +import './browserGlobal.js'; + +export * from '@dynamic-labs/global-wallet-client/features'; diff --git a/packages/sei-global-wallet/src/lib/dynamicSolana.ts b/packages/sei-global-wallet/src/lib/dynamicSolana.ts new file mode 100644 index 00000000..516314ee --- /dev/null +++ b/packages/sei-global-wallet/src/lib/dynamicSolana.ts @@ -0,0 +1,3 @@ +import './browserGlobal.js'; + +export { createSolanaWallet, registerWallet } from '@dynamic-labs/global-wallet-client/solana'; diff --git a/packages/sei-global-wallet/src/lib/dynamicZerodev.ts b/packages/sei-global-wallet/src/lib/dynamicZerodev.ts new file mode 100644 index 00000000..00e4adb5 --- /dev/null +++ b/packages/sei-global-wallet/src/lib/dynamicZerodev.ts @@ -0,0 +1,3 @@ +import './browserGlobal.js'; + +export { createKernelClient } from '@dynamic-labs/global-wallet-client/zerodev'; diff --git a/packages/sei-global-wallet/src/lib/registerSolanaStandard.ts b/packages/sei-global-wallet/src/lib/registerSolanaStandard.ts index d4652faf..e21921ac 100644 --- a/packages/sei-global-wallet/src/lib/registerSolanaStandard.ts +++ b/packages/sei-global-wallet/src/lib/registerSolanaStandard.ts @@ -1,16 +1,23 @@ -import { createSolanaWallet, registerWallet } from '@dynamic-labs/global-wallet-client/solana'; import type { WalletIcon } from '@wallet-standard/base'; import { config } from './config.js'; +import { createSolanaWallet, registerWallet } from './dynamicSolana.js'; import Wallet from './wallet.js'; +let registeredWallet: ReturnType | undefined; + export const registerSolanaStandard = () => { - registerWallet( - createSolanaWallet( - { - icon: config.walletIcon as WalletIcon, - name: config.walletName - }, - Wallet - ) + if (typeof window === 'undefined') return undefined; + if (registeredWallet) return registeredWallet; + + const wallet = createSolanaWallet( + { + icon: config.walletIcon as WalletIcon, + name: config.walletName + }, + Wallet ); + registerWallet(wallet); + registeredWallet = wallet; + + return wallet; }; diff --git a/packages/sei-global-wallet/src/lib/wallet.ts b/packages/sei-global-wallet/src/lib/wallet.ts index ebc142a5..9d74dd08 100644 --- a/packages/sei-global-wallet/src/lib/wallet.ts +++ b/packages/sei-global-wallet/src/lib/wallet.ts @@ -1,5 +1,5 @@ -import { createGlobalWalletClient, type GlobalWalletClient } from '@dynamic-labs/global-wallet-client'; import { config } from './config.js'; +import { createGlobalWalletClient, type GlobalWalletClient } from './dynamicClient.js'; const Wallet: GlobalWalletClient = createGlobalWalletClient({ environmentId: config.environmentId, diff --git a/packages/sei-global-wallet/src/solana.ts b/packages/sei-global-wallet/src/solana.ts index d76c11b5..95d09734 100644 --- a/packages/sei-global-wallet/src/solana.ts +++ b/packages/sei-global-wallet/src/solana.ts @@ -2,4 +2,5 @@ import { registerSolanaStandard } from './lib/registerSolanaStandard.js'; registerSolanaStandard(); -export { createSolanaWallet } from '@dynamic-labs/global-wallet-client/solana'; +export { createSolanaWallet } from './lib/dynamicSolana.js'; +export { registerSolanaStandard }; diff --git a/packages/sei-global-wallet/src/zerodev.ts b/packages/sei-global-wallet/src/zerodev.ts index 70a02d3f..0d1596bf 100644 --- a/packages/sei-global-wallet/src/zerodev.ts +++ b/packages/sei-global-wallet/src/zerodev.ts @@ -1 +1 @@ -export { createKernelClient } from '@dynamic-labs/global-wallet-client/zerodev'; +export { createKernelClient } from './lib/dynamicZerodev.js'; diff --git a/packages/sei-global-wallet/tsconfig.json b/packages/sei-global-wallet/tsconfig.json index 2bc744cb..ccc04c8a 100644 --- a/packages/sei-global-wallet/tsconfig.json +++ b/packages/sei-global-wallet/tsconfig.json @@ -7,6 +7,7 @@ "rootDir": "./src", "module": "NodeNext", "moduleResolution": "NodeNext", - "declaration": true + "declaration": true, + "lib": ["ES2022", "DOM"] } } diff --git a/scripts/check-sei-global-wallet.ts b/scripts/check-sei-global-wallet.ts new file mode 100644 index 00000000..00632398 --- /dev/null +++ b/scripts/check-sei-global-wallet.ts @@ -0,0 +1,830 @@ +import assert from 'node:assert/strict'; +import { webcrypto } from 'node:crypto'; +import { mkdir, mkdtemp, readdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import vm from 'node:vm'; +import { collectDynamicLineVersions, findDynamicLineConflicts, formatDynamicLineConflicts } from './dynamic-package-lock.js'; + +interface ProcessResult { + exitCode: number; + stderr: string; + stdout: string; +} + +interface AuditReport { + metadata?: { + vulnerabilities?: { + total?: number; + }; + }; + vulnerabilities?: Record; +} + +interface PackResult { + filename: string; + files: Array<{ path: string }>; +} + +interface PackageLock { + packages: Record; version?: string }>; +} + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); +const packageDir = join(root, 'packages/sei-global-wallet'); +const temporaryRoot = await mkdtemp(join(tmpdir(), 'sei-global-wallet-release-')); +// Documented in packages/sei-global-wallet/README.md: skips the clean npm +// consumers and the whole Bun path, so it never substitutes for a full run. +const fastCheck = process.env.SEI_GLOBAL_WALLET_FAST_CHECK === '1'; +const acceptedBunAdvisories: readonly string[] = ['GHSA-378v-28hj-76wf', 'GHSA-58qx-3vcg-4xpx', 'GHSA-96hv-2xvq-fx4p']; + +const manifest = JSON.parse(await readFile(join(packageDir, 'package.json'), 'utf8')) as { + dependencies: Record; + peerDependencies: Record; +}; +const dynamicRange = manifest.dependencies['@dynamic-labs/global-wallet-client']; + +// The versions the consumers are built against: Dynamic 4.96.3's own peer +// contract. The published peer ranges stay wider than these on purpose, so an +// application that already carries one of these packages keeps resolving. +const testedPeerVersions = { + '@dynamic-labs/ethereum-aa': '4.96.3', + '@solana/wallet-standard-features': '^1.2.0', + '@solana/web3.js': '1.98.1', + '@wallet-standard/base': '^1.0.1', + '@wallet-standard/features': '^1.0.3', + '@wallet-standard/wallet': '^1.1.0', + '@zerodev/sdk': '5.5.7', + viem: '2.45.3', + 'zksync-sso': '0.2.0' +} as const; + +// Guards the two from drifting: every version the harness installs has to be +// allowed by the range the package publishes. +for (const [name, tested] of Object.entries(testedPeerVersions)) { + const declared = manifest.peerDependencies[name]; + if (!declared || tested.startsWith('^')) continue; + + assert(Bun.semver.satisfies(tested, declared), `Harness installs ${name}@${tested}, which the published ${name}@${declared} peer range excludes`); +} + +// The published manifest carries a range so consumers inherit upstream fixes; +// exactness belongs to the lockfile, not the contract. +const assertSatisfiesDynamicRange = (version: string | undefined, label: string) => { + assert(version, `${label} did not resolve @dynamic-labs/global-wallet-client`); + assert(Bun.semver.satisfies(version, dynamicRange), `${label} resolved @dynamic-labs/global-wallet-client@${version}, outside the declared ${dynamicRange}`); + return version; +}; + +const reportWaiverProgress = (message: string) => { + console.warn(`[waiver] ${message}`); +}; + +const run = async (command: string[], cwd: string, allowFailure = false): Promise => { + const child = Bun.spawn(command, { + cwd, + env: process.env, + stderr: 'pipe', + stdout: 'pipe' + }); + const [stdout, stderr, exitCode] = await Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited]); + + if (!allowFailure && exitCode !== 0) { + throw new Error(`${command.join(' ')} failed in ${cwd}\n${stderr}${stdout}`); + } + + return { exitCode, stderr, stdout }; +}; + +const parseJsonOutput = (output: string): T => { + const plain = output; + const objectStart = plain.indexOf('{'); + const arrayStart = plain.indexOf('['); + const start = objectStart === -1 ? arrayStart : arrayStart === -1 ? objectStart : Math.min(objectStart, arrayStart); + const end = Math.max(plain.lastIndexOf('}'), plain.lastIndexOf(']')); + assert(start >= 0 && end >= start, `Expected JSON output, received:\n${plain}`); + return JSON.parse(plain.slice(start, end + 1)) as T; +}; + +const writeJson = (path: string, value: unknown) => writeFile(path, `${JSON.stringify(value, null, 2)}\n`); + +const baseSafeOverrides = { + axios: '1.18.0', + uuid: '11.1.1' +}; +const npmRuntimeOverrides = { + ...baseSafeOverrides, + viem: { + ws: '8.21.0' + } +}; +const npmSafeOverrides = { + ...npmRuntimeOverrides, + 'ethjs-unit': { + 'bn.js': '4.12.5' + }, + 'number-to-bn': { + 'bn.js': '4.12.5' + } +}; + +const walletOnlyManifest = (tarball: string, overrides?: Record) => ({ + name: 'sei-global-wallet-audit-consumer', + private: true, + type: 'module', + dependencies: { + '@sei-js/sei-global-wallet': `file:${tarball}` + }, + ...(overrides ? { overrides } : {}) +}); + +const fullConsumerManifest = (tarball: string, packageManager: 'bun' | 'npm') => ({ + name: 'sei-global-wallet-full-consumer', + private: true, + type: 'module', + dependencies: { + ...testedPeerVersions, + '@sei-js/sei-global-wallet': `file:${tarball}`, + 'ethjs-unit': '0.1.6', + 'number-to-bn': '1.7.0' + }, + devDependencies: { + esbuild: '0.28.2', + typescript: '5.9.3', + vite: '8.2.2' + }, + overrides: packageManager === 'npm' ? npmSafeOverrides : baseSafeOverrides +}); + +/** + * Typechecks the entrypoints that need no optional peer, with `skipLibCheck` + * off, inside a consumer where no optional peer is installed. The full consumer + * installs every peer and skips lib checks, so it cannot catch an emitted + * declaration that leaks a type from a peer the consumer never installed. + */ +const assertDeclarationsResolveWithoutOptionalPeers = async (consumerDir: string) => { + await writeFile( + join(consumerDir, 'strict-types.ts'), + `import Wallet, { connect } from '@sei-js/sei-global-wallet'; +import { eip6963ProviderInfo, registerEIP6963Provider, unregisterEIP6963Provider } from '@sei-js/sei-global-wallet/eip6963'; +import { createEIP1193Provider } from '@sei-js/sei-global-wallet/ethereum'; + +void [Wallet, connect, eip6963ProviderInfo, registerEIP6963Provider, unregisterEIP6963Provider, createEIP1193Provider]; +` + ); + await writeJson(join(consumerDir, 'tsconfig.strict.json'), { + compilerOptions: { + lib: ['ES2022', 'DOM'], + module: 'ESNext', + moduleResolution: 'Bundler', + noEmit: true, + skipLibCheck: false, + strict: true, + target: 'ES2022' + }, + files: ['strict-types.ts'] + }); + + // Uses the workspace compiler so the minimal consumer stays free of a + // TypeScript install, which would otherwise pollute its audit surface. + const strictTypecheck = await run([join(root, 'node_modules/.bin/tsc'), '--project', 'tsconfig.strict.json'], consumerDir, true); + const output = `${strictTypecheck.stdout}${strictTypecheck.stderr}`; + // Upstream declarations are outside this package's control, so only fail on + // diagnostics raised against what this package publishes. + const ownDiagnostics = output + .split('\n') + .filter((line) => /@sei-js\/sei-global-wallet\/dist\/.*error TS/.test(line)) + .sort(); + assert.deepEqual( + ownDiagnostics, + [], + `Published declarations do not resolve without optional peers installed:\n${ownDiagnostics.join('\n')}\n\nFull output:\n${output}` + ); +}; + +const setupConsumerFiles = async (consumerDir: string) => { + await writeFile( + join(consumerDir, 'check-ssr.mjs'), + `import assert from 'node:assert/strict'; +import { createHash } from 'node:crypto'; +const [root, eip6963, ethereum, solana, zerodev] = await Promise.all([ + import('@sei-js/sei-global-wallet'), + import('@sei-js/sei-global-wallet/eip6963'), + import('@sei-js/sei-global-wallet/ethereum'), + import('@sei-js/sei-global-wallet/solana'), + import('@sei-js/sei-global-wallet/zerodev') +]); +assert.equal(root.default, root.Wallet); +assert.equal(typeof root.connect, 'function'); +assert.equal(typeof eip6963.registerEIP6963Provider, 'function'); +assert.equal(typeof ethereum.createEIP1193Provider, 'function'); +assert.equal(typeof solana.createSolanaWallet, 'function'); +assert.equal(typeof solana.registerSolanaStandard, 'function'); +assert.equal(typeof zerodev.createKernelClient, 'function'); +const iconBytes = Buffer.from(eip6963.eip6963ProviderInfo.icon.split(',')[1], 'base64'); +assert.equal(iconBytes.at(-1), 10); +assert.equal(createHash('sha256').update(iconBytes).digest('hex'), 'e288cd08b510afbc19f1ea85c990397de2cad2077459a6833d64e26f86b761fa'); +console.log('SSR entrypoints OK'); +` + ); + await writeFile( + join(consumerDir, 'check-edge-native.mjs'), + `import assert from 'node:assert/strict'; + +Reflect.deleteProperty(globalThis, 'global'); +Reflect.deleteProperty(globalThis, 'process'); +const [root, eip6963, ethereum, solana, zerodev] = await Promise.all([ + import('@sei-js/sei-global-wallet'), + import('@sei-js/sei-global-wallet/eip6963'), + import('@sei-js/sei-global-wallet/ethereum'), + import('@sei-js/sei-global-wallet/solana'), + import('@sei-js/sei-global-wallet/zerodev') +]); +assert.equal(globalThis.global, globalThis); +assert.equal(globalThis.process?.browser, true); +assert.equal(root.default, root.Wallet); +assert.equal(typeof eip6963.registerEIP6963Provider, 'function'); +assert.equal(typeof ethereum.createEIP1193Provider, 'function'); +assert.equal(typeof solana.createSolanaWallet, 'function'); +assert.equal(typeof zerodev.createKernelClient, 'function'); +` + ); + await writeFile( + join(consumerDir, 'check-local-aa.mjs'), + `import assert from 'node:assert/strict'; +import ethjsUnit from 'ethjs-unit'; +import numberToBN from 'number-to-bn'; +import { getEcdsaValidator, PaymasterTypeEnum } from '@dynamic-labs/ethereum-aa'; +import { Connection, PublicKey } from '@solana/web3.js'; +import { KernelEIP1193Provider } from '@zerodev/sdk/providers'; + +const converted = numberToBN('0x2a'); +assert.equal(converted.constructor.wordSize, 26); +assert.equal(converted.toString(10), '42'); +assert.equal(ethjsUnit.toWei('1', 'ether').toString(10), '1000000000000000000'); +assert.equal(PaymasterTypeEnum.NONE, 'NONE'); +assert.equal(typeof getEcdsaValidator(), 'function'); +const publicKey = new PublicKey('11111111111111111111111111111111'); +assert.equal(publicKey.toBase58(), '11111111111111111111111111111111'); +const connection = new Connection('http://127.0.0.1:8899', 'confirmed'); +assert.equal(connection.rpcEndpoint, 'http://127.0.0.1:8899'); + +const address = '0x0000000000000000000000000000000000000042'; +const kernelClient = { + account: { address }, + transport: { + request() { + throw new Error('network transport must not be called'); + } + } +}; +const provider = new KernelEIP1193Provider(kernelClient); +assert.deepEqual(await provider.request({ method: 'eth_accounts' }), [address]); +let emitted = false; +provider.on('accountsChanged', () => { + emitted = true; +}); +provider.emit('accountsChanged', [address]); +assert.equal(emitted, true); +` + ); + await writeFile( + join(consumerDir, 'browser-setup.mjs'), + `globalThis.__eipAnnouncements = []; +globalThis.__solanaWallets = []; +window.addEventListener('eip6963:announceProvider', (event) => { + globalThis.__eipAnnouncements.push(event.detail); +}); +window.addEventListener('wallet-standard:register-wallet', (event) => { + event.detail({ + register(wallet) { + globalThis.__solanaWallets.push(wallet); + } + }); +}); +` + ); + await writeFile( + join(consumerDir, 'browser-entry.mjs'), + `import './browser-setup.mjs'; +import Wallet, { connect } from '@sei-js/sei-global-wallet'; +import { eip6963ProviderInfo } from '@sei-js/sei-global-wallet/eip6963'; +import { createEIP1193Provider } from '@sei-js/sei-global-wallet/ethereum'; +import { createSolanaWallet } from '@sei-js/sei-global-wallet/solana'; + +window.dispatchEvent(new Event('eip6963:requestProvider')); +window.dispatchEvent(new Event('eip6963:requestProvider')); +globalThis.__walletCheck = { + announcements: globalThis.__eipAnnouncements.length, + globalReady: global === globalThis, + icon: eip6963ProviderInfo.icon, + name: eip6963ProviderInfo.name, + root: typeof Wallet === 'object' && typeof connect === 'function', + solanaRegistrations: globalThis.__solanaWallets.length, + subpaths: + typeof createEIP1193Provider === 'function' && + typeof createSolanaWallet === 'function' +}; +` + ); + await writeFile( + join(consumerDir, 'browser-esbuild-entry.mjs'), + `import './browser-entry.mjs'; +import { createKernelClient } from '@sei-js/sei-global-wallet/zerodev'; +import { KernelEIP1193Provider } from '@zerodev/sdk/providers'; + +const address = '0x0000000000000000000000000000000000000042'; +const provider = new KernelEIP1193Provider({ + account: { address }, + transport: { + request() { + throw new Error('browser check must not use the network'); + } + } +}); +const publicExportOperation = createKernelClient({ wallet: {} }).then( + () => false, + (error) => error instanceof Error && error.message === 'Client not present' +); +const providerOperation = provider + .request({ method: 'eth_accounts' }) + .then((accounts) => accounts.length === 1 && accounts[0] === address); +globalThis.__walletCheck.zerodev = Promise.all([publicExportOperation, providerOperation]).then((checks) => checks.every(Boolean)); +` + ); + await writeFile( + join(consumerDir, 'edge-entry.mjs'), + `import Wallet, { connect } from '@sei-js/sei-global-wallet'; +import { registerEIP6963Provider } from '@sei-js/sei-global-wallet/eip6963'; +import { createEIP1193Provider } from '@sei-js/sei-global-wallet/ethereum'; +import { createSolanaWallet } from '@sei-js/sei-global-wallet/solana'; +import { createKernelClient } from '@sei-js/sei-global-wallet/zerodev'; + +globalThis.__edgeCheck = { + aliases: global === globalThis && process.browser === true, + entrypoints: + typeof Wallet === 'object' && + typeof connect === 'function' && + typeof registerEIP6963Provider === 'function' && + typeof createEIP1193Provider === 'function' && + typeof createSolanaWallet === 'function' && + typeof createKernelClient === 'function' +}; +` + ); + await writeFile( + join(consumerDir, 'typecheck.ts'), + `import Wallet, { connect } from '@sei-js/sei-global-wallet'; +import { + type EIP6963ProviderDetail, + eip6963ProviderInfo, + registerEIP6963Provider +} from '@sei-js/sei-global-wallet/eip6963'; +import { createEIP1193Provider } from '@sei-js/sei-global-wallet/ethereum'; +import { createSolanaWallet, registerSolanaStandard } from '@sei-js/sei-global-wallet/solana'; +import { createKernelClient } from '@sei-js/sei-global-wallet/zerodev'; + +const detail: EIP6963ProviderDetail | undefined = undefined; +void [Wallet, connect, detail, eip6963ProviderInfo, registerEIP6963Provider, createEIP1193Provider, createSolanaWallet, registerSolanaStandard, createKernelClient]; +` + ); + await writeJson(join(consumerDir, 'tsconfig.json'), { + compilerOptions: { + lib: ['ES2022', 'DOM'], + module: 'ESNext', + moduleResolution: 'Bundler', + noEmit: true, + skipLibCheck: true, + strict: true, + target: 'ES2022' + }, + files: ['typecheck.ts'] + }); + await writeFile( + join(consumerDir, 'vite.config.mjs'), + `import { defineConfig } from 'vite'; +import { fileURLToPath } from 'node:url'; + +export default defineConfig({ + build: { + emptyOutDir: true, + lib: { + entry: fileURLToPath(new URL('./browser-esbuild-entry.mjs', import.meta.url)), + fileName: 'wallet', + formats: ['iife'], + name: 'SeiWalletConsumerCheck' + }, + minify: false, + outDir: 'vite-dist' + } +}); +` + ); +}; + +const makeStorage = (): Storage => { + const values = new Map(); + return { + get length() { + return values.size; + }, + clear: () => values.clear(), + getItem: (key) => values.get(key) ?? null, + key: (index) => [...values.keys()][index] ?? null, + removeItem: (key) => values.delete(key), + setItem: (key, value) => values.set(key, String(value)) + }; +}; + +const runBrowserBundle = async (bundlePath: string, expectZeroDev: boolean) => { + const eventTarget = new EventTarget(); + const storage = makeStorage(); + const context: Record = { + AbortController, + Blob, + CustomEvent, + Event, + EventTarget, + MessageChannel, + TextDecoder, + TextEncoder, + URL, + URLSearchParams, + addEventListener: eventTarget.addEventListener.bind(eventTarget), + atob, + btoa, + clearInterval, + clearTimeout, + console: { + ...console, + debug: () => {}, + error: () => {} + }, + crypto: webcrypto, + dispatchEvent: eventTarget.dispatchEvent.bind(eventTarget), + fetch, + localStorage: storage, + navigator: {}, + performance, + queueMicrotask, + removeEventListener: eventTarget.removeEventListener.bind(eventTarget), + sessionStorage: makeStorage(), + setInterval, + setTimeout, + structuredClone + }; + context.globalThis = context; + context.self = context; + context.window = context; + + const bundle = await readFile(bundlePath, 'utf8'); + assert(!bundle.includes('node:worker_threads'), `${bundlePath} contains a node:worker_threads browser runtime path`); + assert(!bundle.includes('"worker_threads"'), `${bundlePath} contains a worker_threads browser runtime path`); + try { + vm.runInNewContext(bundle, context, { + filename: bundlePath, + timeout: 10_000 + }); + } catch (error) { + const message = + typeof error === 'object' && error && 'stack' in error && typeof error.stack === 'string' + ? error.stack + : error instanceof Error + ? error.message + : String(error); + const line = Number(message.match(/:(\d+):\d+/)?.[1]); + const lines = bundle.split('\n'); + const nearbySource = Number.isFinite(line) ? lines.slice(Math.max(0, line - 4), line + 3).join('\n') : ''; + const shimLine = lines.findIndex((sourceLine) => sourceLine.includes('Dynamic 4.x reads')); + const shimSource = shimLine >= 0 ? lines.slice(shimLine, shimLine + 28).join('\n') : 'browser shim not found'; + throw new Error(`${message}\n${nearbySource}\n--- browser shim (line ${shimLine + 1}) ---\n${shimSource}`); + } + + const result = context.__walletCheck as { + announcements: number; + globalReady: boolean; + icon: string; + name: string; + root: boolean; + solanaRegistrations: number; + subpaths: boolean; + zerodev?: boolean | Promise; + }; + assert(result, `Browser bundle did not produce a result: ${bundlePath}`); + assert.equal(result.globalReady, true); + assert.equal(result.root, true); + assert.equal(result.subpaths, true); + if (expectZeroDev) assert.equal(await result.zerodev, true); + assert.equal(result.announcements, 3); + assert.equal(result.solanaRegistrations, 1); + assert.equal(result.name, 'Sei Global Wallet'); + assert.match(result.icon, /^data:image\/svg\+xml;base64,/); +}; + +const runEdgeBundle = async (bundlePath: string) => { + const context: Record = { + AbortController, + Blob, + CustomEvent, + Event, + EventTarget, + MessageChannel, + TextDecoder, + TextEncoder, + URL, + URLSearchParams, + atob, + btoa, + clearInterval, + clearTimeout, + console, + crypto: webcrypto, + fetch, + performance, + queueMicrotask, + setInterval, + setTimeout, + structuredClone + }; + context.globalThis = context; + context.self = context; + + const bundle = await readFile(bundlePath, 'utf8'); + assert(!bundle.includes('node:worker_threads'), `${bundlePath} contains a node:worker_threads edge runtime path`); + assert(!bundle.includes('"worker_threads"'), `${bundlePath} contains a worker_threads edge runtime path`); + vm.runInNewContext(bundle, context, { + filename: bundlePath, + timeout: 10_000 + }); + assert.deepEqual(context.__edgeCheck, { + aliases: true, + entrypoints: true + }); +}; + +const assertAuditClean = (report: AuditReport, label: string) => { + assert.equal(report.metadata?.vulnerabilities?.total ?? Object.keys(report.vulnerabilities ?? {}).length, 0, `${label} audit was not clean`); +}; + +const assertAcceptedBunAudit = (result: ProcessResult) => { + if (result.exitCode === 0) { + reportWaiverProgress( + 'the Bun AA consumer now audits clean: every accepted advisory was fixed upstream. Delete the waiver from packages/sei-global-wallet/README.md and acceptedBunAdvisories in this script.' + ); + return; + } + + const report = parseJsonOutput>>(result.stdout); + const serialized = JSON.stringify(report); + const reported = new Set(serialized.match(/GHSA-[a-z0-9-]+/gi) ?? []); + + // A subset check, not an exact set: the advisory database changes on its own + // schedule, so a withdrawn or upstream-fixed advisory must not fail an + // unrelated pull request, while any new exposure still must. + const unwaived = [...reported].filter((advisory) => !acceptedBunAdvisories.includes(advisory)).sort(); + assert.deepEqual( + unwaived, + [], + `Bun AA consumer reported advisories outside the accepted waiver: ${unwaived.join(', ')}. Assess them and update packages/sei-global-wallet/README.md before releasing.` + ); + + const fixed = acceptedBunAdvisories.filter((advisory) => !reported.has(advisory)); + if (fixed.length > 0) { + reportWaiverProgress( + `Bun no longer reports ${fixed.join(', ')}. Narrow the waiver in packages/sei-global-wallet/README.md and acceptedBunAdvisories in this script.` + ); + } + + // The documented Axios and UUID overrides must still be taking effect. + assert.doesNotMatch(serialized, /axios|uuid/i); + console.log( + `Bun AA consumer advisories, all within the waiver: ${Object.entries(report) + .map(([name, findings]) => `${name} (${findings.map(({ severity }) => severity).join(', ')})`) + .join('; ')}` + ); +}; + +const assertNpmDynamicGraph = (lock: PackageLock) => { + const resolved = assertSatisfiesDynamicRange(lock.packages['node_modules/@dynamic-labs/global-wallet-client']?.version, 'npm consumer'); + assert.equal(lock.packages['node_modules/@dynamic-labs/ethereum-aa']?.version, testedPeerVersions['@dynamic-labs/ethereum-aa']); + + const conflicts = findDynamicLineConflicts(lock.packages, resolved); + assert.deepEqual(conflicts, [], `npm consumer kept a stale Dynamic ${resolved} subtree: ${formatDynamicLineConflicts(conflicts)}`); + + return resolved; +}; + +const assertBunDynamicGraph = (lockText: string) => { + // Bun records every resolution as a "@" specifier rather than + // as install locations, so group the specifiers instead of lock paths. + const versions = new Map>(); + for (const [, name, version] of lockText.matchAll(/"(@dynamic-labs\/[^"@/]+)@([^"]+)"/g)) { + versions.set(name, (versions.get(name) ?? new Set()).add(version)); + } + + const walletClient = [...(versions.get('@dynamic-labs/global-wallet-client') ?? [])]; + assert.equal(walletClient.length, 1, `Bun consumer resolved multiple Dynamic clients: ${walletClient.join(', ')}`); + const resolved = assertSatisfiesDynamicRange(walletClient[0], 'Bun consumer'); + assert( + [...(versions.get('@dynamic-labs/ethereum-aa') ?? [])].includes(testedPeerVersions['@dynamic-labs/ethereum-aa']), + `Bun consumer did not resolve @dynamic-labs/ethereum-aa@${testedPeerVersions['@dynamic-labs/ethereum-aa']}` + ); + + // Same major-line invariant as the npm graph: versions outside the client's + // major belong to unrelated dependents, such as zksync-sso's Dynamic 5.x. + const line = `${resolved.split('.')[0]}.`; + const conflicts = [...versions] + .map(([name, resolved]) => [name, [...resolved].filter((version) => version.startsWith(line)).sort()] as const) + .filter(([, inLine]) => inLine.length > 1) + .map(([name, inLine]) => `${name}@{${inLine.join(', ')}}`) + .sort(); + assert.deepEqual(conflicts, [], `Bun consumer kept a stale Dynamic ${resolved} subtree: ${conflicts.join(', ')}`); +}; + +const assertBrowserMetafile = async (metafilePath: string, lock: PackageLock) => { + const metafile = JSON.parse(await readFile(metafilePath, 'utf8')) as { + inputs: Record; + outputs: Record }>; + }; + const inputs = Object.keys(metafile.inputs); + assert(inputs.some((path) => path.includes('node_modules/@dynamic-labs/global-wallet-client/'))); + const clientVersion = assertSatisfiesDynamicRange(lock.packages['node_modules/@dynamic-labs/global-wallet-client']?.version, 'Browser bundle lockfile'); + const lineVersions = collectDynamicLineVersions(lock.packages, clientVersion); + const resolvedDynamicInputs = new Map(); + for (const path of inputs) { + const normalizedPath = path.replaceAll('\\', '/'); + const marker = 'node_modules/@dynamic-labs/'; + const packageMarker = normalizedPath.lastIndexOf(marker); + if (packageMarker === -1) continue; + + const packageStart = packageMarker + 'node_modules/'.length; + const packageName = normalizedPath.slice(packageStart).split('/').slice(0, 2).join('/'); + const lockStart = normalizedPath.indexOf('node_modules/'); + const lockLocation = normalizedPath.slice(lockStart, packageStart + packageName.length); + const version = lock.packages[lockLocation]?.version; + assert(version, `Metafile input did not resolve to its nearest npm lock installation: ${path} -> ${lockLocation}`); + const inLine = [...(lineVersions.get(packageName) ?? [])]; + // The lock is already asserted conflict-free within the client's major + // line, so a bundled input from that line must be its single version. + if (inLine.length > 0) { + assert.deepEqual(inLine, [version], `Bundle pulled a stale Dynamic runtime: ${path} -> ${lockLocation}@${version}, expected ${inLine.join(', ')}`); + } + resolvedDynamicInputs.set(packageName, version); + } + assert.equal( + resolvedDynamicInputs.get('@dynamic-labs/global-wallet-client'), + clientVersion, + 'Browser bundle did not load the Dynamic client the lockfile resolved' + ); + const runtimeImports = Object.values(metafile.outputs).flatMap((output) => output.imports ?? []); + assert(!runtimeImports.some(({ path }) => path === 'node:worker_threads' || path === 'worker_threads')); +}; + +try { + await run(['bun', 'run', '--cwd', packageDir, 'build'], root); + const pack = await run(['npm', 'pack', '--json', '--pack-destination', temporaryRoot], packageDir); + const [packResult] = parseJsonOutput(pack.stdout); + assert(packResult, 'npm pack returned no package'); + const packedPaths = packResult.files.map(({ path }) => path); + for (const entrypoint of ['index', 'eip6963', 'ethereum', 'solana', 'zerodev']) { + assert(packedPaths.includes(`dist/${entrypoint}.js`), `Missing dist/${entrypoint}.js`); + assert(packedPaths.includes(`dist/${entrypoint}.d.ts`), `Missing dist/${entrypoint}.d.ts`); + } + assert(packedPaths.every((path) => path.startsWith('dist/') || ['LICENSE', 'README.md', 'package.json'].includes(path))); + assert(!packedPaths.some((path) => path.includes('__tests__') || path.startsWith('src/'))); + const tarball = join(temporaryRoot, packResult.filename); + + if (!fastCheck) { + const unwaivedDir = join(temporaryRoot, 'npm-unwaived'); + await mkdir(unwaivedDir); + await writeJson(join(unwaivedDir, 'package.json'), walletOnlyManifest(tarball)); + await run(['npm', 'install', '--no-audit', '--no-fund'], unwaivedDir); + const unwaivedLock = JSON.parse(await readFile(join(unwaivedDir, 'package-lock.json'), 'utf8')) as { + packages: Record; + }; + const lockedVersions = (name: string) => + Object.entries(unwaivedLock.packages) + .filter(([location]) => location === `node_modules/${name}` || location.endsWith(`/node_modules/${name}`)) + .map(([, metadata]) => metadata.version); + const unwaivedAuditResult = await run(['npm', 'audit', '--json'], unwaivedDir, true); + const unwaivedAudit = parseJsonOutput(unwaivedAuditResult.stdout); + // Reported, not asserted: an unwaived consumer going clean means Dynamic + // corrected its transitive pins, which must not read as a CI failure. + const stillVulnerable = ['axios', 'uuid'].filter((name) => unwaivedAudit.vulnerabilities?.[name]); + if (stillVulnerable.length === 0) { + reportWaiverProgress( + 'an npm consumer without overrides now audits clean: Dynamic corrected its transitive axios and uuid pins. Remove the override guidance from packages/sei-global-wallet/README.md and the changeset.' + ); + } else { + console.log(`Overrides still required for ${stillVulnerable.map((name) => `${name}@${[...new Set(lockedVersions(name))].join('/')}`).join(', ')}`); + } + + const waivedDir = join(temporaryRoot, 'npm-waived'); + await mkdir(waivedDir); + await writeJson(join(waivedDir, 'package.json'), walletOnlyManifest(tarball, npmRuntimeOverrides)); + await run(['npm', 'install', '--no-audit', '--no-fund'], waivedDir); + await run( + [ + 'node', + '--input-type=module', + '--eval', + "await Promise.all([import('@sei-js/sei-global-wallet'), import('@sei-js/sei-global-wallet/eip6963'), import('@sei-js/sei-global-wallet/ethereum')]);" + ], + waivedDir + ); + const waivedAudit = parseJsonOutput((await run(['npm', 'audit', '--json'], waivedDir)).stdout); + assertAuditClean(waivedAudit, 'Waived npm consumer'); + await assertDeclarationsResolveWithoutOptionalPeers(waivedDir); + } + + const npmConsumerDir = join(temporaryRoot, 'npm-full'); + await mkdir(npmConsumerDir); + await writeJson(join(npmConsumerDir, 'package.json'), fullConsumerManifest(tarball, 'npm')); + await setupConsumerFiles(npmConsumerDir); + await run(['npm', 'install', '--no-audit', '--no-fund'], npmConsumerDir); + const npmLock = JSON.parse(await readFile(join(npmConsumerDir, 'package-lock.json'), 'utf8')) as PackageLock; + assertNpmDynamicGraph(npmLock); + assert.equal(npmLock.packages['node_modules/ethjs-unit/node_modules/bn.js']?.version, '4.12.5'); + assert.equal(npmLock.packages['node_modules/number-to-bn/node_modules/bn.js']?.version, '4.12.5'); + assert.equal(npmLock.packages['node_modules/bn.js']?.version, '5.2.5'); + assert.equal(npmLock.packages['node_modules/ws']?.version, '8.21.0'); + assert.equal(npmLock.packages['node_modules/viem']?.dependencies?.ws, '8.18.3'); + assert.equal(npmLock.packages['node_modules/jayson']?.dependencies?.ws, '^7.5.10'); + assert.equal(npmLock.packages['node_modules/jayson/node_modules/ws']?.version, '7.5.13'); + await run(['node', 'check-ssr.mjs'], npmConsumerDir); + await run(['node', 'check-edge-native.mjs'], npmConsumerDir); + await run(['node', 'check-local-aa.mjs'], npmConsumerDir); + await run(['npx', '--no-install', 'tsc', '--project', 'tsconfig.json'], npmConsumerDir); + await run( + [ + 'npx', + '--no-install', + 'esbuild', + 'browser-esbuild-entry.mjs', + '--bundle', + '--format=iife', + '--platform=browser', + '--outfile=browser-esbuild.js', + '--metafile=browser-meta.json' + ], + npmConsumerDir + ); + await assertBrowserMetafile(join(npmConsumerDir, 'browser-meta.json'), npmLock); + await runBrowserBundle(join(npmConsumerDir, 'browser-esbuild.js'), true); + await run( + [ + 'npx', + '--no-install', + 'esbuild', + 'edge-entry.mjs', + '--bundle', + '--format=iife', + '--platform=browser', + '--outfile=edge-esbuild.js', + '--metafile=edge-meta.json' + ], + npmConsumerDir + ); + await assertBrowserMetafile(join(npmConsumerDir, 'edge-meta.json'), npmLock); + await runEdgeBundle(join(npmConsumerDir, 'edge-esbuild.js')); + const viteBuild = await run(['npx', '--no-install', 'vite', 'build'], npmConsumerDir); + // Vite routes externalization warnings through its own logger, which writes + // to stdout in most versions, so scan both streams. + const viteOutput = `${viteBuild.stdout}\n${viteBuild.stderr}`; + assert.doesNotMatch( + viteOutput, + /(?:node:)?worker_threads.*externaliz|externaliz.*(?:node:)?worker_threads/i, + `Vite externalized worker_threads:\n${viteOutput}` + ); + const viteFiles = (await readdir(join(npmConsumerDir, 'vite-dist'))).filter((path) => path.endsWith('.js')); + assert.equal(viteFiles.length, 1, `Expected one Vite bundle, found: ${viteFiles.join(', ')}`); + await runBrowserBundle(join(npmConsumerDir, 'vite-dist', viteFiles[0]), true); + assertAuditClean(parseJsonOutput((await run(['npm', 'audit', '--json'], npmConsumerDir)).stdout), 'Full npm consumer'); + + if (!fastCheck) { + const bunConsumerDir = join(temporaryRoot, 'bun-full'); + await mkdir(bunConsumerDir); + await writeJson(join(bunConsumerDir, 'package.json'), fullConsumerManifest(tarball, 'bun')); + await setupConsumerFiles(bunConsumerDir); + await run(['bun', 'install'], bunConsumerDir); + const bunLock = await readFile(join(bunConsumerDir, 'bun.lock'), 'utf8'); + assertBunDynamicGraph(bunLock); + assert.match(bunLock, /"bn\.js": \["bn\.js@5\.2\.5"/); + assert.match(bunLock, /"ethjs-unit\/bn\.js": \["bn\.js@4\.11\.6"/); + assert.match(bunLock, /"number-to-bn\/bn\.js": \["bn\.js@4\.11\.6"/); + assert.match(bunLock, /"jayson\/ws": \["ws@7\.5\.13"/); + assert.match(bunLock, /"ws": \["ws@8\.18\.3"/); + await run(['bun', 'check-ssr.mjs'], bunConsumerDir); + await run(['bun', 'check-local-aa.mjs'], bunConsumerDir); + assertAcceptedBunAudit(await run(['bun', 'audit', '--json'], bunConsumerDir, true)); + } + + console.log( + fastCheck + ? 'Sei Global Wallet fast npm consumer checks passed.' + : `Sei Global Wallet consumer checks passed: npm scoped patched bn.js/ws8 while preserving Solana bn5/Jayson ws7 with a clean audit; Bun preserved compatible majors and accepted exactly ${acceptedBunAdvisories.join(', ')}.` + ); +} finally { + await rm(temporaryRoot, { force: true, recursive: true }); +} diff --git a/scripts/dynamic-package-lock.test.ts b/scripts/dynamic-package-lock.test.ts new file mode 100644 index 00000000..72842b63 --- /dev/null +++ b/scripts/dynamic-package-lock.test.ts @@ -0,0 +1,75 @@ +import { describe, expect, test } from 'bun:test'; +import { collectDynamicLineVersions, dynamicLockPackageName, findDynamicLineConflicts, formatDynamicLineConflicts } from './dynamic-package-lock.js'; + +describe('dynamic package lock locations', () => { + test('names hoisted and nested Dynamic packages, and nothing else', () => { + expect(dynamicLockPackageName('node_modules/@dynamic-labs/logger')).toBe('@dynamic-labs/logger'); + expect(dynamicLockPackageName('node_modules/@dynamic-labs/global-wallet-client')).toBe('@dynamic-labs/global-wallet-client'); + expect(dynamicLockPackageName('node_modules/foo/node_modules/@dynamic-labs/logger')).toBe('@dynamic-labs/logger'); + expect(dynamicLockPackageName('node_modules/a/node_modules/b/node_modules/@dynamic-labs/store')).toBe('@dynamic-labs/store'); + expect(dynamicLockPackageName('node_modules/@dynamic-labs')).toBeUndefined(); + expect(dynamicLockPackageName('node_modules/axios')).toBeUndefined(); + }); + + test('collects only the resolutions inside the requested major line', () => { + const versions = collectDynamicLineVersions( + { + 'node_modules/@dynamic-labs/logger': { version: '4.96.3' }, + 'node_modules/viem/node_modules/@dynamic-labs/logger': { version: '4.96.1' }, + // zksync-sso brings its own Dynamic 5.x line, which is not this runtime. + 'node_modules/zksync-sso/node_modules/@dynamic-labs/logger': { version: '5.3.2' }, + 'node_modules/@dynamic-labs/sdk-api-core': { version: '0.12.0' }, + 'node_modules/axios': { version: '1.18.0' } + }, + '4.96.3' + ); + + expect([...(versions.get('@dynamic-labs/logger') ?? [])].sort()).toEqual(['4.96.1', '4.96.3']); + expect(versions.has('@dynamic-labs/sdk-api-core')).toBe(false); + expect(versions.has('axios')).toBe(false); + }); + + test('reports a stale nested copy inside the line', () => { + const conflicts = findDynamicLineConflicts( + { + 'node_modules/@dynamic-labs/logger': { version: '4.96.3' }, + 'node_modules/viem/node_modules/@dynamic-labs/logger': { version: '4.96.1' }, + 'node_modules/@dynamic-labs/message-transport': { version: '4.96.3' }, + 'node_modules/foo/node_modules/@dynamic-labs/message-transport': { version: '4.96.1' }, + 'node_modules/@dynamic-labs/global-wallet-client': { version: '4.96.3' } + }, + '4.96.3' + ); + + expect(conflicts.map(([name]) => name)).toEqual(['@dynamic-labs/logger', '@dynamic-labs/message-transport']); + expect(formatDynamicLineConflicts(conflicts)).toBe('@dynamic-labs/logger@{4.96.1, 4.96.3}, @dynamic-labs/message-transport@{4.96.1, 4.96.3}'); + }); + + test('accepts unrelated major lines and independently versioned Dynamic packages', () => { + expect( + findDynamicLineConflicts( + { + 'node_modules/@dynamic-labs/global-wallet-client': { version: '4.96.3' }, + 'node_modules/@dynamic-labs/logger': { version: '4.96.3' }, + 'node_modules/zksync-sso/node_modules/@dynamic-labs/logger': { version: '5.3.2' }, + 'node_modules/@dynamic-labs/sdk-api-core': { version: '0.0.1093' }, + 'node_modules/foo/node_modules/@dynamic-labs/sdk-api-core': { version: '0.12.0' } + }, + '4.96.3' + ) + ).toEqual([]); + }); + + test('stays meaningful after a Dynamic major upgrade', () => { + const packages = { + 'node_modules/@dynamic-labs/global-wallet-client': { version: '5.4.0' }, + 'node_modules/@dynamic-labs/logger': { version: '5.4.0' }, + 'node_modules/foo/node_modules/@dynamic-labs/logger': { version: '5.3.2' } + }; + + expect(findDynamicLineConflicts(packages, '5.4.0').map(([name]) => name)).toEqual(['@dynamic-labs/logger']); + // The same graph read against the old line reports nothing, because 5.x is + // no longer the runtime being checked. + expect(findDynamicLineConflicts(packages, '4.96.3')).toEqual([]); + }); +}); diff --git a/scripts/dynamic-package-lock.ts b/scripts/dynamic-package-lock.ts new file mode 100644 index 00000000..4b9df812 --- /dev/null +++ b/scripts/dynamic-package-lock.ts @@ -0,0 +1,41 @@ +const DYNAMIC_PACKAGE_LOCATION = /(?:^|\/)node_modules\/(@dynamic-labs\/[^/]+)$/; + +/** Returns the `@dynamic-labs/*` package a lock location installs, at any nesting depth. */ +export const dynamicLockPackageName = (location: string) => location.match(DYNAMIC_PACKAGE_LOCATION)?.[1]; + +const majorOf = (version: string) => version.split('.')[0]; + +/** + * Maps every `@dynamic-labs/*` package that resolved within one major line to + * the versions it resolved to. Other majors are excluded because they belong to + * unrelated dependents — `zksync-sso` ships its own Dynamic 5.x line — and are + * not the runtime this package loads. + */ +export const collectDynamicLineVersions = (packages: Record, lineVersion: string) => { + const line = majorOf(lineVersion); + const versions = new Map>(); + + for (const [location, metadata] of Object.entries(packages)) { + const name = dynamicLockPackageName(location); + if (!name || !metadata.version || majorOf(metadata.version) !== line) continue; + + versions.set(name, (versions.get(name) ?? new Set()).add(metadata.version)); + } + + return versions; +}; + +/** + * Dynamic pins its internal packages to the client's exact version, so any name + * resolving to two versions inside that line means a stale nested subtree + * survived. Checking for disagreement rather than for a known-bad version keeps + * this meaningful across Dynamic upgrades. + */ +export const findDynamicLineConflicts = (packages: Record, lineVersion: string) => + [...collectDynamicLineVersions(packages, lineVersion)] + .filter(([, versions]) => versions.size > 1) + .map(([name, versions]) => [name, [...versions].sort()] as const) + .sort(([left], [right]) => left.localeCompare(right)); + +export const formatDynamicLineConflicts = (conflicts: ReturnType) => + conflicts.map(([name, versions]) => `${name}@{${versions.join(', ')}}`).join(', '); diff --git a/scripts/mcp-zod-lock.test.ts b/scripts/mcp-zod-lock.test.ts new file mode 100644 index 00000000..d87ba272 --- /dev/null +++ b/scripts/mcp-zod-lock.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, test } from 'bun:test'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { type ParseError, parse } from 'jsonc-parser'; +import { bunLockResolvedVersion, findMcpSdkZodSplit, formatMcpSdkZodSplit } from './mcp-zod-lock.js'; + +describe('mcp sdk zod lock', () => { + test('reads versions from bun.lock package tuples', () => { + expect(bunLockResolvedVersion(['zod@3.25.76', '', {}, 'sha'])).toBe('3.25.76'); + expect(bunLockResolvedVersion(['@modelcontextprotocol/sdk@1.30.0', '', {}, 'sha'])).toBe('1.30.0'); + expect(bunLockResolvedVersion({ version: '4.0.5' })).toBeUndefined(); + }); + + test('reports when the MCP SDK nested a different zod than the workspace', () => { + const split = findMcpSdkZodSplit({ + zod: ['zod@3.25.76', '', {}, 'sha'], + '@modelcontextprotocol/sdk/zod': ['zod@4.0.5', '', {}, 'sha'] + }); + + expect(split).toEqual({ hoisted: '3.25.76', nested: '4.0.5' }); + expect(formatMcpSdkZodSplit(split!)).toBe('@modelcontextprotocol/sdk resolved zod@4.0.5 while the workspace hoisted zod@3.25.76'); + }); + + test('accepts a shared resolution, including an exact nested pin of the same version', () => { + expect( + findMcpSdkZodSplit({ + zod: ['zod@4.0.5', '', {}, 'sha'] + }) + ).toBeUndefined(); + expect( + findMcpSdkZodSplit({ + zod: ['zod@4.0.5', '', {}, 'sha'], + '@modelcontextprotocol/sdk/zod': ['zod@4.0.5', '', {}, 'sha'] + }) + ).toBeUndefined(); + }); + + test('the repo lockfile keeps the MCP SDK on the same zod as mcp-server', () => { + const parseErrors: ParseError[] = []; + const lockfile = parse(readFileSync(join(import.meta.dir, '..', 'bun.lock'), 'utf8'), parseErrors, { + allowTrailingComma: true + }) as { packages?: Record }; + + expect(parseErrors).toEqual([]); + expect(findMcpSdkZodSplit(lockfile.packages ?? {})).toBeUndefined(); + }); +}); diff --git a/scripts/mcp-zod-lock.ts b/scripts/mcp-zod-lock.ts new file mode 100644 index 00000000..dc817dee --- /dev/null +++ b/scripts/mcp-zod-lock.ts @@ -0,0 +1,31 @@ +export const HOISTED_ZOD_LOCK_KEY = 'zod'; +export const MCP_SDK_ZOD_LOCK_KEY = '@modelcontextprotocol/sdk/zod'; + +/** Reads `name@version` from a bun.lock package tuple. */ +export const bunLockResolvedVersion = (entry: unknown): string | undefined => { + if (!Array.isArray(entry) || typeof entry[0] !== 'string') return undefined; + const specifier = entry[0]; + const at = specifier.lastIndexOf('@'); + if (at <= 0) return undefined; + return specifier.slice(at + 1); +}; + +/** + * The MCP SDK accepts zod 3 or 4. If it resolves a different copy than the + * hoisted workspace `zod`, `server.tool` / `server.prompt` compare two + * incompatible recursive schema trees and `tsc` OOMs. + */ +export const findMcpSdkZodSplit = (packages: Record) => { + const hoisted = bunLockResolvedVersion(packages[HOISTED_ZOD_LOCK_KEY]); + const nested = bunLockResolvedVersion(packages[MCP_SDK_ZOD_LOCK_KEY]); + if (!hoisted) { + throw new Error('bun.lock is missing a hoisted zod resolution'); + } + if (nested && nested !== hoisted) { + return { hoisted, nested }; + } + return undefined; +}; + +export const formatMcpSdkZodSplit = (split: { hoisted: string; nested: string }) => + `@modelcontextprotocol/sdk resolved zod@${split.nested} while the workspace hoisted zod@${split.hoisted}`;