@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.
- package/README.md +171 -12
- package/dist/chunk-GUAI55LL.mjs +179 -0
- package/dist/chunk-GUAI55LL.mjs.map +1 -0
- package/dist/chunk-IHBVEU33.mjs +20 -0
- package/dist/chunk-IHBVEU33.mjs.map +1 -0
- package/dist/chunk-IIACVHR3.mjs +28 -0
- package/dist/chunk-IIACVHR3.mjs.map +1 -0
- package/dist/chunk-N6KE5CII.mjs +72 -0
- package/dist/chunk-N6KE5CII.mjs.map +1 -0
- package/dist/{chunk-SXISYG2P.mjs → chunk-THKG3FAG.mjs} +137 -255
- package/dist/chunk-THKG3FAG.mjs.map +1 -0
- package/dist/{client-BrMrhetG.d.mts → client-B_CzDa_I.d.ts} +360 -857
- package/dist/{client-BrMrhetG.d.ts → client-F4DnFM8d.d.mts} +360 -857
- package/dist/headless.d.mts +109 -0
- package/dist/headless.d.ts +109 -0
- package/dist/headless.js +467 -0
- package/dist/headless.js.map +1 -0
- package/dist/headless.mjs +382 -0
- package/dist/headless.mjs.map +1 -0
- package/dist/index.d.mts +96 -144
- package/dist/index.d.ts +96 -144
- package/dist/index.js +3212 -756
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3010 -705
- package/dist/index.mjs.map +1 -1
- package/dist/{provider-CDl9wYEc.d.mts → provider-Cd7Ip5L-.d.ts} +6 -5
- package/dist/{provider-Dgv533YQ.d.ts → provider-IvYXPMpk.d.mts} +6 -5
- package/dist/react.d.mts +42 -2
- package/dist/react.d.ts +42 -2
- package/dist/react.js +92 -2
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +66 -1
- package/dist/react.mjs.map +1 -1
- package/dist/server.d.mts +118 -0
- package/dist/server.d.ts +118 -0
- package/dist/server.js +356 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +282 -0
- package/dist/server.mjs.map +1 -0
- package/dist/types-U_dwxbtS.d.mts +1488 -0
- package/dist/types-U_dwxbtS.d.ts +1488 -0
- package/dist/verify-BLgZzwmJ.d.ts +150 -0
- package/dist/verify-C8-a5c3K.d.mts +150 -0
- package/dist/wagmi.d.mts +5 -2
- package/dist/wagmi.d.ts +5 -2
- package/dist/wagmi.js +138 -43
- package/dist/wagmi.js.map +1 -1
- package/dist/wagmi.mjs +3 -1
- package/dist/wagmi.mjs.map +1 -1
- package/package.json +15 -2
- 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` |
|
|
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
|
|
87
|
+
### Server-Side JWT Sponsorship
|
|
88
88
|
|
|
89
89
|
```typescript
|
|
90
|
-
import {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
- `
|
|
122
|
-
- `
|
|
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":[]}
|