@qntx/wallet-worker 0.5.2 → 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.
@@ -72,9 +72,6 @@ import { parentPort, workerData } from "node:worker_threads";
72
72
  var NostrError = class extends Error {
73
73
  name = "NostrError";
74
74
  };
75
- function errorMessage(error) {
76
- return error instanceof Error ? error.message : String(error);
77
- }
78
75
  var HexError = class extends NostrError {
79
76
  name = "HexError";
80
77
  };
@@ -1829,7 +1826,7 @@ class KernelError extends Error {
1829
1826
  this.corrupted = corrupted;
1830
1827
  }
1831
1828
  }
1832
- // ../wallet/dist/core-Dmz5SbyJ.mjs
1829
+ // ../wallet/dist/core-Cq5h9X0X.mjs
1833
1830
  var ValidationError = class extends Error {
1834
1831
  name = "ValidationError";
1835
1832
  };
@@ -1970,6 +1967,7 @@ var publicKey = str(64, /^[0-9a-f]{64}$/);
1970
1967
  var credentialId = str(1364, /^[A-Za-z0-9_-]+$/);
1971
1968
  var safeTime = int(0, Number.MAX_SAFE_INTEGER);
1972
1969
  var groupIndex = int(0, 2147483647);
1970
+ var identityIndex = int(0, 2147483646);
1973
1971
  var factor = union(obj({ kind: literal("hardware") }), obj({
1974
1972
  kind: literal("password"),
1975
1973
  password: bytes(1, 1024)
@@ -2196,7 +2194,7 @@ var CORE_METHODS = {
2196
2194
  }
2197
2195
  };
2198
2196
 
2199
- // ../wallet/dist/protocol-DvzlUKB8.mjs
2197
+ // ../wallet/dist/protocol-DbpLncEB.mjs
2200
2198
  var MAX_FACTOR_CHARS = 512;
2201
2199
  var MAX_STRING_BYTES = 64000;
2202
2200
  var MAX_PAYLOAD_CHARS = 1400000;
@@ -2218,15 +2216,11 @@ var backupEnvelope = obj({
2218
2216
  factors: arr(backupEnvelopeFactor, 5, 1),
2219
2217
  payload: str(MAX_PAYLOAD_CHARS)
2220
2218
  });
2221
- var backupHeader = obj({
2222
- wallets: arr(obj({
2223
- walletId: str(128),
2224
- origin: union(literal("mnemonic"), literal("passkey")),
2225
- words: union(literal(12), literal(15), literal(18), literal(21), literal(24))
2226
- }), 1000),
2227
- importedNostrKeyCount: int(0, 1e4),
2228
- importedChainKeyCount: int(0, 1e4)
2229
- });
2219
+ var backupHeader = obj({ wallets: arr(obj({
2220
+ walletId: str(128),
2221
+ origin: union(literal("mnemonic"), literal("passkey")),
2222
+ words: union(literal(12), literal(15), literal(18), literal(21), literal(24))
2223
+ }), 1000) });
2230
2224
  var backupEnvelopeText = str(MAX_ENVELOPE_CHARS);
2231
2225
  var backupInfo = obj({
2232
2226
  id: str(32, /^[0-9a-f]{32}$/),
@@ -2375,36 +2369,19 @@ var CHAINS_METHODS = {
2375
2369
  }
2376
2370
  };
2377
2371
  var hex128 = str(128, /^[0-9a-f]{128}$/);
2378
- var nostrKeyRef = union(obj({
2372
+ var nostrKeyRef = obj({
2379
2373
  kind: literal("derived"),
2380
2374
  walletId,
2381
- groupIndex
2382
- }), obj({
2383
- kind: literal("standalone"),
2384
- id: publicKey
2385
- }), obj({
2386
- kind: literal("nip46-client"),
2387
- id: publicKey
2388
- }));
2389
- var nostrKeyInfo = union(obj({
2390
- ref: nostrKeyRef,
2391
- publicKey,
2392
- origin: union(literal("nsec"), literal("ncryptsec"), literal("generated")),
2393
- backedUp: bool,
2394
- createdAt: safeTime
2395
- }), obj({
2375
+ index: identityIndex
2376
+ });
2377
+ var nostrKeyInfo = obj({
2396
2378
  ref: nostrKeyRef,
2397
2379
  publicKey,
2398
- origin: union(literal("derived"), literal("nip46-client")),
2399
2380
  createdAt: safeTime
2400
- }));
2401
- var nostrStatus = obj({ keys: arr(nostrKeyInfo, MAX_SNAPSHOT_ITEMS) });
2402
- var keyResult = obj({
2403
- key: nostrKeyInfo,
2404
- session: sessionId
2405
2381
  });
2382
+ var nostrStatus = obj({ keys: arr(nostrKeyInfo, MAX_SNAPSHOT_ITEMS) });
2406
2383
  var derivedNostrKey = obj({
2407
- groupIndex,
2384
+ index: identityIndex,
2408
2385
  publicKey
2409
2386
  });
2410
2387
  var tags = arr(arr(str(MAX_TAG_ITEM), 64), MAX_TAGS);
@@ -2432,14 +2409,6 @@ var rumor = obj({
2432
2409
  tags,
2433
2410
  content: str(MAX_TEXT)
2434
2411
  });
2435
- var nostrImportInput = union(obj({
2436
- kind: literal("nsec"),
2437
- nsec: bytes(1, 256)
2438
- }), obj({
2439
- kind: literal("ncryptsec"),
2440
- ncryptsec: str(512, /^ncryptsec1/),
2441
- password: bytes(1, 1024)
2442
- }));
2443
2412
  var nostrExportFormat = union(obj({ kind: literal("nsec") }), obj({
2444
2413
  kind: literal("ncryptsec"),
2445
2414
  password: bytes(1, 1024)
@@ -2456,7 +2425,7 @@ var NOSTR_METHODS = {
2456
2425
  presence: false,
2457
2426
  params: obj({
2458
2427
  walletId,
2459
- from: groupIndex,
2428
+ from: identityIndex,
2460
2429
  count: int(1, 20)
2461
2430
  }),
2462
2431
  result: obj({ keys: arr(derivedNostrKey, 20) })
@@ -2466,38 +2435,14 @@ var NOSTR_METHODS = {
2466
2435
  presence: false,
2467
2436
  params: obj({
2468
2437
  walletId,
2469
- groupIndex
2470
- }),
2471
- result: nostrKeyInfo
2472
- },
2473
- "nostr.importKey": {
2474
- session: false,
2475
- presence: true,
2476
- params: obj({
2477
- input: nostrImportInput,
2478
- factor: opt(factor)
2438
+ index: identityIndex
2479
2439
  }),
2480
- result: keyResult
2481
- },
2482
- "nostr.generateKey": {
2483
- session: false,
2484
- presence: true,
2485
- params: obj({ factor: opt(factor) }),
2486
- result: keyResult
2487
- },
2488
- "nostr.createNip46ClientKey": {
2489
- session: true,
2490
- presence: false,
2491
- params: nullValue,
2492
2440
  result: nostrKeyInfo
2493
2441
  },
2494
2442
  "nostr.removeKey": {
2495
2443
  session: true,
2496
2444
  presence: true,
2497
- params: obj({
2498
- publicKey,
2499
- verify: opt(factor)
2500
- }),
2445
+ params: obj({ publicKey }),
2501
2446
  result: nullValue
2502
2447
  },
2503
2448
  "nostr.exportKey": {
@@ -2551,6 +2496,39 @@ var NOSTR_METHODS = {
2551
2496
  events: arr(signedEvent, 64)
2552
2497
  }),
2553
2498
  result: obj({ results: arr(union(obj({ ok: rumor }), obj({ err: walletErrorCode })), 64) })
2499
+ },
2500
+ "nostr.syncKey": {
2501
+ session: true,
2502
+ presence: false,
2503
+ params: obj({ walletId }),
2504
+ result: obj({ publicKey })
2505
+ },
2506
+ "nostr.syncSign": {
2507
+ session: true,
2508
+ presence: false,
2509
+ params: obj({
2510
+ walletId,
2511
+ event: signEventInput
2512
+ }),
2513
+ result: signedEvent
2514
+ },
2515
+ "nostr.syncEncrypt": {
2516
+ session: true,
2517
+ presence: false,
2518
+ params: obj({
2519
+ walletId,
2520
+ text: str(MAX_TEXT)
2521
+ }),
2522
+ result: textResult
2523
+ },
2524
+ "nostr.syncDecrypt": {
2525
+ session: true,
2526
+ presence: false,
2527
+ params: obj({
2528
+ walletId,
2529
+ text: str(MAX_TEXT * 4)
2530
+ }),
2531
+ result: textResult
2554
2532
  }
2555
2533
  };
2556
2534
  var MAX_LEAVES = 256;
@@ -2736,6 +2714,27 @@ var decimalU128 = (value) => {
2736
2714
  throw new ValidationError;
2737
2715
  return value;
2738
2716
  };
2717
+ var outflowIntents = [
2718
+ obj({
2719
+ kind: literal("exit"),
2720
+ address: str(128),
2721
+ amountSats: satsAmount(),
2722
+ maxFeeSats: satsAmount()
2723
+ }),
2724
+ obj({
2725
+ kind: literal("onchain"),
2726
+ address: str(128),
2727
+ maxFeeSats: satsAmount()
2728
+ }),
2729
+ obj({
2730
+ kind: literal("token"),
2731
+ tokenIdentifier: bytes(32),
2732
+ receivers: arr(obj({
2733
+ ownerPublicKey: bytes(33),
2734
+ amount: decimalU128
2735
+ }), 256, 1)
2736
+ })
2737
+ ];
2739
2738
  var approveIntent = union(obj({
2740
2739
  kind: literal("transfer"),
2741
2740
  receivers: arr(obj({
@@ -2747,23 +2746,7 @@ var approveIntent = union(obj({
2747
2746
  invoice: str(4096),
2748
2747
  amountSats: opt(satsAmount()),
2749
2748
  maxFeeSats: satsAmount()
2750
- }), obj({
2751
- kind: literal("exit"),
2752
- address: str(128),
2753
- amountSats: satsAmount(),
2754
- maxFeeSats: satsAmount()
2755
- }), obj({
2756
- kind: literal("onchain"),
2757
- address: str(128),
2758
- maxFeeSats: satsAmount()
2759
- }), obj({
2760
- kind: literal("token"),
2761
- tokenIdentifier: bytes(32),
2762
- receivers: arr(obj({
2763
- ownerPublicKey: bytes(33),
2764
- amount: decimalU128
2765
- }), 256, 1)
2766
- }));
2749
+ }), ...outflowIntents);
2767
2750
  var approvalSummary = union(obj({
2768
2751
  kind: literal("transfer"),
2769
2752
  receivers: arr(obj({
@@ -2775,23 +2758,7 @@ var approvalSummary = union(obj({
2775
2758
  paymentHash: bytes(32),
2776
2759
  invoiceAmountSats: opt(satsAmount()),
2777
2760
  maxFeeSats: satsAmount()
2778
- }), obj({
2779
- kind: literal("exit"),
2780
- address: str(128),
2781
- amountSats: satsAmount(),
2782
- maxFeeSats: satsAmount()
2783
- }), obj({
2784
- kind: literal("onchain"),
2785
- address: str(128),
2786
- maxFeeSats: satsAmount()
2787
- }), obj({
2788
- kind: literal("token"),
2789
- tokenIdentifier: bytes(32),
2790
- receivers: arr(obj({
2791
- ownerPublicKey: bytes(33),
2792
- amount: decimalU128
2793
- }), 256, 1)
2794
- }));
2761
+ }), ...outflowIntents);
2795
2762
  var SPARK_METHODS = {
2796
2763
  "spark.identityPublicKey": {
2797
2764
  session: true,
@@ -2916,7 +2883,7 @@ var SPARK_METHODS = {
2916
2883
  result: approvalSummary
2917
2884
  }
2918
2885
  };
2919
- var PROTOCOL_VERSION = 1;
2886
+ var PROTOCOL_VERSION = 2;
2920
2887
  var UNATTRIBUTED_ID = 0;
2921
2888
  var MAX_REQUEST_BYTES = 1048576;
2922
2889
  function requestBytes(value) {
@@ -2966,7 +2933,7 @@ function isProtocolValue(value) {
2966
2933
  return Object.values(value).every(isProtocolValue);
2967
2934
  }
2968
2935
  var statusPayload = obj({
2969
- protocol: literal(1),
2936
+ protocol: literal(2),
2970
2937
  vault: union(literal("uninitialized"), literal("locked"), literal("unlocked"), literal("corrupted"), literal("unsupported"), literal("unavailable")),
2971
2938
  factors: arr(factorInfo, MAX_SNAPSHOT_ITEMS),
2972
2939
  wallets: arr(walletInfo, MAX_SNAPSHOT_ITEMS),
@@ -2991,11 +2958,11 @@ var errorPayload = obj({
2991
2958
  retryAt: opt(safeTime)
2992
2959
  });
2993
2960
  var kernelEvent = union(obj({
2994
- v: literal(1),
2961
+ v: literal(2),
2995
2962
  event: literal("status"),
2996
2963
  status: statusPayload
2997
2964
  }), obj({
2998
- v: literal(1),
2965
+ v: literal(2),
2999
2966
  event: literal("locked"),
3000
2967
  reason: lockReason
3001
2968
  }));
@@ -3929,14 +3896,8 @@ function asPublicKey(value) {
3929
3896
  }
3930
3897
  return value;
3931
3898
  }
3932
- function asGroupIndex(value) {
3933
- if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0 || value > 2147483647) {
3934
- throw headerFailure();
3935
- }
3936
- return value;
3937
- }
3938
- function asBoolean(value) {
3939
- if (typeof value !== "boolean") {
3899
+ function asIdentityIndex(value) {
3900
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0 || value > 2147483646) {
3940
3901
  throw headerFailure();
3941
3902
  }
3942
3903
  return value;
@@ -4250,7 +4211,7 @@ var RECORD_KIND_PATTERN = /^[a-z][a-z0-9-]{0,31}$/;
4250
4211
  function isRecordKind(kind) {
4251
4212
  return RECORD_KIND_PATTERN.test(kind);
4252
4213
  }
4253
- var VAULT_FORMAT = 2;
4214
+ var VAULT_FORMAT = 3;
4254
4215
 
4255
4216
  // ../wallet-kernel/src/vault/vault.ts
4256
4217
  var BACKOFF_MS = [30000, 60000, 300000];
@@ -6877,6 +6838,14 @@ async function seedFromMnemonic(mnemonic, passphrase, pbkdf2) {
6877
6838
  async function seedFromEntropy(entropy, pbkdf2) {
6878
6839
  return seedFromMnemonic(mnemonicFromEntropy(entropy), "", pbkdf2);
6879
6840
  }
6841
+ async function withSeed(pbkdf2, entropy, use) {
6842
+ const seed = await seedFromEntropy(entropy, pbkdf2);
6843
+ try {
6844
+ return await use(seed);
6845
+ } finally {
6846
+ wipe(seed);
6847
+ }
6848
+ }
6880
6849
 
6881
6850
  // ../wallet-kernel/src/session/session.ts
6882
6851
  function wipePendingFactor(pending) {
@@ -7050,6 +7019,14 @@ class Session {
7050
7019
  }
7051
7020
 
7052
7021
  // ../wallet-kernel/src/commands/vault.ts
7022
+ async function withWalletSeed(pbkdf2, dek, record, use) {
7023
+ const entropy = openRecord(dek, record);
7024
+ try {
7025
+ return await withSeed(pbkdf2, entropy, use);
7026
+ } finally {
7027
+ wipe(entropy);
7028
+ }
7029
+ }
7053
7030
  var EMPTY_IMAGE = { meta: undefined, records: [], factors: [] };
7054
7031
  async function unlockVault(ctx, params) {
7055
7032
  await ctx.reload();
@@ -7134,13 +7111,10 @@ async function removeFactor(ctx, params, requestSession) {
7134
7111
 
7135
7112
  // ../wallet-kernel/src/commands/wallets.ts
7136
7113
  async function deriveWalletMaterial(platform, entropy) {
7137
- const seed = await seedFromEntropy(entropy, platform.pbkdf2);
7138
- try {
7114
+ return withSeed(platform.pbkdf2, entropy, (seed) => {
7139
7115
  const master = masterKey(seed);
7140
7116
  return { master, walletId: walletIdOf(master) };
7141
- } finally {
7142
- wipe(seed);
7143
- }
7117
+ });
7144
7118
  }
7145
7119
  async function walletMaterial(platform, entropy, origin, words) {
7146
7120
  const { master, walletId } = await deriveWalletMaterial(platform, entropy);
@@ -7480,13 +7454,11 @@ function parseHeader(text) {
7480
7454
  return header;
7481
7455
  }
7482
7456
  var PAYLOAD_WALLET = 1;
7483
- var PAYLOAD_NOSTR = 2;
7484
- var PAYLOAD_CHAIN = 3;
7485
- function encodePayload(wallets, nostrSecrets) {
7486
- const size = 1 + wallets.reduce((acc, w) => acc + 3 + w.entropy.length, 0) + nostrSecrets.length * 33;
7457
+ function encodePayload(wallets) {
7458
+ const size = 1 + wallets.reduce((acc, w) => acc + 3 + w.entropy.length, 0);
7487
7459
  const out = new Uint8Array(size);
7488
7460
  let off = 0;
7489
- out[off++] = 1;
7461
+ out[off++] = 2;
7490
7462
  for (const w of wallets) {
7491
7463
  out[off++] = PAYLOAD_WALLET;
7492
7464
  out[off++] = ORIGIN_BYTE[w.origin] ?? 0;
@@ -7494,19 +7466,13 @@ function encodePayload(wallets, nostrSecrets) {
7494
7466
  out.set(w.entropy, off);
7495
7467
  off += w.entropy.length;
7496
7468
  }
7497
- for (const s of nostrSecrets) {
7498
- out[off++] = PAYLOAD_NOSTR;
7499
- out.set(s, off);
7500
- off += s.length;
7501
- }
7502
7469
  return out;
7503
7470
  }
7504
7471
  function decodePayload2(bytes) {
7505
- if (bytes.length === 0 || bytes[0] !== 1) {
7472
+ if (bytes.length === 0 || bytes[0] !== 2) {
7506
7473
  throw new KernelError("integrity", "backup: bad payload version");
7507
7474
  }
7508
7475
  const wallets = [];
7509
- const nostrSecrets = [];
7510
7476
  let off = 1;
7511
7477
  while (off < bytes.length) {
7512
7478
  const tag = bytes[off++];
@@ -7526,17 +7492,6 @@ function decodePayload2(bytes) {
7526
7492
  }
7527
7493
  wallets.push({ origin, entropy: bytes.slice(off, off + len) });
7528
7494
  off += len;
7529
- } else if (tag === PAYLOAD_NOSTR) {
7530
- if (off + 32 > bytes.length) {
7531
- throw new KernelError("integrity", "backup: truncated payload");
7532
- }
7533
- nostrSecrets.push(bytes.slice(off, off + 32));
7534
- off += 32;
7535
- } else if (tag === PAYLOAD_CHAIN) {
7536
- if (off + 33 > bytes.length) {
7537
- throw new KernelError("integrity", "backup: truncated payload");
7538
- }
7539
- off += 33;
7540
7495
  } else {
7541
7496
  throw new KernelError("integrity", "backup: unknown payload tag");
7542
7497
  }
@@ -7544,7 +7499,7 @@ function decodePayload2(bytes) {
7544
7499
  if (off !== bytes.length) {
7545
7500
  throw new KernelError("integrity", "backup: payload length mismatch");
7546
7501
  }
7547
- return { wallets, nostrSecrets };
7502
+ return { wallets };
7548
7503
  }
7549
7504
  function collectContents(ctx, dek) {
7550
7505
  const wallets = [];
@@ -7567,13 +7522,10 @@ function collectContents(ctx, dek) {
7567
7522
  throw error;
7568
7523
  }
7569
7524
  }
7570
- const nostrSecrets = ctx.modules.flatMap((module) => module.backupSecrets(ctx.image, dek));
7571
7525
  const header = JSON.stringify({
7572
- wallets: wallets.map(({ walletId, origin, words }) => ({ walletId, origin, words })),
7573
- importedNostrKeyCount: nostrSecrets.length,
7574
- importedChainKeyCount: 0
7526
+ wallets: wallets.map(({ walletId, origin, words }) => ({ walletId, origin, words }))
7575
7527
  });
7576
- return { wallets, nostrSecrets, header };
7528
+ return { wallets, header };
7577
7529
  } catch (error) {
7578
7530
  for (const wallet of wallets) {
7579
7531
  wallet.entropy.fill(0);
@@ -7585,9 +7537,6 @@ function wipeContents(contents) {
7585
7537
  for (const wallet of contents.wallets) {
7586
7538
  wallet.entropy.fill(0);
7587
7539
  }
7588
- for (const secretKey of contents.nostrSecrets) {
7589
- secretKey.fill(0);
7590
- }
7591
7540
  }
7592
7541
  var PURPOSE_PROTECTION = 2;
7593
7542
  function backupMaterialOf(result) {
@@ -7710,7 +7659,7 @@ function updateBackup(ctx, _params, requestSession) {
7710
7659
  const bek = openRecord(dek, record);
7711
7660
  const contents = collectContents(ctx, dek);
7712
7661
  try {
7713
- const plaintext = encodePayload(contents.wallets, contents.nostrSecrets);
7662
+ const plaintext = encodePayload(contents.wallets);
7714
7663
  const sealedPayload = seal(bek, plaintext, payloadContext(contents.header), "", ctx.platform.random);
7715
7664
  try {
7716
7665
  const envelope = {
@@ -7780,7 +7729,7 @@ async function restoreBackup(ctx, params, requestSession) {
7780
7729
  }
7781
7730
  const header = parseHeader(envelope.header);
7782
7731
  const decoded = decodePayload2(plaintext);
7783
- if (decoded.nostrSecrets.length !== header.importedNostrKeyCount || decoded.wallets.length !== header.wallets.length) {
7732
+ if (decoded.wallets.length !== header.wallets.length) {
7784
7733
  plaintext.fill(0);
7785
7734
  throw new KernelError("integrity", "backup: payload does not match header");
7786
7735
  }
@@ -7812,9 +7761,6 @@ async function restoreBackup(ctx, params, requestSession) {
7812
7761
  records.push(record);
7813
7762
  restored.push(walletInfoOf(material, backup, now));
7814
7763
  }
7815
- for (const module of ctx.modules) {
7816
- records.push(...module.backupRecords(ctx.platform, ctx.image, gated.dek, decoded.nostrSecrets, now));
7817
- }
7818
7764
  if (gated.uninitialized) {
7819
7765
  records.push(backupKeyRecord(ctx, gated.dek, bek, envelope.factors));
7820
7766
  }
@@ -7832,9 +7778,6 @@ async function restoreBackup(ctx, params, requestSession) {
7832
7778
  for (const wallet of decoded.wallets) {
7833
7779
  wallet.entropy.fill(0);
7834
7780
  }
7835
- for (const secretKey of decoded.nostrSecrets) {
7836
- secretKey.fill(0);
7837
- }
7838
7781
  }
7839
7782
  } finally {
7840
7783
  bek.fill(0);
@@ -8034,7 +7977,30 @@ var ChainError = class extends Error {
8034
7977
  }
8035
7978
  };
8036
7979
 
8037
- // ../wallet-chains/dist/message-DtIS0ZWB.mjs
7980
+ // ../wallet-chains/dist/account-ref-C2qrDbVT.mjs
7981
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7982
+ var isAccountRef = (value) => {
7983
+ if (!isRecord2(value) || typeof value["kind"] !== "string")
7984
+ return false;
7985
+ if (value["kind"] === "hd")
7986
+ return typeof value["walletId"] === "string" && typeof value["groupIndex"] === "number" && typeof value["template"] === "string";
7987
+ return value["kind"] === "imported" && typeof value["keyId"] === "string" && typeof value["family"] === "string";
7988
+ };
7989
+ var requestBase = (request) => {
7990
+ if (!isRecord2(request))
7991
+ throw new ChainError("invalid-request", "request must be an object");
7992
+ const { network, account } = request;
7993
+ if (typeof network !== "string")
7994
+ throw new ChainError("invalid-request", "request.network must be a string");
7995
+ if (!isAccountRef(account))
7996
+ throw new ChainError("invalid-request", "request.account must be an AccountRef");
7997
+ return {
7998
+ network,
7999
+ account
8000
+ };
8001
+ };
8002
+
8003
+ // ../wallet-chains/dist/message-DuJ5KSRT.mjs
8038
8004
  var MAINNET_BIP32 = {
8039
8005
  public: 76067358,
8040
8006
  private: 76066276
@@ -8185,15 +8151,15 @@ var signLegacyMessage = (message, privateKey) => {
8185
8151
  };
8186
8152
 
8187
8153
  // ../wallet-chains/dist/bitcoin.mjs
8188
- var isRecord$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8154
+ var isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8189
8155
  var derivationPaths = (map) => {
8190
- const entries = map instanceof Map ? [...map.values()] : Array.isArray(map) ? map : isRecord$2(map) ? Object.values(map) : [];
8156
+ const entries = map instanceof Map ? [...map.values()] : Array.isArray(map) ? map : isRecord$1(map) ? Object.values(map) : [];
8191
8157
  const paths = [];
8192
8158
  for (const entry of entries) {
8193
8159
  const value = Array.isArray(entry) ? entry[1] : entry;
8194
- if (!isRecord$2(value))
8160
+ if (!isRecord$1(value))
8195
8161
  continue;
8196
- const { path } = isRecord$2(value["der"]) ? value["der"] : value;
8162
+ const { path } = isRecord$1(value["der"]) ? value["der"] : value;
8197
8163
  if (Array.isArray(path) && path.every((p) => typeof p === "number"))
8198
8164
  paths.push(path);
8199
8165
  }
@@ -8332,26 +8298,10 @@ var decodeBitcoinPsbt = (request, ownScripts = /* @__PURE__ */ new Set) => {
8332
8298
  verifiability
8333
8299
  };
8334
8300
  };
8335
- var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8336
- var isAccountRef = (value) => {
8337
- if (!isRecord2(value) || typeof value["kind"] !== "string")
8338
- return false;
8339
- if (value["kind"] === "hd")
8340
- return typeof value["walletId"] === "string" && typeof value["groupIndex"] === "number" && typeof value["template"] === "string";
8341
- return value["kind"] === "imported" && typeof value["keyId"] === "string" && typeof value["family"] === "string";
8342
- };
8343
8301
  var common = (request) => {
8344
- const { network } = request;
8345
- const { account } = request;
8346
- if (typeof network !== "string")
8347
- throw new ChainError("invalid-request", "request.network must be a string");
8348
- if (!isAccountRef(account))
8349
- throw new ChainError("invalid-request", "request.account must be an AccountRef");
8350
- bitcoinNetwork(network);
8351
- return {
8352
- network,
8353
- account
8354
- };
8302
+ const base = requestBase(request);
8303
+ bitcoinNetwork(base.network);
8304
+ return base;
8355
8305
  };
8356
8306
  var messageTarget = (address, network, format) => {
8357
8307
  if (typeof address !== "string")
@@ -8421,26 +8371,6 @@ var normalizeBitcoin = (request) => {
8421
8371
  };
8422
8372
  var decodeMessageTarget = (request) => messageTarget(request.address, request.network, request.format);
8423
8373
 
8424
- // ../wallet-chains/dist/bitcoin-sign.mjs
8425
- var signBitcoinPsbt = (psbt, inputs, auxRand) => {
8426
- const tx = Transaction.fromPSBT(psbt);
8427
- for (const { index, privateKey } of inputs) {
8428
- if (privateKey.length !== 32)
8429
- throw new ChainError("invalid-request", `input ${index}: private key must be 32 bytes`);
8430
- if (!tx.signIdx(privateKey, index, undefined, auxRand(index)))
8431
- throw new ChainError("invalid-request", `input ${index}: key cannot sign`);
8432
- }
8433
- return tx.toPSBT(0);
8434
- };
8435
- var signBitcoinMessage = (request, privateKey, auxRand) => {
8436
- if (privateKey.length !== 32)
8437
- throw new ChainError("invalid-request", "private key must be 32 bytes");
8438
- if (request.format === "legacy")
8439
- return signLegacyMessage(request.message, privateKey);
8440
- const scriptPubKey = OutScript.encode(Address(bitcoinNetwork(request.network).params).decode(request.address));
8441
- return signBip322Simple(request.message, scriptPubKey, privateKey, auxRand);
8442
- };
8443
-
8444
8374
  // ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/sha3.js
8445
8375
  var _0n4 = BigInt(0);
8446
8376
  var _1n4 = BigInt(1);
@@ -8664,15 +8594,15 @@ class Keccak {
8664
8594
  var genKeccak = (suffix, blockLen, outputLen, info = {}) => createHasher(() => new Keccak(blockLen, suffix, outputLen), info);
8665
8595
  var keccak_256 = /* @__PURE__ */ genKeccak(1, 136, 32);
8666
8596
 
8667
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/version.js
8597
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/version.js
8668
8598
  var version = "0.1.1";
8669
8599
 
8670
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/errors.js
8600
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/errors.js
8671
8601
  function getVersion() {
8672
8602
  return version;
8673
8603
  }
8674
8604
 
8675
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Errors.js
8605
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Errors.js
8676
8606
  class BaseError extends Error {
8677
8607
  details;
8678
8608
  docs;
@@ -8752,7 +8682,7 @@ function walk(err, fn) {
8752
8682
  return fn ? null : err;
8753
8683
  }
8754
8684
 
8755
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/bytes.js
8685
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/bytes.js
8756
8686
  function assertSize(bytes, size_) {
8757
8687
  if (size(bytes) > size_)
8758
8688
  throw new SizeOverflowError({
@@ -8821,7 +8751,7 @@ function trim(value, options = {}) {
8821
8751
  return start === 0 && end === value.length ? value : value.slice(start, end);
8822
8752
  }
8823
8753
 
8824
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/codec/hex.js
8754
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/codec/hex.js
8825
8755
  var hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_v, i) => i.toString(16).padStart(2, "0"));
8826
8756
  var nibbleTable = /* @__PURE__ */ (() => {
8827
8757
  const table = new Uint8Array(256).fill(255);
@@ -8909,7 +8839,7 @@ class InvalidLengthError extends BaseError {
8909
8839
  }
8910
8840
  }
8911
8841
 
8912
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/codec/int.js
8842
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/codec/int.js
8913
8843
  function bytesToBigInt(value, signed) {
8914
8844
  const len = value.length;
8915
8845
  if (len === 0)
@@ -8966,11 +8896,11 @@ class IntegerOutOfRangeError extends BaseError {
8966
8896
  }
8967
8897
  }
8968
8898
 
8969
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/codec/utf8.js
8899
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/codec/utf8.js
8970
8900
  var encoder = /* @__PURE__ */ new TextEncoder;
8971
8901
  var decoder = /* @__PURE__ */ new TextDecoder;
8972
8902
 
8973
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Json.js
8903
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Json.js
8974
8904
  var bigIntSuffix = "#__bigint";
8975
8905
  function stringify(value, replacer, space) {
8976
8906
  return JSON.stringify(value, (key, value) => {
@@ -8982,9 +8912,9 @@ function stringify(value, replacer, space) {
8982
8912
  }, space);
8983
8913
  }
8984
8914
 
8985
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Hex.js
8915
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Hex.js
8986
8916
  function assert(value, options = {}) {
8987
- const { strict = false } = options;
8917
+ const { even = false, strict = false } = options;
8988
8918
  if (typeof value !== "string")
8989
8919
  throw new InvalidHexTypeError(value);
8990
8920
  if (strict) {
@@ -8993,6 +8923,8 @@ function assert(value, options = {}) {
8993
8923
  }
8994
8924
  if (!value.startsWith("0x"))
8995
8925
  throw new InvalidHexValueError2(value);
8926
+ if (even && value.length % 2 !== 0)
8927
+ throw new InvalidLengthError2(value);
8996
8928
  }
8997
8929
  function concat2(...values) {
8998
8930
  if (values.length === 0)
@@ -9116,9 +9048,9 @@ function toBytes(hex, options = {}) {
9116
9048
  return hexToBytes4(value);
9117
9049
  }
9118
9050
  function validate2(value, options = {}) {
9119
- const { strict = false } = options;
9051
+ const { even = false, strict = false } = options;
9120
9052
  try {
9121
- assert(value, { strict });
9053
+ assert(value, { even, strict });
9122
9054
  return true;
9123
9055
  } catch {
9124
9056
  return false;
@@ -9134,6 +9066,8 @@ class InvalidHexTypeError extends BaseError {
9134
9066
  }
9135
9067
  }
9136
9068
  var InvalidHexValueError2 = InvalidHexValueError;
9069
+ var InvalidLengthError2 = InvalidLengthError;
9070
+
9137
9071
  class SizeOverflowError2 extends BaseError {
9138
9072
  name = "Hex.SizeOverflowError";
9139
9073
  constructor({ givenSize, maxSize }) {
@@ -9155,7 +9089,7 @@ class SizeExceedsPaddingSizeError2 extends BaseError {
9155
9089
  }
9156
9090
  }
9157
9091
 
9158
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/hex.js
9092
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/hex.js
9159
9093
  function assertSize2(hex, size_) {
9160
9094
  const len = hex.length - 2;
9161
9095
  const size = len + 1 >> 1;
@@ -9216,7 +9150,7 @@ function trim2(value, options = {}) {
9216
9150
  return `0x${data.slice(start, end)}`;
9217
9151
  }
9218
9152
 
9219
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Bytes.js
9153
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Bytes.js
9220
9154
  function from2(value) {
9221
9155
  if (value instanceof Uint8Array)
9222
9156
  return value;
@@ -9327,7 +9261,7 @@ class SizeExceedsPaddingSizeError extends BaseError {
9327
9261
  }
9328
9262
  }
9329
9263
 
9330
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/lru.js
9264
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/lru.js
9331
9265
  class BoundedMap extends Map {
9332
9266
  maxSize;
9333
9267
  constructor(size) {
@@ -9344,20 +9278,20 @@ class BoundedMap extends Map {
9344
9278
  }
9345
9279
  }
9346
9280
 
9347
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Caches.js
9281
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Caches.js
9348
9282
  var caches = {
9349
9283
  checksum: /* @__PURE__ */ new BoundedMap(32768)
9350
9284
  };
9351
9285
  var checksum = caches.checksum;
9352
9286
 
9353
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/engine.js
9287
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/engine.js
9354
9288
  var overrides = {};
9355
9289
 
9356
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/hash.js
9290
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/hash.js
9357
9291
  var keccak256Default = (input) => keccak_256(input);
9358
9292
  var keccak256 = (input) => (overrides.Hash?.keccak256 ?? keccak256Default)(input);
9359
9293
 
9360
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Hash.js
9294
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Hash.js
9361
9295
  function keccak2562(value, options = {}) {
9362
9296
  const isBytes = value instanceof Uint8Array;
9363
9297
  const { as = isBytes ? "Bytes" : "Hex" } = options;
@@ -9367,7 +9301,7 @@ function keccak2562(value, options = {}) {
9367
9301
  return fromBytes(bytes);
9368
9302
  }
9369
9303
 
9370
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/address.js
9304
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/address.js
9371
9305
  var length = 42;
9372
9306
  var ZERO2 = 48;
9373
9307
  var NINE = 57;
@@ -9420,7 +9354,7 @@ function lowercase(value) {
9420
9354
  return String.fromCharCode.apply(null, out);
9421
9355
  }
9422
9356
 
9423
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Address.js
9357
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Address.js
9424
9358
  function assert2(value, options = {}) {
9425
9359
  const { strict = true } = options;
9426
9360
  const kind = classify(value);
@@ -9500,7 +9434,7 @@ class InvalidChecksumError extends BaseError {
9500
9434
  }
9501
9435
  }
9502
9436
 
9503
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/regex.js
9437
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/regex.js
9504
9438
  function execTyped(regex, string) {
9505
9439
  const match = regex.exec(string);
9506
9440
  return match?.groups;
@@ -9509,7 +9443,7 @@ var bytesRegex = /^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/;
9509
9443
  var integerRegex = /^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/;
9510
9444
  var isTupleRegex = /^\(.+?\).*?$/;
9511
9445
 
9512
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/formatAbiParameter.js
9446
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/formatAbiParameter.js
9513
9447
  var tupleRegex = /^tuple(?<array>(\[(\d*)\])*)$/;
9514
9448
  function formatAbiParameter(abiParameter) {
9515
9449
  let type = abiParameter.type;
@@ -9536,7 +9470,7 @@ function formatAbiParameter(abiParameter) {
9536
9470
  return type;
9537
9471
  }
9538
9472
 
9539
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/formatAbiParameters.js
9473
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/formatAbiParameters.js
9540
9474
  function formatAbiParameters(abiParameters) {
9541
9475
  let params = "";
9542
9476
  const length = abiParameters.length;
@@ -9549,7 +9483,7 @@ function formatAbiParameters(abiParameters) {
9549
9483
  return params;
9550
9484
  }
9551
9485
 
9552
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/errors.js
9486
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/errors.js
9553
9487
  class InvalidAbiItemError extends BaseError {
9554
9488
  name = "AbiItem.InvalidAbiItemError";
9555
9489
  constructor({ signature }) {
@@ -9684,7 +9618,7 @@ class CircularReferenceError extends BaseError {
9684
9618
  }
9685
9619
  }
9686
9620
 
9687
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/runtime/signatures.js
9621
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/runtime/signatures.js
9688
9622
  var errorSignatureRegex = /^error (?<name>[a-zA-Z$_][a-zA-Z0-9$_]*)\((?<parameters>.*?)\)$/;
9689
9623
  function isErrorSignature(signature) {
9690
9624
  return errorSignatureRegex.test(signature);
@@ -9744,7 +9678,7 @@ var functionModifiers = new Set([
9744
9678
  "storage"
9745
9679
  ]);
9746
9680
 
9747
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/runtime/cache.js
9681
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/runtime/cache.js
9748
9682
  function getParameterCacheKey(param, type, structs) {
9749
9683
  let structKey = "";
9750
9684
  if (structs)
@@ -9813,7 +9747,7 @@ var parameterCache = new Map([
9813
9747
  ]
9814
9748
  ]);
9815
9749
 
9816
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/runtime/utils.js
9750
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/runtime/utils.js
9817
9751
  function parseSignature(signature, structs = {}) {
9818
9752
  if (isFunctionSignature(signature))
9819
9753
  return parseFunctionSignature(signature, structs);
@@ -10012,7 +9946,7 @@ function isValidDataLocation(type, isArray) {
10012
9946
  return isArray || type === "bytes" || type === "string" || type === "tuple";
10013
9947
  }
10014
9948
 
10015
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/runtime/structs.js
9949
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/runtime/structs.js
10016
9950
  function parseStructs(signatures) {
10017
9951
  const shallowStructs = {};
10018
9952
  const signaturesLength = signatures.length;
@@ -10082,7 +10016,7 @@ function resolveStructs(abiParameters = [], structs = {}, ancestors = new Set) {
10082
10016
  return components;
10083
10017
  }
10084
10018
 
10085
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Solidity.js
10019
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Solidity.js
10086
10020
  var arrayRegex = /^(.*)\[([0-9]*)\]$/;
10087
10021
  var bytesRegex2 = /^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/;
10088
10022
  var integerRegex2 = /^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/;
@@ -10183,7 +10117,7 @@ var maxUint240 = 2n ** 240n - 1n;
10183
10117
  var maxUint248 = 2n ** 248n - 1n;
10184
10118
  var maxUint256 = 2n ** 256n - 1n;
10185
10119
 
10186
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/abiParameters.js
10120
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/abiParameters.js
10187
10121
  function decodeParameter(cursor, param, options) {
10188
10122
  const { checksumAddress, staticPosition } = options;
10189
10123
  const arrayComponents = getArrayComponents(param.type);
@@ -10725,7 +10659,7 @@ function hasDynamicChild(param) {
10725
10659
  return false;
10726
10660
  }
10727
10661
 
10728
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/cursor.js
10662
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/cursor.js
10729
10663
  var staticCursor = {
10730
10664
  bytes: new Uint8Array,
10731
10665
  dataView: new DataView(new ArrayBuffer(0)),
@@ -10916,7 +10850,7 @@ class RecursiveReadLimitExceededError extends BaseError {
10916
10850
  }
10917
10851
  }
10918
10852
 
10919
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/AbiParameters.js
10853
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/AbiParameters.js
10920
10854
  function decode2(parameters, data, options = {}) {
10921
10855
  const { as = "Array", checksumAddress = true } = options;
10922
10856
  const bytes = typeof data === "string" ? fromHex(data) : data;
@@ -11092,7 +11026,7 @@ class InvalidTypeError extends BaseError {
11092
11026
  }
11093
11027
  }
11094
11028
 
11095
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/TypedData.js
11029
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/TypedData.js
11096
11030
  function assert3(value) {
11097
11031
  const { domain, message, primaryType, types } = value;
11098
11032
  const validateValue = (type, value, name) => {
@@ -11204,7 +11138,7 @@ function encodeType(value) {
11204
11138
  function extractEip712DomainTypes(domain) {
11205
11139
  return [
11206
11140
  typeof domain?.name === "string" && { name: "name", type: "string" },
11207
- domain?.version && { name: "version", type: "string" },
11141
+ typeof domain?.version === "string" && { name: "version", type: "string" },
11208
11142
  (typeof domain?.chainId === "number" || typeof domain?.chainId === "bigint") && {
11209
11143
  name: "chainId",
11210
11144
  type: "uint256"
@@ -11387,7 +11321,7 @@ function validateReference(type) {
11387
11321
  throw new InvalidStructTypeError({ type });
11388
11322
  }
11389
11323
 
11390
- // ../wallet-chains/dist/typed-data-C6XANSQF.mjs
11324
+ // ../wallet-chains/dist/typed-data-Cf1g7JVv.mjs
11391
11325
  var isEvmAddress = (value) => /^0x[0-9a-fA-F]{40}$/.test(value);
11392
11326
  function checksumEvmAddress(address) {
11393
11327
  if (!isEvmAddress(address))
@@ -11594,7 +11528,7 @@ var typedDataWarnings = (parsed) => parsed.chainIdMismatch ? [{
11594
11528
  message: "typed-data domain.chainId does not match the request network"
11595
11529
  }] : [];
11596
11530
 
11597
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/formatAbiItem.js
11531
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/formatAbiItem.js
11598
11532
  function formatAbiItem(abiItem) {
11599
11533
  if (abiItem.type === "function")
11600
11534
  return `function ${abiItem.name}(${formatAbiParameters(abiItem.inputs)})${abiItem.stateMutability && abiItem.stateMutability !== "nonpayable" ? ` ${abiItem.stateMutability}` : ""}${abiItem.outputs?.length ? ` returns (${formatAbiParameters(abiItem.outputs)})` : ""}`;
@@ -11609,7 +11543,7 @@ function formatAbiItem(abiItem) {
11609
11543
  return "receive() external payable";
11610
11544
  }
11611
11545
 
11612
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/parseAbiItem.js
11546
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/human-readable/parseAbiItem.js
11613
11547
  function parseAbiItem(signature) {
11614
11548
  let abiItem;
11615
11549
  if (typeof signature === "string")
@@ -11630,7 +11564,7 @@ function parseAbiItem(signature) {
11630
11564
  return abiItem;
11631
11565
  }
11632
11566
 
11633
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/abiItem.js
11567
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/abiItem.js
11634
11568
  function normalizeSignature(signature) {
11635
11569
  let active = true;
11636
11570
  let current = "";
@@ -11675,8 +11609,6 @@ function normalizeSignature(signature) {
11675
11609
  }
11676
11610
  var integerTypeRegex = /^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/;
11677
11611
  var fixedBytesTypeRegex = /^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/;
11678
- var arrayTypeRegex = /[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/;
11679
- var arraySuffixStripRegex = /(\[[0-9]{0,}\])$/;
11680
11612
  function isArgOfType(arg, abiParameter) {
11681
11613
  const argType = typeof arg;
11682
11614
  const abiParameterType = abiParameter.type;
@@ -11692,16 +11624,18 @@ function isArgOfType(arg, abiParameter) {
11692
11624
  default: {
11693
11625
  if (abiParameterType === "tuple" && "components" in abiParameter)
11694
11626
  return Object.values(abiParameter.components).every((component, index) => {
11695
- return isArgOfType(Object.values(arg)[index], component);
11627
+ return argType === "object" && isArgOfType(getComponentValue(arg, component, index), component);
11696
11628
  });
11697
11629
  if (integerTypeRegex.test(abiParameterType))
11698
11630
  return argType === "number" || argType === "bigint";
11699
11631
  if (fixedBytesTypeRegex.test(abiParameterType))
11700
11632
  return argType === "string" || arg instanceof Uint8Array;
11701
- if (arrayTypeRegex.test(abiParameterType)) {
11702
- return Array.isArray(arg) && arg.every((x) => isArgOfType(x, {
11633
+ const arrayComponents = getArrayComponents(abiParameterType);
11634
+ if (arrayComponents) {
11635
+ const [length, type] = arrayComponents;
11636
+ return Array.isArray(arg) && (length === null || arg.length === length) && arg.every((x) => isArgOfType(x, {
11703
11637
  ...abiParameter,
11704
- type: abiParameterType.replace(arraySuffixStripRegex, "")
11638
+ type
11705
11639
  }));
11706
11640
  }
11707
11641
  return false;
@@ -11712,8 +11646,33 @@ function getAmbiguousTypes(sourceParameters, targetParameters, args) {
11712
11646
  for (let parameterIndex = 0;parameterIndex < sourceParameters.length; parameterIndex++) {
11713
11647
  const sourceParameter = sourceParameters[parameterIndex];
11714
11648
  const targetParameter = targetParameters[parameterIndex];
11715
- if (sourceParameter.type === "tuple" && targetParameter.type === "tuple" && "components" in sourceParameter && "components" in targetParameter)
11716
- return getAmbiguousTypes(sourceParameter.components, targetParameter.components, args[parameterIndex]);
11649
+ if (!targetParameter)
11650
+ continue;
11651
+ if (sourceParameter.type === "tuple" && targetParameter.type === "tuple" && "components" in sourceParameter && "components" in targetParameter) {
11652
+ const value = args[parameterIndex];
11653
+ const types = getAmbiguousTypes(sourceParameter.components, targetParameter.components, sourceParameter.components.map((component, index) => getComponentValue(value, component, index)));
11654
+ if (types)
11655
+ return types;
11656
+ continue;
11657
+ }
11658
+ const sourceArray = getArrayComponents(sourceParameter.type);
11659
+ const targetArray = getArrayComponents(targetParameter.type);
11660
+ if (sourceArray && targetArray) {
11661
+ const types = getAmbiguousTypes([
11662
+ {
11663
+ ...sourceParameter,
11664
+ type: sourceArray[1]
11665
+ }
11666
+ ], [
11667
+ {
11668
+ ...targetParameter,
11669
+ type: targetArray[1]
11670
+ }
11671
+ ], [args[parameterIndex]?.[0]]);
11672
+ if (types)
11673
+ return types;
11674
+ continue;
11675
+ }
11717
11676
  const sourceType = sourceParameter.type;
11718
11677
  const targetType = targetParameter.type;
11719
11678
  const arg = args[parameterIndex];
@@ -11721,14 +11680,19 @@ function getAmbiguousTypes(sourceParameters, targetParameters, args) {
11721
11680
  if (sourceType === "address" && targetType === "bytes20" || sourceType === "bytes20" && targetType === "address")
11722
11681
  ambiguous = true;
11723
11682
  else if (sourceType === "address" && targetType === "string" || sourceType === "string" && targetType === "address" || sourceType === "address" && targetType === "bytes" || sourceType === "bytes" && targetType === "address")
11724
- ambiguous = validate3(arg, { strict: false });
11683
+ ambiguous = arg === undefined || validate3(arg, { strict: false });
11725
11684
  if (ambiguous)
11726
11685
  return [sourceType, targetType];
11727
11686
  }
11728
11687
  return;
11729
11688
  }
11689
+ function getComponentValue(arg, component, index) {
11690
+ if (Array.isArray(arg))
11691
+ return arg[index];
11692
+ return arg?.[component.name];
11693
+ }
11730
11694
 
11731
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/AbiItem.js
11695
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/AbiItem.js
11732
11696
  function from3(abiItem, options = {}) {
11733
11697
  const { prepare = true } = options;
11734
11698
  const item = (() => {
@@ -11896,7 +11860,7 @@ class InvalidSelectorSizeError extends BaseError {
11896
11860
  }
11897
11861
  }
11898
11862
 
11899
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/AbiFunction.js
11863
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/AbiFunction.js
11900
11864
  function decodeData(...parameters) {
11901
11865
  const [abiFunction, data, options] = (() => {
11902
11866
  if (Array.isArray(parameters[0])) {
@@ -11958,7 +11922,7 @@ function getSelector2(abiItem) {
11958
11922
  return getSelector(abiItem);
11959
11923
  }
11960
11924
 
11961
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/PersonalMessage.js
11925
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/PersonalMessage.js
11962
11926
  var erc191PrefixHex = "0x19" + fromString(`Ethereum Signed Message:
11963
11927
  `).slice(2);
11964
11928
  function encode3(data) {
@@ -11969,7 +11933,7 @@ function getSignPayload2(data) {
11969
11933
  return keccak2562(encode3(data));
11970
11934
  }
11971
11935
 
11972
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Signature.js
11936
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Signature.js
11973
11937
  function assert4(signature, options = {}) {
11974
11938
  const { recovered } = options;
11975
11939
  if (typeof signature.r === "undefined")
@@ -12093,8 +12057,8 @@ function fromRecoveredBytes(bytes) {
12093
12057
  }
12094
12058
  function toHex(signature) {
12095
12059
  assert4(signature);
12096
- const r = signature.r;
12097
- const s = signature.s;
12060
+ const r = padLeft(signature.r, 32);
12061
+ const s = padLeft(signature.s, 32);
12098
12062
  const signature_ = concat2(r, s, typeof signature.yParity === "number" ? fromNumber(yParityToV(signature.yParity), { size: 1 }) : "0x");
12099
12063
  return signature_;
12100
12064
  }
@@ -12166,7 +12130,7 @@ class InvalidVError extends BaseError {
12166
12130
  }
12167
12131
  }
12168
12132
 
12169
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/cryptoIo.js
12133
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/cryptoIo.js
12170
12134
  function formatSignature(signature, as) {
12171
12135
  if (as === "Hex")
12172
12136
  return toHex(signature);
@@ -12175,7 +12139,7 @@ function formatSignature(signature, as) {
12175
12139
  return signature;
12176
12140
  }
12177
12141
 
12178
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/secp256k1.js
12142
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/secp256k1.js
12179
12143
  var signDefault = (payload, privateKey, options) => secp256k1.sign(payload, privateKey, {
12180
12144
  ...options,
12181
12145
  format: "recovered",
@@ -12183,9 +12147,9 @@ var signDefault = (payload, privateKey, options) => secp256k1.sign(payload, priv
12183
12147
  });
12184
12148
  var sign = (payload, privateKey, options) => (overrides.Secp256k1?.sign ?? signDefault)(payload, privateKey, options);
12185
12149
 
12186
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/entropy.js
12150
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/entropy.js
12187
12151
  var extraEntropy = false;
12188
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Secp256k1.js
12152
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Secp256k1.js
12189
12153
  function sign2(options) {
12190
12154
  const { as = "Object", extraEntropy: extraEntropy2 = extraEntropy, hash, payload, privateKey } = options;
12191
12155
  const sigBytes = sign(from2(payload), from2(privateKey), {
@@ -12198,7 +12162,7 @@ function sign2(options) {
12198
12162
  var fromPrfDomain = fromString2("ox.secp256k1.fromPrf.v1");
12199
12163
  var fromSeedDomain = fromString2("ox.secp256k1.fromSeed.v1");
12200
12164
 
12201
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/AccessList.js
12165
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/AccessList.js
12202
12166
  function toTupleList(accessList) {
12203
12167
  if (!accessList || accessList.length === 0)
12204
12168
  return [];
@@ -12223,7 +12187,7 @@ class InvalidStorageKeySizeError extends BaseError {
12223
12187
  }
12224
12188
  }
12225
12189
 
12226
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/internal/tx.js
12190
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/internal/tx.js
12227
12191
  function quantityToHex(value) {
12228
12192
  if (value === undefined || value === null)
12229
12193
  return "0x";
@@ -12232,7 +12196,7 @@ function quantityToHex(value) {
12232
12196
  return fromNumber(value);
12233
12197
  }
12234
12198
 
12235
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Rlp.js
12199
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Rlp.js
12236
12200
  function from6(value, options) {
12237
12201
  const { as } = options;
12238
12202
  const ctx = { lengths: [], cursor: 0 };
@@ -12475,7 +12439,7 @@ function invalidNibble(hex) {
12475
12439
  return new BaseError(`Invalid hex string \`${hex}\`.`);
12476
12440
  }
12477
12441
 
12478
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/TxEnvelopeEip2930.js
12442
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/TxEnvelopeEip2930.js
12479
12443
  function assert5(envelope) {
12480
12444
  const { chainId, gasPrice, to } = envelope;
12481
12445
  if (chainId <= 0)
@@ -12519,7 +12483,7 @@ function serialize(envelope, options = {}) {
12519
12483
  return concat2("0x01", fromHex3(serialized));
12520
12484
  }
12521
12485
 
12522
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/TxEnvelopeLegacy.js
12486
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/TxEnvelopeLegacy.js
12523
12487
  function assert6(envelope) {
12524
12488
  const { chainId, gasPrice, to } = envelope;
12525
12489
  if (to)
@@ -12596,7 +12560,7 @@ function serialize2(envelope, options = {}) {
12596
12560
  return fromHex3(serialized);
12597
12561
  }
12598
12562
 
12599
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/Value.js
12563
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/Value.js
12600
12564
  var exponents = {
12601
12565
  wei: 0,
12602
12566
  gwei: 9,
@@ -12629,7 +12593,7 @@ class InvalidDecimalsError extends BaseError {
12629
12593
  }
12630
12594
  }
12631
12595
 
12632
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/TxEnvelope.js
12596
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/TxEnvelope.js
12633
12597
  class FeeCapTooHighError extends BaseError {
12634
12598
  name = "TransactionEnvelope.FeeCapTooHighError";
12635
12599
  constructor({ feeCap } = {}) {
@@ -12660,7 +12624,7 @@ class TipAboveFeeCapError extends BaseError {
12660
12624
  }
12661
12625
  }
12662
12626
 
12663
- // ../../node_modules/.bun/ox@1.8.9+ff0372d108591aba/node_modules/ox/dist/core/TxEnvelopeEip1559.js
12627
+ // ../../node_modules/.bun/ox@1.8.12+ff0372d108591aba/node_modules/ox/dist/core/TxEnvelopeEip1559.js
12664
12628
  var serializedType = "0x02";
12665
12629
  function assert7(envelope) {
12666
12630
  const { chainId, maxPriorityFeePerGas, maxFeePerGas, to } = envelope;
@@ -12932,24 +12896,10 @@ var decodeEvm = (request, table = evmTokenTable) => {
12932
12896
  verifiability: "opaque"
12933
12897
  };
12934
12898
  };
12935
- var isAccountRef2 = (value) => {
12936
- if (!isRecord3(value) || typeof value["kind"] !== "string")
12937
- return false;
12938
- if (value["kind"] === "hd")
12939
- return typeof value["walletId"] === "string" && typeof value["groupIndex"] === "number" && typeof value["template"] === "string";
12940
- if (value["kind"] === "imported")
12941
- return typeof value["keyId"] === "string" && typeof value["family"] === "string";
12942
- return false;
12943
- };
12944
12899
  var normalizeEvm = (request) => {
12945
12900
  if (!isRecord3(request) || typeof request["kind"] !== "string")
12946
12901
  throw new ChainError("invalid-request", "request must be an object with a kind");
12947
- const { network } = request;
12948
- const { account } = request;
12949
- if (typeof network !== "string")
12950
- throw new ChainError("invalid-request", "request.network must be a string");
12951
- if (!isAccountRef2(account))
12952
- throw new ChainError("invalid-request", "request.account must be an AccountRef");
12902
+ const { network, account } = requestBase(request);
12953
12903
  const chainId = networkChainId(network);
12954
12904
  if (request["kind"] === "evm.transaction") {
12955
12905
  const tx = normalizeEvmTransaction(request["tx"]);
@@ -12988,79 +12938,6 @@ var normalizeEvm = (request) => {
12988
12938
  throw new ChainError("unsupported", `unsupported sign request kind: ${request["kind"]}`);
12989
12939
  };
12990
12940
 
12991
- // ../wallet-chains/dist/evm-sign.mjs
12992
- function assertHex2(value, field) {
12993
- if (!/^0x(?:[0-9a-fA-F]{2})*$/.test(value))
12994
- throw new ChainError("invalid-quantity", `${field} must be 0x-prefixed bytes`);
12995
- }
12996
- var hex = (value, field) => {
12997
- assertHex2(value, field);
12998
- return value;
12999
- };
13000
- var key = (privateKey) => {
13001
- if (privateKey.length !== 32)
13002
- throw new ChainError("invalid-request", "private key must be 32 bytes");
13003
- return fromBytes(privateKey);
13004
- };
13005
- var accessEntry = (entry) => ({
13006
- address: hex(entry.address, "accessList.address"),
13007
- storageKeys: entry.storageKeys.map((k) => hex(k, "accessList.storageKeys"))
13008
- });
13009
- var sign3 = (payload, privateKey) => sign2({
13010
- payload,
13011
- privateKey,
13012
- as: "Object"
13013
- });
13014
- var signEvmTransaction = (tx, privateKey) => {
13015
- const privateKeyHex = key(privateKey);
13016
- const base = {
13017
- chainId: tx.chainId,
13018
- to: tx.to === undefined ? undefined : hex(tx.to, "to"),
13019
- data: hex(tx.data, "data"),
13020
- value: BigInt(tx.value),
13021
- nonce: BigInt(tx.nonce),
13022
- gas: BigInt(tx.gas)
13023
- };
13024
- if (tx.type === "legacy") {
13025
- const envelope = {
13026
- ...base,
13027
- type: "legacy",
13028
- gasPrice: BigInt(tx.gasPrice)
13029
- };
13030
- const signature = sign3(getSignPayload4(envelope), privateKeyHex);
13031
- return toBytes(serialize2(envelope, { signature }));
13032
- }
13033
- if (tx.type === "eip2930") {
13034
- const envelope = {
13035
- ...base,
13036
- type: "eip2930",
13037
- gasPrice: BigInt(tx.gasPrice),
13038
- accessList: tx.accessList.map(accessEntry)
13039
- };
13040
- const signature = sign3(getSignPayload3(envelope), privateKeyHex);
13041
- return toBytes(serialize(envelope, { signature }));
13042
- }
13043
- const envelope = {
13044
- ...base,
13045
- type: "eip1559",
13046
- accessList: tx.accessList.map(accessEntry),
13047
- maxFeePerGas: BigInt(tx.maxFeePerGas),
13048
- maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas)
13049
- };
13050
- const signature = sign3(getSignPayload5(envelope), privateKeyHex);
13051
- return toBytes(serialize3(envelope, { signature }));
13052
- };
13053
- var rs_v = (signature) => toBytes2(signature);
13054
- var signEvmPersonalMessage = (message, privateKey) => {
13055
- const payload = getSignPayload2(fromBytes(message));
13056
- return rs_v(from5(sign3(payload, key(privateKey))));
13057
- };
13058
- var signEvmTypedData = (typedData, privateKey) => {
13059
- const { json } = parseTypedData(typedData, "eip155:1");
13060
- const payload = getSignPayload(json);
13061
- return rs_v(from5(sign3(payload, key(privateKey))));
13062
- };
13063
-
13064
12941
  // ../../node_modules/.bun/@solana+errors@8.5.0+ff0372d108591aba/node_modules/@solana/errors/dist/index.node.mjs
13065
12942
  var SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED = 1;
13066
12943
  var SOLANA_ERROR__INVALID_NONCE = 2;
@@ -14535,14 +14412,14 @@ var parseCompiledMessage = (bytes) => {
14535
14412
  lookupAddresses: lookups.map((l) => l.lookupTableAddress)
14536
14413
  };
14537
14414
  };
14538
- var isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
14415
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
14539
14416
  var isStringArray = (value) => Array.isArray(value) && value.every((v) => typeof v === "string");
14540
14417
  var isNumberArray = (value) => Array.isArray(value) && value.every((v) => typeof v === "number");
14541
- var isArrayLike = (value) => isRecord$1(value) && typeof value["length"] === "number";
14542
- var isIx = (value) => isRecord$1(value) && typeof value["programAddressIndex"] === "number" && (value["accountIndices"] === undefined || isNumberArray(value["accountIndices"]) || isArrayLike(value["accountIndices"])) && value["data"] instanceof Uint8Array;
14543
- var isLookup = (value) => isRecord$1(value) && typeof value["lookupTableAddress"] === "string" && (isNumberArray(value["readonlyIndexes"]) || value["readonlyIndexes"] instanceof Uint8Array) && (isNumberArray(value["writableIndexes"]) || value["writableIndexes"] instanceof Uint8Array);
14418
+ var isArrayLike = (value) => isRecord4(value) && typeof value["length"] === "number";
14419
+ var isIx = (value) => isRecord4(value) && typeof value["programAddressIndex"] === "number" && (value["accountIndices"] === undefined || isNumberArray(value["accountIndices"]) || isArrayLike(value["accountIndices"])) && value["data"] instanceof Uint8Array;
14420
+ var isLookup = (value) => isRecord4(value) && typeof value["lookupTableAddress"] === "string" && (isNumberArray(value["readonlyIndexes"]) || value["readonlyIndexes"] instanceof Uint8Array) && (isNumberArray(value["writableIndexes"]) || value["writableIndexes"] instanceof Uint8Array);
14544
14421
  var asCompiledMessage = (value) => {
14545
- if (!isRecord$1(value) || !isRecord$1(value["header"]))
14422
+ if (!isRecord4(value) || !isRecord4(value["header"]))
14546
14423
  throw new ChainError("invalid-request", "compiled message failed to decode");
14547
14424
  const { header } = value;
14548
14425
  const { staticAccounts } = value;
@@ -14763,14 +14640,6 @@ var decodeTransaction2 = (request, signer, table) => {
14763
14640
  };
14764
14641
  };
14765
14642
  var decodeSolana = (request, signer, table = solanaTokenTable) => decodeTransaction2(request, signer, table);
14766
- var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
14767
- var isAccountRef3 = (value) => {
14768
- if (!isRecord4(value) || typeof value["kind"] !== "string")
14769
- return false;
14770
- if (value["kind"] === "hd")
14771
- return typeof value["walletId"] === "string" && typeof value["groupIndex"] === "number" && typeof value["template"] === "string";
14772
- return value["kind"] === "imported" && typeof value["keyId"] === "string" && typeof value["family"] === "string";
14773
- };
14774
14643
  var solanaNetwork = (network) => {
14775
14644
  if (!SOLANA_NETWORKS.includes(network))
14776
14645
  throw new ChainError("unsupported", `unknown Solana network: ${network}`);
@@ -14781,7 +14650,7 @@ var validateLookupTablesRaw = (tables) => {
14781
14650
  if (!Array.isArray(tables))
14782
14651
  throw new ChainError("unknown-field", "lookupTables must be an array");
14783
14652
  const parsed = tables.map((t) => {
14784
- if (!isRecord4(t) || typeof t["address"] !== "string" || !Array.isArray(t["addresses"]))
14653
+ if (!isRecord2(t) || typeof t["address"] !== "string" || !Array.isArray(t["addresses"]))
14785
14654
  throw new ChainError("unknown-field", "lookupTables entries must be { address, addresses }");
14786
14655
  const addresses = t["addresses"].map((a) => {
14787
14656
  if (typeof a !== "string")
@@ -14797,14 +14666,9 @@ var validateLookupTablesRaw = (tables) => {
14797
14666
  return parsed;
14798
14667
  };
14799
14668
  var normalizeSolana = (request, signer) => {
14800
- if (!isRecord4(request) || typeof request["kind"] !== "string")
14669
+ if (!isRecord2(request) || typeof request["kind"] !== "string")
14801
14670
  throw new ChainError("invalid-request", "request must be an object with a kind");
14802
- const { network } = request;
14803
- const { account } = request;
14804
- if (typeof network !== "string")
14805
- throw new ChainError("invalid-request", "request.network must be a string");
14806
- if (!isAccountRef3(account))
14807
- throw new ChainError("invalid-request", "request.account must be an AccountRef");
14671
+ const { network, account } = requestBase(request);
14808
14672
  solanaNetwork(network);
14809
14673
  if (!isSolanaAddress(signer))
14810
14674
  throw new ChainError("invalid-address", "signer is not a base58 address");
@@ -14850,16 +14714,24 @@ var normalizeSolana = (request, signer) => {
14850
14714
  throw new ChainError("unsupported", `unsupported sign request kind: ${request["kind"]}`);
14851
14715
  };
14852
14716
 
14853
- // ../wallet-chains/dist/solana-sign.mjs
14854
- var signSolanaMessage = (message, privateKey) => {
14717
+ // ../wallet-chains/dist/bitcoin-sign.mjs
14718
+ var signBitcoinPsbt = (psbt, inputs, auxRand) => {
14719
+ const tx = Transaction.fromPSBT(psbt);
14720
+ for (const { index, privateKey } of inputs) {
14721
+ if (privateKey.length !== 32)
14722
+ throw new ChainError("invalid-request", `input ${index}: private key must be 32 bytes`);
14723
+ if (!tx.signIdx(privateKey, index, undefined, auxRand(index)))
14724
+ throw new ChainError("invalid-request", `input ${index}: key cannot sign`);
14725
+ }
14726
+ return tx.toPSBT(0);
14727
+ };
14728
+ var signBitcoinMessage = (request, privateKey, auxRand) => {
14855
14729
  if (privateKey.length !== 32)
14856
- throw new ChainError("invalid-request", "private key must be a 32-byte seed");
14857
- if (message.length === 0)
14858
- throw new ChainError("invalid-request", "message must not be empty");
14859
- const signature = ed25519.sign(message, privateKey);
14860
- if (signature.length !== 64)
14861
- throw new ChainError("invalid-request", "ed25519 produced a malformed signature");
14862
- return signature;
14730
+ throw new ChainError("invalid-request", "private key must be 32 bytes");
14731
+ if (request.format === "legacy")
14732
+ return signLegacyMessage(request.message, privateKey);
14733
+ const scriptPubKey = OutScript.encode(Address(bitcoinNetwork(request.network).params).decode(request.address));
14734
+ return signBip322Simple(request.message, scriptPubKey, privateKey, auxRand);
14863
14735
  };
14864
14736
 
14865
14737
  // ../wallet-kernel/src/modules/chains/prompt.ts
@@ -14926,15 +14798,407 @@ function chainPrompt(locale, intent) {
14926
14798
  return { title, description: joinLines(lines.length === 0 ? [catalog.generic] : lines) };
14927
14799
  }
14928
14800
 
14929
- // ../wallet-kernel/src/modules/chains/index.ts
14801
+ // ../wallet-kernel/src/modules/chains/shared.ts
14930
14802
  var BTC_PURPOSE = {
14931
14803
  "btc.bip44": 44,
14932
14804
  "btc.bip49": 49,
14933
14805
  "btc.bip84": 84,
14934
14806
  "btc.bip86": 86
14935
14807
  };
14808
+ var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof Uint8Array);
14809
+ var deepEqual = (a, b) => {
14810
+ if (a === b) {
14811
+ return true;
14812
+ }
14813
+ if (a instanceof Uint8Array || b instanceof Uint8Array) {
14814
+ return a instanceof Uint8Array && b instanceof Uint8Array && a.length === b.length && a.every((byte, i) => byte === b[i]);
14815
+ }
14816
+ if (Array.isArray(a) || Array.isArray(b)) {
14817
+ return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
14818
+ }
14819
+ if (!isObj(a) || !isObj(b)) {
14820
+ return false;
14821
+ }
14822
+ const ka = Object.keys(a);
14823
+ const kb = new Set(Object.keys(b));
14824
+ return ka.length === kb.size && ka.every((k) => kb.has(k) && deepEqual(a[k], b[k]));
14825
+ };
14826
+ function mapChainError(error) {
14827
+ if (error instanceof ChainError) {
14828
+ throw new KernelError("invalid-input", "invalid sign request");
14829
+ }
14830
+ throw error;
14831
+ }
14832
+
14833
+ // ../wallet-kernel/src/modules/chains/bitcoin.ts
14936
14834
  var isPath = (path) => Array.isArray(path) && path.every((p) => typeof p === "number");
14937
- var hex2 = (bytes) => bytes.reduce((acc, b) => acc + b.toString(16).padStart(2, "0"), "");
14835
+ var hex = (bytes) => bytes.reduce((acc, b) => acc + b.toString(16).padStart(2, "0"), "");
14836
+ var derivationOf = (owner, fingerprint, purpose) => {
14837
+ if (purpose === 86) {
14838
+ const claimed = (der) => der.fingerprint === fingerprint && isPath(der.path);
14839
+ for (const [key, value] of owner.tapBip32Derivation ?? []) {
14840
+ if (claimed(value.der)) {
14841
+ return { pubkey: key, fingerprint: value.der.fingerprint, path: value.der.path };
14842
+ }
14843
+ }
14844
+ return;
14845
+ }
14846
+ for (const [key, value] of owner.bip32Derivation ?? []) {
14847
+ if (value.fingerprint === fingerprint) {
14848
+ return { pubkey: key, fingerprint: value.fingerprint, path: value.path };
14849
+ }
14850
+ }
14851
+ return;
14852
+ };
14853
+ var expectedPub = (purpose, pub) => purpose === 86 ? pub.slice(1) : pub;
14854
+ var expectedScript = (purpose, pub) => purpose === 86 ? p2tr(pub.slice(1)).script : purpose === 49 ? p2sh(p2wpkh(pub)).script : purpose === 44 ? p2pkh(pub).script : p2wpkh(pub).script;
14855
+ var childAt = (b, path) => {
14856
+ const [purposePath, coinPath, groupPath, change, index] = path;
14857
+ if (path.length !== 5 || purposePath !== b.purpose + b.hardened || coinPath !== b.coinType + b.hardened || groupPath !== b.account.groupIndex + b.hardened || change === undefined || index === undefined || change !== 0 && change !== 1 || index < 0 || index >= b.hardened) {
14858
+ throw new KernelError("invalid-input", "derivation path is not this account's");
14859
+ }
14860
+ const child = b.accountNode.deriveChild(change).deriveChild(index);
14861
+ b.children.push(child);
14862
+ return child;
14863
+ };
14864
+ var bitcoinInput = (b, tx, idx) => {
14865
+ const input = tx.getInput(idx);
14866
+ const entry = derivationOf(input, b.master.fingerprint, b.purpose);
14867
+ if (entry === undefined) {
14868
+ throw new KernelError("invalid-input", `input ${idx}: derivation entry is not this account's`);
14869
+ }
14870
+ const child = childAt(b, entry.path);
14871
+ const pub = child.publicKey;
14872
+ const priv = child.privateKey;
14873
+ if (pub === null || priv === null) {
14874
+ throw new KernelError("integrity", "no key at derivation path");
14875
+ }
14876
+ if (!deepEqual(entry.pubkey, expectedPub(b.purpose, pub))) {
14877
+ throw new KernelError("invalid-input", `input ${idx}: derivation pubkey is not ours`);
14878
+ }
14879
+ if (b.purpose === 44) {
14880
+ if (input.nonWitnessUtxo === undefined) {
14881
+ throw new KernelError("invalid-input", `input ${idx}: P2PKH spend requires nonWitnessUtxo`);
14882
+ }
14883
+ const vout = input.index;
14884
+ const outs = input.nonWitnessUtxo.outputs;
14885
+ const prevScript = vout === undefined ? undefined : outs[vout]?.script;
14886
+ if (prevScript === undefined || !deepEqual(prevScript, expectedScript(b.purpose, pub))) {
14887
+ throw new KernelError("invalid-input", `input ${idx}: nonWitnessUtxo prevout is not this key's`);
14888
+ }
14889
+ } else {
14890
+ const prevout = input.witnessUtxo;
14891
+ if (prevout === undefined || !deepEqual(prevout.script, expectedScript(b.purpose, pub))) {
14892
+ throw new KernelError("invalid-input", `input ${idx}: prevout script is not this key's`);
14893
+ }
14894
+ if (b.purpose === 49 && (input.redeemScript === undefined || !deepEqual(input.redeemScript, p2wpkh(pub).script))) {
14895
+ throw new KernelError("invalid-input", `input ${idx}: redeemScript is not this key's`);
14896
+ }
14897
+ }
14898
+ return { index: idx, privateKey: priv };
14899
+ };
14900
+ var bitcoinOwnScripts = (b, tx) => {
14901
+ const ownScripts = new Set;
14902
+ for (let i = 0;i < tx.outputsLength; i++) {
14903
+ const output = tx.getOutput(i);
14904
+ if (output.script === undefined) {
14905
+ continue;
14906
+ }
14907
+ const entry = derivationOf(output, b.master.fingerprint, b.purpose);
14908
+ if (entry === undefined) {
14909
+ continue;
14910
+ }
14911
+ try {
14912
+ const child = childAt(b, entry.path);
14913
+ const pub = child.publicKey;
14914
+ if (pub !== null && deepEqual(output.script, expectedScript(b.purpose, pub))) {
14915
+ ownScripts.add(hex(output.script));
14916
+ }
14917
+ } catch {
14918
+ continue;
14919
+ }
14920
+ }
14921
+ return ownScripts;
14922
+ };
14923
+ var executeBitcoinPsbt = (b, request, random, intentOnly) => {
14924
+ const tx = Transaction.fromPSBT(request.psbt);
14925
+ const inputs = request.sign.map((idx) => bitcoinInput(b, tx, idx));
14926
+ const ownScripts = bitcoinOwnScripts(b, tx);
14927
+ let normalized;
14928
+ try {
14929
+ normalized = normalizeBitcoin(request);
14930
+ } catch (error) {
14931
+ mapChainError(error);
14932
+ }
14933
+ if (!deepEqual(normalized, request)) {
14934
+ throw new KernelError("invalid-input", "request is not normalized");
14935
+ }
14936
+ let intent;
14937
+ try {
14938
+ intent = decodeBitcoinPsbt(request, ownScripts);
14939
+ } catch (error) {
14940
+ mapChainError(error);
14941
+ }
14942
+ if (intentOnly) {
14943
+ return { prompt: intent };
14944
+ }
14945
+ return {
14946
+ kind: "bitcoin.psbt",
14947
+ signedPsbt: signBitcoinPsbt(request.psbt, inputs, () => random(32))
14948
+ };
14949
+ };
14950
+ var executeBitcoinMessage = (b, request, random, intentOnly) => {
14951
+ if (request.format === "legacy" && b.account.template !== "btc.bip44") {
14952
+ throw new KernelError("unsupported", "legacy message format is btc.bip44-only");
14953
+ }
14954
+ const child = childAt(b, [
14955
+ b.purpose + b.hardened,
14956
+ b.coinType + b.hardened,
14957
+ b.account.groupIndex + b.hardened,
14958
+ request.change,
14959
+ request.index
14960
+ ]);
14961
+ const pub = child.publicKey;
14962
+ const priv = child.privateKey;
14963
+ if (pub === null || priv === null) {
14964
+ throw new KernelError("integrity", "no key at derivation path");
14965
+ }
14966
+ const derived = b.purpose === 86 ? p2tr(pub.slice(1), undefined, b.net.params).address : b.purpose === 49 ? p2sh(p2wpkh(pub, b.net.params), b.net.params).address : b.purpose === 44 ? p2pkh(pub, b.net.params).address : p2wpkh(pub, b.net.params).address;
14967
+ if (derived !== request.address) {
14968
+ throw new KernelError("invalid-input", "address is not ours");
14969
+ }
14970
+ let normalized;
14971
+ try {
14972
+ normalized = normalizeBitcoin(request);
14973
+ } catch (error) {
14974
+ mapChainError(error);
14975
+ }
14976
+ if (!deepEqual(normalized, request)) {
14977
+ throw new KernelError("invalid-input", "request is not normalized");
14978
+ }
14979
+ try {
14980
+ decodeMessageTarget(request);
14981
+ } catch (error) {
14982
+ mapChainError(error);
14983
+ }
14984
+ if (intentOnly) {
14985
+ return { prompt: MESSAGE_INTENT };
14986
+ }
14987
+ return {
14988
+ kind: "bitcoin.message",
14989
+ signature: signBitcoinMessage(request, priv, () => random(32))
14990
+ };
14991
+ };
14992
+ var executeBitcoin = (random, request, account, seed, intentOnly) => {
14993
+ const net = bitcoinNetwork(request.network);
14994
+ const master = HDKey.fromMasterSeed(seed, net.bip32);
14995
+ try {
14996
+ const purpose = BTC_PURPOSE[account.template] ?? 84;
14997
+ const { coinType } = net;
14998
+ const hardened = 2147483648;
14999
+ const accountNode = master.derive(`m/${purpose}'/${coinType}'/${account.groupIndex}'`);
15000
+ const children = [];
15001
+ const b = {
15002
+ master,
15003
+ accountNode,
15004
+ children,
15005
+ purpose,
15006
+ coinType,
15007
+ hardened,
15008
+ account,
15009
+ net
15010
+ };
15011
+ try {
15012
+ if (request.kind === "bitcoin.psbt") {
15013
+ return executeBitcoinPsbt(b, request, random, intentOnly);
15014
+ }
15015
+ return executeBitcoinMessage(b, request, random, intentOnly);
15016
+ } finally {
15017
+ for (const child of children) {
15018
+ child.wipePrivateData();
15019
+ }
15020
+ accountNode.wipePrivateData();
15021
+ }
15022
+ } finally {
15023
+ master.wipePrivateData();
15024
+ }
15025
+ };
15026
+
15027
+ // ../wallet-chains/dist/evm-sign.mjs
15028
+ function assertHex2(value, field) {
15029
+ if (!/^0x(?:[0-9a-fA-F]{2})*$/.test(value))
15030
+ throw new ChainError("invalid-quantity", `${field} must be 0x-prefixed bytes`);
15031
+ }
15032
+ var hex2 = (value, field) => {
15033
+ assertHex2(value, field);
15034
+ return value;
15035
+ };
15036
+ var key = (privateKey) => {
15037
+ if (privateKey.length !== 32)
15038
+ throw new ChainError("invalid-request", "private key must be 32 bytes");
15039
+ return fromBytes(privateKey);
15040
+ };
15041
+ var accessEntry = (entry) => ({
15042
+ address: hex2(entry.address, "accessList.address"),
15043
+ storageKeys: entry.storageKeys.map((k) => hex2(k, "accessList.storageKeys"))
15044
+ });
15045
+ var sign3 = (payload, privateKey) => sign2({
15046
+ payload,
15047
+ privateKey,
15048
+ as: "Object"
15049
+ });
15050
+ var signEvmTransaction = (tx, privateKey) => {
15051
+ const privateKeyHex = key(privateKey);
15052
+ const base = {
15053
+ chainId: tx.chainId,
15054
+ to: tx.to === undefined ? undefined : hex2(tx.to, "to"),
15055
+ data: hex2(tx.data, "data"),
15056
+ value: BigInt(tx.value),
15057
+ nonce: BigInt(tx.nonce),
15058
+ gas: BigInt(tx.gas)
15059
+ };
15060
+ if (tx.type === "legacy") {
15061
+ const envelope = {
15062
+ ...base,
15063
+ type: "legacy",
15064
+ gasPrice: BigInt(tx.gasPrice)
15065
+ };
15066
+ const signature = sign3(getSignPayload4(envelope), privateKeyHex);
15067
+ return toBytes(serialize2(envelope, { signature }));
15068
+ }
15069
+ if (tx.type === "eip2930") {
15070
+ const envelope = {
15071
+ ...base,
15072
+ type: "eip2930",
15073
+ gasPrice: BigInt(tx.gasPrice),
15074
+ accessList: tx.accessList.map(accessEntry)
15075
+ };
15076
+ const signature = sign3(getSignPayload3(envelope), privateKeyHex);
15077
+ return toBytes(serialize(envelope, { signature }));
15078
+ }
15079
+ const envelope = {
15080
+ ...base,
15081
+ type: "eip1559",
15082
+ accessList: tx.accessList.map(accessEntry),
15083
+ maxFeePerGas: BigInt(tx.maxFeePerGas),
15084
+ maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas)
15085
+ };
15086
+ const signature = sign3(getSignPayload5(envelope), privateKeyHex);
15087
+ return toBytes(serialize3(envelope, { signature }));
15088
+ };
15089
+ var rs_v = (signature) => toBytes2(signature);
15090
+ var signEvmPersonalMessage = (message, privateKey) => {
15091
+ const payload = getSignPayload2(fromBytes(message));
15092
+ return rs_v(from5(sign3(payload, key(privateKey))));
15093
+ };
15094
+ var signEvmTypedData = (typedData, privateKey) => {
15095
+ const { json } = parseTypedData(typedData, "eip155:1");
15096
+ const payload = getSignPayload(json);
15097
+ return rs_v(from5(sign3(payload, key(privateKey))));
15098
+ };
15099
+
15100
+ // ../wallet-kernel/src/modules/chains/evm.ts
15101
+ var executeEvm = (request, account, seed, intentOnly) => {
15102
+ const master = HDKey.fromMasterSeed(seed);
15103
+ try {
15104
+ const child = master.derive(`m/44'/60'/0'/0/${account.groupIndex}`);
15105
+ try {
15106
+ const priv = child.privateKey;
15107
+ if (priv === null) {
15108
+ throw new KernelError("integrity", "no private key at account path");
15109
+ }
15110
+ let normalized;
15111
+ try {
15112
+ normalized = normalizeEvm(request);
15113
+ } catch (error) {
15114
+ mapChainError(error);
15115
+ }
15116
+ if (!deepEqual(normalized, request)) {
15117
+ throw new KernelError("invalid-input", "request is not normalized");
15118
+ }
15119
+ let intent;
15120
+ try {
15121
+ intent = decodeEvm(request);
15122
+ } catch (error) {
15123
+ mapChainError(error);
15124
+ }
15125
+ if (intentOnly) {
15126
+ return { prompt: intent };
15127
+ }
15128
+ if (request.kind === "evm.transaction") {
15129
+ return {
15130
+ kind: "evm.transaction",
15131
+ signedTransaction: signEvmTransaction(request.tx, priv)
15132
+ };
15133
+ }
15134
+ if (request.kind === "evm.personal-message") {
15135
+ return {
15136
+ kind: "evm.personal-message",
15137
+ signature: signEvmPersonalMessage(request.message, priv)
15138
+ };
15139
+ }
15140
+ return {
15141
+ kind: "evm.typed-data",
15142
+ signature: signEvmTypedData(request.typedData, priv)
15143
+ };
15144
+ } finally {
15145
+ child.wipePrivateData();
15146
+ }
15147
+ } finally {
15148
+ master.wipePrivateData();
15149
+ }
15150
+ };
15151
+
15152
+ // ../wallet-chains/dist/solana-sign.mjs
15153
+ var signSolanaMessage = (message, privateKey) => {
15154
+ if (privateKey.length !== 32)
15155
+ throw new ChainError("invalid-request", "private key must be a 32-byte seed");
15156
+ if (message.length === 0)
15157
+ throw new ChainError("invalid-request", "message must not be empty");
15158
+ const signature = ed25519.sign(message, privateKey);
15159
+ if (signature.length !== 64)
15160
+ throw new ChainError("invalid-request", "ed25519 produced a malformed signature");
15161
+ return signature;
15162
+ };
15163
+
15164
+ // ../wallet-kernel/src/modules/chains/solana.ts
15165
+ var executeSolana = (request, account, seed, intentOnly) => {
15166
+ const root = HDKey2.fromMasterSeed(seed);
15167
+ const child = root.derive(`m/44'/501'/${account.groupIndex}'/0'`);
15168
+ try {
15169
+ const priv = child.privateKey;
15170
+ const signer = solanaAddressFromPublicKey(ed25519.getPublicKey(priv));
15171
+ let normalized;
15172
+ try {
15173
+ normalized = normalizeSolana(request, signer);
15174
+ } catch (error) {
15175
+ mapChainError(error);
15176
+ }
15177
+ if (!deepEqual(normalized, request)) {
15178
+ throw new KernelError("invalid-input", "request is not normalized");
15179
+ }
15180
+ let intent = MESSAGE_INTENT;
15181
+ if (request.kind === "solana.transaction") {
15182
+ try {
15183
+ intent = decodeSolana(request, signer);
15184
+ } catch (error) {
15185
+ mapChainError(error);
15186
+ }
15187
+ }
15188
+ if (intentOnly) {
15189
+ return { prompt: intent };
15190
+ }
15191
+ return {
15192
+ kind: request.kind,
15193
+ signature: signSolanaMessage(request.message, priv)
15194
+ };
15195
+ } finally {
15196
+ wipe(child.privateKey);
15197
+ wipe(root.privateKey);
15198
+ }
15199
+ };
15200
+
15201
+ // ../wallet-kernel/src/modules/chains/index.ts
14938
15202
  var checkNetwork = (template, network) => {
14939
15203
  if (template.startsWith("evm.") && !network.startsWith("eip155:")) {
14940
15204
  throw new KernelError("invalid-input", "evm.bip44 requires an eip155 network");
@@ -14991,382 +15255,86 @@ var accountFor = (master, seed, template, network, groupIndex) => {
14991
15255
  account.wipePrivateData();
14992
15256
  }
14993
15257
  };
14994
- var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof Uint8Array);
14995
- var deepEqual = (a, b) => {
14996
- if (a === b) {
14997
- return true;
14998
- }
14999
- if (a instanceof Uint8Array || b instanceof Uint8Array) {
15000
- return a instanceof Uint8Array && b instanceof Uint8Array && a.length === b.length && a.every((byte, i) => byte === b[i]);
15001
- }
15002
- if (Array.isArray(a) || Array.isArray(b)) {
15003
- return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
15004
- }
15005
- if (!isObj(a) || !isObj(b)) {
15006
- return false;
15007
- }
15008
- const ka = Object.keys(a);
15009
- const kb = new Set(Object.keys(b));
15010
- return ka.length === kb.size && ka.every((k) => kb.has(k) && deepEqual(a[k], b[k]));
15011
- };
15012
- function mapChainError(error) {
15013
- if (error instanceof ChainError) {
15014
- throw new KernelError("invalid-input", "invalid sign request");
15258
+ var walletAccounts = async (ctx, params, session) => {
15259
+ const { dek } = ctx.session.require(session);
15260
+ const record = ctx.image.records.find((entry) => entry.kind === "entropy" && entry.id === params.walletId);
15261
+ if (record === undefined) {
15262
+ throw new KernelError("not-found", "no such wallet");
15015
15263
  }
15016
- throw error;
15017
- }
15018
- function createChainsModule() {
15019
- const vault = {
15020
- ownsRecord: () => false,
15021
- claimUnlocked: (records) => records,
15022
- forget: () => {
15023
- return;
15024
- },
15025
- lock: () => {
15026
- return;
15027
- },
15028
- backupSecrets: () => [],
15029
- backupRecords: () => [],
15030
- walletDeleteOps: () => []
15031
- };
15032
- const handlers = {
15033
- "wallet.accounts": async (ctx, params, session) => {
15034
- const { dek } = ctx.session.require(session);
15035
- const record = ctx.image.records.find((entry) => entry.kind === "entropy" && entry.id === params.walletId);
15036
- if (record === undefined) {
15037
- throw new KernelError("not-found", "no such wallet");
15038
- }
15039
- checkNetwork(params.template, params.network);
15040
- const entropy = openRecord(dek, record);
15041
- let seed;
15042
- try {
15043
- seed = await seedFromEntropy(entropy, ctx.platform.pbkdf2);
15044
- } finally {
15045
- wipe(entropy);
15046
- }
15047
- try {
15048
- const master = HDKey.fromMasterSeed(seed, params.template.startsWith("btc.") ? bitcoinNetwork(params.network).bip32 : undefined);
15049
- try {
15050
- return accountFor(master, seed, params.template, params.network, params.groupIndex);
15051
- } finally {
15052
- master.wipePrivateData();
15053
- }
15054
- } finally {
15055
- wipe(seed);
15056
- }
15057
- },
15058
- "chain.sign": async (ctx, params, session) => {
15059
- const { dek } = ctx.session.require(session);
15060
- const { request, verify } = params;
15061
- const { account } = request;
15062
- if (account.kind !== "hd") {
15063
- throw new KernelError("unsupported", "imported accounts are not yet supported");
15064
- }
15065
- const [family] = request.kind.split(".", 1);
15066
- const expected = family === "evm" ? ["evm.bip44"] : family === "solana" ? ["solana.bip44-change"] : ["btc.bip44", "btc.bip49", "btc.bip84", "btc.bip86"];
15067
- if (!expected.includes(account.template)) {
15068
- throw new KernelError("invalid-input", "account template does not match the request family");
15069
- }
15070
- const record = ctx.image.records.find((entry) => entry.kind === "entropy" && entry.id === account.walletId);
15071
- if (record === undefined) {
15072
- throw new KernelError("not-found", "no such wallet");
15073
- }
15074
- const execute = (seed, intentOnly) => {
15075
- if (request.kind === "evm.transaction" || request.kind === "evm.personal-message" || request.kind === "evm.typed-data") {
15076
- const master = HDKey.fromMasterSeed(seed);
15077
- try {
15078
- const child = master.derive(`m/44'/60'/0'/0/${account.groupIndex}`);
15079
- try {
15080
- const priv = child.privateKey;
15081
- if (priv === null) {
15082
- throw new KernelError("integrity", "no private key at account path");
15083
- }
15084
- let normalized;
15085
- try {
15086
- normalized = normalizeEvm(request);
15087
- } catch (error) {
15088
- mapChainError(error);
15089
- }
15090
- if (!deepEqual(normalized, request)) {
15091
- throw new KernelError("invalid-input", "request is not normalized");
15092
- }
15093
- let intent;
15094
- try {
15095
- intent = decodeEvm(request);
15096
- } catch (error) {
15097
- mapChainError(error);
15098
- }
15099
- if (intentOnly) {
15100
- return { prompt: intent };
15101
- }
15102
- if (request.kind === "evm.transaction") {
15103
- return {
15104
- kind: "evm.transaction",
15105
- signedTransaction: signEvmTransaction(request.tx, priv)
15106
- };
15107
- }
15108
- if (request.kind === "evm.personal-message") {
15109
- return {
15110
- kind: "evm.personal-message",
15111
- signature: signEvmPersonalMessage(request.message, priv)
15112
- };
15113
- }
15114
- return {
15115
- kind: "evm.typed-data",
15116
- signature: signEvmTypedData(request.typedData, priv)
15117
- };
15118
- } finally {
15119
- child.wipePrivateData();
15120
- }
15121
- } finally {
15122
- master.wipePrivateData();
15123
- }
15124
- }
15125
- if (request.kind === "bitcoin.psbt" || request.kind === "bitcoin.message") {
15126
- const net = bitcoinNetwork(request.network);
15127
- const master = HDKey.fromMasterSeed(seed, net.bip32);
15128
- try {
15129
- const purpose = BTC_PURPOSE[account.template] ?? 84;
15130
- const { coinType } = net;
15131
- const hardened = 2147483648;
15132
- const accountNode = master.derive(`m/${purpose}'/${coinType}'/${account.groupIndex}'`);
15133
- const children = [];
15134
- const derivation = (owner) => {
15135
- if (purpose === 86) {
15136
- const claimed = (der) => der.fingerprint === master.fingerprint && isPath(der.path);
15137
- for (const [key, value] of owner.tapBip32Derivation ?? []) {
15138
- if (claimed(value.der)) {
15139
- return {
15140
- pubkey: key,
15141
- fingerprint: value.der.fingerprint,
15142
- path: value.der.path
15143
- };
15144
- }
15145
- }
15146
- return;
15147
- }
15148
- for (const [key, value] of owner.bip32Derivation ?? []) {
15149
- if (value.fingerprint === master.fingerprint) {
15150
- return { pubkey: key, fingerprint: value.fingerprint, path: value.path };
15151
- }
15152
- }
15153
- return;
15154
- };
15155
- const childAt = (path) => {
15156
- const [purposePath, coinPath, groupPath, change, index] = path;
15157
- if (path.length !== 5 || purposePath !== purpose + hardened || coinPath !== coinType + hardened || groupPath !== account.groupIndex + hardened || change === undefined || index === undefined || change !== 0 && change !== 1 || index < 0 || index >= hardened) {
15158
- throw new KernelError("invalid-input", "derivation path is not this account's");
15159
- }
15160
- const child = accountNode.deriveChild(change).deriveChild(index);
15161
- children.push(child);
15162
- return child;
15163
- };
15164
- const expectedPub = (pub) => purpose === 86 ? pub.slice(1) : pub;
15165
- const expectedScript = (pub) => purpose === 86 ? p2tr(pub.slice(1)).script : purpose === 49 ? p2sh(p2wpkh(pub)).script : purpose === 44 ? p2pkh(pub).script : p2wpkh(pub).script;
15166
- try {
15167
- if (request.kind === "bitcoin.psbt") {
15168
- const tx = Transaction.fromPSBT(request.psbt);
15169
- const inputs = [];
15170
- for (const idx of request.sign) {
15171
- const input = tx.getInput(idx);
15172
- const entry = derivation(input);
15173
- if (entry === undefined) {
15174
- throw new KernelError("invalid-input", `input ${idx}: derivation entry is not this account's`);
15175
- }
15176
- const child = childAt(entry.path);
15177
- const pub = child.publicKey;
15178
- const priv = child.privateKey;
15179
- if (pub === null || priv === null) {
15180
- throw new KernelError("integrity", "no key at derivation path");
15181
- }
15182
- if (!deepEqual(entry.pubkey, expectedPub(pub))) {
15183
- throw new KernelError("invalid-input", `input ${idx}: derivation pubkey is not ours`);
15184
- }
15185
- if (purpose === 44) {
15186
- if (input.nonWitnessUtxo === undefined) {
15187
- throw new KernelError("invalid-input", `input ${idx}: P2PKH spend requires nonWitnessUtxo`);
15188
- }
15189
- const vout = input.index;
15190
- const outs = input.nonWitnessUtxo.outputs;
15191
- const prevScript = vout === undefined ? undefined : outs[vout]?.script;
15192
- if (prevScript === undefined || !deepEqual(prevScript, expectedScript(pub))) {
15193
- throw new KernelError("invalid-input", `input ${idx}: nonWitnessUtxo prevout is not this key's`);
15194
- }
15195
- } else {
15196
- const prevout = input.witnessUtxo;
15197
- if (prevout === undefined || !deepEqual(prevout.script, expectedScript(pub))) {
15198
- throw new KernelError("invalid-input", `input ${idx}: prevout script is not this key's`);
15199
- }
15200
- if (purpose === 49 && (input.redeemScript === undefined || !deepEqual(input.redeemScript, p2wpkh(pub).script))) {
15201
- throw new KernelError("invalid-input", `input ${idx}: redeemScript is not this key's`);
15202
- }
15203
- }
15204
- inputs.push({ index: idx, privateKey: priv });
15205
- }
15206
- const ownScripts = new Set;
15207
- for (let i = 0;i < tx.outputsLength; i++) {
15208
- const output = tx.getOutput(i);
15209
- if (output.script === undefined) {
15210
- continue;
15211
- }
15212
- const entry = derivation(output);
15213
- if (entry === undefined) {
15214
- continue;
15215
- }
15216
- try {
15217
- const child = childAt(entry.path);
15218
- const pub = child.publicKey;
15219
- if (pub !== null && deepEqual(output.script, expectedScript(pub))) {
15220
- ownScripts.add(hex2(output.script));
15221
- }
15222
- } catch {
15223
- continue;
15224
- }
15225
- }
15226
- let normalized;
15227
- try {
15228
- normalized = normalizeBitcoin(request);
15229
- } catch (error) {
15230
- mapChainError(error);
15231
- }
15232
- if (!deepEqual(normalized, request)) {
15233
- throw new KernelError("invalid-input", "request is not normalized");
15234
- }
15235
- let intent;
15236
- try {
15237
- intent = decodeBitcoinPsbt(request, ownScripts);
15238
- } catch (error) {
15239
- mapChainError(error);
15240
- }
15241
- if (intentOnly) {
15242
- return { prompt: intent };
15243
- }
15244
- return {
15245
- kind: "bitcoin.psbt",
15246
- signedPsbt: signBitcoinPsbt(request.psbt, inputs, () => ctx.platform.random(32))
15247
- };
15248
- }
15249
- if (request.format === "legacy" && account.template !== "btc.bip44") {
15250
- throw new KernelError("unsupported", "legacy message format is btc.bip44-only");
15251
- }
15252
- const child = childAt([
15253
- purpose + hardened,
15254
- coinType + hardened,
15255
- account.groupIndex + hardened,
15256
- request.change,
15257
- request.index
15258
- ]);
15259
- const pub = child.publicKey;
15260
- const priv = child.privateKey;
15261
- if (pub === null || priv === null) {
15262
- throw new KernelError("integrity", "no key at derivation path");
15263
- }
15264
- const derived = purpose === 86 ? p2tr(pub.slice(1), undefined, net.params).address : purpose === 49 ? p2sh(p2wpkh(pub, net.params), net.params).address : purpose === 44 ? p2pkh(pub, net.params).address : p2wpkh(pub, net.params).address;
15265
- if (derived !== request.address) {
15266
- throw new KernelError("invalid-input", "address is not ours");
15267
- }
15268
- let normalized;
15269
- try {
15270
- normalized = normalizeBitcoin(request);
15271
- } catch (error) {
15272
- mapChainError(error);
15273
- }
15274
- if (!deepEqual(normalized, request)) {
15275
- throw new KernelError("invalid-input", "request is not normalized");
15276
- }
15277
- try {
15278
- decodeMessageTarget(request);
15279
- } catch (error) {
15280
- mapChainError(error);
15281
- }
15282
- if (intentOnly) {
15283
- return { prompt: MESSAGE_INTENT };
15284
- }
15285
- return {
15286
- kind: "bitcoin.message",
15287
- signature: signBitcoinMessage(request, priv, () => ctx.platform.random(32))
15288
- };
15289
- } finally {
15290
- for (const child of children) {
15291
- child.wipePrivateData();
15292
- }
15293
- accountNode.wipePrivateData();
15294
- }
15295
- } finally {
15296
- master.wipePrivateData();
15297
- }
15298
- }
15299
- const root = HDKey2.fromMasterSeed(seed);
15300
- const child = root.derive(`m/44'/501'/${account.groupIndex}'/0'`);
15301
- try {
15302
- const priv = child.privateKey;
15303
- const signer = solanaAddressFromPublicKey(ed25519.getPublicKey(priv));
15304
- let normalized;
15305
- try {
15306
- normalized = normalizeSolana(request, signer);
15307
- } catch (error) {
15308
- mapChainError(error);
15309
- }
15310
- if (!deepEqual(normalized, request)) {
15311
- throw new KernelError("invalid-input", "request is not normalized");
15312
- }
15313
- let intent = MESSAGE_INTENT;
15314
- if (request.kind === "solana.transaction") {
15315
- try {
15316
- intent = decodeSolana(request, signer);
15317
- } catch (error) {
15318
- mapChainError(error);
15319
- }
15320
- }
15321
- if (intentOnly) {
15322
- return { prompt: intent };
15323
- }
15324
- return {
15325
- kind: request.kind,
15326
- signature: signSolanaMessage(request.message, priv)
15327
- };
15328
- } finally {
15329
- wipe(child.privateKey);
15330
- wipe(root.privateKey);
15331
- }
15332
- };
15333
- const withSeed = async (run) => {
15334
- const entropy = openRecord(dek, record);
15335
- let seed;
15336
- try {
15337
- seed = await seedFromEntropy(entropy, ctx.platform.pbkdf2);
15338
- } finally {
15339
- wipe(entropy);
15340
- }
15341
- try {
15342
- return run(seed);
15343
- } finally {
15344
- wipe(seed);
15345
- }
15346
- };
15347
- const prepared = await withSeed((seed) => execute(seed, true));
15348
- if (!("prompt" in prepared)) {
15349
- throw new KernelError("integrity", "unreachable: prepare pass signed");
15350
- }
15351
- await verifyFactor(ctx.platform, ctx.image, verify, dek, () => chainPrompt(promptLocale(ctx.platform.keystore?.locale ?? "en"), prepared.prompt));
15352
- ctx.session.require(session);
15353
- const signed = await withSeed((seed) => execute(seed, false));
15354
- if ("prompt" in signed) {
15355
- throw new KernelError("integrity", "unreachable: sign pass did not sign");
15356
- }
15357
- return signed;
15264
+ checkNetwork(params.template, params.network);
15265
+ return withWalletSeed(ctx.platform.pbkdf2, dek, record, (seed) => {
15266
+ const master = HDKey.fromMasterSeed(seed, params.template.startsWith("btc.") ? bitcoinNetwork(params.network).bip32 : undefined);
15267
+ try {
15268
+ return accountFor(master, seed, params.template, params.network, params.groupIndex);
15269
+ } finally {
15270
+ master.wipePrivateData();
15271
+ }
15272
+ });
15273
+ };
15274
+ var chainSign = async (ctx, params, session) => {
15275
+ const { dek } = ctx.session.require(session);
15276
+ const { request, verify } = params;
15277
+ const { account } = request;
15278
+ if (account.kind !== "hd") {
15279
+ throw new KernelError("unsupported", "imported accounts are not yet supported");
15280
+ }
15281
+ const [family] = request.kind.split(".", 1);
15282
+ const expected = family === "evm" ? ["evm.bip44"] : family === "solana" ? ["solana.bip44-change"] : ["btc.bip44", "btc.bip49", "btc.bip84", "btc.bip86"];
15283
+ if (!expected.includes(account.template)) {
15284
+ throw new KernelError("invalid-input", "account template does not match the request family");
15285
+ }
15286
+ const record = ctx.image.records.find((entry) => entry.kind === "entropy" && entry.id === account.walletId);
15287
+ if (record === undefined) {
15288
+ throw new KernelError("not-found", "no such wallet");
15289
+ }
15290
+ const execute = (seed, intentOnly) => {
15291
+ if (request.kind === "evm.transaction" || request.kind === "evm.personal-message" || request.kind === "evm.typed-data") {
15292
+ return executeEvm(request, account, seed, intentOnly);
15358
15293
  }
15294
+ if (request.kind === "bitcoin.psbt" || request.kind === "bitcoin.message") {
15295
+ return executeBitcoin(ctx.platform.random, request, account, seed, intentOnly);
15296
+ }
15297
+ return executeSolana(request, account, seed, intentOnly);
15298
+ };
15299
+ const withSeed = async (run) => withWalletSeed(ctx.platform.pbkdf2, dek, record, run);
15300
+ const prepared = await withSeed((seed) => execute(seed, true));
15301
+ if (!("prompt" in prepared)) {
15302
+ throw new KernelError("integrity", "unreachable: prepare pass signed");
15303
+ }
15304
+ await verifyFactor(ctx.platform, ctx.image, verify, dek, () => chainPrompt(promptLocale(ctx.platform.keystore?.locale ?? "en"), prepared.prompt));
15305
+ ctx.session.require(session);
15306
+ const signed = await withSeed((seed) => execute(seed, false));
15307
+ if ("prompt" in signed) {
15308
+ throw new KernelError("integrity", "unreachable: sign pass did not sign");
15309
+ }
15310
+ return signed;
15311
+ };
15312
+ function createChainsModule() {
15313
+ const vault = {
15314
+ ownsRecord: () => false,
15315
+ claimUnlocked: (records) => records,
15316
+ forget: () => {
15317
+ return;
15318
+ },
15319
+ lock: () => {
15320
+ return;
15321
+ },
15322
+ walletDeleteOps: () => []
15323
+ };
15324
+ const handlers = {
15325
+ "wallet.accounts": walletAccounts,
15326
+ "chain.sign": chainSign
15359
15327
  };
15360
15328
  return { vault, handlers };
15361
15329
  }
15362
15330
 
15363
15331
  // ../wallet-kernel/src/modules/nostr/derive.ts
15364
15332
  var NIP06_PATH = "m/44'/1237'/i'/0/0";
15365
- function nostrKeyAt(master, groupIndex) {
15366
- if (!Number.isSafeInteger(groupIndex) || groupIndex < 0 || groupIndex > 2147483647) {
15367
- throw new KernelError("invalid-input", "group index out of range");
15333
+ function nostrKeyAt(master, index) {
15334
+ if (!Number.isSafeInteger(index) || index < 0 || index > 2147483647) {
15335
+ throw new KernelError("invalid-input", "identity index out of range");
15368
15336
  }
15369
- const path = NIP06_PATH.split("/").map((segment) => segment === "i'" ? `${groupIndex}'` : segment).join("/");
15337
+ const path = NIP06_PATH.split("/").map((segment) => segment === "i'" ? `${index}'` : segment).join("/");
15370
15338
  const node = master.derive(path);
15371
15339
  try {
15372
15340
  const key = node.privateKey;
@@ -15381,32 +15349,6 @@ function nostrKeyAt(master, groupIndex) {
15381
15349
 
15382
15350
  // ../../node_modules/.bun/@qntx+nostr@0.15.3+ea9f70ecde856c26/node_modules/@qntx/nostr/dist/nips/nip19.mjs
15383
15351
  var Bech32MaxSize2 = 5000;
15384
- var Nip19Error2 = class extends NostrError {
15385
- name = "Nip19Error";
15386
- };
15387
- function parseTLV(data) {
15388
- const result = {};
15389
- let rest = data;
15390
- while (rest.length > 0) {
15391
- if (rest.length < 2)
15392
- throw new Nip19Error2("not enough data to read TLV");
15393
- const t = rest.at(0);
15394
- const l = rest.at(1);
15395
- if (t === undefined || l === undefined)
15396
- throw new Nip19Error2("not enough data to read TLV");
15397
- const v = rest.slice(2, 2 + l);
15398
- rest = rest.slice(2 + l);
15399
- if (v.length < l)
15400
- throw new Nip19Error2(`not enough data to read on TLV ${t}`);
15401
- result[t] = result[t] ?? [];
15402
- result[t].push(v);
15403
- }
15404
- return result;
15405
- }
15406
- function assertNip19Kind(kind) {
15407
- if (!Number.isInteger(kind) || kind < 0 || kind > 65535)
15408
- throw new Nip19Error2(`invalid kind: ${kind}`);
15409
- }
15410
15352
  function encodeBech32(prefix, data) {
15411
15353
  const words = bech32.toWords(data);
15412
15354
  return bech32.encode(prefix, words, Bech32MaxSize2);
@@ -15418,106 +15360,6 @@ function nsecEncode2(key) {
15418
15360
  assertByteLength(key, 32, "secret key");
15419
15361
  return encodeBytes("nsec", key);
15420
15362
  }
15421
- function readUint32(bytes) {
15422
- return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0);
15423
- }
15424
- function decode3(code) {
15425
- let prefix;
15426
- let data;
15427
- try {
15428
- const { prefix: hrp, words } = bech32.decode(code, Bech32MaxSize2);
15429
- prefix = hrp;
15430
- data = new Uint8Array(bech32.fromWords(words));
15431
- } catch (error) {
15432
- throw new Nip19Error2(`invalid bech32: ${errorMessage(error)}`, { cause: error });
15433
- }
15434
- switch (prefix) {
15435
- case "nprofile": {
15436
- const tlv = parseTLV(data);
15437
- if (!tlv[0]?.[0])
15438
- throw new Nip19Error2("missing TLV 0 for nprofile");
15439
- if (tlv[0][0].length !== 32)
15440
- throw new Nip19Error2("TLV 0 should be 32 bytes");
15441
- return {
15442
- type: "nprofile",
15443
- data: {
15444
- pubkey: bytesToHex$1(tlv[0][0]),
15445
- relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
15446
- }
15447
- };
15448
- }
15449
- case "nevent": {
15450
- const tlv = parseTLV(data);
15451
- if (!tlv[0]?.[0])
15452
- throw new Nip19Error2("missing TLV 0 for nevent");
15453
- if (tlv[0][0].length !== 32)
15454
- throw new Nip19Error2("TLV 0 should be 32 bytes");
15455
- if (tlv[2]?.[0] && tlv[2][0].length !== 32)
15456
- throw new Nip19Error2("TLV 2 should be 32 bytes");
15457
- if (tlv[3]?.[0] && tlv[3][0].length !== 4)
15458
- throw new Nip19Error2("TLV 3 should be 4 bytes");
15459
- const pointer = {
15460
- id: bytesToHex$1(tlv[0][0]),
15461
- relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
15462
- };
15463
- const author = tlv[2]?.[0];
15464
- if (author !== undefined)
15465
- pointer.author = bytesToHex$1(author);
15466
- const kind = tlv[3]?.[0];
15467
- if (kind !== undefined) {
15468
- pointer.kind = readUint32(kind);
15469
- assertNip19Kind(pointer.kind);
15470
- }
15471
- return {
15472
- type: "nevent",
15473
- data: pointer
15474
- };
15475
- }
15476
- case "naddr": {
15477
- const tlv = parseTLV(data);
15478
- if (!tlv[0]?.[0])
15479
- throw new Nip19Error2("missing TLV 0 for naddr");
15480
- if (!tlv[2]?.[0])
15481
- throw new Nip19Error2("missing TLV 2 for naddr");
15482
- if (tlv[2][0].length !== 32)
15483
- throw new Nip19Error2("TLV 2 should be 32 bytes");
15484
- if (!tlv[3]?.[0])
15485
- throw new Nip19Error2("missing TLV 3 for naddr");
15486
- if (tlv[3][0].length !== 4)
15487
- throw new Nip19Error2("TLV 3 should be 4 bytes");
15488
- const naddrKind = readUint32(tlv[3][0]);
15489
- assertNip19Kind(naddrKind);
15490
- return {
15491
- type: "naddr",
15492
- data: {
15493
- identifier: utf8Decoder.decode(tlv[0][0]),
15494
- pubkey: bytesToHex$1(tlv[2][0]),
15495
- kind: naddrKind,
15496
- relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
15497
- }
15498
- };
15499
- }
15500
- case "nsec":
15501
- if (data.length !== 32)
15502
- throw new Nip19Error2("nsec must be 32 bytes");
15503
- if (!secp256k1.utils.isValidSecretKey(data))
15504
- throw new Nip19Error2("invalid nsec secret key");
15505
- return {
15506
- type: "nsec",
15507
- data
15508
- };
15509
- case "npub":
15510
- case "note":
15511
- if (data.length !== 32)
15512
- throw new Nip19Error2(`${prefix} must be 32 bytes`);
15513
- return {
15514
- type: prefix,
15515
- data: bytesToHex$1(data)
15516
- };
15517
- default:
15518
- throw new Nip19Error2(`unknown prefix ${prefix}`);
15519
- }
15520
- }
15521
15363
 
15522
15364
  // ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/scrypt.js
15523
15365
  function XorAndSalsa(prev, pi, input, ii, out, oi) {
@@ -15682,7 +15524,6 @@ async function scryptAsync(password, salt, opts) {
15682
15524
  var VERSION2 = 2;
15683
15525
  var SALT_LEN = 16;
15684
15526
  var NONCE_LEN = 24;
15685
- var PAYLOAD_LEN = 91;
15686
15527
  var LOGN_MIN = 1;
15687
15528
  var LOGN_MAX = 22;
15688
15529
  var SCRYPT_R = 8;
@@ -15726,60 +15567,6 @@ async function encrypt4(secretKey, password, opts) {
15726
15567
  const bytes = concatBytes(Uint8Array.from([VERSION2]), Uint8Array.from([logn]), salt, nonce, aad, ciphertext);
15727
15568
  return encodeBytes("ncryptsec", bytes);
15728
15569
  }
15729
- async function decrypt4(ncryptsec, password, opts) {
15730
- const { maxLogN } = opts;
15731
- if (!Number.isInteger(maxLogN) || maxLogN < LOGN_MIN || maxLogN > LOGN_MAX)
15732
- throw new Nip49Error(`invalid maxLogN ${maxLogN}, expected integer ${LOGN_MIN}..${LOGN_MAX}`);
15733
- let prefix;
15734
- let b;
15735
- try {
15736
- const decoded = bech32.decode(ncryptsec, Bech32MaxSize2);
15737
- ({ prefix } = decoded);
15738
- b = new Uint8Array(bech32.fromWords(decoded.words));
15739
- } catch (error) {
15740
- throw new Nip49Error("invalid ncryptsec", { cause: error });
15741
- }
15742
- if (prefix !== "ncryptsec")
15743
- throw new Nip49Error(`invalid prefix ${prefix}, expected 'ncryptsec'`);
15744
- if (b.length !== PAYLOAD_LEN)
15745
- throw new Nip49Error("invalid ncryptsec length");
15746
- const version = b.at(0);
15747
- if (version === undefined)
15748
- throw new Nip49Error("invalid ncryptsec length");
15749
- if (version !== VERSION2)
15750
- throw new Nip49Error(`invalid version ${version}, expected 0x02`);
15751
- const logn = b.at(1);
15752
- if (logn === undefined)
15753
- throw new Nip49Error("invalid ncryptsec length");
15754
- if (logn > maxLogN)
15755
- throw new Nip49Error(`logn ${logn} exceeds maxLogN ${maxLogN}`);
15756
- const salt = b.subarray(2, 18);
15757
- const nonce = b.subarray(18, 42);
15758
- const ksb = b.at(42);
15759
- if (ksb === undefined)
15760
- throw new Nip49Error("invalid ncryptsec length");
15761
- if (ksb !== 0 && ksb !== 1 && ksb !== 2)
15762
- throw new Nip49Error(`invalid key security byte ${ksb}, expected 0x00, 0x01, or 0x02`);
15763
- const aad = Uint8Array.from([ksb]);
15764
- const ciphertext = b.subarray(43);
15765
- try {
15766
- const key = await deriveKey(password, salt, logn, opts.scrypt ?? nobleScrypt);
15767
- try {
15768
- const secret = xchacha20poly1305(key, nonce, aad).decrypt(ciphertext);
15769
- if (!secp256k1.utils.isValidSecretKey(secret)) {
15770
- secret.fill(0);
15771
- throw new Nip49Error("invalid secret key");
15772
- }
15773
- return secret;
15774
- } finally {
15775
- key.fill(0);
15776
- }
15777
- } catch (error) {
15778
- if (error instanceof Nip49Error)
15779
- throw error;
15780
- throw new Nip49Error("failed to decrypt", { cause: error });
15781
- }
15782
- }
15783
15570
 
15784
15571
  // ../wallet-kernel/src/modules/nostr/pool.ts
15785
15572
  class SignerPool {
@@ -15822,63 +15609,6 @@ class SignerPool {
15822
15609
  this.signers.clear();
15823
15610
  }
15824
15611
  }
15825
- function generateSecretKey(random, backend) {
15826
- for (;; ) {
15827
- const candidate = random(32);
15828
- try {
15829
- Keys.fromSecretKey(candidate, backend);
15830
- return candidate;
15831
- } catch {
15832
- wipe(candidate);
15833
- }
15834
- }
15835
- }
15836
- function secretKeyFromNsec(nsec) {
15837
- const text = decodeUtf8Secret(nsec);
15838
- try {
15839
- const decoded = decode3(text);
15840
- if (decoded.type !== "nsec") {
15841
- throw new KernelError("invalid-input", "expected an nsec bech32 string");
15842
- }
15843
- return new Uint8Array(decoded.data);
15844
- } catch (error) {
15845
- if (error instanceof KernelError) {
15846
- throw error;
15847
- }
15848
- throw new KernelError("invalid-input", "invalid nsec");
15849
- }
15850
- }
15851
- async function secretKeyFromNcryptsec(ncryptsec, password, maxLogN, scrypt) {
15852
- const text = decodeUtf8Secret(password);
15853
- let payload;
15854
- try {
15855
- const decoded = bech32.decode(ncryptsec, Bech32MaxSize2);
15856
- payload = new Uint8Array(bech32.fromWords(decoded.words));
15857
- if (decoded.prefix !== "ncryptsec" || payload.length !== 91 || payload.at(0) !== 2) {
15858
- throw new KernelError("invalid-input", "malformed ncryptsec");
15859
- }
15860
- } catch (error) {
15861
- if (error instanceof KernelError) {
15862
- throw error;
15863
- }
15864
- throw new KernelError("invalid-input", "malformed ncryptsec");
15865
- }
15866
- const logN = payload.at(1) ?? 0;
15867
- if (logN > maxLogN) {
15868
- throw new KernelError("unsupported", "ncryptsec scrypt logN exceeds the policy limit");
15869
- }
15870
- try {
15871
- return await decrypt4(ncryptsec, text, {
15872
- maxLogN,
15873
- ...scrypt === undefined ? {} : { scrypt }
15874
- });
15875
- } catch (error) {
15876
- if (error instanceof Nip49Error && error.cause instanceof RangeError) {
15877
- throw new KernelError("unavailable", "ncryptsec scrypt allocation failed");
15878
- }
15879
- throw new KernelError("wrong-password", "invalid ncryptsec or wrong password");
15880
- }
15881
- }
15882
15612
  function nsecFromSecretKey(secretKey) {
15883
15613
  return new TextEncoder().encode(nsecEncode2(secretKey));
15884
15614
  }
@@ -15918,58 +15648,27 @@ async function unwrapGiftWrap(pool, publicKey, event, backend) {
15918
15648
  // ../wallet-kernel/src/modules/nostr/record.ts
15919
15649
  var NOSTR_KEY_KIND = "nostr-key";
15920
15650
  function encodeNostrKeyHeader(header) {
15921
- if (header.origin === "derived") {
15922
- return JSON.stringify({
15923
- publicKey: header.publicKey,
15924
- origin: header.origin,
15925
- walletId: header.walletId,
15926
- groupIndex: header.groupIndex,
15927
- createdAt: header.createdAt
15928
- });
15929
- }
15930
- return header.origin === "nip46-client" ? JSON.stringify({
15931
- publicKey: header.publicKey,
15932
- origin: header.origin,
15933
- createdAt: header.createdAt
15934
- }) : JSON.stringify({
15651
+ return JSON.stringify({
15935
15652
  publicKey: header.publicKey,
15936
- origin: header.origin,
15937
- backedUp: header.backedUp,
15653
+ walletId: header.walletId,
15654
+ index: header.index,
15938
15655
  createdAt: header.createdAt
15939
15656
  });
15940
15657
  }
15941
15658
  function parseNostrKeyHeader(text) {
15942
15659
  const object = headerObject(text);
15943
- const publicKey = asPublicKey(object["publicKey"]);
15944
- const createdAt = asCreatedAt(object["createdAt"]);
15945
- if (object["origin"] === "derived") {
15946
- expectKeys(object, ["publicKey", "origin", "walletId", "groupIndex", "createdAt"]);
15947
- return {
15948
- publicKey,
15949
- origin: "derived",
15950
- walletId: asWalletId(object["walletId"]),
15951
- groupIndex: asGroupIndex(object["groupIndex"]),
15952
- createdAt
15953
- };
15954
- }
15955
- if (object["origin"] === "nip46-client") {
15956
- expectKeys(object, ["publicKey", "origin", "createdAt"]);
15957
- return { publicKey, origin: "nip46-client", createdAt };
15958
- }
15959
- expectKeys(object, ["publicKey", "origin", "backedUp", "createdAt"]);
15960
- const { origin } = object;
15961
- if (origin !== "nsec" && origin !== "ncryptsec" && origin !== "generated") {
15962
- throw headerFailure();
15963
- }
15964
- return { publicKey, origin, backedUp: asBoolean(object["backedUp"]), createdAt };
15660
+ expectKeys(object, ["publicKey", "walletId", "index", "createdAt"]);
15661
+ return {
15662
+ publicKey: asPublicKey(object["publicKey"]),
15663
+ walletId: asWalletId(object["walletId"]),
15664
+ index: asIdentityIndex(object["index"]),
15665
+ createdAt: asCreatedAt(object["createdAt"])
15666
+ };
15965
15667
  }
15966
15668
  function nostrKeyInfo2(header) {
15967
- const standalone = header.origin !== "derived" && header.origin !== "nip46-client";
15968
15669
  return {
15969
- ref: header.origin === "derived" ? { kind: "derived", walletId: header.walletId, groupIndex: header.groupIndex } : header.origin === "nip46-client" ? { kind: "nip46-client", id: header.publicKey } : { kind: "standalone", id: header.publicKey },
15670
+ ref: { kind: "derived", walletId: header.walletId, index: header.index },
15970
15671
  publicKey: header.publicKey,
15971
- origin: header.origin,
15972
- ...standalone ? { backedUp: header.backedUp } : {},
15973
15672
  createdAt: header.createdAt
15974
15673
  };
15975
15674
  }
@@ -15977,14 +15676,39 @@ function nostrStatus2(records) {
15977
15676
  const keys = [];
15978
15677
  let corrupted = false;
15979
15678
  for (const record of records) {
15980
- try {
15981
- keys.push(nostrKeyInfo2(parseNostrKeyHeader(record.header)));
15982
- } catch {
15983
- corrupted = true;
15679
+ if (record.kind === NOSTR_KEY_KIND) {
15680
+ try {
15681
+ keys.push(nostrKeyInfo2(parseNostrKeyHeader(record.header)));
15682
+ } catch {
15683
+ corrupted = true;
15684
+ }
15685
+ } else if (record.kind === NOSTR_SYNC_KEY_KIND) {
15686
+ try {
15687
+ parseSyncKeyHeader(record.header);
15688
+ } catch {
15689
+ corrupted = true;
15690
+ }
15984
15691
  }
15985
15692
  }
15986
15693
  return { keys, corrupted };
15987
15694
  }
15695
+ var NOSTR_SYNC_KEY_KIND = "nostr-sync-key";
15696
+ function encodeSyncKeyHeader(header) {
15697
+ return JSON.stringify({
15698
+ publicKey: header.publicKey,
15699
+ walletId: header.walletId,
15700
+ createdAt: header.createdAt
15701
+ });
15702
+ }
15703
+ function parseSyncKeyHeader(text) {
15704
+ const object = headerObject(text);
15705
+ expectKeys(object, ["publicKey", "walletId", "createdAt"]);
15706
+ return {
15707
+ publicKey: asPublicKey(object["publicKey"]),
15708
+ walletId: asWalletId(object["walletId"]),
15709
+ createdAt: asCreatedAt(object["createdAt"])
15710
+ };
15711
+ }
15988
15712
 
15989
15713
  // ../wallet-kernel/src/modules/nostr/keys.ts
15990
15714
  function keyRecord(ctx, dek, secretKey, header) {
@@ -15995,114 +15719,57 @@ function ensureNewKey(ctx, publicKey) {
15995
15719
  throw new KernelError("already-exists", "nostr key already exists");
15996
15720
  }
15997
15721
  }
15998
- async function addKey(ctx, secretKey, origin, backedUp, factor, requestSession) {
15999
- const gated = gate(ctx, factor, requestSession);
16000
- const publicKey = getPublicKey(secretKey);
16001
- ensureNewKey(ctx, publicKey);
16002
- const header = {
16003
- publicKey,
16004
- origin,
16005
- backedUp,
16006
- createdAt: ctx.platform.now()
16007
- };
16008
- const record = keyRecord(ctx, gated.dek, secretKey, header);
16009
- const session = await commitRecords(ctx, gated, factor, [record]);
16010
- return { key: nostrKeyInfo2(header), session };
16011
- }
16012
- async function importKey(ctx, params, requestSession) {
16013
- const { input } = params;
16014
- const secretKey = input.kind === "nsec" ? secretKeyFromNsec(input.nsec) : await secretKeyFromNcryptsec(input.ncryptsec, input.password, ctx.platform.policy.maxScryptLogN);
16015
- try {
16016
- return await addKey(ctx, secretKey, input.kind, true, params.factor, requestSession);
16017
- } finally {
16018
- wipe(secretKey);
16019
- }
16020
- }
16021
- async function generateKey(ctx, params, requestSession) {
16022
- const secretKey = generateSecretKey(ctx.platform.random, ctx.platform.backends.nostr);
16023
- try {
16024
- return await addKey(ctx, secretKey, "generated", false, params.factor, requestSession);
16025
- } finally {
16026
- wipe(secretKey);
16027
- }
16028
- }
16029
- async function createNip46ClientKey(ctx, requestSession) {
16030
- const { dek } = ctx.session.require(requestSession);
16031
- const secretKey = generateSecretKey(ctx.platform.random, ctx.platform.backends.nostr);
16032
- try {
16033
- const publicKey = getPublicKey(secretKey);
16034
- ensureNewKey(ctx, publicKey);
16035
- const header = {
16036
- publicKey,
16037
- origin: "nip46-client",
16038
- createdAt: ctx.platform.now()
16039
- };
16040
- await ctx.commit([{ op: "put-record", record: keyRecord(ctx, dek, secretKey, header) }]);
16041
- return nostrKeyInfo2(header);
16042
- } finally {
16043
- wipe(secretKey);
16044
- }
16045
- }
16046
15722
  async function deriveKey2(ctx, params, requestSession) {
16047
15723
  const { dek } = ctx.session.require(requestSession);
16048
15724
  const record = findRecord(ctx.image, "entropy", params.walletId);
16049
- const entropy = openRecord(dek, record);
16050
- const seed = await seedFromEntropy(entropy, ctx.platform.pbkdf2);
16051
- wipe(entropy);
16052
- const master = masterKey(seed);
16053
- wipe(seed);
16054
- try {
16055
- const secretKey = nostrKeyAt(master, params.groupIndex);
15725
+ return withWalletSeed(ctx.platform.pbkdf2, dek, record, async (seed) => {
15726
+ const master = masterKey(seed);
16056
15727
  try {
16057
- const publicKey = getPublicKey(secretKey);
16058
- ensureNewKey(ctx, publicKey);
16059
- const header = {
16060
- publicKey,
16061
- origin: "derived",
16062
- walletId: params.walletId,
16063
- groupIndex: params.groupIndex,
16064
- createdAt: ctx.platform.now()
16065
- };
16066
- await ctx.commit([{ op: "put-record", record: keyRecord(ctx, dek, secretKey, header) }]);
16067
- return nostrKeyInfo2(header);
15728
+ const secretKey = nostrKeyAt(master, params.index);
15729
+ try {
15730
+ const publicKey = getPublicKey(secretKey);
15731
+ ensureNewKey(ctx, publicKey);
15732
+ const header = {
15733
+ publicKey,
15734
+ walletId: params.walletId,
15735
+ index: params.index,
15736
+ createdAt: ctx.platform.now()
15737
+ };
15738
+ await ctx.commit([{ op: "put-record", record: keyRecord(ctx, dek, secretKey, header) }]);
15739
+ return nostrKeyInfo2(header);
15740
+ } finally {
15741
+ wipe(secretKey);
15742
+ }
16068
15743
  } finally {
16069
- wipe(secretKey);
15744
+ master.wipePrivateData();
16070
15745
  }
16071
- } finally {
16072
- master.wipePrivateData();
16073
- }
15746
+ });
16074
15747
  }
16075
15748
  async function discover(ctx, params, requestSession) {
16076
15749
  const { dek } = ctx.session.require(requestSession);
16077
15750
  const record = findRecord(ctx.image, "entropy", params.walletId);
16078
- const entropy = openRecord(dek, record);
15751
+ if (params.from + params.count - 1 > 2147483646) {
15752
+ throw new KernelError("invalid-input", "identity range exceeds 0x7ffffffe");
15753
+ }
16079
15754
  const keys = [];
16080
- const seed = await seedFromEntropy(entropy, ctx.platform.pbkdf2);
16081
- wipe(entropy);
16082
- const master = masterKey(seed);
16083
- wipe(seed);
16084
- try {
16085
- for (let i = 0;i < params.count; i++) {
16086
- const groupIndex = params.from + i;
16087
- const secretKey = nostrKeyAt(master, groupIndex);
16088
- keys.push({ groupIndex, publicKey: getPublicKey(secretKey) });
16089
- wipe(secretKey);
15755
+ return withWalletSeed(ctx.platform.pbkdf2, dek, record, (seed) => {
15756
+ const master = masterKey(seed);
15757
+ try {
15758
+ for (let i = 0;i < params.count; i++) {
15759
+ const index = params.from + i;
15760
+ const secretKey = nostrKeyAt(master, index);
15761
+ keys.push({ index, publicKey: getPublicKey(secretKey) });
15762
+ wipe(secretKey);
15763
+ }
15764
+ return { keys };
15765
+ } finally {
15766
+ master.wipePrivateData();
16090
15767
  }
16091
- return { keys };
16092
- } finally {
16093
- master.wipePrivateData();
16094
- }
15768
+ });
16095
15769
  }
16096
15770
  async function removeKey(ctx, params, requestSession) {
16097
- const { dek } = ctx.session.require(requestSession);
16098
- const record = findRecord(ctx.image, NOSTR_KEY_KIND, params.publicKey);
16099
- const header = parseNostrKeyHeader(record.header);
16100
- if (header.origin !== "nip46-client") {
16101
- if (params.verify === undefined) {
16102
- throw new KernelError("invalid-input", "factor verification is required");
16103
- }
16104
- await verifyFactor(ctx.platform, ctx.image, params.verify, dek, corePromptFor(ctx.platform.keystore, "remove-key"));
16105
- }
15771
+ ctx.session.require(requestSession);
15772
+ findRecord(ctx.image, NOSTR_KEY_KIND, params.publicKey);
16106
15773
  await ctx.commit([{ op: "delete-record", kind: NOSTR_KEY_KIND, id: params.publicKey }]);
16107
15774
  return null;
16108
15775
  }
@@ -16110,60 +15777,132 @@ async function exportKey(ctx, params, requestSession) {
16110
15777
  const { dek } = ctx.session.require(requestSession);
16111
15778
  await verifyFactor(ctx.platform, ctx.image, params.verify, dek, corePromptFor(ctx.platform.keystore, "export-key"));
16112
15779
  const record = findRecord(ctx.image, NOSTR_KEY_KIND, params.publicKey);
16113
- const header = parseNostrKeyHeader(record.header);
16114
15780
  const secretKey = openRecord(dek, record);
16115
15781
  try {
16116
15782
  const exported = params.format.kind === "nsec" ? nsecFromSecretKey(secretKey) : await ncryptsecFromSecretKey(secretKey, params.format.password);
16117
- if (header.origin !== "derived" && header.origin !== "nip46-client" && !header.backedUp) {
16118
- const updated = { ...header, backedUp: true };
16119
- await ctx.commit([
16120
- {
16121
- op: "put-record",
16122
- record: keyRecord(ctx, dek, secretKey, updated)
15783
+ return exported;
15784
+ } finally {
15785
+ wipe(secretKey);
15786
+ }
15787
+ }
15788
+
15789
+ // ../wallet-kernel/src/modules/nostr/ops.ts
15790
+ async function sign4(ctx, pool, params, requestSession) {
15791
+ ctx.session.require(requestSession);
15792
+ return signEvent2(pool, params.publicKey, params.event);
15793
+ }
15794
+ async function nip04Encrypt(ctx, pool, params, requestSession) {
15795
+ ctx.session.require(requestSession);
15796
+ return pool.get(params.publicKey).nip04Encrypt(params.peer, params.text);
15797
+ }
15798
+ async function nip04Decrypt(ctx, pool, params, requestSession) {
15799
+ ctx.session.require(requestSession);
15800
+ return pool.get(params.publicKey).nip04Decrypt(params.peer, params.text);
15801
+ }
15802
+ async function nip44Encrypt(ctx, pool, params, requestSession) {
15803
+ ctx.session.require(requestSession);
15804
+ return pool.get(params.publicKey).nip44Encrypt(params.peer, params.text);
15805
+ }
15806
+ async function nip44Decrypt(ctx, pool, params, requestSession) {
15807
+ ctx.session.require(requestSession);
15808
+ return pool.get(params.publicKey).nip44Decrypt(params.peer, params.text);
15809
+ }
15810
+ async function unwrapGiftWraps(ctx, pool, params, requestSession) {
15811
+ ctx.session.require(requestSession);
15812
+ const results = [];
15813
+ for (const event of params.events) {
15814
+ results.push(await unwrapGiftWrap(pool, params.publicKey, event, ctx.platform.backends.nostr));
15815
+ }
15816
+ return { results };
15817
+ }
15818
+
15819
+ // ../wallet-kernel/src/modules/nostr/sync.ts
15820
+ var SYNC_INDEX = 2147483647;
15821
+ function syncRecord(ctx, walletId) {
15822
+ findRecord(ctx.image, "entropy", walletId);
15823
+ for (const record of ctx.image.records) {
15824
+ if (record.kind === NOSTR_SYNC_KEY_KIND) {
15825
+ const header = parseSyncKeyHeader(record.header);
15826
+ if (header.walletId === walletId) {
15827
+ return record;
15828
+ }
15829
+ }
15830
+ }
15831
+ throw new KernelError("not-found", "no sync key for this wallet");
15832
+ }
15833
+ async function syncKey(ctx, params, requestSession) {
15834
+ const { dek } = ctx.session.require(requestSession);
15835
+ try {
15836
+ return { publicKey: parseSyncKeyHeader(syncRecord(ctx, params.walletId).header).publicKey };
15837
+ } catch (error) {
15838
+ if (!(error instanceof KernelError && error.code === "not-found")) {
15839
+ throw error;
15840
+ }
15841
+ }
15842
+ const wallet = findRecord(ctx.image, "entropy", params.walletId);
15843
+ return withWalletSeed(ctx.platform.pbkdf2, dek, wallet, async (seed) => {
15844
+ const master = masterKey(seed);
15845
+ try {
15846
+ const secretKey = nostrKeyAt(master, SYNC_INDEX);
15847
+ try {
15848
+ const publicKey = getPublicKey(secretKey);
15849
+ const header = {
15850
+ publicKey,
15851
+ walletId: params.walletId,
15852
+ createdAt: ctx.platform.now()
15853
+ };
15854
+ const record = sealRecord(ctx.platform, dek, NOSTR_SYNC_KEY_KIND, publicKey, encodeSyncKeyHeader(header), new Uint8Array(secretKey));
15855
+ try {
15856
+ await ctx.commit([{ op: "put-record", record }]);
15857
+ } catch (error) {
15858
+ if (!(error instanceof KernelError && error.code === "conflict")) {
15859
+ throw error;
15860
+ }
15861
+ return {
15862
+ publicKey: parseSyncKeyHeader(syncRecord(ctx, params.walletId).header).publicKey
15863
+ };
16123
15864
  }
16124
- ]);
15865
+ return { publicKey };
15866
+ } finally {
15867
+ wipe(secretKey);
15868
+ }
15869
+ } finally {
15870
+ master.wipePrivateData();
16125
15871
  }
16126
- return exported;
16127
- } finally {
16128
- wipe(secretKey);
16129
- }
16130
- }
16131
-
16132
- // ../wallet-kernel/src/modules/nostr/ops.ts
16133
- async function sign4(ctx, pool, params, requestSession) {
16134
- ctx.session.require(requestSession);
16135
- return signEvent2(pool, params.publicKey, params.event);
16136
- }
16137
- async function nip04Encrypt(ctx, pool, params, requestSession) {
16138
- ctx.session.require(requestSession);
16139
- return pool.get(params.publicKey).nip04Encrypt(params.peer, params.text);
15872
+ });
16140
15873
  }
16141
- async function nip04Decrypt(ctx, pool, params, requestSession) {
16142
- ctx.session.require(requestSession);
16143
- return pool.get(params.publicKey).nip04Decrypt(params.peer, params.text);
15874
+ function requireSyncSigner(ctx, pool, walletId) {
15875
+ const record = syncRecord(ctx, walletId);
15876
+ const { publicKey } = parseSyncKeyHeader(record.header);
15877
+ return pool.get(publicKey);
16144
15878
  }
16145
- async function nip44Encrypt(ctx, pool, params, requestSession) {
15879
+ async function syncSign(ctx, pool, params, requestSession) {
16146
15880
  ctx.session.require(requestSession);
16147
- return pool.get(params.publicKey).nip44Encrypt(params.peer, params.text);
15881
+ if (params.event.kind !== 30078) {
15882
+ throw new KernelError("invalid-input", "sync signer authors kind-30078 events only");
15883
+ }
15884
+ const signer = requireSyncSigner(ctx, pool, params.walletId);
15885
+ return signEvent2(pool, signer.keys.publicKey, params.event);
16148
15886
  }
16149
- async function nip44Decrypt(ctx, pool, params, requestSession) {
15887
+ async function syncEncrypt(ctx, pool, params, requestSession) {
16150
15888
  ctx.session.require(requestSession);
16151
- return pool.get(params.publicKey).nip44Decrypt(params.peer, params.text);
15889
+ const signer = requireSyncSigner(ctx, pool, params.walletId);
15890
+ const { publicKey } = signer.keys;
15891
+ return signer.nip44Encrypt(publicKey, params.text);
16152
15892
  }
16153
- async function unwrapGiftWraps(ctx, pool, params, requestSession) {
15893
+ async function syncDecrypt(ctx, pool, params, requestSession) {
16154
15894
  ctx.session.require(requestSession);
16155
- const results = [];
16156
- for (const event of params.events) {
16157
- results.push(await unwrapGiftWrap(pool, params.publicKey, event, ctx.platform.backends.nostr));
16158
- }
16159
- return { results };
15895
+ const signer = requireSyncSigner(ctx, pool, params.walletId);
15896
+ const { publicKey } = signer.keys;
15897
+ return signer.nip44Decrypt(publicKey, params.text);
16160
15898
  }
16161
15899
 
16162
15900
  // ../wallet-kernel/src/modules/nostr/index.ts
16163
15901
  function createNostrModule(backend) {
16164
15902
  const pool = new SignerPool(backend);
15903
+ const syncPool = new SignerPool(backend);
16165
15904
  const vault = {
16166
- ownsRecord: (kind) => kind === NOSTR_KEY_KIND,
15905
+ ownsRecord: (kind) => kind === NOSTR_KEY_KIND || kind === NOSTR_SYNC_KEY_KIND,
16167
15906
  claimUnlocked: (records) => {
16168
15907
  const unclaimed = [];
16169
15908
  for (const record of records) {
@@ -16172,6 +15911,11 @@ function createNostrModule(backend) {
16172
15911
  if (claimed !== record.id) {
16173
15912
  throw new KernelError("integrity", "nostr key does not match its record", undefined, true);
16174
15913
  }
15914
+ } else if (record.kind === NOSTR_SYNC_KEY_KIND) {
15915
+ const claimed = syncPool.add(record.plaintext);
15916
+ if (claimed !== record.id) {
15917
+ throw new KernelError("integrity", "sync key does not match its record", undefined, true);
15918
+ }
16175
15919
  } else {
16176
15920
  unclaimed.push(record);
16177
15921
  }
@@ -16181,61 +15925,35 @@ function createNostrModule(backend) {
16181
15925
  forget: (kind, id) => {
16182
15926
  if (kind === NOSTR_KEY_KIND) {
16183
15927
  pool.remove(id);
15928
+ } else if (kind === NOSTR_SYNC_KEY_KIND) {
15929
+ syncPool.remove(id);
16184
15930
  }
16185
15931
  },
16186
15932
  lock: () => {
16187
15933
  pool.dispose();
15934
+ syncPool.dispose();
16188
15935
  },
16189
15936
  walletDeleteOps: (image, walletId) => {
16190
15937
  const ops = [];
16191
15938
  for (const record of image.records) {
16192
- if (record.kind !== NOSTR_KEY_KIND) {
16193
- continue;
16194
- }
16195
- const header = parseNostrKeyHeader(record.header);
16196
- if (header.origin === "derived" && header.walletId === walletId) {
16197
- ops.push({ op: "delete-record", kind: NOSTR_KEY_KIND, id: record.id });
15939
+ if (record.kind === NOSTR_KEY_KIND) {
15940
+ const header = parseNostrKeyHeader(record.header);
15941
+ if (header.walletId === walletId) {
15942
+ ops.push({ op: "delete-record", kind: NOSTR_KEY_KIND, id: record.id });
15943
+ }
15944
+ } else if (record.kind === NOSTR_SYNC_KEY_KIND) {
15945
+ const header = parseSyncKeyHeader(record.header);
15946
+ if (header.walletId === walletId) {
15947
+ ops.push({ op: "delete-record", kind: NOSTR_SYNC_KEY_KIND, id: record.id });
15948
+ }
16198
15949
  }
16199
15950
  }
16200
15951
  return ops;
16201
- },
16202
- backupSecrets: (image, dek) => {
16203
- const secrets = [];
16204
- for (const record of image.records) {
16205
- if (record.kind !== NOSTR_KEY_KIND) {
16206
- continue;
16207
- }
16208
- const header = parseNostrKeyHeader(record.header);
16209
- if (header.origin === "derived" || header.origin === "nip46-client") {
16210
- continue;
16211
- }
16212
- secrets.push(openRecord(dek, record));
16213
- }
16214
- return secrets;
16215
- },
16216
- backupRecords: (platform, image, dek, secrets, now) => {
16217
- const records = [];
16218
- for (const secretKey of secrets) {
16219
- const publicKey = getPublicKey(secretKey);
16220
- if (image.records.some((r) => r.kind === NOSTR_KEY_KIND && r.id === publicKey)) {
16221
- continue;
16222
- }
16223
- records.push(sealRecord(platform, dek, NOSTR_KEY_KIND, publicKey, encodeNostrKeyHeader({
16224
- publicKey,
16225
- origin: "nsec",
16226
- backedUp: true,
16227
- createdAt: now
16228
- }), secretKey));
16229
- }
16230
- return records;
16231
15952
  }
16232
15953
  };
16233
15954
  const handlers = {
16234
15955
  "nostr.discover": async (ctx, params, session) => discover(ctx, params, session),
16235
15956
  "nostr.deriveKey": async (ctx, params, session) => deriveKey2(ctx, params, session),
16236
- "nostr.importKey": async (ctx, params, session) => importKey(ctx, params, session),
16237
- "nostr.generateKey": async (ctx, params, session) => generateKey(ctx, params, session),
16238
- "nostr.createNip46ClientKey": async (ctx, _params, session) => createNip46ClientKey(ctx, session),
16239
15957
  "nostr.removeKey": async (ctx, params, session) => removeKey(ctx, params, session),
16240
15958
  "nostr.exportKey": async (ctx, params, session) => exportKey(ctx, params, session),
16241
15959
  "nostr.signEvent": async (ctx, params, session) => sign4(ctx, pool, params, session),
@@ -16243,7 +15961,11 @@ function createNostrModule(backend) {
16243
15961
  "nostr.nip04Decrypt": async (ctx, params, session) => nip04Decrypt(ctx, pool, params, session),
16244
15962
  "nostr.nip44Encrypt": async (ctx, params, session) => nip44Encrypt(ctx, pool, params, session),
16245
15963
  "nostr.nip44Decrypt": async (ctx, params, session) => nip44Decrypt(ctx, pool, params, session),
16246
- "nostr.unwrapGiftWraps": async (ctx, params, session) => unwrapGiftWraps(ctx, pool, params, session)
15964
+ "nostr.unwrapGiftWraps": async (ctx, params, session) => unwrapGiftWraps(ctx, pool, params, session),
15965
+ "nostr.syncKey": async (ctx, params, session) => syncKey(ctx, params, session),
15966
+ "nostr.syncSign": async (ctx, params, session) => syncSign(ctx, syncPool, params, session),
15967
+ "nostr.syncEncrypt": async (ctx, params, session) => syncEncrypt(ctx, syncPool, params, session),
15968
+ "nostr.syncDecrypt": async (ctx, params, session) => syncDecrypt(ctx, syncPool, params, session)
16247
15969
  };
16248
15970
  return {
16249
15971
  vault,
@@ -16427,228 +16149,241 @@ var decodeIntent = (rt, account, intent) => {
16427
16149
  }
16428
16150
  return result;
16429
16151
  };
16430
- function createSparkModule() {
16431
- const signers = new Map;
16432
- const approvals = new Map;
16433
- let backendPromise;
16434
- let runtimePromise;
16435
- const sparkRuntime = async () => {
16436
- runtimePromise ??= (async () => {
16437
- try {
16438
- return await loadSparkRuntime();
16439
- } catch (error) {
16440
- runtimePromise = undefined;
16441
- throw error instanceof KernelError ? error : new KernelError("unavailable", "spark runtime failed to load");
16442
- }
16443
- })();
16444
- return runtimePromise;
16445
- };
16446
- const liveApprovals = (ctx, key) => {
16447
- const now = ctx.platform.elapsed();
16448
- const live = (approvals.get(key) ?? []).filter((approval) => approval.deadline > now);
16449
- if (live.length === 0) {
16450
- approvals.delete(key);
16451
- } else {
16452
- approvals.set(key, live);
16453
- }
16454
- return live;
16455
- };
16456
- const backend = async (ctx) => {
16457
- backendPromise ??= (async () => {
16458
- try {
16459
- return await ctx.platform.backends.spark();
16460
- } catch (error) {
16461
- backendPromise = undefined;
16462
- throw error instanceof KernelError ? error : new KernelError("unavailable", "spark backend failed to load");
16463
- }
16464
- })();
16465
- return backendPromise;
16466
- };
16467
- const disposeAll = () => {
16468
- for (const signer of signers.values()) {
16469
- signer.dispose();
16470
- }
16471
- signers.clear();
16472
- approvals.clear();
16473
- };
16474
- const disposeWallet = (walletId) => {
16475
- for (const [key, signer] of signers) {
16476
- if (key.startsWith(`${walletId}/`)) {
16477
- signer.dispose();
16478
- signers.delete(key);
16479
- }
16480
- }
16481
- for (const key of approvals.keys()) {
16482
- if (key.startsWith(`${walletId}/`)) {
16483
- approvals.delete(key);
16484
- }
16485
- }
16486
- };
16487
- const poison = () => {
16488
- backendPromise = undefined;
16489
- disposeAll();
16490
- return new KernelError("unavailable", "spark backend was disposed after a wasm fault");
16491
- };
16492
- const mapError = (error) => {
16493
- if (error instanceof KernelError) {
16494
- return error;
16495
- }
16496
- if (isNamed(error, "WasmPoisonedError")) {
16497
- return poison();
16498
- }
16499
- if (isNamed(error, "SparkInputError")) {
16500
- return new KernelError("invalid-input", "spark signer rejected the request");
16501
- }
16502
- return new KernelError("integrity", "spark signer call failed");
16503
- };
16504
- const signerFor = async (ctx, account, requestSession) => {
16505
- const { dek } = ctx.session.require(requestSession);
16506
- const record = ctx.image.records.find((entry) => entry.kind === "entropy" && entry.id === account.walletId);
16507
- if (record === undefined) {
16508
- disposeWallet(account.walletId);
16509
- throw new KernelError("not-found", "no such wallet");
16510
- }
16511
- const existing = signers.get(poolKey(account));
16512
- if (existing !== undefined) {
16513
- return existing;
16514
- }
16515
- const entropy = openRecord(dek, record);
16516
- const seed = await seedFromEntropy(entropy, ctx.platform.pbkdf2);
16517
- wipe(entropy);
16518
- let accountKey;
16519
- const rt = await sparkRuntime();
16152
+ var sparkRuntime = async (state) => {
16153
+ state.runtimePromise ??= (async () => {
16520
16154
  try {
16521
- accountKey = rt.sparkAccountKey(seed, account.network, account.network === "mainnet" ? account.groupIndex + 1 : account.groupIndex);
16155
+ return await loadSparkRuntime();
16522
16156
  } catch (error) {
16523
- throw mapError(error);
16524
- } finally {
16525
- wipe(seed);
16157
+ state.runtimePromise = undefined;
16158
+ throw error instanceof KernelError ? error : new KernelError("unavailable", "spark runtime failed to load");
16526
16159
  }
16527
- const crypto2 = await backend(ctx);
16160
+ })();
16161
+ return state.runtimePromise;
16162
+ };
16163
+ var liveApprovals = (state, ctx, key) => {
16164
+ const now = ctx.platform.elapsed();
16165
+ const live = (state.approvals.get(key) ?? []).filter((approval) => approval.deadline > now);
16166
+ if (live.length === 0) {
16167
+ state.approvals.delete(key);
16168
+ } else {
16169
+ state.approvals.set(key, live);
16170
+ }
16171
+ return live;
16172
+ };
16173
+ var backend = async (state, ctx) => {
16174
+ state.backendPromise ??= (async () => {
16528
16175
  try {
16529
- const signer = rt.createSparkSigner({
16530
- crypto: crypto2,
16531
- accountKey,
16532
- network: account.network,
16533
- random: ctx.platform.random,
16534
- authorize: async (outflow) => authorizeWithCtx(ctx, account, outflow)
16535
- });
16536
- signers.set(poolKey(account), signer);
16537
- return signer;
16176
+ return await ctx.platform.backends.spark();
16538
16177
  } catch (error) {
16539
- throw mapError(error);
16540
- } finally {
16541
- wipe(accountKey);
16178
+ state.backendPromise = undefined;
16179
+ throw error instanceof KernelError ? error : new KernelError("unavailable", "spark backend failed to load");
16542
16180
  }
16543
- };
16544
- const authorizeWithCtx = async (ctx, account, outflow) => {
16545
- const rt = await sparkRuntime();
16546
- if (outflow.kind === "swap") {
16547
- return;
16181
+ })();
16182
+ return state.backendPromise;
16183
+ };
16184
+ var disposeAll = (state) => {
16185
+ for (const signer of state.signers.values()) {
16186
+ signer.dispose();
16187
+ }
16188
+ state.signers.clear();
16189
+ state.approvals.clear();
16190
+ };
16191
+ var disposeWallet = (state, walletId) => {
16192
+ for (const [key, signer] of state.signers) {
16193
+ if (key.startsWith(`${walletId}/`)) {
16194
+ signer.dispose();
16195
+ state.signers.delete(key);
16548
16196
  }
16549
- const key = poolKey(account);
16550
- const live = liveApprovals(ctx, key);
16551
- for (const approval of live) {
16552
- const { intent } = approval;
16553
- if (outflow.kind === "transfer" && intent.kind === "transfer") {
16554
- const covered = outflow.receivers.every((receiver) => intent.receivers.some((wanted) => equalBytes(wanted.identityPublicKey, receiver.identityPublicKey) && receiver.amountSats <= wanted.amountSats));
16555
- if (covered) {
16556
- live.splice(live.indexOf(approval), 1);
16557
- return;
16558
- }
16559
- continue;
16560
- }
16561
- if (outflow.kind === "lightning" && intent.kind === "lightning") {
16562
- const signer = signers.get(key);
16563
- const { paymentHash } = approval.decoded;
16564
- const budget = approval.decoded.invoiceAmountSats;
16565
- if (signer === undefined || paymentHash === undefined || budget === undefined) {
16566
- continue;
16567
- }
16568
- const expected = rt.lightningSendHtlcScript({
16569
- paymentHash,
16570
- sspIdentityPublicKey: rt.sparkNetworkConfig(account.network).ssp.identityPublicKey,
16571
- identityPublicKey: await signer.identityPublicKey()
16572
- });
16573
- if (!equalBytes(expected, outflow.htlcOutputScript)) {
16574
- continue;
16575
- }
16576
- const fresh = outflow.leaves.filter((leaf) => !approval.spentLeaves.has(bytesToHex(leaf.publicKey)));
16577
- const total = fresh.reduce((sum, leaf) => sum + leaf.amountSats, 0) + [...approval.spentLeaves.values()].reduce((sum, v) => sum + v, 0);
16578
- if (total > budget + intent.maxFeeSats) {
16579
- continue;
16580
- }
16581
- for (const leaf of fresh) {
16582
- approval.spentLeaves.set(bytesToHex(leaf.publicKey), leaf.amountSats);
16583
- }
16584
- return;
16585
- }
16586
- if (outflow.kind === "exit" && intent.kind === "exit") {
16587
- const { addressScript } = approval.decoded;
16588
- if (addressScript === undefined) {
16589
- continue;
16590
- }
16591
- const pays = outflow.exitOutputs.some((output) => equalBytes(output.script, addressScript) && output.amountSats >= intent.amountSats);
16592
- if (!pays) {
16593
- continue;
16594
- }
16595
- const fresh = outflow.leaves.filter((leaf) => !approval.spentLeaves.has(bytesToHex(leaf.publicKey)));
16596
- const total = fresh.reduce((sum, leaf) => sum + leaf.amountSats, 0) + [...approval.spentLeaves.values()].reduce((sum, v) => sum + v, 0);
16597
- if (total > intent.amountSats + intent.maxFeeSats) {
16598
- continue;
16599
- }
16600
- for (const leaf of fresh) {
16601
- approval.spentLeaves.set(bytesToHex(leaf.publicKey), leaf.amountSats);
16602
- }
16603
- return;
16604
- }
16605
- if (outflow.kind === "onchain" && intent.kind === "onchain") {
16606
- const { addressScript } = approval.decoded;
16607
- if (addressScript === undefined) {
16608
- continue;
16609
- }
16610
- const every = outflow.outputs.every((output) => equalBytes(output.script, rt.EPHEMERAL_ANCHOR_SCRIPT) || equalBytes(output.script, addressScript));
16611
- if (!every) {
16612
- continue;
16613
- }
16614
- const fee = outflow.inputSats - outflow.totalSats;
16615
- if (fee > intent.maxFeeSats) {
16616
- continue;
16617
- }
16618
- live.splice(live.indexOf(approval), 1);
16619
- return;
16620
- }
16621
- if (outflow.kind === "token" && intent.kind === "token") {
16622
- const signer = signers.get(key);
16623
- if (signer === undefined) {
16624
- continue;
16625
- }
16626
- const identity = await signer.identityPublicKey();
16627
- const covered = outflow.outputs.every((output) => {
16628
- if (equalBytes(output.ownerPublicKey, identity)) {
16629
- return true;
16630
- }
16631
- if (!equalBytes(output.tokenIdentifier, intent.tokenIdentifier)) {
16632
- return false;
16633
- }
16634
- return intent.receivers.some((wanted) => equalBytes(wanted.ownerPublicKey, output.ownerPublicKey) && output.amount <= BigInt(wanted.amount));
16635
- });
16636
- if (!covered) {
16637
- continue;
16638
- }
16639
- live.splice(live.indexOf(approval), 1);
16640
- return;
16641
- }
16197
+ }
16198
+ for (const key of state.approvals.keys()) {
16199
+ if (key.startsWith(`${walletId}/`)) {
16200
+ state.approvals.delete(key);
16642
16201
  }
16643
- throw approvalRequired();
16644
- };
16645
- const call = async (ctx, account, requestSession, run) => {
16646
- const signer = await signerFor(ctx, account, requestSession);
16647
- try {
16648
- return await run(signer);
16649
- } catch (error) {
16650
- throw mapError(error);
16202
+ }
16203
+ };
16204
+ var poison = (state) => {
16205
+ state.backendPromise = undefined;
16206
+ disposeAll(state);
16207
+ return new KernelError("unavailable", "spark backend was disposed after a wasm fault");
16208
+ };
16209
+ var mapError = (state, error) => {
16210
+ if (error instanceof KernelError) {
16211
+ return error;
16212
+ }
16213
+ if (isNamed(error, "WasmPoisonedError")) {
16214
+ return poison(state);
16215
+ }
16216
+ if (isNamed(error, "SparkInputError")) {
16217
+ return new KernelError("invalid-input", "spark signer rejected the request");
16218
+ }
16219
+ return new KernelError("integrity", "spark signer call failed");
16220
+ };
16221
+ var signerFor = async (state, ctx, account, requestSession) => {
16222
+ const { dek } = ctx.session.require(requestSession);
16223
+ const record = ctx.image.records.find((entry) => entry.kind === "entropy" && entry.id === account.walletId);
16224
+ if (record === undefined) {
16225
+ disposeWallet(state, account.walletId);
16226
+ throw new KernelError("not-found", "no such wallet");
16227
+ }
16228
+ const existing = state.signers.get(poolKey(account));
16229
+ if (existing !== undefined) {
16230
+ return existing;
16231
+ }
16232
+ let accountKey;
16233
+ const rt = await sparkRuntime(state);
16234
+ try {
16235
+ accountKey = await withWalletSeed(ctx.platform.pbkdf2, dek, record, (seed) => rt.sparkAccountKey(seed, account.network, account.network === "mainnet" ? account.groupIndex + 1 : account.groupIndex));
16236
+ } catch (error) {
16237
+ throw mapError(state, error);
16238
+ }
16239
+ const crypto2 = await backend(state, ctx);
16240
+ try {
16241
+ const signer = rt.createSparkSigner({
16242
+ crypto: crypto2,
16243
+ accountKey,
16244
+ network: account.network,
16245
+ random: ctx.platform.random,
16246
+ authorize: async (outflow) => authorizeWithCtx(state, ctx, account, outflow)
16247
+ });
16248
+ state.signers.set(poolKey(account), signer);
16249
+ return signer;
16250
+ } catch (error) {
16251
+ throw mapError(state, error);
16252
+ } finally {
16253
+ wipe(accountKey);
16254
+ }
16255
+ };
16256
+ var transferCovered = (outflow, intent) => outflow.receivers.every((receiver) => intent.receivers.some((wanted) => equalBytes(wanted.identityPublicKey, receiver.identityPublicKey) && receiver.amountSats <= wanted.amountSats));
16257
+ var lightningCovered = async (state, rt, key, network, outflow, approval, intent) => {
16258
+ const signer = state.signers.get(key);
16259
+ const { paymentHash } = approval.decoded;
16260
+ const budget = approval.decoded.invoiceAmountSats;
16261
+ if (signer === undefined || paymentHash === undefined || budget === undefined) {
16262
+ return false;
16263
+ }
16264
+ const expected = rt.lightningSendHtlcScript({
16265
+ paymentHash,
16266
+ sspIdentityPublicKey: rt.sparkNetworkConfig(network).ssp.identityPublicKey,
16267
+ identityPublicKey: await signer.identityPublicKey()
16268
+ });
16269
+ if (!equalBytes(expected, outflow.htlcOutputScript)) {
16270
+ return false;
16271
+ }
16272
+ const fresh = outflow.leaves.filter((leaf) => !approval.spentLeaves.has(bytesToHex(leaf.publicKey)));
16273
+ const total = fresh.reduce((sum, leaf) => sum + leaf.amountSats, 0) + [...approval.spentLeaves.values()].reduce((sum, v) => sum + v, 0);
16274
+ if (total > budget + intent.maxFeeSats) {
16275
+ return false;
16276
+ }
16277
+ for (const leaf of fresh) {
16278
+ approval.spentLeaves.set(bytesToHex(leaf.publicKey), leaf.amountSats);
16279
+ }
16280
+ return true;
16281
+ };
16282
+ var exitCovered = (outflow, approval, intent) => {
16283
+ const { addressScript } = approval.decoded;
16284
+ if (addressScript === undefined) {
16285
+ return false;
16286
+ }
16287
+ const pays = outflow.exitOutputs.some((output) => equalBytes(output.script, addressScript) && output.amountSats >= intent.amountSats);
16288
+ if (!pays) {
16289
+ return false;
16290
+ }
16291
+ const fresh = outflow.leaves.filter((leaf) => !approval.spentLeaves.has(bytesToHex(leaf.publicKey)));
16292
+ const total = fresh.reduce((sum, leaf) => sum + leaf.amountSats, 0) + [...approval.spentLeaves.values()].reduce((sum, v) => sum + v, 0);
16293
+ if (total > intent.amountSats + intent.maxFeeSats) {
16294
+ return false;
16295
+ }
16296
+ for (const leaf of fresh) {
16297
+ approval.spentLeaves.set(bytesToHex(leaf.publicKey), leaf.amountSats);
16298
+ }
16299
+ return true;
16300
+ };
16301
+ var onchainCovered = (rt, outflow, approval, intent) => {
16302
+ const { addressScript } = approval.decoded;
16303
+ if (addressScript === undefined) {
16304
+ return false;
16305
+ }
16306
+ const every = outflow.outputs.every((output) => equalBytes(output.script, rt.EPHEMERAL_ANCHOR_SCRIPT) || equalBytes(output.script, addressScript));
16307
+ if (!every) {
16308
+ return false;
16309
+ }
16310
+ const fee = outflow.inputSats - outflow.totalSats;
16311
+ return fee <= intent.maxFeeSats;
16312
+ };
16313
+ var tokenCovered = async (state, key, outflow, intent) => {
16314
+ const signer = state.signers.get(key);
16315
+ if (signer === undefined) {
16316
+ return false;
16317
+ }
16318
+ const identity = await signer.identityPublicKey();
16319
+ return outflow.outputs.every((output) => {
16320
+ if (equalBytes(output.ownerPublicKey, identity)) {
16321
+ return true;
16322
+ }
16323
+ if (!equalBytes(output.tokenIdentifier, intent.tokenIdentifier)) {
16324
+ return false;
16325
+ }
16326
+ return intent.receivers.some((wanted) => equalBytes(wanted.ownerPublicKey, output.ownerPublicKey) && output.amount <= BigInt(wanted.amount));
16327
+ });
16328
+ };
16329
+ var authorizeWithCtx = async (state, ctx, account, outflow) => {
16330
+ const rt = await sparkRuntime(state);
16331
+ if (outflow.kind === "swap") {
16332
+ return;
16333
+ }
16334
+ const key = poolKey(account);
16335
+ const live = liveApprovals(state, ctx, key);
16336
+ for (const approval of live) {
16337
+ const { intent } = approval;
16338
+ const covered = outflow.kind === "transfer" && intent.kind === "transfer" ? transferCovered(outflow, intent) : outflow.kind === "lightning" && intent.kind === "lightning" ? await lightningCovered(state, rt, key, account.network, outflow, approval, intent) : outflow.kind === "exit" && intent.kind === "exit" ? exitCovered(outflow, approval, intent) : outflow.kind === "onchain" && intent.kind === "onchain" ? onchainCovered(rt, outflow, approval, intent) : outflow.kind === "token" && intent.kind === "token" ? await tokenCovered(state, key, outflow, intent) : false;
16339
+ if (covered) {
16340
+ live.splice(live.indexOf(approval), 1);
16341
+ return;
16651
16342
  }
16343
+ }
16344
+ throw approvalRequired();
16345
+ };
16346
+ var call = async (state, ctx, account, requestSession, run) => {
16347
+ const signer = await signerFor(state, ctx, account, requestSession);
16348
+ try {
16349
+ return await run(signer);
16350
+ } catch (error) {
16351
+ throw mapError(state, error);
16352
+ }
16353
+ };
16354
+ var sparkApprove = async (state, ctx, params, session) => {
16355
+ const { dek } = ctx.session.require(session);
16356
+ const record = ctx.image.records.find((entry) => entry.kind === "entropy" && entry.id === params.account.walletId);
16357
+ if (record === undefined) {
16358
+ throw new KernelError("not-found", "no such wallet");
16359
+ }
16360
+ const key = poolKey(params.account);
16361
+ if (liveApprovals(state, ctx, key).length >= APPROVAL_MAX_PER_ACCOUNT) {
16362
+ throw new KernelError("invalid-input", "approval cap reached for this account");
16363
+ }
16364
+ const { summary, decoded } = decodeIntent(await sparkRuntime(state), params.account, params.intent);
16365
+ await verifyFactor(ctx.platform, ctx.image, params.verify, dek, () => sparkPrompt(promptLocale(ctx.platform.keystore?.locale ?? "en"), summary));
16366
+ ctx.session.require(session);
16367
+ const live = liveApprovals(state, ctx, key);
16368
+ if (live.length >= APPROVAL_MAX_PER_ACCOUNT) {
16369
+ throw new KernelError("invalid-input", "approval cap reached for this account");
16370
+ }
16371
+ live.push({
16372
+ intent: params.intent,
16373
+ deadline: ctx.platform.elapsed() + APPROVAL_TTL_MS,
16374
+ decoded,
16375
+ summary,
16376
+ spentLeaves: new Map
16377
+ });
16378
+ state.approvals.set(key, live);
16379
+ return summary;
16380
+ };
16381
+ function createSparkModule() {
16382
+ const state = {
16383
+ signers: new Map,
16384
+ approvals: new Map,
16385
+ backendPromise: undefined,
16386
+ runtimePromise: undefined
16652
16387
  };
16653
16388
  const vault = {
16654
16389
  ownsRecord: () => false,
@@ -16657,56 +16392,28 @@ function createSparkModule() {
16657
16392
  return;
16658
16393
  },
16659
16394
  lock: () => {
16660
- disposeAll();
16395
+ disposeAll(state);
16661
16396
  },
16662
- backupSecrets: () => [],
16663
- backupRecords: () => [],
16664
16397
  walletDeleteOps: (_image, walletId) => {
16665
- disposeWallet(walletId);
16398
+ disposeWallet(state, walletId);
16666
16399
  return [];
16667
16400
  }
16668
16401
  };
16669
16402
  const handlers = {
16670
- "spark.identityPublicKey": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.identityPublicKey()),
16671
- "spark.publicKeys": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.publicKeys(params.keys)),
16672
- "spark.receivedPublicKeys": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.receivedPublicKeys(params.ciphertexts)),
16673
- "spark.newCommitments": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.newCommitments(params.count)),
16674
- "spark.signFrost": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.signFrost(params.jobs)),
16675
- "spark.aggregateFrost": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.aggregateFrost(params.jobs)),
16676
- "spark.signIdentity": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.signIdentity(params.statement)),
16677
- "spark.signKeySpend": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.signKeySpend(params.key, params.target)),
16678
- "spark.signHtlcTimeout": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.signHtlcTimeout(params.request)),
16679
- "spark.prepareTransfer": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.prepareTransfer(params.request)),
16680
- "spark.prepareClaim": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.prepareClaim(params.request)),
16681
- "spark.prepareLightningReceive": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.prepareLightningReceive()),
16682
- "spark.claimStaticDeposit": async (ctx, params, session) => call(ctx, params.account, session, async (signer) => signer.claimStaticDeposit(params.request)),
16683
- "spark.approve": async (ctx, params, session) => {
16684
- const { dek } = ctx.session.require(session);
16685
- const record = ctx.image.records.find((entry) => entry.kind === "entropy" && entry.id === params.account.walletId);
16686
- if (record === undefined) {
16687
- throw new KernelError("not-found", "no such wallet");
16688
- }
16689
- const key = poolKey(params.account);
16690
- if (liveApprovals(ctx, key).length >= APPROVAL_MAX_PER_ACCOUNT) {
16691
- throw new KernelError("invalid-input", "approval cap reached for this account");
16692
- }
16693
- const { summary, decoded } = decodeIntent(await sparkRuntime(), params.account, params.intent);
16694
- await verifyFactor(ctx.platform, ctx.image, params.verify, dek, () => sparkPrompt(promptLocale(ctx.platform.keystore?.locale ?? "en"), summary));
16695
- ctx.session.require(session);
16696
- const live = liveApprovals(ctx, key);
16697
- if (live.length >= APPROVAL_MAX_PER_ACCOUNT) {
16698
- throw new KernelError("invalid-input", "approval cap reached for this account");
16699
- }
16700
- live.push({
16701
- intent: params.intent,
16702
- deadline: ctx.platform.elapsed() + APPROVAL_TTL_MS,
16703
- decoded,
16704
- summary,
16705
- spentLeaves: new Map
16706
- });
16707
- approvals.set(key, live);
16708
- return summary;
16709
- }
16403
+ "spark.identityPublicKey": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.identityPublicKey()),
16404
+ "spark.publicKeys": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.publicKeys(params.keys)),
16405
+ "spark.receivedPublicKeys": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.receivedPublicKeys(params.ciphertexts)),
16406
+ "spark.newCommitments": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.newCommitments(params.count)),
16407
+ "spark.signFrost": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.signFrost(params.jobs)),
16408
+ "spark.aggregateFrost": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.aggregateFrost(params.jobs)),
16409
+ "spark.signIdentity": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.signIdentity(params.statement)),
16410
+ "spark.signKeySpend": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.signKeySpend(params.key, params.target)),
16411
+ "spark.signHtlcTimeout": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.signHtlcTimeout(params.request)),
16412
+ "spark.prepareTransfer": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.prepareTransfer(params.request)),
16413
+ "spark.prepareClaim": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.prepareClaim(params.request)),
16414
+ "spark.prepareLightningReceive": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.prepareLightningReceive()),
16415
+ "spark.claimStaticDeposit": async (ctx, params, session) => call(state, ctx, params.account, session, async (signer) => signer.claimStaticDeposit(params.request)),
16416
+ "spark.approve": async (ctx, params, session) => sparkApprove(state, ctx, params, session)
16710
16417
  };
16711
16418
  return { vault, handlers };
16712
16419
  }
@@ -17232,6 +16939,39 @@ function isNodeWorkerData(value) {
17232
16939
  return data !== undefined && Object.keys(data).sort().join(",") === "idleMinutes,storePort" && isIdleMinutes(data["idleMinutes"]) && typeof data["storePort"] === "object" && data["storePort"] !== null;
17233
16940
  }
17234
16941
 
16942
+ // src/worker/subtle.ts
16943
+ var SUBTLE_HASH = {
16944
+ sha256: "SHA-256",
16945
+ sha512: "SHA-512"
16946
+ };
16947
+ var subtlePbkdf2 = async (hash, password, salt, iterations, length) => {
16948
+ const passwordCopy = new Uint8Array(password);
16949
+ const saltCopy = new Uint8Array(salt);
16950
+ try {
16951
+ const key = await crypto.subtle.importKey("raw", passwordCopy, "PBKDF2", false, ["deriveBits"]);
16952
+ const bits = await crypto.subtle.deriveBits({ name: "PBKDF2", hash: SUBTLE_HASH[hash] ?? "SHA-256", salt: saltCopy, iterations }, key, length * 8);
16953
+ return new Uint8Array(bits);
16954
+ } finally {
16955
+ passwordCopy.fill(0);
16956
+ }
16957
+ };
16958
+
16959
+ // src/worker/platform.ts
16960
+ var basePlatform = () => ({
16961
+ random: (length) => crypto.getRandomValues(new Uint8Array(length)),
16962
+ now: () => Date.now(),
16963
+ elapsed: () => performance.now(),
16964
+ setTimer: (delayMs, fn) => {
16965
+ const timer = setTimeout(fn, delayMs);
16966
+ return () => {
16967
+ clearTimeout(timer);
16968
+ };
16969
+ },
16970
+ pbkdf2: subtlePbkdf2,
16971
+ keystore: undefined,
16972
+ passkey: undefined
16973
+ });
16974
+
17235
16975
  // src/worker/spark.ts
17236
16976
  async function loadSparkBackend(readWasm) {
17237
16977
  const { loadSparkWasm, SPARK_WASM_INTEGRITY } = await import("./browser-1vpxmqwn.js");
@@ -17317,23 +17057,6 @@ function createStoreClient(port) {
17317
17057
  };
17318
17058
  }
17319
17059
 
17320
- // src/worker/subtle.ts
17321
- var SUBTLE_HASH = {
17322
- sha256: "SHA-256",
17323
- sha512: "SHA-512"
17324
- };
17325
- var subtlePbkdf2 = async (hash, password, salt, iterations, length) => {
17326
- const passwordCopy = new Uint8Array(password);
17327
- const saltCopy = new Uint8Array(salt);
17328
- try {
17329
- const key = await crypto.subtle.importKey("raw", passwordCopy, "PBKDF2", false, ["deriveBits"]);
17330
- const bits = await crypto.subtle.deriveBits({ name: "PBKDF2", hash: SUBTLE_HASH[hash] ?? "SHA-256", salt: saltCopy, iterations }, key, length * 8);
17331
- return new Uint8Array(bits);
17332
- } finally {
17333
- passwordCopy.fill(0);
17334
- }
17335
- };
17336
-
17337
17060
  // src/worker/node.ts
17338
17061
  var main = parentPort;
17339
17062
  if (main === null) {
@@ -17348,18 +17071,7 @@ var data = {
17348
17071
  };
17349
17072
  data.storePort.start();
17350
17073
  var platform = {
17351
- random: (length) => crypto.getRandomValues(new Uint8Array(length)),
17352
- now: () => Date.now(),
17353
- elapsed: () => performance.now(),
17354
- setTimer: (delayMs, fn) => {
17355
- const timer = setTimeout(fn, delayMs);
17356
- return () => {
17357
- clearTimeout(timer);
17358
- };
17359
- },
17360
- pbkdf2: subtlePbkdf2,
17361
- keystore: undefined,
17362
- passkey: undefined,
17074
+ ...basePlatform(),
17363
17075
  store: createStoreClient({
17364
17076
  post: (message) => {
17365
17077
  data.storePort.postMessage(message);