@rhinestone/1auth 0.6.8 → 0.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +171 -12
  2. package/dist/chunk-GUAI55LL.mjs +179 -0
  3. package/dist/chunk-GUAI55LL.mjs.map +1 -0
  4. package/dist/chunk-IHBVEU33.mjs +20 -0
  5. package/dist/chunk-IHBVEU33.mjs.map +1 -0
  6. package/dist/chunk-IIACVHR3.mjs +28 -0
  7. package/dist/chunk-IIACVHR3.mjs.map +1 -0
  8. package/dist/chunk-N6KE5CII.mjs +72 -0
  9. package/dist/chunk-N6KE5CII.mjs.map +1 -0
  10. package/dist/{chunk-SXISYG2P.mjs → chunk-THKG3FAG.mjs} +137 -255
  11. package/dist/chunk-THKG3FAG.mjs.map +1 -0
  12. package/dist/{client-BrMrhetG.d.mts → client-B_CzDa_I.d.ts} +360 -857
  13. package/dist/{client-BrMrhetG.d.ts → client-F4DnFM8d.d.mts} +360 -857
  14. package/dist/headless.d.mts +109 -0
  15. package/dist/headless.d.ts +109 -0
  16. package/dist/headless.js +467 -0
  17. package/dist/headless.js.map +1 -0
  18. package/dist/headless.mjs +382 -0
  19. package/dist/headless.mjs.map +1 -0
  20. package/dist/index.d.mts +96 -144
  21. package/dist/index.d.ts +96 -144
  22. package/dist/index.js +3212 -756
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +3010 -705
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/{provider-CDl9wYEc.d.mts → provider-Cd7Ip5L-.d.ts} +6 -5
  27. package/dist/{provider-Dgv533YQ.d.ts → provider-IvYXPMpk.d.mts} +6 -5
  28. package/dist/react.d.mts +42 -2
  29. package/dist/react.d.ts +42 -2
  30. package/dist/react.js +92 -2
  31. package/dist/react.js.map +1 -1
  32. package/dist/react.mjs +66 -1
  33. package/dist/react.mjs.map +1 -1
  34. package/dist/server.d.mts +118 -0
  35. package/dist/server.d.ts +118 -0
  36. package/dist/server.js +356 -0
  37. package/dist/server.js.map +1 -0
  38. package/dist/server.mjs +282 -0
  39. package/dist/server.mjs.map +1 -0
  40. package/dist/types-U_dwxbtS.d.mts +1488 -0
  41. package/dist/types-U_dwxbtS.d.ts +1488 -0
  42. package/dist/verify-BLgZzwmJ.d.ts +150 -0
  43. package/dist/verify-C8-a5c3K.d.mts +150 -0
  44. package/dist/wagmi.d.mts +5 -2
  45. package/dist/wagmi.d.ts +5 -2
  46. package/dist/wagmi.js +138 -43
  47. package/dist/wagmi.js.map +1 -1
  48. package/dist/wagmi.mjs +3 -1
  49. package/dist/wagmi.mjs.map +1 -1
  50. package/package.json +15 -2
  51. package/dist/chunk-SXISYG2P.mjs.map +0 -1
package/README.md CHANGED
@@ -28,7 +28,7 @@ The SDK provides multiple entry points for different use cases:
28
28
  |-------------|--------|----------|
29
29
  | Default | `@rhinestone/1auth` | Core client, provider, types |
30
30
  | React | `@rhinestone/1auth/react` | PayButton component |
31
- | Server | `@rhinestone/1auth/server` | Server-side intent signing |
31
+ | Server | `@rhinestone/1auth/server` | JWT signer for app-sponsored intents |
32
32
  | Wagmi | `@rhinestone/1auth/wagmi` | Wagmi connector integration |
33
33
 
34
34
  ## Quick Start
@@ -84,18 +84,22 @@ const config = createConfig({
84
84
  });
85
85
  ```
86
86
 
87
- ### Server-Side Intent Signing
87
+ ### Server-Side JWT Sponsorship
88
88
 
89
89
  ```typescript
90
- import { signIntent, createSignIntentHandler } from '@rhinestone/1auth/server';
91
-
92
- // Sign an intent with Ed25519
93
- const signedIntent = await signIntent(intent, privateKey);
94
-
95
- // Or create a request handler
96
- const handler = createSignIntentHandler({
97
- privateKey: process.env.SIGNING_KEY,
90
+ import { createJwtSigner } from '@rhinestone/1auth/server';
91
+
92
+ const signer = createJwtSigner({
93
+ privateKey: JSON.parse(process.env.RHINESTONE_JWT_PRIVATE_KEY),
94
+ integratorId: process.env.RHINESTONE_INTEGRATOR_ID,
95
+ projectId: process.env.RHINESTONE_PROJECT_ID,
96
+ appId: process.env.RHINESTONE_APP_ID,
97
+ keyId: process.env.RHINESTONE_KEY_ID,
98
98
  });
99
+
100
+ // Use in your API routes
101
+ const accessToken = await signer.accessToken();
102
+ const extensionToken = await signer.getIntentExtensionToken(intentInput);
99
103
  ```
100
104
 
101
105
  ## Core Exports
@@ -118,13 +122,168 @@ const handler = createSignIntentHandler({
118
122
 
119
123
  ### `@rhinestone/1auth/server`
120
124
 
121
- - `signIntent()` - Server-side intent signing with Ed25519
122
- - `createSignIntentHandler()` - Express/fetch handler for intent signing
125
+ - `createJwtSigner()` - Create a JWT signer for app-sponsored intents (RS256)
126
+ - `JwtSignerConfig` - Configuration type for the JWT signer
123
127
 
124
128
  ### `@rhinestone/1auth/wagmi`
125
129
 
126
130
  - `oneAuth()` - Wagmi connector factory
127
131
 
132
+ ## App-Sponsored Intents
133
+
134
+ By default, 1auth sponsors transaction fees for all intents. With sponsorship, your app takes over billing by providing JWT tokens to authorize each intent.
135
+
136
+ ### How It Works
137
+
138
+ ```mermaid
139
+ sequenceDiagram
140
+ participant App as Your App
141
+ participant BE as Your Backend
142
+ participant SDK as @rhinestone/1auth
143
+ participant PS as 1auth Passkey Service
144
+
145
+ Note over App,SDK: 1. Configure the client with sponsorship URLs
146
+ App->>SDK: new OneAuthClient({<br/> sponsorship: {<br/> accessTokenUrl,<br/> extensionTokenUrl<br/> }<br/>})
147
+
148
+ Note over SDK,BE: 2. Prepare the intent
149
+ SDK->>PS: POST /api/intent/prepare
150
+ PS-->>SDK: { intentOp, digestResult, ... }
151
+
152
+ Note over SDK,BE: 3. In parallel: fetch tokens (same-origin, with session cookie) + WebAuthn ceremony
153
+ par
154
+ SDK->>BE: GET /sponsorship/access-token
155
+ BE-->>SDK: { token: "eyJhbG..." }
156
+ and
157
+ SDK->>BE: POST /sponsorship/extension-token { intentOp }
158
+ BE-->>SDK: { token: "eyJhbG..." }
159
+ and
160
+ SDK->>SDK: WebAuthn signing ceremony
161
+ end
162
+
163
+ Note over SDK,PS: 4. Execute with pre-fetched tokens in request body
164
+ SDK->>PS: POST /api/intent/execute<br/>{ signature, sponsorship: { accessToken, extensionToken } }
165
+ PS-->>SDK: { intentId }
166
+ ```
167
+
168
+ ### Why this shape
169
+
170
+ The SDK calls your token endpoints **from the browser, same-origin**. Your app's session cookie naturally authenticates the request — no need to pass bearer tokens through the SDK. The passkey service never fetches from your backend on your behalf, so there's no SSRF surface and no CORS config to worry about.
171
+
172
+ ### Setup
173
+
174
+ **1. Create two endpoints on your backend, on the same origin as your app**
175
+
176
+ Gate these with your existing session auth — the user's cookie rides along automatically.
177
+
178
+ ```typescript
179
+ // GET /api/sponsorship/access-token
180
+ import { createJwtSigner } from '@rhinestone/1auth/server';
181
+
182
+ const signer = createJwtSigner({
183
+ jwt: {
184
+ privateKey: JSON.parse(process.env.RHINESTONE_JWT_PRIVATE_KEY),
185
+ integratorId: process.env.RHINESTONE_INTEGRATOR_ID,
186
+ projectId: process.env.RHINESTONE_PROJECT_ID,
187
+ appId: process.env.RHINESTONE_APP_ID,
188
+ keyId: process.env.RHINESTONE_KEY_ID,
189
+ },
190
+ });
191
+
192
+ export async function handleAccessToken(req, res) {
193
+ const user = await verifySession(req); // your app's auth
194
+ if (!user) return res.status(401).json({ error: 'Unauthorized' });
195
+ const token = await signer.accessToken();
196
+ res.json({ token });
197
+ }
198
+
199
+ // POST /api/sponsorship/extension-token
200
+ // Body: { intentOp: string } — JSON-stringified intent operation
201
+ export async function handleExtensionToken(req, res) {
202
+ const user = await verifySession(req);
203
+ if (!user) return res.status(401).json({ error: 'Unauthorized' });
204
+
205
+ const { intentOp } = req.body;
206
+ const intentInput = JSON.parse(intentOp);
207
+
208
+ // Enforce policies
209
+ if (await exceedsSpendingLimit(user.id)) {
210
+ return res.status(403).json({ error: 'Spending limit exceeded' });
211
+ }
212
+
213
+ const token = await signer.getIntentExtensionToken(intentInput);
214
+ res.json({ token });
215
+ }
216
+ ```
217
+
218
+ **2. Configure `OneAuthClient` with the URL form**
219
+
220
+ ```typescript
221
+ import { OneAuthClient, createOneAuthProvider } from '@rhinestone/1auth';
222
+
223
+ const client = new OneAuthClient({
224
+ providerUrl: 'https://passkey.1auth.box',
225
+ sponsorship: {
226
+ accessTokenUrl: '/api/sponsorship/access-token',
227
+ extensionTokenUrl: '/api/sponsorship/extension-token',
228
+ },
229
+ });
230
+
231
+ const provider = createOneAuthProvider({ client, chainId: 8453 });
232
+
233
+ const txHash = await provider.request({
234
+ method: 'eth_sendTransaction',
235
+ params: [{ to: '0x...', data: '0x...' }],
236
+ });
237
+ ```
238
+
239
+ **React apps** can use the convenience hook:
240
+
241
+ ```tsx
242
+ import { useSponsorship } from '@rhinestone/1auth/react';
243
+ import { OneAuthClient } from '@rhinestone/1auth';
244
+
245
+ function MyApp() {
246
+ const sponsorship = useSponsorship({
247
+ accessTokenUrl: '/api/sponsorship/access-token',
248
+ extensionTokenUrl: '/api/sponsorship/extension-token',
249
+ });
250
+
251
+ const client = useMemo(
252
+ () => new OneAuthClient({ providerUrl, sponsorship }),
253
+ [sponsorship],
254
+ );
255
+ // ...
256
+ }
257
+ ```
258
+
259
+ The hook caches the access token in a ref and refetches on 401 from the extension-token endpoint.
260
+
261
+ ### SponsorshipConfig
262
+
263
+ `SponsorshipConfig` is a discriminated union — pick whichever form fits:
264
+
265
+ ```typescript
266
+ // Convenience form — SDK does the fetching.
267
+ type SponsorshipUrlConfig = {
268
+ accessTokenUrl: string; // GET → { token }
269
+ extensionTokenUrl: string; // POST { intentOp: string } → { token }
270
+ };
271
+
272
+ // Low-level form — you control token sourcing / refresh / caching.
273
+ type SponsorshipCallbackConfig = {
274
+ accessToken: () => Promise<string>;
275
+ getExtensionToken: (intentOp: string) => Promise<string>;
276
+ };
277
+
278
+ type SponsorshipConfig = SponsorshipUrlConfig | SponsorshipCallbackConfig;
279
+ ```
280
+
281
+ The SDK pre-fetches both tokens in parallel with the WebAuthn signing ceremony and passes them to the passkey service via the execute request body. The passkey service never makes outbound requests to your backend.
282
+
283
+ ### Without Sponsorship
284
+
285
+ If you don't pass `sponsorship`, 1auth sponsors intents on your behalf using its own API key. No changes needed — this is the default behavior.
286
+
128
287
  ## License
129
288
 
130
289
  MIT
@@ -0,0 +1,179 @@
1
+ // src/registry.ts
2
+ import { isAddress } from "viem";
3
+ import * as viemChains from "viem/chains";
4
+ import { chainRegistry } from "@rhinestone/shared-configs";
5
+ var env = typeof process !== "undefined" ? process.env : {};
6
+ var VIEM_CHAIN_BY_ID = /* @__PURE__ */ new Map();
7
+ for (const value of Object.values(viemChains)) {
8
+ if (typeof value !== "object" || value === null || !("id" in value) || !("name" in value)) continue;
9
+ const chain = value;
10
+ const existing = VIEM_CHAIN_BY_ID.get(chain.id);
11
+ if (!existing || existing.testnet && !chain.testnet) {
12
+ VIEM_CHAIN_BY_ID.set(chain.id, chain);
13
+ }
14
+ }
15
+ function isEvmRegistryEntry(entry) {
16
+ return entry?.vmType === "evm";
17
+ }
18
+ function getEvmRegistryEntry(chainId) {
19
+ const entry = chainRegistry[String(chainId)];
20
+ return isEvmRegistryEntry(entry) ? entry : void 0;
21
+ }
22
+ var REGISTRY_CHAIN_IDS = Object.entries(chainRegistry).filter(([, entry]) => isEvmRegistryEntry(entry)).map(([id]) => Number(id));
23
+ var SUPPORTED_CHAIN_IDS = new Set(REGISTRY_CHAIN_IDS);
24
+ function tokensForChain(chainId) {
25
+ const entry = getEvmRegistryEntry(chainId);
26
+ if (!entry) return [];
27
+ return entry.tokens.map((token) => ({
28
+ symbol: token.symbol,
29
+ address: token.address,
30
+ decimals: token.decimals
31
+ }));
32
+ }
33
+ function isAddressInput(value) {
34
+ return isAddress(value, { strict: false });
35
+ }
36
+ function parseBool(value) {
37
+ if (value === "true" || value === "1") return true;
38
+ if (value === "false" || value === "0") return false;
39
+ return void 0;
40
+ }
41
+ function resolveIncludeTestnets(explicit) {
42
+ if (explicit !== void 0) return explicit;
43
+ const envValue = parseBool(env.NEXT_PUBLIC_ORCHESTRATOR_USE_TESTNETS) ?? parseBool(env.ORCHESTRATOR_USE_TESTNETS);
44
+ return envValue ?? false;
45
+ }
46
+ function applyChainFilters(chainIds, options) {
47
+ const includeTestnets = resolveIncludeTestnets(options?.includeTestnets);
48
+ const allowlist = options?.chainIds;
49
+ let filtered = chainIds;
50
+ if (!includeTestnets) {
51
+ filtered = filtered.filter((chainId) => !isTestnet(chainId));
52
+ }
53
+ if (allowlist) {
54
+ const allowed = new Set(allowlist);
55
+ filtered = filtered.filter((chainId) => allowed.has(chainId));
56
+ }
57
+ return filtered;
58
+ }
59
+ function getSupportedChainIds(options) {
60
+ return applyChainFilters(REGISTRY_CHAIN_IDS, options);
61
+ }
62
+ function getSupportedChains(options) {
63
+ return getSupportedChainIds(options).map((chainId) => VIEM_CHAIN_BY_ID.get(chainId)).filter((chain) => Boolean(chain));
64
+ }
65
+ function getAllSupportedChainsAndTokens(options) {
66
+ return getSupportedChainIds(options).map((chainId) => ({
67
+ chainId,
68
+ tokens: tokensForChain(chainId)
69
+ }));
70
+ }
71
+ function getChainById(chainId) {
72
+ if (!SUPPORTED_CHAIN_IDS.has(chainId)) {
73
+ throw new Error(`Unsupported chain ID: ${chainId}`);
74
+ }
75
+ const chain = VIEM_CHAIN_BY_ID.get(chainId);
76
+ if (!chain) {
77
+ throw new Error(`Unsupported chain ID: ${chainId}`);
78
+ }
79
+ return chain;
80
+ }
81
+ function getChainName(chainId) {
82
+ try {
83
+ return getChainById(chainId).name;
84
+ } catch {
85
+ return `Chain ${chainId}`;
86
+ }
87
+ }
88
+ function getChainExplorerUrl(chainId) {
89
+ try {
90
+ return getChainById(chainId).blockExplorers?.default?.url;
91
+ } catch {
92
+ return void 0;
93
+ }
94
+ }
95
+ function getChainRpcUrl(chainId) {
96
+ try {
97
+ const chain = getChainById(chainId);
98
+ return chain.rpcUrls?.default?.http?.[0] || chain.rpcUrls?.public?.http?.[0];
99
+ } catch {
100
+ return void 0;
101
+ }
102
+ }
103
+ function getSupportedTokens(chainId) {
104
+ return tokensForChain(chainId);
105
+ }
106
+ function getSupportedTokenSymbols(chainId) {
107
+ return getSupportedTokens(chainId).map((token) => token.symbol);
108
+ }
109
+ function getTokenAddress(symbolOrAddress, chainId) {
110
+ if (isAddressInput(symbolOrAddress)) {
111
+ return symbolOrAddress;
112
+ }
113
+ const match = getSupportedTokens(chainId).find(
114
+ (t) => t.symbol.toUpperCase() === symbolOrAddress.toUpperCase()
115
+ );
116
+ if (!match) {
117
+ throw new Error(`Unsupported token "${symbolOrAddress}" on chain ${chainId}`);
118
+ }
119
+ return match.address;
120
+ }
121
+ function getTokenDecimals(symbolOrAddress, chainId) {
122
+ const match = getSupportedTokens(chainId).find(
123
+ (t) => isAddressInput(symbolOrAddress) ? t.address.toLowerCase() === symbolOrAddress.toLowerCase() : t.symbol.toUpperCase() === symbolOrAddress.toUpperCase()
124
+ );
125
+ if (!match && isAddressInput(symbolOrAddress) && SUPPORTED_CHAIN_IDS.has(chainId)) {
126
+ return 18;
127
+ }
128
+ if (!match) {
129
+ throw new Error(`Unsupported token "${symbolOrAddress}" on chain ${chainId}`);
130
+ }
131
+ return match.decimals;
132
+ }
133
+ function resolveTokenAddress(token, chainId) {
134
+ if (isAddressInput(token)) {
135
+ return token;
136
+ }
137
+ return getTokenAddress(token, chainId);
138
+ }
139
+ function isTestnet(chainId) {
140
+ try {
141
+ return getChainById(chainId).testnet ?? false;
142
+ } catch {
143
+ return false;
144
+ }
145
+ }
146
+ function getTokenSymbol(tokenAddress, chainId) {
147
+ const token = getSupportedTokens(chainId).find(
148
+ (entry) => entry.address.toLowerCase() === tokenAddress.toLowerCase()
149
+ );
150
+ if (token) {
151
+ return token.symbol;
152
+ }
153
+ if (!SUPPORTED_CHAIN_IDS.has(chainId)) {
154
+ throw new Error(`Unsupported chain ID: ${chainId}`);
155
+ }
156
+ return "Token";
157
+ }
158
+ function isTokenAddressSupported(tokenAddress, chainId) {
159
+ return isAddressInput(tokenAddress) && SUPPORTED_CHAIN_IDS.has(chainId);
160
+ }
161
+
162
+ export {
163
+ getSupportedChainIds,
164
+ getSupportedChains,
165
+ getAllSupportedChainsAndTokens,
166
+ getChainById,
167
+ getChainName,
168
+ getChainExplorerUrl,
169
+ getChainRpcUrl,
170
+ getSupportedTokens,
171
+ getSupportedTokenSymbols,
172
+ getTokenAddress,
173
+ getTokenDecimals,
174
+ resolveTokenAddress,
175
+ isTestnet,
176
+ getTokenSymbol,
177
+ isTokenAddressSupported
178
+ };
179
+ //# sourceMappingURL=chunk-GUAI55LL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/registry.ts"],"sourcesContent":["/**\n * @file Chain and token registry for the 1auth SDK.\n *\n * Wraps `@rhinestone/shared-configs` chain/token data and combines it with\n * viem's chain definitions to expose a filtered, testnet-aware registry.\n * Consumers can look up supported chains and tokens, resolve token addresses\n * by symbol, and retrieve chain metadata such as explorer URLs and default RPC\n * endpoints.\n *\n * Testnet inclusion is controlled by the `includeTestnets` option on each\n * function or, as a project-wide default, by the environment variables\n * `NEXT_PUBLIC_ORCHESTRATOR_USE_TESTNETS` or `ORCHESTRATOR_USE_TESTNETS`.\n */\n\nimport type { Address, Chain } from \"viem\";\nimport { isAddress } from \"viem\";\nimport * as viemChains from \"viem/chains\";\nimport { chainRegistry } from \"@rhinestone/shared-configs\";\n\nexport type TokenConfig = {\n symbol: string;\n address: Address;\n decimals: number;\n};\n\nexport type ChainFilterOptions = {\n includeTestnets?: boolean;\n chainIds?: number[];\n};\n\ntype ChainRegistryEntry = (typeof chainRegistry)[keyof typeof chainRegistry];\n\nconst env: Record<string, string | undefined> =\n typeof process !== \"undefined\" ? process.env : {};\n\n// Build a chain-ID-to-Chain map from viem, resolving collisions by preferring\n// mainnet definitions over testnets. viem exports duplicate numeric IDs (e.g.\n// zoraTestnet and hyperEvm both use 999); a naive Map would silently keep\n// whichever entry happens to appear last in iteration order, which may be the\n// testnet. The loop below only overwrites an existing entry when the incoming\n// chain is NOT a testnet and the existing one IS, ensuring the production chain\n// always wins while still falling back to a testnet entry when no mainnet\n// definition exists for a given ID.\nconst VIEM_CHAIN_BY_ID = new Map<number, Chain>();\nfor (const value of Object.values(viemChains)) {\n if (typeof value !== \"object\" || value === null || !(\"id\" in value) || !(\"name\" in value)) continue;\n const chain = value as Chain;\n const existing = VIEM_CHAIN_BY_ID.get(chain.id);\n if (!existing || (existing.testnet && !chain.testnet)) {\n VIEM_CHAIN_BY_ID.set(chain.id, chain);\n }\n}\n\nfunction isEvmRegistryEntry(\n entry: ChainRegistryEntry | undefined,\n): entry is ChainRegistryEntry & { vmType: \"evm\" } {\n return entry?.vmType === \"evm\";\n}\n\nfunction getEvmRegistryEntry(\n chainId: number,\n): (ChainRegistryEntry & { vmType: \"evm\" }) | undefined {\n const entry = chainRegistry[String(chainId) as keyof typeof chainRegistry];\n return isEvmRegistryEntry(entry) ? entry : undefined;\n}\n\n// The shared-configs registry includes non-EVM chains (Solana, Tron). Filter\n// to EVM entries before exposing supported chains — downstream consumers\n// (viem clients, wallet_getCapabilities, RPC proxy) only handle eip155 chains.\nconst REGISTRY_CHAIN_IDS = Object.entries(chainRegistry)\n .filter(([, entry]) => isEvmRegistryEntry(entry))\n .map(([id]) => Number(id));\nconst SUPPORTED_CHAIN_IDS = new Set(REGISTRY_CHAIN_IDS);\n\nfunction tokensForChain(chainId: number): TokenConfig[] {\n const entry = getEvmRegistryEntry(chainId);\n if (!entry) return [];\n return entry.tokens.map((token) => ({\n symbol: token.symbol,\n address: token.address as Address,\n decimals: token.decimals,\n }));\n}\n\n/** Treat any 20-byte hex string as an address; checksum casing is optional for user inputs. */\nfunction isAddressInput(value: string): value is Address {\n return isAddress(value, { strict: false });\n}\n\n/**\n * Parse a string environment variable into a boolean, returning `undefined`\n * when the value is absent or not a recognised truthy/falsy literal.\n *\n * Accepted truthy values: `\"true\"`, `\"1\"`.\n * Accepted falsy values: `\"false\"`, `\"0\"`.\n */\nfunction parseBool(value?: string): boolean | undefined {\n if (value === \"true\" || value === \"1\") return true;\n if (value === \"false\" || value === \"0\") return false;\n return undefined;\n}\n\n/**\n * Determine whether testnet chains should be included in registry results.\n *\n * Precedence (highest to lowest):\n * 1. `explicit` argument — a caller-supplied boolean always wins.\n * 2. `NEXT_PUBLIC_ORCHESTRATOR_USE_TESTNETS` env var — checked first so\n * Next.js browser bundles (which only expose `NEXT_PUBLIC_*` variables)\n * can configure testnet mode without a server-side env var.\n * 3. `ORCHESTRATOR_USE_TESTNETS` env var — server-side / Node.js fallback.\n * 4. Defaults to `false` (testnets excluded) when none of the above is set.\n */\nfunction resolveIncludeTestnets(explicit?: boolean): boolean {\n if (explicit !== undefined) return explicit;\n const envValue =\n parseBool(env.NEXT_PUBLIC_ORCHESTRATOR_USE_TESTNETS) ??\n parseBool(env.ORCHESTRATOR_USE_TESTNETS);\n return envValue ?? false;\n}\n\nfunction applyChainFilters(chainIds: number[], options?: ChainFilterOptions): number[] {\n const includeTestnets = resolveIncludeTestnets(options?.includeTestnets);\n const allowlist = options?.chainIds;\n let filtered = chainIds;\n\n if (!includeTestnets) {\n filtered = filtered.filter((chainId) => !isTestnet(chainId));\n }\n\n if (allowlist) {\n const allowed = new Set(allowlist);\n filtered = filtered.filter((chainId) => allowed.has(chainId));\n }\n\n return filtered;\n}\n\nexport function getSupportedChainIds(options?: ChainFilterOptions): number[] {\n return applyChainFilters(REGISTRY_CHAIN_IDS, options);\n}\n\nexport function getSupportedChains(options?: ChainFilterOptions): Chain[] {\n return getSupportedChainIds(options)\n .map((chainId) => VIEM_CHAIN_BY_ID.get(chainId))\n .filter((chain): chain is Chain => Boolean(chain));\n}\n\nexport function getAllSupportedChainsAndTokens(options?: ChainFilterOptions): Array<{\n chainId: number;\n tokens: TokenConfig[];\n}> {\n return getSupportedChainIds(options).map((chainId) => ({\n chainId,\n tokens: tokensForChain(chainId),\n }));\n}\n\nexport function getChainById(chainId: number): Chain {\n if (!SUPPORTED_CHAIN_IDS.has(chainId)) {\n throw new Error(`Unsupported chain ID: ${chainId}`);\n }\n const chain = VIEM_CHAIN_BY_ID.get(chainId);\n if (!chain) {\n throw new Error(`Unsupported chain ID: ${chainId}`);\n }\n return chain;\n}\n\nexport function getChainName(chainId: number): string {\n try {\n return getChainById(chainId).name;\n } catch {\n return `Chain ${chainId}`;\n }\n}\n\nexport function getChainExplorerUrl(chainId: number): string | undefined {\n try {\n return getChainById(chainId).blockExplorers?.default?.url;\n } catch {\n return undefined;\n }\n}\n\nexport function getChainRpcUrl(chainId: number): string | undefined {\n try {\n const chain = getChainById(chainId);\n return chain.rpcUrls?.default?.http?.[0] || chain.rpcUrls?.public?.http?.[0];\n } catch {\n return undefined;\n }\n}\n\nexport function getSupportedTokens(chainId: number): TokenConfig[] {\n return tokensForChain(chainId);\n}\n\nexport function getSupportedTokenSymbols(chainId: number): string[] {\n return getSupportedTokens(chainId).map((token) => token.symbol);\n}\n\nexport function getTokenAddress(symbolOrAddress: string, chainId: number): Address {\n if (isAddressInput(symbolOrAddress)) {\n return symbolOrAddress;\n }\n const match = getSupportedTokens(chainId).find(\n (t) => t.symbol.toUpperCase() === symbolOrAddress.toUpperCase()\n );\n if (!match) {\n throw new Error(`Unsupported token \"${symbolOrAddress}\" on chain ${chainId}`);\n }\n return match.address;\n}\n\nexport function getTokenDecimals(symbolOrAddress: string, chainId: number): number {\n const match = getSupportedTokens(chainId).find(\n (t) =>\n isAddressInput(symbolOrAddress)\n ? t.address.toLowerCase() === symbolOrAddress.toLowerCase()\n : t.symbol.toUpperCase() === symbolOrAddress.toUpperCase()\n );\n if (!match && isAddressInput(symbolOrAddress) && SUPPORTED_CHAIN_IDS.has(chainId)) {\n return 18;\n }\n if (!match) {\n throw new Error(`Unsupported token \"${symbolOrAddress}\" on chain ${chainId}`);\n }\n return match.decimals;\n}\n\nexport function resolveTokenAddress(token: string, chainId: number): Address {\n if (isAddressInput(token)) {\n return token;\n }\n return getTokenAddress(token, chainId);\n}\n\nexport function isTestnet(chainId: number): boolean {\n try {\n return getChainById(chainId).testnet ?? false;\n } catch {\n return false;\n }\n}\n\nexport function getTokenSymbol(tokenAddress: Address, chainId: number): string {\n const token = getSupportedTokens(chainId).find(\n (entry) => entry.address.toLowerCase() === tokenAddress.toLowerCase()\n );\n if (token) {\n return token.symbol;\n }\n if (!SUPPORTED_CHAIN_IDS.has(chainId)) {\n throw new Error(`Unsupported chain ID: ${chainId}`);\n }\n return \"Token\";\n}\n\nexport function isTokenAddressSupported(tokenAddress: Address, chainId: number): boolean {\n return isAddressInput(tokenAddress) && SUPPORTED_CHAIN_IDS.has(chainId);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,20 @@
1
+ // src/verify.ts
2
+ import { keccak256, toBytes } from "viem";
3
+ var ETHEREUM_MESSAGE_PREFIX = "Ethereum Signed Message:\n";
4
+ function hashMessage(message) {
5
+ const messageBytes = toBytes(message);
6
+ const prefixed = ETHEREUM_MESSAGE_PREFIX + messageBytes.length.toString() + message;
7
+ return keccak256(toBytes(prefixed));
8
+ }
9
+ function verifyMessageHash(message, signedHash) {
10
+ if (!signedHash) return false;
11
+ const expectedHash = hashMessage(message);
12
+ return expectedHash.toLowerCase() === signedHash.toLowerCase();
13
+ }
14
+
15
+ export {
16
+ ETHEREUM_MESSAGE_PREFIX,
17
+ hashMessage,
18
+ verifyMessageHash
19
+ };
20
+ //# sourceMappingURL=chunk-IHBVEU33.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/verify.ts"],"sourcesContent":["import { keccak256, toBytes } from \"viem\";\n\n/**\n * The EIP-191 prefix used for personal message signing.\n * This is the standard Ethereum message prefix for `personal_sign`.\n */\nexport const ETHEREUM_MESSAGE_PREFIX = \"\\x19Ethereum Signed Message:\\n\";\n\n/**\n * Hash a message with the EIP-191 Ethereum prefix.\n *\n * This is the same hashing function used by the passkey sign dialog.\n * Use this to verify that the `signedHash` returned from `signMessage()`\n * matches your original message.\n *\n * Format: keccak256(\"\\x19Ethereum Signed Message:\\n\" + len + message)\n *\n * @example\n * ```typescript\n * const message = \"Sign in to MyApp\\nTimestamp: 1234567890\";\n * const result = await client.signMessage({ username: 'alice', message });\n *\n * // Verify the hash matches\n * const expectedHash = hashMessage(message);\n * if (result.signedHash === expectedHash) {\n * console.log('Hash matches - signature is for this message');\n * }\n * ```\n */\nexport function hashMessage(message: string): `0x${string}` {\n const messageBytes = toBytes(message);\n const prefixed = ETHEREUM_MESSAGE_PREFIX + messageBytes.length.toString() + message;\n return keccak256(toBytes(prefixed));\n}\n\n/**\n * Verify that a signedHash matches the expected message.\n *\n * This is a convenience wrapper around `hashMessage()` that returns\n * a boolean. For full cryptographic verification of the P256 signature,\n * use on-chain verification via the WebAuthn.sol contract.\n *\n * @example\n * ```typescript\n * const result = await client.signMessage({ username: 'alice', message });\n *\n * if (result.success && verifyMessageHash(message, result.signedHash)) {\n * // The signature is for this exact message\n * // For full verification, verify the P256 signature on-chain or server-side\n * }\n * ```\n */\nexport function verifyMessageHash(\n message: string,\n signedHash: string | undefined\n): boolean {\n if (!signedHash) return false;\n const expectedHash = hashMessage(message);\n return expectedHash.toLowerCase() === signedHash.toLowerCase();\n}\n"],"mappings":";AAAA,SAAS,WAAW,eAAe;AAM5B,IAAM,0BAA0B;AAuBhC,SAAS,YAAY,SAAgC;AAC1D,QAAM,eAAe,QAAQ,OAAO;AACpC,QAAM,WAAW,0BAA0B,aAAa,OAAO,SAAS,IAAI;AAC5E,SAAO,UAAU,QAAQ,QAAQ,CAAC;AACpC;AAmBO,SAAS,kBACd,SACA,YACS;AACT,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,eAAe,YAAY,OAAO;AACxC,SAAO,aAAa,YAAY,MAAM,WAAW,YAAY;AAC/D;","names":[]}
@@ -0,0 +1,28 @@
1
+ // src/sponsorship-fetch.ts
2
+ async function extractServerReason(response) {
3
+ try {
4
+ const text = await response.clone().text();
5
+ if (!text) return response.statusText;
6
+ try {
7
+ const data = JSON.parse(text);
8
+ const reason = data.error ?? data.message;
9
+ if (typeof reason === "string" && reason.trim()) return reason;
10
+ } catch {
11
+ }
12
+ return text.slice(0, 200);
13
+ } catch {
14
+ return response.statusText;
15
+ }
16
+ }
17
+ async function tokenFetchError(label, response) {
18
+ const reason = await extractServerReason(response);
19
+ const suffix = reason ? `: ${reason}` : "";
20
+ return new Error(
21
+ `Failed to fetch ${label} (${response.status} ${response.statusText})${suffix}`
22
+ );
23
+ }
24
+
25
+ export {
26
+ tokenFetchError
27
+ };
28
+ //# sourceMappingURL=chunk-IIACVHR3.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sponsorship-fetch.ts"],"sourcesContent":["/**\n * Shared helpers for fetching sponsorship tokens (access + extension) from an\n * integrator's backend endpoints.\n *\n * The integrator's token routes return their failure reason in the JSON body\n * (`{ error: \"createSponsorshipSigner: missing env var(s): ...\" }`). Surfacing\n * only the HTTP status — \"Failed to fetch access token (500 ...)\" — hides that\n * reason and turns every misconfiguration into the same opaque dialog error.\n * These helpers read the body and fold the server's message into the thrown\n * Error so the actual cause (missing env var, bad JWK, expired session) is\n * visible at the call site and in the dialog.\n */\n\n/**\n * Pull a human-readable reason out of a failed token response. Tries the JSON\n * `error`/`message` fields first, then falls back to a short text snippet, then\n * to the HTTP status text. Never throws — diagnostics must not mask the original\n * failure with a parse error.\n */\nasync function extractServerReason(response: Response): Promise<string> {\n try {\n // Clone so a JSON parse failure can still fall back to reading text.\n const text = await response.clone().text();\n if (!text) return response.statusText;\n try {\n const data = JSON.parse(text) as { error?: unknown; message?: unknown };\n const reason = data.error ?? data.message;\n if (typeof reason === \"string\" && reason.trim()) return reason;\n } catch {\n // Body wasn't JSON — fall through to the raw text snippet.\n }\n // Cap the snippet so an HTML error page doesn't flood the message.\n return text.slice(0, 200);\n } catch {\n return response.statusText;\n }\n}\n\n/**\n * Build an Error for a failed token fetch that includes the server's reason.\n * `label` names the token kind, e.g. \"access token\" or \"extension token\".\n */\nexport async function tokenFetchError(\n label: string,\n response: Response,\n): Promise<Error> {\n const reason = await extractServerReason(response);\n const suffix = reason ? `: ${reason}` : \"\";\n return new Error(\n `Failed to fetch ${label} (${response.status} ${response.statusText})${suffix}`,\n );\n}\n"],"mappings":";AAmBA,eAAe,oBAAoB,UAAqC;AACtE,MAAI;AAEF,UAAM,OAAO,MAAM,SAAS,MAAM,EAAE,KAAK;AACzC,QAAI,CAAC,KAAM,QAAO,SAAS;AAC3B,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,YAAM,SAAS,KAAK,SAAS,KAAK;AAClC,UAAI,OAAO,WAAW,YAAY,OAAO,KAAK,EAAG,QAAO;AAAA,IAC1D,QAAQ;AAAA,IAER;AAEA,WAAO,KAAK,MAAM,GAAG,GAAG;AAAA,EAC1B,QAAQ;AACN,WAAO,SAAS;AAAA,EAClB;AACF;AAMA,eAAsB,gBACpB,OACA,UACgB;AAChB,QAAM,SAAS,MAAM,oBAAoB,QAAQ;AACjD,QAAM,SAAS,SAAS,KAAK,MAAM,KAAK;AACxC,SAAO,IAAI;AAAA,IACT,mBAAmB,KAAK,KAAK,SAAS,MAAM,IAAI,SAAS,UAAU,IAAI,MAAM;AAAA,EAC/E;AACF;","names":[]}
@@ -0,0 +1,72 @@
1
+ // src/walletClient/utils.ts
2
+ import { encodeAbiParameters, keccak256 } from "viem";
3
+ var P256_N = BigInt(
4
+ "0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"
5
+ );
6
+ var P256_N_DIV_2 = P256_N / 2n;
7
+ var WEBAUTHN_AUTH_TYPE = {
8
+ type: "tuple",
9
+ components: [
10
+ { type: "bytes", name: "authenticatorData" },
11
+ { type: "string", name: "clientDataJSON" },
12
+ { type: "uint256", name: "challengeIndex" },
13
+ { type: "uint256", name: "typeIndex" },
14
+ { type: "uint256", name: "r" },
15
+ { type: "uint256", name: "s" }
16
+ ]
17
+ };
18
+ function encodeWebAuthnSignature(sig) {
19
+ let s = BigInt(sig.s);
20
+ if (s > P256_N_DIV_2) {
21
+ s = P256_N - s;
22
+ }
23
+ return encodeAbiParameters([WEBAUTHN_AUTH_TYPE], [
24
+ {
25
+ authenticatorData: sig.authenticatorData,
26
+ clientDataJSON: sig.clientDataJSON,
27
+ challengeIndex: BigInt(sig.challengeIndex),
28
+ typeIndex: BigInt(sig.typeIndex),
29
+ r: BigInt(sig.r),
30
+ s
31
+ }
32
+ ]);
33
+ }
34
+ function hashCalls(calls) {
35
+ const encoded = encodeAbiParameters(
36
+ [
37
+ {
38
+ type: "tuple[]",
39
+ components: [
40
+ { type: "address", name: "to" },
41
+ { type: "bytes", name: "data" },
42
+ { type: "uint256", name: "value" }
43
+ ]
44
+ }
45
+ ],
46
+ [
47
+ calls.map((c) => ({
48
+ to: c.to,
49
+ data: c.data || "0x",
50
+ value: c.value || 0n
51
+ }))
52
+ ]
53
+ );
54
+ return keccak256(encoded);
55
+ }
56
+ function buildTransactionReview(calls) {
57
+ return {
58
+ actions: calls.map((call, i) => ({
59
+ type: "custom",
60
+ label: call.label || `Contract Call ${i + 1}`,
61
+ sublabel: call.sublabel || `To: ${call.to.slice(0, 10)}...${call.to.slice(-8)}`,
62
+ amount: call.value ? `${call.value} wei` : void 0
63
+ }))
64
+ };
65
+ }
66
+
67
+ export {
68
+ encodeWebAuthnSignature,
69
+ hashCalls,
70
+ buildTransactionReview
71
+ };
72
+ //# sourceMappingURL=chunk-N6KE5CII.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/walletClient/utils.ts"],"sourcesContent":["import { encodeAbiParameters, keccak256 } from 'viem';\nimport type { Hex } from 'viem';\nimport type { WebAuthnSignature } from '../types';\nimport type { TransactionCall } from './types';\n\n/**\n * P-256 curve order (n)\n * Used for signature malleability normalization\n */\nconst P256_N = BigInt(\n '0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551'\n);\nconst P256_N_DIV_2 = P256_N / 2n;\n\n/**\n * WebAuthnAuth struct type for ABI encoding\n */\nconst WEBAUTHN_AUTH_TYPE = {\n type: 'tuple',\n components: [\n { type: 'bytes', name: 'authenticatorData' },\n { type: 'string', name: 'clientDataJSON' },\n { type: 'uint256', name: 'challengeIndex' },\n { type: 'uint256', name: 'typeIndex' },\n { type: 'uint256', name: 'r' },\n { type: 'uint256', name: 's' },\n ],\n} as const;\n\n/**\n * Encode a WebAuthn signature for ERC-1271 verification on-chain\n *\n * @param sig - The WebAuthn signature from the passkey\n * @returns ABI-encoded signature bytes\n */\nexport function encodeWebAuthnSignature(sig: WebAuthnSignature): Hex {\n // Normalize s to prevent signature malleability\n let s = BigInt(sig.s);\n if (s > P256_N_DIV_2) {\n s = P256_N - s;\n }\n\n return encodeAbiParameters([WEBAUTHN_AUTH_TYPE], [\n {\n authenticatorData: sig.authenticatorData as Hex,\n clientDataJSON: sig.clientDataJSON,\n challengeIndex: BigInt(sig.challengeIndex),\n typeIndex: BigInt(sig.typeIndex),\n r: BigInt(sig.r),\n s,\n },\n ]);\n}\n\n/**\n * Hash an array of transaction calls for signing\n *\n * @param calls - Array of transaction calls\n * @returns keccak256 hash of the encoded calls\n */\nexport function hashCalls(calls: TransactionCall[]): Hex {\n const encoded = encodeAbiParameters(\n [\n {\n type: 'tuple[]',\n components: [\n { type: 'address', name: 'to' },\n { type: 'bytes', name: 'data' },\n { type: 'uint256', name: 'value' },\n ],\n },\n ],\n [\n calls.map((c) => ({\n to: c.to,\n data: c.data || '0x',\n value: c.value || 0n,\n })),\n ]\n );\n return keccak256(encoded);\n}\n\n/**\n * Build transaction review display data from calls\n *\n * @param calls - Array of transaction calls\n * @returns TransactionDetails for the signing modal\n */\nexport function buildTransactionReview(calls: TransactionCall[]) {\n return {\n actions: calls.map((call, i) => ({\n type: 'custom' as const,\n label: call.label || `Contract Call ${i + 1}`,\n sublabel: call.sublabel || `To: ${call.to.slice(0, 10)}...${call.to.slice(-8)}`,\n amount: call.value ? `${call.value} wei` : undefined,\n })),\n };\n}\n"],"mappings":";AAAA,SAAS,qBAAqB,iBAAiB;AAS/C,IAAM,SAAS;AAAA,EACb;AACF;AACA,IAAM,eAAe,SAAS;AAK9B,IAAM,qBAAqB;AAAA,EACzB,MAAM;AAAA,EACN,YAAY;AAAA,IACV,EAAE,MAAM,SAAS,MAAM,oBAAoB;AAAA,IAC3C,EAAE,MAAM,UAAU,MAAM,iBAAiB;AAAA,IACzC,EAAE,MAAM,WAAW,MAAM,iBAAiB;AAAA,IAC1C,EAAE,MAAM,WAAW,MAAM,YAAY;AAAA,IACrC,EAAE,MAAM,WAAW,MAAM,IAAI;AAAA,IAC7B,EAAE,MAAM,WAAW,MAAM,IAAI;AAAA,EAC/B;AACF;AAQO,SAAS,wBAAwB,KAA6B;AAEnE,MAAI,IAAI,OAAO,IAAI,CAAC;AACpB,MAAI,IAAI,cAAc;AACpB,QAAI,SAAS;AAAA,EACf;AAEA,SAAO,oBAAoB,CAAC,kBAAkB,GAAG;AAAA,IAC/C;AAAA,MACE,mBAAmB,IAAI;AAAA,MACvB,gBAAgB,IAAI;AAAA,MACpB,gBAAgB,OAAO,IAAI,cAAc;AAAA,MACzC,WAAW,OAAO,IAAI,SAAS;AAAA,MAC/B,GAAG,OAAO,IAAI,CAAC;AAAA,MACf;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAQO,SAAS,UAAU,OAA+B;AACvD,QAAM,UAAU;AAAA,IACd;AAAA,MACE;AAAA,QACE,MAAM;AAAA,QACN,YAAY;AAAA,UACV,EAAE,MAAM,WAAW,MAAM,KAAK;AAAA,UAC9B,EAAE,MAAM,SAAS,MAAM,OAAO;AAAA,UAC9B,EAAE,MAAM,WAAW,MAAM,QAAQ;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM,IAAI,CAAC,OAAO;AAAA,QAChB,IAAI,EAAE;AAAA,QACN,MAAM,EAAE,QAAQ;AAAA,QAChB,OAAO,EAAE,SAAS;AAAA,MACpB,EAAE;AAAA,IACJ;AAAA,EACF;AACA,SAAO,UAAU,OAAO;AAC1B;AAQO,SAAS,uBAAuB,OAA0B;AAC/D,SAAO;AAAA,IACL,SAAS,MAAM,IAAI,CAAC,MAAM,OAAO;AAAA,MAC/B,MAAM;AAAA,MACN,OAAO,KAAK,SAAS,iBAAiB,IAAI,CAAC;AAAA,MAC3C,UAAU,KAAK,YAAY,OAAO,KAAK,GAAG,MAAM,GAAG,EAAE,CAAC,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC;AAAA,MAC7E,QAAQ,KAAK,QAAQ,GAAG,KAAK,KAAK,SAAS;AAAA,IAC7C,EAAE;AAAA,EACJ;AACF;","names":[]}