From 65da0d3301d4d2827acd8c7a2e8abb8cfc3472d6 Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Tue, 22 Jul 2025 18:18:07 -0400 Subject: [PATCH 01/16] Add SIWF v2 reference app demonstrating a basic UI that can be emulated for building authentication to Frequency chain --- siwf-v2-reference-app/.gitignore | 24 + siwf-v2-reference-app/README.md | 59 + siwf-v2-reference-app/eslint.config.js | 23 + siwf-v2-reference-app/index.html | 13 + siwf-v2-reference-app/package-lock.json | 4450 +++++++++++++++++ siwf-v2-reference-app/package.json | 38 + siwf-v2-reference-app/public/vite.svg | 1 + siwf-v2-reference-app/src/App.css | 740 +++ siwf-v2-reference-app/src/App.tsx | 158 + siwf-v2-reference-app/src/assets/react.svg | 1 + .../src/components/MockAuthenticator.tsx | 324 ++ .../src/components/SiwfLogin.tsx | 389 ++ .../src/components/SiwfRequestGenerator.tsx | 437 ++ .../src/components/WalletConnect.tsx | 251 + siwf-v2-reference-app/src/frequency-theme.css | 736 +++ siwf-v2-reference-app/src/hooks/useWallet.ts | 267 + siwf-v2-reference-app/src/index.css | 86 + siwf-v2-reference-app/src/main.tsx | 10 + siwf-v2-reference-app/src/types.ts | 64 + siwf-v2-reference-app/src/utils/siwf.ts | 386 ++ siwf-v2-reference-app/src/vite-env.d.ts | 1 + siwf-v2-reference-app/tsconfig.app.json | 27 + siwf-v2-reference-app/tsconfig.json | 7 + siwf-v2-reference-app/tsconfig.node.json | 25 + siwf-v2-reference-app/vite.config.ts | 22 + 25 files changed, 8539 insertions(+) create mode 100644 siwf-v2-reference-app/.gitignore create mode 100644 siwf-v2-reference-app/README.md create mode 100644 siwf-v2-reference-app/eslint.config.js create mode 100644 siwf-v2-reference-app/index.html create mode 100644 siwf-v2-reference-app/package-lock.json create mode 100644 siwf-v2-reference-app/package.json create mode 100644 siwf-v2-reference-app/public/vite.svg create mode 100644 siwf-v2-reference-app/src/App.css create mode 100644 siwf-v2-reference-app/src/App.tsx create mode 100644 siwf-v2-reference-app/src/assets/react.svg create mode 100644 siwf-v2-reference-app/src/components/MockAuthenticator.tsx create mode 100644 siwf-v2-reference-app/src/components/SiwfLogin.tsx create mode 100644 siwf-v2-reference-app/src/components/SiwfRequestGenerator.tsx create mode 100644 siwf-v2-reference-app/src/components/WalletConnect.tsx create mode 100644 siwf-v2-reference-app/src/frequency-theme.css create mode 100644 siwf-v2-reference-app/src/hooks/useWallet.ts create mode 100644 siwf-v2-reference-app/src/index.css create mode 100644 siwf-v2-reference-app/src/main.tsx create mode 100644 siwf-v2-reference-app/src/types.ts create mode 100644 siwf-v2-reference-app/src/utils/siwf.ts create mode 100644 siwf-v2-reference-app/src/vite-env.d.ts create mode 100644 siwf-v2-reference-app/tsconfig.app.json create mode 100644 siwf-v2-reference-app/tsconfig.json create mode 100644 siwf-v2-reference-app/tsconfig.node.json create mode 100644 siwf-v2-reference-app/vite.config.ts diff --git a/siwf-v2-reference-app/.gitignore b/siwf-v2-reference-app/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/siwf-v2-reference-app/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/siwf-v2-reference-app/README.md b/siwf-v2-reference-app/README.md new file mode 100644 index 00000000..b3023b64 --- /dev/null +++ b/siwf-v2-reference-app/README.md @@ -0,0 +1,59 @@ +# SIWF v2 Reference App Demo + +A React + TypeScript frontend demonstrating Sign In With Frequency (SIWF) v2 integration with both MetaMask and Polkadot.js wallets. + +## Prerequisites + +1. **Node.js v22+** +2. **Gateway Server Running on localhost:3013** + - The frontend proxies API requests to avoid CORS issues + - Make sure your Frequency Gateway is running before starting the frontend +3. **Wallet Extensions**: + - [MetaMask](https://metamask.io/) for Ethereum-style wallets + - [Polkadot.js Extension](https://polkadot.js.org/extension/) for Substrate wallets + +## Setup + +1. **Install Dependencies**: + ```bash + npm install + ``` + +2. **Start Gateway Server** (in a separate terminal): + ```bash + # Make sure your Frequency Gateway is running on localhost:3013 + # This is required for SIWF authentication to work + ``` + +3. **Start Frontend**: + ```bash + npm run dev + ``` + +4. **Open Browser**: + - Navigate to `http://localhost:5173` + - Connect your wallet (MetaMask or Polkadot.js) + - Test SIWF authentication + +## Features + +- ✅ **Dual Wallet Support**: MetaMask and Polkadot.js +- ✅ **Real Crypto Signatures**: EIP-712 and CAIP-122 signing +- ✅ **Complete SIWF Flow**: Account detection, MSA creation, credential verification +- ✅ **Persistent Disconnect**: Wallet state persists across page refreshes +- ✅ **UI Re-rendering**: Clean state reset when switching wallets +- ✅ **CORS Handling**: Vite proxy configuration for Gateway API + +## API Proxy + +The frontend uses Vite's proxy feature to forward requests: +- Frontend requests: `http://localhost:5173/api/*` +- Proxied to: `http://localhost:3013/*` + +This avoids CORS issues when calling the Gateway API from the browser. + +## Troubleshooting + +- **CORS Errors**: Ensure Gateway server is running on localhost:3013 +- **Wallet Not Detected**: Check that wallet extensions are installed and enabled +- **SIWF Errors**: Verify Gateway is properly configured and accessible diff --git a/siwf-v2-reference-app/eslint.config.js b/siwf-v2-reference-app/eslint.config.js new file mode 100644 index 00000000..d94e7deb --- /dev/null +++ b/siwf-v2-reference-app/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { globalIgnores } from 'eslint/config' + +export default tseslint.config([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/siwf-v2-reference-app/index.html b/siwf-v2-reference-app/index.html new file mode 100644 index 00000000..e4b78eae --- /dev/null +++ b/siwf-v2-reference-app/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/siwf-v2-reference-app/package-lock.json b/siwf-v2-reference-app/package-lock.json new file mode 100644 index 00000000..d1480607 --- /dev/null +++ b/siwf-v2-reference-app/package-lock.json @@ -0,0 +1,4450 @@ +{ + "name": "siwf-frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "siwf-frontend", + "version": "0.0.0", + "dependencies": { + "@polkadot/extension-dapp": "^0.61.4", + "@polkadot/keyring": "^13.5.3", + "@polkadot/util": "^13.5.3", + "@polkadot/util-crypto": "^13.5.3", + "ethers": "^6.8.0", + "lucide-react": "^0.468.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-hot-toast": "^2.4.1" + }, + "devDependencies": { + "@eslint/js": "^9.30.1", + "@types/node": "^24.0.14", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.6.0", + "eslint": "^9.30.1", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.35.1", + "vite": "^7.0.4" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "license": "MIT" + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", + "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.4.tgz", + "integrity": "sha512-2bKONnuM53lINoDrSmK8qP8W271ms7pygDhZt4SiLOoLwBtoHqeCFi6RG42V8zd3mLHuJFhU/Bmaqo4nX0/kBw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz", + "integrity": "sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz", + "integrity": "sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot-api/metadata-builders": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz", + "integrity": "sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + } + }, + "node_modules/@polkadot-api/observable-client": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz", + "integrity": "sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/metadata-builders": "0.3.2", + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + }, + "peerDependencies": { + "@polkadot-api/substrate-client": "0.1.4", + "rxjs": ">=7.8.0" + } + }, + "node_modules/@polkadot-api/substrate-bindings": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz", + "integrity": "sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@noble/hashes": "^1.3.1", + "@polkadot-api/utils": "0.1.0", + "@scure/base": "^1.1.1", + "scale-ts": "^1.6.0" + } + }, + "node_modules/@polkadot-api/substrate-client": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz", + "integrity": "sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.1", + "@polkadot-api/utils": "0.1.0" + } + }, + "node_modules/@polkadot-api/utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz", + "integrity": "sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot/api": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-16.4.2.tgz", + "integrity": "sha512-Qgphl8520grYGID2bgJgYTT2jMsnmzKbjvDcnmNGZiykCw6X0TqmcDU1Jeiyfiev6h5CI3dPcE6jceiRqNCNqQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-augment": "16.4.2", + "@polkadot/api-base": "16.4.2", + "@polkadot/api-derive": "16.4.2", + "@polkadot/keyring": "^13.5.3", + "@polkadot/rpc-augment": "16.4.2", + "@polkadot/rpc-core": "16.4.2", + "@polkadot/rpc-provider": "16.4.2", + "@polkadot/types": "16.4.2", + "@polkadot/types-augment": "16.4.2", + "@polkadot/types-codec": "16.4.2", + "@polkadot/types-create": "16.4.2", + "@polkadot/types-known": "16.4.2", + "@polkadot/util": "^13.5.3", + "@polkadot/util-crypto": "^13.5.3", + "eventemitter3": "^5.0.1", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-augment": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-16.4.2.tgz", + "integrity": "sha512-MjxZHLP1eDQyttF6XC5URXfwVhcukpAQe7TTNo5TFTbQtv/RKjo+r+gVU1hwiCc4dO/y9lQ9FiDwSiaYozAfLQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-base": "16.4.2", + "@polkadot/rpc-augment": "16.4.2", + "@polkadot/types": "16.4.2", + "@polkadot/types-augment": "16.4.2", + "@polkadot/types-codec": "16.4.2", + "@polkadot/util": "^13.5.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-base": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-16.4.2.tgz", + "integrity": "sha512-tPKRnkUMoHy6Qo4mKBjFRxNjGunbybQ0bRTdaa4vfLPUExp1hVVcMH/VCUMDpuFT/1ueonZSKswkD/sGkN6sig==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "16.4.2", + "@polkadot/types": "16.4.2", + "@polkadot/util": "^13.5.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-derive": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-16.4.2.tgz", + "integrity": "sha512-FxQQP0uX5JUhMRtLeuNQ9BtO9w4wpJS56vPmFG06wHdEdHsLttH+Pykc5lkUYm2ot7hLbhU1Ry3Bjgi1EWvBOw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api": "16.4.2", + "@polkadot/api-augment": "16.4.2", + "@polkadot/api-base": "16.4.2", + "@polkadot/rpc-core": "16.4.2", + "@polkadot/types": "16.4.2", + "@polkadot/types-codec": "16.4.2", + "@polkadot/util": "^13.5.3", + "@polkadot/util-crypto": "^13.5.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/extension-dapp": { + "version": "0.61.4", + "resolved": "https://registry.npmjs.org/@polkadot/extension-dapp/-/extension-dapp-0.61.4.tgz", + "integrity": "sha512-5xs0t1oA9PiUjojBHbWtnE/Z1y1gwg0t440KqwS3mzlwMYlWBpT9XGwf9frD3G4fSSVIGGTfH5PX1uW992SPdQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/extension-inject": "0.61.4", + "@polkadot/util": "^13.5.3", + "@polkadot/util-crypto": "^13.5.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/api": "*", + "@polkadot/util": "*", + "@polkadot/util-crypto": "*" + } + }, + "node_modules/@polkadot/extension-inject": { + "version": "0.61.4", + "resolved": "https://registry.npmjs.org/@polkadot/extension-inject/-/extension-inject-0.61.4.tgz", + "integrity": "sha512-di2FSd7CEQsQY7v8d3L2KJGcswiW1FBZgZtzB4ONVd/R+3tV1XXqH16uV2+KkxZGfhLQMw/6jfR5ZjPN5sciHQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api": "^16.4.1", + "@polkadot/rpc-provider": "^16.4.1", + "@polkadot/types": "^16.4.1", + "@polkadot/util": "^13.5.3", + "@polkadot/util-crypto": "^13.5.3", + "@polkadot/x-global": "^13.5.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/api": "*", + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/keyring": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.5.3.tgz", + "integrity": "sha512-b9vxcb29jMHEc9OrWRxOstkOIWjIBsSzF9Zg5EsUeYtfwxzKinDccI5uAbkx0R6x7+IjJ6xeFJGpbX2A2U/nWg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "13.5.3", + "@polkadot/util-crypto": "13.5.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.3", + "@polkadot/util-crypto": "13.5.3" + } + }, + "node_modules/@polkadot/networks": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.5.3.tgz", + "integrity": "sha512-90UbcIYZArg0DcP+6ZRWKy6Xqo0r46WfBuaKvYJIvfObgr5Pm4aPnAagEKehLJAStRdhEOpYozmKT1v3z8dHcw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "13.5.3", + "@substrate/ss58-registry": "^1.51.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-augment": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-16.4.2.tgz", + "integrity": "sha512-svg+HYT+TCbaDHTxZLnEbN1QQg8drsbc8LtGZYFJX9sE4261TaZ1+NvaldfjyD4u+6DwSHbWxTcXxX78Jk/3hA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "16.4.2", + "@polkadot/types": "16.4.2", + "@polkadot/types-codec": "16.4.2", + "@polkadot/util": "^13.5.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-core": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-16.4.2.tgz", + "integrity": "sha512-Thh3T0f9D0iWI2Aeb7Cgec/7hhGoC6l+LT9028VeumSSy47K7/keTPkY6u43tYAnZB6Quhyd7SMwJjgNmhmHEA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-augment": "16.4.2", + "@polkadot/rpc-provider": "16.4.2", + "@polkadot/types": "16.4.2", + "@polkadot/util": "^13.5.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-provider": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-16.4.2.tgz", + "integrity": "sha512-Nl2AIoZedoR3/RoTJ484eItuaFBiiaaum+yoLISd4GRRw1IgAB5DSu/gcs/5dlOxYT70ITJVWf/rLvFzPL7lTA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^13.5.3", + "@polkadot/types": "16.4.2", + "@polkadot/types-support": "16.4.2", + "@polkadot/util": "^13.5.3", + "@polkadot/util-crypto": "^13.5.3", + "@polkadot/x-fetch": "^13.5.3", + "@polkadot/x-global": "^13.5.3", + "@polkadot/x-ws": "^13.5.3", + "eventemitter3": "^5.0.1", + "mock-socket": "^9.3.1", + "nock": "^13.5.5", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@substrate/connect": "0.8.11" + } + }, + "node_modules/@polkadot/types": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-16.4.2.tgz", + "integrity": "sha512-Y/3ycSzdCm4WBja/6nGLTon14wgEEYyyNmjyjJ8KG1V3WScP3yeraVD5xXBbmm/Zi4Tg+D2MlFa31oJHfvTJyw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^13.5.3", + "@polkadot/types-augment": "16.4.2", + "@polkadot/types-codec": "16.4.2", + "@polkadot/types-create": "16.4.2", + "@polkadot/util": "^13.5.3", + "@polkadot/util-crypto": "^13.5.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-augment": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-16.4.2.tgz", + "integrity": "sha512-pyVkN6Xc9+b08PQbC0YOHifCgtqjyug6/jEpiDPzS0njOWGjZbS9KYvCRXscVw3M11SYTQ5utY0uFudvoYOtXg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types": "16.4.2", + "@polkadot/types-codec": "16.4.2", + "@polkadot/util": "^13.5.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-codec": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-16.4.2.tgz", + "integrity": "sha512-HDI5nNf0Argi+Oaxo+rZPjr4iMvof3fX/GHr0D/P4V5bQe01zlixSa2d3wJ0wFWSIrarEEs35D7ZjFrNNo2vgQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^13.5.3", + "@polkadot/x-bigint": "^13.5.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-create": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-16.4.2.tgz", + "integrity": "sha512-vcX44omoWDIKJWpSWHRwP6F4clZ+mWPC/uPN5xzRISVCbzNCxNf354m37g/k3c47FxuDAvv91cqncWuh5ZbqyA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types-codec": "16.4.2", + "@polkadot/util": "^13.5.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-known": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-16.4.2.tgz", + "integrity": "sha512-nEpBh3d27Oe28l0NUL/Bj2OLtMivuroDKtwxseLyUJe1zvQCp6aobVDhybSYbrHWLcTGI0Yrc5upGpUzCRD+7A==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/networks": "^13.5.3", + "@polkadot/types": "16.4.2", + "@polkadot/types-codec": "16.4.2", + "@polkadot/types-create": "16.4.2", + "@polkadot/util": "^13.5.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-support": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-16.4.2.tgz", + "integrity": "sha512-2bhp31pMv/2ae+XlubpNzpkSyewZhBjr8HZDLC209+fp2BlV4P935P+rHfahw9cPkW/sOZtfw0sPCM5wunWXmw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^13.5.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-13.5.3.tgz", + "integrity": "sha512-dPqXvkzICTNz9vL85RdPyLzTDgB0/KtmROF8DB8taQksWyQp1RH3uU5mHHOmHtb0IJQBA5O/kumaXUfMQNo9Qw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-bigint": "13.5.3", + "@polkadot/x-global": "13.5.3", + "@polkadot/x-textdecoder": "13.5.3", + "@polkadot/x-textencoder": "13.5.3", + "@types/bn.js": "^5.1.6", + "bn.js": "^5.2.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.3.tgz", + "integrity": "sha512-/GLv2+DpiyciN7yAwFTjQdFA5JDMVVLUrP5a6YuAVUGQywRnGC1k940d2pFsqdwNvGa2Xcf50DFNxvnfQiyZlQ==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.5.3", + "@polkadot/util": "13.5.3", + "@polkadot/wasm-crypto": "^7.4.1", + "@polkadot/wasm-util": "^7.4.1", + "@polkadot/x-bigint": "13.5.3", + "@polkadot/x-randomvalues": "13.5.3", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.3" + } + }, + "node_modules/@polkadot/wasm-bridge": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.4.1.tgz", + "integrity": "sha512-tdkJaV453tezBxhF39r4oeG0A39sPKGDJmN81LYLf+Fihb7astzwju+u75BRmDrHZjZIv00un3razJEWCxze6g==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.4.1", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.4.1.tgz", + "integrity": "sha512-kHN/kF7hYxm1y0WeFLWeWir6oTzvcFmR4N8fJJokR+ajYbdmrafPN+6iLgQVbhZnDdxyv9jWDuRRsDnBx8tPMQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.4.1", + "@polkadot/wasm-crypto-asmjs": "7.4.1", + "@polkadot/wasm-crypto-init": "7.4.1", + "@polkadot/wasm-crypto-wasm": "7.4.1", + "@polkadot/wasm-util": "7.4.1", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.4.1.tgz", + "integrity": "sha512-pwU8QXhUW7IberyHJIQr37IhbB6DPkCG5FhozCiNTq4vFBsFPjm9q8aZh7oX1QHQaiAZa2m2/VjIVE+FHGbvHQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-init": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.4.1.tgz", + "integrity": "sha512-AVka33+f7MvXEEIGq5U0dhaA2SaXMXnxVCQyhJTaCnJ5bRDj0Xlm3ijwDEQUiaDql7EikbkkRtmlvs95eSUWYQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.4.1", + "@polkadot/wasm-crypto-asmjs": "7.4.1", + "@polkadot/wasm-crypto-wasm": "7.4.1", + "@polkadot/wasm-util": "7.4.1", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-wasm": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.4.1.tgz", + "integrity": "sha512-PE1OAoupFR0ZOV2O8tr7D1FEUAwaggzxtfs3Aa5gr+yxlSOaWUKeqsOYe1KdrcjmZVV3iINEAXxgrbzCmiuONg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.4.1", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-util": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.4.1.tgz", + "integrity": "sha512-RAcxNFf3zzpkr+LX/ItAsvj+QyM56TomJ0xjUMo4wKkHjwsxkz4dWJtx5knIgQz/OthqSDMR59VNEycQeNuXzA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/x-bigint": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.5.3.tgz", + "integrity": "sha512-o408qh3P+st/3ghTgVd4ATrePqExd7UgWHXPTJ0i74Q7/3iI1cWMNloNQFNDZxnSNIPB/AnFk8sfEWfpfPLucw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-fetch": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-13.5.3.tgz", + "integrity": "sha512-+AFbo8JthkIEZtseOG8WhogAg0HnkvK4fUrCqn5YB8L7TJrIWxaAmccCarMLYQEAwYT7OKlBMbrMwRllGI9yRg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.3", + "node-fetch": "^3.3.2", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-global": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.5.3.tgz", + "integrity": "sha512-b8zEhDk6XDIXRGaPXnSxamQ3sVObm0xPRbkxbk2l9QiMB4MO1pOtAm5knQkHpC2Z+tVTy1SrSqUN5iqVnavicQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-randomvalues": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.5.3.tgz", + "integrity": "sha512-BrKE5Q4dzHWNjwq0PX08uWlJIQOztVCJIYuZiIAj0ic33oLRrQuPojXFWhw/3McjXlVXscFNtsgIXsRli+boiQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.3", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/x-textdecoder": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.5.3.tgz", + "integrity": "sha512-qXQ0qxlKAl7FLCHgeKdHbtLFQgkBGNYp1RXtbUSIWGE1qKwTMTSQkrsXegwSXG3YM1MiJk2qHc7nlyuCK0xWVw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-textencoder": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.5.3.tgz", + "integrity": "sha512-Gb3jW/pMdWd1P0Q+K7NYbeo8ivbeGn+UBkCYYIEcShun8u8XlHMiGBnYE9fFcx9GRAzoViZJ7htL5KaFzLtUkg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-ws": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.5.3.tgz", + "integrity": "sha512-vIi9im6Zeo0eAagPSUF8WhdFBI1oomj4jF1R2jepiKmBVkT5HVn39MK2mix5fNjLESSa2K79iWYzS5VoVi0gxA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.3", + "tslib": "^2.8.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", + "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", + "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", + "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", + "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", + "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", + "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", + "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", + "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", + "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", + "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", + "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", + "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", + "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", + "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", + "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", + "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", + "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", + "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", + "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", + "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@substrate/connect": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.11.tgz", + "integrity": "sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw==", + "deprecated": "versions below 1.x are no longer maintained", + "license": "GPL-3.0-only", + "optional": true, + "dependencies": { + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "@substrate/light-client-extension-helpers": "^1.0.0", + "smoldot": "2.0.26" + } + }, + "node_modules/@substrate/connect-extension-protocol": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.2.tgz", + "integrity": "sha512-t66jwrXA0s5Goq82ZtjagLNd7DPGCNjHeehRlE/gcJmJ+G56C0W+2plqOMRicJ8XGR1/YFnUSEqUFiSNbjGrAA==", + "license": "GPL-3.0-only", + "optional": true + }, + "node_modules/@substrate/connect-known-chains": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.10.3.tgz", + "integrity": "sha512-OJEZO1Pagtb6bNE3wCikc2wrmvEU5x7GxFFLqqbz1AJYYxSlrPCGu4N2og5YTExo4IcloNMQYFRkBGue0BKZ4w==", + "license": "GPL-3.0-only", + "optional": true + }, + "node_modules/@substrate/light-client-extension-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz", + "integrity": "sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "^0.0.1", + "@polkadot-api/json-rpc-provider-proxy": "^0.1.0", + "@polkadot-api/observable-client": "^0.3.0", + "@polkadot-api/substrate-client": "^0.1.2", + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "rxjs": "^7.8.1" + }, + "peerDependencies": { + "smoldot": "2.x" + } + }, + "node_modules/@substrate/ss58-registry": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz", + "integrity": "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==", + "license": "Apache-2.0" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/react": { + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", + "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.38.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.38.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bn.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.190", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.190.tgz", + "integrity": "sha512-k4McmnB2091YIsdCgkS0fMVMPOJgxl93ltFzaryXqwip1AaxeDqKCGLxkXODDA5Ab/D+tV5EL5+aTx76RvLRxw==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.31.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ethers": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.15.0.tgz", + "integrity": "sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/ethers/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/ethers/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/goober": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", + "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.468.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz", + "integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mock-socket": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nock": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-hot-toast": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz", + "integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", + "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.45.1", + "@rollup/rollup-android-arm64": "4.45.1", + "@rollup/rollup-darwin-arm64": "4.45.1", + "@rollup/rollup-darwin-x64": "4.45.1", + "@rollup/rollup-freebsd-arm64": "4.45.1", + "@rollup/rollup-freebsd-x64": "4.45.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", + "@rollup/rollup-linux-arm-musleabihf": "4.45.1", + "@rollup/rollup-linux-arm64-gnu": "4.45.1", + "@rollup/rollup-linux-arm64-musl": "4.45.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-musl": "4.45.1", + "@rollup/rollup-linux-s390x-gnu": "4.45.1", + "@rollup/rollup-linux-x64-gnu": "4.45.1", + "@rollup/rollup-linux-x64-musl": "4.45.1", + "@rollup/rollup-win32-arm64-msvc": "4.45.1", + "@rollup/rollup-win32-ia32-msvc": "4.45.1", + "@rollup/rollup-win32-x64-msvc": "4.45.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/scale-ts": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.1.tgz", + "integrity": "sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==", + "license": "MIT", + "optional": true + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/smoldot": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.26.tgz", + "integrity": "sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "optional": true, + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.2", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/siwf-v2-reference-app/package.json b/siwf-v2-reference-app/package.json new file mode 100644 index 00000000..72e346f6 --- /dev/null +++ b/siwf-v2-reference-app/package.json @@ -0,0 +1,38 @@ +{ + "name": "siwf-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@polkadot/extension-dapp": "^0.61.4", + "@polkadot/keyring": "^13.5.3", + "@polkadot/util": "^13.5.3", + "@polkadot/util-crypto": "^13.5.3", + + "ethers": "^6.8.0", + "lucide-react": "^0.468.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-hot-toast": "^2.4.1" + }, + "devDependencies": { + "@eslint/js": "^9.30.1", + "@types/node": "^24.0.14", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.6.0", + "eslint": "^9.30.1", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.35.1", + "vite": "^7.0.4" + } +} diff --git a/siwf-v2-reference-app/public/vite.svg b/siwf-v2-reference-app/public/vite.svg new file mode 100644 index 00000000..e7b8dfb1 --- /dev/null +++ b/siwf-v2-reference-app/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/siwf-v2-reference-app/src/App.css b/siwf-v2-reference-app/src/App.css new file mode 100644 index 00000000..c1340d9c --- /dev/null +++ b/siwf-v2-reference-app/src/App.css @@ -0,0 +1,740 @@ +/* SIWF App Styles with Frequency Design System - Responsive Edition */ + +html, body { + width: 100vw; + overflow-x: auto; +} + +#root { + width: 100vw; + overflow-x: auto; +} + +.app { + min-height: 100vh; + display: flex; + flex-direction: column; + background: var(--bg-secondary); +} + +/* ===== RESPONSIVE HEADER STYLES ===== */ +.app-header { + background: var(--bg-primary); + border-bottom: 1px solid var(--border-light); + box-shadow: var(--shadow-sm); + position: sticky; + top: 0; + z-index: 50; + backdrop-filter: blur(8px); + background: rgba(255, 255, 255, 0.95); + width: 100%; +} + +.header-content { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-6) 0; + gap: var(--space-6); + flex-wrap: wrap; +} + +.brand-section { + display: flex; + align-items: center; + gap: var(--space-4); + flex-shrink: 0; +} + +.frequency-logo { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.brand-text { + display: flex; + flex-direction: column; + gap: var(--space-1); +} + +.app-title { + font-size: var(--text-3xl); + font-weight: 700; + color: var(--text-primary); + margin: 0; + line-height: 1.2; +} + +.app-subtitle { + font-size: var(--text-sm); + color: var(--text-secondary); + margin: 0; + font-weight: 500; +} + +.wallet-status { + display: flex; + align-items: center; + flex-shrink: 0; +} + +.status-indicator { + display: flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-2) var(--space-4); + background: var(--bg-secondary); + border: 1px solid var(--border-light); + border-radius: var(--radius-md); + white-space: nowrap; +} + +.status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + flex-shrink: 0; +} + +.status-dot.connected { + background: var(--success); + animation: pulse 2s infinite; +} + +.status-dot.disconnected { + background: var(--gray-400); +} + +.status-text { + font-size: var(--text-xs); + font-weight: 500; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +/* Header Responsive Breakpoints */ + +/* Phone (up to 768px) */ +@media (max-width: 768px) { + .header-content { + flex-direction: column; + text-align: center; + gap: var(--space-4); + padding: var(--space-4) 0; + } + + .brand-section { + flex-direction: column; + text-align: center; + gap: var(--space-3); + } + + .app-title { + font-size: var(--text-2xl); + } + + .app-subtitle { + font-size: var(--text-xs); + } + + .status-indicator { + padding: var(--space-3) var(--space-4); + } + + .status-text { + font-size: var(--text-xs); + } +} + +/* Tablet (768px to 1024px) */ +@media (min-width: 769px) and (max-width: 1024px) { + .header-content { + padding: var(--space-5) 0; + gap: var(--space-5); + } + + .app-title { + font-size: var(--text-3xl); + } + + .brand-section { + gap: var(--space-3); + } +} + +/* Desktop (1024px and above) */ +@media (min-width: 1025px) { + .header-content { + padding: var(--space-6) 0; + gap: var(--space-8); + } + + .app-title { + font-size: var(--text-4xl); + } +} + +/* ===== RESPONSIVE MAIN CONTENT ===== */ +.app-main { + flex: 1; + padding: var(--space-8) 0; + width: 100%; + max-width: none; +} + +.content-grid { + display: grid; + gap: var(--space-6); + width: 100%; + max-width: none; + margin: 0; +} + +.grid-item { + display: flex; + flex-direction: column; +} + +/* Override frequency-grid-2 constraints for true full width */ +.content-grid.frequency-grid-2 { + width: 100%; + max-width: none; +} + +/* Main Content Responsive Breakpoints */ + +/* Phone (up to 768px) */ +@media (max-width: 768px) { + .app-main { + padding: var(--space-6) 0; + } + + .content-grid { + grid-template-columns: 1fr; + gap: var(--space-6); + width: 100%; + padding: 0 var(--space-4); + } +} + +/* Tablet (768px to 1024px) */ +@media (min-width: 769px) and (max-width: 1024px) { + .app-main { + padding: var(--space-10) 0; + } + + .content-grid { + grid-template-columns: 1fr; + gap: var(--space-8); + max-width: 600px; + padding: 0 var(--space-6); + } +} + +/* Desktop (1024px and above) - Centered content */ +@media (min-width: 1025px) { + .app-main { + padding: var(--space-12) 0; + width: 100%; + display: flex; + justify-content: center; + align-items: flex-start; + } + + .content-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--space-8); + width: auto; + min-width: 800px; + max-width: 1200px; + padding: 0 var(--space-8); + } + + .content-grid.frequency-grid-2 { + display: grid; + grid-template-columns: 1fr 1fr; + } + + .grid-item { + width: 100%; + max-width: none; + } +} + + + +/* ===== RESPONSIVE FOOTER STYLES ===== */ +.app-footer { + background: var(--bg-primary); + border-top: 1px solid var(--border-light); + padding: var(--space-6) 0; + margin-top: auto; +} + +.footer-content { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-6); + flex-wrap: wrap; +} + +.footer-links { + display: flex; + align-items: center; + gap: var(--space-4); + flex-wrap: wrap; +} + +.footer-link { + color: var(--text-secondary); + font-size: var(--text-sm); + font-weight: 500; + text-decoration: none; + transition: color var(--transition-fast); +} + +.footer-link:hover { + color: var(--frequency-primary); + text-decoration: underline; +} + +.footer-separator { + color: var(--text-tertiary); + font-size: var(--text-sm); +} + +.debug-section { + display: flex; + align-items: center; +} + +.debug-info { + font-family: var(--font-family-mono); + font-size: var(--text-xs); + color: var(--text-tertiary); + background: var(--bg-tertiary); + padding: var(--space-1) var(--space-2); + border-radius: var(--radius-sm); + border: 1px solid var(--border-light); +} + +/* Footer Responsive Breakpoints */ + +/* Phone (up to 768px) */ +@media (max-width: 768px) { + .app-footer { + padding: var(--space-4) 0; + } + + .footer-content { + flex-direction: column; + text-align: center; + gap: var(--space-4); + } + + .footer-links { + flex-direction: column; + gap: var(--space-2); + } + + .footer-separator { + display: none; + } + + .debug-info { + font-size: 10px; + padding: var(--space-1); + } +} + +/* Tablet (768px to 1024px) */ +@media (min-width: 769px) and (max-width: 1024px) { + .footer-content { + gap: var(--space-5); + } +} + +/* ===== RESPONSIVE CARD STYLES ===== */ +.card { + background: var(--bg-primary); + border: 1px solid var(--border-light); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-sm); + overflow: hidden; + transition: all var(--transition-normal); + height: fit-content; +} + +.card:hover { + box-shadow: var(--shadow-md); + border-color: var(--border-medium); + transform: translateY(-1px); +} + +.card-header { + padding: var(--space-6); + border-bottom: 1px solid var(--border-light); + background: var(--bg-secondary); +} + +.card-title { + display: flex; + align-items: center; + gap: var(--space-3); + margin: 0 0 var(--space-2) 0; + font-size: var(--text-xl); + font-weight: 600; + color: var(--text-primary); +} + +.card-description { + margin: 0; + color: var(--text-secondary); + font-size: var(--text-sm); + line-height: 1.5; +} + +.card-content { + padding: var(--space-6); +} + +/* Card Responsive Breakpoints */ + +/* Phone (up to 768px) */ +@media (max-width: 768px) { + .card-content, + .card-header { + padding: var(--space-4); + } + + .card-title { + font-size: var(--text-lg); + gap: var(--space-2); + } + + .card-description { + font-size: var(--text-xs); + } +} + +/* ===== RESPONSIVE FORM STYLES ===== */ +.form-section { + margin-bottom: var(--space-6); +} + +.form-section h3 { + margin: 0 0 var(--space-3) 0; + font-size: var(--text-lg); + font-weight: 600; + color: var(--text-primary); +} + +.form-description { + margin: 0 0 var(--space-6) 0; + color: var(--text-secondary); + font-size: var(--text-sm); + line-height: 1.5; +} + +.form-group { + margin-bottom: var(--space-6); +} + +.form-group label { + display: flex; + align-items: center; + gap: var(--space-2); + margin-bottom: var(--space-2); + font-weight: 500; + font-size: var(--text-sm); + color: var(--text-primary); +} + +.form-input { + width: 100%; + padding: var(--space-3) var(--space-4); + border: 1px solid var(--border-medium); + border-radius: var(--radius-md); + font-size: var(--text-sm); + background: var(--bg-primary); + color: var(--text-primary); + transition: all var(--transition-fast); +} + +.form-input:focus { + outline: none; + border-color: var(--frequency-primary); + box-shadow: 0 0 0 3px rgba(91, 69, 255, 0.1); +} + +.form-input:disabled { + background: var(--bg-tertiary); + color: var(--text-tertiary); + cursor: not-allowed; +} + +/* Form Responsive Breakpoints */ + +/* Phone (up to 768px) */ +@media (max-width: 768px) { + .form-section { + margin-bottom: var(--space-4); + } + + .form-section h3 { + font-size: var(--text-base); + } + + .form-description { + font-size: var(--text-xs); + margin-bottom: var(--space-4); + } + + .form-group { + margin-bottom: var(--space-4); + } + + .form-input { + padding: var(--space-4); + font-size: var(--text-base); + } +} + +/* ===== RESPONSIVE BUTTON STYLES ===== */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--space-2); + padding: var(--space-3) var(--space-6); + border: 1px solid transparent; + border-radius: var(--radius-md); + font-size: var(--text-sm); + font-weight: 500; + text-decoration: none; + cursor: pointer; + transition: all var(--transition-fast); + user-select: none; + font-family: inherit; + min-height: 44px; +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn-primary { + background: var(--frequency-primary); + color: var(--text-inverse); + border-color: var(--frequency-primary); +} + +.btn-primary:hover:not(:disabled) { + background: var(--frequency-primary-dark); + border-color: var(--frequency-primary-dark); + color: var(--text-inverse); + text-decoration: none; + transform: translateY(-1px); + box-shadow: var(--shadow-md); +} + +.btn-secondary { + background: var(--bg-primary); + color: var(--text-primary); + border-color: var(--border-medium); +} + +.btn-secondary:hover:not(:disabled) { + background: var(--bg-secondary); + border-color: var(--border-dark); + color: var(--text-primary); + text-decoration: none; +} + +.btn-large { + padding: var(--space-4) var(--space-8); + font-size: var(--text-base); + width: 100%; + min-height: 48px; +} + +.btn-disabled { + background: var(--bg-tertiary); + color: var(--text-tertiary); + border-color: var(--border-light); + cursor: not-allowed; +} + +/* Button Responsive Breakpoints */ + +/* Phone (up to 768px) */ +@media (max-width: 768px) { + .btn { + padding: var(--space-4) var(--space-6); + font-size: var(--text-base); + min-height: 48px; + } + + .btn-large { + padding: var(--space-5) var(--space-8); + font-size: var(--text-lg); + min-height: 52px; + } +} + +/* ===== RESPONSIVE ICON STYLES ===== */ +.icon { + width: 1.25rem; + height: 1.25rem; + flex-shrink: 0; +} + +.wallet-icon { + width: 2rem; + height: 2rem; + margin-bottom: var(--space-3); +} + +.spinning { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +/* Icon Responsive Breakpoints */ + +/* Phone (up to 768px) */ +@media (max-width: 768px) { + .icon { + width: 1rem; + height: 1rem; + } + + .wallet-icon { + width: 1.5rem; + height: 1.5rem; + margin-bottom: var(--space-2); + } +} + +/* ===== RESPONSIVE WALLET CONNECTION STYLES ===== */ +.not-connected { + text-align: center; +} + +.description { + margin: 0 0 var(--space-6) 0; + color: var(--text-secondary); + line-height: 1.5; +} + +.requirements { + margin: var(--space-4) 0 0 0; + font-size: var(--text-xs); + color: var(--text-tertiary); +} + +.connected { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-4); +} + +.wallet-info { + flex: 1; +} + +.status { + display: flex; + align-items: center; + gap: var(--space-2); + margin-bottom: var(--space-2); + font-weight: 500; +} + +.address { + font-size: var(--text-sm); + color: var(--text-secondary); + font-family: var(--font-family-mono); +} + +.wallet-option { + margin-bottom: var(--space-4); +} + +.wallet-note { + margin: var(--space-2) 0 0 0; + font-size: var(--text-xs); + color: var(--text-secondary); +} + +/* Wallet Connection Responsive Breakpoints */ + +Phone (up to 768px) +@media (max-width: 768px) { + .connected { + flex-direction: column; + align-items: stretch; + gap: var(--space-4); + } + + .wallet-info { + text-align: center; + } + + .status { + justify-content: center; + margin-bottom: var(--space-3); + } + + .address { + font-size: var(--text-xs); + text-align: center; + } + + .wallet-option { + margin-bottom: var(--space-6); + } + + .wallet-note { + text-align: center; + margin-top: var(--space-3); + } +} + +/* Tablet (768px to 1024px) */ +@media (min-width: 769px) and (max-width: 1024px) { + .connected { + flex-direction: column; + align-items: center; + text-align: center; + gap: var(--space-4); + } +} + +/* ===== DARK MODE RESPONSIVE SUPPORT ===== */ +@media (prefers-color-scheme: dark) { + .app-header { + background: rgba(17, 24, 39, 0.95); + } +} + diff --git a/siwf-v2-reference-app/src/App.tsx b/siwf-v2-reference-app/src/App.tsx new file mode 100644 index 00000000..7efc9d3d --- /dev/null +++ b/siwf-v2-reference-app/src/App.tsx @@ -0,0 +1,158 @@ +import React, { useEffect, useState } from 'react'; +import { Toaster } from 'react-hot-toast'; +import toast from 'react-hot-toast'; +import { WalletConnect } from './components/WalletConnect'; +import { SiwfLogin } from './components/SiwfLogin'; +import { useWallet } from './hooks/useWallet'; +import { initializeSiwfCallback } from './utils/siwf'; +import './App.css'; + +function App() { + const { wallet } = useWallet(); + const [rerenderCounter, setRerenderCounter] = useState(0); + + // Create a unique key based on wallet state to force re-rendering on wallet changes + const walletKey = `${wallet.walletType || 'none'}-${wallet.account || 'none'}-${wallet.isConnected}-${rerenderCounter}`; + + // Initialize SIWF callback handling on mount + useEffect(() => { + initializeSiwfCallback( + (result) => { + console.log('✅ SIWF authentication successful:', result); + toast.success('SIWF authentication completed!'); + }, + (error) => { + console.error('❌ SIWF authentication failed:', error); + toast.error('SIWF authentication failed: ' + error.message); + } + ); + }, []); + + // Force re-render when wallet state changes + useEffect(() => { + console.log('🔄 App: Wallet state changed, forcing component re-render', { + walletType: wallet.walletType, + account: wallet.account?.slice(0, 8) + '...', + isConnected: wallet.isConnected, + newKey: walletKey + }); + + setRerenderCounter(prev => prev + 1); + }, [wallet.account, wallet.walletType, wallet.isConnected]); + + return ( +
+ + + {/* Header with Frequency Branding */} +
+
+
+
+
+ + + + + + + + + + +
+
+

Sign In With Frequency(SIWF) v2

+

+ Demo Application • Frequency Blockchain Integration + SIWF Demo +

+
+
+
+
+ + + + {wallet.isConnected ? `Connected: ${wallet.walletType}` : 'Not Connected'} + + + {wallet.isConnected ? '🟢' : '🔴'} + + +
+
+
+
+
+ + {/* Main Content */} +
+
+
+ +
+
+ +
+
+
+ + {/* Footer */} + +
+ ); +} + +export default App; diff --git a/siwf-v2-reference-app/src/assets/react.svg b/siwf-v2-reference-app/src/assets/react.svg new file mode 100644 index 00000000..6c87de9b --- /dev/null +++ b/siwf-v2-reference-app/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/siwf-v2-reference-app/src/components/MockAuthenticator.tsx b/siwf-v2-reference-app/src/components/MockAuthenticator.tsx new file mode 100644 index 00000000..e3f3b3cd --- /dev/null +++ b/siwf-v2-reference-app/src/components/MockAuthenticator.tsx @@ -0,0 +1,324 @@ +import React, { useState } from 'react'; +import { User, Mail, Lock, Key, CheckCircle, Loader, ArrowLeft } from 'lucide-react'; +import toast from 'react-hot-toast'; + +interface MockAuthenticatorProps { + signedRequest: string; + onSuccess: (result: any) => void; + onCancel: () => void; +} + +interface AuthCredentials { + email: string; + password: string; + handle?: string; +} + +export const MockAuthenticator: React.FC = ({ + signedRequest, + onSuccess, + onCancel +}) => { + const [authMode, setAuthMode] = useState<'choose' | 'login' | 'signup'>('choose'); + const [isLoading, setIsLoading] = useState(false); + const [credentials, setCredentials] = useState({ + email: '', + password: '', + handle: '' + }); + + const generateMockAuthData = (userEmail: string, userHandle?: string) => { + // Generate mock authorizationCode and authorizationPayload as requested + const mockAuthorizationCode = `680a0a68-6d3b-4d6d-89b7-0b01a6f7e86f`; + + const mockAuthorizationPayload = `eyJ1c2VyUHVibGljS2V5Ijp7ImVuY29kZWRWYWx1ZSI6ImY2YWt1ZmtxOUxleDZyVDhSQ0VEUnVvWlFSZ281cFdpUnplbzgxbm1LTkdXR05KZEoiLCJlbmNvZGluZyI6ImJhc2U1OCIsImZvcm1hdCI6InNzNTgiLCJ0eXBlIjoiU3IyNTUxOSJ9LCJwYXlsb2FkcyI6W3sic2lnbmF0dXJlIjp7ImFsZ28iOiJTUjI1NTE5IiwiZW5jb2RpbmciOiJiYXNlMTYiLCJlbmNvZGVkVmFsdWUiOiIweGNjNDY5Y2U1MzA0NzY4YzE2MTY0ZTQyNDQyZGZlNDA3NjlhNzQyNzg2YmQzNjBjYWJiOGZkYWYxNWEyNTc2NWM5YzE4ZDcxYjhjODg5NzJmNDJhYjZmOTM4ZTcwM2Y0Y2M0MDM1NjU2YTdjOWRkMTkzYWNhMjYyNmEzZDdmODg3In0sImVuZHBvaW50Ijp7InBhbGxldCI6Im1zYSIsImV4dHJpbnNpYyI6ImNyZWF0ZVNwb25zb3JlZEFjY291bnRXaXRoRGVsZWdhdGlvbiJ9LCJ0eXBlIjoiYWRkUHJvdmlkZXIiLCJwYXlsb2FkIjp7ImF1dGhvcml6ZWRNc2FJZCI6MSwic2NoZW1hSWRzIjpbNSw3LDgsOSwxMF0sImV4cGlyYXRpb24iOjI0fX0seyJzaWduYXR1cmUiOnsiYWxnbyI6IlNSMjU1MTkiLCJlbmNvZGluZyI6ImJhc2UxNiIsImVuY29kZWRWYWx1ZSI6IjB4OTA1ODNlOGMyYmRiNDZhZmQ2MmNhZWQ5NGViMmY2Yzk5ZjY2MzA4YTkxNTkxZGExNDQ3ZDJlZjc4MTAwMjYyMmVmMTJiOTYwZmM4NmRlODgxMjFlMjQzYWQzY2Y1ZTE1YTYxNWQ0OGU3YzJlMjBjODYwYTdhZGY2MTk5Y2ZiODgifSwiZW5kcG9pbnQiOnsicGFsbGV0Ijoic3RhdGVmdWxTdG9yYWdlIiwiZXh0cmluc2ljIjoiYXBwbHlJdGVtQWN0aW9uc1dpdGhTaWduYXR1cmVWMiJ9LCJ0eXBlIjoiaXRlbUFjdGlvbnMiLCJwYXlsb2FkIjp7InNjaGVtYUlkIjo3LCJ0YXJnZXRIYXNoIjowLCJleHBpcmF0aW9uIjoyMCwiYWN0aW9ucyI6W3sidHlwZSI6ImFkZEl0ZW0iLCJwYXlsb2FkSGV4IjoiMHg0MGVlYTFlMzlkMmYxNTQ1ODRjNGIxY2E4ZjIyOGJiNDlhZTVhMTQ3ODZlZDYzYzkwMDI1ZTc1NWYxNmJkNThkMzcifV19fSx7InNpZ25hdHVyZSI6eyJhbGdvIjoiU1IyNTUxOSIsImVuY29kaW5nIjoiYmFzZTE2IiwiZW5jb2RlZFZhbHVlIjoiMHhhMjkxYjIyZjZkM2UyMTI1ODIwMGYxMjdlYTIzYjc0MTI0OGE1ODQyMDhkOTY3ODNmYjM1OTlhNzMzYTg3YzJlNjdmZTQyNzQ5MWE2YWE5ODRlMzk4MmU1Y2NhZmNiYjdmMTcwYTE3YTVkYjYwODIyMzBhMWM4ZTRhYzJhM2Y4YyJ9LCJlbmRwb2ludCI6eyJwYWxsZXQiOiJoYW5kbGVzIiwiZXh0cmluc2ljIjoiY2xhaW1IYW5kbGUifSwidHlwZSI6ImNsYWltSGFuZGxlIiwicGF5bG9hZCI6eyJiYXNlSGFuZGxlIjoiRXhhbXBsZUhhbmRsZSIsImV4cGlyYXRpb24iOjI0fX0seyJzaWduYXR1cmUiOnsiYWxnbyI6IlNSMjU1MTkiLCJlbmNvZGluZyI6ImJhc2UxNiIsImVuY29kZWRWYWx1ZSI6IjB4MDQwODRhZThjZWQ0ZTllMjdjYmMyN2JmMjVhM2MzNGNiNmZhZWVjNWZiZGNiMzMxMDIxZDk4ZGM0YTIxMWYxNzI1NmMwOGFhOTZkZWZjYjU5N2YzODY3YjA4YjNhOTAxZDYxYzhjZTM2ZTBmNGNjZGEwYmY3OTQ1OGFkMzk1ODAifSwiZW5kcG9pbnQiOnsicGFsbGV0IjoibXNhIiwiZXh0cmluc2ljIjoiYWRkUmVjb3ZlcnlDb21taXRtZW50In0sInR5cGUiOiJyZWNvdmVyeUNvbW1pdG1lbnQiLCJwYXlsb2FkIjp7InJlY292ZXJ5Q29tbWl0bWVudEhleCI6IjB4ZWVhMWUzOWQyZjE1NDU4NGM0YjFjYThmMjI4YmI0OWFlNWExNDc4NmVkNjNjOTAwMjVlNzU1ZjE2YmQ1OGQzNyIsImV4cGlyYXRpb24iOjIwfX1dLCJjcmVkZW50aWFscyI6W3siQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnL25zL2NyZWRlbnRpYWxzL3YyIiwiaHR0cHM6Ly93d3cudzMub3JnL25zL2NyZWRlbnRpYWxzL3VuZGVmaW5lZC10ZXJtcy92MiJdLCJ0eXBlIjpbIlZlcmlmaWVkRW1haWxBZGRyZXNzQ3JlZGVudGlhbCIsIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImlzc3VlciI6ImRpZDp3ZWI6ZnJlcXVlbmN5YWNjZXNzLmNvbSIsInZhbGlkRnJvbSI6IjIwMjQtMDgtMjFUMjE6Mjg6MDguMjg5KzAwMDAiLCJjcmVkZW50aWFsU2NoZW1hIjp7InR5cGUiOiJKc29uU2NoZW1hIiwiaWQiOiJodHRwczovL3NjaGVtYXMuZnJlcXVlbmN5YWNjZXNzLmNvbS9WZXJpZmllZEVtYWlsQWRkcmVzc0NyZWRlbnRpYWwvYmNpcWU0cW9jemhmdGljaTRkemZ2ZmJlbDdmbzRoNHNyNWdyY28zb292d3lrNnk0eW5mNDR0c2kuanNvbiJ9LCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDprZXk6ejZRTnVjUVY0QUYxWE1RVjRrbmdibW5CSHdZYTZtVnN3UEVHcmtGclVheWh0dFQxIiwiZW1haWxBZGRyZXNzIjoiam9obi5kb2VAZXhhbXBsZS5jb20iLCJsYXN0VmVyaWZpZWQiOiIyMDI0LTA4LTIxVDIxOjI3OjU5LjMwOSswMDAwIn0sInByb29mIjp7InR5cGUiOiJEYXRhSW50ZWdyaXR5UHJvb2YiLCJ2ZXJpZmljYXRpb25NZXRob2QiOiJkaWQ6d2ViOmZyZXF1ZW5jeWFjY2Vzcy5jb20jejZNa29mV0V4V2tVdlRaZVhiOVRtTHRhNW1CVDZRdGo1OGVzNUZxZzFMNUJDV1FEIiwiY3J5cHRvc3VpdGUiOiJlZGRzYS1yZGZjLTIwMjIiLCJwcm9vZlB1cnBvc2UiOiJhc3NlcnRpb25NZXRob2QiLCJwcm9vZlZhbHVlIjoiejRqQXJuUHd1d1l4TG5iQmlyTGFucGtjeUJwbVF3bXluNWYzUGRUWW54aHB5NDhxcGd2SEhhdjZ3YXJqaXpqdnRMTWc2ajNGSzNCcWJSMm51eVQyVVRTV0MifX0seyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvbnMvY3JlZGVudGlhbHMvdjIiLCJodHRwczovL3d3dy53My5vcmcvbnMvY3JlZGVudGlhbHMvdW5kZWZpbmVkLXRlcm1zL3YyIl0sInR5cGUiOlsiVmVyaWZpZWRHcmFwaEtleUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJpc3N1ZXIiOiJkaWQ6a2V5Ono2UU51Y1FWNEFGMVhNUVY0a25nYm1uQkh3WWE2bVZzd1BFR3JrRnJVYXlodHRUMSIsInZhbGlkRnJvbSI6IjIwMjQtMDgtMjFUMjE6Mjg6MDguMjg5KzAwMDAiLCJjcmVkZW50aWFsU2NoZW1hIjp7InR5cGUiOiJKc29uU2NoZW1hIiwiaWQiOiJodHRwczovL3NjaGVtYXMuZnJlcXVlbmN5YWNjZXNzLmNvbS9WZXJpZmllZEdyYXBoS2V5Q3JlZGVudGlhbC9iY2lxbWR2bXhkNTR6dmU1a2lmeWNnc2R0b2FoczVlY2Y0aGFsMnRzM2VleGtnb2N5YzVvY2EyeS5qc29uIn0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6NlFOdWNRVjRBRjFYTVFWNGtuZ2JtbkJId1lhNm1Wc3dQRUdya0ZyVWF5aHR0VDEiLCJlbmNvZGVkUHVibGljS2V5VmFsdWUiOiIweGI1MDMyOTAwMjkzZjFjOWU1ODIyZmQ5YzEyMGIyNTNjYjRhNGRmZTk0YzIxNGU2ODhlMDFmMzJkYjllZWRmMTciLCJlbmNvZGVkUHJpdmF0ZUtleVZhbHVlIjoiMHhkMDkxMGM4NTM1NjM3MjMyNTNjNGVkMTA1YzA4NjE0ZmM4YWFhZjFiMDg3MTM3NTUyMGQ3MjI1MTQ5NmU4ZDg3IiwiZW5jb2RpbmciOiJiYXNlMTYiLCJmb3JtYXQiOiJiYXJlIiwidHlwZSI6IlgyNTUxOSIsImtleVR5cGUiOiJkc25wLnB1YmxpYy1rZXkta2V5LWFncmVlbWVudCJ9fSx7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy9ucy9jcmVkZW50aWFscy92MiIsImh0dHBzOi8vd3d3LnczLm9yZy9ucy9jcmVkZW50aWFscy91bmRlZmluZWQtdGVybXMvdjIiXSwidHlwZSI6WyJWZXJpZmllZFJlY292ZXJ5U2VjcmV0Q3JlZGVudGlhbCIsIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImlzc3VlciI6ImRpZDprZXk6ejZRTnVjUVY0QUYxWE1RVjRrbmdibW5CSHdZYTZtVnN3UEVHcmtGclVheWh0dFQxIiwidmFsaWRGcm9tIjoiMjAyNC0wOC0yMVQyMToyODowOC4yODkrMDAwMCIsImNyZWRlbnRpYWxTY2hlbWEiOnsidHlwZSI6Ikpzb25TY2hlbWEiLCJpZCI6Imh0dHBzOi8vc2NoZW1hcy5mcmVxdWVuY3lhY2Nlc3MuY29tL1ZlcmlmaWVkUmVjb3ZlcnlTZWNyZXRDcmVkZW50aWFsL2JjaXFwZzZxbTRybnUyajR2NmdoeHFxZ3drZ2dva3d2eHMzdDJiZXhiZDNvYmt5cGtpcnl5bHhxLmpzb24ifSwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiJkaWQ6a2V5Ono2UU51Y1FWNEFGMVhNUVY0a25nYm1uQkh3WWE2bVZzd1BFR3JrRnJVYXlodHRUMSIsInJlY292ZXJ5U2VjcmV0IjoiNjlFQy0yMzgyLUUxRTYtNzZGMy0zNDFGLTM0MTQtOURENS1DRkE1LTY5MzItRTQxOC05Mzg1LTAzNTgtMzFERi1BRkVBLTk4MjgtRDNCNyJ9fV19`; + + return { + authorizationCode: mockAuthorizationCode, + authorizationPayload: mockAuthorizationPayload + }; + }; + + const callAccountService = async (authorizationCode: string, authorizationPayload: string) => { + // Mock the account service API call for demonstration + console.log('📡 Making mock call to /v2/accounts/siwf with:', { + authorizationCode, + authorizationPayload: authorizationPayload.substring(0, 50) + '...' + }); + + // Simulate API delay + await new Promise(resolve => setTimeout(resolve, 1500)); + + // Return mock response matching WalletV2LoginResponseDto structure + const mockResponse = { + controlKey: "f6cL4wq1HUNx11TcvdABNf9UNXXoyH47mVUwT59tzSFRW8yDH", + signUpReferenceId: "MjY3MjI3NWZlMGM0NTZmYjY3MWU0ZjQxN2ZiMmY5ODkyYzc1NzNiYQo", + signUpStatus: "waiting", + msaId: "314159265358979323846264338", + email: credentials.email, + phoneNumber: "555-867-5309", + graphKey: "f6Y86vfvou3d4RGjYJM2k5L7g1HMjVTDMAtVMDh8g67i3VLZi", + recoverySecret: "69EC-2382-E1E6-76F3-341F-3414-9DD5-CFA5-6932-E418-9385-0358-31DF-AFEA-9828-D3B7", + rawCredentials: [ + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://www.w3.org/ns/credentials/undefined-terms/v2" + ], + "type": [ + "VerifiedEmailAddressCredential", + "VerifiableCredential" + ], + "issuer": "did:web:frequencyaccess.com", + "validFrom": "2024-08-21T21:28:08.289+0000", + "credentialSchema": { + "type": "JsonSchema", + "id": "https://schemas.frequencyaccess.com/VerifiedEmailAddressCredential/bciqe4qoczhftici4dzfvfbel7fo4h4sr5grco3oovwyk6y4ynf44tsi.json" + }, + "credentialSubject": { + "id": "did:key:z6QNucQV4AF1XMQV4kngbmnBHwYa6mVswPEGrkFrUayhttT1", + "emailAddress": credentials.email, + "lastVerified": "2024-08-21T21:27:59.309+0000" + }, + "proof": { + "type": "DataIntegrityProof", + "verificationMethod": "did:web:frequencyaccess.com#z6MkofWExWkUvTZeXb9TmLta5mBT6Qtj58es5Fqg1L5BCWQD", + "cryptosuite": "eddsa-rdfc-2022", + "proofPurpose": "assertionMethod", + "proofValue": "z4jArnPwuwYxLnbBirLanpkcyBpmQwmyn5f3PdTYnxhpy48qpgvHHav6warjizjvtLMg6j3FK3BqbR2nuyT2UTSWC" + } + }, + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://www.w3.org/ns/credentials/undefined-terms/v2" + ], + "type": [ + "VerifiedGraphKeyCredential", + "VerifiableCredential" + ], + "issuer": "did:key:z6QNucQV4AF1XMQV4kngbmnBHwYa6mVswPEGrkFrUayhttT1", + "validFrom": "2024-08-21T21:28:08.289+0000", + "credentialSchema": { + "type": "JsonSchema", + "id": "https://schemas.frequencyaccess.com/VerifiedGraphKeyCredential/bciqmdvmxd54zve5kifycgsdtoahs5ecf4hal2ts3eexkgocyc5oca2y.json" + }, + "credentialSubject": { + "id": "did:key:z6QNucQV4AF1XMQV4kngbmnBHwYa6mVswPEGrkFrUayhttT1", + "encodedPublicKeyValue": "0xb5032900293f1c9e5822fd9c120b253cb4a4dfe94c214e688e01f32db9eedf17", + "encodedPrivateKeyValue": "0xd0910c853563723253c4ed105c08614fc8aaaf1b0871375520d72251496e8d87", + "encoding": "base16", + "format": "bare", + "type": "X25519", + "keyType": "dsnp.public-key-key-agreement" + }, + "proof": { + "type": "DataIntegrityProof", + "verificationMethod": "did:key:z6MktZ15TNtrJCW2gDLFjtjmxEdhCadNCaDizWABYfneMqhA", + "cryptosuite": "eddsa-rdfc-2022", + "proofPurpose": "assertionMethod", + "proofValue": "z2HHWwtWggZfvGqNUk4S5AAbDGqZRFXjpMYAsXXmEksGxTk4DnnkN3upCiL1mhgwHNLkxY3s8YqNyYnmpuvUke7jF" + } + } + ] + }; + + console.log('✅ Mock account service response:', mockResponse); + return mockResponse; + }; + + const handleAuthenticate = async () => { + if (!credentials.email || !credentials.password) { + toast.error('Please fill in all required fields'); + return; + } + + if (authMode === 'signup' && !credentials.handle) { + toast.error('Handle is required for signup'); + return; + } + + setIsLoading(true); + + try { + console.log(`🔐 Mock ${authMode} authentication for:`, credentials.email); + + // Generate mock authorization data + const mockAuthData = generateMockAuthData(credentials.email, credentials.handle); + + console.log('🔗 Generated mock auth data:', mockAuthData); + + // Call the account service API + console.log('📡 Calling account service API...'); + const accountResponse = await callAccountService( + mockAuthData.authorizationCode, + mockAuthData.authorizationPayload + ); + + console.log('✅ Account service response:', accountResponse); + + toast.success(`${authMode === 'login' ? 'Login' : 'Signup'} successful!`); + + // Pass the result back to parent + onSuccess({ + authMode, + credentials, + mockAuthData, + accountResponse + }); + + } catch (error: any) { + console.error(`❌ Mock ${authMode} failed:`, error); + toast.error(error.message || `${authMode} failed`); + } finally { + setIsLoading(false); + } + }; + + const renderChooseMode = () => ( +
+
+

🎭 Mock Frequency Access

+

Choose how you want to authenticate

+
+ +
+ + + +
+ +
+

+ Note: This is a mock authentication interface that bypasses Frequency Access. + It will generate the same mock authorizationCode and authorizationPayload for demonstration purposes. +

+
+
+ ); + + const renderAuthForm = () => ( +
+
+ +

+ {authMode === 'login' ? '🔑 Login' : '🆕 Sign Up'} +

+

+ {authMode === 'login' + ? 'Enter your existing account credentials' + : 'Create a new Frequency account' + } +

+
+ +
+
+ + setCredentials({...credentials, email: e.target.value})} + placeholder="user@example.com" + className="form-input" + required + /> +
+ +
+ + setCredentials({...credentials, password: e.target.value})} + placeholder="Enter your password" + className="form-input" + required + /> +
+ + {authMode === 'signup' && ( +
+ + setCredentials({...credentials, handle: e.target.value})} + placeholder="cooluser" + className="form-input" + required + /> +
+ )} + + +
+
+ ); + + return ( +
+
+
+ {authMode === 'choose' && renderChooseMode()} + {(authMode === 'login' || authMode === 'signup') && renderAuthForm()} + +
+ +
+
+
+
+ ); +}; \ No newline at end of file diff --git a/siwf-v2-reference-app/src/components/SiwfLogin.tsx b/siwf-v2-reference-app/src/components/SiwfLogin.tsx new file mode 100644 index 00000000..59d7b11b --- /dev/null +++ b/siwf-v2-reference-app/src/components/SiwfLogin.tsx @@ -0,0 +1,389 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { Key, User, Mail, Loader, CheckCircle, XCircle, AlertTriangle, RefreshCw } from 'lucide-react'; +import toast from 'react-hot-toast'; +import { useWallet } from '../hooks/useWallet'; +import { SiwfRequestGenerator } from './SiwfRequestGenerator'; +import { MockAuthenticator } from './MockAuthenticator'; +import type { SiwfResult, SiwfState } from '../types'; + +export const SiwfLogin: React.FC = () => { + const { wallet } = useWallet(); + const [siwfState, setSiwfState] = useState({ + isLoading: false, + result: null, + error: null + }); + const [isResetting, setIsResetting] = useState(false); + const [componentKey, setComponentKey] = useState(0); + const [showMockAuth, setShowMockAuth] = useState(false); + const [currentSignedRequest, setCurrentSignedRequest] = useState(''); + + // Track previous wallet state to detect changes + const prevWalletRef = useRef<{ + account: string | null; + walletType: string | null; + isConnected: boolean; + }>({ + account: null, + walletType: null, + isConnected: false + }); + + // Force component re-render by incrementing key + const forceRerender = () => { + setComponentKey(prev => prev + 1); + console.log('🔄 Forcing component re-render, new key:', componentKey + 1); + }; + + // Reset component state when wallet changes + useEffect(() => { + const currentWallet = { + account: wallet.account, + walletType: wallet.walletType, + isConnected: wallet.isConnected + }; + + const previousWallet = prevWalletRef.current; + + const walletChanged = + currentWallet.account !== previousWallet.account || + currentWallet.walletType !== previousWallet.walletType || + currentWallet.isConnected !== previousWallet.isConnected; + + if (walletChanged) { + console.log('🔄 SiwfLogin: Wallet state CHANGED:', { + previous: previousWallet, + current: currentWallet, + timestamp: new Date().toISOString() + }); + + setIsResetting(true); + setSiwfState({ + isLoading: false, + result: null, + error: null + }); + + forceRerender(); + prevWalletRef.current = currentWallet; + } + }, [wallet.account, wallet.walletType, wallet.isConnected]); + + // Separate effect to handle reset animation timeout + useEffect(() => { + if (isResetting) { + console.log('🔄 Starting reset animation...'); + + const timer = setTimeout(() => { + console.log('🔄 Clearing reset animation (primary)'); + setIsResetting(false); + }, 800); + + const safetyTimer = setTimeout(() => { + console.log('🔄 SAFETY: Force clearing reset animation'); + setIsResetting(false); + }, 3000); + + return () => { + console.log('🔄 Cleanup: clearing reset timers'); + clearTimeout(timer); + clearTimeout(safetyTimer); + }; + } + }, [isResetting]); + + // Additional effect to reset on mount + useEffect(() => { + console.log('🔄 SiwfLogin: Component mounted/remounted'); + setSiwfState({ + isLoading: false, + result: null, + error: null + }); + }, []); + + const handleStartAuthentication = (signedRequest: string) => { + console.log('🚀 Starting MOCK SIWF authentication with signed request', signedRequest); + setCurrentSignedRequest(signedRequest); + setShowMockAuth(true); + toast.success('Opening mock authentication interface...'); + }; + + const handleMockAuthSuccess = (result: any) => { + console.log('✅ Mock authentication completed:', result); + setSiwfState({ + isLoading: false, + result: result, + error: null + }); + setShowMockAuth(false); + toast.success('Authentication successful!'); + }; + + const handleMockAuthCancel = () => { + console.log('❌ Mock authentication cancelled'); + setShowMockAuth(false); + setSiwfState({ + isLoading: false, + result: null, + error: null + }); + toast('Authentication cancelled'); + }; + + const getWalletDisplayName = () => { + switch (wallet.walletType) { + case 'metamask': + return '🦊 MetaMask'; + case 'polkadot': + return '🔴 Polkadot.js'; + default: + return 'Unknown'; + } + }; + + const getShortAddress = (address: string) => { + // Responsive address display - show more on larger screens + const isMobile = window.innerWidth <= 768; + if (isMobile) { + return `${address.slice(0, 6)}...${address.slice(-4)}`; + } else { + return `${address.slice(0, 8)}...${address.slice(-6)}`; + } + }; + + return ( +
+
+
+

+ + Sign In With Frequency + SIWF + {isResetting && } + + #{componentKey} + +

+

+ Generate and use SIWF authentication requests + Generate SIWF authentication +

+
+ +
+ {!wallet.isConnected ? ( +
+ +

+ Connect your wallet to see connected account info + Connect wallet for account info +

+

+ You can still generate SIWF requests without a connected wallet. + You can still generate SIWF requests. +

+
+ ) : ( +
+

+ Connected Wallet + Wallet Info +

+
+
+ Type: + {getWalletDisplayName()} +
+
+ Address: + + {wallet.account && getShortAddress(wallet.account)} + +
+
+
+ )} + + {siwfState.error && ( +
+
+ + {siwfState.error} +
+
+ )} + + {showMockAuth ? ( + + ) : siwfState.result ? ( +
+

+ + Authentication Result +

+
+

Mode: {siwfState.result.authMode}

+

Email: {siwfState.result.credentials?.email}

+ {siwfState.result.credentials?.handle && ( +

Handle: {siwfState.result.credentials.handle}

+ )} +

Authorization Code: {siwfState.result.mockAuthData?.authorizationCode}

+ {siwfState.result.accountResponse && ( +
+ Account Service Response +
+                      {JSON.stringify(siwfState.result.accountResponse, null, 2)}
+                    
+
+ )} +
+ +
+ ) : ( + + )} + + {/* Emergency reset button for debugging - only show on larger screens */} + {isResetting && ( + + )} +
+
+
+ ); +}; \ No newline at end of file diff --git a/siwf-v2-reference-app/src/components/SiwfRequestGenerator.tsx b/siwf-v2-reference-app/src/components/SiwfRequestGenerator.tsx new file mode 100644 index 00000000..5dc4355d --- /dev/null +++ b/siwf-v2-reference-app/src/components/SiwfRequestGenerator.tsx @@ -0,0 +1,437 @@ +import React, { useState } from 'react'; +import { Settings, Copy, ExternalLink, ChevronDown, ChevronUp, Key } from 'lucide-react'; +import toast from 'react-hot-toast'; +import { generateSiwfSignedRequest, type SiwfRequestOptions } from '../utils/siwf'; +import type { WalletType } from '../types'; + +// SIWF Permission definitions +const PERMISSION_BUNDLES = { + 'public-private-graph': { + name: 'Bundle: Public & Private Graph', + permissions: [6, 7, 8, 9, 10], + description: 'Manage both public and private social connections' + }, + 'dsnp-v13-content': { + name: 'Bundle: DSNP v1.3 Content', + permissions: [1, 2, 3, 4, 5], + description: 'Legacy content management (Deprecated)' + } +}; + +const INDIVIDUAL_PERMISSIONS = [ + { id: 1, name: 'dsnp.broadcast: Create new public content (v1)', deprecated: true }, + { id: 2, name: 'dsnp.profile: Update profile information (v1)', deprecated: true }, + { id: 3, name: 'dsnp.reply: Public reply to content (v1)', deprecated: true }, + { id: 4, name: 'dsnp.tombstone: Mark content for deletion (v1)', deprecated: true }, + { id: 5, name: 'dsnp.update: Update an existing post or reply (v1)' }, + { id: 6, name: 'dsnp.broadcast: Create new public content (v2)' }, + { id: 7, name: 'dsnp.dsnp-content-attribute: Create an authenticated attribute set for DSNP content (v1)' }, + { id: 8, name: 'dsnp.ext-content-attribute: Create an authenticated attribute set for content external to DSNP (v1)' }, + { id: 9, name: 'dsnp.private-connections: Update private friendship connections (v1)' }, + { id: 10, name: 'dsnp.private-follows: Update private follow list (v1)' }, + { id: 11, name: 'dsnp.profile-resources: Update user profile information (v1)' }, + { id: 12, name: 'dsnp.public-follows: Update public follow list (v1)' }, + { id: 13, name: 'dsnp.reaction: Public reaction to content (v1)' }, + { id: 14, name: 'dsnp.reply: Public reply to content (v2)' }, + { id: 15, name: 'dsnp.tombstone: Mark content for deletion (v2)' }, + { id: 16, name: 'dsnp.update: Update an existing post or reply (v2)' }, + { id: 17, name: 'dsnp.user-attribute-set: Create an authenticated attribute set for a DSNP User (v2)' } +]; + +interface SiwfRequestGeneratorProps { + onStartAuthentication: (signedRequest: string) => void; + walletInfo?: { + account: string; + walletType: WalletType; + }; +} + +export const SiwfRequestGenerator: React.FC = ({ + onStartAuthentication, + walletInfo +}) => { + // Form state + const [callbackUri, setCallbackUri] = useState('http://localhost:5173/login/callback'); + const [selectedBundle, setSelectedBundle] = useState('public-private-graph'); + const [customPermissions, setCustomPermissions] = useState([]); + const [additionalPermissions, setAdditionalPermissions] = useState(''); + const [schemaIds, setSchemaIds] = useState(''); + const [credentials, setCredentials] = useState({ + graphKey: true, + recoverySecret: false, + email: true, + phone: true + }); + const [applicationContextUrl, setApplicationContextUrl] = useState(''); + + // Result state + const [generatedRequest, setGeneratedRequest] = useState(null); + const [showJson, setShowJson] = useState(false); + + const generateSignedRequest = () => { + try { + // Get permissions from bundle or custom selection + let permissions: number[] = []; + if (selectedBundle && PERMISSION_BUNDLES[selectedBundle as keyof typeof PERMISSION_BUNDLES]) { + permissions = PERMISSION_BUNDLES[selectedBundle as keyof typeof PERMISSION_BUNDLES].permissions; + } else { + permissions = customPermissions; + } + + // Add additional permissions + if (additionalPermissions.trim()) { + const additional = additionalPermissions.split(',').map(p => parseInt(p.trim())).filter(n => !isNaN(n)); + permissions = [...new Set([...permissions, ...additional])]; + } + + // Build the request options + const options: SiwfRequestOptions = { + callbackUri, + permissions, + additionalPermissions, + schemaIds: schemaIds ? schemaIds.split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n)) : [], + credentials, + applicationContextUrl: applicationContextUrl || undefined + }; + + // Generate the signed request + const result = generateSiwfSignedRequest(options, walletInfo); + setGeneratedRequest(result); + + toast.success('Signed request generated successfully!'); + } catch (error) { + console.error('Error generating signed request:', error); + toast.error('Failed to generate signed request'); + } + }; + + const copyToClipboard = (text: string) => { + navigator.clipboard.writeText(text); + toast.success('Copied to clipboard!'); + }; + + const startAuthentication = () => { + if (generatedRequest) { + onStartAuthentication(generatedRequest.signedRequest); + } + }; + + return ( +
+
+
+

+ + SIWF Request Generator + Request Generator +

+

+ Generate a signed request for SIWF authentication + Generate SIWF request +

+
+ +
+
+ {/* Wallet Status */} + {walletInfo ? ( +
+
+ ✅ Using Connected Wallet: {walletInfo.walletType === 'metamask' ? '🦊 MetaMask' : '🔴 Polkadot.js'} ({walletInfo.account.slice(0, 8)}...{walletInfo.account.slice(-6)}) +
+
+ ) : ( +
+
+ ⚠️ No Wallet Connected: Using mock public key for demonstration +
+
+ )} + + {/* Callback URI */} +
+ + setCallbackUri(e.target.value)} + placeholder="http://localhost:5173/login/callback" + className="form-input" + required + /> +
+ + {/* Permissions */} +
+ +
+
+ +
+
+ +
+
+ +
+
+ + {selectedBundle === 'custom' && ( +
+ {INDIVIDUAL_PERMISSIONS.map(permission => ( +
+ +
+ ))} +
+ )} +
+ + {/* Additional Permissions */} +
+ + setAdditionalPermissions(e.target.value)} + placeholder="18, 19, 20" + className="form-input" + /> +
+ + {/* Schema IDs */} +
+ + setSchemaIds(e.target.value)} + placeholder="1, 2, 3" + className="form-input" + /> +
+ + {/* Credentials */} +
+ +
+ + + + +
+
+ + {/* Application Context URL */} +
+ + setApplicationContextUrl(e.target.value)} + placeholder="https://example.org/myapp/siwf-manifest.json" + className="form-input" + /> +
+ + {/* Generate Button */} + +
+ + {/* Results Section */} + {generatedRequest && ( +
+

Result

+ +
+

Login Request Payload and Signature (signedRequest parameter)

+
+ + +
+
+ +
+

Mainnet/Production URL

+
+ + + + + +
+
+ +
+

Testnet/Staging URL

+
+ + + + + +
+
+ +
+
+ + {showJson && ( +
+                      {JSON.stringify(generatedRequest.jsonPayload, null, 2)}
+                    
+ )} +
+
+ + {/* Start Authentication Button */} + +
+ )} +
+
+
+ ); +}; \ No newline at end of file diff --git a/siwf-v2-reference-app/src/components/WalletConnect.tsx b/siwf-v2-reference-app/src/components/WalletConnect.tsx new file mode 100644 index 00000000..cc35fdc0 --- /dev/null +++ b/siwf-v2-reference-app/src/components/WalletConnect.tsx @@ -0,0 +1,251 @@ +import React, { useEffect, useState } from 'react'; +import { Wallet, WalletCards, Loader, AlertCircle, CheckCircle } from 'lucide-react'; +import { useWallet } from '../hooks/useWallet'; + +export const WalletConnect: React.FC = () => { + const { + wallet, + isMetaMaskAvailable, + isPolkadotAvailable, + connectMetaMask, + connectPolkadot, + disconnectWallet + } = useWallet(); + + const [justConnected, setJustConnected] = useState(false); + + // Show "just connected" feedback briefly + useEffect(() => { + if (wallet.isConnected) { + setJustConnected(true); + const timer = setTimeout(() => setJustConnected(false), 2000); + return () => clearTimeout(timer); + } + }, [wallet.account, wallet.walletType]); + + const getWalletTypeDisplay = (walletType: string | null) => { + switch (walletType) { + case 'metamask': + return '🦊 MetaMask'; + case 'polkadot': + return '🔴 Polkadot.js'; + default: + return 'Unknown'; + } + }; + + const getShortAddress = (address: string) => { + // Responsive address display - show more on larger screens + const isMobile = window.innerWidth <= 768; + if (isMobile) { + return `${address.slice(0, 6)}...${address.slice(-4)}`; + } else { + return `${address.slice(0, 8)}...${address.slice(-6)}`; + } + }; + + return ( +
+
+
+

+ + Wallet Connection + Wallet +

+

+ {wallet.isConnected + ? ( + <> + Your wallet is connected and ready to use + Connected and ready + + ) + : ( + <> + Connect your wallet to start using Sign In With Frequency + Connect to start using SIWF + + ) + } +

+
+ +
+ {!wallet.isConnected ? ( +
+
+ +
+ + {wallet.error && ( +
+ + {wallet.error} +
+ )} + +
+

+ Choose a Wallet: + Select Wallet: +

+ + {/* MetaMask Option */} +
+ + {!isMetaMaskAvailable && ( +

+ + MetaMask not detected. Install MetaMask + + + Install MetaMask + +

+ )} +
+ + {/* Polkadot.js Option */} +
+ + {!isPolkadotAvailable && ( +

+ + Don't have Polkadot.js? Install Extension + + + Install Polkadot.js + +

+ )} +
+
+
+ ) : ( +
+
+
+
+ + + Connected via {getWalletTypeDisplay(wallet.walletType)} + + + {getWalletTypeDisplay(wallet.walletType)} + + + {justConnected && ( + + )} +
+
+ Address: + + {wallet.account && getShortAddress(wallet.account)} + +
+
+ +
+ )} +
+
+
+ ); +}; \ No newline at end of file diff --git a/siwf-v2-reference-app/src/frequency-theme.css b/siwf-v2-reference-app/src/frequency-theme.css new file mode 100644 index 00000000..406ce577 --- /dev/null +++ b/siwf-v2-reference-app/src/frequency-theme.css @@ -0,0 +1,736 @@ +/* Frequency Design System for SIWF v2 Reference App - Responsive Edition */ + +/* ===== CSS VARIABLES ===== */ +:root { + /* Colors - Frequency Brand */ + --frequency-primary: #5B45FF; + --frequency-primary-dark: #4A3AE0; + --frequency-primary-light: #7B6BFF; + --frequency-secondary: #FF6B35; + --frequency-accent: #00D4AA; + + /* Grays */ + --gray-50: #F9FAFB; + --gray-100: #F3F4F6; + --gray-200: #E5E7EB; + --gray-300: #D1D5DB; + --gray-400: #9CA3AF; + --gray-500: #6B7280; + --gray-600: #4B5563; + --gray-700: #374151; + --gray-800: #1F2937; + --gray-900: #111827; + + /* Semantic Colors */ + --success: #10B981; + --warning: #F59E0B; + --error: #EF4444; + --info: #3B82F6; + + /* Background Colors */ + --bg-primary: #FFFFFF; + --bg-secondary: var(--gray-50); + --bg-tertiary: var(--gray-100); + --bg-dark: var(--gray-900); + + /* Text Colors */ + --text-primary: var(--gray-900); + --text-secondary: var(--gray-600); + --text-tertiary: var(--gray-400); + --text-inverse: #FFFFFF; + + /* Border Colors */ + --border-light: var(--gray-200); + --border-medium: var(--gray-300); + --border-dark: var(--gray-400); + + /* Shadows */ + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + + /* Typography */ + --font-family-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --font-family-mono: "SF Mono", Monaco, Inconsolata, "Roboto Mono", Consolas, "Courier New", monospace; + + /* Responsive Breakpoints */ + --breakpoint-phone: 768px; + --breakpoint-tablet: 1024px; + --breakpoint-desktop: 1024px; + + /* Font Sizes - Responsive */ + --text-xs: 0.75rem; /* 12px */ + --text-sm: 0.875rem; /* 14px */ + --text-base: 1rem; /* 16px */ + --text-lg: 1.125rem; /* 18px */ + --text-xl: 1.25rem; /* 20px */ + --text-2xl: 1.5rem; /* 24px */ + --text-3xl: 1.875rem; /* 30px */ + --text-4xl: 2.25rem; /* 36px */ + --text-5xl: 3rem; /* 48px */ + + /* Spacing - Responsive */ + --space-1: 0.25rem; /* 4px */ + --space-2: 0.5rem; /* 8px */ + --space-3: 0.75rem; /* 12px */ + --space-4: 1rem; /* 16px */ + --space-5: 1.25rem; /* 20px */ + --space-6: 1.5rem; /* 24px */ + --space-8: 2rem; /* 32px */ + --space-10: 2.5rem; /* 40px */ + --space-12: 3rem; /* 48px */ + --space-16: 4rem; /* 64px */ + --space-20: 5rem; /* 80px */ + + /* Container Sizes */ + --container-sm: 640px; + --container-md: 768px; + --container-lg: 1024px; + --container-xl: 1280px; + --container-2xl: 1536px; + + /* Border Radius */ + --radius-sm: 0.25rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; + --radius-xl: 0.75rem; + --radius-2xl: 1rem; + --radius-full: 9999px; + + /* Transitions */ + --transition-fast: 150ms ease-in-out; + --transition-normal: 250ms ease-in-out; + --transition-slow: 350ms ease-in-out; +} + +/* ===== RESPONSIVE FONT SIZES ===== */ +@media (max-width: 768px) { + :root { + --text-3xl: 1.5rem; /* 24px on mobile */ + --text-4xl: 1.875rem; /* 30px on mobile */ + --text-5xl: 2.25rem; /* 36px on mobile */ + } +} + +@media (min-width: 769px) and (max-width: 1024px) { + :root { + --text-3xl: 1.75rem; /* 28px on tablet */ + --text-4xl: 2rem; /* 32px on tablet */ + --text-5xl: 2.5rem; /* 40px on tablet */ + } +} + +/* ===== RESPONSIVE SPACING ===== */ +@media (max-width: 768px) { + :root { + --space-12: 2rem; /* 32px on mobile */ + --space-16: 2.5rem; /* 40px on mobile */ + --space-20: 3rem; /* 48px on mobile */ + } +} + +/* ===== GLOBAL STYLES ===== */ +* { + box-sizing: border-box; +} + +body { + margin: 0; + padding: 0; + font-family: var(--font-family-sans); + font-size: var(--text-base); + line-height: 1.6; + color: var(--text-primary); + background-color: var(--bg-secondary); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* ===== TYPOGRAPHY ===== */ +h1, h2, h3, h4, h5, h6 { + margin: 0 0 var(--space-4) 0; + font-weight: 600; + line-height: 1.3; + color: var(--text-primary); +} + +h1 { + font-size: var(--text-4xl); + font-weight: 700; +} + +h2 { + font-size: var(--text-3xl); +} + +h3 { + font-size: var(--text-2xl); +} + +h4 { + font-size: var(--text-xl); +} + +h5 { + font-size: var(--text-lg); +} + +h6 { + font-size: var(--text-base); +} + +p { + margin: 0 0 var(--space-4) 0; + color: var(--text-secondary); +} + +a { + color: var(--frequency-primary); + text-decoration: none; + transition: color var(--transition-fast); +} + +a:hover { + color: var(--frequency-primary-dark); + text-decoration: underline; +} + +code { + font-family: var(--font-family-mono); + font-size: 0.875em; + background-color: var(--bg-tertiary); + padding: var(--space-1) var(--space-2); + border-radius: var(--radius-sm); + border: 1px solid var(--border-light); +} + +/* ===== RESPONSIVE LAYOUT COMPONENTS ===== */ +.frequency-container { + width: 100%; + margin: 0 auto; + padding: 0 var(--space-4); +} + +.frequency-container-narrow { + width: 100%; + margin: 0 auto; + padding: 0 var(--space-4); +} + +/* Phone (up to 768px) */ +@media (max-width: 768px) { + .frequency-container { + max-width: 100%; + padding: 0 var(--space-4); + } + + .frequency-container-narrow { + max-width: 100%; + padding: 0 var(--space-4); + } +} + +/* Tablet (768px to 1024px) */ +@media (min-width: 769px) and (max-width: 1024px) { + .frequency-container { + max-width: var(--container-md); + padding: 0 var(--space-6); + } + + .frequency-container-narrow { + max-width: 600px; + padding: 0 var(--space-6); + } +} + +/* Desktop (1024px and above) - Full screen width */ +@media (min-width: 1025px) { + .frequency-container { + max-width: 100%; + padding: 0 var(--space-8); + } + + .frequency-container-narrow { + max-width: 100%; + padding: 0 var(--space-8); + } +} + +/* ===== RESPONSIVE GRID SYSTEM ===== */ +.frequency-grid { + display: grid; + gap: var(--space-6); +} + +.frequency-grid-1 { + grid-template-columns: 1fr; +} + +.frequency-grid-2 { + grid-template-columns: 1fr; +} + +.frequency-grid-3 { + grid-template-columns: 1fr; +} + +/* Tablet and up */ +@media (min-width: 769px) { + .frequency-grid-2 { + grid-template-columns: repeat(2, 1fr); + } + + .frequency-grid-3 { + grid-template-columns: repeat(2, 1fr); + } +} + +/* Desktop and up */ +@media (min-width: 1025px) { + .frequency-grid-3 { + grid-template-columns: repeat(3, 1fr); + } +} + +/* ===== CARD COMPONENTS ===== */ +.frequency-card { + background: var(--bg-primary); + border: 1px solid var(--border-light); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-sm); + overflow: hidden; + transition: all var(--transition-normal); +} + +.frequency-card:hover { + box-shadow: var(--shadow-md); + border-color: var(--border-medium); +} + +.frequency-card-header { + padding: var(--space-6); + border-bottom: 1px solid var(--border-light); + background: var(--bg-secondary); +} + +.frequency-card-content { + padding: var(--space-6); +} + +.frequency-card-footer { + padding: var(--space-6); + border-top: 1px solid var(--border-light); + background: var(--bg-secondary); +} + +/* Responsive card padding */ +@media (max-width: 768px) { + .frequency-card-header, + .frequency-card-content, + .frequency-card-footer { + padding: var(--space-4); + } +} + +/* ===== RESPONSIVE BUTTON COMPONENTS ===== */ +.frequency-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--space-2); + padding: var(--space-3) var(--space-6); + border: 1px solid transparent; + border-radius: var(--radius-md); + font-size: var(--text-sm); + font-weight: 500; + text-decoration: none; + cursor: pointer; + transition: all var(--transition-fast); + user-select: none; + min-height: 44px; /* Touch-friendly minimum */ +} + +.frequency-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.frequency-btn-primary { + background: var(--frequency-primary); + color: var(--text-inverse); + border-color: var(--frequency-primary); +} + +.frequency-btn-primary:hover:not(:disabled) { + background: var(--frequency-primary-dark); + border-color: var(--frequency-primary-dark); + color: var(--text-inverse); + text-decoration: none; + transform: translateY(-1px); + box-shadow: var(--shadow-md); +} + +.frequency-btn-secondary { + background: var(--bg-primary); + color: var(--text-primary); + border-color: var(--border-medium); +} + +.frequency-btn-secondary:hover:not(:disabled) { + background: var(--bg-secondary); + border-color: var(--border-dark); + color: var(--text-primary); + text-decoration: none; +} + +.frequency-btn-lg { + padding: var(--space-4) var(--space-8); + font-size: var(--text-base); + min-height: 48px; +} + +.frequency-btn-sm { + padding: var(--space-2) var(--space-4); + font-size: var(--text-xs); + min-height: 36px; +} + +/* Mobile button adjustments */ +@media (max-width: 768px) { + .frequency-btn { + padding: var(--space-4) var(--space-6); + font-size: var(--text-base); + min-height: 48px; + } + + .frequency-btn-lg { + padding: var(--space-5) var(--space-8); + font-size: var(--text-lg); + min-height: 52px; + } +} + +/* ===== RESPONSIVE FORM COMPONENTS ===== */ +.frequency-form-group { + margin-bottom: var(--space-6); +} + +.frequency-label { + display: block; + font-size: var(--text-sm); + font-weight: 500; + color: var(--text-primary); + margin-bottom: var(--space-2); +} + +.frequency-input { + width: 100%; + padding: var(--space-3) var(--space-4); + border: 1px solid var(--border-medium); + border-radius: var(--radius-md); + font-size: var(--text-base); + background: var(--bg-primary); + color: var(--text-primary); + transition: all var(--transition-fast); + min-height: 44px; /* Touch-friendly */ +} + +.frequency-input:focus { + outline: none; + border-color: var(--frequency-primary); + box-shadow: 0 0 0 3px rgba(91, 69, 255, 0.1); +} + +.frequency-input:disabled { + background: var(--bg-tertiary); + color: var(--text-tertiary); + cursor: not-allowed; +} + +/* Mobile form adjustments */ +@media (max-width: 768px) { + .frequency-input { + padding: var(--space-4); + font-size: var(--text-base); + min-height: 48px; + } +} + +/* ===== STATUS INDICATORS ===== */ +.frequency-badge { + display: inline-flex; + align-items: center; + padding: var(--space-1) var(--space-3); + font-size: var(--text-xs); + font-weight: 500; + border-radius: var(--radius-full); + text-transform: uppercase; + letter-spacing: 0.025em; +} + +.frequency-badge-success { + background: rgba(16, 185, 129, 0.1); + color: var(--success); +} + +.frequency-badge-warning { + background: rgba(245, 158, 11, 0.1); + color: var(--warning); +} + +.frequency-badge-error { + background: rgba(239, 68, 68, 0.1); + color: var(--error); +} + +.frequency-badge-info { + background: rgba(59, 130, 246, 0.1); + color: var(--info); +} + +/* ===== RESPONSIVE UTILITIES ===== */ +.responsive-hide-phone { + display: block; +} + +.responsive-hide-tablet { + display: block; +} + +.responsive-hide-desktop { + display: block; +} + +@media (max-width: 768px) { + .responsive-hide-phone { + display: none !important; + } +} + +@media (min-width: 769px) and (max-width: 1024px) { + .responsive-hide-tablet { + display: none !important; + } +} + +@media (min-width: 1025px) { + .responsive-hide-desktop { + display: none !important; + } +} + +.responsive-show-phone { + display: none; +} + +.responsive-show-tablet { + display: none; +} + +.responsive-show-desktop { + display: none; +} + +@media (max-width: 768px) { + .responsive-show-phone { + display: block !important; + } +} + +@media (min-width: 769px) and (max-width: 1024px) { + .responsive-show-tablet { + display: block !important; + } +} + +@media (min-width: 1025px) { + .responsive-show-desktop { + display: block !important; + } +} + +/* ===== ANIMATIONS ===== */ +@keyframes frequency-fade-in { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes frequency-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.frequency-animate-fade-in { + animation: frequency-fade-in 0.5s ease-out; +} + +.frequency-animate-spin { + animation: frequency-spin 1s linear infinite; +} + +/* ===== DARK MODE SUPPORT ===== */ +@media (prefers-color-scheme: dark) { + :root { + --bg-primary: var(--gray-900); + --bg-secondary: var(--gray-800); + --bg-tertiary: var(--gray-700); + --text-primary: var(--gray-100); + --text-secondary: var(--gray-300); + --text-tertiary: var(--gray-500); + --border-light: var(--gray-700); + --border-medium: var(--gray-600); + --border-dark: var(--gray-500); + } +} + +/* Mock Authenticator Styles */ +.mock-authenticator { + width: 100%; + max-width: 600px; + margin: 0 auto; +} + +.mock-auth-choose { + text-align: center; + padding: var(--space-6); +} + +.mock-auth-choose .auth-header { + margin-bottom: var(--space-6); +} + +.mock-auth-choose .auth-header h2 { + margin: 0 0 var(--space-2) 0; + color: var(--text-primary); + font-size: var(--text-xl); + font-weight: 600; +} + +.mock-auth-choose .auth-header p { + margin: 0; + color: var(--text-secondary); + font-size: var(--text-base); +} + +.mock-auth-choose .auth-buttons { + display: flex; + flex-direction: column; + gap: var(--space-3); + margin-bottom: var(--space-6); +} + +.mock-auth-choose .auth-note { + padding: var(--space-4); + background-color: rgba(245, 158, 11, 0.1); + border: 1px solid rgba(245, 158, 11, 0.2); + border-radius: var(--radius-md); + font-size: var(--text-sm); + color: var(--text-secondary); + text-align: left; +} + +.mock-auth-form { + padding: var(--space-6); +} + +.mock-auth-form .auth-header { + margin-bottom: var(--space-6); +} + +.mock-auth-form .auth-header h2 { + margin: 0 0 var(--space-2) 0; + color: var(--text-primary); + font-size: var(--text-xl); + font-weight: 600; + display: flex; + align-items: center; + gap: var(--space-2); +} + +.mock-auth-form .auth-header p { + margin: 0; + color: var(--text-secondary); + font-size: var(--text-base); +} + +.mock-auth-form .auth-form { + display: flex; + flex-direction: column; + gap: var(--space-4); +} + +.mock-auth-form .form-group { + display: flex; + flex-direction: column; + gap: var(--space-2); +} + +.mock-auth-form .form-group label { + font-weight: 600; + color: var(--text-primary); + font-size: var(--text-sm); + display: flex; + align-items: center; + gap: var(--space-2); +} + +.mock-auth-form .form-group .required { + color: var(--error); +} + +.mock-auth-form .form-input { + padding: var(--space-3); + border: 1px solid var(--border-light); + border-radius: var(--radius-md); + font-size: var(--text-base); + background-color: var(--bg-primary); + color: var(--text-primary); + transition: border-color 0.2s ease; +} + +.mock-auth-form .form-input:focus { + outline: none; + border-color: var(--frequency-primary); + box-shadow: 0 0 0 3px rgba(var(--frequency-primary-rgb), 0.1); +} + +.mock-authenticator .auth-footer { + padding: var(--space-4); + border-top: 1px solid var(--border-light); + display: flex; + justify-content: center; +} + +/* Responsive adjustments for mobile */ +@media (max-width: 768px) { + .mock-auth-choose .auth-buttons { + gap: var(--space-2); + } + + .mock-auth-form { + padding: var(--space-4); + } + + .mock-auth-choose { + padding: var(--space-4); + } + + .mock-auth-choose .auth-header h2 { + font-size: var(--text-lg); + } + + .mock-auth-form .auth-header h2 { + font-size: var(--text-lg); + } +} \ No newline at end of file diff --git a/siwf-v2-reference-app/src/hooks/useWallet.ts b/siwf-v2-reference-app/src/hooks/useWallet.ts new file mode 100644 index 00000000..41ef9716 --- /dev/null +++ b/siwf-v2-reference-app/src/hooks/useWallet.ts @@ -0,0 +1,267 @@ +import { useState, useEffect, useCallback } from 'react'; +import { ethers } from 'ethers'; +import { web3Enable, web3Accounts, web3FromAddress } from '@polkadot/extension-dapp'; +import { stringToHex } from '@polkadot/util'; +import toast from 'react-hot-toast'; +import type { WalletState, WalletType, PolkadotAccount } from '../types'; + +// Extend window to include injected wallets +declare global { + interface Window { + ethereum?: any; + injectedWeb3?: Record; + } +} + +// LocalStorage keys for persisting wallet state +const WALLET_DISCONNECTED_KEY = 'siwf_wallet_disconnected'; +const WALLET_TYPE_KEY = 'siwf_wallet_type'; + +export const useWallet = () => { + const [wallet, setWallet] = useState({ + isConnected: false, + account: null, + walletType: null, + isConnecting: false, + error: null, + }); + + // Check if wallets are available + const isMetaMaskAvailable = () => typeof window.ethereum !== 'undefined'; + const isPolkadotAvailable = () => typeof window.injectedWeb3 !== 'undefined'; + + // Check if user previously disconnected + const wasManuallyDisconnected = () => { + return localStorage.getItem(WALLET_DISCONNECTED_KEY) === 'true'; + }; + + // Clear disconnect flag and store wallet type + const setConnected = (walletType: WalletType, account: string) => { + localStorage.removeItem(WALLET_DISCONNECTED_KEY); + localStorage.setItem(WALLET_TYPE_KEY, walletType); + setWallet({ + isConnected: true, + account, + walletType, + isConnecting: false, + error: null, + }); + }; + + // Set disconnect flag and clear wallet type + const setDisconnected = () => { + localStorage.setItem(WALLET_DISCONNECTED_KEY, 'true'); + localStorage.removeItem(WALLET_TYPE_KEY); + setWallet({ + isConnected: false, + account: null, + walletType: null, + isConnecting: false, + error: null, + }); + }; + + // Check existing connections on load + useEffect(() => { + // Don't auto-connect if user previously disconnected + if (wasManuallyDisconnected()) { + console.log('🚫 Skipping auto-connection - user previously disconnected'); + return; + } + + checkExistingConnections(); + }, []); + + const checkExistingConnections = async () => { + // Get the last used wallet type + const lastWalletType = localStorage.getItem(WALLET_TYPE_KEY) as WalletType; + + // Check MetaMask first if it was the last used wallet or if no preference + if ((!lastWalletType || lastWalletType === 'metamask') && isMetaMaskAvailable()) { + try { + const provider = new ethers.BrowserProvider(window.ethereum); + const accounts = await provider.listAccounts(); + + if (accounts.length > 0) { + setConnected('metamask', accounts[0].address); + return; + } + } catch (error) { + console.error('Error checking MetaMask connection:', error); + } + } + + // Check Polkadot.js if MetaMask wasn't connected or if it was the last used wallet + if (lastWalletType === 'polkadot' || !lastWalletType) { + try { + const extensions = await web3Enable('SIWF Frontend'); + if (extensions.length > 0) { + const accounts = await web3Accounts(); + if (accounts.length > 0) { + setConnected('polkadot', accounts[0].address); + } + } + } catch (error) { + console.error('Error checking Polkadot.js connection:', error); + } + } + }; + + const connectMetaMask = useCallback(async () => { + if (!isMetaMaskAvailable()) { + const error = 'Please install MetaMask wallet extension'; + setWallet(prev => ({ ...prev, error })); + toast.error(error); + return; + } + + setWallet(prev => ({ ...prev, isConnecting: true, error: null })); + + try { + await window.ethereum.request({ method: 'eth_requestAccounts' }); + + const provider = new ethers.BrowserProvider(window.ethereum); + const signer = await provider.getSigner(); + const address = await signer.getAddress(); + + setConnected('metamask', address); + toast.success(`Connected to MetaMask: ${address.slice(0, 6)}...${address.slice(-4)}`); + } catch (error: any) { + console.error('Error connecting MetaMask:', error); + const errorMessage = 'Failed to connect MetaMask: ' + error.message; + setWallet(prev => ({ ...prev, isConnecting: false, error: errorMessage })); + toast.error(errorMessage); + } + }, []); + + const connectPolkadot = useCallback(async () => { + setWallet(prev => ({ ...prev, isConnecting: true, error: null })); + + try { + // Enable the extension + const extensions = await web3Enable('SIWF Frontend'); + + if (extensions.length === 0) { + throw new Error('Please install Polkadot.js extension'); + } + + // Get accounts + const accounts = await web3Accounts(); + + if (accounts.length === 0) { + throw new Error('No accounts found in Polkadot.js extension'); + } + + // Use the first account for now + const account = accounts[0]; + + setConnected('polkadot', account.address); + toast.success(`Connected to Polkadot.js: ${account.address.slice(0, 6)}...${account.address.slice(-4)}`); + } catch (error: any) { + console.error('Error connecting Polkadot.js:', error); + const errorMessage = 'Failed to connect Polkadot.js: ' + error.message; + setWallet(prev => ({ ...prev, isConnecting: false, error: errorMessage })); + toast.error(errorMessage); + } + }, []); + + const disconnectWallet = useCallback(() => { + setDisconnected(); + toast.success('Wallet disconnected'); + }, []); + + // Sign typed data (EIP-712) - MetaMask only + const signTypedData = useCallback(async (typedData: any) => { + if (!wallet.isConnected || !wallet.account) { + throw new Error('Wallet not connected'); + } + + if (wallet.walletType !== 'metamask') { + throw new Error('Typed data signing only supported with MetaMask'); + } + + try { + const provider = new ethers.BrowserProvider(window.ethereum); + const signer = await provider.getSigner(); + + const signature = await signer.signTypedData( + typedData.domain, + typedData.types, + typedData.message + ); + return signature; + } catch (error) { + console.error('Error signing typed data:', error); + throw error; + } + }, [wallet]); + + // Sign message - works with both wallets + const signMessage = useCallback(async (message: string) => { + if (!wallet.isConnected || !wallet.account) { + throw new Error('Wallet not connected'); + } + + try { + if (wallet.walletType === 'metamask') { + const provider = new ethers.BrowserProvider(window.ethereum); + const signer = await provider.getSigner(); + return await signer.signMessage(message); + } else if (wallet.walletType === 'polkadot') { + const injector = await web3FromAddress(wallet.account); + const signRaw = injector?.signer?.signRaw; + + if (!signRaw) { + throw new Error('Polkadot.js signer not available'); + } + + const { signature } = await signRaw({ + address: wallet.account, + data: stringToHex(message), + type: 'bytes' + }); + + return signature; + } else { + throw new Error('Unknown wallet type'); + } + } catch (error) { + console.error('Error signing message:', error); + throw error; + } + }, [wallet]); + + // Listen for account changes in MetaMask + useEffect(() => { + if (window.ethereum && wallet.walletType === 'metamask') { + const handleAccountsChanged = (accounts: string[]) => { + if (accounts.length === 0) { + // User disconnected from MetaMask + setDisconnected(); + toast('MetaMask account disconnected'); + } else if (accounts[0] !== wallet.account) { + // User switched accounts + setConnected('metamask', accounts[0]); + toast(`Switched to account: ${accounts[0].slice(0, 6)}...${accounts[0].slice(-4)}`); + } + }; + + window.ethereum.on('accountsChanged', handleAccountsChanged); + + return () => { + window.ethereum.removeListener('accountsChanged', handleAccountsChanged); + }; + } + }, [wallet.walletType, wallet.account]); + + return { + wallet, + isMetaMaskAvailable: isMetaMaskAvailable(), + isPolkadotAvailable: isPolkadotAvailable(), + connectMetaMask, + connectPolkadot, + disconnectWallet, + signTypedData, + signMessage, + }; +}; \ No newline at end of file diff --git a/siwf-v2-reference-app/src/index.css b/siwf-v2-reference-app/src/index.css new file mode 100644 index 00000000..5a9c5899 --- /dev/null +++ b/siwf-v2-reference-app/src/index.css @@ -0,0 +1,86 @@ +/* Import Frequency Design System */ +@import './frequency-theme.css'; + +/* Import Inter font for better typography */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); + +/* Override default styling with Frequency design system */ +:root { + font-family: var(--font-family-sans); + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: var(--text-primary); + background-color: var(--bg-secondary); + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: var(--frequency-primary); + text-decoration: inherit; +} + +a:hover { + color: var(--frequency-primary-dark); +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; + background-color: var(--bg-secondary); +} + +h1 { + font-size: var(--text-4xl); + line-height: 1.1; + color: var(--text-primary); + font-weight: 700; +} + +button { + border-radius: var(--radius-md); + border: 1px solid transparent; + padding: var(--space-3) var(--space-6); + font-size: var(--text-sm); + font-weight: 500; + font-family: inherit; + background-color: var(--frequency-primary); + color: var(--text-inverse); + cursor: pointer; + transition: all var(--transition-fast); +} + +button:hover { + background-color: var(--frequency-primary-dark); + transform: translateY(-1px); +} + +button:focus, +button:focus-visible { + outline: 2px solid var(--frequency-primary); + outline-offset: 2px; +} + +/* Dark mode adjustments */ +@media (prefers-color-scheme: dark) { + a:hover { + color: var(--frequency-primary-light); + } + + button { + background-color: var(--frequency-primary); + } + + button:hover { + background-color: var(--frequency-primary-light); + } +} diff --git a/siwf-v2-reference-app/src/main.tsx b/siwf-v2-reference-app/src/main.tsx new file mode 100644 index 00000000..bef5202a --- /dev/null +++ b/siwf-v2-reference-app/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/siwf-v2-reference-app/src/types.ts b/siwf-v2-reference-app/src/types.ts new file mode 100644 index 00000000..0f3fec9c --- /dev/null +++ b/siwf-v2-reference-app/src/types.ts @@ -0,0 +1,64 @@ +// Wallet Types +export type WalletType = 'metamask' | 'polkadot'; + +export interface WalletState { + isConnected: boolean; + account: string | null; + walletType: WalletType | null; + isConnecting: boolean; + error: string | null; +} + +export interface PolkadotAccount { + address: string; + meta: { + name?: string; + source: string; + }; +} + +// SIWF Types +export interface SiwfOptions { + handle: string; + email: string; +} + +export interface SiwfResult { + msaId?: string; + accountId?: string; + handle?: string; + credentials?: any; + isNewUser?: boolean; + authMode?: string; + mockAuthData?: { + authorizationCode: string; + authorizationPayload: string; + }; + accountResponse?: any; +} + +export interface SiwfState { + isLoading: boolean; + result: SiwfResult | null; + error: string | null; +} + +// API Response Types +export interface GatewayResponse { + data?: T; + error?: string; + message?: string; +} + +export interface MsaResponse { + msaId: string; + providerId: string; +} + +// Signature Function Types +export interface SignatureParams { + account: string; + walletType: WalletType; +} + +export type SignatureFunction = (data: any, params: SignatureParams) => Promise; \ No newline at end of file diff --git a/siwf-v2-reference-app/src/utils/siwf.ts b/siwf-v2-reference-app/src/utils/siwf.ts new file mode 100644 index 00000000..9cbbac86 --- /dev/null +++ b/siwf-v2-reference-app/src/utils/siwf.ts @@ -0,0 +1,386 @@ +// Complete SIWF v2 Implementation +// Based on: https://projectlibertylabs.github.io/siwf/v2/docs/ + +import type { + SiwfOptions, + SiwfResult, + WalletType, + GatewayResponse, + MsaResponse +} from '../types'; + +// SIWF Permission definitions from documentation +export const SIWF_PERMISSIONS = { + // Bundles + PUBLIC_PRIVATE_GRAPH: [6, 7, 8, 9, 10], + DSNP_V13_CONTENT: [1, 2, 3, 4, 5], + + // Individual permissions + DSNP_BROADCAST_V1: 1, // Create new public content (v1) - Deprecated + DSNP_PROFILE_V1: 2, // Update profile information (v1) - Deprecated + DSNP_REPLY_V1: 3, // Public reply to content (v1) - Deprecated + DSNP_TOMBSTONE_V1: 4, // Mark content for deletion (v1) - Deprecated + DSNP_UPDATE_V1: 5, // Update an existing post or reply (v1) + DSNP_BROADCAST_V2: 6, // Create new public content (v2) + DSNP_CONTENT_ATTRIBUTE: 7, // Create authenticated attribute set for DSNP content (v1) + DSNP_EXT_CONTENT_ATTRIBUTE: 8, // Create authenticated attribute set for external content (v1) + DSNP_PRIVATE_CONNECTIONS: 9, // Update private friendship connections (v1) + DSNP_PRIVATE_FOLLOWS: 10, // Update private follow list (v1) + DSNP_PROFILE_RESOURCES: 11, // Update user profile information (v1) + DSNP_PUBLIC_FOLLOWS: 12, // Update public follow list (v1) + DSNP_REACTION: 13, // Public reaction to content (v1) + DSNP_REPLY_V2: 14, // Public reply to content (v2) + DSNP_TOMBSTONE_V2: 15, // Mark content for deletion (v2) + DSNP_UPDATE_V2: 16, // Update existing post or reply (v2) + DSNP_USER_ATTRIBUTE_SET: 17 // Create authenticated attribute set for DSNP User (v2) +}; + +// Credential definitions +export const SIWF_CREDENTIALS = { + GRAPH_KEY: { + type: "VerifiedGraphKeyCredential", + hash: ["bciqmdvmxd54zve5kifycgsdtoahs5ecf4hal2ts3eexkgocyc5oca2y"] + }, + RECOVERY_SECRET: { + type: "VerifiedRecoverySecretCredential", + hash: ["bciqpg6qm4rnu2j4v6ghxqqgwkggokwvxs3t2bexbd3obkypkiryylxq"] + }, + EMAIL: { + type: "VerifiedEmailAddressCredential", + hash: ["bciqe4qoczhftici4dzfvfbel7fo4h4sr5grco3oovwyk6y4ynf44tsi"] + }, + PHONE: { + type: "VerifiedPhoneNumberCredential", + hash: ["bciqjspnbwpc3wjx4fewcek5daysdjpbf5xjimz5wnu5uj7e3vu2uwnq"] + } +}; + +// Types for SIWF request generation +export interface SiwfRequestOptions { + callbackUri: string; + permissions: number[]; + additionalPermissions?: string; + schemaIds?: number[]; + credentials: { + graphKey: boolean; + recoverySecret: boolean; + email: boolean; + phone: boolean; + }; + applicationContextUrl?: string; +} + +export interface SiwfSignedRequest { + requestedSignatures: { + publicKey: { + encodedValue: string; + encoding: string; + format: string; + type: string; + }; + signature: { + algo: string; + encoding: string; + encodedValue: string; + }; + payload: { + callback: string; + permissions: number[]; + userIdentifierAdminUrl?: string; + }; + }; + requestedCredentials?: any[]; + applicationContext?: { + url: string; + }; +} + +export interface SiwfGeneratedRequest { + signedRequest: string; + mainnetUrl: string; + testnetUrl: string; + jsonPayload: SiwfSignedRequest; +} + +/** + * Generate SIWF v2 Signed Request + * Based on: https://projectlibertylabs.github.io/siwf/v2/docs/SignatureGeneration.html + */ +export function generateSiwfSignedRequest( + options: SiwfRequestOptions, + walletInfo?: { account: string; walletType: WalletType } +): SiwfGeneratedRequest { + console.log("🔐 Generating SIWF signed request with options:", options); + console.log("🔐 Using wallet info:", walletInfo); + + try { + // Build requested credentials array + const requestedCredentials = []; + + // Add individual credentials + if (options.credentials.graphKey) { + requestedCredentials.push(SIWF_CREDENTIALS.GRAPH_KEY); + } + + if (options.credentials.recoverySecret) { + requestedCredentials.push(SIWF_CREDENTIALS.RECOVERY_SECRET); + } + + // Add anyOf credentials (email or phone) + if (options.credentials.email || options.credentials.phone) { + const anyOfCredentials = []; + + if (options.credentials.email) { + anyOfCredentials.push(SIWF_CREDENTIALS.EMAIL); + } + + if (options.credentials.phone) { + anyOfCredentials.push(SIWF_CREDENTIALS.PHONE); + } + + if (anyOfCredentials.length > 0) { + requestedCredentials.push({ anyOf: anyOfCredentials }); + } + } + + // For demo purposes, we use consistent mock data for both public key and signature + // In a real implementation, you would: + // 1. Use the actual wallet's public key + // 2. Sign the payload with the actual wallet's private key + // 3. The signature would cryptographically match the public key + + const publicKeyInfo = { + encodedValue: "f6cL4wq1HUNx11TcvdABNf9UNXXoyH47mVUwT59tzSFRW8yDH", // Mock public key + encoding: "base58", + format: "ss58", + type: "Sr25519" + }; + + // Log the connected wallet info for debugging (but use mock data for the actual request) + if (walletInfo?.account) { + console.log(`🔐 Connected wallet: ${walletInfo.walletType} (${walletInfo.account})`); + console.log("⚠️ Note: Using mock keys for demo - in production, would use actual wallet signing"); + } else { + console.log("⚠️ No wallet connected, using mock public key"); + } + + // Build the complete signed request + const signedRequestObj: SiwfSignedRequest = { + requestedSignatures: { + publicKey: publicKeyInfo, + signature: { + algo: "SR25519", + encoding: "base16", + encodedValue: "0x" + "a".repeat(128) // Mock signature for demo - matches mock public key + }, + payload: { + callback: options.callbackUri, + permissions: options.permissions + } + } + }; + + // Add optional fields + if (requestedCredentials.length > 0) { + signedRequestObj.requestedCredentials = requestedCredentials; + } + + if (options.applicationContextUrl) { + signedRequestObj.applicationContext = { + url: options.applicationContextUrl + }; + } + + // Encode to base64url + const signedRequest = encodeSignedRequest(signedRequestObj); + + // Generate URLs + const mainnetUrl = generateAuthenticationUrl(signedRequest, undefined, 'production'); + const testnetUrl = generateAuthenticationUrl(signedRequest, undefined, 'staging'); + + console.log("✅ SIWF signed request generated successfully"); + + return { + signedRequest, + mainnetUrl, + testnetUrl, + jsonPayload: signedRequestObj + }; + + } catch (error) { + console.error("❌ Failed to generate SIWF signed request:", error); + throw error; + } +} + +/** + * Encode signed request to base64url + */ +function encodeSignedRequest(signedRequest: SiwfSignedRequest): string { + const jsonString = JSON.stringify(signedRequest); + return btoa(jsonString) + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, ''); +} + +/** + * Generate authentication URL + */ +function generateAuthenticationUrl( + signedRequest: string, + additionalParams?: URLSearchParams, + endpoint: 'production' | 'staging' = 'staging' +): string { + const baseUrl = endpoint === 'production' + ? 'https://www.frequencyaccess.com/siwa/start' + : 'https://testnet.frequencyaccess.com/siwa/start'; + + const params = new URLSearchParams(); + params.set('signedRequest', signedRequest); + + if (additionalParams) { + additionalParams.forEach((value, key) => { + params.set(key, value); + }); + } + + return `${baseUrl}?${params.toString()}`; +} + +/** + * Handle SIWF callback and extract authorization code + */ +export function handleSiwfCallback(callbackUrl: string): { + authorizationCode?: string; + sessionId?: string; + additionalParams: Record; +} { + const url = new URL(callbackUrl); + const params = new URLSearchParams(url.search); + + const authorizationCode = params.get('authorizationCode') || undefined; + const sessionId = params.get('sessionId') || undefined; + + const additionalParams: Record = {}; + params.forEach((value, key) => { + if (key !== 'authorizationCode' && key !== 'sessionId') { + additionalParams[key] = value; + } + }); + + return { + authorizationCode, + sessionId, + additionalParams + }; +} + +/** + * Retrieve login result using authorization code + */ +export async function getLoginResult( + authorizationCode: string, + endpoint: 'production' | 'staging' = 'staging' +): Promise { + const baseUrl = endpoint === 'production' + ? 'https://www.frequencyaccess.com/siwa/api/payload' + : 'https://testnet.frequencyaccess.com/siwa/api/payload'; + + const url = `${baseUrl}?authorizationCode=${encodeURIComponent(authorizationCode)}`; + + console.log('🔍 Fetching SIWF login result:', url); + + try { + const response = await fetch(url, { + method: 'GET', + headers: { 'Accept': 'application/json' }, + }); + + if (!response.ok) { + throw new Error(`SIWF API error: ${response.status} ${response.statusText}`); + } + + const result = await response.json(); + console.log('✅ SIWF login result received'); + + return result; + } catch (error) { + console.error('❌ Failed to get SIWF login result:', error); + throw error; + } +} + +/** + * Initialize SIWF callback handler + * Call this to check if the current page is a SIWF callback + */ +export function initializeSiwfCallback( + onSuccess: (result: any) => void, + onError: (error: Error) => void +): void { + const urlParams = new URLSearchParams(window.location.search); + const authorizationCode = urlParams.get('authorizationCode'); + + if (authorizationCode) { + console.log("🔄 SIWF callback detected, processing..."); + + getLoginResult(authorizationCode, 'staging') + .then(result => { + console.log("✅ SIWF callback processed successfully:", result); + onSuccess(result); + }) + .catch(error => { + console.error("❌ SIWF callback processing failed:", error); + onError(error); + }); + } +} + +// Legacy compatibility functions +export function validateAccountId(accountId: string, walletType: WalletType): boolean { + if (walletType === 'metamask') { + return /^0x[a-fA-F0-9]{40}$/.test(accountId); + } else if (walletType === 'polkadot') { + return accountId.length >= 40; + } + return false; +} + +export function createSignatureFn( + signTypedData: (data: any) => Promise, + signMessage: (message: string) => Promise, + walletType: WalletType, + account: string +) { + return async (request: any, accountId: string): Promise => { + if (walletType === 'metamask') { + if (request.method === "eth_signTypedData_v4") { + const [address, typedDataString] = request.params; + const typedData = JSON.parse(typedDataString); + return await signTypedData(typedData); + } + if (request.method === "personal_sign") { + const [message, address] = request.params; + return await signMessage(message); + } + } else if (walletType === 'polkadot') { + if (request.method === "personal_sign") { + const [message, address] = request.params; + return await signMessage(message); + } + } + throw new Error(`Unsupported signing method: ${request.method}`); + }; +} + +export async function siwfLogin( + options: SiwfOptions, + signatureFn: (...args: any[]) => Promise, + onMsaCreated: (account: any) => void, + accountId: string, + walletType: WalletType +): Promise { + console.log("🚀 SIWF Login - use the SIWF Request Generator instead"); + throw new Error("Please use the SIWF Request Generator to start authentication"); +} \ No newline at end of file diff --git a/siwf-v2-reference-app/src/vite-env.d.ts b/siwf-v2-reference-app/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/siwf-v2-reference-app/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/siwf-v2-reference-app/tsconfig.app.json b/siwf-v2-reference-app/tsconfig.app.json new file mode 100644 index 00000000..227a6c67 --- /dev/null +++ b/siwf-v2-reference-app/tsconfig.app.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/siwf-v2-reference-app/tsconfig.json b/siwf-v2-reference-app/tsconfig.json new file mode 100644 index 00000000..1ffef600 --- /dev/null +++ b/siwf-v2-reference-app/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/siwf-v2-reference-app/tsconfig.node.json b/siwf-v2-reference-app/tsconfig.node.json new file mode 100644 index 00000000..f85a3990 --- /dev/null +++ b/siwf-v2-reference-app/tsconfig.node.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/siwf-v2-reference-app/vite.config.ts b/siwf-v2-reference-app/vite.config.ts new file mode 100644 index 00000000..f29909b9 --- /dev/null +++ b/siwf-v2-reference-app/vite.config.ts @@ -0,0 +1,22 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + server: { + proxy: { + // Proxy Gateway API requests to avoid CORS issues + '/api': { + target: 'http://localhost:3013', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ''), + configure: (proxy, _options) => { + proxy.on('error', (err, _req, _res) => { + console.log('Gateway proxy error:', err); + }); + }, + }, + }, + }, +}) From e97f1ecf3a13326236757b78f5d49cc50343f797 Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Wed, 23 Jul 2025 15:33:36 -0400 Subject: [PATCH 02/16] Simplify re-render logic --- siwf-v2-reference-app/src/App.tsx | 38 +----- .../src/components/SiwfLogin.tsx | 123 +----------------- siwf-v2-reference-app/src/hooks/useWallet.ts | 4 +- 3 files changed, 16 insertions(+), 149 deletions(-) diff --git a/siwf-v2-reference-app/src/App.tsx b/siwf-v2-reference-app/src/App.tsx index 7efc9d3d..f94d9906 100644 --- a/siwf-v2-reference-app/src/App.tsx +++ b/siwf-v2-reference-app/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { Toaster } from 'react-hot-toast'; import toast from 'react-hot-toast'; import { WalletConnect } from './components/WalletConnect'; @@ -9,10 +9,6 @@ import './App.css'; function App() { const { wallet } = useWallet(); - const [rerenderCounter, setRerenderCounter] = useState(0); - - // Create a unique key based on wallet state to force re-rendering on wallet changes - const walletKey = `${wallet.walletType || 'none'}-${wallet.account || 'none'}-${wallet.isConnected}-${rerenderCounter}`; // Initialize SIWF callback handling on mount useEffect(() => { @@ -28,18 +24,6 @@ function App() { ); }, []); - // Force re-render when wallet state changes - useEffect(() => { - console.log('🔄 App: Wallet state changed, forcing component re-render', { - walletType: wallet.walletType, - account: wallet.account?.slice(0, 8) + '...', - isConnected: wallet.isConnected, - newKey: walletKey - }); - - setRerenderCounter(prev => prev + 1); - }, [wallet.account, wallet.walletType, wallet.isConnected]); - return (
-

Sign In With Frequency(SIWF) v2

+

SIWF v2

- Demo Application • Frequency Blockchain Integration - SIWF Demo + Reference App + Reference App

@@ -105,10 +89,10 @@ function App() {
- +
- +
@@ -138,16 +122,6 @@ function App() { SIWF -
- - - Render #{rerenderCounter} • {wallet.walletType || 'No Wallet'} • {wallet.isConnected ? '🟢' : '🔴'} - - - #{rerenderCounter} - - -
diff --git a/siwf-v2-reference-app/src/components/SiwfLogin.tsx b/siwf-v2-reference-app/src/components/SiwfLogin.tsx index 59d7b11b..e6f7009f 100644 --- a/siwf-v2-reference-app/src/components/SiwfLogin.tsx +++ b/siwf-v2-reference-app/src/components/SiwfLogin.tsx @@ -1,5 +1,5 @@ -import React, { useState, useEffect, useRef } from 'react'; -import { Key, User, Mail, Loader, CheckCircle, XCircle, AlertTriangle, RefreshCw } from 'lucide-react'; +import React, { useState, useEffect } from 'react'; +import { Key, User, Mail, Loader, CheckCircle, XCircle, AlertTriangle } from 'lucide-react'; import toast from 'react-hot-toast'; import { useWallet } from '../hooks/useWallet'; import { SiwfRequestGenerator } from './SiwfRequestGenerator'; @@ -13,94 +13,19 @@ export const SiwfLogin: React.FC = () => { result: null, error: null }); - const [isResetting, setIsResetting] = useState(false); - const [componentKey, setComponentKey] = useState(0); const [showMockAuth, setShowMockAuth] = useState(false); const [currentSignedRequest, setCurrentSignedRequest] = useState(''); - - // Track previous wallet state to detect changes - const prevWalletRef = useRef<{ - account: string | null; - walletType: string | null; - isConnected: boolean; - }>({ - account: null, - walletType: null, - isConnected: false - }); - - // Force component re-render by incrementing key - const forceRerender = () => { - setComponentKey(prev => prev + 1); - console.log('🔄 Forcing component re-render, new key:', componentKey + 1); - }; - - // Reset component state when wallet changes - useEffect(() => { - const currentWallet = { - account: wallet.account, - walletType: wallet.walletType, - isConnected: wallet.isConnected - }; - - const previousWallet = prevWalletRef.current; - - const walletChanged = - currentWallet.account !== previousWallet.account || - currentWallet.walletType !== previousWallet.walletType || - currentWallet.isConnected !== previousWallet.isConnected; - if (walletChanged) { - console.log('🔄 SiwfLogin: Wallet state CHANGED:', { - previous: previousWallet, - current: currentWallet, - timestamp: new Date().toISOString() - }); - - setIsResetting(true); - setSiwfState({ - isLoading: false, - result: null, - error: null - }); - - forceRerender(); - prevWalletRef.current = currentWallet; - } - }, [wallet.account, wallet.walletType, wallet.isConnected]); - - // Separate effect to handle reset animation timeout + // Reset SIWF state when wallet changes useEffect(() => { - if (isResetting) { - console.log('🔄 Starting reset animation...'); - - const timer = setTimeout(() => { - console.log('🔄 Clearing reset animation (primary)'); - setIsResetting(false); - }, 800); - - const safetyTimer = setTimeout(() => { - console.log('🔄 SAFETY: Force clearing reset animation'); - setIsResetting(false); - }, 3000); - - return () => { - console.log('🔄 Cleanup: clearing reset timers'); - clearTimeout(timer); - clearTimeout(safetyTimer); - }; - } - }, [isResetting]); - - // Additional effect to reset on mount - useEffect(() => { - console.log('🔄 SiwfLogin: Component mounted/remounted'); setSiwfState({ isLoading: false, result: null, error: null }); - }, []); + setShowMockAuth(false); + setCurrentSignedRequest(''); + }, [wallet.account, wallet.walletType, wallet.isConnected]); const handleStartAuthentication = (signedRequest: string) => { console.log('🚀 Starting MOCK SIWF authentication with signed request', signedRequest); @@ -154,28 +79,12 @@ export const SiwfLogin: React.FC = () => { return (
-
+

Sign In With Frequency SIWF - {isResetting && } - - #{componentKey} -

Generate and use SIWF authentication requests @@ -364,24 +273,6 @@ export const SiwfLogin: React.FC = () => { } : undefined} /> )} - - {/* Emergency reset button for debugging - only show on larger screens */} - {isResetting && ( - - )}

diff --git a/siwf-v2-reference-app/src/hooks/useWallet.ts b/siwf-v2-reference-app/src/hooks/useWallet.ts index 41ef9716..9414f384 100644 --- a/siwf-v2-reference-app/src/hooks/useWallet.ts +++ b/siwf-v2-reference-app/src/hooks/useWallet.ts @@ -35,10 +35,11 @@ export const useWallet = () => { return localStorage.getItem(WALLET_DISCONNECTED_KEY) === 'true'; }; - // Clear disconnect flag and store wallet type + // Clear disconnect flag and store wallet type const setConnected = (walletType: WalletType, account: string) => { localStorage.removeItem(WALLET_DISCONNECTED_KEY); localStorage.setItem(WALLET_TYPE_KEY, walletType); + setWallet({ isConnected: true, account, @@ -52,6 +53,7 @@ export const useWallet = () => { const setDisconnected = () => { localStorage.setItem(WALLET_DISCONNECTED_KEY, 'true'); localStorage.removeItem(WALLET_TYPE_KEY); + setWallet({ isConnected: false, account: null, From 9a5346fc01682b7e3519337f1cf326608fb03af6 Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Fri, 25 Jul 2025 17:47:21 -0400 Subject: [PATCH 03/16] Add educational tutorial tooltip that pops up on start and walks user throught the steps --- siwf-v2-reference-app/src/App.css | 113 ++++++- siwf-v2-reference-app/src/App.tsx | 84 ++++- .../src/components/MockAuthenticator.tsx | 14 +- .../src/components/SiwfLogin.tsx | 83 +++-- .../src/components/Tooltip.tsx | 247 +++++++++++++++ .../src/components/WalletConnect.tsx | 22 +- .../src/hooks/useTooltipTour.ts | 298 ++++++++++++++++++ siwf-v2-reference-app/src/hooks/useWallet.ts | 2 +- 8 files changed, 834 insertions(+), 29 deletions(-) create mode 100644 siwf-v2-reference-app/src/components/Tooltip.tsx create mode 100644 siwf-v2-reference-app/src/hooks/useTooltipTour.ts diff --git a/siwf-v2-reference-app/src/App.css b/siwf-v2-reference-app/src/App.css index c1340d9c..3985bfe2 100644 --- a/siwf-v2-reference-app/src/App.css +++ b/siwf-v2-reference-app/src/App.css @@ -689,7 +689,7 @@ html, body { /* Wallet Connection Responsive Breakpoints */ -Phone (up to 768px) +/* Phone (up to 768px) */ @media (max-width: 768px) { .connected { flex-direction: column; @@ -738,3 +738,114 @@ Phone (up to 768px) } } +/* Educational Tooltip Styles */ +.educational-tooltip { + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + animation: tooltipFadeIn 0.3s ease-out; +} + +@keyframes tooltipFadeIn { + from { + opacity: 0; + transform: scale(0.95) translateY(-5px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +.tooltip-backdrop { + animation: backdropFadeIn 0.3s ease-out; +} + +@keyframes backdropFadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +/* Tooltip highlight effect for targeted elements */ +.tooltip-highlight { + position: relative; + z-index: 9997; +} + +.tooltip-highlight::before { + content: ''; + position: absolute; + top: -4px; + left: -4px; + right: -4px; + bottom: -4px; + background: linear-gradient(135deg, var(--frequency-primary), var(--frequency-secondary)); + border-radius: calc(var(--radius-lg) + 4px); + opacity: 0.2; + animation: highlightPulse 2s infinite; + pointer-events: none; +} + +@keyframes highlightPulse { + 0%, 100% { + opacity: 0.2; + transform: scale(1); + } + 50% { + opacity: 0.4; + transform: scale(1.02); + } +} + +/* Tour button styles */ +.tour-control-button { + transition: all 0.2s ease; +} + +.tour-control-button:hover { + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +/* Responsive tooltip adjustments */ +@media (max-width: 768px) { + .educational-tooltip { + max-width: calc(100vw - 32px); + min-width: calc(100vw - 32px); + left: 16px !important; + right: 16px !important; + } + + .tooltip-backdrop { + backdrop-filter: blur(1px); + } +} + +/* Enhanced card animations for tooltip interaction */ +.frequency-card { + transition: all 0.3s ease; +} + +.frequency-card.tooltip-target { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(91, 69, 255, 0.15); +} + +/* Progress indicator for tooltip tour */ +.tooltip-progress { + height: 2px; + background: rgba(255, 255, 255, 0.3); + border-radius: 1px; + overflow: hidden; + margin-top: var(--space-2); +} + +.tooltip-progress-bar { + height: 100%; + background: white; + border-radius: 1px; + transition: width 0.3s ease; +} + diff --git a/siwf-v2-reference-app/src/App.tsx b/siwf-v2-reference-app/src/App.tsx index f94d9906..53429a90 100644 --- a/siwf-v2-reference-app/src/App.tsx +++ b/siwf-v2-reference-app/src/App.tsx @@ -1,14 +1,26 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useRef } from 'react'; import { Toaster } from 'react-hot-toast'; import toast from 'react-hot-toast'; +import { HelpCircle, RotateCcw } from 'lucide-react'; import { WalletConnect } from './components/WalletConnect'; import { SiwfLogin } from './components/SiwfLogin'; +import { Tooltip } from './components/Tooltip'; import { useWallet } from './hooks/useWallet'; +import { useTooltipTour } from './hooks/useTooltipTour'; import { initializeSiwfCallback } from './utils/siwf'; import './App.css'; function App() { const { wallet } = useWallet(); + const { + isVisible: tooltipVisible, + hasCompletedTour, + startTour, + resetTour, + getCurrentTooltipProps + } = useTooltipTour(); + + const appRef = useRef(null); // Initialize SIWF callback handling on mount useEffect(() => { @@ -24,8 +36,26 @@ function App() { ); }, []); + // Auto-start tour for new users + useEffect(() => { + if (!hasCompletedTour) { + // Small delay to ensure components are mounted + const timer = setTimeout(() => { + startTour(); + }, 1000); + return () => clearTimeout(timer); + } + }, [hasCompletedTour, startTour]); + + const tooltipProps = getCurrentTooltipProps(); + + const getTargetElement = (selector?: string) => { + if (!selector) return null; + return document.querySelector(selector) as HTMLElement; + }; + return ( -
+
+ + {/* Tour Control Buttons */} +
+ {hasCompletedTour ? ( + + ) : ( + + )} +
@@ -125,6 +196,15 @@ function App() { + + {/* Educational Tooltip */} + {tooltipProps && ( + + )} ); } diff --git a/siwf-v2-reference-app/src/components/MockAuthenticator.tsx b/siwf-v2-reference-app/src/components/MockAuthenticator.tsx index e3f3b3cd..1c02ba6d 100644 --- a/siwf-v2-reference-app/src/components/MockAuthenticator.tsx +++ b/siwf-v2-reference-app/src/components/MockAuthenticator.tsx @@ -6,6 +6,7 @@ interface MockAuthenticatorProps { signedRequest: string; onSuccess: (result: any) => void; onCancel: () => void; + onStart?: () => void; } interface AuthCredentials { @@ -17,7 +18,8 @@ interface AuthCredentials { export const MockAuthenticator: React.FC = ({ signedRequest, onSuccess, - onCancel + onCancel, + onStart }) => { const [authMode, setAuthMode] = useState<'choose' | 'login' | 'signup'>('choose'); const [isLoading, setIsLoading] = useState(false); @@ -27,6 +29,12 @@ export const MockAuthenticator: React.FC = ({ handle: '' }); + // Call onStart when user begins authentication process + const handleModeSelect = (mode: 'login' | 'signup') => { + setAuthMode(mode); + onStart?.(); // Notify parent component that auth has started + }; + const generateMockAuthData = (userEmail: string, userHandle?: string) => { // Generate mock authorizationCode and authorizationPayload as requested const mockAuthorizationCode = `680a0a68-6d3b-4d6d-89b7-0b01a6f7e86f`; @@ -184,7 +192,7 @@ export const MockAuthenticator: React.FC = ({
) : ( - +
+ +
)} diff --git a/siwf-v2-reference-app/src/components/Tooltip.tsx b/siwf-v2-reference-app/src/components/Tooltip.tsx new file mode 100644 index 00000000..34b6d277 --- /dev/null +++ b/siwf-v2-reference-app/src/components/Tooltip.tsx @@ -0,0 +1,247 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { X, ArrowRight, ArrowLeft, Lightbulb, BookOpen } from 'lucide-react'; + +export interface TooltipProps { + isVisible: boolean; + title: string; + content: string; + position?: 'top' | 'bottom' | 'left' | 'right' | 'center'; + onNext?: () => void; + onPrevious?: () => void; + onClose: () => void; + showNextButton?: boolean; + showPreviousButton?: boolean; + nextButtonText?: string; + targetElement?: HTMLElement | null; + step?: number; + totalSteps?: number; +} + +export const Tooltip: React.FC = ({ + isVisible, + title, + content, + position = 'center', + onNext, + onPrevious, + onClose, + showNextButton = false, + showPreviousButton = false, + nextButtonText = 'Next', + targetElement, + step, + totalSteps +}) => { + const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 }); + const tooltipRef = useRef(null); + + useEffect(() => { + if (isVisible && targetElement && tooltipRef.current) { + const targetRect = targetElement.getBoundingClientRect(); + const tooltipRect = tooltipRef.current.getBoundingClientRect(); + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; + + let top = 0; + let left = 0; + + switch (position) { + case 'top': + top = targetRect.top - tooltipRect.height - 16; + left = targetRect.left + (targetRect.width / 2) - (tooltipRect.width / 2); + break; + case 'bottom': + top = targetRect.bottom + 16; + left = targetRect.left + (targetRect.width / 2) - (tooltipRect.width / 2); + break; + case 'left': + top = targetRect.top + (targetRect.height / 2) - (tooltipRect.height / 2); + left = targetRect.left - tooltipRect.width - 16; + break; + case 'right': + top = targetRect.top + (targetRect.height / 2) - (tooltipRect.height / 2); + left = targetRect.right + 16; + break; + default: // center + top = (viewportHeight / 2) - (tooltipRect.height / 2); + left = (viewportWidth / 2) - (tooltipRect.width / 2); + } + + // Ensure tooltip stays within viewport + if (left < 16) left = 16; + if (left + tooltipRect.width > viewportWidth - 16) { + left = viewportWidth - tooltipRect.width - 16; + } + if (top < 16) top = 16; + if (top + tooltipRect.height > viewportHeight - 16) { + top = viewportHeight - tooltipRect.height - 16; + } + + setTooltipPosition({ top, left }); + } + }, [isVisible, targetElement, position]); + + if (!isVisible) return null; + + return ( + <> + {/* Backdrop */} +
+ + {/* Tooltip */} +
+ {/* Header */} +
+
+ +

+ {title} +

+
+
+ {step && totalSteps && ( + + {step}/{totalSteps} + + )} + +
+
+ + {/* Content */} +
+
+
+ + {/* Footer */} +
+
+ {showPreviousButton && onPrevious && ( + + )} +
+ +
+ + {showNextButton && onNext && ( + + )} +
+
+
+ + ); +}; \ No newline at end of file diff --git a/siwf-v2-reference-app/src/components/WalletConnect.tsx b/siwf-v2-reference-app/src/components/WalletConnect.tsx index cc35fdc0..da078a8f 100644 --- a/siwf-v2-reference-app/src/components/WalletConnect.tsx +++ b/siwf-v2-reference-app/src/components/WalletConnect.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import { Wallet, WalletCards, Loader, AlertCircle, CheckCircle } from 'lucide-react'; import { useWallet } from '../hooks/useWallet'; +import { useTooltipTour } from '../hooks/useTooltipTour'; export const WalletConnect: React.FC = () => { const { @@ -12,6 +13,7 @@ export const WalletConnect: React.FC = () => { disconnectWallet } = useWallet(); + const { currentStage, goToStage } = useTooltipTour(); const [justConnected, setJustConnected] = useState(false); // Show "just connected" feedback briefly @@ -23,6 +25,22 @@ export const WalletConnect: React.FC = () => { } }, [wallet.account, wallet.walletType]); + // Advance tooltip tour when connection button is about to be shown + useEffect(() => { + if (currentStage === 'welcome' && !wallet.isConnected) { + // Show wallet-needed tooltip when on welcome stage and no wallet connected + setTimeout(() => goToStage('wallet-needed'), 500); + } + }, [currentStage, wallet.isConnected, goToStage]); + + const handleConnectMetaMask = async () => { + await connectMetaMask(); + }; + + const handleConnectPolkadot = async () => { + await connectPolkadot(); + }; + const getWalletTypeDisplay = (walletType: string | null) => { switch (walletType) { case 'metamask': @@ -118,7 +136,7 @@ export const WalletConnect: React.FC = () => { {/* MetaMask Option */}
+
+ ) : showSignupForm ? ( +
+

+ + New User Signup +

+
+
+ + setSignupData(prev => ({ ...prev, handle: e.target.value }))} + placeholder="Enter your handle (e.g., alice)" + className="frequency-input" + required + minLength={3} + style={{ width: '100%' }} + /> +
+
+ + setSignupData(prev => ({ ...prev, email: e.target.value }))} + placeholder="Enter your email address" + className="frequency-input" + required + style={{ width: '100%' }} + /> +
+
+ + +
+
+
+ ) : ( +
+ +
+ )} +
+
+ + ); +}; \ No newline at end of file diff --git a/siwf-v2-reference-app/src/hooks/useSiwfSdk.ts b/siwf-v2-reference-app/src/hooks/useSiwfSdk.ts new file mode 100644 index 00000000..1f7a63d2 --- /dev/null +++ b/siwf-v2-reference-app/src/hooks/useSiwfSdk.ts @@ -0,0 +1,206 @@ +import { useState, useCallback, useMemo } from 'react'; +import { + createGatewayFetchFn, + createMockGatewayFetchFn, + createSignatureFn, + authenticateWithSdk, + getAccountInfo, + type SiwfSdkConfig, + type SiwfAuthOptions, + type SiwfSdkResult, + type GatewaySiwfResponse, + type AccountResponse +} from '../services/siwfSdk'; +import { useWallet } from './useWallet'; + +export interface UseSiwfSdkOptions { + useTestMode?: boolean; + gatewayBaseUrl?: string; + authToken?: string; +} + +export const useSiwfSdk = (options: UseSiwfSdkOptions = {}) => { + const { wallet, signMessage, signTypedData } = useWallet(); + const [isLoading, setIsLoading] = useState(false); + const [result, setResult] = useState(null); + const [error, setError] = useState(null); + const [accountInfo, setAccountInfo] = useState(null); + + // Configuration - use test mode by default or from environment + const useTestMode = options.useTestMode ?? + (import.meta.env.REACT_APP_USE_MOCK_GATEWAY === 'true'); + + const sdkConfig: SiwfSdkConfig = useMemo(() => ({ + gatewayBaseUrl: options.gatewayBaseUrl || + import.meta.env.REACT_APP_GATEWAY_URL || + 'http://localhost:3001', + authHeaders: { + 'Authorization': `Bearer ${options.authToken || import.meta.env.REACT_APP_GATEWAY_TOKEN || 'demo-token'}`, + 'X-API-Key': import.meta.env.REACT_APP_GATEWAY_API_KEY || '', + } + }), [options.gatewayBaseUrl, options.authToken]); + + // Create gateway fetch function based on test mode + const gatewayFetchFn = useMemo(() => { + if (useTestMode) { + console.log('🧪 Using mock gateway for SIWF SDK'); + return createMockGatewayFetchFn(); + } else { + console.log('🌐 Using real gateway for SIWF SDK:', sdkConfig.gatewayBaseUrl); + return createGatewayFetchFn(sdkConfig); + } + }, [useTestMode, sdkConfig]); + + // Check if current account exists on chain + const checkAccountExists = useCallback(async (accountId?: string) => { + const targetAccountId = accountId || wallet.account; + if (!targetAccountId) { + throw new Error('No account ID provided'); + } + + setIsLoading(true); + setError(null); + + try { + const account = await getAccountInfo(targetAccountId, gatewayFetchFn, wallet.walletType || undefined); + setAccountInfo(account); + return account; + } catch (err: any) { + setError(err.message); + throw err; + } finally { + setIsLoading(false); + } + }, [wallet.account, gatewayFetchFn]); + + // Main authentication function + const authenticate = useCallback(async (authOptions: SiwfAuthOptions) => { + if (!wallet.isConnected || !wallet.account || !wallet.walletType) { + throw new Error('Wallet not connected'); + } + + console.log('🚀 Starting SIWF SDK authentication with wallet:', { + walletType: wallet.walletType, + account: wallet.account, + isConnected: wallet.isConnected + }); + + setIsLoading(true); + setError(null); + + try { + // Create signature function for the current wallet + const signatureFn = createSignatureFn(signMessage, signTypedData, wallet.walletType, wallet.chainId); + + // Execute authentication + const authResult = await authenticateWithSdk( + { + ...authOptions, + accountId: authOptions.accountId || wallet.account, + }, + signatureFn, + gatewayFetchFn, + wallet.walletType + ); + + setResult(authResult); + console.log('✅ Authentication completed successfully'); + return authResult; + } catch (err: any) { + const errorMessage = err.message || 'Authentication failed'; + setError(errorMessage); + console.error('❌ Authentication failed:', err); + throw err; + } finally { + setIsLoading(false); + } + }, [wallet, signMessage, signTypedData, gatewayFetchFn]); + + // Helper function to determine if user is new or existing + const getUserStatus = useCallback(async (accountId?: string) => { + try { + const account = await checkAccountExists(accountId); + return { + isNewUser: account === null, + existingAccount: account, + needsSignup: account === null + }; + } catch (error) { + console.error('Error checking user status:', error); + return { + isNewUser: true, // Default to new user if check fails + existingAccount: null, + needsSignup: true + }; + } + }, [checkAccountExists]); + + // Simplified authentication that auto-detects login vs signup + const authenticateAuto = useCallback(async ( + signedRequest: string, + signupData?: { handle: string; email: string } + ) => { + if (!wallet.account) { + throw new Error('No wallet account available'); + } + + // Check if user exists + const userStatus = await getUserStatus(wallet.account); + console.log('👤 User status:', userStatus); + + if (userStatus.isNewUser && !signupData) { + throw new Error('New user detected but signup data not provided'); + } + + return await authenticate({ + accountId: wallet.account, + encodedSiwfSignedRequest: signedRequest, + signUpHandle: userStatus.isNewUser ? signupData?.handle : undefined, + signUpEmail: userStatus.isNewUser ? signupData?.email : undefined, + onMsaCreated: (account) => { + console.log('🎉 MSA created for new user:', account); + setAccountInfo(account); + } + }); + }, [wallet.account, getUserStatus, authenticate]); + + // Reset all state + const reset = useCallback(() => { + setResult(null); + setError(null); + setAccountInfo(null); + setIsLoading(false); + }, []); + + // Clear error + const clearError = useCallback(() => { + setError(null); + }, []); + + return { + // State + isLoading, + result, + error, + accountInfo, + + // Configuration + useTestMode, + isWalletReady: wallet.isConnected && wallet.account && wallet.walletType, + + // Methods + authenticate, + authenticateAuto, + checkAccountExists, + getUserStatus, + reset, + clearError, + + // Wallet info + walletInfo: { + isConnected: wallet.isConnected, + account: wallet.account, + walletType: wallet.walletType + } + }; +}; \ No newline at end of file diff --git a/siwf-v2-reference-app/src/services/siwfSdk.ts b/siwf-v2-reference-app/src/services/siwfSdk.ts new file mode 100644 index 00000000..dbd404b8 --- /dev/null +++ b/siwf-v2-reference-app/src/services/siwfSdk.ts @@ -0,0 +1,382 @@ +import { + startSiwf, + getAccountForAccountId +} from '@projectlibertylabs/siwf-embedded-wallet-sdk'; +import { u8aToHex } from '@polkadot/util'; +import { addressToEvm } from '@polkadot/util-crypto'; + +// Type definitions based on SDK internals +export type Address = `0x${string}`; + +export interface WindowEthereumRequest { + method: string; + params: any[]; +} + +export type SignatureFn = (request: WindowEthereumRequest) => Promise; + +export type GatewayFetchFn = ( + method: 'GET' | 'POST', + path: string, + body?: { authorizationPayload: string } +) => Promise; + +export interface AccountResponse { + msaId: string; + handle?: { + base_handle: string; + canonical_base: string; + suffix: number; + }; +} + +export interface GraphKeySubject { + id: string; + encodedPublicKeyValue: string; + encodedPrivateKeyValue: string; + encoding: 'base16'; + format: 'bare'; + type: 'X25519'; + keyType: 'dsnp.public-key-key-agreement'; +} + +export interface GatewaySiwfResponse { + controlKey: string; + signUpReferenceId?: string; + signUpStatus?: string; + msaId?: string; + email?: string; + phoneNumber?: string; + graphKey?: GraphKeySubject; + rawCredentials?: object[]; + recoverySecret?: string; +} + +export type MsaCreationCallbackFn = (account: AccountResponse) => void; + +export interface SiwfSdkConfig { + gatewayBaseUrl: string; + authHeaders: Record; +} + +export interface SiwfAuthOptions { + accountId: string; + encodedSiwfSignedRequest: string; + signUpHandle?: string; + signUpEmail?: string; + onMsaCreated?: MsaCreationCallbackFn; +} + +export interface SiwfSdkResult { + isLoading: boolean; + result: GatewaySiwfResponse | null; + error: string | null; +} + +/** + * Converts a Polkadot SS58 address to Ethereum format (0x prefixed hex) + * Returns the address unchanged if it's already in Ethereum format + */ +export function convertToEthereumAddress(address: string): string { + // If already ethereum format (0x prefixed), return as-is + if (address.startsWith('0x') && address.length === 42) { + return address; + } + + try { + // Convert SS58 to EVM address + const evmBytes = addressToEvm(address); + const ethereumAddress = u8aToHex(evmBytes); + console.log(`🔄 Converted SS58 address ${address} to Ethereum format: ${ethereumAddress}`); + return ethereumAddress; + } catch (error) { + console.error('❌ Failed to convert address:', error); + throw new Error(`Invalid address format: ${address}. Must be either Ethereum (0x...) or valid SS58 format.`); + } +} + +/** + * Validates and converts an address to the format expected by the SDK + */ +export function prepareAccountIdForSdk(accountId: string, walletType: 'metamask' | 'polkadot'): string { + if (walletType === 'metamask') { + // MetaMask addresses should already be in Ethereum format + if (!accountId.startsWith('0x') || accountId.length !== 42) { + throw new Error('Invalid MetaMask address format'); + } + return accountId; + } else if (walletType === 'polkadot') { + // Convert Polkadot SS58 address to Ethereum format + return convertToEthereumAddress(accountId); + } else { + throw new Error(`Unsupported wallet type: ${walletType}`); + } +} + +/** + * Creates a gateway fetch function that communicates with the Frequency Gateway Account Service + */ +export function createGatewayFetchFn(config: SiwfSdkConfig): GatewayFetchFn { + return async (method: 'GET' | 'POST', path: string, body?: { authorizationPayload: string }) => { + const url = new URL(config.gatewayBaseUrl + path); + + console.log(`🌐 Gateway ${method} request to: ${url.toString()}`); + + const requestOptions: RequestInit = { + method, + headers: { + 'Content-Type': 'application/json', + ...config.authHeaders, + }, + }; + + if (body) { + requestOptions.body = JSON.stringify(body); + console.log('🌐 Request body:', body); + } + + try { + const response = await fetch(url.toString(), requestOptions); + console.log(`🌐 Gateway response status: ${response.status}`); + return response; + } catch (error) { + console.error('🌐 Gateway fetch error:', error); + throw error; + } + }; +} + +/** + * Creates a signature function that adapts wallet signing methods to the SDK interface + */ +export function createSignatureFn( + signMessage: (message: string) => Promise, + signTypedData: (typedData: any) => Promise, + walletType: 'metamask' | 'polkadot', + connectedChainId?: number +): SignatureFn { + return async (request: any) => { + console.log(`🔐 Signature request:`, request); + console.log(`🔐 Request method: ${request.method}, wallet: ${walletType}`); + + try { + if (request.method === 'eth_signTypedData_v4') { + if (walletType !== 'metamask') { + // For Polkadot wallets, convert EIP-712 to personal_sign + console.log('🔄 Converting EIP-712 to personal_sign for Polkadot wallet'); + const [, typedData] = request.params; + + // Extract the message from EIP-712 typed data + // This is a simplified approach - in production you might want more sophisticated conversion + let messageToSign = ''; + + if (typedData && typedData.message) { + // Try to create a human-readable message from the typed data + const message = typedData.message; + const domain = typedData.domain; + + messageToSign = `Sign to ${domain?.name || 'Frequency'}:\n\n`; + + // Add each field from the message + Object.entries(message).forEach(([key, value]) => { + messageToSign += `${key}: ${value}\n`; + }); + + messageToSign += `\nDomain: ${domain?.name || 'Unknown'}`; + messageToSign += `\nChain ID: ${domain?.chainId || 'Unknown'}`; + } else { + // Fallback to stringifying the entire typed data + messageToSign = `Sign structured data:\n${JSON.stringify(typedData, null, 2)}`; + } + + console.log('🔐 Converted message for Polkadot signing:', messageToSign); + return await signMessage(messageToSign); + } + + // MetaMask handling - typedData is already an object, don't parse it + const [, typedData] = request.params; + console.log('🔐 Signing typed data with MetaMask'); + console.log('🔐 Original TypedData object:', typedData); + + // Fix chainId mismatch: Replace Frequency chainId with connected network chainId + // This allows MetaMask to sign when connected to any Ethereum network + const targetChainId = connectedChainId ? `0x${connectedChainId.toString(16)}` : "0x1"; + const modifiedTypedData = { + ...typedData, + domain: { + ...typedData.domain, + chainId: targetChainId + } + }; + + console.log('🔄 Modified chainId from', typedData.domain?.chainId, 'to', modifiedTypedData.domain.chainId, + `(connected chainId: ${connectedChainId})`); + console.log('🔐 Modified TypedData object:', modifiedTypedData); + + return await signTypedData(modifiedTypedData); + } + + if (request.method === 'personal_sign') { + const [, message] = request.params; // Second parameter is the message + console.log(`🔐 Signing message with ${walletType}`); + console.log(`🔐 Message to sign:`, message); + return await signMessage(message); + } + + throw new Error(`Unsupported signing method: ${request.method}`); + } catch (error) { + console.error('🔐 Signature error:', error); + throw error; + } + }; +} + +/** + * Creates a mock gateway fetch function for development testing + */ +export function createMockGatewayFetchFn(): GatewayFetchFn { + return async (method: 'GET' | 'POST', path: string, body?: { authorizationPayload: string }) => { + console.log(`🧪 Mock Gateway ${method} request to: ${path}`); + + // Simulate network delay + await new Promise(resolve => setTimeout(resolve, 500)); + + if (path.includes('/accounts/account/')) { + const accountId = path.split('/').pop(); + console.log(`🧪 Mock: Looking up account ${accountId}`); + + // Handle our mock provider account from SIWF request generator + const mockProviderKey = 'f6cL4wq1HUNx11TcvdABNf9UNXXoyH47mVUwT59tzSFRW8yDH'; + + // Check if this is the mock provider account + if (accountId === mockProviderKey || accountId?.includes(mockProviderKey.slice(0, 20))) { + console.log('💼 Mock: Returning mock provider account'); + return new Response(JSON.stringify({ + msaId: '999999', // Mock provider MSA ID + handle: { base_handle: 'mockprovider', canonical_base: 'mockprovider', suffix: 1 } + }), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + } + + // For demo, return existing account for addresses containing 'existing' + if (accountId?.includes('existing')) { + console.log('👤 Mock: Returning existing user account'); + return new Response(JSON.stringify({ + msaId: '12345', + handle: { base_handle: 'testuser', canonical_base: 'testuser', suffix: 1 } + }), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + } else { + console.log('🆕 Mock: Account not found - treating as new user'); + return new Response('Not Found', { status: 404 }); + } + } + + if (path === '/v2/accounts/siwf') { + console.log('🧪 Mock: Processing SIWF authentication'); + const mockResponse: GatewaySiwfResponse = { + controlKey: '0x1234567890abcdef1234567890abcdef12345678', + msaId: Math.floor(Math.random() * 100000).toString(), + signUpStatus: 'complete', + email: body?.authorizationPayload ? 'user@example.com' : undefined, + graphKey: { + id: 'mock-graph-key-id', + encodedPublicKeyValue: '0xabcdef1234567890abcdef1234567890abcdef12', + encodedPrivateKeyValue: '0xfedcba0987654321fedcba0987654321fedcba09', + encoding: 'base16' as const, + format: 'bare' as const, + type: 'X25519' as const, + keyType: 'dsnp.public-key-key-agreement' as const + } + }; + + return new Response(JSON.stringify(mockResponse), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + } + + if (path === '/v1/frequency/blockinfo') { + console.log('🧪 Mock: Returning block info'); + return new Response(JSON.stringify({ + blocknumber: 12345, + finalized_blocknumber: 12340, + genesis: '0x1234567890abcdef', + runtime_version: 1 + }), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + } + + throw new Error(`Mock gateway: Unsupported path ${path}`); + }; +} + +/** + * Main SDK integration function that handles both login and signup + */ +export async function authenticateWithSdk( + options: SiwfAuthOptions, + signatureFn: SignatureFn, + gatewayFetchFn: GatewayFetchFn, + walletType: 'metamask' | 'polkadot' +): Promise { + console.log('🚀 Starting SIWF SDK authentication'); + console.log('🚀 Options:', { + accountId: options.accountId, + walletType, + hasSignedRequest: !!options.encodedSiwfSignedRequest, + signUpHandle: options.signUpHandle, + signUpEmail: options.signUpEmail + }); + + try { + // Convert account ID to format expected by SDK + const sdkAccountId = prepareAccountIdForSdk(options.accountId, walletType); + console.log('🔄 Using SDK account ID:', sdkAccountId); + + const result = await startSiwf( + sdkAccountId, + signatureFn, + gatewayFetchFn, + options.encodedSiwfSignedRequest, + options.signUpHandle, + options.signUpEmail, + options.onMsaCreated + ); + + console.log('✅ SIWF SDK authentication successful:', result); + return result; + } catch (error) { + console.error('❌ SIWF SDK authentication failed:', error); + throw error; + } +} + +/** + * Get account information for a given account ID + */ +export async function getAccountInfo( + accountId: string, + gatewayFetchFn: GatewayFetchFn, + walletType?: 'metamask' | 'polkadot' +): Promise { + console.log('🔍 Getting account info for:', accountId, 'wallet type:', walletType); + + try { + // Convert account ID to format expected by SDK if needed + const sdkAccountId = walletType ? prepareAccountIdForSdk(accountId, walletType) : accountId; + + const result = await getAccountForAccountId(gatewayFetchFn, sdkAccountId); + console.log('✅ Account info retrieved:', result); + return result; + } catch (error) { + console.error('❌ Failed to get account info:', error); + throw error; + } +} \ No newline at end of file From 08d811679fe176ea1ccd34c42ce0e587488c4906 Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Wed, 30 Jul 2025 15:15:47 -0400 Subject: [PATCH 07/16] Add SDK integration to App component --- siwf-v2-reference-app/src/App.tsx | 36 ++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/siwf-v2-reference-app/src/App.tsx b/siwf-v2-reference-app/src/App.tsx index 53429a90..22acc913 100644 --- a/siwf-v2-reference-app/src/App.tsx +++ b/siwf-v2-reference-app/src/App.tsx @@ -4,6 +4,7 @@ import toast from 'react-hot-toast'; import { HelpCircle, RotateCcw } from 'lucide-react'; import { WalletConnect } from './components/WalletConnect'; import { SiwfLogin } from './components/SiwfLogin'; +import { SiwfSdkAuth } from './components/SiwfSdkAuth'; import { Tooltip } from './components/Tooltip'; import { useWallet } from './hooks/useWallet'; import { useTooltipTour } from './hooks/useTooltipTour'; @@ -22,19 +23,24 @@ function App() { const appRef = useRef(null); - // Initialize SIWF callback handling on mount + // Feature flag to switch between old SIWF and new SDK implementation + const useEmbeddedSdk = import.meta.env.REACT_APP_BYPASS_FREQUENCY_ACCESS === 'true' || true; // Default to true for demo + + // Initialize SIWF callback handling on mount (only for legacy mode) useEffect(() => { - initializeSiwfCallback( - (result) => { - console.log('✅ SIWF authentication successful:', result); - toast.success('SIWF authentication completed!'); - }, - (error) => { - console.error('❌ SIWF authentication failed:', error); - toast.error('SIWF authentication failed: ' + error.message); - } - ); - }, []); + if (!useEmbeddedSdk) { + initializeSiwfCallback( + (result) => { + console.log('✅ SIWF authentication successful:', result); + toast.success('SIWF authentication completed!'); + }, + (error) => { + console.error('❌ SIWF authentication failed:', error); + toast.error('SIWF authentication failed: ' + error.message); + } + ); + } + }, [useEmbeddedSdk]); // Auto-start tour for new users useEffect(() => { @@ -163,7 +169,11 @@ function App() {
- + {useEmbeddedSdk ? ( + + ) : ( + + )}
From 4ba0f2419e9e7001722d222a6f4854eda417feb0 Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Wed, 30 Jul 2025 15:16:33 -0400 Subject: [PATCH 08/16] Add chainId to WalletState interface --- siwf-v2-reference-app/src/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/siwf-v2-reference-app/src/types.ts b/siwf-v2-reference-app/src/types.ts index 0f3fec9c..9069ce95 100644 --- a/siwf-v2-reference-app/src/types.ts +++ b/siwf-v2-reference-app/src/types.ts @@ -7,6 +7,7 @@ export interface WalletState { walletType: WalletType | null; isConnecting: boolean; error: string | null; + chainId?: number; } export interface PolkadotAccount { From 9efe3765d055e5a5fb762a26d4159076413e8edf Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Fri, 1 Aug 2025 14:39:23 -0400 Subject: [PATCH 09/16] Add env example --- siwf-v2-reference-app/src/hooks/useSiwfSdk.ts | 9 ++------- siwf-v2-reference-app/src/services/siwfSdk.ts | 2 -- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/siwf-v2-reference-app/src/hooks/useSiwfSdk.ts b/siwf-v2-reference-app/src/hooks/useSiwfSdk.ts index 1f7a63d2..c27c5978 100644 --- a/siwf-v2-reference-app/src/hooks/useSiwfSdk.ts +++ b/siwf-v2-reference-app/src/hooks/useSiwfSdk.ts @@ -16,7 +16,6 @@ import { useWallet } from './useWallet'; export interface UseSiwfSdkOptions { useTestMode?: boolean; gatewayBaseUrl?: string; - authToken?: string; } export const useSiwfSdk = (options: UseSiwfSdkOptions = {}) => { @@ -33,12 +32,8 @@ export const useSiwfSdk = (options: UseSiwfSdkOptions = {}) => { const sdkConfig: SiwfSdkConfig = useMemo(() => ({ gatewayBaseUrl: options.gatewayBaseUrl || import.meta.env.REACT_APP_GATEWAY_URL || - 'http://localhost:3001', - authHeaders: { - 'Authorization': `Bearer ${options.authToken || import.meta.env.REACT_APP_GATEWAY_TOKEN || 'demo-token'}`, - 'X-API-Key': import.meta.env.REACT_APP_GATEWAY_API_KEY || '', - } - }), [options.gatewayBaseUrl, options.authToken]); + 'http://localhost:3013' + }), [options.gatewayBaseUrl]); // Create gateway fetch function based on test mode const gatewayFetchFn = useMemo(() => { diff --git a/siwf-v2-reference-app/src/services/siwfSdk.ts b/siwf-v2-reference-app/src/services/siwfSdk.ts index dbd404b8..4732e473 100644 --- a/siwf-v2-reference-app/src/services/siwfSdk.ts +++ b/siwf-v2-reference-app/src/services/siwfSdk.ts @@ -56,7 +56,6 @@ export type MsaCreationCallbackFn = (account: AccountResponse) => void; export interface SiwfSdkConfig { gatewayBaseUrl: string; - authHeaders: Record; } export interface SiwfAuthOptions { @@ -126,7 +125,6 @@ export function createGatewayFetchFn(config: SiwfSdkConfig): GatewayFetchFn { method, headers: { 'Content-Type': 'application/json', - ...config.authHeaders, }, }; From bf0d6de572602fad9756625a36b2e6035d5786bd Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Fri, 1 Aug 2025 14:50:42 -0400 Subject: [PATCH 10/16] Add developer documentation --- siwf-v2-reference-app/README.md | 237 +++++++++++++++++++++++++++----- 1 file changed, 204 insertions(+), 33 deletions(-) diff --git a/siwf-v2-reference-app/README.md b/siwf-v2-reference-app/README.md index b3023b64..0e1064fb 100644 --- a/siwf-v2-reference-app/README.md +++ b/siwf-v2-reference-app/README.md @@ -1,59 +1,230 @@ -# SIWF v2 Reference App Demo +# SIWF v2 Reference Application -A React + TypeScript frontend demonstrating Sign In With Frequency (SIWF) v2 integration with both MetaMask and Polkadot.js wallets. +A comprehensive React + TypeScript reference implementation demonstrating **Sign In With Frequency (SIWF) v2** integration using the embedded wallet SDK approach. This application showcases cross-wallet compatibility with both MetaMask and Polkadot.js wallets, implementing real cryptographic signatures and complete authentication flows. -## Prerequisites +## 🎯 Project Overview + +Unlike traditional OAuth flows, this implementation leverages cryptographic signatures directly from user wallets to authenticate with the Frequency blockchain. + +### Key Concepts + +- **SIWF (Sign In With Frequency)**: The Frequency chains authentication standard that uses cryptographic signatures instead of passwords +- **Embedded SDK**: Handles the authentication flow, handshakes, and signatures +- **Cross-Chain Signatures**: EIP-712 for MetaMask, CAIP-122 for Polkadot wallets +- **MSA (Message Source Account)**: Frequency's account system for managing identities and permissions + +## 🏗️ Architecture + +``` +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ React App │────│ SIWF SDK │────│ Frequency │ +│ │ │ │ │ Gateway │ +│ • Wallet UI │ │ • Signature Mgmt │ │ │ +│ • Auth Flow │ │ • Payload Encode │ │ • Account Mgmt │ +│ • State Mgmt │ │ • Cross-wallet │ │ • MSA Operations│ +└─────────────────┘ └──────────────────┘ └─────────────────┘ + │ │ │ + └───────────────────────┼───────────────────────┘ + │ + ┌─────────────────────────┐ + │ User Wallets │ + │ │ + │ • MetaMask (EIP-712) │ + │ • Polkadot.js (CAIP-122)│ + └─────────────────────────┘ +``` + +## 📁 Project Structure + +``` +src/ +├── components/ # React UI components +│ ├── SiwfSdkAuth.tsx # Main SIWF authentication interface +│ ├── SiwfLogin.tsx # Traditional SIWF login flow +│ ├── WalletConnect.tsx # Wallet connection UI +│ └── MockAuthenticator.tsx # Development testing component +├── hooks/ # Custom React hooks +│ ├── useSiwfSdk.ts # SIWF SDK integration hook +│ ├── useWallet.ts # Wallet management hook +│ └── useTooltipTour.ts # UI guidance system +├── services/ # Business logic and API calls +│ └── siwfSdk.ts # SIWF SDK service layer +├── utils/ # Utility functions +│ └── siwf.ts # SIWF request generation and validation +└── types.ts # TypeScript type definitions +``` + +## 🔐 Signature Flows + +### Login Flow (1 signature) +``` +User → Sign CAIP-122 message → Gateway verification → Authentication complete +``` + +### Signup Flow (up to 4 signatures) +``` +User → 1. Add Provider Payload (EIP-712) + → 2. Claim Handle Payload (EIP-712) + → 3. Graph Key Payload (EIP-712) [optional] + → 4. Recovery Secret (EIP-712) [optional] + → MSA creation → Authentication complete +``` + +### Cross-Wallet Compatibility +- **MetaMask**: Native EIP-712 structured data signing +- **Polkadot.js**: EIP-712 converted to human-readable personal_sign messages + +## 🚀 Quick Start + +### Prerequisites 1. **Node.js v22+** -2. **Gateway Server Running on localhost:3013** - - The frontend proxies API requests to avoid CORS issues - - Make sure your Frequency Gateway is running before starting the frontend +2. **Frequency Gateway Server** running on `localhost:3013` (optional, gateway is mocked out of the box) 3. **Wallet Extensions**: - - [MetaMask](https://metamask.io/) for Ethereum-style wallets - - [Polkadot.js Extension](https://polkadot.js.org/extension/) for Substrate wallets + - [MetaMask](https://metamask.io/) for Ethereum/Solidity + - [Polkadot.js Extension](https://polkadot.js.org/extension/) for Polkadot/Substrate -## Setup +### Installation -1. **Install Dependencies**: +1. **Clone and Install**: ```bash + git clone + cd siwf-v2-reference-app npm install ``` -2. **Start Gateway Server** (in a separate terminal): +2. **Configure Environment**: ```bash - # Make sure your Frequency Gateway is running on localhost:3013 - # This is required for SIWF authentication to work + cp .env.example .env + # Edit .env file with your configuration ``` -3. **Start Frontend**: +3. **Start Development**: ```bash + # Terminal 1: Start your Frequency Gateway if you're running one locally + # (Gateway should run on localhost:3013) + + # Terminal 2: Start the reference app npm run dev ``` -4. **Open Browser**: - - Navigate to `http://localhost:5173` - - Connect your wallet (MetaMask or Polkadot.js) - - Test SIWF authentication +4. **Test the Application**: + - Open `http://localhost:5173` + - Connect a wallet (MetaMask or Polkadot.js) + - Test SIWF authentication flows + +## ⚙️ Configuration -## Features +### Environment Variables -- ✅ **Dual Wallet Support**: MetaMask and Polkadot.js -- ✅ **Real Crypto Signatures**: EIP-712 and CAIP-122 signing -- ✅ **Complete SIWF Flow**: Account detection, MSA creation, credential verification -- ✅ **Persistent Disconnect**: Wallet state persists across page refreshes -- ✅ **UI Re-rendering**: Clean state reset when switching wallets -- ✅ **CORS Handling**: Vite proxy configuration for Gateway API +Create a `.env` file based on `.env.example`: -## API Proxy +```bash +# Gateway Service Configuration +REACT_APP_GATEWAY_URL=http://localhost:3013 -The frontend uses Vite's proxy feature to forward requests: -- Frontend requests: `http://localhost:5173/api/*` +# Development/Testing Configuration +REACT_APP_USE_MOCK_GATEWAY=false + +# Debug Configuration +REACT_APP_DEBUG_MODE=false +``` + +### API Proxy Configuration + +The Vite development server proxies API calls to avoid CORS issues when using a local Gateway: +- Frontend: `http://localhost:5173/api/*` - Proxied to: `http://localhost:3013/*` -This avoids CORS issues when calling the Gateway API from the browser. +## 🧩 Key Components + +### `useSiwfSdk` Hook +Central hook managing SIWF SDK integration: +- Gateway communication +- Signature function creation +- Account status checking +- Authentication orchestration + +### `useWallet` Hook +Cross-wallet management: +- MetaMask and Polkadot.js support +- Signature method abstraction +- Connection state management +- Chain-specific adaptations + +### `SiwfSdkAuth` Component +Main authentication interface: +- User status detection (new vs existing) +- Signup form handling +- Authentication flow management +- Real-time feedback and guidance + +### Signature Service +Handles cryptographic operations: +- EIP-712 typed data signing (MetaMask) +- Personal message signing (Polkadot.js) +- Cross-wallet compatibility layer +- Signature verification + +## 🔄 Development Workflow + +### Mock vs Real Gateway + +**Mock Mode** (`REACT_APP_USE_MOCK_GATEWAY=true`): +- Uses pre-defined responses +- No external dependencies +- Ideal for UI/UX development +- Fast iteration cycles + +**Real Gateway Mode** (`REACT_APP_USE_MOCK_GATEWAY=false`): +- Connects to actual Frequency Gateway +- Real blockchain interactions +- End-to-end testing +- Production-like behavior + +### Testing Flows + +1. **Wallet Connection**: Test both MetaMask and Polkadot.js +2. **New User Signup**: Complete 4-signature registration flow +3. **Existing User Login**: Single signature authentication +4. **Error Handling**: Network issues, signature rejections +5. **State Management**: Wallet switching, page refreshes + +## 🐛 Troubleshooting + +### Common Issues + +**CORS Errors** +- Ensure Gateway server is running on `localhost:3013` +- Check Vite proxy configuration in `vite.config.ts` + +**Wallet Not Detected** +- Verify browser extensions are installed and enabled +- Check for wallet conflicts (multiple wallet extensions) +- Ensure wallet is unlocked and connected + +**Signature Failures** +- Verify wallet has sufficient funds for gas (MetaMask) +- Check network connectivity to Frequency Gateway +- Enable debug mode for detailed logging + +**Development vs Production** +- Mock gateway responses may differ from real gateway +- Test with real gateway before production deployment +- Verify all environment variables are configured + +## 📚 Additional Resources + +- [SIWF v2 Specification](https://projectlibertylabs.github.io/siwf/v2/) +- [EIP-712 Standard](https://eips.ethereum.org/EIPS/eip-712) +- [CAIP-122 Specification](https://chainagnostic.org/CAIPs/caip-122) +- [Frequency Documentation](https://docs.frequency.xyz) + +## 🤝 Contributing -## Troubleshooting +1. Fork the repository +2. Create a feature branch: `git checkout -b feature/your-feature` +3. Make your changes and test thoroughly +4. Submit a pull request with detailed description -- **CORS Errors**: Ensure Gateway server is running on localhost:3013 -- **Wallet Not Detected**: Check that wallet extensions are installed and enabled -- **SIWF Errors**: Verify Gateway is properly configured and accessible +For detailed SDK integration information, see [README-SDK-INTEGRATION.md](./README-SDK-INTEGRATION.md). From 47602eb7e9dd0f095c496a901d06a3d227d665bd Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Fri, 1 Aug 2025 14:54:28 -0400 Subject: [PATCH 11/16] Add app img to readme --- siwf-v2-reference-app/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/siwf-v2-reference-app/README.md b/siwf-v2-reference-app/README.md index 0e1064fb..52271ba9 100644 --- a/siwf-v2-reference-app/README.md +++ b/siwf-v2-reference-app/README.md @@ -1,5 +1,7 @@ # SIWF v2 Reference Application +![SIWF Demo](https://imgur.com/psWItJP.png) + A comprehensive React + TypeScript reference implementation demonstrating **Sign In With Frequency (SIWF) v2** integration using the embedded wallet SDK approach. This application showcases cross-wallet compatibility with both MetaMask and Polkadot.js wallets, implementing real cryptographic signatures and complete authentication flows. ## 🎯 Project Overview From 9d0eebca85a8dfe770a7b2e16488392810bc5652 Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Fri, 1 Aug 2025 16:05:28 -0400 Subject: [PATCH 12/16] Add .env.example file --- siwf-v2-reference-app/.env.example | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 siwf-v2-reference-app/.env.example diff --git a/siwf-v2-reference-app/.env.example b/siwf-v2-reference-app/.env.example new file mode 100644 index 00000000..768aaff3 --- /dev/null +++ b/siwf-v2-reference-app/.env.example @@ -0,0 +1,32 @@ +# ======================================== +# SIWF Reference App Environment Variables +# ======================================== +# Copy this file to .env and configure the values for your environment + +# ======================================== +# Gateway Service Configuration +# ======================================== + +# URL of the SIWF Gateway service +# Default: http://localhost:3013 +REACT_APP_GATEWAY_URL=http://localhost:3013 + +# ======================================== +# Development/Testing Configuration +# ======================================== + +# Whether to use the mock gateway instead of real gateway +# Set to 'true' to use mock responses for development +# Set to 'false' to use real gateway service +# Default: false (uses real gateway unless specified) +REACT_APP_USE_MOCK_GATEWAY=false + +# ======================================== +# Debug Configuration +# ======================================== + +# Enable debug mode for verbose logging +# Set to 'true' for detailed console output +# Set to 'false' for minimal logging +# Default: false +REACT_APP_DEBUG_MODE=false From 41263af78c17ddf76fd15c757ebe23b58883def4 Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Thu, 18 Sep 2025 22:58:34 -0400 Subject: [PATCH 13/16] Add sr25519 support --- siwf-v2-reference-app/src/hooks/useSiwfSdk.ts | 25 ++++- siwf-v2-reference-app/src/hooks/useWallet.ts | 91 ++++++++++++++++++- 2 files changed, 109 insertions(+), 7 deletions(-) diff --git a/siwf-v2-reference-app/src/hooks/useSiwfSdk.ts b/siwf-v2-reference-app/src/hooks/useSiwfSdk.ts index c27c5978..75e8333e 100644 --- a/siwf-v2-reference-app/src/hooks/useSiwfSdk.ts +++ b/siwf-v2-reference-app/src/hooks/useSiwfSdk.ts @@ -16,10 +16,23 @@ import { useWallet } from './useWallet'; export interface UseSiwfSdkOptions { useTestMode?: boolean; gatewayBaseUrl?: string; + wallet?: { + isConnected: boolean; + account: string | null; + walletType: 'metamask' | 'polkadot' | null; + chainId?: number; + }; + signMessage?: (message: string) => Promise; + signTypedData?: (typedData: any) => Promise; } export const useSiwfSdk = (options: UseSiwfSdkOptions = {}) => { - const { wallet, signMessage, signTypedData } = useWallet(); + const internalWallet = useWallet(); + + // Use passed wallet state if available, otherwise use internal wallet + const wallet = options.wallet || internalWallet.wallet; + const signMessage = options.signMessage || internalWallet.signMessage; + const signTypedData = options.signTypedData || internalWallet.signTypedData; const [isLoading, setIsLoading] = useState(false); const [result, setResult] = useState(null); const [error, setError] = useState(null); @@ -135,8 +148,14 @@ export const useSiwfSdk = (options: UseSiwfSdkOptions = {}) => { signedRequest: string, signupData?: { handle: string; email: string } ) => { - if (!wallet.account) { - throw new Error('No wallet account available'); + console.log('🔐 authenticateAuto called with wallet state:', { + isConnected: wallet.isConnected, + account: wallet.account, + walletType: wallet.walletType + }); + + if (!wallet.isConnected || !wallet.account) { + throw new Error('No wallet account available. Please connect a wallet first.'); } // Check if user exists diff --git a/siwf-v2-reference-app/src/hooks/useWallet.ts b/siwf-v2-reference-app/src/hooks/useWallet.ts index 279fd265..e277b8ca 100644 --- a/siwf-v2-reference-app/src/hooks/useWallet.ts +++ b/siwf-v2-reference-app/src/hooks/useWallet.ts @@ -1,7 +1,7 @@ import { useState, useEffect, useCallback } from 'react'; import { useAccount, useConnect, useDisconnect, useSignMessage, useSignTypedData } from 'wagmi'; import { web3Enable, web3Accounts, web3FromAddress } from '@polkadot/extension-dapp'; -import { stringToHex } from '@polkadot/util'; +import { stringToHex, stringToU8a, u8aToHex } from '@polkadot/util'; import toast from 'react-hot-toast'; import type { WalletState, WalletType } from '../types'; @@ -39,7 +39,15 @@ export const useWallet = () => { // Check if wallets are available const isMetaMaskAvailable = () => typeof window.ethereum !== 'undefined'; - const isPolkadotAvailable = () => typeof window.injectedWeb3 !== 'undefined'; + const isPolkadotAvailable = () => { + const available = typeof window.injectedWeb3 !== 'undefined'; + if (!available) { + console.log('🔍 Polkadot.js extension not detected. Make sure the extension is installed and enabled.'); + } else { + console.log('🔍 Polkadot.js extension detected:', Object.keys(window.injectedWeb3 || {})); + } + return available; + }; // Check if user previously disconnected const wasManuallyDisconnected = () => { @@ -167,11 +175,19 @@ export const useWallet = () => { }, [connect, connectors]); const connectPolkadot = useCallback(async () => { + if (!isPolkadotAvailable()) { + toast.error('Polkadot.js extension not found. Please install it first.'); + return; + } + setPolkadotWallet(prev => ({ ...prev, isConnecting: true, error: null })); try { + console.log('🔌 Attempting to connect to Polkadot.js extension...'); + // Enable the extension const extensions = await web3Enable('SIWF Frontend'); + console.log('🔌 Available extensions:', extensions.map(ext => ({ name: ext.name, version: ext.version }))); if (extensions.length === 0) { throw new Error('Please install Polkadot.js extension'); @@ -179,13 +195,17 @@ export const useWallet = () => { // Get accounts const accounts = await web3Accounts(); + console.log('🔌 Available accounts:', accounts.map(acc => ({ address: acc.address, name: acc.meta.name }))); if (accounts.length === 0) { - throw new Error('No accounts found in Polkadot.js extension'); + const errorMsg = 'No accounts found in Polkadot.js extension. Please create an account in the extension first.'; + console.error('❌', errorMsg); + throw new Error(errorMsg); } // Use the first account for now const account = accounts[0]; + console.log('✅ Selected account:', { address: account.address, name: account.meta.name }); setPolkadotWallet({ isConnected: true, @@ -196,7 +216,7 @@ export const useWallet = () => { setConnected('polkadot'); toast.success(`Connected to Polkadot.js: ${account.address.slice(0, 6)}...${account.address.slice(-4)}`); } catch (error: any) { - console.error('Error connecting Polkadot.js:', error); + console.error('❌ Error connecting Polkadot.js:', error); const errorMessage = 'Failed to connect Polkadot.js: ' + error.message; setPolkadotWallet(prev => ({ ...prev, @@ -294,6 +314,67 @@ export const useWallet = () => { } }, [wallet, wagmiSignMessage]); + // Sign SIWF request payload with sr25519 for Polkadot wallets + const signSiwfRequest = useCallback(async (payload: string) => { + if (!wallet.isConnected || !wallet.account) { + throw new Error('Wallet not connected'); + } + + if (wallet.walletType !== 'polkadot') { + throw new Error('SIWF request signing is only supported for Polkadot wallets'); + } + + try { + console.log('🔐 Signing SIWF request payload with sr25519'); + console.log('🔐 Payload to sign:', payload); + + const injector = await web3FromAddress(wallet.account); + const signRaw = injector?.signer?.signRaw; + + if (!signRaw) { + throw new Error('Polkadot.js signer not available'); + } + + // Convert payload to bytes for signing + const payloadBytes = stringToU8a(payload); + + const { signature } = await signRaw({ + address: wallet.account, + data: u8aToHex(payloadBytes), + type: 'bytes' + }); + + console.log('✅ SIWF request signed successfully'); + return signature; + } catch (error) { + console.error('❌ Error signing SIWF request:', error); + throw error; + } + }, [wallet]); + + // Get public key for Polkadot wallet (for SIWF requests) + const getPolkadotPublicKey = useCallback(async () => { + if (!wallet.isConnected || !wallet.account || wallet.walletType !== 'polkadot') { + throw new Error('Polkadot wallet not connected'); + } + + try { + // Get account info from Polkadot extension + const accounts = await web3Accounts(); + const account = accounts.find(acc => acc.address === wallet.account); + + if (!account) { + throw new Error('Account not found in Polkadot extension'); + } + + // The public key is available in the account object + return account.address; // For now, we'll use the address as the public key identifier + } catch (error) { + console.error('❌ Error getting Polkadot public key:', error); + throw error; + } + }, [wallet]); + // Handle wagmi connection success useEffect(() => { if (wagmiIsConnected && wagmiAddress) { @@ -321,5 +402,7 @@ export const useWallet = () => { disconnectWallet, signTypedData, signMessage, + signSiwfRequest, + getPolkadotPublicKey, }; }; \ No newline at end of file From 74537944ed69731d1d4e43c6975d22b2b49e523b Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Thu, 18 Sep 2025 23:02:21 -0400 Subject: [PATCH 14/16] Modify components to add sr25519 support --- .../src/components/SiwfRequestGenerator.tsx | 16 +++++--- .../src/components/SiwfSdkAuth.tsx | 20 ++++++++-- .../src/components/WalletConnect.tsx | 38 +++++++++++++++++++ 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/siwf-v2-reference-app/src/components/SiwfRequestGenerator.tsx b/siwf-v2-reference-app/src/components/SiwfRequestGenerator.tsx index 5dc4355d..84fe130c 100644 --- a/siwf-v2-reference-app/src/components/SiwfRequestGenerator.tsx +++ b/siwf-v2-reference-app/src/components/SiwfRequestGenerator.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { Settings, Copy, ExternalLink, ChevronDown, ChevronUp, Key } from 'lucide-react'; import toast from 'react-hot-toast'; -import { generateSiwfSignedRequest, type SiwfRequestOptions } from '../utils/siwf'; +import { generateSiwfSignedRequest, type SiwfRequestOptions, type SiwfGeneratedRequest } from '../utils/siwf'; import type { WalletType } from '../types'; // SIWF Permission definitions @@ -44,11 +44,15 @@ interface SiwfRequestGeneratorProps { account: string; walletType: WalletType; }; + signSiwfRequest?: (payload: string) => Promise; + getPolkadotPublicKey?: () => Promise; } export const SiwfRequestGenerator: React.FC = ({ onStartAuthentication, - walletInfo + walletInfo, + signSiwfRequest, + getPolkadotPublicKey }) => { // Form state const [callbackUri, setCallbackUri] = useState('http://localhost:5173/login/callback'); @@ -65,10 +69,10 @@ export const SiwfRequestGenerator: React.FC = ({ const [applicationContextUrl, setApplicationContextUrl] = useState(''); // Result state - const [generatedRequest, setGeneratedRequest] = useState(null); + const [generatedRequest, setGeneratedRequest] = useState(null); const [showJson, setShowJson] = useState(false); - const generateSignedRequest = () => { + const generateSignedRequest = async () => { try { // Get permissions from bundle or custom selection let permissions: number[] = []; @@ -94,8 +98,8 @@ export const SiwfRequestGenerator: React.FC = ({ applicationContextUrl: applicationContextUrl || undefined }; - // Generate the signed request - const result = generateSiwfSignedRequest(options, walletInfo); + // Generate the signed request with real signing functions + const result = await generateSiwfSignedRequest(options, walletInfo, signSiwfRequest, getPolkadotPublicKey); setGeneratedRequest(result); toast.success('Signed request generated successfully!'); diff --git a/siwf-v2-reference-app/src/components/SiwfSdkAuth.tsx b/siwf-v2-reference-app/src/components/SiwfSdkAuth.tsx index 00882034..7155cbcc 100644 --- a/siwf-v2-reference-app/src/components/SiwfSdkAuth.tsx +++ b/siwf-v2-reference-app/src/components/SiwfSdkAuth.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { Key, User, Mail, Loader, CheckCircle, XCircle, AlertTriangle, Settings } from 'lucide-react'; +import { Key, User, Loader, CheckCircle, XCircle, AlertTriangle } from 'lucide-react'; import toast from 'react-hot-toast'; import { useWallet } from '../hooks/useWallet'; import { useSiwfSdk } from '../hooks/useSiwfSdk'; @@ -13,7 +13,7 @@ interface SignupFormData { } export const SiwfSdkAuth: React.FC = () => { - const { wallet } = useWallet(); + const { wallet, signMessage, signTypedData, signSiwfRequest, getPolkadotPublicKey } = useWallet(); const { goToStage, currentStage } = useTooltipTour(); const { isLoading, @@ -27,7 +27,12 @@ export const SiwfSdkAuth: React.FC = () => { getUserStatus, reset, clearError - } = useSiwfSdk({ useTestMode: true }); // Use test mode by default + } = useSiwfSdk({ + useTestMode: true, // Use test mode by default + wallet: wallet, + signMessage: signMessage, + signTypedData: signTypedData + }); const [currentSignedRequest, setCurrentSignedRequest] = useState(''); const [userStatus, setUserStatus] = useState<{ @@ -97,6 +102,12 @@ export const SiwfSdkAuth: React.FC = () => { }; const handleAuthenticate = async (signedRequest?: string) => { + console.log('🔐 handleAuthenticate called with wallet state:', { + isConnected: wallet.isConnected, + account: wallet.account, + walletType: wallet.walletType + }); + const requestToUse = signedRequest || currentSignedRequest; if (!requestToUse) { toast.error('No signed request available'); @@ -109,6 +120,7 @@ export const SiwfSdkAuth: React.FC = () => { email: signupData.email } : undefined; + console.log('🔐 Calling authenticateAuto with:', { requestToUse, signupDataToUse }); const result = await authenticateAuto(requestToUse, signupDataToUse); toast.success( @@ -549,6 +561,8 @@ export const SiwfSdkAuth: React.FC = () => { account: wallet.account, walletType: wallet.walletType } : undefined} + signSiwfRequest={signSiwfRequest} + getPolkadotPublicKey={getPolkadotPublicKey} /> )} diff --git a/siwf-v2-reference-app/src/components/WalletConnect.tsx b/siwf-v2-reference-app/src/components/WalletConnect.tsx index da078a8f..b01bf4e0 100644 --- a/siwf-v2-reference-app/src/components/WalletConnect.tsx +++ b/siwf-v2-reference-app/src/components/WalletConnect.tsx @@ -203,6 +203,44 @@ export const WalletConnect: React.FC = () => {

)} + + {/* Troubleshooting section for Polkadot connection issues */} + {isPolkadotAvailable && ( +
+
+ + 🔧 Having trouble connecting? + +
+

+ If you're getting "No accounts found" error: +

+
    +
  1. Open the Polkadot.js extension
  2. +
  3. Create an account if you don't have one
  4. +
  5. Make sure the extension is unlocked
  6. +
  7. Allow the extension to run on this site
  8. +
  9. Refresh this page and try again
  10. +
+
+
+
+ )} From 820975750fe17858beef7728a4f1ef984dc09135 Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Thu, 18 Sep 2025 23:03:18 -0400 Subject: [PATCH 15/16] add eslint fix script --- siwf-v2-reference-app/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/siwf-v2-reference-app/package.json b/siwf-v2-reference-app/package.json index 6b08bc98..2687bd07 100644 --- a/siwf-v2-reference-app/package.json +++ b/siwf-v2-reference-app/package.json @@ -7,6 +7,7 @@ "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", + "lint:fix": "eslint . --fix", "preview": "vite preview" }, "dependencies": { From c2894a6efde9ace20eeb4a00cee07c56924e582a Mon Sep 17 00:00:00 2001 From: emresurmeli Date: Thu, 18 Sep 2025 23:05:21 -0400 Subject: [PATCH 16/16] Modify siwf service to add support for sr25519, still needs further testing, so mock fallback is in place --- siwf-v2-reference-app/src/utils/siwf.ts | 66 ++++++++++++++++--------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/siwf-v2-reference-app/src/utils/siwf.ts b/siwf-v2-reference-app/src/utils/siwf.ts index 9cbbac86..6c47fdd3 100644 --- a/siwf-v2-reference-app/src/utils/siwf.ts +++ b/siwf-v2-reference-app/src/utils/siwf.ts @@ -4,9 +4,7 @@ import type { SiwfOptions, SiwfResult, - WalletType, - GatewayResponse, - MsaResponse + WalletType } from '../types'; // SIWF Permission definitions from documentation @@ -106,10 +104,12 @@ export interface SiwfGeneratedRequest { * Generate SIWF v2 Signed Request * Based on: https://projectlibertylabs.github.io/siwf/v2/docs/SignatureGeneration.html */ -export function generateSiwfSignedRequest( +export async function generateSiwfSignedRequest( options: SiwfRequestOptions, - walletInfo?: { account: string; walletType: WalletType } -): SiwfGeneratedRequest { + walletInfo?: { account: string; walletType: WalletType }, + signSiwfRequest?: (payload: string) => Promise, + getPolkadotPublicKey?: () => Promise +): Promise { console.log("🔐 Generating SIWF signed request with options:", options); console.log("🔐 Using wallet info:", walletInfo); @@ -143,25 +143,42 @@ export function generateSiwfSignedRequest( } } - // For demo purposes, we use consistent mock data for both public key and signature - // In a real implementation, you would: - // 1. Use the actual wallet's public key - // 2. Sign the payload with the actual wallet's private key - // 3. The signature would cryptographically match the public key + // Use real wallet signing for Polkadot wallets + let publicKeyInfo; + let signature; - const publicKeyInfo = { - encodedValue: "f6cL4wq1HUNx11TcvdABNf9UNXXoyH47mVUwT59tzSFRW8yDH", // Mock public key - encoding: "base58", - format: "ss58", - type: "Sr25519" - }; - - // Log the connected wallet info for debugging (but use mock data for the actual request) - if (walletInfo?.account) { - console.log(`🔐 Connected wallet: ${walletInfo.walletType} (${walletInfo.account})`); - console.log("⚠️ Note: Using mock keys for demo - in production, would use actual wallet signing"); + if (walletInfo?.walletType === 'polkadot' && signSiwfRequest && getPolkadotPublicKey) { + console.log(`🔐 Using real sr25519 signing for Polkadot wallet: ${walletInfo.account}`); + + // Get the actual public key from the wallet + const publicKey = await getPolkadotPublicKey(); + publicKeyInfo = { + encodedValue: publicKey, + encoding: "base58", + format: "ss58", + type: "Sr25519" + }; + + // Create the payload to sign + const payload = JSON.stringify({ + callback: options.callbackUri, + permissions: options.permissions + }); + + // Sign the payload with the wallet + signature = await signSiwfRequest(payload); + console.log("✅ Real sr25519 signature generated"); } else { - console.log("⚠️ No wallet connected, using mock public key"); + // Fallback to mock data for demo purposes or when wallet functions are not available + console.log("⚠️ Using mock signature - wallet signing functions not available or not a Polkadot wallet"); + console.log("⚠️ This is for demonstration purposes only. In production, use real wallet signing."); + publicKeyInfo = { + encodedValue: "f6cL4wq1HUNx11TcvdABNf9UNXXoyH47mVUwT59tzSFRW8yDH", // Mock public key for demo + encoding: "base58", + format: "ss58", + type: "Sr25519" + }; + signature = "0x" + "a".repeat(128); // Mock signature for demo } // Build the complete signed request @@ -171,7 +188,7 @@ export function generateSiwfSignedRequest( signature: { algo: "SR25519", encoding: "base16", - encodedValue: "0x" + "a".repeat(128) // Mock signature for demo - matches mock public key + encodedValue: signature }, payload: { callback: options.callbackUri, @@ -382,5 +399,6 @@ export async function siwfLogin( walletType: WalletType ): Promise { console.log("🚀 SIWF Login - use the SIWF Request Generator instead"); + console.log("⚠️ Legacy function - please use generateSiwfSignedRequest with real wallet signing"); throw new Error("Please use the SIWF Request Generator to start authentication"); } \ No newline at end of file