diff --git a/package.json b/package.json index c9783e56..8ddc628e 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@vercel/analytics": "^1.6.1", "@vercel/speed-insights": "^1.3.1", "abitype": "^1.2.3", + "accounts": "^0.4.23", "cva": "1.0.0-beta.4", "mermaid": "^11.12.2", "monaco-editor": "^0.55.1", @@ -33,10 +34,9 @@ "sql-formatter": "^15.7.0", "tailwind-merge": "^3.4.0", "tailwindcss": "^4.1.18", - "tempo.ts": "^0.14.0", "unplugin-auto-import": "^21.0.0", "unplugin-icons": "^23.0.1", - "viem": "^2.47.5", + "viem": "^2.47.10", "vocs": "https://pkg.pr.new/vocs@70a7c8c", "wagmi": "^3.5.0", "waku": "1.0.0-alpha.4", @@ -54,7 +54,8 @@ "tsx": "^4.21.0", "typescript": "^5.9.3", "use-sync-external-store": "^1.6.0", - "vite": "^7.3.1" + "vite": "^7.3.1", + "vite-plugin-mkcert": "^1.17.10" }, "devEngines": { "runtime": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0effa0eb..6e659898 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,6 +37,9 @@ importers: abitype: specifier: ^1.2.3 version: 1.2.3(typescript@5.9.3)(zod@4.3.5) + accounts: + specifier: ^0.4.23 + version: 0.4.23(@modelcontextprotocol/sdk@1.29.0(zod@4.3.5))(@types/react@19.2.9)(@wagmi/core@3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)))(express@5.2.1)(hono@4.11.5)(openapi-types@12.1.3)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)) cva: specifier: 1.0.0-beta.4 version: 1.0.0-beta.4(typescript@5.9.3) @@ -76,9 +79,6 @@ importers: tailwindcss: specifier: ^4.1.18 version: 4.1.18 - tempo.ts: - specifier: ^0.14.0 - version: 0.14.0(@remix-run/headers@0.17.2)(@remix-run/route-pattern@0.15.3)(@remix-run/session@0.4.1)(typescript@5.9.3)(viem@2.47.5(typescript@5.9.3)(zod@4.3.5))(zod@4.3.5) unplugin-auto-import: specifier: ^21.0.0 version: 21.0.0 @@ -86,17 +86,17 @@ importers: specifier: ^23.0.1 version: 23.0.1(@svgr/core@8.1.0(typescript@5.9.3)) viem: - specifier: ^2.47.5 - version: 2.47.5(typescript@5.9.3)(zod@4.3.5) + specifier: ^2.47.10 + version: 2.47.10(typescript@5.9.3)(zod@4.3.5) vocs: specifier: https://pkg.pr.new/vocs@70a7c8c - version: https://pkg.pr.new/vocs@70a7c8c(@types/react@19.2.9)(mermaid@11.12.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(rollup@4.56.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(waku@1.0.0-alpha.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + version: https://pkg.pr.new/vocs@70a7c8c(@types/react@19.2.9)(mermaid@11.12.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(rollup@4.56.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(waku@1.0.0-alpha.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) wagmi: specifier: ^3.5.0 - version: 3.5.0(@tanstack/query-core@5.90.19)(@tanstack/react-query@5.90.19(react@19.2.3))(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(viem@2.47.5(typescript@5.9.3)(zod@4.3.5)) + version: 3.5.0(@tanstack/query-core@5.90.19)(@tanstack/react-query@5.90.19(react@19.2.3))(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)) waku: specifier: 1.0.0-alpha.4 - version: 1.0.0-alpha.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + version: 1.0.0-alpha.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) zod: specifier: ^4.3.5 version: 4.3.5 @@ -121,7 +121,7 @@ importers: version: 7.0.0-dev.20260122.3 '@vitejs/plugin-react': specifier: ^5.1.2 - version: 5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + version: 5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) anser: specifier: ^2.3.5 version: 2.3.5 @@ -139,7 +139,10 @@ importers: version: 1.6.0(react@19.2.3) vite: specifier: ^7.3.1 - version: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + version: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite-plugin-mkcert: + specifier: ^1.17.10 + version: 1.17.10(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) packages: @@ -149,10 +152,20 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + '@apidevtools/json-schema-ref-parser@14.2.1': + resolution: {integrity: sha512-HmdFw9CDYqM6B25pqGBpNeLCKvGPlIx1EbLrVL0zPvj50CJQUHyBNBw45Muk0kEIkogo1VZvOKHajdMuAzSxRg==} + engines: {node: '>= 20'} + peerDependencies: + '@types/json-schema': ^7.0.15 + '@babel/code-frame@7.28.6': resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.6': resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==} engines: {node: '>=6.9.0'} @@ -220,8 +233,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} '@babel/template@7.28.6': @@ -542,12 +555,22 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@hono/node-server@1.19.12': + resolution: {integrity: sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@hono/node-server@1.19.9': resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 + '@humanwhocodes/momoa@2.0.4': + resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} + engines: {node: '>=10.10.0'} + '@iconify-json/lucide@1.2.86': resolution: {integrity: sha512-W/Jz7/gGOkI9u43r+UHmQtZtcyw2YLvMwiHa01WV6V4DYltrPNXiD+bCa+djV8LZB1uwF8CiympOMIbgiQ74nA==} @@ -624,8 +647,8 @@ packages: '@mermaid-js/parser@0.6.3': resolution: {integrity: sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==} - '@modelcontextprotocol/sdk@1.25.3': - resolution: {integrity: sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ==} + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -785,21 +808,36 @@ packages: peerDependencies: react: '>=16.8' - '@remix-run/fetch-router@0.12.0': - resolution: {integrity: sha512-BDG/VepZg2ZJ7wav3HDrB9ZJLpzZONHi9ItOkFMcKsrbm5g7jjrxW5Vdijbbebz12pbJQu6VKTwLVXp/LgFusA==} + '@readme/better-ajv-errors@2.4.0': + resolution: {integrity: sha512-9WODaOAKSl/mU+MYNZ2aHCrkoRSvmQ+1YkLj589OEqqjOAhbn8j7Z+ilYoiTu/he6X63/clsxxAB4qny9/dDzg==} + engines: {node: '>=18'} peerDependencies: - '@remix-run/headers': ^0.17.2 - '@remix-run/route-pattern': ^0.15.3 - '@remix-run/session': ^0.4.0 + ajv: 4.11.8 - 8 - '@remix-run/headers@0.17.2': - resolution: {integrity: sha512-IfHVCftsRKfk7kIQUxP9WDCe0OXj9X0lDRfFxk3CPcXJenBUEsYEPeBoW/YCZlKhdRWZjQlrofdk63lMSJmy8w==} + '@readme/openapi-parser@6.0.0': + resolution: {integrity: sha512-PaTnrKlKgEJZzjJ77AAhGe28NiyLBdiKMx95rJ9xlLZ8QLqYitMpPBQAKhsuEGOWQQbsIMfBZEPavbXghACQHA==} + engines: {node: '>=20'} + peerDependencies: + openapi-types: '>=7' + + '@readme/openapi-schemas@3.1.0': + resolution: {integrity: sha512-9FC/6ho8uFa8fV50+FPy/ngWN53jaUu4GRXlAjcxIRrzhltJnpKkBG2Tp0IDraFJeWrOpk84RJ9EMEEYzaI1Bw==} + engines: {node: '>=18'} + + '@remix-run/fetch-proxy@0.7.1': + resolution: {integrity: sha512-rPLfOpAaCXtm1dLI45uIPKERNbXbrh0P9AJc1sliz8pWd/McaFYjdr5KzB4QrFSfPvEt/Wmy6F2521qB1kK0ug==} + + '@remix-run/fetch-router@0.17.0': + resolution: {integrity: sha512-3FeJGrTqrKKCvZdQWijbCXTEHKcdttkLFbI2ogfpZ+iDYSNZ9036wgDXuuoZqg6d+D0E8Unhk5ZwrLKDCd/hOw==} + + '@remix-run/headers@0.19.0': + resolution: {integrity: sha512-+62NbkXuXm9r/NdG6KfH9OCKofCWm8VjkrVPICiHKtRl8Gf2Vi6eFTN4mGgBlZRhd5mmEVRV4hTIn/JUSHDAOw==} '@remix-run/node-fetch-server@0.13.0': resolution: {integrity: sha512-1EsNo0ZpgXu/90AWoRZf/oE3RVTUS80tiTUpt+hv5pjtAkw7icN4WskDwz/KdAw5ARbJLMhZBrO1NqThmy/McA==} - '@remix-run/route-pattern@0.15.3': - resolution: {integrity: sha512-7s4Oy9q6Oz9Vfwg0iZscpmYVASNG9fLqbCa+YY0+SWKksDpvCRiW46xp3S3zEvT7zEP7G55FKA+JdrqqK2AOXw==} + '@remix-run/route-pattern@0.19.0': + resolution: {integrity: sha512-RXKaIJ2Lx01uyZc0iw+yLzowFCa1/NuB8jN7QTo4QUe2CaUGtvPGdhgrTUp75lyNNCSJIrM9SaAJ6c1pjZdmoA==} '@remix-run/session@0.4.1': resolution: {integrity: sha512-Bm6aKYgutb/raHZ3laloz8g/Qu7f3CeK3o4gUVDMxtEiAdWCzJamwHoTpGOc5+g1Kuy7z85v4M6nGrF06MFDSg==} @@ -1246,6 +1284,9 @@ packages: peerDependencies: react: ^18 || ^19 + '@toon-format/toon@2.1.0': + resolution: {integrity: sha512-JwWptdF5eOA0HaQxbKAzkpQtR4wSWTEfDlEy/y3/4okmOAX1qwnpLZMmtEWr+ncAhTTY1raCKH0kteHhSXnQqg==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1387,6 +1428,9 @@ packages: '@types/node@25.0.10': resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==} + '@types/node@25.5.0': + resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -1629,6 +1673,20 @@ packages: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} + accounts@0.4.23: + resolution: {integrity: sha512-P/lv8ymHx/+8IfdFSNjJSTHsNEtlFDWOwbQb3OBqQ95yKjpl9q83kZFeyIGQl1iLBMuxgRkEsMKf8vnJa6pfBw==} + peerDependencies: + '@wagmi/core': '>=2' + react: '>=18' + viem: '>=2.43.3' + peerDependenciesMeta: + '@wagmi/core': + optional: true + react: + optional: true + viem: + optional: true + acorn-import-phases@1.0.4: resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} engines: {node: '>=10.13.0'} @@ -1649,6 +1707,19 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -1670,8 +1741,8 @@ packages: peerDependencies: ajv: ^8.8.2 - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} anser@2.3.5: resolution: {integrity: sha512-vcZjxvvVoxTeR5XBNJB38oTu/7eDCZlwdz32N1eNgpyPF7j/Z7Idf+CUwQOkKKpJ7RJyjxgLHCM7vdIK0iCNMQ==} @@ -1686,6 +1757,12 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.14.0: + resolution: {integrity: sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==} + bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -1785,6 +1862,10 @@ packages: collapse-white-space@2.1.0: resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -1831,8 +1912,8 @@ packages: core-js@3.48.0: resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} cose-base@1.0.3: @@ -2046,6 +2127,10 @@ packages: delaunator@5.0.1: resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -2095,8 +2180,8 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - enhanced-resolve@5.18.4: - resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} + enhanced-resolve@5.20.1: + resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} entities@4.5.0: @@ -2121,6 +2206,10 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + es5-ext@0.10.64: resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} engines: {node: '>=0.10'} @@ -2236,8 +2325,8 @@ packages: resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} engines: {node: ^18.19.0 || >=20.5.0} - express-rate-limit@7.5.1: - resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + express-rate-limit@8.3.2: + resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' @@ -2293,6 +2382,10 @@ packages: debug: optional: true + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} @@ -2365,6 +2458,10 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -2394,6 +2491,10 @@ packages: resolution: {integrity: sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g==} engines: {node: '>=16.9.0'} + hono@4.12.9: + resolution: {integrity: sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA==} + engines: {node: '>=16.9.0'} + html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} @@ -2417,6 +2518,9 @@ packages: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} + idb-keyval@6.2.2: + resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -2429,6 +2533,10 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + incur@0.3.13: + resolution: {integrity: sha512-ypjWHtDSS8h6TPz+elg9cmQMe7gZu6YpUSAcv4HGIppdQ47VYHXZdho7P4I08CPSzz9VV4tVsWej2pfzXl8jgg==} + hasBin: true + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2446,6 +2554,10 @@ packages: resolution: {integrity: sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==} deprecated: The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019. + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -2499,8 +2611,8 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jose@6.1.3: - resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + jose@6.2.2: + resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2531,6 +2643,10 @@ packages: engines: {node: '>=6'} hasBin: true + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + katex@0.16.28: resolution: {integrity: sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg==} hasBin: true @@ -2548,6 +2664,10 @@ packages: layout-base@2.0.1: resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + lightningcss-android-arm64@1.30.2: resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} engines: {node: '>= 12.0.0'} @@ -2907,6 +3027,25 @@ packages: moo@0.5.2: resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + mppx@0.5.5: + resolution: {integrity: sha512-HKGlnnLpRu6zXZCY17rbZNUC786aook9R5Eul5aafePAigpfK8ytlZ68QeRr11J21iR6je6rIztTk46qKcV79Q==} + hasBin: true + peerDependencies: + '@modelcontextprotocol/sdk': '>=1.25.0' + elysia: '>=1' + express: '>=5' + hono: '>=4' + viem: '>=2.47.5' + peerDependenciesMeta: + '@modelcontextprotocol/sdk': + optional: true + elysia: + optional: true + express: + optional: true + hono: + optional: true + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -3077,6 +3216,9 @@ packages: oniguruma-to-es@4.3.4: resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + outvariant@1.4.0: resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==} @@ -3088,8 +3230,16 @@ packages: typescript: optional: true - ox@0.14.5: - resolution: {integrity: sha512-HgmHmBveYO40H/R3K6TMrwYtHsx/u6TAB+GpZlgJCoW0Sq5Ttpjih0IZZiwGQw7T6vdW4IAyobYrE2mdAvyF8Q==} + ox@0.14.10: + resolution: {integrity: sha512-PYsqEnSP7CrcxISS3uVBtw9yPy2gATAnWNptTI0pMnlrXLTiw0Xw/IIivJVHDFgGvKuRAtBSafhVjs+jis3CVA==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.14.7: + resolution: {integrity: sha512-zSQ/cfBdolj7U4++NAvH7sI+VG0T3pEohITCgcQj8KlawvTDY4vGVhDT64Atsm0d6adWfIYHDpu88iUBMMp+AQ==} peerDependencies: typescript: '>=5.4.0' peerDependenciesMeta: @@ -3129,8 +3279,8 @@ packages: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} engines: {node: '>=12'} - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + path-to-regexp@8.4.1: + resolution: {integrity: sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw==} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -3220,8 +3370,12 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} - qs@6.14.1: - resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + qs@6.15.0: + resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} quansync@0.2.11: @@ -3237,9 +3391,6 @@ packages: resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} engines: {node: '>=0.12'} - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -3382,9 +3533,6 @@ packages: rw@1.3.3: resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -3406,9 +3554,6 @@ packages: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} - serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - serve-static@2.2.1: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} @@ -3544,8 +3689,8 @@ packages: tailwindcss@4.1.18: resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} - tapable@2.3.0: - resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + tapable@2.3.2: + resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==} engines: {node: '>=6'} tar@7.2.0: @@ -3553,16 +3698,8 @@ packages: engines: {node: '>=18'} deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me - tempo.ts@0.14.0: - resolution: {integrity: sha512-tyNg6pomYGqXpiRm0PDLwzOcifd//C9J+B+4rvbIHIwvwqxE1jres1YuaVSayo0JE0hzmXi/HZjJOsbSRdu+kg==} - peerDependencies: - viem: '>=2.43.3' - peerDependenciesMeta: - viem: - optional: true - - terser-webpack-plugin@5.3.16: - resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} + terser-webpack-plugin@5.4.0: + resolution: {integrity: sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -3577,8 +3714,8 @@ packages: uglify-js: optional: true - terser@5.46.0: - resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} + terser@5.46.1: + resolution: {integrity: sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==} engines: {node: '>=10'} hasBin: true @@ -3601,6 +3738,9 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + tokenx@1.3.0: + resolution: {integrity: sha512-NLdXTEZkKiO0gZuLtMoZKjCXTREXeZZt8nnnNeyoXtNZAfG/GKGSbQtLU5STspc0rMSwcA+UJfWZkbNU01iKmQ==} + toml@3.0.0: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} @@ -3654,6 +3794,9 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} @@ -3783,8 +3926,8 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - viem@2.47.5: - resolution: {integrity: sha512-nVrJEQ8GL4JoVIrMBF3wwpTUZun0cpojfnOZ+96GtDWhqxZkVdy6vOEgu+jwfXqfTA/+wrR+YsN9TBQmhDUk0g==} + viem@2.47.10: + resolution: {integrity: sha512-D+l6SDDZWB5bh8u9hgICzMX2/egMrgEQ+Pef/QkZgmOl6bOTyCQMSgWAH8jZTWJ/218J9QNv7s/9BH6Wu5oPDg==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -3794,6 +3937,12 @@ packages: vite-plugin-arraybuffer@0.1.2: resolution: {integrity: sha512-KQLgCTnKJE25blJLvW8smUzhuTQyfcX1+aFqkTRKXpeMeqt6tX/AOGngdBd80oc6o0RX7bEsdaF48EQGXOIPWA==} + vite-plugin-mkcert@1.17.10: + resolution: {integrity: sha512-703hecAoGZYgNrkY76OIbo0M9j0tfGIIM6n7c0sYvhaczQMPwD0nFi+bS44d8kwdbCtV7885FYBWnTzhsaC7QQ==} + engines: {node: '>=v16.7.0'} + peerDependencies: + vite: '>=3' + vite-plugin-wasm@3.5.0: resolution: {integrity: sha512-X5VWgCnqiQEGb+omhlBVsvTfxikKtoOgAzQ95+BZ8gQ+VfMHIjSHr0wyvXFQCa0eKQ0fKyaL0kWcEnYqBac4lQ==} peerDependencies: @@ -3848,7 +3997,7 @@ packages: optional: true vocs@https://pkg.pr.new/vocs@70a7c8c: - resolution: {integrity: sha512-lAJHXJ4gAKQAwwFqpfReXLcEqHTArrOgZwqBga1X3oEOljzzCMXfngLYgOyqlRCKqAQMXiRbxQWMqcaBFKBRVw==, tarball: https://pkg.pr.new/vocs@70a7c8c} + resolution: {tarball: https://pkg.pr.new/vocs@70a7c8c} version: 0.0.0 hasBin: true peerDependencies: @@ -3918,8 +4067,11 @@ packages: web-vitals@4.2.4: resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} - webpack-sources@3.3.3: - resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + webauthx@0.1.0: + resolution: {integrity: sha512-Z43YHetVeXdV5/4+YqKSz+cAflpbMmxSMz//kXEb2u3ZUqVbPG1zrM+Zp7KaME/QgUFGhGkAE2XHwqCAXnU75g==} + + webpack-sources@3.3.4: + resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} engines: {node: '>=10.13.0'} webpack-virtual-modules@0.6.2: @@ -3962,8 +4114,8 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} - yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} engines: {node: '>= 14.6'} hasBin: true @@ -3974,14 +4126,17 @@ packages: zimmerframe@1.1.4: resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} - zod-to-json-schema@3.25.1: - resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} peerDependencies: - zod: ^3.25 || ^4 + zod: ^3.25.28 || ^4 zod@4.3.5: resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zustand@5.0.0: resolution: {integrity: sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==} engines: {node: '>=12.20.0'} @@ -4000,6 +4155,24 @@ packages: use-sync-external-store: optional: true + zustand@5.0.12: + resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -4012,12 +4185,23 @@ snapshots: package-manager-detector: 1.6.0 tinyexec: 1.0.2 + '@apidevtools/json-schema-ref-parser@14.2.1(@types/json-schema@7.0.15)': + dependencies: + '@types/json-schema': 7.0.15 + js-yaml: 4.1.1 + '@babel/code-frame@7.28.6': dependencies: '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.28.6': {} '@babel/core@7.28.6': @@ -4101,7 +4285,7 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - '@babel/runtime@7.28.6': {} + '@babel/runtime@7.29.2': {} '@babel/template@7.28.6': dependencies: @@ -4128,7 +4312,7 @@ snapshots: '@base-ui/react@1.1.0(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@base-ui/utils': 0.2.4(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@floating-ui/react-dom': 2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@floating-ui/utils': 0.2.10 @@ -4142,7 +4326,7 @@ snapshots: '@base-ui/utils@0.2.4(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@floating-ui/utils': 0.2.10 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) @@ -4408,10 +4592,16 @@ snapshots: '@floating-ui/utils@0.2.10': {} + '@hono/node-server@1.19.12(hono@4.12.9)': + dependencies: + hono: 4.12.9 + '@hono/node-server@1.19.9(hono@4.11.5)': dependencies: hono: 4.11.5 + '@humanwhocodes/momoa@2.0.4': {} + '@iconify-json/lucide@1.2.86': dependencies: '@iconify/types': 2.0.0 @@ -4496,7 +4686,7 @@ snapshots: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 '@types/mdx': 2.0.13 - acorn: 8.15.0 + acorn: 8.16.0 collapse-white-space: 2.1.0 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 @@ -4505,7 +4695,7 @@ snapshots: hast-util-to-jsx-runtime: 2.3.6 markdown-extensions: 2.0.0 recma-build-jsx: 1.0.0 - recma-jsx: 1.0.1(acorn@8.15.0) + recma-jsx: 1.0.1(acorn@8.16.0) recma-stringify: 1.0.0 rehype-recma: 1.0.0 remark-mdx: 3.1.1 @@ -4540,26 +4730,49 @@ snapshots: dependencies: langium: 3.3.1 - '@modelcontextprotocol/sdk@1.25.3(hono@4.11.5)(zod@4.3.5)': + '@modelcontextprotocol/sdk@1.29.0(zod@4.3.5)': dependencies: - '@hono/node-server': 1.19.9(hono@4.11.5) - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) + '@hono/node-server': 1.19.12(hono@4.12.9) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 - cors: 2.8.5 + cors: 2.8.6 cross-spawn: 7.0.6 eventsource: 3.0.7 eventsource-parser: 3.0.6 express: 5.2.1 - express-rate-limit: 7.5.1(express@5.2.1) - jose: 6.1.3 + express-rate-limit: 8.3.2(express@5.2.1) + hono: 4.12.9 + jose: 6.2.2 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 zod: 4.3.5 - zod-to-json-schema: 3.25.1(zod@4.3.5) + zod-to-json-schema: 3.25.2(zod@4.3.5) + transitivePeerDependencies: + - supports-color + optional: true + + '@modelcontextprotocol/sdk@1.29.0(zod@4.3.6)': + dependencies: + '@hono/node-server': 1.19.12(hono@4.12.9) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 8.3.2(express@5.2.1) + hono: 4.12.9 + jose: 6.2.2 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.3.6 + zod-to-json-schema: 3.25.2(zod@4.3.6) transitivePeerDependencies: - - hono - supports-color '@monaco-editor/loader@1.7.0': @@ -4706,17 +4919,42 @@ snapshots: dependencies: react: 19.2.3 - '@remix-run/fetch-router@0.12.0(@remix-run/headers@0.17.2)(@remix-run/route-pattern@0.15.3)(@remix-run/session@0.4.1)': + '@readme/better-ajv-errors@2.4.0(ajv@8.18.0)': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.29.2 + '@humanwhocodes/momoa': 2.0.4 + ajv: 8.18.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + picocolors: 1.1.1 + + '@readme/openapi-parser@6.0.0(openapi-types@12.1.3)': + dependencies: + '@apidevtools/json-schema-ref-parser': 14.2.1(@types/json-schema@7.0.15) + '@readme/better-ajv-errors': 2.4.0(ajv@8.18.0) + '@readme/openapi-schemas': 3.1.0 + '@types/json-schema': 7.0.15 + ajv: 8.18.0 + ajv-draft-04: 1.0.0(ajv@8.18.0) + openapi-types: 12.1.3 + + '@readme/openapi-schemas@3.1.0': {} + + '@remix-run/fetch-proxy@0.7.1': + dependencies: + '@remix-run/headers': 0.19.0 + + '@remix-run/fetch-router@0.17.0': dependencies: - '@remix-run/headers': 0.17.2 - '@remix-run/route-pattern': 0.15.3 + '@remix-run/route-pattern': 0.19.0 '@remix-run/session': 0.4.1 - '@remix-run/headers@0.17.2': {} + '@remix-run/headers@0.19.0': {} '@remix-run/node-fetch-server@0.13.0': {} - '@remix-run/route-pattern@0.15.3': {} + '@remix-run/route-pattern@0.19.0': {} '@remix-run/session@0.4.1': {} @@ -4957,7 +5195,7 @@ snapshots: '@tailwindcss/node@4.1.18': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.18.4 + enhanced-resolve: 5.20.1 jiti: 2.6.1 lightningcss: 1.30.2 magic-string: 0.30.21 @@ -5015,12 +5253,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 - '@tailwindcss/vite@4.1.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@tailwindcss/vite@4.1.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@tailwindcss/node': 4.1.18 '@tailwindcss/oxide': 4.1.18 tailwindcss: 4.1.18 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) '@takumi-rs/core-darwin-arm64@0.62.8': optional: true @@ -5074,6 +5312,8 @@ snapshots: '@tanstack/query-core': 5.90.19 react: 19.2.3 + '@toon-format/toon@2.1.0': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.6 @@ -5252,6 +5492,10 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/node@25.5.0': + dependencies: + undici-types: 7.18.2 + '@types/react-dom@19.2.3(@types/react@19.2.9)': dependencies: '@types/react': 19.2.9 @@ -5315,7 +5559,7 @@ snapshots: optionalDependencies: react: 19.2.3 - '@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@babel/core': 7.28.6 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.6) @@ -5323,11 +5567,11 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.53 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-rsc@0.5.21(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@vitejs/plugin-rsc@0.5.21(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.5 es-module-lexer: 2.0.0 @@ -5339,23 +5583,23 @@ snapshots: srvx: 0.11.12 strip-literal: 3.1.0 turbo-stream: 3.1.0 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) - vitefu: 1.1.1(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vitefu: 1.1.1(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) optionalDependencies: react-server-dom-webpack: 19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1) - '@wagmi/connectors@7.2.1(@wagmi/core@3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.47.5(typescript@5.9.3)(zod@4.3.5)))(typescript@5.9.3)(viem@2.47.5(typescript@5.9.3)(zod@4.3.5))': + '@wagmi/connectors@7.2.1(@wagmi/core@3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)))(typescript@5.9.3)(viem@2.47.10(typescript@5.9.3)(zod@4.3.5))': dependencies: - '@wagmi/core': 3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.47.5(typescript@5.9.3)(zod@4.3.5)) - viem: 2.47.5(typescript@5.9.3)(zod@4.3.5) + '@wagmi/core': 3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)) + viem: 2.47.10(typescript@5.9.3)(zod@4.3.5) optionalDependencies: typescript: 5.9.3 - '@wagmi/core@3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.47.5(typescript@5.9.3)(zod@4.3.5))': + '@wagmi/core@3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.47.10(typescript@5.9.3)(zod@4.3.5))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.9.3) - viem: 2.47.5(typescript@5.9.3)(zod@4.3.5) + viem: 2.47.10(typescript@5.9.3)(zod@4.3.5) zustand: 5.0.0(@types/react@19.2.9)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) optionalDependencies: '@tanstack/query-core': 5.90.19 @@ -5452,39 +5696,78 @@ snapshots: typescript: 5.9.3 zod: 4.3.5 + abitype@1.2.3(typescript@5.9.3)(zod@4.3.6): + optionalDependencies: + typescript: 5.9.3 + zod: 4.3.6 + accepts@2.0.0: dependencies: mime-types: 3.0.2 negotiator: 1.0.0 - acorn-import-phases@1.0.4(acorn@8.15.0): + accounts@0.4.23(@modelcontextprotocol/sdk@1.29.0(zod@4.3.5))(@types/react@19.2.9)(@wagmi/core@3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)))(express@5.2.1)(hono@4.11.5)(openapi-types@12.1.3)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)): dependencies: - acorn: 8.15.0 + '@remix-run/fetch-router': 0.17.0 + idb-keyval: 6.2.2 + mipd: 0.0.7(typescript@5.9.3) + mppx: 0.5.5(@modelcontextprotocol/sdk@1.29.0(zod@4.3.5))(express@5.2.1)(hono@4.11.5)(openapi-types@12.1.3)(typescript@5.9.3)(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)) + ox: 0.14.10(typescript@5.9.3)(zod@4.3.6) + tsx: 4.21.0 + webauthx: 0.1.0(typescript@5.9.3)(zod@4.3.6) + zod: 4.3.6 + zustand: 5.0.12(@types/react@19.2.9)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) + optionalDependencies: + '@wagmi/core': 3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)) + react: 19.2.3 + viem: 2.47.10(typescript@5.9.3)(zod@4.3.5) + transitivePeerDependencies: + - '@cfworker/json-schema' + - '@modelcontextprotocol/sdk' + - '@types/react' + - elysia + - express + - hono + - immer + - openapi-types + - supports-color + - typescript + - use-sync-external-store - acorn-jsx@5.3.2(acorn@8.15.0): + acorn-import-phases@1.0.4(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 acorn-loose@8.5.2: dependencies: - acorn: 8.15.0 + acorn: 8.16.0 acorn@8.15.0: {} - ajv-formats@2.1.1(ajv@8.17.1): + acorn@8.16.0: {} + + ajv-draft-04@1.0.0(ajv@8.18.0): optionalDependencies: - ajv: 8.17.1 + ajv: 8.18.0 - ajv-formats@3.0.1(ajv@8.17.1): + ajv-formats@2.1.1(ajv@8.18.0): optionalDependencies: - ajv: 8.17.1 + ajv: 8.18.0 - ajv-keywords@5.1.0(ajv@8.17.1): + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv-keywords@5.1.0(ajv@8.18.0): dependencies: - ajv: 8.17.1 + ajv: 8.18.0 fast-deep-equal: 3.1.3 - ajv@8.17.1: + ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 fast-uri: 3.1.0 @@ -5499,6 +5782,16 @@ snapshots: astring@1.9.0: {} + asynckit@0.4.0: {} + + axios@1.14.0(debug@4.4.3): + dependencies: + follow-redirects: 1.15.11(debug@4.4.3) + form-data: 4.0.5 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + bail@2.0.2: {} base64-js@1.5.1: {} @@ -5513,7 +5806,7 @@ snapshots: http-errors: 2.0.1 iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.14.1 + qs: 6.15.0 raw-body: 3.0.2 type-is: 2.0.1 transitivePeerDependencies: @@ -5594,6 +5887,10 @@ snapshots: collapse-white-space@2.1.0: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + comma-separated-tokens@2.0.3: {} commander@2.20.3: {} @@ -5620,7 +5917,7 @@ snapshots: core-js@3.48.0: {} - cors@2.8.5: + cors@2.8.6: dependencies: object-assign: 4.1.1 vary: 1.1.2 @@ -5861,6 +6158,8 @@ snapshots: dependencies: robust-predicates: 3.0.2 + delayed-stream@1.0.0: {} + depd@2.0.0: {} dequal@2.0.3: {} @@ -5902,10 +6201,10 @@ snapshots: encodeurl@2.0.0: {} - enhanced-resolve@5.18.4: + enhanced-resolve@5.20.1: dependencies: graceful-fs: 4.2.11 - tapable: 2.3.0 + tapable: 2.3.2 entities@4.5.0: {} @@ -5923,6 +6222,13 @@ snapshots: dependencies: es-errors: 1.3.0 + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + es5-ext@0.10.64: dependencies: es6-iterator: 2.0.3 @@ -5951,7 +6257,7 @@ snapshots: esast-util-from-js@2.0.1: dependencies: '@types/estree-jsx': 1.0.5 - acorn: 8.15.0 + acorn: 8.16.0 esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 @@ -6087,9 +6393,10 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.1.2 - express-rate-limit@7.5.1(express@5.2.1): + express-rate-limit@8.3.2(express@5.2.1): dependencies: express: 5.2.1 + ip-address: 10.1.0 express@5.2.1: dependencies: @@ -6113,7 +6420,7 @@ snapshots: once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.14.1 + qs: 6.15.0 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 @@ -6161,7 +6468,17 @@ snapshots: transitivePeerDependencies: - supports-color - follow-redirects@1.15.11: {} + follow-redirects@1.15.11(debug@4.4.3): + optionalDependencies: + debug: 4.4.3 + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 format@0.2.2: {} @@ -6222,6 +6539,10 @@ snapshots: has-symbols@1.1.0: {} + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -6299,6 +6620,8 @@ snapshots: hono@4.11.5: {} + hono@4.12.9: {} + html-void-elements@3.0.0: {} http-errors@2.0.1: @@ -6312,7 +6635,7 @@ snapshots: http-proxy@1.18.1: dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.15.11 + follow-redirects: 1.15.11(debug@4.4.3) requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -6327,6 +6650,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + idb-keyval@6.2.2: {} + ieee754@1.2.1: {} image-size@2.0.2: {} @@ -6336,6 +6661,19 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + incur@0.3.13(openapi-types@12.1.3): + dependencies: + '@modelcontextprotocol/sdk': 1.29.0(zod@4.3.6) + '@readme/openapi-parser': 6.0.0(openapi-types@12.1.3) + '@toon-format/toon': 2.1.0 + tokenx: 1.3.0 + yaml: 2.8.3 + zod: 4.3.6 + transitivePeerDependencies: + - '@cfworker/json-schema' + - openapi-types + - supports-color + inherits@2.0.4: {} inline-style-parser@0.2.7: {} @@ -6346,6 +6684,8 @@ snapshots: intersection-observer@0.10.0: {} + ip-address@10.1.0: {} + ipaddr.js@1.9.1: {} is-alphabetical@2.0.1: {} @@ -6381,13 +6721,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 25.0.10 + '@types/node': 25.5.0 merge-stream: 2.0.0 supports-color: 8.1.1 jiti@2.6.1: {} - jose@6.1.3: {} + jose@6.2.2: {} js-tokens@4.0.0: {} @@ -6407,6 +6747,8 @@ snapshots: json5@2.2.3: {} + jsonpointer@5.0.1: {} + katex@0.16.28: dependencies: commander: 8.3.0 @@ -6425,6 +6767,8 @@ snapshots: layout-base@2.0.1: {} + leven@3.1.0: {} + lightningcss-android-arm64@1.30.2: optional: true @@ -6869,8 +7213,8 @@ snapshots: micromark-extension-mdxjs@3.0.0: dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) micromark-extension-mdx-expression: 3.0.1 micromark-extension-mdx-jsx: 3.0.2 micromark-extension-mdx-md: 2.0.0 @@ -7054,6 +7398,24 @@ snapshots: moo@0.5.2: {} + mppx@0.5.5(@modelcontextprotocol/sdk@1.29.0(zod@4.3.5))(express@5.2.1)(hono@4.11.5)(openapi-types@12.1.3)(typescript@5.9.3)(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)): + dependencies: + '@remix-run/fetch-proxy': 0.7.1 + '@remix-run/node-fetch-server': 0.13.0 + incur: 0.3.13(openapi-types@12.1.3) + ox: 0.14.7(typescript@5.9.3)(zod@4.3.6) + viem: 2.47.10(typescript@5.9.3)(zod@4.3.5) + zod: 4.3.6 + optionalDependencies: + '@modelcontextprotocol/sdk': 1.29.0(zod@4.3.5) + express: 5.2.1 + hono: 4.11.5 + transitivePeerDependencies: + - '@cfworker/json-schema' + - openapi-types + - supports-color + - typescript + ms@2.1.3: {} mz@2.7.0: @@ -7118,6 +7480,8 @@ snapshots: regex: 6.1.0 regex-recursion: 6.0.2 + openapi-types@12.1.3: {} + outvariant@1.4.0: {} ox@0.11.3(typescript@5.9.3)(zod@4.3.5): @@ -7135,7 +7499,22 @@ snapshots: transitivePeerDependencies: - zod - ox@0.14.5(typescript@5.9.3)(zod@4.3.5): + ox@0.14.10(typescript@5.9.3)(zod@4.3.6): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@4.3.6) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.14.7(typescript@5.9.3)(zod@4.3.5): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -7150,6 +7529,21 @@ snapshots: transitivePeerDependencies: - zod + ox@0.14.7(typescript@5.9.3)(zod@4.3.6): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@4.3.6) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + package-manager-detector@1.6.0: {} parent-module@1.0.1: @@ -7168,7 +7562,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.28.6 + '@babel/code-frame': 7.29.0 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -7183,7 +7577,7 @@ snapshots: path-key@4.0.0: {} - path-to-regexp@8.3.0: {} + path-to-regexp@8.4.1: {} path-type@4.0.0: {} @@ -7295,7 +7689,9 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 - qs@6.14.1: + proxy-from-env@2.1.0: {} + + qs@6.15.0: dependencies: side-channel: 1.1.0 @@ -7310,10 +7706,6 @@ snapshots: discontinuous-range: 1.0.0 ret: 0.1.15 - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - range-parser@1.2.1: {} raw-body@3.0.2: @@ -7347,7 +7739,7 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) webpack: 5.104.1 - webpack-sources: 3.3.3 + webpack-sources: 3.3.4 react@19.2.3: {} @@ -7357,10 +7749,10 @@ snapshots: estree-util-build-jsx: 3.0.1 vfile: 6.0.3 - recma-jsx@1.0.1(acorn@8.15.0): + recma-jsx@1.0.1(acorn@8.16.0): dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) estree-util-to-js: 2.0.0 recma-parse: 1.0.0 recma-stringify: 1.0.0 @@ -7457,7 +7849,7 @@ snapshots: toml: 3.0.0 unified: 11.0.5 unist-util-mdx-define: 1.1.2 - yaml: 2.8.2 + yaml: 2.8.3 remark-mdx@3.1.1: dependencies: @@ -7547,7 +7939,7 @@ snapshots: depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 - path-to-regexp: 8.3.0 + path-to-regexp: 8.4.1 transitivePeerDependencies: - supports-color @@ -7555,8 +7947,6 @@ snapshots: rw@1.3.3: {} - safe-buffer@5.2.1: {} - safer-buffer@2.1.2: {} scheduler@0.27.0: {} @@ -7564,9 +7954,9 @@ snapshots: schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 - ajv: 8.17.1 - ajv-formats: 2.1.1(ajv@8.17.1) - ajv-keywords: 5.1.0(ajv@8.17.1) + ajv: 8.18.0 + ajv-formats: 2.1.1(ajv@8.18.0) + ajv-keywords: 5.1.0(ajv@8.18.0) scule@1.3.0: {} @@ -7588,10 +7978,6 @@ snapshots: transitivePeerDependencies: - supports-color - serialize-javascript@6.0.2: - dependencies: - randombytes: 2.1.0 - serve-static@2.2.1: dependencies: encodeurl: 2.0.0 @@ -7742,7 +8128,7 @@ snapshots: tailwindcss@4.1.18: {} - tapable@2.3.0: {} + tapable@2.3.2: {} tar@7.2.0: dependencies: @@ -7753,32 +8139,18 @@ snapshots: mkdirp: 3.0.1 yallist: 5.0.0 - tempo.ts@0.14.0(@remix-run/headers@0.17.2)(@remix-run/route-pattern@0.15.3)(@remix-run/session@0.4.1)(typescript@5.9.3)(viem@2.47.5(typescript@5.9.3)(zod@4.3.5))(zod@4.3.5): - dependencies: - '@remix-run/fetch-router': 0.12.0(@remix-run/headers@0.17.2)(@remix-run/route-pattern@0.15.3)(@remix-run/session@0.4.1) - ox: 0.11.3(typescript@5.9.3)(zod@4.3.5) - optionalDependencies: - viem: 2.47.5(typescript@5.9.3)(zod@4.3.5) - transitivePeerDependencies: - - '@remix-run/headers' - - '@remix-run/route-pattern' - - '@remix-run/session' - - typescript - - zod - - terser-webpack-plugin@5.3.16(webpack@5.104.1): + terser-webpack-plugin@5.4.0(webpack@5.104.1): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 - serialize-javascript: 6.0.2 - terser: 5.46.0 + terser: 5.46.1 webpack: 5.104.1 - terser@5.46.0: + terser@5.46.1: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.15.0 + acorn: 8.16.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -7799,6 +8171,8 @@ snapshots: toidentifier@1.0.1: {} + tokenx@1.3.0: {} + toml@3.0.0: {} trim-lines@3.0.1: {} @@ -7844,6 +8218,8 @@ snapshots: undici-types@7.16.0: {} + undici-types@7.18.2: {} + unicorn-magic@0.3.0: {} unified@11.0.5: @@ -7985,7 +8361,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - viem@2.47.5(typescript@5.9.3)(zod@4.3.5): + viem@2.47.10(typescript@5.9.3)(zod@4.3.5): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 @@ -7993,7 +8369,7 @@ snapshots: '@scure/bip39': 1.6.0 abitype: 1.2.3(typescript@5.9.3)(zod@4.3.5) isows: 1.0.7(ws@8.18.3) - ox: 0.14.5(typescript@5.9.3)(zod@4.3.5) + ox: 0.14.7(typescript@5.9.3)(zod@4.3.5) ws: 8.18.3 optionalDependencies: typescript: 5.9.3 @@ -8004,11 +8380,20 @@ snapshots: vite-plugin-arraybuffer@0.1.2: {} - vite-plugin-wasm@3.5.0(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): + vite-plugin-mkcert@1.17.10(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + dependencies: + axios: 1.14.0(debug@4.4.3) + debug: 4.4.3 + picocolors: 1.1.1 + vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + transitivePeerDependencies: + - supports-color + + vite-plugin-wasm@3.5.0(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) @@ -8021,15 +8406,15 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.30.2 - terser: 5.46.0 + terser: 5.46.1 tsx: 4.21.0 - yaml: 2.8.2 + yaml: 2.8.3 - vitefu@1.1.1(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): + vitefu@1.1.1(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): optionalDependencies: - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vocs@https://pkg.pr.new/vocs@70a7c8c(@types/react@19.2.9)(mermaid@11.12.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(rollup@4.56.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(waku@1.0.0-alpha.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): + vocs@https://pkg.pr.new/vocs@70a7c8c(@types/react@19.2.9)(mermaid@11.12.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(rollup@4.56.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(waku@1.0.0-alpha.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@base-ui/react': 1.1.0(@types/react@19.2.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@codesandbox/sandpack-react': 2.20.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -8041,7 +8426,7 @@ snapshots: '@mdx-js/mdx': 3.1.1 '@mdx-js/react': 3.1.1(@types/react@19.2.9)(react@19.2.3) '@mdx-js/rollup': 3.1.1(rollup@4.56.0) - '@modelcontextprotocol/sdk': 1.25.3(hono@4.11.5)(zod@4.3.5) + '@modelcontextprotocol/sdk': 1.29.0(zod@4.3.6) '@remix-run/node-fetch-server': 0.13.0 '@shikijs/rehype': 3.21.0 '@shikijs/transformers': 3.21.0 @@ -8049,11 +8434,11 @@ snapshots: '@shikijs/types': 3.21.0 '@svgr/core': 8.1.0(typescript@5.9.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) - '@tailwindcss/vite': 4.1.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tailwindcss/vite': 4.1.18(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) '@takumi-rs/image-response': 0.62.8 '@takumi-rs/wasm': 0.62.8 - '@vitejs/plugin-react': 5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) - '@vitejs/plugin-rsc': 0.5.21(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + '@vitejs/plugin-react': 5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitejs/plugin-rsc': 0.5.21(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) cac: 6.7.14 cva: class-variance-authority@0.7.1 debug: 4.4.3 @@ -8062,7 +8447,7 @@ snapshots: estree-util-visit: 2.0.0 extend: 3.0.2 github-slugger: 2.0.0 - hono: 4.11.5 + hono: 4.12.9 image-size: 2.0.2 mdast-util-from-markdown: 2.0.2 mdast-util-gfm: 3.1.0 @@ -8099,13 +8484,13 @@ snapshots: urlpattern-polyfill: 10.1.0 vfile: 6.0.3 vite-plugin-arraybuffer: 0.1.2 - vite-plugin-wasm: 3.5.0(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) - yaml: 2.8.2 - zod: 4.3.5 + vite-plugin-wasm: 3.5.0(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + yaml: 2.8.3 + zod: 4.3.6 optionalDependencies: mermaid: 11.12.2 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) - waku: 1.0.0-alpha.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + waku: 1.0.0-alpha.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - '@cfworker/json-schema' - '@remix-run/react' @@ -8143,14 +8528,14 @@ snapshots: w3c-keyname@2.2.8: {} - wagmi@3.5.0(@tanstack/query-core@5.90.19)(@tanstack/react-query@5.90.19(react@19.2.3))(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(viem@2.47.5(typescript@5.9.3)(zod@4.3.5)): + wagmi@3.5.0(@tanstack/query-core@5.90.19)(@tanstack/react-query@5.90.19(react@19.2.3))(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)): dependencies: '@tanstack/react-query': 5.90.19(react@19.2.3) - '@wagmi/connectors': 7.2.1(@wagmi/core@3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.47.5(typescript@5.9.3)(zod@4.3.5)))(typescript@5.9.3)(viem@2.47.5(typescript@5.9.3)(zod@4.3.5)) - '@wagmi/core': 3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.47.5(typescript@5.9.3)(zod@4.3.5)) + '@wagmi/connectors': 7.2.1(@wagmi/core@3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)))(typescript@5.9.3)(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)) + '@wagmi/core': 3.4.0(@tanstack/query-core@5.90.19)(@types/react@19.2.9)(ox@0.11.3(typescript@5.9.3)(zod@4.3.5))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(viem@2.47.10(typescript@5.9.3)(zod@4.3.5)) react: 19.2.3 use-sync-external-store: 1.4.0(react@19.2.3) - viem: 2.47.5(typescript@5.9.3)(zod@4.3.5) + viem: 2.47.10(typescript@5.9.3)(zod@4.3.5) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -8166,11 +8551,11 @@ snapshots: - ox - porto - waku@1.0.0-alpha.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + waku@1.0.0-alpha.4(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: '@hono/node-server': 1.19.9(hono@4.11.5) - '@vitejs/plugin-react': 5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) - '@vitejs/plugin-rsc': 0.5.21(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + '@vitejs/plugin-react': 5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitejs/plugin-rsc': 0.5.21(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1))(react@19.2.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) dotenv: 17.3.1 hono: 4.11.5 magic-string: 0.30.21 @@ -8179,7 +8564,7 @@ snapshots: react-dom: 19.2.3(react@19.2.3) react-server-dom-webpack: 19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.104.1) rsc-html-stream: 0.0.7 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.0.10)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - '@types/node' - jiti @@ -8201,7 +8586,14 @@ snapshots: web-vitals@4.2.4: {} - webpack-sources@3.3.3: {} + webauthx@0.1.0(typescript@5.9.3)(zod@4.3.6): + dependencies: + ox: 0.14.10(typescript@5.9.3)(zod@4.3.6) + transitivePeerDependencies: + - typescript + - zod + + webpack-sources@3.3.4: {} webpack-virtual-modules@0.6.2: {} @@ -8213,11 +8605,11 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.15.0 - acorn-import-phases: 1.0.4(acorn@8.15.0) + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.4 + enhanced-resolve: 5.20.1 es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -8228,10 +8620,10 @@ snapshots: mime-types: 2.1.35 neo-async: 2.6.2 schema-utils: 4.3.3 - tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(webpack@5.104.1) + tapable: 2.3.2 + terser-webpack-plugin: 5.4.0(webpack@5.104.1) watchpack: 2.5.1 - webpack-sources: 3.3.3 + webpack-sources: 3.3.4 transitivePeerDependencies: - '@swc/core' - esbuild @@ -8249,22 +8641,35 @@ snapshots: yallist@5.0.0: {} - yaml@2.8.2: {} + yaml@2.8.3: {} yoctocolors@2.1.2: {} zimmerframe@1.1.4: {} - zod-to-json-schema@3.25.1(zod@4.3.5): + zod-to-json-schema@3.25.2(zod@4.3.5): dependencies: zod: 4.3.5 + optional: true + + zod-to-json-schema@3.25.2(zod@4.3.6): + dependencies: + zod: 4.3.6 zod@4.3.5: {} + zod@4.3.6: {} + zustand@5.0.0(@types/react@19.2.9)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)): optionalDependencies: '@types/react': 19.2.9 react: 19.2.3 use-sync-external-store: 1.6.0(react@19.2.3) + zustand@5.0.12(@types/react@19.2.9)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)): + optionalDependencies: + '@types/react': 19.2.9 + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 90eddccc..f3b4405e 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,3 +3,5 @@ patchedDependencies: dayjs@1.11.19: patches/dayjs@1.11.19.patch minimumReleaseAge: 1440 +minimumReleaseAgeExclude: + - accounts diff --git a/src/components/guides/AccountsSignIn.tsx b/src/components/guides/AccountsSignIn.tsx new file mode 100644 index 00000000..5c0e9682 --- /dev/null +++ b/src/components/guides/AccountsSignIn.tsx @@ -0,0 +1,41 @@ +'use client' +import { useConnect, useConnection, useConnectors, useDisconnect } from 'wagmi' +import { Button } from './Demo' + +export function AccountsSignIn() { + const account = useConnection() + const connect = useConnect() + const disconnect = useDisconnect() + const connector = useTempoWalletConnector() + + if (!connector) return null + + if (account.address) + return ( +
+ +
+ ) + + if (connect.isPending) + return ( +
+ +
+ ) + + return ( +
+ +
+ ) +} + +function useTempoWalletConnector() { + const connectors = useConnectors() + return connectors.find((c) => c.id === 'xyz.tempo') +} diff --git a/src/components/guides/CreatePasskeyAccount.tsx b/src/components/guides/CreatePasskeyAccount.tsx deleted file mode 100644 index a5882cc4..00000000 --- a/src/components/guides/CreatePasskeyAccount.tsx +++ /dev/null @@ -1,90 +0,0 @@ -'use client' -import { useAccount, useConnect, useConnectors, useDisconnect } from 'wagmi' - -export function Connect() { - const { connect } = useConnect() - const connectors = useConnectors() - - const handleConnect = - ({ type }: { type: 'sign-in' | 'sign-up' }) => - () => { - const connector = connectors.find((c) => c.id === 'webAuthn') - if (connector) { - connect({ capabilities: { type }, connector }) - } else { - console.error('webauthn connector not found') - } - } - - return ( -
- - -
- ) -} - -export function ConnectAndDisconnect() { - const { isConnected } = useAccount() - const { connect } = useConnect() - const connectors = useConnectors() - const { disconnect } = useDisconnect() - - const handleConnect = - ({ type }: { type: 'sign-in' | 'sign-up' }) => - () => { - const connector = connectors.find((c) => c.id === 'webAuthn') - if (connector) { - connect({ capabilities: { type }, connector }) - } else { - console.error('webauthn connector not found') - } - } - - const handleDisconnect = () => { - disconnect() - } - - if (!isConnected) { - return ( -
- - -
- ) - } - - return ( - - ) -} diff --git a/src/components/guides/Demo.tsx b/src/components/guides/Demo.tsx index ac2ebb2e..7b94b16e 100644 --- a/src/components/guides/Demo.tsx +++ b/src/components/guides/Demo.tsx @@ -5,7 +5,7 @@ import * as React from 'react' import type { Address, BaseError } from 'viem' import { formatUnits } from 'viem' import { tempoModerato } from 'viem/chains' -import { useAccount, useConnect, useConnections, useConnectors, useDisconnect } from 'wagmi' +import { useAccount, useConnect, useConnections, useDisconnect } from 'wagmi' import { Hooks } from 'wagmi/tempo' import LucideCheck from '~icons/lucide/check' import LucideCopy from '~icons/lucide/copy' @@ -15,6 +15,7 @@ import LucideRotateCcw from '~icons/lucide/rotate-ccw' import LucideWalletCards from '~icons/lucide/wallet-cards' import { cva, cx } from '../../../cva.config' import { usePostHogTracking } from '../../lib/posthog' +import { useTempoWalletConnector } from '../../wagmi.config' import { Container as ParentContainer } from '../Container' import { alphaUsd } from './tokens' @@ -23,15 +24,6 @@ export { alphaUsd, betaUsd, pathUsd, thetaUsd } from './tokens' export const FAKE_RECIPIENT = '0xbeefcafe54750903ac1c8909323af7beb21ea2cb' export const FAKE_RECIPIENT_2 = '0xdeadbeef54750903ac1c8909323af7beb21ea2cb' -export function useWebAuthnConnector() { - const connectors = useConnectors() - return React.useMemo( - // biome-ignore lint/style/noNonNullAssertion: webAuthn connector always defined in wagmi.config.ts - () => connectors.find((connector) => connector.id === 'webAuthn')!, - [connectors], - ) -} - function getExplorerHost() { const { VITE_TEMPO_ENV, VITE_EXPLORER_OVERRIDE } = import.meta.env @@ -117,12 +109,16 @@ export function Container( if (!source) return address if (source === 'webAuthn') { - const webAuthnConnection = connections.find((c) => c.connector.id === 'webAuthn') + const webAuthnConnection = connections.find( + (c) => c.connector.id === 'webAuthn' || c.connector.id === 'xyz.tempo', + ) return webAuthnConnection?.accounts[0] } if (source === 'wallet') { - const walletConnection = connections.find((c) => c.connector.id !== 'webAuthn') + const walletConnection = connections.find( + (c) => c.connector.id !== 'webAuthn' && c.connector.id !== 'xyz.tempo', + ) return walletConnection?.accounts[0] } @@ -363,7 +359,7 @@ export namespace StringFormatter { export function Login() { const connect = useConnect() - const connector = useWebAuthnConnector() + const connector = useTempoWalletConnector() return (
@@ -373,32 +369,14 @@ export function Login() { Check prompt ) : ( -
- - -
+ )}
) diff --git a/src/components/guides/EmbedPasskeys.tsx b/src/components/guides/EmbedPasskeys.tsx index a11585b0..ad3b1337 100644 --- a/src/components/guides/EmbedPasskeys.tsx +++ b/src/components/guides/EmbedPasskeys.tsx @@ -1,6 +1,7 @@ 'use client' import { useAccount, useConnect, useDisconnect } from 'wagmi' -import { Button, useWebAuthnConnector } from './Demo' +import { useWebAuthnConnector } from '../../wagmi.config' +import { Button } from './Demo' export function EmbedPasskeys() { const account = useAccount() diff --git a/src/components/guides/steps/wallet/AddFundsToWallet.tsx b/src/components/guides/steps/wallet/AddFundsToWallet.tsx index a8f8bf4a..840497bc 100644 --- a/src/components/guides/steps/wallet/AddFundsToWallet.tsx +++ b/src/components/guides/steps/wallet/AddFundsToWallet.tsx @@ -14,7 +14,9 @@ import type { DemoStepProps } from '../types' export function AddFundsToWallet(props: DemoStepProps) { const { stepNumber = 2, last = false } = props const { address, connector } = useConnection() - const hasNonWebAuthnWallet = Boolean(address && connector?.id !== 'webAuthn') + const hasNonWebAuthnWallet = Boolean( + address && connector?.id !== 'webAuthn' && connector?.id !== 'xyz.tempo', + ) const queryClient = useQueryClient() const { data: balance, refetch: balanceRefetch } = Hooks.token.useGetBalance({ diff --git a/src/components/guides/steps/wallet/AddTokensToWallet.tsx b/src/components/guides/steps/wallet/AddTokensToWallet.tsx index 5a833e21..308b697c 100644 --- a/src/components/guides/steps/wallet/AddTokensToWallet.tsx +++ b/src/components/guides/steps/wallet/AddTokensToWallet.tsx @@ -58,7 +58,9 @@ function AddTokenButton(props: { export function AddTokensToWallet(props: DemoStepProps) { const { stepNumber = 3, last = false } = props const { address, connector } = useConnection() - const hasNonWebAuthnWallet = Boolean(address && connector?.id !== 'webAuthn') + const hasNonWebAuthnWallet = Boolean( + address && connector?.id !== 'webAuthn' && connector?.id !== 'xyz.tempo', + ) const [addedTokens, setAddedTokens] = React.useState>(new Set()) const [expanded, setExpanded] = React.useState(false) diff --git a/src/components/guides/steps/wallet/SetFeeToken.tsx b/src/components/guides/steps/wallet/SetFeeToken.tsx index e7ffa936..59c90f61 100644 --- a/src/components/guides/steps/wallet/SetFeeToken.tsx +++ b/src/components/guides/steps/wallet/SetFeeToken.tsx @@ -27,7 +27,9 @@ const DEFAULT_FEE_TOKEN_OPTION = FEE_TOKEN_OPTIONS[0] export function SetFeeToken(props: DemoStepProps) { const { stepNumber = 1 } = props const { address, connector } = useConnection() - const hasNonWebAuthnWallet = Boolean(address && connector?.id !== 'webAuthn') + const hasNonWebAuthnWallet = Boolean( + address && connector?.id !== 'webAuthn' && connector?.id !== 'xyz.tempo', + ) const chainId = useChainId() const config = useConfig() diff --git a/src/components/lib/wallets.ts b/src/components/lib/wallets.ts index d897aa97..56a4e522 100644 --- a/src/components/lib/wallets.ts +++ b/src/components/lib/wallets.ts @@ -4,10 +4,13 @@ const UNSUPPORTED_WALLET_IDS = new Set(['app.phantom']) const UNSUPPORTED_WALLET_NAMES = new Set(['Phantom']) export function filterSupportedInjectedConnectors(connectors: readonly Connector[]) { - return connectors.filter( - (connector) => - connector.id !== 'webAuthn' && - !UNSUPPORTED_WALLET_IDS.has(connector.id) && - !UNSUPPORTED_WALLET_NAMES.has(connector.name), - ) + const seen = new Set() + return connectors.filter((connector) => { + if (connector.id === 'webAuthn') return false + if (UNSUPPORTED_WALLET_IDS.has(connector.id)) return false + if (UNSUPPORTED_WALLET_NAMES.has(connector.name)) return false + if (seen.has(connector.id)) return false + seen.add(connector.id) + return true + }) } diff --git a/src/pages/accounts/api/adapters.mdx b/src/pages/accounts/api/adapters.mdx new file mode 100644 index 00000000..4195d908 --- /dev/null +++ b/src/pages/accounts/api/adapters.mdx @@ -0,0 +1,39 @@ +--- +title: Adapters +description: Pluggable adapters for the Tempo Accounts SDK Provider. +--- + +import { Cards, Card } from 'vocs' + +# Adapters + +Adapters control how accounts are created and managed. Pass an adapter to `Provider.create()` to configure the account management strategy. + +| Adapter | Uses | Best for | +| --- | --- | --- | +| [`dialog`](/accounts/api/dialog) (default) | Hosted dialog | Apps wanting a universal account experience | +| [`webAuthn`](/accounts/api/webAuthn) | Domain-bound passkeys | Wallets integrating Passkey accounts, or Apps wanting to host and manage their own Passkey accounts | +| [`local`](/accounts/api/local) | Arbitrary keys | Custom signing and key management | + +--- + + + + + + diff --git a/src/pages/accounts/api/dialog.iframe.mdx b/src/pages/accounts/api/dialog.iframe.mdx new file mode 100644 index 00000000..d780474f --- /dev/null +++ b/src/pages/accounts/api/dialog.iframe.mdx @@ -0,0 +1,20 @@ +--- +title: Dialog.iframe +description: Embed the Tempo Wallet auth UI in an iframe dialog element. +--- + +# `Dialog.iframe` + +Creates an iframe dialog that embeds the auth app in a `` element. This is the default on most browsers in secure (HTTPS) contexts. + +Falls back to a popup on Safari (which does not support WebAuthn in cross-origin iframes) and insecure (HTTP) origins. + +## Usage + +```ts +import { dialog, Dialog, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: dialog({ dialog: Dialog.iframe() }), +}) +``` diff --git a/src/pages/accounts/api/dialog.mdx b/src/pages/accounts/api/dialog.mdx new file mode 100644 index 00000000..2b2ace93 --- /dev/null +++ b/src/pages/accounts/api/dialog.mdx @@ -0,0 +1,105 @@ +--- +title: dialog +description: Adapter for the Tempo Wallet dialog, an embedded iframe or popup for account management. +--- + +# `dialog` + +Enables universal wallet experiences by delegating signing to an external origin dialog. Also exported as `tempoWallet`. + +## Usage + +```ts twoslash +import { dialog, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: dialog(), +}) +``` + +## Parameters + +### dialog + +- **Type:** `Dialog` +- **Default:** `Dialog.iframe()` (or `Dialog.popup()` in Safari/insecure contexts) + +Dialog to use for the embed app. + +```ts twoslash +import { dialog, Dialog, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: dialog({ + dialog: Dialog.popup(), // [!code focus] + }), +}) +``` + +### host + +- **Type:** `string` +- **Default:** `'https://wallet.tempo.xyz/embed'` + +URL of the embed app. + +```ts twoslash +import { dialog, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: dialog({ + host: 'https://wallet.tempo.xyz/embed', // [!code focus] + }), +}) +``` + +### icon + +- **Type:** `` `data:image/${string}` `` +- **Optional** + +Data URI of the provider icon. + +```ts twoslash +import { dialog, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: dialog({ + icon: 'data:image/svg+xml,...', // [!code focus] + }), +}) +``` + +### name + +- **Type:** `string` +- **Default:** `'Tempo Wallet'` + +Display name of the provider. + +```ts twoslash +import { dialog, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: dialog({ + name: 'My Wallet', // [!code focus] + }), +}) +``` + +### rdns + +- **Type:** `string` +- **Default:** `'xyz.tempo'` + +Reverse DNS identifier for [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) provider discovery. + +```ts twoslash +import { dialog, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: dialog({ + rdns: 'com.example.wallet', // [!code focus] + }), +}) +``` diff --git a/src/pages/accounts/api/dialog.popup.mdx b/src/pages/accounts/api/dialog.popup.mdx new file mode 100644 index 00000000..5d287442 --- /dev/null +++ b/src/pages/accounts/api/dialog.popup.mdx @@ -0,0 +1,27 @@ +--- +title: Dialog.popup +description: Open the Tempo Wallet auth UI in a popup window. +--- + +# `Dialog.popup` + +Opens the auth app in a new browser window. Used as the default on Safari and insecure (HTTP) origins. + +## Usage + +```ts +import { dialog, Dialog, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: dialog({ dialog: Dialog.popup() }), +}) +``` + +## Parameters + +### size + +- **Type:** `{ width: number; height: number }` +- **Default:** `{ width: 360, height: 440 }` + +Popup window dimensions. diff --git a/src/pages/accounts/api/dialogs.mdx b/src/pages/accounts/api/dialogs.mdx new file mode 100644 index 00000000..8eb95ff5 --- /dev/null +++ b/src/pages/accounts/api/dialogs.mdx @@ -0,0 +1,25 @@ +--- +title: Dialog +description: Dialog modes for embedding the Tempo Wallet. +--- + +import { Cards, Card } from 'vocs' + +# Dialog + +Dialog modes control how the Tempo Wallet is embedded into your app. Pass a dialog to the `dialog()` adapter to configure the embed mode. + + + + + diff --git a/src/pages/accounts/api/expiry.mdx b/src/pages/accounts/api/expiry.mdx new file mode 100644 index 00000000..0c5170d6 --- /dev/null +++ b/src/pages/accounts/api/expiry.mdx @@ -0,0 +1,47 @@ +--- +title: Expiry +description: Utility functions for computing access key expiry timestamps. +--- + +# `Expiry` + +Utility functions that return a Unix timestamp (seconds) offset from the current time. Useful for setting access key expiry. + +## Usage + +```ts +import { Expiry } from 'accounts' + +// Access key that expires in 1 day +const expiry = Expiry.days(1) +``` + +## Functions + +### Expiry.seconds(n) + +Returns a Unix timestamp `n` seconds from now. + +### Expiry.minutes(n) + +Returns a Unix timestamp `n` minutes from now. + +### Expiry.hours(n) + +Returns a Unix timestamp `n` hours from now. + +### Expiry.days(n) + +Returns a Unix timestamp `n` days from now. + +### Expiry.weeks(n) + +Returns a Unix timestamp `n` weeks from now. + +### Expiry.months(n) + +Returns a Unix timestamp `n` months (30 days) from now. + +### Expiry.years(n) + +Returns a Unix timestamp `n` years (365 days) from now. diff --git a/src/pages/accounts/api/local.mdx b/src/pages/accounts/api/local.mdx new file mode 100644 index 00000000..48c76dde --- /dev/null +++ b/src/pages/accounts/api/local.mdx @@ -0,0 +1,117 @@ +--- +title: local +description: Key-agnostic adapter for defining arbitrary account types and signing mechanisms. +--- + +# `local` + +Creates a local adapter where the app manages keys and signing in-process. Use this when you need full control over account creation and transaction signing. + +## Usage + +```ts +import { local, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: local({ + loadAccounts: async () => ({ + accounts: [{ address: '0x...' }], + }), + }), +}) +``` + +## Parameters + +### createAccount + +- **Type:** `(params) => Promise<{ accounts: Account[] }>` +- **Optional** + +Create a new account. Omit for login-only flows. + +```ts twoslash +import { local, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: local({ + loadAccounts: async () => ({ accounts: [{ address: '0x...' }] }), + createAccount: async () => ({ // [!code focus] + accounts: [{ address: '0x...' }], // [!code focus] + }), // [!code focus] + }), +}) +``` + +### icon + +- **Type:** `` `data:image/${string}` `` +- **Optional** + +Data URI of the provider icon. + +```ts twoslash +import { local, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: local({ + loadAccounts: async () => ({ accounts: [{ address: '0x...' }] }), + icon: 'data:image/svg+xml,...', // [!code focus] + }), +}) +``` + +### loadAccounts + +- **Type:** `(params?) => Promise<{ accounts: Account[] }>` +- **Required** + +Discover existing accounts (e.g. via WebAuthn assertion or key lookup). + +```ts twoslash +import { local, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: local({ + loadAccounts: async () => ({ // [!code focus] + accounts: [{ address: '0x...' }], // [!code focus] + }), // [!code focus] + }), +}) +``` + +### name + +- **Type:** `string` +- **Default:** `'Injected Wallet'` + +Display name of the provider. + +```ts twoslash +import { local, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: local({ + loadAccounts: async () => ({ accounts: [{ address: '0x...' }] }), + name: 'My Wallet', // [!code focus] + }), +}) +``` + +### rdns + +- **Type:** `string` +- **Optional** + +Reverse DNS identifier. + +```ts twoslash +import { local, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: local({ + loadAccounts: async () => ({ accounts: [{ address: '0x...' }] }), + rdns: 'com.example.wallet', // [!code focus] + }), +}) +``` diff --git a/src/pages/accounts/api/provider.mdx b/src/pages/accounts/api/provider.mdx new file mode 100644 index 00000000..6e880841 --- /dev/null +++ b/src/pages/accounts/api/provider.mdx @@ -0,0 +1,184 @@ +--- +title: Provider +description: Create an EIP-1193 provider for managing accounts on Tempo. +--- + +# `Provider` + +Creates an EIP-1193 provider with a pluggable adapter for managing accounts on Tempo. + +## Usage + +```ts twoslash +import { Provider } from 'accounts' + +const provider = Provider.create() +``` + +### Using JSON-RPC Methods + +You can interact with accounts via the provider's `request` method. + +```ts twoslash +// @noErrors +import { Provider } from 'accounts' + +const provider = Provider.create() + +const { accounts } = await provider.request({ // [!code focus] + method: 'wallet_connect', // [!code focus] +}) // [!code focus] +``` + +### Using Viem Actions + +If you prefer to use Viem actions over raw JSON-RPC calls, the provider exposes a `getClient` accessor. + +```ts twoslash +// @noErrors +import { parseUnits } from 'viem' +import { tempoActions } from 'viem/tempo' +import { Provider } from 'accounts' + +const provider = Provider.create() + +const client = provider.getClient().extend(tempoActions()) // [!code focus] + +const { receipt } = await client.token.transferSync({ // [!code focus] + amount: parseUnits('100', 6), // [!code focus] + to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb', // [!code focus] + token: '0x20c0000000000000000000000000000000000001', // [!code focus] +}) // [!code focus] +``` + +## Parameters + +### adapter + +- **Type:** `Adapter` +- **Default:** `dialog()` + +Adapter to use for account management. + +```ts twoslash +import { Provider, webAuthn } from 'accounts' + +const provider = Provider.create({ + adapter: webAuthn({ authUrl: '/auth' }), // [!code focus] +}) +``` + +### authorizeAccessKey + +- **Type:** `() => { expiry: number; limits?: { token: Address; limit: bigint }[] }` +- **Optional** + +Default access key parameters for `wallet_connect`. When set, `wallet_connect` will automatically authorize an access key. + +```ts twoslash +import { parseUnits } from 'viem' +import { Expiry, Provider } from 'accounts' + +const provider = Provider.create({ + authorizeAccessKey: () => ({ // [!code focus] + expiry: Expiry.days(7), // [!code focus] + limits: [{ // [!code focus] + token: '0x20c0000000000000000000000000000000000001', // [!code focus] + limit: parseUnits('500', 6), // [!code focus] + }], // [!code focus] + }), // [!code focus] +}) +``` + +### chains + +- **Type:** `readonly [Chain, ...Chain[]]` +- **Default:** `[tempo, tempoModerato]` + +Supported chains. The first chain is the default. + +```ts twoslash +import { Provider } from 'accounts' +import { tempo } from 'viem/chains' + +const provider = Provider.create({ + chains: [tempo], // [!code focus] +}) +``` + +### feePayerUrl + +- **Type:** `string` +- **Optional** + +Fee payer URL for interacting with a service running [`Handler.feePayer`](/accounts/server/handler.feePayer) from `accounts/server`. + +```ts twoslash +import { Provider } from 'accounts' + +const provider = Provider.create({ + feePayerUrl: 'https://myapp.com/fee-payer', // [!code focus] +}) +``` + +### mpp + +- **Type:** `boolean` +- **Default:** `false` + +Enable [Machine Payment Protocol](https://mpp.dev) support. + +```ts twoslash +import { Provider } from 'accounts' + +const provider = Provider.create({ + mpp: true, // [!code focus] +}) +``` + +### storage + +- **Type:** `Storage` +- **Default:** `Storage.idb()` in browser, `Storage.memory()` otherwise + +Storage adapter for persistence. + +```ts twoslash +import { Provider, Storage } from 'accounts' + +const provider = Provider.create({ + storage: Storage.memory(), // [!code focus] +}) +``` + +### testnet + +- **Type:** `boolean` +- **Default:** `false` + +Use testnet. When `true`, the default chain will be the first testnet chain in `chains`. + +```ts twoslash +import { Provider } from 'accounts' + +const provider = Provider.create({ + testnet: true, // [!code focus] +}) +``` + +## Return Type + +```ts +type ReturnType = { + /** EIP-1193 request method. */ + request(args: RequestArguments): Promise + /** Returns a Viem Client for the current (or specified) chain. */ + getClient(options?: { chainId?: number }): Client + /** Returns a Viem Account for the given address (or active account). */ + getAccount(options?: { address?: Address }): Account + /** Reactive state store with account and chain state. */ + store: Store + /** Configured chains. */ + chains: readonly [Chain, ...Chain[]] +} +``` diff --git a/src/pages/accounts/api/webAuthn.mdx b/src/pages/accounts/api/webAuthn.mdx new file mode 100644 index 00000000..b475acba --- /dev/null +++ b/src/pages/accounts/api/webAuthn.mdx @@ -0,0 +1,116 @@ +--- +title: webAuthn +description: Adapter for passkey-based accounts using WebAuthn registration and authentication. +--- + +# `webAuthn` + +Enables domain-bound passkey experiences by wrapping the `local` adapter with WebAuthn registration and authentication flows. + +## Usage + +```ts +import { webAuthn, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: webAuthn({ authUrl: '/auth' }), +}) +``` + +## Parameters + +### authUrl + +- **Type:** `string` +- **Optional** + +URL of a [WebAuthn server handler](/accounts/server/handler.webAuthn) (shorthand for `WebAuthnCeremony.server({ url }){:js}`) + +:::warning +Cannot be used with `ceremony`. +::: + +```ts twoslash +import { webAuthn, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: webAuthn({ + authUrl: '/auth', // [!code focus] + }), +}) +``` + +### ceremony + +- **Type:** `WebAuthnCeremony` +- **Default:** `WebAuthnCeremony.server({ url: authUrl }){:js}` + +Ceremony strategy for WebAuthn registration and authentication. [See more](/accounts/api/webauthnceremony). + +:::warning +Cannot be used with `authUrl`. +::: + +```ts twoslash +import { webAuthn, WebAuthnCeremony, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: webAuthn({ + ceremony: WebAuthnCeremony.server({ url: '/auth' }), // [!code focus] + }), +}) +``` + +### icon + +- **Type:** `` `data:image/${string}` `` +- **Optional** + +Data URI of the provider icon. + +```ts twoslash +import { webAuthn, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: webAuthn({ + authUrl: '/auth', + icon: 'data:image/svg+xml,...', // [!code focus] + }), +}) +``` + +### name + +- **Type:** `string` +- **Default:** `'Injected Wallet'` + +Display name of the provider. + +```ts twoslash +import { webAuthn, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: webAuthn({ + authUrl: '/auth', + name: 'My Wallet', // [!code focus] + }), +}) +``` + +### rdns + +- **Type:** `string` +- **Optional** + +Reverse DNS identifier. + +```ts twoslash +import { webAuthn, Provider } from 'accounts' + +const provider = Provider.create({ + adapter: webAuthn({ + authUrl: '/auth', + rdns: 'com.example.wallet', // [!code focus] + }), +}) +``` diff --git a/src/pages/accounts/api/webauthnceremony.from.mdx b/src/pages/accounts/api/webauthnceremony.from.mdx new file mode 100644 index 00000000..f8e6b4a2 --- /dev/null +++ b/src/pages/accounts/api/webauthnceremony.from.mdx @@ -0,0 +1,47 @@ +--- +title: WebAuthnCeremony.from +description: Create a WebAuthnCeremony from a custom implementation. +--- + +# `WebAuthnCeremony.from` + +Creates a `WebAuthnCeremony` from a custom implementation. + +## Usage + +```ts +import { WebAuthnCeremony } from 'accounts' + +const ceremony = WebAuthnCeremony.from({ + async getRegistrationOptions(params) { /* ... */ }, + async verifyRegistration(credential) { /* ... */ }, + async getAuthenticationOptions(params) { /* ... */ }, + async verifyAuthentication(response) { /* ... */ }, +}) +``` + +## Properties + +### getRegistrationOptions + +- **Type:** `(params: { name: string; userId?: string; excludeCredentialIds?: readonly string[] }) => Promise<{ options: Registration.Options }>` + +Get credential creation options for `navigator.credentials.create(){:js}`. + +### verifyRegistration + +- **Type:** `(credential: Registration.Credential, options?: { name?: string }) => Promise<{ credentialId: string; publicKey: Hex }>` + +Verify a registration response and extract the public key. + +### getAuthenticationOptions + +- **Type:** `(params?: { allowCredentialIds?: readonly string[]; challenge?: Hex; credentialId?: string; mediation?: 'conditional' | 'optional' | 'required' | 'silent' }) => Promise<{ options: Authentication.Options }>` + +Get credential request options for `navigator.credentials.get(){:js}`. + +### verifyAuthentication + +- **Type:** `(response: Authentication.Response) => Promise<{ credentialId: string; publicKey: Hex; userId?: string }>` + +Verify an authentication response and extract the public key. diff --git a/src/pages/accounts/api/webauthnceremony.mdx b/src/pages/accounts/api/webauthnceremony.mdx new file mode 100644 index 00000000..d7002b1d --- /dev/null +++ b/src/pages/accounts/api/webauthnceremony.mdx @@ -0,0 +1,25 @@ +--- +title: WebAuthnCeremony +description: Pluggable strategy for WebAuthn registration and authentication ceremonies. +--- + +import { Cards, Card } from 'vocs' + +# `WebAuthnCeremony` + +Pluggable strategy for WebAuthn registration and authentication ceremonies. A `WebAuthnCeremony` controls how challenges are generated and responses are verified during passkey registration and login. + + + + + diff --git a/src/pages/accounts/api/webauthnceremony.server.mdx b/src/pages/accounts/api/webauthnceremony.server.mdx new file mode 100644 index 00000000..9f64317b --- /dev/null +++ b/src/pages/accounts/api/webauthnceremony.server.mdx @@ -0,0 +1,25 @@ +--- +title: WebAuthnCeremony.server +description: Server-backed WebAuthn ceremony that delegates to a remote handler. +--- + +# `WebAuthnCeremony.server` + +Creates a server-backed ceremony that delegates to a remote [WebAuthn server handler](/accounts/server/handler.webAuthn). All challenge generation, verification, and credential storage happen server-side. + +## Usage + +```ts +import { WebAuthnCeremony } from 'accounts' + +const ceremony = WebAuthnCeremony.server({ url: 'https://example.com/webauthn' }) +``` + +## Parameters + +### url + +- **Type:** `string` +- **Required** + +Base URL of the WebAuthn handler (e.g. `"https://example.com/webauthn"{:js}`). diff --git a/src/pages/accounts/faq.mdx b/src/pages/accounts/faq.mdx new file mode 100644 index 00000000..c9038293 --- /dev/null +++ b/src/pages/accounts/faq.mdx @@ -0,0 +1,54 @@ +--- +title: FAQ +description: Frequently asked questions about the Tempo Accounts SDK. +--- + +# Frequently Asked Questions + +## What adapter should I use? + +Most apps should use [Tempo Wallet](/accounts/api/dialog) - it provides a universal account experience with a central account for users that includes embedded onramp, access keys, and transaction orchestration out of the box. + +Use [domain-bound passkeys](/accounts/api/webAuthn) when you want to build your own wallet experience or when your app needs to manage and own the WebAuthn ceremony directly. + +[Learn more](/accounts/guides/create-and-use-accounts). + +## Which browsers are supported? + +The Tempo Accounts SDK supports the following browsers: + +- Safari +- Chrome +- Firefox +- Brave +- Arc + +If a browser is not listed above, it likely works but is not officially supported. + +## Which password managers are supported? + +The Tempo Accounts SDK supports the following password managers: + +- iCloud Keychain +- Google Password Manager +- 1Password +- Bitwarden + +If a password manager is not listed above, it likely works but is not officially supported. + +## Which operating systems are supported? + +The Tempo Accounts SDK supports the following operating systems: + +- iOS +- iPadOS +- macOS +- Android +- Linux +- Windows + +If an operating system is not listed above, it likely works but is not officially supported. + +## How does the Tempo Accounts SDK work with my Content Security Policy? + +If you've deployed a Content Security Policy, see the [Deploying to Production](/accounts/production) page for the full set of required directives. diff --git a/src/pages/accounts/guides/create-and-use-accounts.mdx b/src/pages/accounts/guides/create-and-use-accounts.mdx new file mode 100644 index 00000000..00ee504a --- /dev/null +++ b/src/pages/accounts/guides/create-and-use-accounts.mdx @@ -0,0 +1,45 @@ +--- +title: Create & Use Accounts +description: Choose between universal wallet experiences or domain-bound passkey accounts for your app. +--- + +import { Cards, Card } from 'vocs' + +# Create & Use Accounts + +Create and integrate Tempo accounts into your product with the universal Tempo Wallet or domain-bound passkeys. + +## What should I use? + +### Tempo Wallet + +Most apps should use [Tempo Wallet](/accounts/api/dialog) - it provides a universal account experience with a central account for users that includes embedded onramp, access keys, and transaction orchestration out of the box. + +### Domain-bound Passkeys + +Use [domain-bound passkeys](/accounts/api/webAuthn) when you want to build your own wallet experience or when your app needs to manage and own the WebAuthn ceremony directly. + +:::info +Tempo Wallet uses domain-bound passkeys (on `tempo.xyz`) under the hood. +::: + +### Other Wallets + +Offer users a range of existing wallets to connect from, such as MetaMask, Coinbase Wallet, and others. [See the guide](/guide/use-accounts/connect-to-wallets). + +--- + + + + + diff --git a/src/pages/accounts/index.mdx b/src/pages/accounts/index.mdx new file mode 100644 index 00000000..ab782166 --- /dev/null +++ b/src/pages/accounts/index.mdx @@ -0,0 +1,261 @@ +--- +title: Accounts SDK – Getting Started +description: Set up the Tempo Accounts SDK to create, manage, and interact with accounts on Tempo. +--- + +import { Cards, Card } from 'vocs' +import { AccountsSignIn } from '../../components/guides/AccountsSignIn' + +# Getting Started + +## Overview + +The Tempo Accounts SDK is a TypeScript library for applications and wallets to create, manage, and interact with accounts on Tempo. + +Try it out by logging into Tempo Wallet below: + + + +## Quick Prompt + +Prompt your agent: + +``` +Read docs.tempo.xyz/accounts and integrate Tempo Wallet into my application +``` + +## Install + +The Tempo Accounts SDK is available as an [NPM package](https://www.npmjs.com/package/accounts) under `accounts` + +:::code-group +```bash [npm] +npm i accounts +``` +```bash [pnpm] +pnpm i accounts +``` +```bash [bun] +bun i accounts +``` +::: + +## Wagmi Usage + +The Tempo Accounts SDK is best used in conjunction with [Wagmi](https://wagmi.sh/) to provide a seamless experience for developers and end-users. + +::::steps + +### Set up Wagmi + +Get started with Wagmi by following the [official guide](https://wagmi.sh/react/getting-started). + +### Configure + +After you have set up Wagmi, you can set up the Tempo Accounts SDK by using a Connector from `accounts/wagmi`. + +:::code-group +```tsx twoslash [Tempo Wallet] +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { tempoWallet } from 'accounts/wagmi' // [!code hl] + +export const wagmiConfig = createConfig({ + chains: [tempo], + connectors: [tempoWallet()], // [!code hl] + transports: { + [tempo.id]: http(), + }, +}) +``` +```tsx twoslash [Domain-bound Passkeys] +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { webAuthn } from 'accounts/wagmi' // [!code hl] + +export const wagmiConfig = createConfig({ + chains: [tempo], + connectors: [webAuthn({ authUrl: '/auth' })], // [!code hl] + transports: { + [tempo.id]: http(), + }, +}) +``` +::: + +:::info[What connector should I use?] +Most apps should use **Tempo Wallet** - it provides a universal account experience with embedded onramp, access keys, and transaction orchestration. **Domain-bound Passkeys** is for apps that want to manage domain-bound passkey accounts directly. [Learn more](/accounts/faq#what-connector-should-i-use). +::: + +:::tip +If you are using a wallet connection library and cannot supply a custom connector, +you can use `Provider.create()` to create a new provider instance, and inject itself +into the wallet connection library via [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963). + +```tsx +import { Provider } from 'accounts' +Provider.create() +``` +::: + +### Use Accounts + +You can now use [Wagmi Hooks](https://wagmi.sh/react/api/hooks) like `useConnect`, or [Tempo Hooks](https://wagmi.sh/tempo) like `useTransfer`. + +```tsx twoslash +// @noErrors +import { useConnect, useConnectors } from 'wagmi' + +function Connect() { + const connect = useConnect() + const connectors = useConnectors() + + return connectors?.map((connector) => ( + + )) +} +``` + +### Next Steps + + + + + + + + + + +:::: + +## Vanilla + Viem Usage + +You can get started with the Tempo Accounts SDK by creating a new `Provider` instance. +Once set up, you can use the provider to interact with accounts via JSON-RPC, or use [Viem Actions](https://viem.sh/tempo) with `.getClient(){:js}`. + +```tsx twoslash +// @noErrors +import { Provider } from 'accounts' +import { parseUnits } from 'viem' +import { tempoActions } from 'viem/tempo' + +const provider = Provider.create() + +const { accounts } = await provider.request({ + method: 'wallet_connect', +}) + +const client = provider.getClient().extend(tempoActions()) + +const { receipt } = await client.token.transferSync({ + token: '0x20c0000000000000000000000000000000000001', + to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb', + amount: parseUnits('10', 6), +}) +``` + +## Examples + +Check out these examples to get started with the Tempo Accounts SDK. + +| Example | Description | | +| --- | --- | --- | +| [Embed Tempo Wallet](https://github.com/tempoxyz/accounts/tree/main/examples/basic) | Wagmi-based setup using the `tempoWallet` connector to connect to Tempo Wallets. | [Guide](/guide/use-accounts/embed-tempo-wallet) | +| [Embed Domain-bound Passkeys](https://github.com/tempoxyz/accounts/tree/main/examples/domain-bound-webauthn) | Domain-bound passkey example using Wagmi and the `webAuthn` connector. | [Guide](/guide/use-accounts/embed-passkeys) | +| [CLI + Tempo Wallets](https://github.com/tempoxyz/accounts/tree/main/examples/cli) | Minimal CLI setup to connect and authorize local keys using Tempo Wallets. | | +| [Access Keys + Tempo Wallets](https://github.com/tempoxyz/accounts/tree/main/examples/with-access-key) | Authorize access keys using Tempo Wallets to submit transactions without prompts. | | +| [Access Keys + Domain-bound Passkeys](https://github.com/tempoxyz/accounts/tree/main/examples/with-access-key-and-webauthn) | Authorize access keys using domain-bound Passkeys. | | +| [Sponsor Fees + Tempo Wallets](https://github.com/tempoxyz/accounts/tree/main/examples/with-fee-payer) | Sponsor transactions via Tempo Wallets. | [Guide](/guide/payments/sponsor-user-fees) | +| [Sponsor Fees + Domain-bound Passkeys](https://github.com/tempoxyz/accounts/tree/main/examples/with-fee-payer-and-webauthn) | Sponsor transactions via domain-bound Passkeys. | [Guide](/guide/payments/sponsor-user-fees) | + +## Secure Origins (HTTPS) + +The Tempo Accounts SDK is designed to be used on secure origins (HTTPS). If you are using HTTP, +it will fallback to using a popup instead of an iframe. This is because +WebAuthn is not supported on iframes embedded on insecure origins (HTTP). + +Web frameworks typically default to HTTP in development environments. You +will need to ensure to turn on HTTPS in development to leverage the iframe dialog. + +### Portless + +[Portless](https://github.com/vercel-labs/portless) replaces port numbers with stable, named `.localhost` URLs and can enable HTTPS with auto-generated certificates. + +```sh +npm install -g portless +portless run dev +# → https://myapp.localhost +``` + +### Next.js + +HTTPS can be enabled on Next.js' dev server by setting the `--experimental-https` flag on the `next dev` command. + +```bash +next dev --experimental-https +``` + +### Vite + +HTTPS can be enabled on Vite's dev server by installing and configuring the `vite-plugin-mkcert` plugin. + +```bash +npm i vite-plugin-mkcert +``` + +:::code-group +```ts [vite.config.ts] +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import mkcert from 'vite-plugin-mkcert' + +export default defineConfig({ + plugins: [ + mkcert(), + react(), + ], +}) +``` +::: + +## Getting Help + +Have questions or building something cool with the Accounts SDK? + +Join the Telegram group to chat with the team and other devs: [@mpp_devs](https://t.me/mpp_devs) diff --git a/src/pages/accounts/production.mdx b/src/pages/accounts/production.mdx new file mode 100644 index 00000000..52d8d25d --- /dev/null +++ b/src/pages/accounts/production.mdx @@ -0,0 +1,24 @@ +--- +title: Deploying to Production +description: Things to consider before deploying your application with the Tempo Accounts SDK to production. +--- + +# Deploying to Production + +Below are some things to consider before deploying to production. + +## Trusted Hosts + +To enable the `iframe` dialog on all browsers in production, ensure your website hostname is added to the [trusted hosts list](https://github.com/tempoxyz/accounts/blob/main/src/trusted-hosts.json). + +Without this, the `iframe` dialog will fallback to a popup on browsers that do not support the [IntersectionObserver v2 API](https://web.dev/articles/intersectionobserver-v2). + +## Content Security Policy + +If you've deployed a Content Security Policy, ensure that you have the Tempo Accounts SDK configured with your CSPs. + +The full set of directives that the Tempo Accounts SDK requires are: + +| Directive | Value | +| --- | --- | +| `frame-src` | `https://wallet.tempo.xyz` | diff --git a/src/pages/accounts/rpc/eth_fillTransaction.mdx b/src/pages/accounts/rpc/eth_fillTransaction.mdx new file mode 100644 index 00000000..8fb027c4 --- /dev/null +++ b/src/pages/accounts/rpc/eth_fillTransaction.mdx @@ -0,0 +1,47 @@ +--- +title: eth_fillTransaction +description: Fill missing transaction fields like gas and nonce via the node. +--- + +# `eth_fillTransaction` + +Fills in missing transaction fields (gas, nonce, fees) by querying the node. This is a node RPC method, not handled by the accounts provider directly. + +## Request + +```ts +type Request = { + method: 'eth_fillTransaction' + params: [TransactionRequest] +} +``` + +## Response + +```ts +type Response = { + raw: Hex + tx: TransactionRequest +} +``` + +## Example + +```ts +import { createPublicClient, http } from 'viem' +import { tempo } from 'viem/chains' + +const client = createPublicClient({ + chain: tempo, + transport: http(), +}) + +const filled = await client.request({ + method: 'eth_fillTransaction', + params: [{ + from: '0x...', + to: '0x...', + value: '0x0', + }], +}) +``` diff --git a/src/pages/accounts/rpc/eth_sendTransaction.mdx b/src/pages/accounts/rpc/eth_sendTransaction.mdx new file mode 100644 index 00000000..7f1e57d8 --- /dev/null +++ b/src/pages/accounts/rpc/eth_sendTransaction.mdx @@ -0,0 +1,36 @@ +--- +title: eth_sendTransaction +description: Send a transaction from the connected account. +--- + +# `eth_sendTransaction` + +Signs and broadcasts a transaction, returning the transaction hash. + +## Request + +```ts +type Request = { + method: 'eth_sendTransaction' + params: [TransactionRequest] +} +``` + +## Response + +```ts +type Response = Hex // transaction hash +``` + +## Example + +```ts +const hash = await provider.request({ + method: 'eth_sendTransaction', + params: [{ + to: '0x...', + value: '0x0', + data: '0x...', + }], +}) +``` diff --git a/src/pages/accounts/rpc/eth_sendTransactionSync.mdx b/src/pages/accounts/rpc/eth_sendTransactionSync.mdx new file mode 100644 index 00000000..90b902c4 --- /dev/null +++ b/src/pages/accounts/rpc/eth_sendTransactionSync.mdx @@ -0,0 +1,36 @@ +--- +title: eth_sendTransactionSync +description: Send a transaction and wait for the receipt. +--- + +# `eth_sendTransactionSync` + +Signs, broadcasts, and waits for a transaction receipt. Returns the full receipt instead of just the hash. + +## Request + +```ts +type Request = { + method: 'eth_sendTransactionSync' + params: [TransactionRequest] +} +``` + +## Response + +```ts +type Response = TransactionReceipt +``` + +## Example + +```ts +const receipt = await provider.request({ + method: 'eth_sendTransactionSync', + params: [{ + to: '0x...', + value: '0x0', + data: '0x...', + }], +}) +``` diff --git a/src/pages/accounts/rpc/personal_sign.mdx b/src/pages/accounts/rpc/personal_sign.mdx new file mode 100644 index 00000000..3869d335 --- /dev/null +++ b/src/pages/accounts/rpc/personal_sign.mdx @@ -0,0 +1,40 @@ +--- +title: personal_sign +description: Sign a message with the connected account. +--- + +# `personal_sign` + +Signs an arbitrary message with the connected account's key. + +## Request + +```ts +type Request = { + method: 'personal_sign' + params: [ + Hex, // message + Address // signer address + ] +} +``` + +## Response + +```ts +type Response = Hex // signature +``` + +## Example + +```ts +import { Hex } from 'ox' + +const signature = await provider.request({ + method: 'personal_sign', + params: [ + Hex.fromString('Hello, Tempo!'), + '0x...', + ], +}) +``` diff --git a/src/pages/accounts/rpc/wallet_authorizeAccessKey.mdx b/src/pages/accounts/rpc/wallet_authorizeAccessKey.mdx new file mode 100644 index 00000000..58b48471 --- /dev/null +++ b/src/pages/accounts/rpc/wallet_authorizeAccessKey.mdx @@ -0,0 +1,53 @@ +--- +title: wallet_authorizeAccessKey +description: Authorize an access key for delegated transaction signing. +--- + +# `wallet_authorizeAccessKey` + +Authorizes an access key with an expiry and optional spending limits for delegated transaction signing. + +## Request + +```ts +type Request = { + method: 'wallet_authorizeAccessKey' + params: [{ + /** Unix timestamp (seconds) when the key expires. */ + expiry: number + /** TIP-20 spending limits. */ + limits?: { token: Address; limit: Hex }[] + /** Public key to authorize. */ + publicKey?: Hex + /** Key type. */ + keyType?: 'secp256k1' | 'p256' | 'webAuthn' + /** Address of the key (alternative to publicKey). */ + address?: Address + }] +} +``` + +## Response + +```ts +type Response = { + keyAuthorization: KeyAuthorization + rootAddress: Address +} +``` + +## Example + +```ts +import { Expiry } from 'accounts' + +const result = await provider.request({ + method: 'wallet_authorizeAccessKey', + params: [{ + expiry: Expiry.days(1), + limits: [ + { token: '0x20c0000000000000000000000000000000000001', limit: '0x5F5E100' }, + ], + }], +}) +``` diff --git a/src/pages/accounts/rpc/wallet_connect.mdx b/src/pages/accounts/rpc/wallet_connect.mdx new file mode 100644 index 00000000..85c16763 --- /dev/null +++ b/src/pages/accounts/rpc/wallet_connect.mdx @@ -0,0 +1,54 @@ +--- +title: wallet_connect +description: Connect account(s) with optional capabilities like access key authorization. +--- + +# `wallet_connect` + +Requests to connect account(s) with optional capabilities. + +## Request + +```ts +type Request = { + method: 'wallet_connect' + params?: [{ + capabilities?: { + /** Authorize an access key on connect. */ + authorizeAccessKey?: { + expiry: number + limits?: { token: Address; limit: Hex }[] + publicKey?: Hex + } + /** Authentication method. */ + method?: 'register' | 'login' + } + }] +} +``` + +## Response + +```ts +type Response = { + accounts: { + address: Address + capabilities: { + keyAuthorization?: KeyAuthorization + signature?: Hex + } + }[] +} +``` + +## Example + +```ts +import { Provider } from 'accounts' + +const provider = Provider.create() + +const { accounts } = await provider.request({ + method: 'wallet_connect', +}) +``` diff --git a/src/pages/accounts/rpc/wallet_disconnect.mdx b/src/pages/accounts/rpc/wallet_disconnect.mdx new file mode 100644 index 00000000..4dcc8b0d --- /dev/null +++ b/src/pages/accounts/rpc/wallet_disconnect.mdx @@ -0,0 +1,30 @@ +--- +title: wallet_disconnect +description: Disconnect the connected account(s). +--- + +# `wallet_disconnect` + +Disconnects the connected account(s) and clears session state. + +## Request + +```ts +type Request = { + method: 'wallet_disconnect' +} +``` + +## Response + +```ts +type Response = undefined +``` + +## Example + +```ts +await provider.request({ + method: 'wallet_disconnect', +}) +``` diff --git a/src/pages/accounts/rpc/wallet_getBalances.mdx b/src/pages/accounts/rpc/wallet_getBalances.mdx new file mode 100644 index 00000000..ad2c5557 --- /dev/null +++ b/src/pages/accounts/rpc/wallet_getBalances.mdx @@ -0,0 +1,45 @@ +--- +title: wallet_getBalances +description: Get token balances for an account. +--- + +# `wallet_getBalances` + +Returns token balances for the connected account. + +## Request + +```ts +type Request = { + method: 'wallet_getBalances' + params?: [{ + /** Account address. Defaults to connected account. */ + account?: Address + /** Chain ID. */ + chainId?: number + /** Filter to specific token addresses. */ + tokens?: Address[] + }] +} +``` + +## Response + +```ts +type Response = { + address: Address + balance: bigint + decimals: number + display: string + name: string + symbol: string +}[] +``` + +## Example + +```ts +const balances = await provider.request({ + method: 'wallet_getBalances', +}) +``` diff --git a/src/pages/accounts/rpc/wallet_getCallsStatus.mdx b/src/pages/accounts/rpc/wallet_getCallsStatus.mdx new file mode 100644 index 00000000..0a537ba9 --- /dev/null +++ b/src/pages/accounts/rpc/wallet_getCallsStatus.mdx @@ -0,0 +1,39 @@ +--- +title: wallet_getCallsStatus +description: Get the status of a batch of calls sent via wallet_sendCalls. +--- + +# `wallet_getCallsStatus` + +Returns the status and receipts of a previously submitted batch of calls. + +## Request + +```ts +type Request = { + method: 'wallet_getCallsStatus' + params?: [string] +} +``` + +## Response + +```ts +type Response = { + atomic: boolean + chainId: number + id: string + receipts?: TransactionReceipt[] + status: number + version: string +} +``` + +## Example + +```ts +const status = await provider.request({ + method: 'wallet_getCallsStatus', + params: ['0x...'], +}) +``` diff --git a/src/pages/accounts/rpc/wallet_getCapabilities.mdx b/src/pages/accounts/rpc/wallet_getCapabilities.mdx new file mode 100644 index 00000000..8a2f8f2f --- /dev/null +++ b/src/pages/accounts/rpc/wallet_getCapabilities.mdx @@ -0,0 +1,35 @@ +--- +title: wallet_getCapabilities +description: Get account capabilities for specified chains. +--- + +# `wallet_getCapabilities` + +Returns capabilities for the connected account across chains. + +## Request + +```ts +type Request = { + method: 'wallet_getCapabilities' + params?: [Address] | [Address, Hex[]] +} +``` + +## Response + +```ts +type Response = Record +``` + +## Example + +```ts +const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: ['0x...'], +}) +``` diff --git a/src/pages/accounts/rpc/wallet_revokeAccessKey.mdx b/src/pages/accounts/rpc/wallet_revokeAccessKey.mdx new file mode 100644 index 00000000..a62cc6c6 --- /dev/null +++ b/src/pages/accounts/rpc/wallet_revokeAccessKey.mdx @@ -0,0 +1,40 @@ +--- +title: wallet_revokeAccessKey +description: Revoke a previously authorized access key. +--- + +# `wallet_revokeAccessKey` + +Revokes a previously authorized access key. + +## Request + +```ts +type Request = { + method: 'wallet_revokeAccessKey' + params: [{ + /** Address of the account. */ + address: Address + /** Address of the access key to revoke. */ + accessKeyAddress: Address + }] +} +``` + +## Response + +```ts +type Response = undefined +``` + +## Example + +```ts +await provider.request({ + method: 'wallet_revokeAccessKey', + params: [{ + address: '0x...', + accessKeyAddress: '0x...', + }], +}) +``` diff --git a/src/pages/accounts/rpc/wallet_sendCalls.mdx b/src/pages/accounts/rpc/wallet_sendCalls.mdx new file mode 100644 index 00000000..dc907327 --- /dev/null +++ b/src/pages/accounts/rpc/wallet_sendCalls.mdx @@ -0,0 +1,48 @@ +--- +title: wallet_sendCalls +description: Send a batch of calls from the connected account. +--- + +# `wallet_sendCalls` + +Sends a batch of calls from the connected account. Supports atomic execution. + +## Request + +```ts +type Request = { + method: 'wallet_sendCalls' + params?: [{ + calls: { to: Address; data?: Hex; value?: Hex }[] + from?: Address + chainId?: number + capabilities?: { sync?: boolean } + atomicRequired?: boolean + }] +} +``` + +## Response + +```ts +type Response = { + id: string + status?: number + receipts?: TransactionReceipt[] + chainId?: number + atomic?: boolean +} +``` + +## Example + +```ts +const result = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + calls: [ + { to: '0x...', data: '0x...' }, + ], + }], +}) +``` diff --git a/src/pages/accounts/server/handler.compose.mdx b/src/pages/accounts/server/handler.compose.mdx new file mode 100644 index 00000000..161a983f --- /dev/null +++ b/src/pages/accounts/server/handler.compose.mdx @@ -0,0 +1,54 @@ +--- +title: Handler.compose +description: Compose multiple server handlers into a single handler. +--- + +# `Handler.compose` + +Composes multiple handlers into a single handler, routing requests to the handler that matches the request path. + +## Usage + +```ts +import { Handler } from 'accounts/server' + +const handler = Handler.compose([ + Handler.feePayer({ + account: privateKeyToAccount('0x...') + }), + Handler.webAuthn({ + kv: Kv.memory(), + origin: 'https://example.com', + rpId: 'example.com' + }), +]) +``` + +Then plug `handler` into your server framework of choice: + +```ts +createServer(handler.listener) // Node.js +Bun.serve(handler) // Bun +Deno.serve(handler) // Deno +app.all('*', c => handler.fetch(c.request)) // Elysia +app.use(handler.listener) // Express +app.use(c => handler.fetch(c.req.raw)) // Hono +export const GET = handler.fetch // Next.js +export const POST = handler.fetch // Next.js +``` + +## Parameters + +### handlers + +- **Type:** `Handler[]` +- **Required** + +Array of handlers to compose. Requests are routed to each handler in order. + +### path + +- **Type:** `string` +- **Default:** `'/'` + +Base path prefix for all composed handlers. diff --git a/src/pages/accounts/server/handler.feePayer.mdx b/src/pages/accounts/server/handler.feePayer.mdx new file mode 100644 index 00000000..0ff5cf43 --- /dev/null +++ b/src/pages/accounts/server/handler.feePayer.mdx @@ -0,0 +1,129 @@ +--- +title: Handler.feePayer +description: Server handler that sponsors transaction fees for users. +--- + +# `Handler.feePayer` + +Creates a server handler that acts as a fee payer for transactions, enabling you to subsidize gas costs for users by signing transactions with a dedicated fee payer account on your backend. + +:::info +[See the guide](/guide/payments/sponsor-user-fees) +::: + +## Usage + +```ts +import { privateKeyToAccount } from 'viem/accounts' +import { Handler } from 'accounts/server' + +const handler = Handler.feePayer({ + account: privateKeyToAccount('0x...'), +}) +``` + +Then plug `handler` into your server framework of choice: + +```ts +createServer(handler.listener) // Node.js +Bun.serve(handler) // Bun +Deno.serve(handler) // Deno +app.all('*', c => handler.fetch(c.request)) // Elysia +app.use(handler.listener) // Express +app.use(c => handler.fetch(c.req.raw)) // Hono +export const GET = handler.fetch // Next.js +export const POST = handler.fetch // Next.js +``` + +## Parameters + +### account + +- **Type:** `LocalAccount` +- **Required** + +The account to use as the fee payer. This account will sign all transactions and pay the gas fees. + +```ts twoslash +import { privateKeyToAccount } from 'viem/accounts' +import { Handler } from 'accounts/server' + +const handler = Handler.feePayer({ + account: privateKeyToAccount('0x...'), // [!code focus] +}) +``` + +### chains + +- **Type:** `readonly [Chain, ...Chain[]]` +- **Default:** `[tempo, tempoModerato]` + +Supported chains. The handler resolves the client based on the `chainId` in the incoming transaction. + +```ts twoslash +import { privateKeyToAccount } from 'viem/accounts' +import { tempo } from 'viem/chains' +import { Handler } from 'accounts/server' + +const handler = Handler.feePayer({ + account: privateKeyToAccount('0x...'), + chains: [tempo], // [!code focus] +}) +``` + +### onRequest + +- **Type:** `(request: RpcRequest) => Promise` +- **Optional** + +Callback called before processing each request. Useful for logging, rate limiting, or custom validation. + +```ts twoslash +import { privateKeyToAccount } from 'viem/accounts' +import { Handler } from 'accounts/server' + +const handler = Handler.feePayer({ + account: privateKeyToAccount('0x...'), + onRequest: async (request) => { // [!code focus] + console.log('Processing request:', request.method) // [!code focus] + }, // [!code focus] +}) +``` + +### path + +- **Type:** `string` +- **Default:** `'/'` + +Path where the handler listens for requests. + +```ts twoslash +import { privateKeyToAccount } from 'viem/accounts' +import { Handler } from 'accounts/server' + +const handler = Handler.feePayer({ + account: privateKeyToAccount('0x...'), + path: '/fee-payer', // [!code focus] +}) +``` + +### transports + +- **Type:** `Record` +- **Default:** `http()` for each chain + +Transports keyed by chain ID. + +```ts twoslash +import { http } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { tempo } from 'viem/chains' +import { Handler } from 'accounts/server' + +const handler = Handler.feePayer({ + account: privateKeyToAccount('0x...'), + transports: { // [!code focus] + [tempo.id]: http('https://rpc.tempo.xyz'), // [!code focus] + }, // [!code focus] +}) +``` diff --git a/src/pages/accounts/server/handler.webAuthn.mdx b/src/pages/accounts/server/handler.webAuthn.mdx new file mode 100644 index 00000000..16124294 --- /dev/null +++ b/src/pages/accounts/server/handler.webAuthn.mdx @@ -0,0 +1,177 @@ +--- +title: Handler.webAuthn +description: Server-side WebAuthn ceremony handler for registration and authentication. +--- + +# `Handler.webAuthn` + +Creates a WebAuthn ceremony handler that manages registration and authentication flows server-side. + +:::info +[See the guide](/guide/use-accounts/embed-passkeys) +::: + +Exposes 4 POST endpoints: + +- `POST /register/options` — Generate credential creation options +- `POST /register` — Verify registration and store credential +- `POST /login/options` — Generate credential request options +- `POST /login` — Verify authentication + +## Usage + +```ts +import { Handler, Kv } from 'accounts/server' + +const handler = Handler.webAuthn({ + kv: Kv.memory(), + origin: 'https://example.com', + rpId: 'example.com', +}) +``` + +Then plug `handler` into your server framework of choice: + +```ts +createServer(handler.listener) // Node.js +Bun.serve(handler) // Bun +Deno.serve(handler) // Deno +app.all('*', c => handler.fetch(c.request)) // Elysia +app.use(handler.listener) // Express +app.use(c => handler.fetch(c.req.raw)) // Hono +export const GET = handler.fetch // Next.js +export const POST = handler.fetch // Next.js +``` + +:::warning +`Kv.memory()` is not recommended for production use. Instead, use a persistent store like Cloudflare or Vercel KV, or a Redis instance. See [`Kv`](/accounts/server/kv) for available adapters. +::: + +## Parameters + +### challengeTtl + +- **Type:** `number` +- **Default:** `300` (5 minutes) + +Maximum age of a challenge in seconds before it expires. + +```ts twoslash +import { Handler, Kv } from 'accounts/server' + +const handler = Handler.webAuthn({ + kv: Kv.memory(), + origin: 'https://example.com', + rpId: 'example.com', + challengeTtl: 600, // [!code focus] +}) +``` + +### kv + +- **Type:** [`Kv`](/accounts/server/kv) +- **Required** + +Key-value store for challenges and credentials. [See `Kv`](/accounts/server/kv) for available adapters. + +```ts twoslash +import { Handler, Kv } from 'accounts/server' + +const handler = Handler.webAuthn({ + kv: Kv.memory(), // [!code focus] + origin: 'https://example.com', + rpId: 'example.com', +}) +``` + +### onAuthenticate + +- **Type:** `(params: { credentialId, publicKey, userId?, request }) => Response | void` +- **Optional** + +Called after a successful authentication. + +```ts twoslash +import { Handler, Kv } from 'accounts/server' + +const handler = Handler.webAuthn({ + kv: Kv.memory(), + origin: 'https://example.com', + rpId: 'example.com', + onAuthenticate: ({ credentialId, publicKey }) => { // [!code focus] + console.log('Authenticated:', credentialId) // [!code focus] + }, // [!code focus] +}) +``` + +### onRegister + +- **Type:** `(params: { credentialId, publicKey, request }) => Response | void` +- **Optional** + +Called after a successful registration. The returned response is merged onto the default JSON response. + +```ts twoslash +import { Handler, Kv } from 'accounts/server' + +const handler = Handler.webAuthn({ + kv: Kv.memory(), + origin: 'https://example.com', + rpId: 'example.com', + onRegister: ({ credentialId, publicKey }) => { // [!code focus] + console.log('Registered:', credentialId) // [!code focus] + }, // [!code focus] +}) +``` + +### origin + +- **Type:** `string | readonly string[]` +- **Required** + +Expected origin(s) for WebAuthn verification (e.g. `'https://example.com'`). + +```ts twoslash +import { Handler, Kv } from 'accounts/server' + +const handler = Handler.webAuthn({ + kv: Kv.memory(), + origin: 'https://example.com', // [!code focus] + rpId: 'example.com', +}) +``` + +### path + +- **Type:** `string` +- **Default:** `''` + +Path prefix for the WebAuthn endpoints. + +```ts twoslash +import { Handler, Kv } from 'accounts/server' + +const handler = Handler.webAuthn({ + kv: Kv.memory(), + origin: 'https://example.com', + rpId: 'example.com', + path: '/webauthn', // [!code focus] +}) +``` + +### rpId + +- **Type:** `string` +- **Required** + +Relying Party ID (e.g. `'example.com'`). + +```ts twoslash +import { Handler, Kv } from 'accounts/server' + +const handler = Handler.webAuthn({ + kv: Kv.memory(), + origin: 'https://example.com', + rpId: 'example.com', // [!code focus] +}) +``` diff --git a/src/pages/sdk/typescript/server/handlers.mdx b/src/pages/accounts/server/index.mdx similarity index 56% rename from src/pages/sdk/typescript/server/handlers.mdx rename to src/pages/accounts/server/index.mdx index 98e80414..ee469483 100644 --- a/src/pages/sdk/typescript/server/handlers.mdx +++ b/src/pages/accounts/server/index.mdx @@ -1,10 +1,11 @@ --- -description: Framework-agnostic server handlers for Tempo protocol operations. Works with Node.js, Bun, Deno, Express, Hono, and Next.js. +title: Handlers +description: Server-side handlers for the Tempo Accounts SDK. --- import { Cards, Card } from 'vocs' -# Overview +# Handlers Server handlers are framework-agnostic handlers that run on your backend to manage protocol operations that require server-side logic. @@ -13,51 +14,40 @@ Handlers are compatible with any server framework that supports the: - [Node.js `RequestListener` API](https://nodejs.org/api/http.html#http_class_http_serverrequestlistener), and is exposed via the `Handler#listener` function ```ts -import { Handler } from 'tempo.ts/server' -import { account, client } from './config' +import { Handler } from 'accounts/server' +import { privateKeyToAccount } from 'viem/accounts' const handler = Handler.feePayer({ - account, - client, - feeToken: '0x20c0…0001' - path: '/fee-payer', + account: privateKeyToAccount('0x...'), }) createServer(handler.listener) // Node.js - Bun.serve(handler) // Bun - Deno.serve(handler) // Deno - app.all('*', c => handler.fetch(c.request)) // Elysia - app.use(handler.listener) // Express - app.use(c => handler.fetch(c.req.raw)) // Hono - export const GET = handler.fetch // Next.js export const POST = handler.fetch // Next.js ``` -## Handlers - - \ No newline at end of file + diff --git a/src/pages/accounts/server/kv.mdx b/src/pages/accounts/server/kv.mdx new file mode 100644 index 00000000..efa1252f --- /dev/null +++ b/src/pages/accounts/server/kv.mdx @@ -0,0 +1,54 @@ +--- +title: Kv +description: Key-value store adapters for server-side persistence. +--- + +# `Kv` + +Key-value store interface used by server handlers for persistence (challenges, credentials, device codes). + +## Usage + +```ts +import { Kv } from 'accounts/server' + +// In-memory (for development/testing) +const kv = Kv.memory() + +// Cloudflare Workers KV +const kv = Kv.cloudflare(env.MY_KV_NAMESPACE) +``` + +## Adapters + +### Kv.memory() + +Creates an in-memory key-value store. Useful for development and testing. + +### Kv.cloudflare(namespace) + +Creates a key-value store backed by Cloudflare Workers KV. + +- **namespace** — Cloudflare KV namespace binding. + +### Kv.from(kv) + +Creates a key-value store from a custom implementation. + +```ts +const kv = Kv.from({ + async get(key) { /* ... */ }, + async set(key, value) { /* ... */ }, + async delete(key) { /* ... */ }, +}) +``` + +## Interface + +```ts +type Kv = { + get: (key: string) => Promise + set: (key: string, value: unknown) => Promise + delete: (key: string) => Promise +} +``` diff --git a/src/pages/accounts/wagmi/tempoWallet.mdx b/src/pages/accounts/wagmi/tempoWallet.mdx new file mode 100644 index 00000000..fcb0d7c1 --- /dev/null +++ b/src/pages/accounts/wagmi/tempoWallet.mdx @@ -0,0 +1,179 @@ +--- +title: tempoWallet +description: Wagmi connector for the Tempo Wallet dialog. +--- + +# `tempoWallet` + +Creates a Wagmi connector backed by the Tempo Wallet dialog adapter. + +## Usage + +```ts +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { tempoWallet } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [tempoWallet()], + transports: { + [tempo.id]: http(), + }, +}) +``` + +## Parameters + +Accepts all [`dialog`](/accounts/api/dialog) adapter options, plus all [`Provider`](/accounts/api/provider) options except `adapter`. + +### authorizeAccessKey + +- **Type:** `() => { expiry: number; limits?: { token: Address; limit: bigint }[] }` +- **Optional** + +Default access key parameters for `wallet_connect`. + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { parseUnits } from 'viem' +import { Expiry } from 'accounts' +import { tempoWallet } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + tempoWallet({ + authorizeAccessKey: () => ({ // [!code focus] + expiry: Expiry.days(7), // [!code focus] + limits: [{ // [!code focus] + token: '0x20c0000000000000000000000000000000000001', // [!code focus] + limit: parseUnits('500', 6), // [!code focus] + }], // [!code focus] + }), // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` + +### dialog + +- **Type:** `Dialog` +- **Default:** `Dialog.iframe()` (or `Dialog.popup()` in Safari/insecure contexts) + +Dialog to use for the embed app. + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { Dialog } from 'accounts' +import { tempoWallet } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + tempoWallet({ + dialog: Dialog.popup(), // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` + +### feePayerUrl + +- **Type:** `string` +- **Optional** + +Fee payer URL for interacting with a service running [`Handler.feePayer`](/accounts/server/handler.feePayer) from `accounts/server`. + +:::info +[See the guide](/guide/payments/sponsor-user-fees) +::: + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { tempoWallet } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + tempoWallet({ + feePayerUrl: 'https://myapp.com/fee-payer', // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` + +### host + +- **Type:** `string` +- **Default:** `'https://wallet.tempo.xyz/embed'` + +URL of the embed app. + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { tempoWallet } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + tempoWallet({ + host: 'https://wallet.tempo.xyz/embed', // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` + +### mpp + +- **Type:** `boolean` +- **Default:** `false` + +Enable [Machine Payment Protocol](https://mpp.dev) support. + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { tempoWallet } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + tempoWallet({ + mpp: true, // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` + +### testnet + +- **Type:** `boolean` +- **Default:** `false` + +Use testnet. When `true`, the default chain will be the first testnet chain in `chains`. + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { tempoWallet } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + tempoWallet({ + testnet: true, // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` diff --git a/src/pages/accounts/wagmi/webAuthn.mdx b/src/pages/accounts/wagmi/webAuthn.mdx new file mode 100644 index 00000000..bbe5970f --- /dev/null +++ b/src/pages/accounts/wagmi/webAuthn.mdx @@ -0,0 +1,199 @@ +--- +title: webAuthn +description: Wagmi connector for passkey-based WebAuthn accounts. +--- + +# `webAuthn` + +Creates a Wagmi connector backed by the WebAuthn adapter. + +## Usage + +```ts +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { webAuthn } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [webAuthn({ authUrl: '/auth' })], + transports: { + [tempo.id]: http(), + }, +}) +``` + +## Parameters + +Accepts all [`webAuthn`](/accounts/api/webAuthn) adapter options, plus all [`Provider`](/accounts/api/provider) options except `adapter`. + +### authUrl + +- **Type:** `string` +- **Optional** + +URL of a [WebAuthn server handler](/accounts/server/handler.webAuthn). + +:::info +[See the guide](/guide/use-accounts/embed-passkeys) +::: + +:::warning +Cannot be used with `ceremony`. +::: + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { webAuthn } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + webAuthn({ + authUrl: '/auth', // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` + +### authorizeAccessKey + +- **Type:** `() => { expiry: number; limits?: { token: Address; limit: bigint }[] }` +- **Optional** + +Default access key parameters for `wallet_connect`. + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { parseUnits } from 'viem' +import { Expiry } from 'accounts' +import { webAuthn } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + webAuthn({ + authUrl: '/auth', + authorizeAccessKey: () => ({ // [!code focus] + expiry: Expiry.days(7), // [!code focus] + limits: [{ // [!code focus] + token: '0x20c0000000000000000000000000000000000001', // [!code focus] + limit: parseUnits('500', 6), // [!code focus] + }], // [!code focus] + }), // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` + +### ceremony + +- **Type:** `WebAuthnCeremony` +- **Default:** `WebAuthnCeremony.server({ url: authUrl }){:js}` + +Ceremony strategy for WebAuthn registration and authentication. [See more](/accounts/api/webauthnceremony). + +:::info +[See the guide](/guide/use-accounts/embed-passkeys) +::: + +:::warning +Cannot be used with `authUrl`. +::: + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { WebAuthnCeremony } from 'accounts' +import { webAuthn } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + webAuthn({ + ceremony: WebAuthnCeremony.server({ url: '/auth' }), // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` + +### feePayerUrl + +- **Type:** `string` +- **Optional** + +Fee payer URL for interacting with a service running [`Handler.feePayer`](/accounts/server/handler.feePayer) from `accounts/server`. + +:::info +[See the guide](/guide/payments/sponsor-user-fees) +::: + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { webAuthn } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + webAuthn({ + authUrl: '/auth', + feePayerUrl: 'https://myapp.com/fee-payer', // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` + +### mpp + +- **Type:** `boolean` +- **Default:** `false` + +Enable [Machine Payment Protocol](https://mpp.dev) support. + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { webAuthn } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + webAuthn({ + authUrl: '/auth', + mpp: true, // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` + +### testnet + +- **Type:** `boolean` +- **Default:** `false` + +Use testnet. When `true`, the default chain will be the first testnet chain in `chains`. + +```ts twoslash +import { createConfig, http } from 'wagmi' +import { tempo } from 'wagmi/chains' +import { webAuthn } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [ + webAuthn({ + authUrl: '/auth', + testnet: true, // [!code focus] + }), + ], + transports: { [tempo.id]: http() }, +}) +``` diff --git a/src/pages/guide/_template.mdx b/src/pages/guide/_template.mdx index 516907a4..a5ee7e4c 100644 --- a/src/pages/guide/_template.mdx +++ b/src/pages/guide/_template.mdx @@ -43,13 +43,11 @@ export function Component() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -75,13 +73,11 @@ export function Component() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, diff --git a/src/pages/guide/issuance/create-a-stablecoin.mdx b/src/pages/guide/issuance/create-a-stablecoin.mdx index cfe25a5c..a1a8dab4 100644 --- a/src/pages/guide/issuance/create-a-stablecoin.mdx +++ b/src/pages/guide/issuance/create-a-stablecoin.mdx @@ -69,13 +69,11 @@ export function AddFunds() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -130,13 +128,11 @@ export function CreateStablecoin() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -198,13 +194,11 @@ export function CreateStablecoin() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -266,13 +260,11 @@ export function CreateStablecoin() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, diff --git a/src/pages/guide/issuance/distribute-rewards.mdx b/src/pages/guide/issuance/distribute-rewards.mdx index dd9c43af..f6b5e5fb 100644 --- a/src/pages/guide/issuance/distribute-rewards.mdx +++ b/src/pages/guide/issuance/distribute-rewards.mdx @@ -77,13 +77,11 @@ export function OptInToRewards() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -137,13 +135,11 @@ export function StartReward() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -190,13 +186,11 @@ export function ClaimReward() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, diff --git a/src/pages/guide/issuance/manage-stablecoin.mdx b/src/pages/guide/issuance/manage-stablecoin.mdx index 4cd91cb0..abbba2e3 100644 --- a/src/pages/guide/issuance/manage-stablecoin.mdx +++ b/src/pages/guide/issuance/manage-stablecoin.mdx @@ -74,13 +74,11 @@ export function GrantRoles() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -153,13 +151,11 @@ export function GrantRoles() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -266,13 +262,11 @@ export function RevokeRoles() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -337,13 +331,11 @@ export function SetSupplyCap() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -446,13 +438,11 @@ export function LinkTokenPolicy() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -526,13 +516,11 @@ export function PauseUnpauseTransfers() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -600,13 +588,11 @@ export function BurnBlocked() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, diff --git a/src/pages/guide/issuance/mint-stablecoins.mdx b/src/pages/guide/issuance/mint-stablecoins.mdx index b4b8caca..96b9e482 100644 --- a/src/pages/guide/issuance/mint-stablecoins.mdx +++ b/src/pages/guide/issuance/mint-stablecoins.mdx @@ -77,13 +77,11 @@ export function GrantIssuerRole() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -180,13 +178,11 @@ export function MintToken() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, @@ -339,13 +335,11 @@ export function BurnToken() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, diff --git a/src/pages/guide/issuance/use-for-fees.mdx b/src/pages/guide/issuance/use-for-fees.mdx index 4ad675c4..b0248b9a 100644 --- a/src/pages/guide/issuance/use-for-fees.mdx +++ b/src/pages/guide/issuance/use-for-fees.mdx @@ -201,13 +201,11 @@ export function PayWithIssuedToken() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ - keyManager: KeyManager.localStorage(), - })], + connectors: [tempoWallet()], transports: { [tempo.id]: http(), }, diff --git a/src/pages/guide/machine-payments/client.mdx b/src/pages/guide/machine-payments/client.mdx index 4ae816d8..add1b853 100644 --- a/src/pages/guide/machine-payments/client.mdx +++ b/src/pages/guide/machine-payments/client.mdx @@ -97,11 +97,11 @@ Mppx.create({ ```ts [config.ts] import { createConfig, http } from 'wagmi' -import { webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' import { tempoModerato } from 'viem/chains' export const config = createConfig({ - connectors: [webAuthn()], + connectors: [tempoWallet()], chains: [tempoModerato], transports: { [tempoModerato.id]: http(), diff --git a/src/pages/guide/payments/sponsor-user-fees.mdx b/src/pages/guide/payments/sponsor-user-fees.mdx index dcdfe8f8..ea4f377d 100644 --- a/src/pages/guide/payments/sponsor-user-fees.mdx +++ b/src/pages/guide/payments/sponsor-user-fees.mdx @@ -13,7 +13,7 @@ Enable gasless transactions by sponsoring transaction fees for your users. Tempo ## Demo - + @@ -29,24 +29,72 @@ Enable gasless transactions by sponsoring transaction fees for your users. Tempo Tempo provides a public testnet fee payer service at `https://sponsor.moderato.tempo.xyz` that you can use for development and testing. If you want to run your own, follow the instructions below. ::: -You can stand up a minimal fee payer service using the `Handler.feePayer` handler provided by the Tempo TypeScript SDK ([link](/sdk/typescript/server/handler.feePayer)). To sponsor transactions, you need a funded account that will act as the fee payer. +You can stand up a minimal fee payer service using the [`Handler.feePayer`](/accounts/server/handler.feePayer) handler provided by the [Tempo Accounts SDK](/accounts). To sponsor transactions, you need a funded account that will act as the fee payer. ```ts twoslash [server.ts] // @noErrors // [!include ~/snippets/unformatted/withFeePayer.ts:server] ``` -### Configure your client to use the fee payer service +Then plug `handler` into your server framework of choice: + +```ts +createServer(handler.listener) // Node.js +Bun.serve(handler) // Bun +Deno.serve(handler) // Deno +app.all('*', c => handler.fetch(c.request)) // Elysia +app.use(handler.listener) // Express +app.use(c => handler.fetch(c.req.raw)) // Hono +export const GET = handler.fetch // Next.js +export const POST = handler.fetch // Next.js +``` -Use the `withFeePayer` transport provided by Viem ([link](https://viem.sh/tempo/transports/withFeePayer)). It routes transactions to the configured fee payer service for sponsorship when `feePayer: true` is requested on a transaction. +### Configure your client to use the fee payer service -```ts twoslash [wagmi.config.ts] +:::code-group + +```ts twoslash [Tempo Wallet] // @noErrors -// [!include ~/snippets/wagmi.config.ts:withFeePayer] +import { tempoWallet } from 'accounts/wagmi' +import { tempo } from 'viem/chains' +import { createConfig, http } from 'wagmi' + +export const config = createConfig({ + connectors: [tempoWallet({ + feePayerUrl: 'https://sponsor.moderato.tempo.xyz', // [!code focus] + })], + chains: [tempo], + multiInjectedProviderDiscovery: false, + transports: { + [tempo.id]: http(), + }, +}) ``` +```ts twoslash [WebAuthn] +// @noErrors +import { webAuthn } from 'accounts/wagmi' +import { tempo } from 'viem/chains' +import { withFeePayer } from 'viem/tempo' +import { createConfig, http } from 'wagmi' + +export const config = createConfig({ + connectors: [webAuthn({ authUrl: '/auth' })], + chains: [tempo], + multiInjectedProviderDiscovery: false, + transports: { + [tempo.id]: withFeePayer( // [!code focus] + http(), // [!code focus] + http('https://sponsor.moderato.tempo.xyz'), // [!code focus] + ), // [!code focus] + }, +}) +``` + +::: + ### Sponsor your user's transactions Now you can sponsor transactions by passing `feePayer: true` in the transaction parameters. For more details on how to send a transaction, see the [Send a payment](/guide/payments/send-a-payment) guide. diff --git a/src/pages/guide/stablecoin-dex/managing-fee-liquidity.mdx b/src/pages/guide/stablecoin-dex/managing-fee-liquidity.mdx index 70fa56f2..f4511fde 100644 --- a/src/pages/guide/stablecoin-dex/managing-fee-liquidity.mdx +++ b/src/pages/guide/stablecoin-dex/managing-fee-liquidity.mdx @@ -55,15 +55,11 @@ function ManageFeeLiquidity() { ```ts twoslash [wagmi.config.ts] // @noErrors import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' import { createConfig, http } from 'wagmi' export const config = createConfig({ - connectors: [ - webAuthn({ - keyManager: KeyManager.localStorage(), - }), - ], + connectors: [tempoWallet()], chains: [tempo], multiInjectedProviderDiscovery: false, transports: { @@ -123,15 +119,11 @@ function ManageFeeLiquidity() { ```ts twoslash [wagmi.config.ts] // @noErrors import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' import { createConfig, http } from 'wagmi' export const config = createConfig({ - connectors: [ - webAuthn({ - keyManager: KeyManager.localStorage(), - }), - ], + connectors: [tempoWallet()], chains: [tempo], multiInjectedProviderDiscovery: false, transports: { @@ -198,15 +190,11 @@ function ManageFeeLiquidity() { ```ts twoslash [wagmi.config.ts] // @noErrors import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' import { createConfig, http } from 'wagmi' export const config = createConfig({ - connectors: [ - webAuthn({ - keyManager: KeyManager.localStorage(), - }), - ], + connectors: [tempoWallet()], chains: [tempo], multiInjectedProviderDiscovery: false, transports: { @@ -285,15 +273,11 @@ function ManageFeeLiquidity() { ```ts twoslash [wagmi.config.ts] // @noErrors import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' import { createConfig, http } from 'wagmi' export const config = createConfig({ - connectors: [ - webAuthn({ - keyManager: KeyManager.localStorage(), - }), - ], + connectors: [tempoWallet()], chains: [tempo], multiInjectedProviderDiscovery: false, transports: { @@ -355,15 +339,11 @@ function MonitorSwaps() { ```ts twoslash [wagmi.config.ts] // @noErrors import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' import { createConfig, http } from 'wagmi' export const config = createConfig({ - connectors: [ - webAuthn({ - keyManager: KeyManager.localStorage(), - }), - ], + connectors: [tempoWallet()], chains: [tempo], multiInjectedProviderDiscovery: false, transports: { @@ -423,15 +403,11 @@ function RebalancePool() { ```ts twoslash [wagmi.config.ts] // @noErrors import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' +import { tempoWallet } from 'accounts/wagmi' import { createConfig, http } from 'wagmi' export const config = createConfig({ - connectors: [ - webAuthn({ - keyManager: KeyManager.localStorage(), - }), - ], + connectors: [tempoWallet()], chains: [tempo], multiInjectedProviderDiscovery: false, transports: { diff --git a/src/pages/guide/use-accounts/embed-passkeys.mdx b/src/pages/guide/use-accounts/embed-passkeys.mdx index c5266c9c..59024b85 100644 --- a/src/pages/guide/use-accounts/embed-passkeys.mdx +++ b/src/pages/guide/use-accounts/embed-passkeys.mdx @@ -8,9 +8,9 @@ import { EmbedPasskeys, SignInButtons } from '../../../components/guides/EmbedPa # Embed Passkey Accounts -Create a domain-bound passkey account on Tempo using WebAuthn signatures for secure, passwordless authentication with [Tempo transactions](/protocol/transactions/spec-tempo-transaction). +Create a domain-bound passkey account on Tempo using the [Tempo Accounts SDK](/accounts) and WebAuthn signatures for secure, passwordless authentication with [Tempo transactions](/protocol/transactions/spec-tempo-transaction). -Passkeys enable users to authenticate with biometrics (fingerprint, Face ID, Touch ID) they already use for other apps. Keys are stored in the device's secure enclave and sync across devices via iCloud Keychain or Google Password Manager. +The [`webAuthn`](/accounts/wagmi/webAuthn) Wagmi connector is the easiest way to get started. Passkeys enable users to authenticate with biometrics (fingerprint, Face ID, Touch ID) they already use for other apps. Keys are stored in the device's secure enclave and sync across devices via iCloud Keychain or Google Password Manager. :::info @@ -20,8 +20,7 @@ WebAuthn credentials are bound to a specific domain (the [Relying Party](https:/ This means that credentials created for one domain (e.g., `example.com`) will only work on that domain (and its subdomains) and cannot be used to authenticate on other domains. -This means your users won't be able to use the same passkey account on other applications. If this is not what you want, head to the [Connect to wallets](/guide/use-accounts/connect-to-wallets) -guide that walks you through on how to connect your application to a universal wallet like MetaMask. +This means your users won't be able to use the same passkey account on other applications. If this is not what you want, head to the [Embed Tempo Wallet](/guide/use-accounts/embed-tempo-wallet) guide for a universal account experience. ::: @@ -29,7 +28,7 @@ guide that walks you through on how to connect your application to a universal w By the end of this guide, you will be able to embed passkey accounts into your application. - + @@ -41,21 +40,51 @@ By the end of this guide, you will be able to embed passkey accounts into your a Ensure that you have set up your project with Wagmi by following the [guide](/sdk/typescript#wagmi-setup). +### Set up the WebAuthn server + +Set up a [`Handler.webAuthn`](/accounts/server/handler.webAuthn) server to handle passkey registration and authentication ceremonies. + +```ts twoslash [server.ts] +// @noErrors +import { Handler, Kv } from 'accounts/server' + +const handler = Handler.webAuthn({ + kv: Kv.memory(), + origin: 'https://example.com', + rpId: 'example.com', +}) +``` + +Then plug `handler` into your server framework of choice: + +```ts +createServer(handler.listener) // Node.js +Bun.serve(handler) // Bun +Deno.serve(handler) // Deno +app.all('*', c => handler.fetch(c.request)) // Elysia +app.use(handler.listener) // Express +app.use(c => handler.fetch(c.req.raw)) // Hono +export const GET = handler.fetch // Next.js +export const POST = handler.fetch // Next.js +``` + +:::warning +`Kv.memory()` is not recommended for production use. Instead, use a persistent store like Cloudflare or Vercel KV, or a Redis instance. See [`Kv`](/accounts/server/kv) for available adapters. +::: + ### Configure the WebAuthn Connector -Next, we will need to configure the `webAuthn` connector in our Wagmi config. +Next, we will need to configure the `webAuthn` connector in our Wagmi config, passing the (relative or absolute) url of your WebAuthn server to `authUrl`. ```tsx twoslash [config.ts] // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' // [!code ++] +import { webAuthn } from 'accounts/wagmi' // [!code ++] export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ // [!code ++] - keyManager: KeyManager.localStorage(), // [!code ++] - })], // [!code ++] + connectors: [webAuthn({ authUrl: '/auth' })], // [!code ++] multiInjectedProviderDiscovery: false, transports: { [tempo.id]: http(), @@ -63,14 +92,6 @@ export const config = createConfig({ }) ``` -:::warning - -The `KeyManager.localStorage()` implementation is not recommended for production use as it stores public keys on the client device, meaning it cannot be re-extracted when the user's storage is cleared or if the user is on another device. - -For production, you should opt for a remote key manager such as [`KeyManager.http`](https://wagmi.sh/tempo/keyManagers/http). - -::: - :::tip This Wagmi configuration sets `multiInjectedProviderDiscovery` to `false` to @@ -131,13 +152,11 @@ export function Example() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' // [!code ++] +import { webAuthn } from 'accounts/wagmi' // [!code ++] export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ // [!code ++] - keyManager: KeyManager.localStorage(), // [!code ++] - })], // [!code ++] + connectors: [webAuthn({ authUrl: '/auth' })], // [!code ++] multiInjectedProviderDiscovery: false, transports: { [tempo.id]: http(), @@ -209,13 +228,11 @@ export function Example() { // @noErrors import { createConfig, http } from 'wagmi' import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' // [!code ++] +import { webAuthn } from 'accounts/wagmi' // [!code ++] export const config = createConfig({ chains: [tempo], - connectors: [webAuthn({ // [!code ++] - keyManager: KeyManager.localStorage(), // [!code ++] - })], // [!code ++] + connectors: [webAuthn({ authUrl: '/auth' })], // [!code ++] multiInjectedProviderDiscovery: false, transports: { [tempo.id]: http(), @@ -299,15 +316,21 @@ export function Example() { + diff --git a/src/pages/guide/use-accounts/embed-tempo-wallet.mdx b/src/pages/guide/use-accounts/embed-tempo-wallet.mdx new file mode 100644 index 00000000..35021a35 --- /dev/null +++ b/src/pages/guide/use-accounts/embed-tempo-wallet.mdx @@ -0,0 +1,286 @@ +--- +description: Embed the Tempo Wallet dialog into your application for a universal wallet experience with account management, passkeys, and fee sponsorship. +--- + +import { Cards, Card } from 'vocs' +import * as Demo from '../../../components/guides/Demo.tsx' +import { AccountsSignIn } from '../../../components/guides/AccountsSignIn.tsx' + +# Embed Tempo Wallet + +Embed the [Tempo Wallet](https://wallet.tempo.xyz) dialog into your application using the [Tempo Accounts SDK](/accounts) for a universal wallet experience. + +The [`tempoWallet`](/accounts/wagmi/tempoWallet) Wagmi connector is the easiest way to get started. It wraps the Accounts SDK and provides a complete account management experience for your users, including sign-up, sign-in, balance management, and transaction signing, all within an embedded dialog. + +:::info + +**When should I use Tempo Wallet vs. domain-bound Passkeys?** + +The **Tempo Wallet** provides a universal wallet experience – users manage their account through the Tempo Wallet interface, and their account is portable across all applications that embed it. + +**Domain-bound Passkeys** are tied to your specific domain and cannot be used on other applications. This gives you full control over the authentication experience but requires more setup. + +If you want a quick integration with a full-featured wallet, use the [Tempo Wallet](/accounts/api/dialog). If you want a fully custom, domain-bound authentication experience, use [domain-bound Passkeys](/guide/use-accounts/embed-passkeys). + +::: + +## Demo + +By the end of this guide, you will be able to embed the Tempo Wallet into your application. + + + + + +## Steps + +::::steps + +### Set up Wagmi + +Ensure that you have set up your project with Wagmi by following the [guide](/sdk/typescript#wagmi-setup). + +### Configure the Tempo Wallet Connector + +Next, we will configure the `tempoWallet` connector in our Wagmi config. + +```tsx twoslash [config.ts] +// @noErrors +import { createConfig, http } from 'wagmi' +import { tempo } from 'viem/chains' +import { tempoWallet } from 'accounts/wagmi' // [!code ++] + +export const config = createConfig({ + chains: [tempo], + connectors: [tempoWallet()], // [!code ++] + multiInjectedProviderDiscovery: false, + transports: { + [tempo.id]: http(), + }, +}) +``` + +:::tip + +This Wagmi configuration sets `multiInjectedProviderDiscovery` to `false` to +prevent injected browser wallets from being detected, and to prefer the `tempoWallet` connector. +If you would like to allow connection to other wallets, set this property to `true`. + +::: + +### Display Sign In Button + +After that, we will set up a "Sign in" button that opens the Tempo Wallet dialog for the user. + +We will create a new `Example.tsx` component to work in. + +:::code-group + +```tsx twoslash [Example.tsx] +// @noErrors +import { useConnect, useConnectors } from 'wagmi' + +export function Example() { + const connect = useConnect() + const connectors = useConnectors() + const connector = connectors.find((c) => c.id === 'xyz.tempo') + + return ( +
+ +
+ ) +} +``` + +```tsx twoslash [config.ts] filename="config.ts" +// @noErrors +import { createConfig, http } from 'wagmi' +import { tempo } from 'viem/chains' +import { tempoWallet } from 'accounts/wagmi' // [!code ++] + +export const config = createConfig({ + chains: [tempo], + connectors: [tempoWallet()], // [!code ++] + multiInjectedProviderDiscovery: false, + transports: { + [tempo.id]: http(), + }, +}) +``` + +::: + +### Display Account & Sign Out + +After the user has signed in, we can display the account information and a sign out button. + +:::code-group + +
+ +
+ +
+
+
+ +::: + +:::code-group + +```tsx twoslash [Example.tsx] +// @noErrors +import { useConnection, useConnect, useConnectors, useDisconnect } from 'wagmi' + +export function Example() { + const account = useConnection() // [!code ++] + const connect = useConnect() + const connectors = useConnectors() + const connector = connectors.find((c) => c.id === 'xyz.tempo') + const disconnect = useDisconnect() // [!code ++] + + if (account.address) // [!code ++] + return ( // [!code ++] +
{/* [!code ++] */} +
{account.address.slice(0, 6)}...{account.address.slice(-4)}
{/* [!code ++] */} + {/* [!code ++] */} +
{/* [!code ++] */} + ) // [!code ++] + + return ( +
+ +
+ ) +} + +``` + +```tsx twoslash [config.ts] filename="config.ts" +// @noErrors +import { createConfig, http } from 'wagmi' +import { tempo } from 'viem/chains' +import { tempoWallet } from 'accounts/wagmi' // [!code ++] + +export const config = createConfig({ + chains: [tempo], + connectors: [tempoWallet()], // [!code ++] + multiInjectedProviderDiscovery: false, + transports: { + [tempo.id]: http(), + }, +}) +``` + +::: + +### Deploy to Production + +When you're ready to go live, follow the [Deploying to Production](/accounts/production) guide to configure your application for production use. + +### Next Steps + +Now that you have embedded the Tempo Wallet, you can now: +- learn the [Best Practices](#best-practices) below +- follow a guide on how to [make a payment](/guide/payments), [create a stablecoin](/guide/issuance), or [exchange stablecoins](/guide/stablecoin-dex). + +:::: + +## Best Practices + +### Loading State + +When the user is logging in or signing out, we should show loading state to indicate that the process is happening. + +We can use the `isPending` property from the `useConnect` hook to show pending state to the user. + +```tsx twoslash [Example.tsx] +// @noErrors +import { useConnection, useConnect, useConnectors, useDisconnect } from 'wagmi' + +export function Example() { + const account = useConnection() + const connect = useConnect() + const connectors = useConnectors() + const connector = connectors.find((c) => c.id === 'xyz.tempo') + const disconnect = useDisconnect() + + if (connect.isPending) // [!code ++] + return
Check prompt...
{/* [!code ++] */} + return (/* ... */) +} +``` + +### Error Handling + +If an error unexpectedly occurs, we should display an error message to the user. + +We can use the `error` property from the `useConnect` hook to show error state to the user. + +```tsx twoslash [Example.tsx] +// @noErrors +import { useConnection, useConnect, useConnectors, useDisconnect } from 'wagmi' + +export function Example() { + const account = useConnection() + const connect = useConnect() + const connectors = useConnectors() + const connector = connectors.find((c) => c.id === 'xyz.tempo') + const disconnect = useDisconnect() + + if (connect.error) // [!code ++] + return
Error: {connect.error.message}
{/* [!code ++] */} + return (/* ... */) +} +``` + +### Fee Sponsorship + +The `tempoWallet` connector supports fee sponsorship via a `feePayerUrl`. This allows you to sponsor transaction fees for your users. + +```tsx twoslash [config.ts] +// @noErrors +import { createConfig, http } from 'wagmi' +import { tempo } from 'viem/chains' +import { tempoWallet } from 'accounts/wagmi' + +export const config = createConfig({ + chains: [tempo], + connectors: [tempoWallet({ + feePayerUrl: 'https://sponsor.example.com', // [!code ++] + })], + transports: { + [tempo.id]: http(), + }, +}) +``` + +See the [Sponsor user fees](/guide/payments/sponsor-user-fees) guide for more details on setting up a fee payer server. + +## Learning Resources + + + + + + diff --git a/src/pages/guide/use-accounts/index.mdx b/src/pages/guide/use-accounts/index.mdx index 0a044acd..bbd749ac 100644 --- a/src/pages/guide/use-accounts/index.mdx +++ b/src/pages/guide/use-accounts/index.mdx @@ -1,39 +1,56 @@ --- -description: Create and integrate Tempo accounts with domain-bound passkeys or connect to EVM-compatible wallets. Choose embedded or universal account experiences. +description: Create and integrate Tempo accounts with the universal Tempo Wallet or domain-bound passkeys. --- import { Cards, Card } from 'vocs' # Create & Use Accounts -Create and integrate Tempo accounts into your product with domain-bound passkeys or connecting your app to EVM-compatible wallets. +Create and integrate Tempo accounts into your product with the universal Tempo Wallet or domain-bound passkeys. -:::tip -**Should I use a Passkey account or a wallet?** +## What should I use? -- If you need a **domain-bound account** experience, you can embed a Passkey account. -- If you need a **universal account** experience, you can integrate your app with wallets. +### Tempo Wallet -You can even use both if you would like to offer both experiences. +Most apps should use [Tempo Wallet](/accounts/api/dialog) - it provides a universal account experience with a central account for users that includes embedded onramp, access keys, and transaction orchestration out of the box. + +### Domain-bound Passkeys + +Use [domain-bound passkeys](/accounts/api/webAuthn) when you want to build your own wallet experience or when your app needs to manage and own the WebAuthn ceremony directly. + +:::info +Tempo Wallet uses domain-bound passkeys (on `tempo.xyz`) under the hood. ::: +### Other Wallets + +Offer users a range of existing wallets to connect from, such as MetaMask, Coinbase Wallet, and others. + +--- + + diff --git a/src/pages/sdk/typescript/index.mdx b/src/pages/sdk/typescript/index.mdx index bb9c435a..8fdf9a16 100644 --- a/src/pages/sdk/typescript/index.mdx +++ b/src/pages/sdk/typescript/index.mdx @@ -6,18 +6,6 @@ import { Cards, Card } from 'vocs' # TypeScript SDKs -:::note -**Note:** `tempo.ts/chains` & `tempo.ts/viem` have been upstreamed into [Viem](https://viem.sh/tempo) as of `viem@2.43.0`. If you are using either entrypoint, please update to use `viem/chains` or `viem/tempo` instead. - -[Migration Guide](https://github.com/tempoxyz/tempo-ts/releases/tag/tempo.ts%400.12.0) -::: - -:::note -**Note:** `tempo.ts/wagmi` has been upstreamed into [Wagmi](https://wagmi.sh/tempo) as of `wagmi@3.2.0` and `@wagmi/core@3.1.0`. Please update to use `wagmi/tempo` or `@wagmi/core/tempo` instead. - -[Migration Guide](https://github.com/tempoxyz/tempo-ts/releases/tag/tempo.ts%400.13.0) -::: - Tempo distributes TypeScript SDKs for: - [Viem](https://viem.sh): TypeScript interface for EVM blockchains diff --git a/src/pages/sdk/typescript/server/handler.compose.mdx b/src/pages/sdk/typescript/server/handler.compose.mdx deleted file mode 100644 index 2f6b2798..00000000 --- a/src/pages/sdk/typescript/server/handler.compose.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -description: Combine multiple Tempo server handlers into a single endpoint. Run fee payer and key manager services together from one server. ---- - -# `Handler.compose` - -Composes multiple handlers into a single handler. This is useful when you want to run multiple services (like fee payer and key manager) from a single endpoint. - -## Usage - -```ts twoslash [server.ts] -// @noErrors -import { Handler, Kv } from 'tempo.ts/server' -import { tempo } from 'viem/chains' -import { http } from 'viem' -import { privateKeyToAccount } from 'viem/accounts' - -const handler = Handler.compose([ - // Create a fee payer handler - Handler.feePayer({ - account: privateKeyToAccount('0x...'), - chain: tempo.extend({ - feeToken: '0x20c0...0001' - }), - transport: http(), - path: '/fee-payer', - }) - - // Create a key manager handler - Handler.keyManager({ - kv: Kv.memory(), - path: '/keys', - rp: 'example.com', - }) -]) -``` - -Then plug `handler` into your server framework of choice. For example: - -```ts -createServer(handler.listener) // Node.js -Bun.serve(handler) // Bun -Deno.serve(handler) // Deno -app.all('*', c => handler.fetch(c.request)) // Elysia -app.use(handler.listener) // Express -app.use(c => handler.fetch(c.req.raw)) // Hono -export const GET = handler.fetch // Next.js -export const POST = handler.fetch // Next.js -``` -### Base Path - -You can configure the base path for the composed handler. - -```ts -const handler = Handler.compose([ - Handler.feePayer({ path: '/fee-payer', ... }), - Handler.keyManager({ path: '/keys', ... }), -], { path: '/api' }) -``` - -Requests are routed as follows: -- `POST /api/fee-payer` → Fee payer handler -- `GET /api/keys/challenge` → Key manager handler -- `GET /api/keys/:credentialId` → Key manager handler -- `POST /api/keys/:credentialId` → Key manager handler - -## Parameters - -### handlers - -- **Type:** `Handler[]` - -An array of handlers to compose. Handlers are tried in order, and the first one that doesn't return a 404 wins. - -```ts twoslash -// @noErrors -import { Handler } from 'tempo.ts/server' - -const handler = Handler.compose([ - Handler.feePayer({ /* ... */ }), - Handler.keyManager({ /* ... */ }), - // Add more handlers as needed -]) -``` - -### options.path - -- **Type:** `string` -- **Default:** `'/'` - -The base path where all composed handlers will be mounted. This path is stripped from incoming requests before forwarding to individual handlers. - -```ts twoslash -// @noErrors -import { Handler } from 'tempo.ts/server' - -const handler = Handler.compose( - [ - Handler.feePayer({ path: '/fee-payer', /* ... */ }), - Handler.keyManager({ path: '/keys', /* ... */ }), - ], - { path: '/api' }, // [!code focus] -) - -// Endpoints will be: -// POST /api/fee-payer -// GET /api/keys/challenge -// GET /api/keys/:credentialId -// POST /api/keys/:credentialId -``` diff --git a/src/pages/sdk/typescript/server/handler.feePayer.mdx b/src/pages/sdk/typescript/server/handler.feePayer.mdx deleted file mode 100644 index aa897add..00000000 --- a/src/pages/sdk/typescript/server/handler.feePayer.mdx +++ /dev/null @@ -1,117 +0,0 @@ ---- -description: Create a server handler to subsidize gas costs for users. Sign transactions with a dedicated fee payer account on your backend. ---- - -# `Handler.feePayer` - -Creates a server handler that acts as a fee payer for transactions. -This enables you to subsidize gas costs for your users by signing transactions with a dedicated fee payer account on your backend. - -## Usage - -:::code-group - -```ts twoslash [server.ts] -// @noErrors -import { Handler } from 'tempo.ts/server' -import { tempo } from 'viem/chains' -import { http } from 'viem' -import { privateKeyToAccount } from 'viem/accounts' - -const handler = Handler.feePayer({ - account: privateKeyToAccount('0x...'), - chain: tempo.extend({ feeToken: '0x20c0...0001' }), - path: '/fee-payer', - transport: http(), -}) -``` - -```ts twoslash [example.client.ts] -// @noErrors -import { createClient, http, walletActions } from 'viem' -import { tempo } from 'viem/chains' -import { tempoActions, withFeePayer } from 'viem/tempo' - -const client = createClient({ - chain: tempo, - transport: withFeePayer( - http(), // Default transport - http('http://localhost:3000/fee-payer'), // Fee payer transport (your server) - ), -}).extend(tempoActions()) - -// Send a fee-sponsored payment -const receipt = await client.token.transferSync({ - amount: parseUnits('10', 6), - feePayer: true, - to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb', - token: '0x20c0000000000000000000000000000000000001', -}) -``` - -::: - -Then plug `handler` into your server framework of choice. For example: - -```ts -createServer(handler.listener) // Node.js -Bun.serve(handler) // Bun -Deno.serve(handler) // Deno -app.all('*', c => handler.fetch(c.request)) // Elysia -app.use(handler.listener) // Express -app.use(c => handler.fetch(c.req.raw)) // Hono -export const GET = handler.fetch // Next.js -export const POST = handler.fetch // Next.js -``` - - -## How It Works - -The fee payer handler intercepts RPC requests and handles the following methods: - -- **`eth_signTransaction`** - Signs a transaction with the fee payer account -- **`eth_signRawTransaction`** - Signs a pre-serialized transaction with the fee payer account -- **`eth_sendRawTransaction`** - Signs and broadcasts a transaction with the fee payer account -- **`eth_sendRawTransactionSync`** - Signs, broadcasts, and waits for confirmation - -For each of these methods, the handler: -1. Deserializes the transaction (if necessary) -2. Signs the transaction with your fee payer account -3. Returns the signed transaction or transaction hash - -## Parameters - -### account - -- **Type:** `LocalAccount` -- **Required:** Yes - -The account to use as the fee payer. This account will sign all transactions and pay the gas fees. - -### chain - -- **Type:** `Chain` - -Chain (and fee token) to use. - -### transport - -- **Type:** `Transport` - -Transport to use. - -### path - -- **Type:** `string` -- **Default:** `'/'` -- **Optional** - -The path where the handler will listen for requests. - -### onRequest - -- **Type:** `(request: RpcRequest) => Promise` -- **Optional** - -A callback function that's called before processing each request. Useful for logging, rate limiting, or custom validation. - diff --git a/src/pages/sdk/typescript/server/handler.keyManager.mdx b/src/pages/sdk/typescript/server/handler.keyManager.mdx deleted file mode 100644 index c5f04c6c..00000000 --- a/src/pages/sdk/typescript/server/handler.keyManager.mdx +++ /dev/null @@ -1,168 +0,0 @@ ---- -description: Create a server handler to manage WebAuthn credential public keys. Enable passkey authentication for users across multiple devices. ---- - -# `Handler.keyManager` - -Creates a server handler that manages WebAuthn credential public keys. This handler stores and retrieves the public keys associated with passkey credentials, enabling users to access their accounts from any device. - -## Usage - -:::code-group - -```ts twoslash [server.ts] -// @noErrors -import { Handler, Kv } from 'tempo.ts/server' - -const handler = Handler.keyManager({ - kv: Kv.memory(), - path: '/keys', - rp: 'example.com', -}) -``` - -```ts twoslash [wagmi.config.ts] -// @noErrors -import { tempo } from 'viem/chains' -import { KeyManager, webAuthn } from 'wagmi/tempo' -import { createConfig, http } from 'wagmi' - -export const config = createConfig({ - connectors: [ - webAuthn({ - keyManager: KeyManager.http('http://localhost:3000/keys'), - rpId: 'example.com', - }), - ], - chains: [tempo], - transports: { - [tempo.id]: http(), - }, -}) -``` - -::: - -Then plug `handler` into your server framework of choice. For example: - -```ts -createServer(handler.listener) // Node.js -Bun.serve(handler) // Bun -Deno.serve(handler) // Deno -app.all('*', c => handler.fetch(c.request)) // Elysia -app.use(handler.listener) // Express -app.use(c => handler.fetch(c.req.raw)) // Hono -export const GET = handler.fetch // Next.js -export const POST = handler.fetch // Next.js -``` - -:::warning - -It is not recommended to use `Kv.memory()` in production. Instead, use a persistent store like Cloudflare or Vercel KV, or a Redis instance. - -::: - -## How It Works - -The key manager handler exposes three endpoints: - -### `GET /{path}/challenge` - -Generates a random challenge for WebAuthn credential creation. The challenge is stored temporarily (with a 5-minute expiration) to be verified later during credential registration. - -### `GET /{path}/:credentialId` - -Retrieves the public key for a given credential ID. This is used when a user authenticates with an existing passkey. - -### `POST /{path}/:credentialId` - -Stores a public key for a new credential. This endpoint: -1. Verifies the challenge was previously issued and is still valid -2. Validates the credential's `clientDataJSON.type` is `'webauthn.create'` -3. Verifies the origin matches the configured RP ID (if set) -4. Extracts and stores the public key from the authenticator data - -## Parameters - -### kv - -- **Type:** `Kv` -- **Required:** Yes - -The key-value store to use for persisting challenges and public keys. tempo.ts provides adapters for common KV stores: - -```ts twoslash -// @noErrors -import { Kv } from 'tempo.ts/server' - -// In-memory store (for development/testing) -const kv = Kv.memory() - -// Cloudflare KV -import { env } from 'cloudflare:workers' -const kv = Kv.cloudflare(env.KEY_STORE) -``` - -:::warning -`Kv.memory()` stores data only in memory and will be lost when the server restarts. Use a persistent store like Cloudflare or Vercel KV, or a Redis instance in production. -::: - -### path - -- **Type:** `string` - -The base path where the handler will listen for requests. All three endpoints (`/challenge`, `/:credentialId`) will be mounted under this path. - -```ts twoslash -// @noErrors -import { Handler } from 'tempo.ts/server' - -const handler = Handler.keyManager({ - // ... other options - path: '/api/keys', // [!code focus] -}) - -// Endpoints will be: -// GET /api/keys/challenge -// GET /api/keys/:credentialId -// POST /api/keys/:credentialId -``` - -### rp - -- **Type:** `string | { id: string, name?: string }` - -The Relying Party (RP) identifier and name. This is used to: -- Include RP information in the challenge response -- Verify the origin of credential registration requests - -```ts twoslash -// @noErrors -import { Handler } from 'tempo.ts/server' - -// Simple string (uses as both id and name) -const handler = Handler.keyManager({ - // ... other options - rp: 'example.com', // [!code focus] -}) - -// Object with custom name -const handler2 = Handler.keyManager({ - // ... other options - rp: { // [!code focus] - id: 'example.com', // [!code focus] - name: 'Example App', // [!code focus] - }, // [!code focus] -}) -``` - -:::info -The RP ID must match the domain where your application is hosted. For localhost, the origin verification is automatically skipped. -::: - -## Storage Schema - -The handler uses the following key patterns in the KV store: - -- **`challenge:{hex}`** - Stores issued challenges -- **`credential:{credentialId}`** - Stores public keys diff --git a/src/snippets/unformatted/withFeePayer.ts b/src/snippets/unformatted/withFeePayer.ts index 2ec49c4e..91299c37 100644 --- a/src/snippets/unformatted/withFeePayer.ts +++ b/src/snippets/unformatted/withFeePayer.ts @@ -30,23 +30,13 @@ const _receipt2 = await client.sendTransactionSync({ // [!endregion usage] -import { Handler } from 'tempo.ts/server' // [!region server] -import { createClient, http } from 'viem' +import { Handler } from 'accounts/server' import { privateKeyToAccount } from 'viem/accounts' -import { tempo } from 'viem/chains' - -const client = createClient({ - chain: tempo.extend({ - feeToken: '0x20c0000000000000000000000000000000000001', - }), - transport: http(), -}) const handler = Handler.feePayer({ // [!code hl] account: privateKeyToAccount('0x...'), // [!code hl] - client, // [!code hl] }) // [!code hl] const server = createServer(handler.listener) diff --git a/src/snippets/wagmi.config.ts b/src/snippets/wagmi.config.ts index 0fce1d1f..d0706f92 100644 --- a/src/snippets/wagmi.config.ts +++ b/src/snippets/wagmi.config.ts @@ -2,18 +2,14 @@ // biome-ignore-all lint: snippet // biome-ignore-all format: snippet -import { KeyManager, webAuthn } from 'tempo.ts/wagmi' // [!region setup] +import { tempoWallet } from 'accounts/wagmi' import { tempo } from 'viem/chains' import { createConfig, http } from 'wagmi' import { KeyManager, webAuthn } from 'wagmi/tempo' export const config = createConfig({ - connectors: [ - webAuthn({ - keyManager: KeyManager.http('https://keys.tempo.xyz'), - }), - ], + connectors: [tempoWallet()], chains: [tempo], multiInjectedProviderDiscovery: false, transports: { @@ -23,8 +19,8 @@ export const config = createConfig({ // [!endregion setup] -import { KeyManager, webAuthn } from 'tempo.ts/wagmi' // [!region withFeePayer] +import { tempoWallet } from 'accounts/wagmi' import { tempo } from 'viem/chains' import { withFeePayer } from 'viem/tempo' import { createConfig, http } from 'wagmi' @@ -32,14 +28,14 @@ import { KeyManager, webAuthn } from 'wagmi/tempo' export const config = createConfig({ connectors: [ - webAuthn({ - keyManager: KeyManager.http('https://keys.tempo.xyz'), + tempoWallet({ + feePayerUrl: 'https://sponsor.moderato.tempo.xyz', }), ], chains: [tempo], multiInjectedProviderDiscovery: false, transports: { - [tempo.id]: withFeePayer(http(), http('https://sponsor.moderato.tempo.xyz')), + [tempo.id]: http(), }, }) // [!endregion withFeePayer] diff --git a/src/wagmi.config.ts b/src/wagmi.config.ts index 02c77c1a..d36b8906 100644 --- a/src/wagmi.config.ts +++ b/src/wagmi.config.ts @@ -1,8 +1,20 @@ import { QueryClient } from '@tanstack/react-query' +import { Expiry } from 'accounts' +import { tempoWallet } from 'accounts/wagmi' +import * as React from 'react' +import { parseUnits } from 'viem' import { tempoDevnet, tempoLocalnet, tempoModerato } from 'viem/chains' import { withFeePayer } from 'viem/tempo' -import { type CreateConfigParameters, createConfig, createStorage, http, webSocket } from 'wagmi' +import { + type CreateConfigParameters, + createConfig, + createStorage, + http, + useConnectors, + webSocket, +} from 'wagmi' import { KeyManager, webAuthn } from 'wagmi/tempo' +import { alphaUsd, betaUsd, pathUsd, thetaUsd } from './components/guides/tokens' const feeToken = '0x20c0000000000000000000000000000000000001' @@ -28,8 +40,23 @@ export function getConfig(options: getConfig.Options = {}) { }, chains: [chain], connectors: [ + tempoWallet({ + authorizeAccessKey: () => ({ + expiry: Expiry.days(1), + limits: [ + { token: pathUsd, limit: parseUnits('500', 6) }, + { token: alphaUsd, limit: parseUnits('500', 6) }, + { token: betaUsd, limit: parseUnits('500', 6) }, + { token: thetaUsd, limit: parseUnits('500', 6) }, + ], + }), + feePayerUrl: 'https://sponsor.moderato.tempo.xyz', + }), webAuthn({ - grantAccessKey: { chainId: BigInt(chain.id) } as any, + grantAccessKey: { + // @ts-expect-error - TODO: migrate to webAuthn on Accounts SDK + chainId: BigInt(chain.id), + }, keyManager: KeyManager.http('https://keys.tempo.xyz'), rpId, }), @@ -67,6 +94,24 @@ export const config = getConfig() export const queryClient = new QueryClient() +export function useTempoWalletConnector() { + const connectors = useConnectors() + return React.useMemo( + // biome-ignore lint/style/noNonNullAssertion: _ + () => connectors.find((connector) => connector.id === 'xyz.tempo')!, + [connectors], + ) +} + +export function useWebAuthnConnector() { + const connectors = useConnectors() + return React.useMemo( + // biome-ignore lint/style/noNonNullAssertion: _ + () => connectors.find((connector) => connector.id === 'webAuthn')!, + [connectors], + ) +} + declare module 'wagmi' { interface Register { config: typeof config diff --git a/vite.config.ts b/vite.config.ts index 5fb213cb..ddbda55a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,6 +3,7 @@ import * as path from 'node:path' import react from '@vitejs/plugin-react' import { Instance } from 'prool' import { defineConfig, loadEnv, type Plugin } from 'vite' +import mkcert from 'vite-plugin-mkcert' import { vocs } from 'vocs/vite' // https://vite.dev/config/ @@ -12,7 +13,7 @@ export default defineConfig(({ mode }) => { if (!(key in process.env)) process.env[key] = env[key] } return { - plugins: [syncTips(), vocs(), react(), tempoNode()], + plugins: [syncTips(), vocs(), react(), mkcert(), tempoNode()], } }) diff --git a/vocs.config.ts b/vocs.config.ts index 5d486188..68233621 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -93,11 +93,15 @@ export default defineConfig({ link: '/guide/use-accounts', }, { - text: 'Embed Passkey accounts', + text: 'Embed Tempo Wallet', + link: '/guide/use-accounts/embed-tempo-wallet', + }, + { + text: 'Embed domain-bound Passkeys', link: '/guide/use-accounts/embed-passkeys', }, { - text: 'Connect to wallets', + text: 'Connect to other wallets', link: '/guide/use-accounts/connect-to-wallets', }, { @@ -549,6 +553,10 @@ export default defineConfig({ { text: 'Tempo Developer Tools', items: [ + { + text: 'Accounts SDK', + link: '/accounts', + }, { text: 'CLI', collapsed: true, @@ -599,32 +607,6 @@ export default defineConfig({ text: 'Wagmi Reference', link: 'https://wagmi.sh/tempo', }, - { - text: 'Server Reference', - items: [ - { - text: 'Handlers', - items: [ - { - text: 'Overview', - link: '/sdk/typescript/server/handlers', - }, - { - text: 'compose', - link: '/sdk/typescript/server/handler.compose', - }, - { - text: 'feePayer', - link: '/sdk/typescript/server/handler.feePayer', - }, - { - text: 'keyManager', - link: '/sdk/typescript/server/handler.keyManager', - }, - ], - }, - ], - }, { text: 'Prool Reference', items: [ @@ -730,6 +712,244 @@ export default defineConfig({ // ], // }, ], + '/accounts': { + backLink: true, + items: [ + { + text: 'Accounts SDK', + items: [ + { + text: 'Getting Started', + link: '/accounts', + }, + { + text: 'Deploying to Production', + link: '/accounts/production', + }, + { + text: 'FAQ', + link: '/accounts/faq', + }, + ], + }, + { + text: 'Guides', + items: [ + { + text: 'Create & Use Accounts', + link: '/guide/use-accounts', + external: true, + }, + { + text: 'Make Payments', + link: '/guide/payments', + external: true, + }, + { + text: 'Sponsor Fees', + link: '/guide/payments/sponsor-user-fees', + external: true, + }, + { + text: 'Issue Stablecoins', + link: '/guide/issuance', + external: true, + }, + { + text: 'Exchange Stablecoins', + link: '/guide/stablecoin-dex', + external: true, + }, + ], + }, + { + text: 'Core', + items: [ + { + text: 'Provider', + link: '/accounts/api/provider', + }, + { + text: 'Adapters', + collapsed: true, + items: [ + { + text: 'Overview', + link: '/accounts/api/adapters', + }, + { + text: 'dialog / tempoWallet', + link: '/accounts/api/dialog', + }, + { + text: 'webAuthn', + link: '/accounts/api/webAuthn', + }, + { + text: 'local', + link: '/accounts/api/local', + }, + ], + }, + { + text: 'Dialog', + collapsed: true, + items: [ + { + text: 'Overview', + link: '/accounts/api/dialogs', + }, + { + text: '.iframe', + link: '/accounts/api/dialog.iframe', + }, + { + text: '.popup', + link: '/accounts/api/dialog.popup', + }, + ], + }, + { + text: 'Expiry', + link: '/accounts/api/expiry', + }, + { + text: 'WebAuthnCeremony', + collapsed: true, + items: [ + { + text: 'Overview', + link: '/accounts/api/webauthnceremony', + }, + { + text: '.from', + link: '/accounts/api/webauthnceremony.from', + }, + { + text: '.server', + link: '/accounts/api/webauthnceremony.server', + }, + ], + }, + ], + }, + { + text: 'Wagmi', + items: [ + { + text: 'Connectors', + collapsed: true, + items: [ + { + text: 'tempoWallet', + link: '/accounts/wagmi/tempoWallet', + }, + { + text: 'webAuthn', + link: '/accounts/wagmi/webAuthn', + }, + ], + }, + ], + }, + { + text: 'Server', + items: [ + { + text: 'Handlers', + collapsed: true, + items: [ + { + text: 'Overview', + link: '/accounts/server', + }, + { + text: '.compose', + link: '/accounts/server/handler.compose', + }, + { + text: '.feePayer', + link: '/accounts/server/handler.feePayer', + }, + { + text: '.webAuthn', + link: '/accounts/server/handler.webAuthn', + }, + ], + }, + { + text: 'Kv', + link: '/accounts/server/kv', + }, + ], + }, + { + text: 'JSON-RPC', + items: [ + { + text: 'wallet_connect 🚧', + disabled: true, + link: '/accounts/rpc/wallet_connect', + }, + { + text: 'wallet_disconnect 🚧', + disabled: true, + link: '/accounts/rpc/wallet_disconnect', + }, + { + text: 'wallet_authorizeAccessKey 🚧', + disabled: true, + link: '/accounts/rpc/wallet_authorizeAccessKey', + }, + { + text: 'wallet_revokeAccessKey 🚧', + disabled: true, + link: '/accounts/rpc/wallet_revokeAccessKey', + }, + { + text: 'wallet_getBalances 🚧', + disabled: true, + link: '/accounts/rpc/wallet_getBalances', + }, + { + text: 'wallet_getCapabilities 🚧', + disabled: true, + link: '/accounts/rpc/wallet_getCapabilities', + }, + { + text: 'wallet_getCallsStatus 🚧', + disabled: true, + link: '/accounts/rpc/wallet_getCallsStatus', + }, + { + text: 'wallet_sendCalls 🚧', + disabled: true, + link: '/accounts/rpc/wallet_sendCalls', + }, + { + text: 'eth_sendTransaction 🚧', + disabled: true, + link: '/accounts/rpc/eth_sendTransaction', + }, + { + text: 'eth_sendTransactionSync 🚧', + disabled: true, + link: '/accounts/rpc/eth_sendTransactionSync', + }, + { + text: 'eth_fillTransaction 🚧', + disabled: true, + link: '/accounts/rpc/eth_fillTransaction', + }, + { + text: 'personal_sign 🚧', + disabled: true, + link: '/accounts/rpc/personal_sign', + }, + ], + }, + ], + }, '/learn': [ { text: 'Home', @@ -879,7 +1099,28 @@ export default defineConfig({ }, { source: '/sdk/typescript/server', - destination: '/sdk/typescript/server/handlers', + destination: '/accounts/server', + status: 301, + }, + { + source: '/sdk/typescript/server/handlers', + destination: '/accounts/server', + status: 301, + }, + { + source: '/sdk/typescript/server/handler.compose', + destination: '/accounts/server/handler.compose', + status: 301, + }, + { + source: '/sdk/typescript/server/handler.feePayer', + destination: '/accounts/server/handler.feePayer', + status: 301, + }, + { + source: '/sdk/typescript/server/handler.keyManager', + destination: '/accounts/server/handler.webAuthn', + status: 301, }, { source: '/sdk/typescript/prool',