@anywalletkit/extension-kit 0.1.2 → 0.1.3

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/dist/index.js CHANGED
@@ -40,6 +40,22 @@ var VAULT_DEFAULT_ITERATIONS = 6e5;
40
40
  var VAULT_SCRYPT = { N: 1 << 17, r: 8, p: 1 };
41
41
  var VAULT_MIN_SCRYPT = { N: 1 << 14, r: 8, p: 1 };
42
42
  var VAULT_MIN_PASSWORD_LENGTH = 8;
43
+ var VAULT_LIMITS = Object.freeze({
44
+ /** scrypt cost: 2^20 × 16 = 2 GiB is the most any envelope may ask for. */
45
+ scrypt: Object.freeze({ N: 1 << 20, r: 16, p: 4 }),
46
+ /** PBKDF2-SHA256 iterations (v1 envelopes). */
47
+ iterations: 5e6,
48
+ /** Sealed secrets in one vault. */
49
+ secrets: 1e3,
50
+ /** Registered passkeys. */
51
+ passkeys: 32,
52
+ /** Plaintext bytes of one secret (`setSecret`). */
53
+ secretBytes: 16 * 1024,
54
+ /** base64 characters of one sealed box (iv or data). */
55
+ boxChars: 32 * 1024,
56
+ /** JSON characters of the whole envelope. */
57
+ envelopeChars: 4 * 1024 * 1024
58
+ });
43
59
  var SALT_BYTES = 32;
44
60
  var IV_BYTES = 12;
45
61
  var DEK_BYTES = 32;
@@ -51,6 +67,8 @@ var aadSecret = (id) => `forgewallet-vault/secret/${id}`;
51
67
  var HKDF_INFO = "forgewallet-vault/2/passkey";
52
68
  var Vault = class {
53
69
  _storage;
70
+ /** Every read-modify-write of the envelope runs here, one after another (a parallel write would drop the other's change). */
71
+ _queue = Promise.resolve();
54
72
  _kdf;
55
73
  _iterations;
56
74
  _scrypt;
@@ -63,10 +81,11 @@ var Vault = class {
63
81
  this._scrypt = options.scrypt ?? VAULT_SCRYPT;
64
82
  this._onProgress = options.onProgress;
65
83
  this._crypto = options.crypto ?? globalThis.crypto;
66
- if (!Number.isInteger(this._iterations) || this._iterations < VAULT_MIN_ITERATIONS) {
67
- throw new VaultError(`PBKDF2 iterations must be an integer \u2265 ${VAULT_MIN_ITERATIONS}`, "invalid-input");
84
+ if (!Number.isInteger(this._iterations) || this._iterations < VAULT_MIN_ITERATIONS || this._iterations > VAULT_LIMITS.iterations) {
85
+ throw new VaultError(`PBKDF2 iterations must be an integer from ${VAULT_MIN_ITERATIONS} to ${VAULT_LIMITS.iterations}`, "invalid-input");
68
86
  }
69
87
  if (!isStrongScrypt(this._scrypt)) throw new VaultError(`scrypt cost must be at least N=${VAULT_MIN_SCRYPT.N} r=${VAULT_MIN_SCRYPT.r} p=${VAULT_MIN_SCRYPT.p} (N a power of two)`, "invalid-input");
88
+ if (!isBoundedScrypt(this._scrypt)) throw new VaultError(`scrypt cost must be at most N=${VAULT_LIMITS.scrypt.N} r=${VAULT_LIMITS.scrypt.r} p=${VAULT_LIMITS.scrypt.p}`, "invalid-input");
70
89
  }
71
90
  async exists() {
72
91
  return await this._storage.get() !== null;
@@ -85,10 +104,12 @@ var Vault = class {
85
104
  */
86
105
  async create(password) {
87
106
  requirePassword(password, VAULT_MIN_PASSWORD_LENGTH);
88
- if (await this.exists()) throw new VaultError("A vault already exists; unlock it or reset explicitly.", "exists");
89
- const dek = this._random(DEK_BYTES);
90
- await this._storage.set(await this._sealEnvelope(password, dek, {}, [], Date.now()));
91
- return dek;
107
+ return this._exclusive(async () => {
108
+ if (await this.exists()) throw new VaultError("A vault already exists; unlock it or reset explicitly.", "exists");
109
+ const dek = this._random(DEK_BYTES);
110
+ await this._write(await this._sealEnvelope(password, dek, {}, [], Date.now()));
111
+ return dek;
112
+ });
92
113
  }
93
114
  /**
94
115
  * Derive the KEK from `password`, unseal and return the DEK. `wrong-password` on
@@ -97,6 +118,9 @@ var Vault = class {
97
118
  */
98
119
  async unlock(password) {
99
120
  requirePassword(password);
121
+ return this._exclusive(() => this._unlock(password));
122
+ }
123
+ async _unlock(password) {
100
124
  const envelope = await this._load();
101
125
  if (isEnvelopeV1(envelope)) {
102
126
  if (envelope.kdf.iterations < VAULT_MIN_ITERATIONS) throw new VaultError(`The vault's KDF (${envelope.kdf.iterations} iterations) is below the floor this build accepts.`, "weak-kdf");
@@ -104,7 +128,7 @@ var Vault = class {
104
128
  const dek = await this._open(kek2, envelope.dek, aadDekV1(envelope.kdf), "wrong-password");
105
129
  if (this._kdf === "scrypt") {
106
130
  const next = await this._sealEnvelope(password, dek, envelope.secrets, [], envelope.createdAt);
107
- await this._storage.set(isEnvelopeV2(next) ? { ...next, upgradedAt: Date.now() } : next);
131
+ await this._write(isEnvelopeV2(next) ? { ...next, upgradedAt: Date.now() } : next);
108
132
  }
109
133
  return dek;
110
134
  }
@@ -115,15 +139,18 @@ var Vault = class {
115
139
  /** Re-wrap the DEK under a new password (fresh salt, current KDF). Secrets and passkeys are untouched. */
116
140
  async changePassword(oldPassword, newPassword) {
117
141
  requirePassword(newPassword, VAULT_MIN_PASSWORD_LENGTH);
118
- const dek = await this.unlock(oldPassword);
119
- try {
120
- const envelope = await this._load();
121
- const passkeys = isEnvelopeV2(envelope) ? envelope.passkeys : [];
122
- const next = await this._sealEnvelope(newPassword, dek, envelope.secrets, passkeys, envelope.createdAt);
123
- await this._storage.set(isEnvelopeV2(envelope) && envelope.upgradedAt !== void 0 && isEnvelopeV2(next) ? { ...next, upgradedAt: envelope.upgradedAt } : next);
124
- } finally {
125
- zeroize(dek);
126
- }
142
+ requirePassword(oldPassword);
143
+ return this._exclusive(async () => {
144
+ const dek = await this._unlock(oldPassword);
145
+ try {
146
+ const envelope = await this._load();
147
+ const passkeys = isEnvelopeV2(envelope) ? envelope.passkeys : [];
148
+ const next = await this._sealEnvelope(newPassword, dek, envelope.secrets, passkeys, envelope.createdAt);
149
+ await this._write(isEnvelopeV2(envelope) && envelope.upgradedAt !== void 0 && isEnvelopeV2(next) ? { ...next, upgradedAt: envelope.upgradedAt } : next);
150
+ } finally {
151
+ zeroize(dek);
152
+ }
153
+ });
127
154
  }
128
155
  // ---- passkeys (v2) ----
129
156
  /** Public view of the registered passkeys (ids, labels; whether each can verify assertions). */
@@ -149,21 +176,24 @@ var Vault = class {
149
176
  try {
150
177
  requireId(passkey.id);
151
178
  if (passkey.prf.length !== PRF_BYTES) throw new VaultError(`A PRF output is ${PRF_BYTES} bytes.`, "invalid-input");
152
- const envelope = await this._load();
153
- if (!isEnvelopeV2(envelope)) throw new VaultError("Unlock with the password once so the vault is upgraded before adding a passkey.", "unsupported-envelope");
154
- if (envelope.passkeys.some((p) => p.id === passkey.id)) throw new VaultError("This passkey is already registered.", "exists");
155
- await this._requireDek(dek, envelope);
156
- const key = await this._derivePasskeyKek(passkey.prf, passkey.id);
157
- const entry = { id: passkey.id, label: passkey.label.slice(0, 64), createdAt: Date.now(), dek: await this._seal(key, dek, aadPasskey(passkey.id)) };
158
- if (passkey.publicKey && passkey.alg !== void 0) {
159
- entry.publicKey = passkey.publicKey;
160
- entry.alg = passkey.alg;
161
- }
162
- await this._storage.set({ ...envelope, passkeys: [...envelope.passkeys, entry] });
179
+ await this._exclusive(() => this._addPasskey(dek, passkey));
163
180
  } finally {
164
181
  zeroize(passkey.prf);
165
182
  }
166
183
  }
184
+ async _addPasskey(dek, passkey) {
185
+ const envelope = await this._load();
186
+ if (!isEnvelopeV2(envelope)) throw new VaultError("Unlock with the password once so the vault is upgraded before adding a passkey.", "unsupported-envelope");
187
+ if (envelope.passkeys.some((p) => p.id === passkey.id)) throw new VaultError("This passkey is already registered.", "exists");
188
+ await this._requireDek(dek, envelope);
189
+ const key = await this._derivePasskeyKek(passkey.prf, passkey.id);
190
+ const entry = { id: passkey.id, label: passkey.label.slice(0, 64), createdAt: Date.now(), dek: await this._seal(key, dek, aadPasskey(passkey.id)) };
191
+ if (passkey.publicKey && passkey.alg !== void 0) {
192
+ entry.publicKey = passkey.publicKey;
193
+ entry.alg = passkey.alg;
194
+ }
195
+ await this._write({ ...envelope, passkeys: [...envelope.passkeys, entry] });
196
+ }
167
197
  /** Unseal the DEK with a passkey's PRF output. `wrong-password` when the box does not open, `missing` when the id is unknown. */
168
198
  async unlockWithPasskey(id, prf) {
169
199
  try {
@@ -181,9 +211,11 @@ var Vault = class {
181
211
  }
182
212
  async removePasskey(id) {
183
213
  requireId(id);
184
- const envelope = await this._load();
185
- if (!isEnvelopeV2(envelope)) return;
186
- await this._storage.set({ ...envelope, passkeys: envelope.passkeys.filter((p) => p.id !== id) });
214
+ return this._exclusive(async () => {
215
+ const envelope = await this._load();
216
+ if (!isEnvelopeV2(envelope)) return;
217
+ await this._write({ ...envelope, passkeys: envelope.passkeys.filter((p) => p.id !== id) });
218
+ });
187
219
  }
188
220
  // ---- secrets ----
189
221
  async listSecretIds() {
@@ -193,10 +225,16 @@ var Vault = class {
193
225
  /** Seal `secret` under the DEK as `sourceId` (overwrites an entry with the same id). */
194
226
  async setSecret(dek, sourceId, secret) {
195
227
  requireId(sourceId);
196
- const envelope = await this._load();
228
+ if (secret.length > VAULT_LIMITS.secretBytes) throw new VaultError(`A secret is at most ${VAULT_LIMITS.secretBytes} bytes.`, "invalid-input");
197
229
  const key = await this._importDek(dek);
198
- const box = await this._seal(key, secret, aadSecret(sourceId));
199
- await this._storage.set({ ...envelope, secrets: { ...envelope.secrets, [sourceId]: box } });
230
+ return this._exclusive(async () => {
231
+ const envelope = await this._load();
232
+ if (!(sourceId in envelope.secrets) && Object.keys(envelope.secrets).length >= VAULT_LIMITS.secrets) {
233
+ throw new VaultError(`A vault holds at most ${VAULT_LIMITS.secrets} secrets.`, "invalid-input");
234
+ }
235
+ const box = await this._seal(key, secret, aadSecret(sourceId));
236
+ await this._write({ ...envelope, secrets: { ...envelope.secrets, [sourceId]: box } });
237
+ });
200
238
  }
201
239
  /** Unseal one secret. `null` when the id is unknown; the caller zeroizes the result after use. */
202
240
  async getSecret(dek, sourceId) {
@@ -209,15 +247,29 @@ var Vault = class {
209
247
  }
210
248
  async deleteSecret(sourceId) {
211
249
  requireId(sourceId);
212
- const envelope = await this._load();
213
- if (!(sourceId in envelope.secrets)) return;
214
- const secrets = { ...envelope.secrets };
215
- delete secrets[sourceId];
216
- await this._storage.set({ ...envelope, secrets });
250
+ return this._exclusive(async () => {
251
+ const envelope = await this._load();
252
+ if (!(sourceId in envelope.secrets)) return;
253
+ const secrets = { ...envelope.secrets };
254
+ delete secrets[sourceId];
255
+ await this._write({ ...envelope, secrets });
256
+ });
217
257
  }
218
258
  /** Remove the whole vault. The explicit destructive flow only (back up → wipe → create). */
219
259
  async destroy() {
220
- await this._storage.clear();
260
+ return this._exclusive(() => this._storage.clear());
261
+ }
262
+ // ---- serialized writes ----
263
+ _exclusive(fn) {
264
+ const run = this._queue.then(fn);
265
+ this._queue = run.catch(() => void 0);
266
+ return run;
267
+ }
268
+ /** Store an envelope this build will open again (bounds checked before it is written). */
269
+ async _write(envelope) {
270
+ const problem = envelopeProblem(envelope);
271
+ if (problem) throw new VaultError(`The vault would exceed its limits (${problem}); nothing was written.`, "invalid-input");
272
+ await this._storage.set(envelope);
221
273
  }
222
274
  // ---- crypto ----
223
275
  _random(n) {
@@ -293,6 +345,8 @@ var Vault = class {
293
345
  const envelope = await this._storage.get();
294
346
  if (!envelope) throw new VaultError("No vault in this storage area.", "missing");
295
347
  if (!isEnvelopeV1(envelope) && !isEnvelopeV2(envelope)) throw new VaultError("Unsupported vault envelope.", "unsupported-envelope");
348
+ const problem = envelopeProblem(envelope);
349
+ if (problem) throw new VaultError(`The vault envelope is outside the limits this build opens (${problem}).`, "unsupported-envelope");
296
350
  return envelope;
297
351
  }
298
352
  };
@@ -311,6 +365,22 @@ function isEnvelopeV2(x) {
311
365
  function isBox(x) {
312
366
  return typeof x === "object" && x !== null && typeof x.iv === "string" && typeof x.data === "string";
313
367
  }
368
+ function isBoundedScrypt(c) {
369
+ return c.N <= VAULT_LIMITS.scrypt.N && c.r <= VAULT_LIMITS.scrypt.r && c.p <= VAULT_LIMITS.scrypt.p;
370
+ }
371
+ function envelopeProblem(e) {
372
+ if (e.v === 1 && e.kdf.iterations > VAULT_LIMITS.iterations) return `PBKDF2 iterations ${e.kdf.iterations} > ${VAULT_LIMITS.iterations}`;
373
+ if (e.v === 2 && !isBoundedScrypt(e.kdf)) return `scrypt N=${e.kdf.N} r=${e.kdf.r} p=${e.kdf.p} above N=${VAULT_LIMITS.scrypt.N} r=${VAULT_LIMITS.scrypt.r} p=${VAULT_LIMITS.scrypt.p}`;
374
+ const secrets = Object.values(e.secrets);
375
+ if (secrets.length > VAULT_LIMITS.secrets) return `${secrets.length} secrets > ${VAULT_LIMITS.secrets}`;
376
+ const passkeys = e.v === 2 ? e.passkeys : [];
377
+ if (passkeys.length > VAULT_LIMITS.passkeys) return `${passkeys.length} passkeys > ${VAULT_LIMITS.passkeys}`;
378
+ for (const box of [e.dek, ...secrets, ...passkeys.map((p) => p.dek)]) {
379
+ if (box.iv.length > VAULT_LIMITS.boxChars || box.data.length > VAULT_LIMITS.boxChars) return `a sealed box above ${VAULT_LIMITS.boxChars} characters`;
380
+ }
381
+ if (JSON.stringify(e).length > VAULT_LIMITS.envelopeChars) return `envelope above ${VAULT_LIMITS.envelopeChars} characters`;
382
+ return null;
383
+ }
314
384
  function isStrongScrypt(c) {
315
385
  return Number.isInteger(c.N) && c.N >= VAULT_MIN_SCRYPT.N && (c.N & c.N - 1) === 0 && Number.isInteger(c.r) && c.r >= VAULT_MIN_SCRYPT.r && Number.isInteger(c.p) && c.p >= VAULT_MIN_SCRYPT.p;
316
386
  }
@@ -355,6 +425,13 @@ var Session = class {
355
425
  _alarm;
356
426
  _now;
357
427
  _onLock = /* @__PURE__ */ new Set();
428
+ /**
429
+ * Every operation on the stored record runs here, one after another: a `touch` that read the
430
+ * record before a `lock` removed it would otherwise write it back and undo the lock.
431
+ */
432
+ _queue = Promise.resolve();
433
+ /** Bumped by every lock; a `touch` writes only when no lock happened since it read the record. */
434
+ _epoch = 0;
358
435
  constructor(options) {
359
436
  this._storage = options.storage;
360
437
  this._alarms = options.alarms;
@@ -370,20 +447,19 @@ var Session = class {
370
447
  /** Store the DEK and arm the auto-lock. The caller's copy is zeroized. */
371
448
  async unlock(dek) {
372
449
  if (dek.length !== 32) throw new VaultError("Vault key must be 32 bytes.", "invalid-input");
373
- const expiresAt = this._expiry();
374
- await this._storage.set(this._key, { key: toBase64(dek), expiresAt });
375
- zeroize2(dek);
376
- await this._arm(expiresAt);
450
+ return this._serial(async () => {
451
+ const expiresAt = this._expiry();
452
+ await this._storage.set(this._key, { key: toBase64(dek), expiresAt });
453
+ zeroize2(dek);
454
+ await this._arm(expiresAt);
455
+ });
377
456
  }
378
457
  /** Forget the DEK and cancel the alarm. Safe to call when already locked. */
379
458
  async lock() {
380
- const was = await this._storage.get(this._key);
381
- await this._storage.remove(this._key);
382
- await this._alarms.clear(this._alarm);
383
- if (was !== void 0 && was !== null) for (const cb of this._onLock) cb();
459
+ return this._serial(() => this._lockNow());
384
460
  }
385
461
  async isUnlocked() {
386
- return await this._read() !== null;
462
+ return this._serial(async () => await this._read() !== null);
387
463
  }
388
464
  /**
389
465
  * The DEK for one operation (zeroize it after). `locked` when there is none or the
@@ -391,17 +467,22 @@ var Session = class {
391
467
  * a missed alarm cannot keep the key alive.
392
468
  */
393
469
  async getKey() {
394
- const record = await this._read();
395
- if (!record) throw new VaultError("Wallet is locked.", "locked");
396
- return fromBase64(record.key);
470
+ return this._serial(async () => {
471
+ const record = await this._read();
472
+ if (!record) throw new VaultError("Wallet is locked.", "locked");
473
+ return fromBase64(record.key);
474
+ });
397
475
  }
398
476
  /** User activity: push the auto-lock out again. No-op when locked. */
399
477
  async touch() {
400
- const record = await this._read();
401
- if (!record) return;
402
- const expiresAt = this._expiry();
403
- await this._storage.set(this._key, { ...record, expiresAt });
404
- await this._arm(expiresAt);
478
+ return this._serial(async () => {
479
+ const epoch = this._epoch;
480
+ const record = await this._read();
481
+ if (!record || epoch !== this._epoch) return;
482
+ const expiresAt = this._expiry();
483
+ await this._storage.set(this._key, { ...record, expiresAt });
484
+ await this._arm(expiresAt);
485
+ });
405
486
  }
406
487
  get autoLockMs() {
407
488
  return this._autoLockMs;
@@ -416,6 +497,19 @@ var Session = class {
416
497
  this._onLock.add(callback);
417
498
  return () => this._onLock.delete(callback);
418
499
  }
500
+ _serial(fn) {
501
+ const run = this._queue.then(fn);
502
+ this._queue = run.catch(() => void 0);
503
+ return run;
504
+ }
505
+ /** The lock itself; callers are already in the queue. */
506
+ async _lockNow() {
507
+ this._epoch++;
508
+ const was = await this._storage.get(this._key);
509
+ await this._storage.remove(this._key);
510
+ await this._alarms.clear(this._alarm);
511
+ if (was !== void 0 && was !== null) for (const cb of this._onLock) cb();
512
+ }
419
513
  _expiry() {
420
514
  return this._autoLockMs > 0 ? this._now() + this._autoLockMs : Number.POSITIVE_INFINITY;
421
515
  }
@@ -427,7 +521,7 @@ var Session = class {
427
521
  const raw = await this._storage.get(this._key);
428
522
  if (!raw || typeof raw.key !== "string" || typeof raw.expiresAt !== "number") return null;
429
523
  if (raw.expiresAt <= this._now()) {
430
- await this.lock();
524
+ await this._lockNow();
431
525
  return null;
432
526
  }
433
527
  return raw;
@@ -1074,6 +1168,7 @@ function memoryApprovalWindow() {
1074
1168
  }
1075
1169
 
1076
1170
  // src/router.ts
1171
+ import { UserRejectedError } from "@anywalletkit/core";
1077
1172
  var RouterError = class _RouterError extends Error {
1078
1173
  code;
1079
1174
  data;
@@ -1087,11 +1182,35 @@ var RouterError = class _RouterError extends Error {
1087
1182
  return new _RouterError(e.code, e.message, e.data);
1088
1183
  }
1089
1184
  };
1090
- function toRelayError(e) {
1091
- if (e instanceof RouterError) return e.data === void 0 ? { code: e.code, message: e.message } : { code: e.code, message: e.message, data: e.data };
1092
- if (e instanceof ReviewDeclined) return { code: e.code, message: e.message };
1093
- const code = typeof e?.code === "number" ? e.code : RELAY_ERRORS.internal;
1094
- return { code, message: e instanceof Error ? e.message : String(e) };
1185
+ var PROVIDER_CODES = /* @__PURE__ */ new Set([4001, 4100, 4200, 4900, 4901, 4902]);
1186
+ var isNodeCode = (code) => typeof code === "number" && Number.isInteger(code) && (code === 3 || code >= -32768 && code <= -32e3);
1187
+ var URL_RE = /\b[a-z][a-z0-9+.-]*:\/\/[^\s"'<>()]+/gi;
1188
+ var pageText = (text) => text.replace(URL_RE, "[url]").split("\n")[0].slice(0, 300);
1189
+ var isHexData = (v) => typeof v === "string" && /^0x[0-9a-fA-F]*$/.test(v) && v.length <= 2 + 2 * 8192;
1190
+ function nodeError(e) {
1191
+ for (let cur = e, depth = 0; cur && typeof cur === "object" && depth < 8; cur = cur.cause, depth++) {
1192
+ const o = cur;
1193
+ if (o.name === "RpcRequestError" && isNodeCode(o.code)) {
1194
+ const text = typeof o.details === "string" && o.details ? o.details : typeof o.shortMessage === "string" ? o.shortMessage : "RPC error";
1195
+ return { code: o.code, message: pageText(text), ...isHexData(o.data) ? { data: o.data } : {} };
1196
+ }
1197
+ }
1198
+ if (!(e instanceof Error) && typeof e === "object" && e !== null) {
1199
+ const o = e;
1200
+ if (isNodeCode(o.code) && typeof o.message === "string") return { code: o.code, message: pageText(o.message), ...isHexData(o.data) ? { data: o.data } : {} };
1201
+ }
1202
+ return void 0;
1203
+ }
1204
+ function toRelayError(e, options = {}) {
1205
+ if (e instanceof RouterError) return e.data === void 0 ? { code: e.code, message: pageText(e.message) } : { code: e.code, message: pageText(e.message), data: e.data };
1206
+ if (e instanceof ReviewDeclined) return { code: e.code, message: pageText(e.message) };
1207
+ if (e instanceof UserRejectedError) return { code: RELAY_ERRORS.userRejected, message: pageText(e.message) };
1208
+ const node = nodeError(e);
1209
+ if (node) return node;
1210
+ const code = e?.code;
1211
+ if (e instanceof Error && typeof code === "number" && PROVIDER_CODES.has(code)) return { code, message: pageText(e.message) };
1212
+ options.log?.(e);
1213
+ return { code: RELAY_ERRORS.internal, message: "Internal error" };
1095
1214
  }
1096
1215
  var READ_METHODS = /* @__PURE__ */ new Set([
1097
1216
  "eth_blockNumber",
@@ -1344,6 +1463,10 @@ function describeSiwe(text, ctx) {
1344
1463
  }
1345
1464
 
1346
1465
  // src/signing.ts
1466
+ var safeNonce = (n) => {
1467
+ if (n < 0n || n > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error(`The RPC returned an impossible nonce (${n}).`);
1468
+ return Number(n.toString());
1469
+ };
1347
1470
  var ADDRESS = /^0x[0-9a-fA-F]{40}$/;
1348
1471
  var HEX = /^0x([0-9a-fA-F]{2})*$/;
1349
1472
  var lc = (s) => s.toLowerCase();
@@ -1439,13 +1562,13 @@ async function prepareTransaction(deps, network, from, tx) {
1439
1562
  const base = block?.baseFeePerGas != null ? big(block.baseFeePerGas) : null;
1440
1563
  if (base === null) {
1441
1564
  const gasPrice = big(await deps.rpc(network, "eth_gasPrice", []));
1442
- return { chainId: network.chainId, nonce: Number(big(nonceHex)), gasLimit: hex(gasLimit), gasPrice: hex(gasPrice), maxFee: (gasLimit * gasPrice).toString() };
1565
+ return { chainId: network.chainId, nonce: safeNonce(big(nonceHex)), gasLimit: hex(gasLimit), gasPrice: hex(gasPrice), maxFee: (gasLimit * gasPrice).toString() };
1443
1566
  }
1444
1567
  const floor = deps.tipFloor?.(network.chainId) ?? MIN_TIP_WEI;
1445
1568
  const nodeTip = await deps.rpc(network, "eth_maxPriorityFeePerGas", []).then(big).catch(() => 0n);
1446
1569
  const tip = nodeTip > floor ? nodeTip : floor;
1447
1570
  const maxFeePerGas = base * 2n + tip;
1448
- return { chainId: network.chainId, nonce: Number(big(nonceHex)), gasLimit: hex(gasLimit), maxFeePerGas: hex(maxFeePerGas), maxPriorityFeePerGas: hex(tip), maxFee: (gasLimit * maxFeePerGas).toString() };
1571
+ return { chainId: network.chainId, nonce: safeNonce(big(nonceHex)), gasLimit: hex(gasLimit), maxFeePerGas: hex(maxFeePerGas), maxPriorityFeePerGas: hex(tip), maxFee: (gasLimit * maxFeePerGas).toString() };
1449
1572
  }
1450
1573
  function createSignHandler(deps) {
1451
1574
  return async ({ origin, network, method, params, accounts }) => {
@@ -2201,6 +2324,7 @@ export {
2201
2324
  TRON_PROVIDERS,
2202
2325
  TonConnectError,
2203
2326
  VAULT_DEFAULT_ITERATIONS,
2327
+ VAULT_LIMITS,
2204
2328
  VAULT_MIN_ITERATIONS,
2205
2329
  VAULT_MIN_PASSWORD_LENGTH,
2206
2330
  VAULT_MIN_SCRYPT,