@absol-labs/agent 0.6.0 → 0.7.0
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 +21 -5
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/wallet/autonomous-wallet-broker.d.ts +97 -0
- package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -0
- package/dist/wallet/autonomous-wallet-broker.js +468 -0
- package/dist/wallet/autonomous-wallet-broker.js.map +1 -0
- package/dist/wallet/autonomous-wallet-protocol.d.ts +56 -0
- package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -0
- package/dist/wallet/autonomous-wallet-protocol.js +15 -0
- package/dist/wallet/autonomous-wallet-protocol.js.map +1 -0
- package/dist/wallet/autonomous-wallet.d.ts +106 -0
- package/dist/wallet/autonomous-wallet.d.ts.map +1 -0
- package/dist/wallet/autonomous-wallet.js +494 -0
- package/dist/wallet/autonomous-wallet.js.map +1 -0
- package/dist/wallet/privy-broker-server-entry.d.ts +2 -0
- package/dist/wallet/privy-broker-server-entry.d.ts.map +1 -0
- package/dist/wallet/privy-broker-server-entry.js +8 -0
- package/dist/wallet/privy-broker-server-entry.js.map +1 -0
- package/dist/wallet/privy-broker-server.d.ts +29 -0
- package/dist/wallet/privy-broker-server.d.ts.map +1 -0
- package/dist/wallet/privy-broker-server.js +480 -0
- package/dist/wallet/privy-broker-server.js.map +1 -0
- package/dist/wallet/privy-session-broker.d.ts +109 -0
- package/dist/wallet/privy-session-broker.d.ts.map +1 -0
- package/dist/wallet/privy-session-broker.js +372 -0
- package/dist/wallet/privy-session-broker.js.map +1 -0
- package/dist/wallet/privy-session-provider.d.ts +21 -0
- package/dist/wallet/privy-session-provider.d.ts.map +1 -0
- package/dist/wallet/privy-session-provider.js +94 -0
- package/dist/wallet/privy-session-provider.js.map +1 -0
- package/dist/wallet/provider.d.ts +51 -2
- package/dist/wallet/provider.d.ts.map +1 -1
- package/dist/wallet/provider.js +138 -1
- package/dist/wallet/provider.js.map +1 -1
- package/package.json +3 -1
- package/src/index.ts +73 -0
- package/src/wallet/autonomous-wallet-broker.ts +764 -0
- package/src/wallet/autonomous-wallet-protocol.ts +71 -0
- package/src/wallet/autonomous-wallet.ts +779 -0
- package/src/wallet/privy-broker-server-entry.ts +9 -0
- package/src/wallet/privy-broker-server.ts +573 -0
- package/src/wallet/privy-session-broker.ts +634 -0
- package/src/wallet/privy-session-provider.ts +129 -0
- package/src/wallet/provider.ts +260 -3
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { getAddress, type Address, type Hex } from "viem";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import type { PrivyEip1193Provider } from "./provider.js";
|
|
5
|
+
|
|
6
|
+
const jsonRpcResponseSchema = z.object({
|
|
7
|
+
jsonrpc: z.literal("2.0"),
|
|
8
|
+
id: z.union([z.string(), z.number(), z.null()]),
|
|
9
|
+
result: z.unknown().optional(),
|
|
10
|
+
error: z
|
|
11
|
+
.object({
|
|
12
|
+
code: z.number(),
|
|
13
|
+
message: z.string(),
|
|
14
|
+
data: z.unknown().optional(),
|
|
15
|
+
})
|
|
16
|
+
.optional(),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export interface PrivySessionProviderConfig {
|
|
20
|
+
readonly brokerUrl: string;
|
|
21
|
+
readonly sessionToken: string;
|
|
22
|
+
readonly address: Address;
|
|
23
|
+
readonly rpcUrl?: string;
|
|
24
|
+
readonly fetchImpl?: typeof fetch;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class PrivySessionProviderError extends Error {
|
|
28
|
+
constructor(message: string, options?: { readonly cause?: unknown }) {
|
|
29
|
+
super(message, options);
|
|
30
|
+
this.name = "PrivySessionProviderError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* EIP-1193 provider for a policy-bound Metrik/Privy headless session.
|
|
36
|
+
* Signing and writes go only to the broker; public read RPCs go directly to
|
|
37
|
+
* Base Sepolia and never receive the session token.
|
|
38
|
+
*/
|
|
39
|
+
export function createPrivySessionProvider(
|
|
40
|
+
config: PrivySessionProviderConfig,
|
|
41
|
+
): PrivyEip1193Provider {
|
|
42
|
+
const fetchImpl = config.fetchImpl ?? fetch;
|
|
43
|
+
const address = getAddress(config.address);
|
|
44
|
+
const brokerUrl = new URL("/v1/rpc", requireHttpsBase(config.brokerUrl));
|
|
45
|
+
const rpcUrl = config.rpcUrl ?? "https://base-sepolia-rpc.publicnode.com";
|
|
46
|
+
let rpcId = 0;
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
async request({ method, params = [] }) {
|
|
50
|
+
if (method === "eth_chainId") return "0x14a34";
|
|
51
|
+
if (method === "eth_accounts" || method === "eth_requestAccounts") {
|
|
52
|
+
return [address];
|
|
53
|
+
}
|
|
54
|
+
if (
|
|
55
|
+
method === "eth_sendTransaction" ||
|
|
56
|
+
method === "eth_signTypedData_v4"
|
|
57
|
+
) {
|
|
58
|
+
const response = await fetchImpl(brokerUrl, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: {
|
|
61
|
+
authorization: `Bearer ${config.sessionToken}`,
|
|
62
|
+
"content-type": "application/json",
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify({ method, params }, bigintJson),
|
|
65
|
+
}).catch((error: unknown) => {
|
|
66
|
+
throw new PrivySessionProviderError("session broker is unreachable", {
|
|
67
|
+
cause: error,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
const body = (await response.json().catch(() => null)) as {
|
|
71
|
+
result?: Hex;
|
|
72
|
+
error?: string;
|
|
73
|
+
} | null;
|
|
74
|
+
if (!response.ok || body?.result === undefined) {
|
|
75
|
+
throw new PrivySessionProviderError(
|
|
76
|
+
body?.error ??
|
|
77
|
+
`session broker rejected ${method} (${response.status})`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return body.result;
|
|
81
|
+
}
|
|
82
|
+
if (
|
|
83
|
+
method === "personal_sign" ||
|
|
84
|
+
method === "eth_sign" ||
|
|
85
|
+
method === "eth_signTransaction" ||
|
|
86
|
+
method === "wallet_sendCalls"
|
|
87
|
+
) {
|
|
88
|
+
throw new PrivySessionProviderError(
|
|
89
|
+
`${method} is outside the Metrik agent-session policy`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const id = ++rpcId;
|
|
94
|
+
const response = await fetchImpl(rpcUrl, {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: { "content-type": "application/json" },
|
|
97
|
+
body: JSON.stringify(
|
|
98
|
+
{ jsonrpc: "2.0", id, method, params },
|
|
99
|
+
bigintJson,
|
|
100
|
+
),
|
|
101
|
+
}).catch((error: unknown) => {
|
|
102
|
+
throw new PrivySessionProviderError("Base Sepolia RPC is unreachable", {
|
|
103
|
+
cause: error,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
const body = jsonRpcResponseSchema.parse(await response.json());
|
|
107
|
+
if (!response.ok || body.error !== undefined) {
|
|
108
|
+
throw new PrivySessionProviderError(
|
|
109
|
+
body.error?.message ?? `Base Sepolia RPC rejected ${method}`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return body.result;
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function requireHttpsBase(value: string): URL {
|
|
118
|
+
const url = new URL(value);
|
|
119
|
+
if (url.protocol !== "https:" && url.hostname !== "localhost") {
|
|
120
|
+
throw new PrivySessionProviderError(
|
|
121
|
+
"session broker URL must use HTTPS (except localhost development)",
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return url;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function bigintJson(_key: string, value: unknown) {
|
|
128
|
+
return typeof value === "bigint" ? `0x${value.toString(16)}` : value;
|
|
129
|
+
}
|
package/src/wallet/provider.ts
CHANGED
|
@@ -4,8 +4,19 @@ import {
|
|
|
4
4
|
type EvmServerAccount,
|
|
5
5
|
type EvmSmartAccount,
|
|
6
6
|
} from "@coinbase/cdp-sdk";
|
|
7
|
-
import
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
custom,
|
|
9
|
+
toHex,
|
|
10
|
+
serializeTypedData,
|
|
11
|
+
type Account,
|
|
12
|
+
type Address,
|
|
13
|
+
type Hex,
|
|
14
|
+
type SignableMessage,
|
|
15
|
+
type TypedData,
|
|
16
|
+
type TypedDataDefinition,
|
|
17
|
+
isAddress,
|
|
18
|
+
} from "viem";
|
|
19
|
+
import { parseAccount, privateKeyToAccount, toAccount } from "viem/accounts";
|
|
9
20
|
import { z } from "zod";
|
|
10
21
|
|
|
11
22
|
import {
|
|
@@ -38,6 +49,51 @@ export class AgentWalletConfigError extends Error {
|
|
|
38
49
|
}
|
|
39
50
|
}
|
|
40
51
|
|
|
52
|
+
/** A deliberately small EIP-1193 seam implemented by an authenticated Privy session. */
|
|
53
|
+
export interface PrivyEip1193Provider {
|
|
54
|
+
request(args: {
|
|
55
|
+
readonly method: string;
|
|
56
|
+
readonly params?: readonly unknown[];
|
|
57
|
+
}): Promise<unknown>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Configuration for a user-owned Privy embedded EOA.
|
|
62
|
+
*
|
|
63
|
+
* The browser/site owns authentication and obtains the provider from Privy's
|
|
64
|
+
* authenticated wallet. This package does not import Privy's UI SDK, accept a
|
|
65
|
+
* secret, persist a wallet, or create a wallet from an app id alone.
|
|
66
|
+
*/
|
|
67
|
+
export interface PrivyEmbeddedWalletConfig {
|
|
68
|
+
/** Public Privy app id; never an app secret. */
|
|
69
|
+
readonly appId: string;
|
|
70
|
+
/** Address returned by the authenticated user wallet. */
|
|
71
|
+
readonly address: Address;
|
|
72
|
+
/** Authenticated Privy wallet provider (EIP-1193). */
|
|
73
|
+
readonly provider: PrivyEip1193Provider;
|
|
74
|
+
/** Metrik currently supports Base Sepolia only. */
|
|
75
|
+
readonly chainId?: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class PrivyEmbeddedWalletError extends Error {
|
|
79
|
+
readonly code:
|
|
80
|
+
| "invalid-config"
|
|
81
|
+
| "provider-error"
|
|
82
|
+
| "chain-mismatch"
|
|
83
|
+
| "account-mismatch"
|
|
84
|
+
| "invalid-signature";
|
|
85
|
+
|
|
86
|
+
constructor(
|
|
87
|
+
code: PrivyEmbeddedWalletError["code"],
|
|
88
|
+
message: string,
|
|
89
|
+
options?: { readonly cause?: unknown },
|
|
90
|
+
) {
|
|
91
|
+
super(message, options);
|
|
92
|
+
this.name = "PrivyEmbeddedWalletError";
|
|
93
|
+
this.code = code;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
41
97
|
export interface CdpWalletConfig {
|
|
42
98
|
readonly apiKeyId?: string;
|
|
43
99
|
readonly apiKeySecret?: string;
|
|
@@ -53,13 +109,20 @@ export type AgentWalletInput =
|
|
|
53
109
|
}
|
|
54
110
|
| {
|
|
55
111
|
readonly cdp: CdpWalletConfig;
|
|
112
|
+
}
|
|
113
|
+
| {
|
|
114
|
+
readonly privy: PrivyEmbeddedWalletConfig;
|
|
56
115
|
};
|
|
57
116
|
|
|
58
117
|
export interface ResolvedAgentWallet {
|
|
59
|
-
readonly source: "injected" | "cdp";
|
|
118
|
+
readonly source: "injected" | "cdp" | "privy";
|
|
60
119
|
readonly account: Account;
|
|
61
120
|
readonly cdpOwner?: EvmServerAccount;
|
|
62
121
|
readonly smartAccount?: EvmSmartAccount;
|
|
122
|
+
readonly privy?: {
|
|
123
|
+
readonly appId: string;
|
|
124
|
+
readonly chainId: number;
|
|
125
|
+
};
|
|
63
126
|
}
|
|
64
127
|
|
|
65
128
|
export interface CdpClientLike {
|
|
@@ -98,6 +161,16 @@ export async function resolveAgentWallet(
|
|
|
98
161
|
};
|
|
99
162
|
}
|
|
100
163
|
|
|
164
|
+
if ("privy" in input) {
|
|
165
|
+
const chainId = input.privy.chainId ?? BASE_SEPOLIA_CHAIN_ID;
|
|
166
|
+
const account = await createPrivyEmbeddedAccount(input.privy);
|
|
167
|
+
return {
|
|
168
|
+
source: "privy",
|
|
169
|
+
account,
|
|
170
|
+
privy: { appId: input.privy.appId, chainId },
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
101
174
|
const cdpClient = (options.createCdpClient ?? defaultCdpClientFactory)(
|
|
102
175
|
input.cdp,
|
|
103
176
|
);
|
|
@@ -119,6 +192,62 @@ export async function resolveAgentWallet(
|
|
|
119
192
|
};
|
|
120
193
|
}
|
|
121
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Adapts an authenticated Privy EIP-1193 wallet to a viem JSON-RPC Account.
|
|
197
|
+
* It deliberately remains a JSON-RPC account so viem sends transactions via
|
|
198
|
+
* Privy's documented `eth_sendTransaction` boundary instead of assuming that
|
|
199
|
+
* the embedded provider exposes raw transaction signing. Every signing
|
|
200
|
+
* operation rechecks chain and account identity so a
|
|
201
|
+
* browser account/network switch cannot silently redirect a spend.
|
|
202
|
+
*
|
|
203
|
+
* This is an EOA adapter only. It does not implement EIP-1271, smart accounts,
|
|
204
|
+
* session-key issuance, or gas sponsorship; those require provider/site or
|
|
205
|
+
* backend integration and must be wired explicitly by the caller.
|
|
206
|
+
*/
|
|
207
|
+
export async function createPrivyEmbeddedAccount(
|
|
208
|
+
config: PrivyEmbeddedWalletConfig,
|
|
209
|
+
): Promise<Account> {
|
|
210
|
+
validatePrivyConfig(config);
|
|
211
|
+
const chainId = config.chainId ?? BASE_SEPOLIA_CHAIN_ID;
|
|
212
|
+
await assertPrivySession(config, chainId);
|
|
213
|
+
|
|
214
|
+
const account = {
|
|
215
|
+
...parseAccount(config.address),
|
|
216
|
+
signMessage: async ({ message }: { message: SignableMessage }) => {
|
|
217
|
+
await assertPrivySession(config, chainId);
|
|
218
|
+
const raw =
|
|
219
|
+
typeof message === "string"
|
|
220
|
+
? toHex(message)
|
|
221
|
+
: message.raw instanceof Uint8Array
|
|
222
|
+
? toHex(message.raw)
|
|
223
|
+
: message.raw;
|
|
224
|
+
return requireSignature(
|
|
225
|
+
await requestPrivy(config.provider, "personal_sign", [
|
|
226
|
+
raw,
|
|
227
|
+
config.address,
|
|
228
|
+
]),
|
|
229
|
+
);
|
|
230
|
+
},
|
|
231
|
+
signTypedData: async <
|
|
232
|
+
const typedData extends TypedData | Record<string, unknown>,
|
|
233
|
+
primaryType extends keyof typedData | "EIP712Domain" = keyof typedData,
|
|
234
|
+
>(
|
|
235
|
+
parameters: TypedDataDefinition<typedData, primaryType>,
|
|
236
|
+
) => {
|
|
237
|
+
await assertPrivySession(config, chainId);
|
|
238
|
+
const serialized = serializeTypedData(parameters);
|
|
239
|
+
return requireSignature(
|
|
240
|
+
await requestPrivy(config.provider, "eth_signTypedData_v4", [
|
|
241
|
+
config.address,
|
|
242
|
+
serialized,
|
|
243
|
+
]),
|
|
244
|
+
);
|
|
245
|
+
},
|
|
246
|
+
} as Account;
|
|
247
|
+
|
|
248
|
+
return account;
|
|
249
|
+
}
|
|
250
|
+
|
|
122
251
|
export async function createWalletBackedAgentClient(
|
|
123
252
|
sdkConfig: StreamProofClientConfig,
|
|
124
253
|
walletInput: AgentWalletInput,
|
|
@@ -129,6 +258,9 @@ export async function createWalletBackedAgentClient(
|
|
|
129
258
|
{
|
|
130
259
|
...sdkConfig,
|
|
131
260
|
account: wallet.account,
|
|
261
|
+
...("privy" in walletInput
|
|
262
|
+
? { transport: custom(createCheckedPrivyProvider(walletInput.privy)) }
|
|
263
|
+
: {}),
|
|
132
264
|
},
|
|
133
265
|
{
|
|
134
266
|
...(options.createSdkClient === undefined
|
|
@@ -195,3 +327,128 @@ function defaultCdpClientFactory(config: CdpWalletConfig): CdpClientLike {
|
|
|
195
327
|
: { walletSecret: config.walletSecret }),
|
|
196
328
|
});
|
|
197
329
|
}
|
|
330
|
+
|
|
331
|
+
const BASE_SEPOLIA_CHAIN_ID = 84532;
|
|
332
|
+
|
|
333
|
+
function validatePrivyConfig(config: PrivyEmbeddedWalletConfig): void {
|
|
334
|
+
if (config.appId.trim() === "") {
|
|
335
|
+
throw new PrivyEmbeddedWalletError(
|
|
336
|
+
"invalid-config",
|
|
337
|
+
"Privy public app id is required; an app secret is never accepted by this adapter",
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
if (!isAddress(config.address)) {
|
|
341
|
+
throw new PrivyEmbeddedWalletError(
|
|
342
|
+
"invalid-config",
|
|
343
|
+
"Privy wallet address is invalid",
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
if (
|
|
347
|
+
config.chainId !== undefined &&
|
|
348
|
+
config.chainId !== BASE_SEPOLIA_CHAIN_ID
|
|
349
|
+
) {
|
|
350
|
+
throw new PrivyEmbeddedWalletError(
|
|
351
|
+
"invalid-config",
|
|
352
|
+
`Metrik Privy wallets support Base Sepolia chain ID ${BASE_SEPOLIA_CHAIN_ID} only`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async function assertPrivySession(
|
|
358
|
+
config: PrivyEmbeddedWalletConfig,
|
|
359
|
+
expectedChainId: number,
|
|
360
|
+
): Promise<void> {
|
|
361
|
+
let chainResult: unknown;
|
|
362
|
+
let accountsResult: unknown;
|
|
363
|
+
try {
|
|
364
|
+
[chainResult, accountsResult] = await Promise.all([
|
|
365
|
+
config.provider.request({ method: "eth_chainId" }),
|
|
366
|
+
config.provider.request({ method: "eth_accounts" }),
|
|
367
|
+
]);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
throw new PrivyEmbeddedWalletError(
|
|
370
|
+
"provider-error",
|
|
371
|
+
"Privy wallet provider rejected the session check",
|
|
372
|
+
{ cause: error },
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const chainId = parseChainId(chainResult);
|
|
377
|
+
if (chainId !== expectedChainId) {
|
|
378
|
+
throw new PrivyEmbeddedWalletError(
|
|
379
|
+
"chain-mismatch",
|
|
380
|
+
`Privy wallet chain ID ${chainId} is not Base Sepolia (${expectedChainId})`,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
if (
|
|
384
|
+
!Array.isArray(accountsResult) ||
|
|
385
|
+
accountsResult.length === 0 ||
|
|
386
|
+
typeof accountsResult[0] !== "string" ||
|
|
387
|
+
accountsResult[0].toLowerCase() !== config.address.toLowerCase()
|
|
388
|
+
) {
|
|
389
|
+
throw new PrivyEmbeddedWalletError(
|
|
390
|
+
"account-mismatch",
|
|
391
|
+
"Privy provider account does not match configured address",
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async function requestPrivy(
|
|
397
|
+
provider: PrivyEip1193Provider,
|
|
398
|
+
method: string,
|
|
399
|
+
params: readonly unknown[],
|
|
400
|
+
): Promise<unknown> {
|
|
401
|
+
try {
|
|
402
|
+
return await provider.request({ method, params });
|
|
403
|
+
} catch (error) {
|
|
404
|
+
throw new PrivyEmbeddedWalletError(
|
|
405
|
+
"provider-error",
|
|
406
|
+
`Privy wallet provider rejected ${method}`,
|
|
407
|
+
{ cause: error },
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function createCheckedPrivyProvider(
|
|
413
|
+
config: PrivyEmbeddedWalletConfig,
|
|
414
|
+
): PrivyEip1193Provider {
|
|
415
|
+
const expectedChainId = config.chainId ?? BASE_SEPOLIA_CHAIN_ID;
|
|
416
|
+
return {
|
|
417
|
+
request: async (args) => {
|
|
418
|
+
if (args.method === "eth_sendTransaction") {
|
|
419
|
+
await assertPrivySession(config, expectedChainId);
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
return await config.provider.request(args);
|
|
423
|
+
} catch (error) {
|
|
424
|
+
throw new PrivyEmbeddedWalletError(
|
|
425
|
+
"provider-error",
|
|
426
|
+
`Privy wallet provider rejected ${args.method}`,
|
|
427
|
+
{ cause: error },
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function parseChainId(value: unknown): number {
|
|
435
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value > 0)
|
|
436
|
+
return value;
|
|
437
|
+
if (typeof value === "string" && /^0x[0-9a-fA-F]+$/.test(value)) {
|
|
438
|
+
const parsed = Number.parseInt(value.slice(2), 16);
|
|
439
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) return parsed;
|
|
440
|
+
}
|
|
441
|
+
throw new PrivyEmbeddedWalletError(
|
|
442
|
+
"provider-error",
|
|
443
|
+
"Privy wallet provider returned an invalid chain ID",
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function requireSignature(value: unknown): Hex {
|
|
448
|
+
if (typeof value === "string" && /^0x[0-9a-fA-F]+$/.test(value))
|
|
449
|
+
return value as Hex;
|
|
450
|
+
throw new PrivyEmbeddedWalletError(
|
|
451
|
+
"invalid-signature",
|
|
452
|
+
"Privy wallet provider returned a non-hex signature",
|
|
453
|
+
);
|
|
454
|
+
}
|