@dynamic-labs-wallet/node-midnight 1.1.4 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs CHANGED
@@ -1,8 +1,15 @@
1
1
  'use strict';
2
2
 
3
+ var node_crypto = require('node:crypto');
4
+ var node_util = require('node:util');
3
5
  var node = require('@dynamic-labs-wallet/node');
4
6
  var ledger = require('@midnight-ntwrk/ledger-v8');
7
+ var walletSdkAbstractions = require('@midnight-ntwrk/wallet-sdk-abstractions');
5
8
  var walletSdkAddressFormat = require('@midnight-ntwrk/wallet-sdk-address-format');
9
+ var walletSdkDustWallet = require('@midnight-ntwrk/wallet-sdk-dust-wallet');
10
+ var walletSdkFacade = require('@midnight-ntwrk/wallet-sdk-facade');
11
+ var walletSdkShielded = require('@midnight-ntwrk/wallet-sdk-shielded');
12
+ var walletSdkUnshieldedWallet = require('@midnight-ntwrk/wallet-sdk-unshielded-wallet');
6
13
  var bip32 = require('@scure/bip32');
7
14
  var bech32 = require('bech32');
8
15
 
@@ -37,14 +44,64 @@ var MIDNIGHT_NETWORK_IDS = {
37
44
  preprod: 0,
38
45
  undeployed: 0
39
46
  };
47
+ var _process_env_MIDNIGHT_PROOF_SERVER_URL;
48
+ var MIDNIGHT_PROOF_SERVER_URL = (_process_env_MIDNIGHT_PROOF_SERVER_URL = process.env.MIDNIGHT_PROOF_SERVER_URL) !== null && _process_env_MIDNIGHT_PROOF_SERVER_URL !== void 0 ? _process_env_MIDNIGHT_PROOF_SERVER_URL : 'https://proof-server.us-east-1.dynamic-preprod.xyz';
49
+ /**
50
+ * Per-network wallet-facade infrastructure. There is no public mainnet proof
51
+ * server, so Dynamic's is shared across networks for now.
52
+ */ var MIDNIGHT_INFRA = {
53
+ mainnet: {
54
+ relayUrl: 'wss://rpc.mainnet.midnight.network',
55
+ provingServerUrl: MIDNIGHT_PROOF_SERVER_URL,
56
+ indexerHttpUrl: 'https://indexer.mainnet.midnight.network/api/v4/graphql',
57
+ indexerWsUrl: 'wss://indexer.mainnet.midnight.network/api/v4/graphql/ws'
58
+ },
59
+ preview: {
60
+ relayUrl: 'wss://rpc.preview.midnight.network',
61
+ provingServerUrl: MIDNIGHT_PROOF_SERVER_URL,
62
+ indexerHttpUrl: 'https://indexer.preview.midnight.network/api/v4/graphql',
63
+ indexerWsUrl: 'wss://indexer.preview.midnight.network/api/v4/graphql/ws'
64
+ }
65
+ };
40
66
  /**
41
67
  * Resolves a caller-supplied network identifier (numeric 0/1, network name, or
42
- * `midnight:<network>` chainId) to the facade network tag. Defaults to 'preview'.
68
+ * `midnight:<network>` chainId) to the facade network tag. Omitting it defaults
69
+ * to 'preview'.
70
+ *
71
+ * Throws on anything else rather than falling back, for the same reason
72
+ * {@link getNetworkIdFromChainId} does: an unrecognized identifier that quietly
73
+ * became 'preview' would have the caller sync one network while signing with
74
+ * another network's replay-protection ID, with no error to show for it.
43
75
  */ var resolveMidnightNetworkTag = function(networkId) {
44
76
  if (networkId === undefined || networkId === null) return 'preview';
45
- if (typeof networkId === 'number') return networkId === 1 ? 'mainnet' : 'preview';
77
+ if (typeof networkId === 'number') {
78
+ if (networkId === MIDNIGHT_NETWORK_IDS.mainnet) return 'mainnet';
79
+ if (networkId === MIDNIGHT_NETWORK_IDS.preview) return 'preview';
80
+ throw new Error("".concat(ERROR_UNKNOWN_NETWORK, ": ").concat(networkId));
81
+ }
46
82
  var name = networkId.includes(':') ? networkId.split(':')[1] : networkId;
47
- return name === 'mainnet' ? 'mainnet' : 'preview';
83
+ if (name === 'mainnet' || name === 'preview') return name;
84
+ throw new Error("".concat(ERROR_UNKNOWN_NETWORK, ": ").concat(networkId));
85
+ };
86
+ /**
87
+ * Resolves a network name (or `midnight:<network>` chainId) to its numeric
88
+ * network ID.
89
+ *
90
+ * Throws on unrecognized input rather than defaulting — silently falling back to
91
+ * mainnet would bake the wrong network ID into the transaction hash and break
92
+ * replay protection.
93
+ */ var getNetworkIdFromChainId = function(networkOrChainId) {
94
+ if (networkOrChainId in MIDNIGHT_NETWORK_IDS) {
95
+ return MIDNIGHT_NETWORK_IDS[networkOrChainId];
96
+ }
97
+ if (networkOrChainId.includes(':')) {
98
+ var network = networkOrChainId.split(':')[1];
99
+ if (network && network in MIDNIGHT_NETWORK_IDS) {
100
+ return MIDNIGHT_NETWORK_IDS[network];
101
+ }
102
+ }
103
+ var known = Object.keys(MIDNIGHT_NETWORK_IDS).join(', ');
104
+ throw new Error("".concat(ERROR_UNKNOWN_NETWORK, ': "').concat(networkOrChainId, '". Expected one of: ').concat(known, ', or "midnight:<network>".'));
48
105
  };
49
106
  var ERROR_KEYGEN_FAILED = 'Error with keygen';
50
107
  var ERROR_CREATE_WALLET_ACCOUNT = 'Error creating midnight wallet account';
@@ -52,6 +109,53 @@ var ERROR_IMPORT_PRIVATE_KEY = 'Error importing private key';
52
109
  var ERROR_EXPORT_PRIVATE_KEY = 'Error exporting private key';
53
110
  var ERROR_SIGN_MESSAGE = 'Error signing message';
54
111
  var ERROR_ACCOUNT_ADDRESS_REQUIRED = 'Account address is required';
112
+ var ERROR_SIGN_TRANSACTION = 'Error signing transaction';
113
+ var ERROR_UNKNOWN_NETWORK = 'Unknown Midnight network';
114
+ var ERROR_NETWORK_ID_REQUIRED = 'Network ID is required for Midnight transactions';
115
+ var ERROR_NO_DUST = 'No dust balance — register dust first';
116
+ var ERROR_SUBMIT_TRANSACTION = 'Error submitting transaction';
117
+ var ERROR_CREATE_TRANSFER = 'Error creating transfer transaction';
118
+ var ERROR_GET_BALANCE = 'Error fetching balance';
119
+ var ERROR_REGISTER_DUST = 'Error registering dust';
120
+ var ERROR_BALANCE_TRANSACTION = 'Error balancing contract transaction';
121
+
122
+ /** Whether a sub-wallet side has finished syncing. `highestRelevantIndex` is 0
123
+ * during sync, so `isStrictlyComplete()` is the authoritative signal. */ var isSideSynced = function(side) {
124
+ var _side_state;
125
+ var _side_progress;
126
+ var p = (_side_progress = side === null || side === void 0 ? void 0 : side.progress) !== null && _side_progress !== void 0 ? _side_progress : side === null || side === void 0 ? void 0 : (_side_state = side.state) === null || _side_state === void 0 ? void 0 : _side_state.progress;
127
+ if (!p) return false;
128
+ if (typeof p.isStrictlyComplete === 'function') return p.isStrictlyComplete();
129
+ var _p_appliedIndex;
130
+ var applied = BigInt((_p_appliedIndex = p.appliedIndex) !== null && _p_appliedIndex !== void 0 ? _p_appliedIndex : 0n);
131
+ var _p_highestRelevantIndex;
132
+ var highestRelevant = BigInt((_p_highestRelevantIndex = p.highestRelevantIndex) !== null && _p_highestRelevantIndex !== void 0 ? _p_highestRelevantIndex : 0n);
133
+ return highestRelevant > 0n && applied >= highestRelevant;
134
+ };
135
+ var isSyncResolved = function(state, mode) {
136
+ return mode === 'full' ? Boolean(state.isSynced) : isSideSynced(state.shielded) && isSideSynced(state.unshielded);
137
+ };
138
+ /**
139
+ * Whether a checkpoint may be restored into a facade for `network`.
140
+ *
141
+ * A checkpoint is only meaningful for the network it was taken on — sync
142
+ * indices do not carry across — so a mismatch is treated as no checkpoint at
143
+ * all rather than as an error, since the caller may legitimately hold state for
144
+ * several networks.
145
+ */ var isRestorableState = function(state, network) {
146
+ return state !== undefined && typeof state.shielded === 'string' && typeof state.dust === 'string' && state.network === network;
147
+ };
148
+ /**
149
+ * Whether the wallet is at a clean resting point, with nothing in flight.
150
+ *
151
+ * Checkpointing mid-flight would bake pending entries into whatever the caller
152
+ * stores and carry them forward on every later restore, so a dirty wallet is
153
+ * skipped and the caller simply keeps its previous checkpoint.
154
+ */ var isCleanForCheckpoint = function(state) {
155
+ var _state_pending_all, _state_pending, _state_unshielded_pendingCoins, _state_unshielded, _state_shielded_pendingCoins, _state_shielded, _state_dust_pendingCoins, _state_dust;
156
+ var _state_pending_all_length, _state_unshielded_pendingCoins_length, _state_shielded_pendingCoins_length, _state_dust_pendingCoins_length;
157
+ return ((_state_pending_all_length = state === null || state === void 0 ? void 0 : (_state_pending = state.pending) === null || _state_pending === void 0 ? void 0 : (_state_pending_all = _state_pending.all) === null || _state_pending_all === void 0 ? void 0 : _state_pending_all.length) !== null && _state_pending_all_length !== void 0 ? _state_pending_all_length : 0) === 0 && ((_state_unshielded_pendingCoins_length = state === null || state === void 0 ? void 0 : (_state_unshielded = state.unshielded) === null || _state_unshielded === void 0 ? void 0 : (_state_unshielded_pendingCoins = _state_unshielded.pendingCoins) === null || _state_unshielded_pendingCoins === void 0 ? void 0 : _state_unshielded_pendingCoins.length) !== null && _state_unshielded_pendingCoins_length !== void 0 ? _state_unshielded_pendingCoins_length : 0) === 0 && ((_state_shielded_pendingCoins_length = state === null || state === void 0 ? void 0 : (_state_shielded = state.shielded) === null || _state_shielded === void 0 ? void 0 : (_state_shielded_pendingCoins = _state_shielded.pendingCoins) === null || _state_shielded_pendingCoins === void 0 ? void 0 : _state_shielded_pendingCoins.length) !== null && _state_shielded_pendingCoins_length !== void 0 ? _state_shielded_pendingCoins_length : 0) === 0 && ((_state_dust_pendingCoins_length = state === null || state === void 0 ? void 0 : (_state_dust = state.dust) === null || _state_dust === void 0 ? void 0 : (_state_dust_pendingCoins = _state_dust.pendingCoins) === null || _state_dust_pendingCoins === void 0 ? void 0 : _state_dust_pendingCoins.length) !== null && _state_dust_pendingCoins_length !== void 0 ? _state_dust_pendingCoins_length : 0) === 0;
158
+ };
55
159
 
56
160
  function _array_like_to_array$1(arr, len) {
57
161
  if (len == null || len > arr.length) len = arr.length;
@@ -316,6 +420,40 @@ function _iterable_to_array_limit(arr, i) {
316
420
  function _non_iterable_rest() {
317
421
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
318
422
  }
423
+ function _object_spread(target) {
424
+ for(var i = 1; i < arguments.length; i++){
425
+ var source = arguments[i] != null ? arguments[i] : {};
426
+ var ownKeys = Object.keys(source);
427
+ if (typeof Object.getOwnPropertySymbols === "function") {
428
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
429
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
430
+ }));
431
+ }
432
+ ownKeys.forEach(function(key) {
433
+ _define_property(target, key, source[key]);
434
+ });
435
+ }
436
+ return target;
437
+ }
438
+ function ownKeys(object, enumerableOnly) {
439
+ var keys = Object.keys(object);
440
+ if (Object.getOwnPropertySymbols) {
441
+ var symbols = Object.getOwnPropertySymbols(object);
442
+ keys.push.apply(keys, symbols);
443
+ }
444
+ return keys;
445
+ }
446
+ function _object_spread_props(target, source) {
447
+ source = source != null ? source : {};
448
+ if (Object.getOwnPropertyDescriptors) {
449
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
450
+ } else {
451
+ ownKeys(Object(source)).forEach(function(key) {
452
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
453
+ });
454
+ }
455
+ return target;
456
+ }
319
457
  function _possible_constructor_return(self, call) {
320
458
  if (call && (_type_of(call) === "object" || typeof call === "function")) {
321
459
  return call;
@@ -447,10 +585,117 @@ function _ts_generator(thisArg, body) {
447
585
  };
448
586
  }
449
587
  }
588
+ function _ts_values(o) {
589
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
590
+ if (m) return m.call(o);
591
+ if (o && typeof o.length === "number") return {
592
+ next: function() {
593
+ if (o && i >= o.length) o = void 0;
594
+ return {
595
+ value: o && o[i++],
596
+ done: !o
597
+ };
598
+ }
599
+ };
600
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
601
+ }
450
602
  var logError = node.createLogError('node-midnight');
451
- // Unshielded is NON-hardened to match the server's MPC signing path (Sodot's
452
- // derivePrivateKeyFromXpriv is non-hardened-only). Shielded and dust are hardened Midnight-canonical paths; those roles are signed
453
- // entirely client-side, so the Sodot constraint does not apply.
603
+ /** Strict base64, optional padding validated before host input reaches the
604
+ * WASM deserializer, which accepts silently-truncated garbage otherwise. */ var BASE64_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/;
605
+ /** Upper bound on a serialized transaction we will decode. Generous next to real
606
+ * transactions, small enough that a hostile payload is rejected before WASM. */ var MAX_SERIALIZED_TRANSACTION_BYTES = 4 * 1024 * 1024;
607
+ var SYNC_TIMEOUT_MS = 300000;
608
+ /** One retry is enough: the stale socket reports closed by the second attempt. */ var SUBMIT_ATTEMPTS = 2;
609
+ var SUBMIT_RETRY_DELAY_MS = 1000;
610
+ /**
611
+ * Whether an error is the node client's stale-connection failure rather than a
612
+ * ledger rejection.
613
+ *
614
+ * The websocket close reason sits several `cause` levels below the
615
+ * `SubmissionError` the facade surfaces, and Effect's `FiberFailure` exposes
616
+ * none of them as a readable `cause` property — the chain is only reachable
617
+ * through the rendered form, so that is what gets matched.
618
+ */ var isStaleConnectionError = function(error) {
619
+ return /disconnected from|WebSocket is not connected|Normal Closure/i.test(node_util.inspect(error, {
620
+ depth: 8
621
+ }));
622
+ };
623
+ /**
624
+ * Parses a caller-supplied transfer amount.
625
+ *
626
+ * `BigInt()` is too lenient to use directly here: `BigInt('')` is `0n`, so an
627
+ * empty amount would silently become a zero-value transfer, and `BigInt('-5')`
628
+ * yields a negative that trivially passes the balance check. Decimals do throw,
629
+ * but with an opaque message.
630
+ */ var parseTransferAmount = function(amount) {
631
+ if (typeof amount !== 'string' || !/^\d+$/.test(amount)) {
632
+ throw new Error("Invalid transfer amount ".concat(JSON.stringify(amount), ": expected a whole number in the token's smallest unit, as a string"));
633
+ }
634
+ var parsed = BigInt(amount);
635
+ if (parsed === 0n) {
636
+ throw new Error('Transfer amount must be greater than zero');
637
+ }
638
+ return parsed;
639
+ };
640
+ /**
641
+ * `submitTransaction` returns either a string txId or a TransactionIdentifier
642
+ * with `.toHex()`; normalize so callers always get a string.
643
+ */ var toTxHashString = function(txIdentifier) {
644
+ if (typeof txIdentifier === 'string') return txIdentifier;
645
+ var maybeHex = txIdentifier === null || txIdentifier === void 0 ? void 0 : txIdentifier.toHex;
646
+ if (typeof maybeHex === 'function') return txIdentifier.toHex();
647
+ return String(txIdentifier);
648
+ };
649
+ /**
650
+ * Validates caller-supplied base64 before it reaches the WASM deserializer.
651
+ * `Buffer.from(x, 'base64')` silently drops invalid characters, so a malformed
652
+ * payload would otherwise arrive as truncated-but-plausible bytes and fail deep
653
+ * inside WASM with an opaque error.
654
+ */ var decodeSerializedTransaction = function(transaction, label) {
655
+ if (!transaction || !BASE64_PATTERN.test(transaction)) {
656
+ throw new Error("".concat(label, ": transaction must be base64"));
657
+ }
658
+ var bytes = Buffer.from(transaction, 'base64');
659
+ if (bytes.length === 0 || bytes.length > MAX_SERIALIZED_TRANSACTION_BYTES) {
660
+ throw new Error("".concat(label, ": transaction must decode to 1..").concat(MAX_SERIALIZED_TRANSACTION_BYTES, " bytes, got ").concat(bytes.length));
661
+ }
662
+ return new Uint8Array(bytes);
663
+ };
664
+ /**
665
+ * Parses a caller-supplied ISO-8601 timestamp.
666
+ *
667
+ * `new Date()` alone is too lenient to enforce the contract: it accepts
668
+ * implementation-defined formats like `'March 5, 2026'`, and `'2026-13-45'`
669
+ * parses on some engines. A TTL misread by a few hours silently changes when
670
+ * the balanced transaction stops being valid, so the shape is checked first.
671
+ */ /**
672
+ * Anchored at both ends: `new Date()` also understands worded formats such as
673
+ * `'2026-09-01 1:00 PM'` and `'2026-09-01 12:00 EST'`, which are V8 extensions
674
+ * rather than ISO-8601 and parse differently — or not at all — elsewhere.
675
+ * Matching only a prefix would let those through.
676
+ *
677
+ * Spelled `\d\d` rather than `\d{2}` because each counted repetition adds to
678
+ * the regex complexity budget, and the nesting here already uses most of it.
679
+ */ var ISO_TIMESTAMP = /^(\d\d\d\d)-(\d\d)-(\d\d)([T ]\d\d:\d\d(:\d\d)?(\.\d+)?(Z|[+-]\d\d:?\d\d)?)?$/;
680
+ /** Whether the y/m/d the caller wrote is a date that exists. */ var isRealCalendarDate = function(year, month, day) {
681
+ var asUtc = new Date("".concat(year, "-").concat(month, "-").concat(day, "T00:00:00Z"));
682
+ return !Number.isNaN(asUtc.getTime()) && asUtc.getUTCDate() === Number(day);
683
+ };
684
+ var parseIsoTimestamp = function(value, label) {
685
+ // Date rolls impossible days forward instead of rejecting them, so
686
+ // '2026-02-29' becomes 1 March and the transaction outlives its stated ttl.
687
+ var match = ISO_TIMESTAMP.exec(value);
688
+ var parsed = match && isRealCalendarDate(match[1], match[2], match[3]) ? new Date(value) : new Date(Number.NaN);
689
+ if (Number.isNaN(parsed.getTime())) {
690
+ throw new TypeError("".concat(label, ": ttl must be a valid ISO-8601 date, got ").concat(JSON.stringify(value)));
691
+ }
692
+ return parsed;
693
+ };
694
+ // Unshielded is NON-hardened to match Sodot's derivePrivateKeyFromXpriv, which
695
+ // is non-hardened-only. The hardened Midnight-canonical path would make the
696
+ // wallet track a different address than the server signs for — no error, just
697
+ // silently diverging balances. Shielded and dust use the hardened canonical
698
+ // paths; those roles are signed client-side, so the constraint does not apply.
454
699
  var ROLE_DERIVATION_PATHS = {
455
700
  unshielded: 'm/44/2400/0/0/0',
456
701
  shielded: "m/44'/2400'/0'/3/0",
@@ -1043,6 +1288,1414 @@ var DynamicMidnightWalletClient = /*#__PURE__*/ function(DynamicWalletClient) {
1043
1288
  })();
1044
1289
  }
1045
1290
  },
1291
+ {
1292
+ key: "withWallet",
1293
+ value: /**
1294
+ * Boots a synced wallet, runs `use` against it, and stops it afterwards.
1295
+ *
1296
+ * Nothing is retained between calls: the wallet is created here and torn down
1297
+ * in `finally`, so the client holds no state of its own. Callers who want to
1298
+ * avoid the ~150s genesis sync pass back the checkpoint from a previous call
1299
+ * (`access.walletState`) and receive an updated one alongside every result.
1300
+ */ function withWallet(label, access, mode, use) {
1301
+ var _this = this;
1302
+ return _async_to_generator(function() {
1303
+ var network, booted, state, result, _ref, durable, session;
1304
+ return _ts_generator(this, function(_state) {
1305
+ switch(_state.label){
1306
+ case 0:
1307
+ network = resolveMidnightNetworkTag(access.networkId);
1308
+ return [
1309
+ 4,
1310
+ _this.bootWallet(label, access, network)
1311
+ ];
1312
+ case 1:
1313
+ booted = _state.sent();
1314
+ _state.label = 2;
1315
+ case 2:
1316
+ _state.trys.push([
1317
+ 2,
1318
+ ,
1319
+ 6,
1320
+ 10
1321
+ ]);
1322
+ return [
1323
+ 4,
1324
+ _this.waitForSync(booted.wallet, mode, label)
1325
+ ];
1326
+ case 3:
1327
+ state = _state.sent();
1328
+ return [
1329
+ 4,
1330
+ use(_object_spread_props(_object_spread({}, booted), {
1331
+ state: state
1332
+ }))
1333
+ ];
1334
+ case 4:
1335
+ result = _state.sent();
1336
+ return [
1337
+ 4,
1338
+ _this.checkpoint(booted.wallet, network, label)
1339
+ ];
1340
+ case 5:
1341
+ _ref = _state.sent(), durable = _ref.durable, session = _ref.session;
1342
+ return [
1343
+ 2,
1344
+ {
1345
+ result: result,
1346
+ walletState: durable,
1347
+ sessionState: session
1348
+ }
1349
+ ];
1350
+ case 6:
1351
+ _state.trys.push([
1352
+ 6,
1353
+ 8,
1354
+ ,
1355
+ 9
1356
+ ]);
1357
+ return [
1358
+ 4,
1359
+ booted.wallet.stop()
1360
+ ];
1361
+ case 7:
1362
+ _state.sent();
1363
+ return [
1364
+ 3,
1365
+ 9
1366
+ ];
1367
+ case 8:
1368
+ _state.sent();
1369
+ return [
1370
+ 3,
1371
+ 9
1372
+ ];
1373
+ case 9:
1374
+ return [
1375
+ 7
1376
+ ];
1377
+ case 10:
1378
+ return [
1379
+ 2
1380
+ ];
1381
+ }
1382
+ });
1383
+ })();
1384
+ }
1385
+ },
1386
+ {
1387
+ key: "checkpoint",
1388
+ value: /**
1389
+ * Serializes the wallet's shielded and dust state, or returns undefined when
1390
+ * the wallet has work in flight.
1391
+ *
1392
+ * Checkpointing a dirty wallet would carry pending entries forward into every
1393
+ * later restore, so the caller keeps its previous checkpoint instead — sync
1394
+ * reconciles against the chain either way.
1395
+ */ function checkpoint(wallet, network, label) {
1396
+ var _this = this;
1397
+ return _async_to_generator(function() {
1398
+ var current, _ref, shielded, dust, state, error;
1399
+ return _ts_generator(this, function(_state) {
1400
+ switch(_state.label){
1401
+ case 0:
1402
+ _state.trys.push([
1403
+ 0,
1404
+ 3,
1405
+ ,
1406
+ 4
1407
+ ]);
1408
+ return [
1409
+ 4,
1410
+ _this.readState(wallet)
1411
+ ];
1412
+ case 1:
1413
+ current = _state.sent();
1414
+ return [
1415
+ 4,
1416
+ Promise.all([
1417
+ wallet.shielded.serializeState(),
1418
+ wallet.dust.serializeState()
1419
+ ])
1420
+ ];
1421
+ case 2:
1422
+ _ref = _sliced_to_array.apply(void 0, [
1423
+ _state.sent(),
1424
+ 2
1425
+ ]), shielded = _ref[0], dust = _ref[1];
1426
+ state = {
1427
+ shielded: shielded,
1428
+ dust: dust,
1429
+ network: network,
1430
+ savedAt: Date.now()
1431
+ };
1432
+ // A dirty snapshot is still the truth about what this wallet has spent,
1433
+ // so it is kept for the session; it is only withheld from the caller,
1434
+ // who would otherwise carry pending entries forward on every restore.
1435
+ if (!isCleanForCheckpoint(current)) {
1436
+ _this.logger.info("[Midnight] ".concat(label, ": skipping checkpoint, wallet has work in flight"));
1437
+ return [
1438
+ 2,
1439
+ {
1440
+ session: state
1441
+ }
1442
+ ];
1443
+ }
1444
+ return [
1445
+ 2,
1446
+ {
1447
+ durable: state,
1448
+ session: state
1449
+ }
1450
+ ];
1451
+ case 3:
1452
+ error = _state.sent();
1453
+ // A checkpoint is an optimisation; failing to take one must not fail the
1454
+ // operation the caller actually asked for.
1455
+ _this.logger.warn("[Midnight] ".concat(label, ": could not take checkpoint"), error);
1456
+ return [
1457
+ 2,
1458
+ {}
1459
+ ];
1460
+ case 4:
1461
+ return [
1462
+ 2
1463
+ ];
1464
+ }
1465
+ });
1466
+ })();
1467
+ }
1468
+ },
1469
+ {
1470
+ key: "submitWithReconnect",
1471
+ value: /**
1472
+ * Submits, retrying once past a stale-connection failure.
1473
+ *
1474
+ * The facade's node client calls `api.disconnect()` during startup, and that
1475
+ * promise resolves before the socket has actually closed. A submit issued in
1476
+ * that window sees `isConnected === true`, skips reconnecting, and is then
1477
+ * rejected by the close that lands a moment later. The retry reconnects for
1478
+ * real, because by then the socket reports closed. Resubmitting is safe: the
1479
+ * bytes never reached the node, and an identical transaction has an identical
1480
+ * hash, so a duplicate would be rejected rather than spent twice.
1481
+ */ function submitWithReconnect(wallet, finalized) {
1482
+ var _this = this;
1483
+ return _async_to_generator(function() {
1484
+ var attempt, error;
1485
+ return _ts_generator(this, function(_state) {
1486
+ switch(_state.label){
1487
+ case 0:
1488
+ attempt = 1;
1489
+ _state.label = 1;
1490
+ case 1:
1491
+ _state.trys.push([
1492
+ 1,
1493
+ 3,
1494
+ ,
1495
+ 5
1496
+ ]);
1497
+ return [
1498
+ 4,
1499
+ wallet.submitTransaction(finalized)
1500
+ ];
1501
+ case 2:
1502
+ return [
1503
+ 2,
1504
+ _state.sent()
1505
+ ];
1506
+ case 3:
1507
+ error = _state.sent();
1508
+ if (attempt >= SUBMIT_ATTEMPTS || !isStaleConnectionError(error)) throw error;
1509
+ _this.logger.warn("[Midnight] submitTransaction: node connection was stale, reconnecting and retrying");
1510
+ return [
1511
+ 4,
1512
+ new Promise(function(resolve) {
1513
+ return setTimeout(resolve, SUBMIT_RETRY_DELAY_MS);
1514
+ })
1515
+ ];
1516
+ case 4:
1517
+ _state.sent();
1518
+ return [
1519
+ 3,
1520
+ 5
1521
+ ];
1522
+ case 5:
1523
+ attempt++;
1524
+ return [
1525
+ 3,
1526
+ 1
1527
+ ];
1528
+ case 6:
1529
+ return [
1530
+ 2
1531
+ ];
1532
+ }
1533
+ });
1534
+ })();
1535
+ }
1536
+ },
1537
+ {
1538
+ key: "readState",
1539
+ value: /** First emission from the wallet's state observable. */ function readState(wallet) {
1540
+ return _async_to_generator(function() {
1541
+ return _ts_generator(this, function(_state) {
1542
+ return [
1543
+ 2,
1544
+ new Promise(function(resolve, reject) {
1545
+ // eslint-disable-next-line prefer-const
1546
+ var sub;
1547
+ var timeout = setTimeout(function() {
1548
+ sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
1549
+ reject(new Error('timed out reading wallet state'));
1550
+ }, 10000);
1551
+ sub = wallet.state().subscribe(function(state) {
1552
+ clearTimeout(timeout);
1553
+ queueMicrotask(function() {
1554
+ return sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
1555
+ });
1556
+ resolve(state);
1557
+ });
1558
+ })
1559
+ ];
1560
+ });
1561
+ })();
1562
+ }
1563
+ },
1564
+ {
1565
+ key: "bootWallet",
1566
+ value: function bootWallet(label, access, network) {
1567
+ var _this = this;
1568
+ return _async_to_generator(function() {
1569
+ var roleKeys, shieldedSecretKeys, dustSecretKey, unshieldedKeystore, checkpoint, infra, wallet;
1570
+ return _ts_generator(this, function(_state) {
1571
+ switch(_state.label){
1572
+ case 0:
1573
+ return [
1574
+ 4,
1575
+ _this.getRoleKeys(access)
1576
+ ];
1577
+ case 1:
1578
+ roleKeys = _state.sent();
1579
+ shieldedSecretKeys = ledger__namespace.ZswapSecretKeys.fromSeed(new Uint8Array(Buffer.from(roleKeys.shielded, 'hex')));
1580
+ dustSecretKey = ledger__namespace.DustSecretKey.fromSeed(new Uint8Array(Buffer.from(roleKeys.dust, 'hex')));
1581
+ unshieldedKeystore = walletSdkUnshieldedWallet.createKeystore(new Uint8Array(Buffer.from(roleKeys.unshielded, 'hex')), network);
1582
+ // A checkpoint from another network would corrupt sync, so a mismatch is
1583
+ // treated as absent rather than as an error — callers may hold several.
1584
+ checkpoint = isRestorableState(access.walletState, network) ? access.walletState : undefined;
1585
+ infra = MIDNIGHT_INFRA[network];
1586
+ _this.logger.info("[Midnight] ".concat(label, ": initializing wallet facade (").concat(network, ", ").concat(checkpoint ? "resuming from checkpoint saved ".concat(new Date(checkpoint.savedAt).toISOString()) : 'syncing from genesis', ")"));
1587
+ return [
1588
+ 4,
1589
+ walletSdkFacade.WalletFacade.init({
1590
+ configuration: {
1591
+ networkId: network,
1592
+ costParameters: {
1593
+ feeBlocksMargin: 5
1594
+ },
1595
+ relayURL: new URL(infra.relayUrl),
1596
+ provingServerUrl: new URL(infra.provingServerUrl),
1597
+ indexerClientConnection: {
1598
+ indexerHttpUrl: infra.indexerHttpUrl,
1599
+ indexerWsUrl: infra.indexerWsUrl
1600
+ },
1601
+ // Measured: 2000 and 20000 both sync in ~150s — the fold is apply-bound
1602
+ // (per-entry WASM compute), not fetch-bound, so batch size is not a lever.
1603
+ batchUpdates: {
1604
+ size: 2000
1605
+ },
1606
+ txHistoryStorage: new walletSdkAbstractions.NoOpTransactionHistoryStorage()
1607
+ },
1608
+ shielded: function(cfg) {
1609
+ return checkpoint ? walletSdkShielded.ShieldedWallet(cfg).restore(checkpoint.shielded) : walletSdkShielded.ShieldedWallet(cfg).startWithSecretKeys(shieldedSecretKeys);
1610
+ },
1611
+ unshielded: function(cfg) {
1612
+ return walletSdkUnshieldedWallet.UnshieldedWallet(cfg).startWithPublicKey(walletSdkUnshieldedWallet.PublicKey.fromKeyStore(unshieldedKeystore));
1613
+ },
1614
+ dust: function(cfg) {
1615
+ return checkpoint ? walletSdkDustWallet.DustWallet(cfg).restore(checkpoint.dust) : walletSdkDustWallet.DustWallet(cfg).startWithSecretKey(dustSecretKey, ledger__namespace.LedgerParameters.initialParameters().dust);
1616
+ }
1617
+ })
1618
+ ];
1619
+ case 2:
1620
+ wallet = _state.sent();
1621
+ return [
1622
+ 4,
1623
+ wallet.start(shieldedSecretKeys, dustSecretKey)
1624
+ ];
1625
+ case 3:
1626
+ _state.sent();
1627
+ return [
1628
+ 2,
1629
+ {
1630
+ wallet: wallet,
1631
+ roleKeys: roleKeys,
1632
+ shieldedSecretKeys: shieldedSecretKeys,
1633
+ dustSecretKey: dustSecretKey
1634
+ }
1635
+ ];
1636
+ }
1637
+ });
1638
+ })();
1639
+ }
1640
+ },
1641
+ {
1642
+ key: "waitForSync",
1643
+ value: /**
1644
+ * Resolves once the requested sides report synced.
1645
+ *
1646
+ * Does not stop the wallet on failure — `withWallet` owns the wallet's
1647
+ * lifetime and tears it down in `finally`, whether this resolves or throws.
1648
+ */ function waitForSync(wallet, mode, label) {
1649
+ var _this = this;
1650
+ return _async_to_generator(function() {
1651
+ var startedAt, lastLoggedAt;
1652
+ return _ts_generator(this, function(_state) {
1653
+ switch(_state.label){
1654
+ case 0:
1655
+ startedAt = Date.now();
1656
+ lastLoggedAt = 0;
1657
+ return [
1658
+ 4,
1659
+ new Promise(function(resolve, reject) {
1660
+ // `let` + optional chaining: an already-synced wallet emits synchronously
1661
+ // during .subscribe(), so the callback can touch `sub` before assignment.
1662
+ // eslint-disable-next-line prefer-const
1663
+ var sub;
1664
+ var timeout = setTimeout(function() {
1665
+ sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
1666
+ reject(new Error("[Midnight] ".concat(label, ": wallet sync timeout after ").concat(SYNC_TIMEOUT_MS, "ms")));
1667
+ }, SYNC_TIMEOUT_MS);
1668
+ sub = wallet.state().subscribe(function(state) {
1669
+ var now = Date.now();
1670
+ if (now - lastLoggedAt > 15000) {
1671
+ lastLoggedAt = now;
1672
+ _this.logger.info("[Midnight] ".concat(label, ": syncing ").concat(((now - startedAt) / 1000).toFixed(0), "s \xb7 ") + "shielded=".concat(isSideSynced(state.shielded), " unshielded=").concat(isSideSynced(state.unshielded), " ") + "dust=".concat(isSideSynced(state.dust)));
1673
+ }
1674
+ if (isSyncResolved(state, mode)) {
1675
+ clearTimeout(timeout);
1676
+ queueMicrotask(function() {
1677
+ return sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
1678
+ });
1679
+ _this.logger.info("[Midnight] ".concat(label, ": synced in ").concat(((now - startedAt) / 1000).toFixed(1), "s (").concat(mode, ")"));
1680
+ resolve(state);
1681
+ }
1682
+ });
1683
+ })
1684
+ ];
1685
+ case 1:
1686
+ return [
1687
+ 2,
1688
+ _state.sent()
1689
+ ];
1690
+ }
1691
+ });
1692
+ })();
1693
+ }
1694
+ },
1695
+ {
1696
+ key: "getBalances",
1697
+ value: /**
1698
+ * Reads unshielded, shielded, and dust balances.
1699
+ *
1700
+ * Resolves as soon as shielded + unshielded are synced, leaving dust folding in
1701
+ * the background — `dustSynced` says whether the dust figure is final yet.
1702
+ */ function getBalances(access) {
1703
+ var _this = this;
1704
+ return _async_to_generator(function() {
1705
+ var _ref, result, walletState, error;
1706
+ return _ts_generator(this, function(_state) {
1707
+ switch(_state.label){
1708
+ case 0:
1709
+ _state.trys.push([
1710
+ 0,
1711
+ 2,
1712
+ ,
1713
+ 3
1714
+ ]);
1715
+ return [
1716
+ 4,
1717
+ _this.withWallet('getBalances', access, 'shielded-unshielded', /*#__PURE__*/ function() {
1718
+ var _ref = _async_to_generator(function(param) {
1719
+ var state, _state_dust_balance, _state_dust, _state_dust1, _state_unshielded, _state_shielded, toStringMap, now, _state_dust_balance1, dustBalance, _state_dust_totalCoins, dustCap;
1720
+ return _ts_generator(this, function(_state) {
1721
+ state = param.state;
1722
+ toStringMap = function() {
1723
+ var balances = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
1724
+ return Object.fromEntries(Object.entries(balances).map(function(param) {
1725
+ var _param = _sliced_to_array(param, 2), token = _param[0], value = _param[1];
1726
+ return [
1727
+ token,
1728
+ value.toString()
1729
+ ];
1730
+ }));
1731
+ };
1732
+ now = new Date();
1733
+ dustBalance = (_state_dust_balance1 = (_state_dust = state.dust) === null || _state_dust === void 0 ? void 0 : (_state_dust_balance = _state_dust.balance) === null || _state_dust_balance === void 0 ? void 0 : _state_dust_balance.call(_state_dust, now)) !== null && _state_dust_balance1 !== void 0 ? _state_dust_balance1 : 0n;
1734
+ // v4 exposes the generation cap per coin; the wallet-level cap is their sum.
1735
+ dustCap = ((_state_dust_totalCoins = (_state_dust1 = state.dust) === null || _state_dust1 === void 0 ? void 0 : _state_dust1.totalCoins) !== null && _state_dust_totalCoins !== void 0 ? _state_dust_totalCoins : []).reduce(function(sum, coin) {
1736
+ var _coin_maxCap;
1737
+ return sum + ((_coin_maxCap = coin.maxCap) !== null && _coin_maxCap !== void 0 ? _coin_maxCap : 0n);
1738
+ }, 0n);
1739
+ return [
1740
+ 2,
1741
+ {
1742
+ unshielded: toStringMap((_state_unshielded = state.unshielded) === null || _state_unshielded === void 0 ? void 0 : _state_unshielded.balances),
1743
+ shielded: toStringMap((_state_shielded = state.shielded) === null || _state_shielded === void 0 ? void 0 : _state_shielded.balances),
1744
+ dust: {
1745
+ balance: dustBalance.toString(),
1746
+ cap: dustCap.toString()
1747
+ },
1748
+ dustSynced: isSideSynced(state.dust),
1749
+ address: access.walletMetadata.accountAddress
1750
+ }
1751
+ ];
1752
+ });
1753
+ });
1754
+ return function(_) {
1755
+ return _ref.apply(this, arguments);
1756
+ };
1757
+ }())
1758
+ ];
1759
+ case 1:
1760
+ _ref = _state.sent(), result = _ref.result, walletState = _ref.walletState;
1761
+ return [
1762
+ 2,
1763
+ _object_spread_props(_object_spread({}, result), {
1764
+ walletState: walletState
1765
+ })
1766
+ ];
1767
+ case 2:
1768
+ error = _state.sent();
1769
+ logError({
1770
+ message: ERROR_GET_BALANCE,
1771
+ error: error,
1772
+ context: {
1773
+ accountAddress: access.walletMetadata.accountAddress
1774
+ }
1775
+ });
1776
+ throw _instanceof(error, Error) ? error : new Error(ERROR_GET_BALANCE);
1777
+ case 3:
1778
+ return [
1779
+ 2
1780
+ ];
1781
+ }
1782
+ });
1783
+ })();
1784
+ }
1785
+ },
1786
+ {
1787
+ key: "registerDust",
1788
+ value: /**
1789
+ * Registers the wallet's NIGHT UTXOs for dust generation. Dust pays fees on
1790
+ * every Midnight transaction, so this is a prerequisite for transacting.
1791
+ *
1792
+ * Returns immediately after broadcast — dust accrues continuously on-chain and
1793
+ * can take minutes, so poll {@link getBalances} rather than blocking here.
1794
+ */ function registerDust(access) {
1795
+ var _this = this;
1796
+ return _async_to_generator(function() {
1797
+ var _ref, result, walletState, error;
1798
+ return _ts_generator(this, function(_state) {
1799
+ switch(_state.label){
1800
+ case 0:
1801
+ _state.trys.push([
1802
+ 0,
1803
+ 2,
1804
+ ,
1805
+ 3
1806
+ ]);
1807
+ return [
1808
+ 4,
1809
+ _this.withWallet('registerDust', access, 'full', /*#__PURE__*/ function() {
1810
+ var _ref = _async_to_generator(function(param) {
1811
+ var wallet, roleKeys, state, _state_dust_balance, _state_dust, _state_unshielded, _state_dust_balance1, existingDust, _state_unshielded_availableCoins, allUtxos, unregistered, registered, verifyingKey, recipe, finalized, txId;
1812
+ return _ts_generator(this, function(_state) {
1813
+ switch(_state.label){
1814
+ case 0:
1815
+ wallet = param.wallet, roleKeys = param.roleKeys, state = param.state;
1816
+ existingDust = (_state_dust_balance1 = (_state_dust = state.dust) === null || _state_dust === void 0 ? void 0 : (_state_dust_balance = _state_dust.balance) === null || _state_dust_balance === void 0 ? void 0 : _state_dust_balance.call(_state_dust, new Date())) !== null && _state_dust_balance1 !== void 0 ? _state_dust_balance1 : 0n;
1817
+ if (existingDust > 0n) {
1818
+ return [
1819
+ 2,
1820
+ {
1821
+ status: 'already_has_dust',
1822
+ message: "Wallet already has ".concat(existingDust, " dust; no action needed."),
1823
+ registeredCount: 0,
1824
+ dustBalance: existingDust.toString()
1825
+ }
1826
+ ];
1827
+ }
1828
+ // The flag lives under `.meta`, not on the coin itself — reading it one
1829
+ // level up is silently always undefined, which makes every UTXO look
1830
+ // unregistered and re-submits a registration tx on every call. Matches
1831
+ // packages/midnight (browser), which reads the same shape.
1832
+ allUtxos = (_state_unshielded_availableCoins = (_state_unshielded = state.unshielded) === null || _state_unshielded === void 0 ? void 0 : _state_unshielded.availableCoins) !== null && _state_unshielded_availableCoins !== void 0 ? _state_unshielded_availableCoins : [];
1833
+ unregistered = allUtxos.filter(function(utxo) {
1834
+ var _utxo_meta;
1835
+ return ((_utxo_meta = utxo.meta) === null || _utxo_meta === void 0 ? void 0 : _utxo_meta.registeredForDustGeneration) !== true;
1836
+ });
1837
+ if (unregistered.length === 0) {
1838
+ registered = allUtxos.length > 0;
1839
+ return [
1840
+ 2,
1841
+ {
1842
+ status: registered ? 'already_registered' : 'no_utxos',
1843
+ message: registered ? "All ".concat(allUtxos.length, " NIGHT UTXO(s) are already registered. Dust accrues on-chain — poll getBalances.") : 'No NIGHT UTXOs. Fund the wallet with NIGHT before registering for dust.',
1844
+ registeredCount: 0,
1845
+ dustBalance: '0'
1846
+ }
1847
+ ];
1848
+ }
1849
+ verifyingKey = ledger__namespace.signatureVerifyingKey(roleKeys.unshielded);
1850
+ return [
1851
+ 4,
1852
+ wallet.registerNightUtxosForDustGeneration(unregistered, verifyingKey, function(payload) {
1853
+ return ledger__namespace.signData(roleKeys.unshielded, payload);
1854
+ })
1855
+ ];
1856
+ case 1:
1857
+ recipe = _state.sent();
1858
+ return [
1859
+ 4,
1860
+ wallet.finalizeRecipe(recipe)
1861
+ ];
1862
+ case 2:
1863
+ finalized = _state.sent();
1864
+ return [
1865
+ 4,
1866
+ wallet.submitTransaction(finalized)
1867
+ ];
1868
+ case 3:
1869
+ txId = toTxHashString.apply(void 0, [
1870
+ _state.sent()
1871
+ ]);
1872
+ return [
1873
+ 2,
1874
+ {
1875
+ status: 'registered',
1876
+ message: "Registered ".concat(unregistered.length, " NIGHT UTXO(s) for dust generation (tx ").concat(txId.slice(0, 16), "…). ") + 'Dust accrues continuously — poll getBalances to see it arrive.',
1877
+ registeredCount: unregistered.length,
1878
+ dustBalance: '0',
1879
+ txId: txId
1880
+ }
1881
+ ];
1882
+ }
1883
+ });
1884
+ });
1885
+ return function(_) {
1886
+ return _ref.apply(this, arguments);
1887
+ };
1888
+ }())
1889
+ ];
1890
+ case 1:
1891
+ _ref = _state.sent(), result = _ref.result, walletState = _ref.walletState;
1892
+ return [
1893
+ 2,
1894
+ _object_spread_props(_object_spread({}, result), {
1895
+ walletState: walletState
1896
+ })
1897
+ ];
1898
+ case 2:
1899
+ error = _state.sent();
1900
+ logError({
1901
+ message: ERROR_REGISTER_DUST,
1902
+ error: error,
1903
+ context: {
1904
+ accountAddress: access.walletMetadata.accountAddress
1905
+ }
1906
+ });
1907
+ throw _instanceof(error, Error) ? error : new Error(ERROR_REGISTER_DUST);
1908
+ case 3:
1909
+ return [
1910
+ 2
1911
+ ];
1912
+ }
1913
+ });
1914
+ })();
1915
+ }
1916
+ },
1917
+ {
1918
+ key: "createTransferTransaction",
1919
+ value: /**
1920
+ * Builds an unsigned transfer. No MPC, no proving, no broadcast — pair with
1921
+ * {@link signTransaction} then {@link submitTransaction}.
1922
+ */ function createTransferTransaction(access) {
1923
+ var _this = this;
1924
+ return _async_to_generator(function() {
1925
+ var transfers, _access_ttlMinutes, ttlMinutes, _ref, result, walletState, error;
1926
+ return _ts_generator(this, function(_state) {
1927
+ switch(_state.label){
1928
+ case 0:
1929
+ transfers = access.transfers, _access_ttlMinutes = access.ttlMinutes, ttlMinutes = _access_ttlMinutes === void 0 ? 5 : _access_ttlMinutes;
1930
+ if (!(transfers === null || transfers === void 0 ? void 0 : transfers.length)) {
1931
+ throw new Error('At least one transfer is required');
1932
+ }
1933
+ _state.label = 1;
1934
+ case 1:
1935
+ _state.trys.push([
1936
+ 1,
1937
+ 3,
1938
+ ,
1939
+ 4
1940
+ ]);
1941
+ return [
1942
+ 4,
1943
+ _this.withWallet('createTransferTransaction', access, 'full', /*#__PURE__*/ function() {
1944
+ var _ref = _async_to_generator(function(param) {
1945
+ var wallet, state, shieldedSecretKeys, dustSecretKey, _state_dust_balance, _state_dust, _state_dust_balance1, nativeToken, network, requiredByKey, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, transfer, _transfer_tokenType, token, key, existing, _existing_required, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, _step_value, side, token1, required, _state_unshielded, _state_shielded, balances, _balances_token, have, sdkTransfers, recipe;
1946
+ return _ts_generator(this, function(_state) {
1947
+ switch(_state.label){
1948
+ case 0:
1949
+ wallet = param.wallet, state = param.state, shieldedSecretKeys = param.shieldedSecretKeys, dustSecretKey = param.dustSecretKey;
1950
+ // Dust pays the fee on every Midnight transaction regardless of transfer type.
1951
+ if (((_state_dust_balance1 = (_state_dust = state.dust) === null || _state_dust === void 0 ? void 0 : (_state_dust_balance = _state_dust.balance) === null || _state_dust_balance === void 0 ? void 0 : _state_dust_balance.call(_state_dust, new Date())) !== null && _state_dust_balance1 !== void 0 ? _state_dust_balance1 : 0n) === 0n) {
1952
+ throw new Error(ERROR_NO_DUST);
1953
+ }
1954
+ nativeToken = ledger__namespace.nativeToken().raw;
1955
+ network = resolveMidnightNetworkTag(access.networkId);
1956
+ // Aggregate per (side, token) so multiple outputs spending the same token
1957
+ // are checked against one combined total. NUL separator: it cannot appear
1958
+ // in any Midnight address or token encoding, so keys never collide (a
1959
+ // token identifier may itself contain ':').
1960
+ requiredByKey = new Map();
1961
+ _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1962
+ try {
1963
+ for(_iterator = transfers[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1964
+ transfer = _step.value;
1965
+ ;
1966
+ token = (_transfer_tokenType = transfer.tokenType) !== null && _transfer_tokenType !== void 0 ? _transfer_tokenType : nativeToken;
1967
+ key = "".concat(transfer.type, "\0").concat(token);
1968
+ existing = requiredByKey.get(key);
1969
+ ;
1970
+ requiredByKey.set(key, {
1971
+ side: transfer.type,
1972
+ token: token,
1973
+ required: ((_existing_required = existing === null || existing === void 0 ? void 0 : existing.required) !== null && _existing_required !== void 0 ? _existing_required : 0n) + parseTransferAmount(transfer.amount)
1974
+ });
1975
+ }
1976
+ } catch (err) {
1977
+ _didIteratorError = true;
1978
+ _iteratorError = err;
1979
+ } finally{
1980
+ try {
1981
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1982
+ _iterator.return();
1983
+ }
1984
+ } finally{
1985
+ if (_didIteratorError) {
1986
+ throw _iteratorError;
1987
+ }
1988
+ }
1989
+ }
1990
+ _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
1991
+ try {
1992
+ for(_iterator1 = requiredByKey.values()[Symbol.iterator](); !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
1993
+ _step_value = _step1.value, side = _step_value.side, token1 = _step_value.token, required = _step_value.required;
1994
+ ;
1995
+ balances = side === 'unshielded' ? (_state_unshielded = state.unshielded) === null || _state_unshielded === void 0 ? void 0 : _state_unshielded.balances : (_state_shielded = state.shielded) === null || _state_shielded === void 0 ? void 0 : _state_shielded.balances;
1996
+ ;
1997
+ have = (_balances_token = balances === null || balances === void 0 ? void 0 : balances[token1]) !== null && _balances_token !== void 0 ? _balances_token : 0n;
1998
+ if (have < required) {
1999
+ throw new Error("Insufficient ".concat(side, " balance for token ").concat(token1, ": ").concat(have, " < ").concat(required));
2000
+ }
2001
+ }
2002
+ } catch (err) {
2003
+ _didIteratorError1 = true;
2004
+ _iteratorError1 = err;
2005
+ } finally{
2006
+ try {
2007
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
2008
+ _iterator1.return();
2009
+ }
2010
+ } finally{
2011
+ if (_didIteratorError1) {
2012
+ throw _iteratorError1;
2013
+ }
2014
+ }
2015
+ }
2016
+ sdkTransfers = transfers.map(function(transfer) {
2017
+ var _transfer_tokenType;
2018
+ var token = (_transfer_tokenType = transfer.tokenType) !== null && _transfer_tokenType !== void 0 ? _transfer_tokenType : nativeToken;
2019
+ // The SDK wants a decoded address object, not the bech32m string.
2020
+ var receiverAddress;
2021
+ try {
2022
+ var parsed = walletSdkAddressFormat.MidnightBech32m.parse(transfer.recipientAddress);
2023
+ receiverAddress = transfer.type === 'unshielded' ? parsed.decode(walletSdkAddressFormat.UnshieldedAddress, network) : parsed.decode(walletSdkAddressFormat.ShieldedAddress, network);
2024
+ } catch (err) {
2025
+ throw new Error("Invalid ".concat(transfer.type, " recipient address ").concat(transfer.recipientAddress, ": ").concat(err.message));
2026
+ }
2027
+ return {
2028
+ type: transfer.type,
2029
+ outputs: [
2030
+ {
2031
+ amount: parseTransferAmount(transfer.amount),
2032
+ receiverAddress: receiverAddress,
2033
+ type: token
2034
+ }
2035
+ ]
2036
+ };
2037
+ });
2038
+ return [
2039
+ 4,
2040
+ wallet.transferTransaction(sdkTransfers, {
2041
+ shieldedSecretKeys: shieldedSecretKeys,
2042
+ dustSecretKey: dustSecretKey
2043
+ }, {
2044
+ ttl: new Date(Date.now() + ttlMinutes * 60 * 1000)
2045
+ })
2046
+ ];
2047
+ case 1:
2048
+ recipe = _state.sent();
2049
+ if (recipe.type !== 'UNPROVEN_TRANSACTION') {
2050
+ throw new Error("Unexpected recipe type from transferTransaction: ".concat(recipe.type));
2051
+ }
2052
+ return [
2053
+ 2,
2054
+ {
2055
+ serializedTransaction: Buffer.from(recipe.transaction.serialize()).toString('base64')
2056
+ }
2057
+ ];
2058
+ }
2059
+ });
2060
+ });
2061
+ return function(_) {
2062
+ return _ref.apply(this, arguments);
2063
+ };
2064
+ }())
2065
+ ];
2066
+ case 2:
2067
+ _ref = _state.sent(), result = _ref.result, walletState = _ref.walletState;
2068
+ return [
2069
+ 2,
2070
+ _object_spread_props(_object_spread({}, result), {
2071
+ walletState: walletState
2072
+ })
2073
+ ];
2074
+ case 3:
2075
+ error = _state.sent();
2076
+ logError({
2077
+ message: ERROR_CREATE_TRANSFER,
2078
+ error: error,
2079
+ context: {
2080
+ accountAddress: access.walletMetadata.accountAddress
2081
+ }
2082
+ });
2083
+ throw _instanceof(error, Error) ? error : new Error(ERROR_CREATE_TRANSFER);
2084
+ case 4:
2085
+ return [
2086
+ 2
2087
+ ];
2088
+ }
2089
+ });
2090
+ })();
2091
+ }
2092
+ },
2093
+ {
2094
+ key: "submitTransaction",
2095
+ value: /**
2096
+ * Broadcasts a finalized transaction. Pure RPC — reuses the cached wallet's
2097
+ * node connection; no MPC and no proving.
2098
+ *
2099
+ * Returns both identifiers, because they are not interchangeable:
2100
+ * - `txHash` is the canonical ledger hash, and what the block explorer indexes.
2101
+ * - `txIdentifier` is the submission handle; midnight-js polls
2102
+ * `transactions(offset: { identifier })` to observe inclusion, so a caller
2103
+ * handed only the hash would wait forever on a transaction that succeeded.
2104
+ */ function submitTransaction(access) {
2105
+ var _this = this;
2106
+ return _async_to_generator(function() {
2107
+ var bytes, _ref, result, walletState, error;
2108
+ return _ts_generator(this, function(_state) {
2109
+ switch(_state.label){
2110
+ case 0:
2111
+ // Validate before booting the facade — see signTransaction.
2112
+ bytes = decodeSerializedTransaction(access.transaction, 'submitTransaction');
2113
+ _state.label = 1;
2114
+ case 1:
2115
+ _state.trys.push([
2116
+ 1,
2117
+ 3,
2118
+ ,
2119
+ 4
2120
+ ]);
2121
+ return [
2122
+ 4,
2123
+ _this.withWallet('submitTransaction', access, 'full', /*#__PURE__*/ function() {
2124
+ var _ref = _async_to_generator(function(param) {
2125
+ var wallet, finalized, txIdentifier, txHash, canonical;
2126
+ return _ts_generator(this, function(_state) {
2127
+ switch(_state.label){
2128
+ case 0:
2129
+ wallet = param.wallet;
2130
+ finalized = ledger__namespace.Transaction.deserialize('signature', 'proof', 'binding', bytes);
2131
+ return [
2132
+ 4,
2133
+ _this.submitWithReconnect(wallet, finalized)
2134
+ ];
2135
+ case 1:
2136
+ txIdentifier = toTxHashString.apply(void 0, [
2137
+ _state.sent()
2138
+ ]);
2139
+ txHash = txIdentifier;
2140
+ try {
2141
+ canonical = toTxHashString(finalized.transactionHash());
2142
+ if (canonical) txHash = canonical;
2143
+ } catch (err) {
2144
+ _this.logger.warn('[Midnight] submitTransaction: could not derive canonical hash, using identifier', err);
2145
+ }
2146
+ return [
2147
+ 2,
2148
+ {
2149
+ txHash: txHash,
2150
+ txIdentifier: txIdentifier
2151
+ }
2152
+ ];
2153
+ }
2154
+ });
2155
+ });
2156
+ return function(_) {
2157
+ return _ref.apply(this, arguments);
2158
+ };
2159
+ }())
2160
+ ];
2161
+ case 2:
2162
+ _ref = _state.sent(), result = _ref.result, walletState = _ref.walletState;
2163
+ return [
2164
+ 2,
2165
+ _object_spread_props(_object_spread({}, result), {
2166
+ walletState: walletState
2167
+ })
2168
+ ];
2169
+ case 3:
2170
+ error = _state.sent();
2171
+ logError({
2172
+ message: ERROR_SUBMIT_TRANSACTION,
2173
+ error: error,
2174
+ context: {
2175
+ accountAddress: access.walletMetadata.accountAddress
2176
+ }
2177
+ });
2178
+ throw _instanceof(error, Error) ? error : new Error(ERROR_SUBMIT_TRANSACTION);
2179
+ case 4:
2180
+ return [
2181
+ 2
2182
+ ];
2183
+ }
2184
+ });
2185
+ })();
2186
+ }
2187
+ },
2188
+ {
2189
+ key: "signTransaction",
2190
+ value: /**
2191
+ * MPC-signs every unshielded intent segment, then finalizes (ZK-proves) the
2192
+ * transaction through the proof server.
2193
+ *
2194
+ * Side effect: finalizeRecipe mutates wallet state by adding a pending-tx
2195
+ * entry. If the caller never submits, that entry lingers until the next
2196
+ * re-sync reconciles against the chain.
2197
+ */ function signTransaction(access) {
2198
+ var _this = this;
2199
+ return _async_to_generator(function() {
2200
+ var transaction, networkId, context, resolvedNetworkId, bytes, _ref, result, walletState, error;
2201
+ return _ts_generator(this, function(_state) {
2202
+ switch(_state.label){
2203
+ case 0:
2204
+ transaction = access.transaction, networkId = access.networkId, context = access.context;
2205
+ if (networkId === undefined) {
2206
+ // Never default: the network id is baked into the transaction hash for
2207
+ // replay protection, so guessing wrong is a correctness bug, not a nit.
2208
+ throw new Error(ERROR_NETWORK_ID_REQUIRED);
2209
+ }
2210
+ resolvedNetworkId = typeof networkId === 'number' ? networkId : getNetworkIdFromChainId(networkId);
2211
+ // Validate before booting the facade: a cold sync is ~150s and getRoleKeys
2212
+ // runs a full MPC export, neither of which a malformed payload should cost.
2213
+ bytes = decodeSerializedTransaction(transaction, 'signTransaction');
2214
+ _state.label = 1;
2215
+ case 1:
2216
+ _state.trys.push([
2217
+ 1,
2218
+ 3,
2219
+ ,
2220
+ 4
2221
+ ]);
2222
+ return [
2223
+ 4,
2224
+ _this.withWallet('signTransaction', access, 'full', /*#__PURE__*/ function() {
2225
+ var _ref = _async_to_generator(function(param) {
2226
+ var wallet, unsignedTx, finalized;
2227
+ return _ts_generator(this, function(_state) {
2228
+ switch(_state.label){
2229
+ case 0:
2230
+ wallet = param.wallet;
2231
+ unsignedTx = ledger__namespace.Transaction.deserialize('signature', 'pre-proof', 'pre-binding', bytes);
2232
+ return [
2233
+ 4,
2234
+ _this.mpcSignIntents(unsignedTx, access, {
2235
+ fullTxBase64: transaction,
2236
+ networkId: resolvedNetworkId,
2237
+ baseContext: context
2238
+ })
2239
+ ];
2240
+ case 1:
2241
+ _state.sent();
2242
+ _this.logger.info('[Midnight] signTransaction: finalizing (ZK prove)…');
2243
+ return [
2244
+ 4,
2245
+ wallet.finalizeRecipe({
2246
+ type: 'UNPROVEN_TRANSACTION',
2247
+ transaction: unsignedTx
2248
+ })
2249
+ ];
2250
+ case 2:
2251
+ finalized = _state.sent();
2252
+ return [
2253
+ 2,
2254
+ Buffer.from(finalized.serialize()).toString('base64')
2255
+ ];
2256
+ }
2257
+ });
2258
+ });
2259
+ return function(_) {
2260
+ return _ref.apply(this, arguments);
2261
+ };
2262
+ }())
2263
+ ];
2264
+ case 2:
2265
+ _ref = _state.sent(), result = _ref.result, walletState = _ref.walletState;
2266
+ return [
2267
+ 2,
2268
+ {
2269
+ transaction: result,
2270
+ walletState: walletState
2271
+ }
2272
+ ];
2273
+ case 3:
2274
+ error = _state.sent();
2275
+ logError({
2276
+ message: ERROR_SIGN_TRANSACTION,
2277
+ error: error,
2278
+ context: {
2279
+ accountAddress: access.walletMetadata.accountAddress
2280
+ }
2281
+ });
2282
+ throw _instanceof(error, Error) ? error : new Error(ERROR_SIGN_TRANSACTION);
2283
+ case 4:
2284
+ return [
2285
+ 2
2286
+ ];
2287
+ }
2288
+ });
2289
+ })();
2290
+ }
2291
+ },
2292
+ {
2293
+ key: "getWalletProvider",
2294
+ value: /**
2295
+ * Returns a Midnight.js-compatible wallet provider for use with
2296
+ * midnight-js-contracts (deployment and contract calls). Signing goes through
2297
+ * MPC; no private key is handed to the caller.
2298
+ *
2299
+ * `balanceTx` and `submitTx` boot a wallet per call rather than closing over
2300
+ * one, so nothing is held open between the caller's contract operations. To
2301
+ * keep that from re-syncing from genesis every time, the provider carries the
2302
+ * checkpoint from one call into the next. That state lives in this closure
2303
+ * and dies with the provider — read it with `getWalletState()` to persist it
2304
+ * beyond that.
2305
+ */ function getWalletProvider(access) {
2306
+ var _this = this;
2307
+ return _async_to_generator(function() {
2308
+ var network, numericNetworkId, resume, durable, queue, withLatest, _ref, coinPublicKey, encryptionPublicKey;
2309
+ return _ts_generator(this, function(_state) {
2310
+ switch(_state.label){
2311
+ case 0:
2312
+ network = resolveMidnightNetworkTag(access.networkId);
2313
+ numericNetworkId = MIDNIGHT_NETWORK_IDS[network];
2314
+ // Two views of the same wallet. `resume` is what the next call boots from
2315
+ // and includes work in flight, so a second balanceTx cannot re-spend coins
2316
+ // the first one already reserved. `durable` is the clean subset handed to
2317
+ // the caller, who must not persist pending entries.
2318
+ resume = access.walletState;
2319
+ durable = access.walletState;
2320
+ // Each operation reads `resume`, awaits, then writes it back, so two
2321
+ // overlapping calls would both start from the same state and the later
2322
+ // write would drop the earlier one's reservations. Chaining them means a
2323
+ // caller that fires several without awaiting still gets them in order.
2324
+ queue = Promise.resolve();
2325
+ withLatest = function(label, use) {
2326
+ var run = queue.then(/*#__PURE__*/ _async_to_generator(function() {
2327
+ var _ref, result, walletState, sessionState;
2328
+ return _ts_generator(this, function(_state) {
2329
+ switch(_state.label){
2330
+ case 0:
2331
+ return [
2332
+ 4,
2333
+ _this.withWallet(label, _object_spread_props(_object_spread({}, access), {
2334
+ walletState: resume
2335
+ }), 'full', use)
2336
+ ];
2337
+ case 1:
2338
+ _ref = _state.sent(), result = _ref.result, walletState = _ref.walletState, sessionState = _ref.sessionState;
2339
+ if (sessionState) resume = sessionState;
2340
+ if (walletState) durable = walletState;
2341
+ return [
2342
+ 2,
2343
+ result
2344
+ ];
2345
+ }
2346
+ });
2347
+ }));
2348
+ // Swallow only for the chain's own sake — `run` still rejects for the
2349
+ // caller, but one failure must not poison every later operation.
2350
+ queue = run.catch(function() {
2351
+ return undefined;
2352
+ });
2353
+ return run;
2354
+ };
2355
+ return [
2356
+ 4,
2357
+ withLatest('walletProvider', /*#__PURE__*/ function() {
2358
+ var _ref = _async_to_generator(function(param) {
2359
+ var state;
2360
+ return _ts_generator(this, function(_state) {
2361
+ state = param.state;
2362
+ return [
2363
+ 2,
2364
+ {
2365
+ coinPublicKey: state.shielded.coinPublicKey.toHexString(),
2366
+ encryptionPublicKey: state.shielded.encryptionPublicKey.toHexString()
2367
+ }
2368
+ ];
2369
+ });
2370
+ });
2371
+ return function(_) {
2372
+ return _ref.apply(this, arguments);
2373
+ };
2374
+ }())
2375
+ ];
2376
+ case 1:
2377
+ _ref = _state.sent(), coinPublicKey = _ref.coinPublicKey, encryptionPublicKey = _ref.encryptionPublicKey;
2378
+ return [
2379
+ 2,
2380
+ {
2381
+ getCoinPublicKey: function() {
2382
+ return coinPublicKey;
2383
+ },
2384
+ getEncryptionPublicKey: function() {
2385
+ return encryptionPublicKey;
2386
+ },
2387
+ getWalletState: function() {
2388
+ return durable;
2389
+ },
2390
+ balanceTx: function(tx, ttl) {
2391
+ return withLatest('walletProvider.balanceTx', /*#__PURE__*/ function() {
2392
+ var _ref = _async_to_generator(function(param) {
2393
+ var wallet, shieldedSecretKeys, dustSecretKey, recipe;
2394
+ return _ts_generator(this, function(_state) {
2395
+ switch(_state.label){
2396
+ case 0:
2397
+ wallet = param.wallet, shieldedSecretKeys = param.shieldedSecretKeys, dustSecretKey = param.dustSecretKey;
2398
+ return [
2399
+ 4,
2400
+ wallet.balanceUnboundTransaction(tx, {
2401
+ shieldedSecretKeys: shieldedSecretKeys,
2402
+ dustSecretKey: dustSecretKey
2403
+ }, {
2404
+ ttl: ttl !== null && ttl !== void 0 ? ttl : new Date(Date.now() + 30 * 60 * 1000)
2405
+ })
2406
+ ];
2407
+ case 1:
2408
+ recipe = _state.sent();
2409
+ // The base transaction arrives already proven, so it deserializes and
2410
+ // signs under the 'proof' marker; the balancing transaction the SDK
2411
+ // adds has not been proven yet and uses 'pre-proof'.
2412
+ return [
2413
+ 4,
2414
+ _this.mpcSignIntents(recipe.baseTransaction, access, {
2415
+ fullTxBase64: Buffer.from(recipe.baseTransaction.serialize()).toString('base64'),
2416
+ networkId: numericNetworkId,
2417
+ proofMarker: 'proof'
2418
+ })
2419
+ ];
2420
+ case 2:
2421
+ _state.sent();
2422
+ if (!recipe.balancingTransaction) return [
2423
+ 3,
2424
+ 4
2425
+ ];
2426
+ return [
2427
+ 4,
2428
+ _this.mpcSignIntents(recipe.balancingTransaction, access, {
2429
+ fullTxBase64: Buffer.from(recipe.balancingTransaction.serialize()).toString('base64'),
2430
+ networkId: numericNetworkId,
2431
+ proofMarker: 'pre-proof'
2432
+ })
2433
+ ];
2434
+ case 3:
2435
+ _state.sent();
2436
+ _state.label = 4;
2437
+ case 4:
2438
+ _this.logger.info('[Midnight] walletProvider.balanceTx: finalizing (ZK prove)…');
2439
+ return [
2440
+ 2,
2441
+ wallet.finalizeRecipe(recipe)
2442
+ ];
2443
+ }
2444
+ });
2445
+ });
2446
+ return function(_) {
2447
+ return _ref.apply(this, arguments);
2448
+ };
2449
+ }());
2450
+ },
2451
+ submitTx: function(tx) {
2452
+ return withLatest('walletProvider.submitTx', /*#__PURE__*/ function() {
2453
+ var _ref = _async_to_generator(function(param) {
2454
+ var wallet;
2455
+ return _ts_generator(this, function(_state) {
2456
+ switch(_state.label){
2457
+ case 0:
2458
+ wallet = param.wallet;
2459
+ return [
2460
+ 4,
2461
+ _this.submitWithReconnect(wallet, tx)
2462
+ ];
2463
+ case 1:
2464
+ return [
2465
+ 2,
2466
+ toTxHashString.apply(void 0, [
2467
+ _state.sent()
2468
+ ])
2469
+ ];
2470
+ }
2471
+ });
2472
+ });
2473
+ return function(_) {
2474
+ return _ref.apply(this, arguments);
2475
+ };
2476
+ }());
2477
+ }
2478
+ }
2479
+ ];
2480
+ }
2481
+ });
2482
+ })();
2483
+ }
2484
+ },
2485
+ {
2486
+ key: "balanceContractTransaction",
2487
+ value: /**
2488
+ * Balances a serialized midnight-js UnboundTransaction and returns the
2489
+ * finalized transaction, base64-encoded. A transport-friendly wrapper over
2490
+ * {@link getWalletProvider}'s `balanceTx` for callers that pass transactions
2491
+ * across a process boundary rather than holding ledger objects.
2492
+ */ function balanceContractTransaction(access) {
2493
+ var _this = this;
2494
+ return _async_to_generator(function() {
2495
+ var transaction, ttl, ttlDate, bytes, unbound, provider, finalized, error;
2496
+ return _ts_generator(this, function(_state) {
2497
+ switch(_state.label){
2498
+ case 0:
2499
+ transaction = access.transaction, ttl = access.ttl;
2500
+ // `ttl ? …` would treat '' as absent and quietly apply the default lifetime.
2501
+ ttlDate = ttl === undefined ? undefined : parseIsoTimestamp(ttl, 'balanceContractTransaction');
2502
+ // Validated before the facade boots — see signTransaction. An unbound
2503
+ // contract transaction arrives proven, hence the 'proof' marker.
2504
+ bytes = decodeSerializedTransaction(transaction, 'balanceContractTransaction');
2505
+ _state.label = 1;
2506
+ case 1:
2507
+ _state.trys.push([
2508
+ 1,
2509
+ 4,
2510
+ ,
2511
+ 5
2512
+ ]);
2513
+ unbound = ledger__namespace.Transaction.deserialize('signature', 'proof', 'pre-binding', bytes);
2514
+ return [
2515
+ 4,
2516
+ _this.getWalletProvider(access)
2517
+ ];
2518
+ case 2:
2519
+ provider = _state.sent();
2520
+ return [
2521
+ 4,
2522
+ provider.balanceTx(unbound, ttlDate)
2523
+ ];
2524
+ case 3:
2525
+ finalized = _state.sent();
2526
+ // Booting the provider produces a clean checkpoint before any balancing
2527
+ // happens; dropping it would make the next call re-sync from genesis.
2528
+ return [
2529
+ 2,
2530
+ {
2531
+ transaction: Buffer.from(finalized.serialize()).toString('base64'),
2532
+ walletState: provider.getWalletState()
2533
+ }
2534
+ ];
2535
+ case 4:
2536
+ error = _state.sent();
2537
+ logError({
2538
+ message: ERROR_BALANCE_TRANSACTION,
2539
+ error: error,
2540
+ context: {
2541
+ accountAddress: access.walletMetadata.accountAddress
2542
+ }
2543
+ });
2544
+ throw _instanceof(error, Error) ? error : new Error(ERROR_BALANCE_TRANSACTION);
2545
+ case 5:
2546
+ return [
2547
+ 2
2548
+ ];
2549
+ }
2550
+ });
2551
+ })();
2552
+ }
2553
+ },
2554
+ {
2555
+ key: "mpcSignIntents",
2556
+ value: /**
2557
+ * Signs each intent segment via MPC and writes the signatures back onto the
2558
+ * transaction.
2559
+ *
2560
+ * Strictly serial. `tx.intents` is an immutable persistent map — `.set()`
2561
+ * returns a new map — so a Promise.all would have every iteration read the
2562
+ * same initial map and race to write back, silently dropping all but the last
2563
+ * segment's signature. Relay ceremonies serialize anyway, so little is lost.
2564
+ */ function mpcSignIntents(tx, access, options) {
2565
+ var _this = this;
2566
+ return _async_to_generator(function() {
2567
+ var _tx_intents, _tx_intents_keys_toArray, segments, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _loop, _iterator, _step, err;
2568
+ return _ts_generator(this, function(_state) {
2569
+ switch(_state.label){
2570
+ case 0:
2571
+ segments = (_tx_intents_keys_toArray = (_tx_intents = tx.intents) === null || _tx_intents === void 0 ? void 0 : _tx_intents.keys().toArray()) !== null && _tx_intents_keys_toArray !== void 0 ? _tx_intents_keys_toArray : [];
2572
+ if (segments.length === 0) return [
2573
+ 2
2574
+ ];
2575
+ _this.logger.info("[Midnight] MPC signing ".concat(segments.length, " intent segment(s)…"));
2576
+ _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2577
+ _state.label = 1;
2578
+ case 1:
2579
+ _state.trys.push([
2580
+ 1,
2581
+ 6,
2582
+ 7,
2583
+ 8
2584
+ ]);
2585
+ _loop = function() {
2586
+ var _loop, segment, intent, _options_proofMarker, cloned, sigData, hash, signature, sigBytes, ledgerSig, _i, _iter;
2587
+ return _ts_generator(this, function(_state) {
2588
+ switch(_state.label){
2589
+ case 0:
2590
+ _loop = function(_i, _iter) {
2591
+ var offerKey = _iter[_i];
2592
+ var offer = cloned[offerKey];
2593
+ if (!offer) return "continue";
2594
+ cloned[offerKey] = offer.addSignatures(offer.inputs.map(function(_, index) {
2595
+ var _offer_signatures_at;
2596
+ return (_offer_signatures_at = offer.signatures.at(index)) !== null && _offer_signatures_at !== void 0 ? _offer_signatures_at : ledgerSig;
2597
+ }));
2598
+ };
2599
+ segment = _step.value;
2600
+ intent = tx.intents.get(segment);
2601
+ if (!intent) return [
2602
+ 2,
2603
+ "continue"
2604
+ ];
2605
+ cloned = ledger__namespace.Intent.deserialize('signature', (_options_proofMarker = options.proofMarker) !== null && _options_proofMarker !== void 0 ? _options_proofMarker : 'pre-proof', 'pre-binding', intent.serialize());
2606
+ sigData = cloned.signatureData(segment);
2607
+ hash = node_crypto.createHash('sha256').update(Buffer.from(sigData)).digest('hex');
2608
+ return [
2609
+ 4,
2610
+ _this.sign({
2611
+ message: hash,
2612
+ accountAddress: access.walletMetadata.accountAddress,
2613
+ chainName: _this.chainName,
2614
+ password: access.password,
2615
+ externalServerKeyShares: access.externalServerKeyShares,
2616
+ walletMetadata: access.walletMetadata,
2617
+ walletOperation: node.WalletOperation.SIGN_TRANSACTION,
2618
+ // Policy validation runs per signature, so every segment carries the
2619
+ // whole transaction — same shape as BTC's per-input PSBT context.
2620
+ context: _object_spread_props(_object_spread({}, options.baseContext), {
2621
+ midnightTransaction: {
2622
+ serializedTransaction: options.fullTxBase64,
2623
+ networkId: options.networkId
2624
+ }
2625
+ })
2626
+ })
2627
+ ];
2628
+ case 1:
2629
+ signature = _state.sent();
2630
+ sigBytes = _instanceof(signature, Uint8Array) ? signature : Buffer.from(String(signature), 'base64');
2631
+ ledgerSig = Buffer.from(sigBytes).toString('hex');
2632
+ // Preserve any signature already present at an input index; only fill gaps.
2633
+ for(_i = 0, _iter = [
2634
+ 'guaranteedUnshieldedOffer',
2635
+ 'fallibleUnshieldedOffer'
2636
+ ]; _i < _iter.length; _i++)_loop(_i, _iter);
2637
+ tx.intents = tx.intents.set(segment, cloned);
2638
+ return [
2639
+ 2
2640
+ ];
2641
+ }
2642
+ });
2643
+ };
2644
+ _iterator = segments[Symbol.iterator]();
2645
+ _state.label = 2;
2646
+ case 2:
2647
+ if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
2648
+ 3,
2649
+ 5
2650
+ ];
2651
+ return [
2652
+ 5,
2653
+ _ts_values(_loop())
2654
+ ];
2655
+ case 3:
2656
+ _state.sent();
2657
+ _state.label = 4;
2658
+ case 4:
2659
+ _iteratorNormalCompletion = true;
2660
+ return [
2661
+ 3,
2662
+ 2
2663
+ ];
2664
+ case 5:
2665
+ return [
2666
+ 3,
2667
+ 8
2668
+ ];
2669
+ case 6:
2670
+ err = _state.sent();
2671
+ _didIteratorError = true;
2672
+ _iteratorError = err;
2673
+ return [
2674
+ 3,
2675
+ 8
2676
+ ];
2677
+ case 7:
2678
+ try {
2679
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
2680
+ _iterator.return();
2681
+ }
2682
+ } finally{
2683
+ if (_didIteratorError) {
2684
+ throw _iteratorError;
2685
+ }
2686
+ }
2687
+ return [
2688
+ 7
2689
+ ];
2690
+ case 8:
2691
+ return [
2692
+ 2
2693
+ ];
2694
+ }
2695
+ });
2696
+ })();
2697
+ }
2698
+ },
1046
2699
  {
1047
2700
  key: "getMidnightWallets",
1048
2701
  value: /**
@@ -1078,6 +2731,7 @@ var DynamicMidnightWalletClient = /*#__PURE__*/ function(DynamicWalletClient) {
1078
2731
 
1079
2732
  exports.DynamicMidnightWalletClient = DynamicMidnightWalletClient;
1080
2733
  exports.ERROR_ACCOUNT_ADDRESS_REQUIRED = ERROR_ACCOUNT_ADDRESS_REQUIRED;
2734
+ exports.ERROR_BALANCE_TRANSACTION = ERROR_BALANCE_TRANSACTION;
1081
2735
  exports.ERROR_CREATE_WALLET_ACCOUNT = ERROR_CREATE_WALLET_ACCOUNT;
1082
2736
  exports.ERROR_EXPORT_PRIVATE_KEY = ERROR_EXPORT_PRIVATE_KEY;
1083
2737
  exports.ERROR_IMPORT_PRIVATE_KEY = ERROR_IMPORT_PRIVATE_KEY;