@spectratools/tx-shared 0.4.3 → 0.5.2
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 +128 -32
- package/dist/chunk-HFRJBEDT.js +1144 -0
- package/dist/errors.d.ts +1 -1
- package/dist/execute-tx-DO8p_9dP.d.ts +160 -0
- package/dist/execute-tx.d.ts +4 -63
- package/dist/execute-tx.js +1 -1
- package/dist/index.d.ts +36 -60
- package/dist/index.js +24 -596
- package/package.json +1 -1
- package/dist/chunk-4XI6TBKX.js +0 -130
package/dist/index.js
CHANGED
|
@@ -3,8 +3,22 @@ import {
|
|
|
3
3
|
createAbstractClient
|
|
4
4
|
} from "./chunk-P4ACSL6N.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
attachPrivyPolicyContext,
|
|
7
|
+
createPrivyAccount,
|
|
8
|
+
createPrivyAuthorizationPayload,
|
|
9
|
+
createPrivyClient,
|
|
10
|
+
createPrivySigner,
|
|
11
|
+
executeTx,
|
|
12
|
+
fetchPrivyPolicyVisibility,
|
|
13
|
+
generatePrivyAuthorizationSignature,
|
|
14
|
+
getPrivyPolicyContext,
|
|
15
|
+
normalizePrivyApiUrl,
|
|
16
|
+
normalizePrivyPolicy,
|
|
17
|
+
parsePrivyAuthorizationKey,
|
|
18
|
+
preflightPrivyTransactionPolicy,
|
|
19
|
+
serializePrivyAuthorizationPayload,
|
|
20
|
+
toPrivyPolicyViolationError
|
|
21
|
+
} from "./chunk-HFRJBEDT.js";
|
|
8
22
|
import {
|
|
9
23
|
TxError,
|
|
10
24
|
toTxError
|
|
@@ -57,599 +71,6 @@ function createKeystoreSigner(options) {
|
|
|
57
71
|
return { account, address: account.address, provider: "keystore" };
|
|
58
72
|
}
|
|
59
73
|
|
|
60
|
-
// src/signers/privy-account.ts
|
|
61
|
-
import { isAddress } from "viem";
|
|
62
|
-
var DEFAULT_CHAIN_ID = 2741;
|
|
63
|
-
var HASH_REGEX = /^0x[a-fA-F0-9]{64}$/;
|
|
64
|
-
async function createPrivyAccount(options) {
|
|
65
|
-
const wallet = await options.client.getWallet();
|
|
66
|
-
if (typeof wallet.address !== "string" || !isAddress(wallet.address)) {
|
|
67
|
-
throw new TxError(
|
|
68
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
69
|
-
"Privy get wallet request failed: wallet address is missing or invalid"
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
const address = wallet.address;
|
|
73
|
-
const defaultChainId = options.chainId ?? DEFAULT_CHAIN_ID;
|
|
74
|
-
return {
|
|
75
|
-
address,
|
|
76
|
-
type: "json-rpc",
|
|
77
|
-
async sendTransaction(request) {
|
|
78
|
-
const chainId = normalizeChainId(request.chainId ?? defaultChainId);
|
|
79
|
-
const rpcRequest = createSendTransactionRpcRequest(address, chainId, request);
|
|
80
|
-
const response = await options.client.createRpcIntent(rpcRequest);
|
|
81
|
-
return parsePrivyTransactionHash(response);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
function createSendTransactionRpcRequest(from, chainId, request) {
|
|
86
|
-
const transaction = {
|
|
87
|
-
from
|
|
88
|
-
};
|
|
89
|
-
if (request.to !== void 0) {
|
|
90
|
-
transaction.to = request.to;
|
|
91
|
-
}
|
|
92
|
-
if (request.data !== void 0) {
|
|
93
|
-
transaction.data = request.data;
|
|
94
|
-
}
|
|
95
|
-
if (request.value !== void 0) {
|
|
96
|
-
transaction.value = normalizeNumberish(request.value, "value");
|
|
97
|
-
}
|
|
98
|
-
if (request.nonce !== void 0) {
|
|
99
|
-
transaction.nonce = normalizeNumberish(request.nonce, "nonce");
|
|
100
|
-
}
|
|
101
|
-
if (request.gas !== void 0) {
|
|
102
|
-
transaction.gas_limit = normalizeNumberish(request.gas, "gas");
|
|
103
|
-
}
|
|
104
|
-
if (request.gasPrice !== void 0) {
|
|
105
|
-
transaction.gas_price = normalizeNumberish(request.gasPrice, "gasPrice");
|
|
106
|
-
}
|
|
107
|
-
if (request.maxFeePerGas !== void 0) {
|
|
108
|
-
transaction.max_fee_per_gas = normalizeNumberish(request.maxFeePerGas, "maxFeePerGas");
|
|
109
|
-
}
|
|
110
|
-
if (request.maxPriorityFeePerGas !== void 0) {
|
|
111
|
-
transaction.max_priority_fee_per_gas = normalizeNumberish(
|
|
112
|
-
request.maxPriorityFeePerGas,
|
|
113
|
-
"maxPriorityFeePerGas"
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
if (request.type !== void 0) {
|
|
117
|
-
transaction.type = request.type;
|
|
118
|
-
}
|
|
119
|
-
transaction.chain_id = chainId;
|
|
120
|
-
return {
|
|
121
|
-
method: "eth_sendTransaction",
|
|
122
|
-
caip2: `eip155:${chainId}`,
|
|
123
|
-
params: {
|
|
124
|
-
transaction
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
function parsePrivyTransactionHash(response) {
|
|
129
|
-
const intentId = response.intent_id;
|
|
130
|
-
if (response.status !== "executed") {
|
|
131
|
-
const reason = extractIntentReason(response) ?? "no reason provided";
|
|
132
|
-
if (response.status === "failed") {
|
|
133
|
-
throw new TxError("TX_REVERTED", `Privy rpc intent ${intentId} failed: ${reason}`);
|
|
134
|
-
}
|
|
135
|
-
if (response.status === "rejected" || response.status === "dismissed" || response.status === "expired") {
|
|
136
|
-
throw new TxError(
|
|
137
|
-
"PRIVY_AUTH_FAILED",
|
|
138
|
-
`Privy rpc intent ${intentId} ${response.status}: ${reason}`
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
throw new TxError(
|
|
142
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
143
|
-
`Privy rpc intent ${intentId} did not execute (status: ${response.status})`
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
const hash = extractIntentHash(response);
|
|
147
|
-
if (hash === void 0 || !HASH_REGEX.test(hash)) {
|
|
148
|
-
throw new TxError(
|
|
149
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
150
|
-
`Privy rpc intent ${intentId} executed without a valid transaction hash`
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
return hash;
|
|
154
|
-
}
|
|
155
|
-
function extractIntentHash(response) {
|
|
156
|
-
if (!isRecord(response.action_result)) {
|
|
157
|
-
return void 0;
|
|
158
|
-
}
|
|
159
|
-
const responseBody = response.action_result.response_body;
|
|
160
|
-
if (!isRecord(responseBody)) {
|
|
161
|
-
return void 0;
|
|
162
|
-
}
|
|
163
|
-
const data = responseBody.data;
|
|
164
|
-
if (!isRecord(data)) {
|
|
165
|
-
return void 0;
|
|
166
|
-
}
|
|
167
|
-
return typeof data.hash === "string" ? data.hash : void 0;
|
|
168
|
-
}
|
|
169
|
-
function extractIntentReason(response) {
|
|
170
|
-
if (typeof response.dismissal_reason === "string" && response.dismissal_reason.length > 0) {
|
|
171
|
-
return response.dismissal_reason;
|
|
172
|
-
}
|
|
173
|
-
if (isRecord(response.action_result)) {
|
|
174
|
-
const responseBody = response.action_result.response_body;
|
|
175
|
-
if (isRecord(responseBody)) {
|
|
176
|
-
const error = responseBody.error;
|
|
177
|
-
if (typeof error === "string" && error.length > 0) {
|
|
178
|
-
return error;
|
|
179
|
-
}
|
|
180
|
-
if (isRecord(error)) {
|
|
181
|
-
const errorMessage = error.message;
|
|
182
|
-
if (typeof errorMessage === "string" && errorMessage.length > 0) {
|
|
183
|
-
return errorMessage;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
const message = responseBody.message;
|
|
187
|
-
if (typeof message === "string" && message.length > 0) {
|
|
188
|
-
return message;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return void 0;
|
|
193
|
-
}
|
|
194
|
-
function normalizeChainId(chainId) {
|
|
195
|
-
if (typeof chainId === "number") {
|
|
196
|
-
if (!Number.isInteger(chainId) || chainId <= 0) {
|
|
197
|
-
throw new TxError(
|
|
198
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
199
|
-
"Failed to build Privy eth_sendTransaction payload: chainId must be a positive integer"
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
return chainId;
|
|
203
|
-
}
|
|
204
|
-
if (typeof chainId === "bigint") {
|
|
205
|
-
if (chainId <= 0n || chainId > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
206
|
-
throw new TxError(
|
|
207
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
208
|
-
"Failed to build Privy eth_sendTransaction payload: chainId must be a positive safe integer"
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
return Number(chainId);
|
|
212
|
-
}
|
|
213
|
-
const trimmed = chainId.trim();
|
|
214
|
-
if (trimmed.length === 0) {
|
|
215
|
-
throw new TxError(
|
|
216
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
217
|
-
"Failed to build Privy eth_sendTransaction payload: chainId is empty"
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
if (/^0x[0-9a-fA-F]+$/.test(trimmed)) {
|
|
221
|
-
const parsed2 = Number.parseInt(trimmed.slice(2), 16);
|
|
222
|
-
if (!Number.isSafeInteger(parsed2) || parsed2 <= 0) {
|
|
223
|
-
throw new TxError(
|
|
224
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
225
|
-
"Failed to build Privy eth_sendTransaction payload: chainId must be a positive safe integer"
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
return parsed2;
|
|
229
|
-
}
|
|
230
|
-
if (!/^[0-9]+$/.test(trimmed)) {
|
|
231
|
-
throw new TxError(
|
|
232
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
233
|
-
"Failed to build Privy eth_sendTransaction payload: chainId must be a positive safe integer"
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
const parsed = Number(trimmed);
|
|
237
|
-
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
238
|
-
throw new TxError(
|
|
239
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
240
|
-
"Failed to build Privy eth_sendTransaction payload: chainId must be a positive safe integer"
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
return parsed;
|
|
244
|
-
}
|
|
245
|
-
function normalizeNumberish(value, fieldName) {
|
|
246
|
-
if (typeof value === "bigint") {
|
|
247
|
-
if (value < 0n) {
|
|
248
|
-
throw new TxError(
|
|
249
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
250
|
-
`Failed to build Privy eth_sendTransaction payload: ${fieldName} cannot be negative`
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
return value.toString(10);
|
|
254
|
-
}
|
|
255
|
-
if (typeof value === "number") {
|
|
256
|
-
if (!Number.isFinite(value) || value < 0) {
|
|
257
|
-
throw new TxError(
|
|
258
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
259
|
-
`Failed to build Privy eth_sendTransaction payload: ${fieldName} must be a non-negative number`
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
return Number.isInteger(value) ? value : value.toString();
|
|
263
|
-
}
|
|
264
|
-
const trimmed = value.trim();
|
|
265
|
-
if (trimmed.length === 0) {
|
|
266
|
-
throw new TxError(
|
|
267
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
268
|
-
`Failed to build Privy eth_sendTransaction payload: ${fieldName} is empty`
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
return trimmed;
|
|
272
|
-
}
|
|
273
|
-
function isRecord(value) {
|
|
274
|
-
return typeof value === "object" && value !== null;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// src/signers/privy-signature.ts
|
|
278
|
-
import { createPrivateKey, sign as signWithCrypto } from "crypto";
|
|
279
|
-
var PRIVY_AUTHORIZATION_KEY_PREFIX = "wallet-auth:";
|
|
280
|
-
var PRIVY_AUTHORIZATION_KEY_REGEX = /^wallet-auth:[A-Za-z0-9+/]+={0,2}$/;
|
|
281
|
-
var DEFAULT_PRIVY_API_URL = "https://api.privy.io";
|
|
282
|
-
function normalizePrivyApiUrl(apiUrl) {
|
|
283
|
-
const value = (apiUrl ?? DEFAULT_PRIVY_API_URL).trim();
|
|
284
|
-
if (value.length === 0) {
|
|
285
|
-
throw new TxError(
|
|
286
|
-
"PRIVY_AUTH_FAILED",
|
|
287
|
-
"Invalid PRIVY_API_URL format: expected a non-empty http(s) URL"
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
let parsed;
|
|
291
|
-
try {
|
|
292
|
-
parsed = new URL(value);
|
|
293
|
-
} catch (cause) {
|
|
294
|
-
throw new TxError(
|
|
295
|
-
"PRIVY_AUTH_FAILED",
|
|
296
|
-
"Invalid PRIVY_API_URL format: expected a non-empty http(s) URL",
|
|
297
|
-
cause
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
301
|
-
throw new TxError(
|
|
302
|
-
"PRIVY_AUTH_FAILED",
|
|
303
|
-
"Invalid PRIVY_API_URL format: expected a non-empty http(s) URL"
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
return parsed.toString().replace(/\/+$/, "");
|
|
307
|
-
}
|
|
308
|
-
function parsePrivyAuthorizationKey(authorizationKey) {
|
|
309
|
-
const normalizedKey = authorizationKey.trim();
|
|
310
|
-
if (!PRIVY_AUTHORIZATION_KEY_REGEX.test(normalizedKey)) {
|
|
311
|
-
throw new TxError(
|
|
312
|
-
"PRIVY_AUTH_FAILED",
|
|
313
|
-
"Invalid PRIVY_AUTHORIZATION_KEY format: expected wallet-auth:<base64-pkcs8-p256-private-key>"
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
const rawPrivateKey = normalizedKey.slice(PRIVY_AUTHORIZATION_KEY_PREFIX.length);
|
|
317
|
-
let derKey;
|
|
318
|
-
try {
|
|
319
|
-
derKey = Buffer.from(rawPrivateKey, "base64");
|
|
320
|
-
} catch (cause) {
|
|
321
|
-
throw new TxError(
|
|
322
|
-
"PRIVY_AUTH_FAILED",
|
|
323
|
-
"Invalid PRIVY_AUTHORIZATION_KEY format: expected wallet-auth:<base64-pkcs8-p256-private-key>",
|
|
324
|
-
cause
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
let keyObject;
|
|
328
|
-
try {
|
|
329
|
-
keyObject = createPrivateKey({
|
|
330
|
-
key: derKey,
|
|
331
|
-
format: "der",
|
|
332
|
-
type: "pkcs8"
|
|
333
|
-
});
|
|
334
|
-
} catch (cause) {
|
|
335
|
-
throw new TxError(
|
|
336
|
-
"PRIVY_AUTH_FAILED",
|
|
337
|
-
"Invalid PRIVY_AUTHORIZATION_KEY format: expected wallet-auth:<base64-pkcs8-p256-private-key>",
|
|
338
|
-
cause
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
if (keyObject.asymmetricKeyType !== "ec") {
|
|
342
|
-
throw new TxError(
|
|
343
|
-
"PRIVY_AUTH_FAILED",
|
|
344
|
-
"Invalid PRIVY_AUTHORIZATION_KEY format: expected wallet-auth:<base64-pkcs8-p256-private-key>"
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
const details = keyObject.asymmetricKeyDetails;
|
|
348
|
-
const namedCurve = details !== void 0 && "namedCurve" in details ? details.namedCurve : void 0;
|
|
349
|
-
if (namedCurve !== void 0 && namedCurve !== "prime256v1") {
|
|
350
|
-
throw new TxError(
|
|
351
|
-
"PRIVY_AUTH_FAILED",
|
|
352
|
-
"Invalid PRIVY_AUTHORIZATION_KEY format: expected wallet-auth:<base64-pkcs8-p256-private-key>"
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
return keyObject;
|
|
356
|
-
}
|
|
357
|
-
function createPrivyAuthorizationPayload(options) {
|
|
358
|
-
const appId = options.appId.trim();
|
|
359
|
-
if (appId.length === 0) {
|
|
360
|
-
throw new TxError(
|
|
361
|
-
"PRIVY_AUTH_FAILED",
|
|
362
|
-
"Invalid PRIVY_APP_ID format: expected non-empty string"
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
const url = options.url.trim().replace(/\/+$/, "");
|
|
366
|
-
if (url.length === 0) {
|
|
367
|
-
throw new TxError(
|
|
368
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
369
|
-
"Failed to build Privy authorization payload: request URL is empty"
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
return {
|
|
373
|
-
version: 1,
|
|
374
|
-
method: options.method,
|
|
375
|
-
url,
|
|
376
|
-
headers: {
|
|
377
|
-
"privy-app-id": appId,
|
|
378
|
-
...options.idempotencyKey !== void 0 ? { "privy-idempotency-key": options.idempotencyKey } : {}
|
|
379
|
-
},
|
|
380
|
-
body: options.body
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
function serializePrivyAuthorizationPayload(payload) {
|
|
384
|
-
return canonicalizeJson(payload);
|
|
385
|
-
}
|
|
386
|
-
function generatePrivyAuthorizationSignature(payload, authorizationKey) {
|
|
387
|
-
const privateKey = parsePrivyAuthorizationKey(authorizationKey);
|
|
388
|
-
const serializedPayload = serializePrivyAuthorizationPayload(payload);
|
|
389
|
-
const signature = signWithCrypto("sha256", Buffer.from(serializedPayload), privateKey);
|
|
390
|
-
return signature.toString("base64");
|
|
391
|
-
}
|
|
392
|
-
function canonicalizeJson(value) {
|
|
393
|
-
if (value === null) {
|
|
394
|
-
return "null";
|
|
395
|
-
}
|
|
396
|
-
if (typeof value === "string" || typeof value === "boolean") {
|
|
397
|
-
return JSON.stringify(value);
|
|
398
|
-
}
|
|
399
|
-
if (typeof value === "number") {
|
|
400
|
-
if (!Number.isFinite(value)) {
|
|
401
|
-
throw new TxError(
|
|
402
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
403
|
-
"Failed to build Privy authorization payload: JSON payload contains a non-finite number"
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
return JSON.stringify(value);
|
|
407
|
-
}
|
|
408
|
-
if (Array.isArray(value)) {
|
|
409
|
-
return `[${value.map((item) => canonicalizeJson(item)).join(",")}]`;
|
|
410
|
-
}
|
|
411
|
-
if (typeof value === "object") {
|
|
412
|
-
const record = value;
|
|
413
|
-
const keys = Object.keys(record).sort((left, right) => left.localeCompare(right));
|
|
414
|
-
const entries = [];
|
|
415
|
-
for (const key of keys) {
|
|
416
|
-
const entryValue = record[key];
|
|
417
|
-
if (entryValue === void 0) {
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
entries.push(`${JSON.stringify(key)}:${canonicalizeJson(entryValue)}`);
|
|
421
|
-
}
|
|
422
|
-
return `{${entries.join(",")}}`;
|
|
423
|
-
}
|
|
424
|
-
throw new TxError(
|
|
425
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
426
|
-
"Failed to build Privy authorization payload: JSON payload contains unsupported value type"
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// src/signers/privy-client.ts
|
|
431
|
-
function createPrivyClient(options) {
|
|
432
|
-
const appId = options.appId.trim();
|
|
433
|
-
const walletId = options.walletId.trim();
|
|
434
|
-
if (appId.length === 0) {
|
|
435
|
-
throw new TxError(
|
|
436
|
-
"PRIVY_AUTH_FAILED",
|
|
437
|
-
"Invalid PRIVY_APP_ID format: expected non-empty string"
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
if (walletId.length === 0) {
|
|
441
|
-
throw new TxError(
|
|
442
|
-
"PRIVY_AUTH_FAILED",
|
|
443
|
-
"Invalid PRIVY_WALLET_ID format: expected non-empty string"
|
|
444
|
-
);
|
|
445
|
-
}
|
|
446
|
-
const apiUrl = normalizePrivyApiUrl(options.apiUrl);
|
|
447
|
-
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
448
|
-
return {
|
|
449
|
-
appId,
|
|
450
|
-
walletId,
|
|
451
|
-
apiUrl,
|
|
452
|
-
async createRpcIntent(request, requestOptions) {
|
|
453
|
-
const url = `${apiUrl}/v1/intents/wallets/${walletId}/rpc`;
|
|
454
|
-
const payload = createPrivyAuthorizationPayload({
|
|
455
|
-
appId,
|
|
456
|
-
method: "POST",
|
|
457
|
-
url,
|
|
458
|
-
body: request,
|
|
459
|
-
...requestOptions?.idempotencyKey !== void 0 ? { idempotencyKey: requestOptions.idempotencyKey } : {}
|
|
460
|
-
});
|
|
461
|
-
const signature = generatePrivyAuthorizationSignature(payload, options.authorizationKey);
|
|
462
|
-
return sendPrivyRequest({
|
|
463
|
-
fetchImplementation,
|
|
464
|
-
method: "POST",
|
|
465
|
-
url,
|
|
466
|
-
body: request,
|
|
467
|
-
operation: "create rpc intent",
|
|
468
|
-
headers: {
|
|
469
|
-
"privy-app-id": appId,
|
|
470
|
-
"privy-authorization-signature": signature,
|
|
471
|
-
...requestOptions?.idempotencyKey !== void 0 ? { "privy-idempotency-key": requestOptions.idempotencyKey } : {}
|
|
472
|
-
}
|
|
473
|
-
});
|
|
474
|
-
},
|
|
475
|
-
async getWallet() {
|
|
476
|
-
const url = `${apiUrl}/v1/wallets/${walletId}`;
|
|
477
|
-
return sendPrivyRequest({
|
|
478
|
-
fetchImplementation,
|
|
479
|
-
method: "GET",
|
|
480
|
-
url,
|
|
481
|
-
operation: "get wallet",
|
|
482
|
-
headers: {
|
|
483
|
-
"privy-app-id": appId
|
|
484
|
-
}
|
|
485
|
-
});
|
|
486
|
-
},
|
|
487
|
-
async getPolicy(policyId) {
|
|
488
|
-
if (policyId.trim().length === 0) {
|
|
489
|
-
throw new TxError(
|
|
490
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
491
|
-
"Failed to build Privy policy lookup request: policy id is empty"
|
|
492
|
-
);
|
|
493
|
-
}
|
|
494
|
-
const url = `${apiUrl}/v1/policies/${policyId}`;
|
|
495
|
-
return sendPrivyRequest({
|
|
496
|
-
fetchImplementation,
|
|
497
|
-
method: "GET",
|
|
498
|
-
url,
|
|
499
|
-
operation: "get policy",
|
|
500
|
-
headers: {
|
|
501
|
-
"privy-app-id": appId
|
|
502
|
-
}
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
async function sendPrivyRequest(options) {
|
|
508
|
-
let response;
|
|
509
|
-
try {
|
|
510
|
-
response = await options.fetchImplementation(options.url, {
|
|
511
|
-
method: options.method,
|
|
512
|
-
headers: {
|
|
513
|
-
...options.headers,
|
|
514
|
-
...options.body !== void 0 ? { "content-type": "application/json" } : {}
|
|
515
|
-
},
|
|
516
|
-
...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {}
|
|
517
|
-
});
|
|
518
|
-
} catch (cause) {
|
|
519
|
-
throw new TxError(
|
|
520
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
521
|
-
`Privy ${options.operation} request failed: network error`,
|
|
522
|
-
cause
|
|
523
|
-
);
|
|
524
|
-
}
|
|
525
|
-
const payload = await parseJsonResponse(response, options.operation);
|
|
526
|
-
if (!response.ok) {
|
|
527
|
-
const message = extractPrivyErrorMessage(payload) ?? `HTTP ${response.status}`;
|
|
528
|
-
if (response.status === 401 || response.status === 403) {
|
|
529
|
-
throw new TxError(
|
|
530
|
-
"PRIVY_AUTH_FAILED",
|
|
531
|
-
`Privy authentication failed (${response.status}): ${message}`
|
|
532
|
-
);
|
|
533
|
-
}
|
|
534
|
-
throw new TxError(
|
|
535
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
536
|
-
`Privy ${options.operation} request failed (${response.status}): ${message}`
|
|
537
|
-
);
|
|
538
|
-
}
|
|
539
|
-
if (!isRecord2(payload)) {
|
|
540
|
-
throw new TxError(
|
|
541
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
542
|
-
`Privy ${options.operation} request failed: invalid JSON response shape`
|
|
543
|
-
);
|
|
544
|
-
}
|
|
545
|
-
return payload;
|
|
546
|
-
}
|
|
547
|
-
async function parseJsonResponse(response, operation) {
|
|
548
|
-
const text = await response.text();
|
|
549
|
-
if (text.trim().length === 0) {
|
|
550
|
-
return void 0;
|
|
551
|
-
}
|
|
552
|
-
try {
|
|
553
|
-
return JSON.parse(text);
|
|
554
|
-
} catch (cause) {
|
|
555
|
-
throw new TxError(
|
|
556
|
-
"PRIVY_TRANSPORT_FAILED",
|
|
557
|
-
`Privy ${operation} request failed: invalid JSON response`,
|
|
558
|
-
cause
|
|
559
|
-
);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
function isRecord2(value) {
|
|
563
|
-
return typeof value === "object" && value !== null;
|
|
564
|
-
}
|
|
565
|
-
function extractPrivyErrorMessage(payload) {
|
|
566
|
-
if (!isRecord2(payload)) {
|
|
567
|
-
return void 0;
|
|
568
|
-
}
|
|
569
|
-
const directMessage = payload.message;
|
|
570
|
-
if (typeof directMessage === "string" && directMessage.length > 0) {
|
|
571
|
-
return directMessage;
|
|
572
|
-
}
|
|
573
|
-
const errorValue = payload.error;
|
|
574
|
-
if (typeof errorValue === "string" && errorValue.length > 0) {
|
|
575
|
-
return errorValue;
|
|
576
|
-
}
|
|
577
|
-
if (isRecord2(errorValue)) {
|
|
578
|
-
const nestedMessage = errorValue.message;
|
|
579
|
-
if (typeof nestedMessage === "string" && nestedMessage.length > 0) {
|
|
580
|
-
return nestedMessage;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
return void 0;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// src/signers/privy.ts
|
|
587
|
-
var REQUIRED_FIELDS = [
|
|
588
|
-
"privyAppId",
|
|
589
|
-
"privyWalletId",
|
|
590
|
-
"privyAuthorizationKey"
|
|
591
|
-
];
|
|
592
|
-
var APP_ID_REGEX = /^[A-Za-z0-9_-]{8,128}$/;
|
|
593
|
-
var WALLET_ID_REGEX = /^[A-Za-z0-9_-]{8,128}$/;
|
|
594
|
-
async function createPrivySigner(options) {
|
|
595
|
-
const missing = REQUIRED_FIELDS.filter((field) => {
|
|
596
|
-
const value = options[field];
|
|
597
|
-
return typeof value !== "string" || value.trim().length === 0;
|
|
598
|
-
});
|
|
599
|
-
if (missing.length > 0) {
|
|
600
|
-
const missingLabels = missing.map((field) => {
|
|
601
|
-
if (field === "privyAppId") {
|
|
602
|
-
return "PRIVY_APP_ID";
|
|
603
|
-
}
|
|
604
|
-
if (field === "privyWalletId") {
|
|
605
|
-
return "PRIVY_WALLET_ID";
|
|
606
|
-
}
|
|
607
|
-
return "PRIVY_AUTHORIZATION_KEY";
|
|
608
|
-
}).join(", ");
|
|
609
|
-
throw new TxError(
|
|
610
|
-
"PRIVY_AUTH_FAILED",
|
|
611
|
-
`Privy signer requires configuration: missing ${missingLabels}`
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
const appId = options.privyAppId?.trim() ?? "";
|
|
615
|
-
const walletId = options.privyWalletId?.trim() ?? "";
|
|
616
|
-
const authorizationKey = options.privyAuthorizationKey?.trim() ?? "";
|
|
617
|
-
if (!APP_ID_REGEX.test(appId)) {
|
|
618
|
-
throw new TxError(
|
|
619
|
-
"PRIVY_AUTH_FAILED",
|
|
620
|
-
"Invalid PRIVY_APP_ID format: expected 8-128 chars using letters, numbers, hyphen, or underscore"
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
|
-
if (!WALLET_ID_REGEX.test(walletId)) {
|
|
624
|
-
throw new TxError(
|
|
625
|
-
"PRIVY_AUTH_FAILED",
|
|
626
|
-
"Invalid PRIVY_WALLET_ID format: expected 8-128 chars using letters, numbers, hyphen, or underscore"
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
parsePrivyAuthorizationKey(authorizationKey);
|
|
630
|
-
const apiUrl = normalizePrivyApiUrl(options.privyApiUrl);
|
|
631
|
-
const client = createPrivyClient({
|
|
632
|
-
appId,
|
|
633
|
-
walletId,
|
|
634
|
-
authorizationKey,
|
|
635
|
-
apiUrl
|
|
636
|
-
});
|
|
637
|
-
const account = await createPrivyAccount({
|
|
638
|
-
client
|
|
639
|
-
});
|
|
640
|
-
return {
|
|
641
|
-
provider: "privy",
|
|
642
|
-
account,
|
|
643
|
-
address: account.address,
|
|
644
|
-
privy: {
|
|
645
|
-
appId,
|
|
646
|
-
walletId,
|
|
647
|
-
apiUrl,
|
|
648
|
-
client
|
|
649
|
-
}
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
|
|
653
74
|
// src/resolve-signer.ts
|
|
654
75
|
function hasPrivyConfig(opts) {
|
|
655
76
|
return opts.privyAppId !== void 0 || opts.privyWalletId !== void 0 || opts.privyAuthorizationKey !== void 0;
|
|
@@ -671,12 +92,13 @@ async function resolveSigner(opts) {
|
|
|
671
92
|
});
|
|
672
93
|
}
|
|
673
94
|
if (opts.privy === true || hasPrivyConfig(opts)) {
|
|
674
|
-
|
|
95
|
+
const privySigner = await createPrivySigner({
|
|
675
96
|
...opts.privyAppId !== void 0 ? { privyAppId: opts.privyAppId } : {},
|
|
676
97
|
...opts.privyWalletId !== void 0 ? { privyWalletId: opts.privyWalletId } : {},
|
|
677
98
|
...opts.privyAuthorizationKey !== void 0 ? { privyAuthorizationKey: opts.privyAuthorizationKey } : {},
|
|
678
99
|
...opts.privyApiUrl !== void 0 ? { privyApiUrl: opts.privyApiUrl } : {}
|
|
679
100
|
});
|
|
101
|
+
return privySigner;
|
|
680
102
|
}
|
|
681
103
|
throw new TxError(
|
|
682
104
|
"SIGNER_NOT_CONFIGURED",
|
|
@@ -719,6 +141,7 @@ function toSignerOptions(flags, env) {
|
|
|
719
141
|
export {
|
|
720
142
|
TxError,
|
|
721
143
|
abstractMainnet,
|
|
144
|
+
attachPrivyPolicyContext,
|
|
722
145
|
createAbstractClient,
|
|
723
146
|
createKeystoreSigner,
|
|
724
147
|
createPrivateKeySigner,
|
|
@@ -727,13 +150,18 @@ export {
|
|
|
727
150
|
createPrivyClient,
|
|
728
151
|
createPrivySigner,
|
|
729
152
|
executeTx,
|
|
153
|
+
fetchPrivyPolicyVisibility,
|
|
730
154
|
generatePrivyAuthorizationSignature,
|
|
155
|
+
getPrivyPolicyContext,
|
|
731
156
|
normalizePrivyApiUrl,
|
|
157
|
+
normalizePrivyPolicy,
|
|
732
158
|
parsePrivyAuthorizationKey,
|
|
159
|
+
preflightPrivyTransactionPolicy,
|
|
733
160
|
resolveSigner,
|
|
734
161
|
serializePrivyAuthorizationPayload,
|
|
735
162
|
signerEnvSchema,
|
|
736
163
|
signerFlagSchema,
|
|
164
|
+
toPrivyPolicyViolationError,
|
|
737
165
|
toSignerOptions,
|
|
738
166
|
toTxError
|
|
739
167
|
};
|