@dynamic-labs-wallet/midnight 0.0.0 → 0.0.347
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.d.ts +1 -0
- package/index.cjs.js +3242 -0
- package/index.esm.d.ts +1 -0
- package/index.esm.js +3218 -0
- package/package.json +42 -1
- package/src/client/client.d.ts +253 -0
- package/src/client/client.d.ts.map +1 -0
- package/src/client/constants.d.ts +31 -0
- package/src/client/constants.d.ts.map +1 -0
- package/src/client/index.d.ts +2 -0
- package/src/client/index.d.ts.map +1 -0
- package/src/client/walletStateStorage.d.ts +33 -0
- package/src/client/walletStateStorage.d.ts.map +1 -0
- package/src/index.d.ts +5 -0
- package/src/index.d.ts.map +1 -0
- package/src/utils/deriveMidnightAddress/deriveMidnightAddress.d.ts +41 -0
- package/src/utils/deriveMidnightAddress/deriveMidnightAddress.d.ts.map +1 -0
- package/src/utils/deriveMidnightAddress/index.d.ts +2 -0
- package/src/utils/deriveMidnightAddress/index.d.ts.map +1 -0
- package/src/utils/index.d.ts +2 -0
- package/src/utils/index.d.ts.map +1 -0
package/index.cjs.js
ADDED
|
@@ -0,0 +1,3242 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var browser = require('@dynamic-labs-wallet/browser');
|
|
4
|
+
var ledger = require('@midnight-ntwrk/ledger-v8');
|
|
5
|
+
var walletSdkAddressFormat = require('@midnight-ntwrk/wallet-sdk-address-format');
|
|
6
|
+
var walletSdkDustWallet = require('@midnight-ntwrk/wallet-sdk-dust-wallet');
|
|
7
|
+
var walletSdkFacade = require('@midnight-ntwrk/wallet-sdk-facade');
|
|
8
|
+
var walletSdkShielded = require('@midnight-ntwrk/wallet-sdk-shielded');
|
|
9
|
+
var walletSdkUnshieldedWallet = require('@midnight-ntwrk/wallet-sdk-unshielded-wallet');
|
|
10
|
+
var bip32 = require('@scure/bip32');
|
|
11
|
+
var bs58 = require('bs58');
|
|
12
|
+
var bech32 = require('bech32');
|
|
13
|
+
var sdkApiCore = require('@dynamic-labs/sdk-api-core');
|
|
14
|
+
|
|
15
|
+
function _interopNamespaceDefault(e) {
|
|
16
|
+
var n = Object.create(null);
|
|
17
|
+
if (e) {
|
|
18
|
+
Object.keys(e).forEach(function (k) {
|
|
19
|
+
if (k !== 'default') {
|
|
20
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
21
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get: function () { return e[k]; }
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
n.default = e;
|
|
29
|
+
return Object.freeze(n);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var ledger__namespace = /*#__PURE__*/_interopNamespaceDefault(ledger);
|
|
33
|
+
var sdkApiCore__namespace = /*#__PURE__*/_interopNamespaceDefault(sdkApiCore);
|
|
34
|
+
|
|
35
|
+
// Midnight network identifiers.
|
|
36
|
+
// 0 = non-mainnet, 1 = Mainnet — baked into the transaction hash to prevent
|
|
37
|
+
// cross-network replay. Names match the Zswap NetworkId enum / the strings
|
|
38
|
+
// accepted by @midnight-ntwrk/midnight-js-network-id's setNetworkId().
|
|
39
|
+
var MIDNIGHT_NETWORK_IDS = {
|
|
40
|
+
mainnet: 1,
|
|
41
|
+
preview: 0,
|
|
42
|
+
preprod: 0,
|
|
43
|
+
undeployed: 0
|
|
44
|
+
};
|
|
45
|
+
// Error messages
|
|
46
|
+
var ERROR_ACCOUNT_ADDRESS_REQUIRED = 'Account address is required';
|
|
47
|
+
var ERROR_NETWORK_ID_REQUIRED = 'Network ID is required for Midnight transactions';
|
|
48
|
+
var ERROR_UNKNOWN_NETWORK = 'Unknown Midnight network';
|
|
49
|
+
var ERROR_CREATE_WALLET_ACCOUNT = 'Failed to create Midnight wallet account';
|
|
50
|
+
var ERROR_EXPORT_PRIVATE_KEY = 'Failed to export Midnight private key';
|
|
51
|
+
var ERROR_IMPORT_PRIVATE_KEY = 'Failed to import Midnight private key';
|
|
52
|
+
var ERROR_KEYGEN_FAILED = 'Key generation failed for Midnight wallet';
|
|
53
|
+
var ERROR_SIGN_MESSAGE = 'Failed to sign Midnight message';
|
|
54
|
+
var ERROR_SIGN_TRANSACTION = 'Failed to sign Midnight transaction';
|
|
55
|
+
/**
|
|
56
|
+
* Resolves a Midnight network name (or `"midnight:<network>"` chainId) to its
|
|
57
|
+
* numeric network ID (0 for non-mainnet, 1 for mainnet).
|
|
58
|
+
*
|
|
59
|
+
* Throws on unrecognized input rather than defaulting — silently falling back
|
|
60
|
+
* to mainnet would bake the wrong network ID into the transaction hash and
|
|
61
|
+
* break the replay-protection guarantee. Fail-loud is correct here.
|
|
62
|
+
*
|
|
63
|
+
* @param networkOrChainId `mainnet` | `preview` | `preprod` | `undeployed`,
|
|
64
|
+
* or the `midnight:<network>` chainId form.
|
|
65
|
+
* @throws {Error} ERROR_UNKNOWN_NETWORK when the input doesn't match any
|
|
66
|
+
* recognized network.
|
|
67
|
+
*/ var getNetworkIdFromChainId = function(networkOrChainId) {
|
|
68
|
+
if (networkOrChainId in MIDNIGHT_NETWORK_IDS) {
|
|
69
|
+
return MIDNIGHT_NETWORK_IDS[networkOrChainId];
|
|
70
|
+
}
|
|
71
|
+
if (networkOrChainId.includes(':')) {
|
|
72
|
+
var network = networkOrChainId.split(':')[1];
|
|
73
|
+
if (network && network in MIDNIGHT_NETWORK_IDS) {
|
|
74
|
+
return MIDNIGHT_NETWORK_IDS[network];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
var known = Object.keys(MIDNIGHT_NETWORK_IDS).join(', ');
|
|
78
|
+
throw new Error("".concat(ERROR_UNKNOWN_NETWORK, ': "').concat(networkOrChainId, '". Expected one of: ').concat(known, ', or "midnight:<network>".'));
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* IndexedDB-backed persistence for Midnight sub-wallet sync state.
|
|
83
|
+
*
|
|
84
|
+
* Shielded and Dust sub-wallets support serialize/restore — we cache the
|
|
85
|
+
* serialized state per accountAddress so re-initializing a wallet (either
|
|
86
|
+
* after switching away and back, or after a page reload) resumes sync from
|
|
87
|
+
* the last checkpoint instead of re-scanning the entire chain.
|
|
88
|
+
*
|
|
89
|
+
* Storage shape (object store "state", keyed by accountAddress):
|
|
90
|
+
* { accountAddress, shielded, dust, updatedAt }
|
|
91
|
+
*
|
|
92
|
+
* Security: blobs are stored plaintext, matching Dynamic's existing policy
|
|
93
|
+
* for locally-cached key material (see the role-keys entries in
|
|
94
|
+
* localStorage). Protection comes from the iframe's origin isolation, not
|
|
95
|
+
* from at-rest encryption. If Dynamic decides to tighten the local-storage
|
|
96
|
+
* policy product-wide (password-derived encryption with AES-GCM), this
|
|
97
|
+
* module should be upgraded alongside the localStorage role-keys flow as a
|
|
98
|
+
* single consistent hardening pass.
|
|
99
|
+
*/ function asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, key, arg) {
|
|
100
|
+
try {
|
|
101
|
+
var info = gen[key](arg);
|
|
102
|
+
var value = info.value;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
reject(error);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (info.done) {
|
|
108
|
+
resolve(value);
|
|
109
|
+
} else {
|
|
110
|
+
Promise.resolve(value).then(_next, _throw);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function _async_to_generator$1(fn) {
|
|
114
|
+
return function() {
|
|
115
|
+
var self = this, args = arguments;
|
|
116
|
+
return new Promise(function(resolve, reject) {
|
|
117
|
+
var gen = fn.apply(self, args);
|
|
118
|
+
function _next(value) {
|
|
119
|
+
asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "next", value);
|
|
120
|
+
}
|
|
121
|
+
function _throw(err) {
|
|
122
|
+
asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "throw", err);
|
|
123
|
+
}
|
|
124
|
+
_next(undefined);
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function _instanceof$1(left, right) {
|
|
129
|
+
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
|
|
130
|
+
return !!right[Symbol.hasInstance](left);
|
|
131
|
+
} else {
|
|
132
|
+
return left instanceof right;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function _type_of$1(obj) {
|
|
136
|
+
"@swc/helpers - typeof";
|
|
137
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
138
|
+
}
|
|
139
|
+
function _ts_generator$1(thisArg, body) {
|
|
140
|
+
var f, y, t, g, _ = {
|
|
141
|
+
label: 0,
|
|
142
|
+
sent: function() {
|
|
143
|
+
if (t[0] & 1) throw t[1];
|
|
144
|
+
return t[1];
|
|
145
|
+
},
|
|
146
|
+
trys: [],
|
|
147
|
+
ops: []
|
|
148
|
+
};
|
|
149
|
+
return g = {
|
|
150
|
+
next: verb(0),
|
|
151
|
+
"throw": verb(1),
|
|
152
|
+
"return": verb(2)
|
|
153
|
+
}, typeof Symbol === "function" && (g[Symbol.iterator] = function() {
|
|
154
|
+
return this;
|
|
155
|
+
}), g;
|
|
156
|
+
function verb(n) {
|
|
157
|
+
return function(v) {
|
|
158
|
+
return step([
|
|
159
|
+
n,
|
|
160
|
+
v
|
|
161
|
+
]);
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function step(op) {
|
|
165
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
166
|
+
while(_)try {
|
|
167
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
168
|
+
if (y = 0, t) op = [
|
|
169
|
+
op[0] & 2,
|
|
170
|
+
t.value
|
|
171
|
+
];
|
|
172
|
+
switch(op[0]){
|
|
173
|
+
case 0:
|
|
174
|
+
case 1:
|
|
175
|
+
t = op;
|
|
176
|
+
break;
|
|
177
|
+
case 4:
|
|
178
|
+
_.label++;
|
|
179
|
+
return {
|
|
180
|
+
value: op[1],
|
|
181
|
+
done: false
|
|
182
|
+
};
|
|
183
|
+
case 5:
|
|
184
|
+
_.label++;
|
|
185
|
+
y = op[1];
|
|
186
|
+
op = [
|
|
187
|
+
0
|
|
188
|
+
];
|
|
189
|
+
continue;
|
|
190
|
+
case 7:
|
|
191
|
+
op = _.ops.pop();
|
|
192
|
+
_.trys.pop();
|
|
193
|
+
continue;
|
|
194
|
+
default:
|
|
195
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
196
|
+
_ = 0;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
200
|
+
_.label = op[1];
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
204
|
+
_.label = t[1];
|
|
205
|
+
t = op;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
if (t && _.label < t[2]) {
|
|
209
|
+
_.label = t[2];
|
|
210
|
+
_.ops.push(op);
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
if (t[2]) _.ops.pop();
|
|
214
|
+
_.trys.pop();
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
op = body.call(thisArg, _);
|
|
218
|
+
} catch (e) {
|
|
219
|
+
op = [
|
|
220
|
+
6,
|
|
221
|
+
e
|
|
222
|
+
];
|
|
223
|
+
y = 0;
|
|
224
|
+
} finally{
|
|
225
|
+
f = t = 0;
|
|
226
|
+
}
|
|
227
|
+
if (op[0] & 5) throw op[1];
|
|
228
|
+
return {
|
|
229
|
+
value: op[0] ? op[1] : void 0,
|
|
230
|
+
done: true
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
var DB_NAME = 'dynamic-midnight-wallet-state';
|
|
235
|
+
var DB_VERSION = 1;
|
|
236
|
+
var STORE_NAME = 'state';
|
|
237
|
+
// Normalizes a DOMException | unknown into an Error so promise rejections
|
|
238
|
+
// always carry an Error instance. Object-shaped errors (DOMException, plain
|
|
239
|
+
// objects) are looked up for a `.message` string first so we don't stringify
|
|
240
|
+
// them via Object.prototype.toString (which yields the useless '[object Object]').
|
|
241
|
+
// Exported for direct unit testing of the fallback chain.
|
|
242
|
+
function toError(err, fallbackMessage) {
|
|
243
|
+
if (_instanceof$1(err, Error)) return err;
|
|
244
|
+
if (err == null) return new Error(fallbackMessage);
|
|
245
|
+
if (typeof err === 'string') return new Error(err);
|
|
246
|
+
if (typeof err === 'number' || typeof err === 'boolean' || (typeof err === "undefined" ? "undefined" : _type_of$1(err)) === 'bigint') {
|
|
247
|
+
return new Error(String(err));
|
|
248
|
+
}
|
|
249
|
+
var message = err.message;
|
|
250
|
+
if (typeof message === 'string' && message.length > 0) return new Error(message);
|
|
251
|
+
try {
|
|
252
|
+
return new Error(JSON.stringify(err));
|
|
253
|
+
} catch (e) {
|
|
254
|
+
return new Error(fallbackMessage);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
var dbPromise = null;
|
|
258
|
+
function openDb() {
|
|
259
|
+
if (dbPromise) return dbPromise;
|
|
260
|
+
// On rejection, null the module-level cache so the next caller retries
|
|
261
|
+
// the open instead of receiving the same rejected promise for the rest of
|
|
262
|
+
// the session (private-browsing bootup, transient quota errors, etc).
|
|
263
|
+
// Re-throw so the original caller still sees the rejection.
|
|
264
|
+
dbPromise = new Promise(function(resolve, reject) {
|
|
265
|
+
var req = indexedDB.open(DB_NAME, DB_VERSION);
|
|
266
|
+
req.onupgradeneeded = function() {
|
|
267
|
+
var db = req.result;
|
|
268
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
269
|
+
db.createObjectStore(STORE_NAME, {
|
|
270
|
+
keyPath: 'accountAddress'
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
req.onsuccess = function() {
|
|
275
|
+
return resolve(req.result);
|
|
276
|
+
};
|
|
277
|
+
req.onerror = function() {
|
|
278
|
+
return reject(toError(req.error, 'IndexedDB open failed'));
|
|
279
|
+
};
|
|
280
|
+
}).catch(function(err) {
|
|
281
|
+
dbPromise = null;
|
|
282
|
+
throw err;
|
|
283
|
+
});
|
|
284
|
+
return dbPromise;
|
|
285
|
+
}
|
|
286
|
+
function runTransaction(mode, fn) {
|
|
287
|
+
return _runTransaction.apply(this, arguments);
|
|
288
|
+
}
|
|
289
|
+
function _runTransaction() {
|
|
290
|
+
_runTransaction = _async_to_generator$1(function(mode, fn) {
|
|
291
|
+
var db;
|
|
292
|
+
return _ts_generator$1(this, function(_state) {
|
|
293
|
+
switch(_state.label){
|
|
294
|
+
case 0:
|
|
295
|
+
return [
|
|
296
|
+
4,
|
|
297
|
+
openDb()
|
|
298
|
+
];
|
|
299
|
+
case 1:
|
|
300
|
+
db = _state.sent();
|
|
301
|
+
return [
|
|
302
|
+
2,
|
|
303
|
+
new Promise(function(resolve, reject) {
|
|
304
|
+
var tx = db.transaction(STORE_NAME, mode);
|
|
305
|
+
var store = tx.objectStore(STORE_NAME);
|
|
306
|
+
var req = fn(store);
|
|
307
|
+
req.onsuccess = function() {
|
|
308
|
+
return resolve(req.result);
|
|
309
|
+
};
|
|
310
|
+
req.onerror = function() {
|
|
311
|
+
return reject(toError(req.error, 'IndexedDB request failed'));
|
|
312
|
+
};
|
|
313
|
+
})
|
|
314
|
+
];
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
return _runTransaction.apply(this, arguments);
|
|
319
|
+
}
|
|
320
|
+
function getWalletState(accountAddress) {
|
|
321
|
+
return _getWalletState.apply(this, arguments);
|
|
322
|
+
}
|
|
323
|
+
function _getWalletState() {
|
|
324
|
+
_getWalletState = _async_to_generator$1(function(accountAddress) {
|
|
325
|
+
var result;
|
|
326
|
+
return _ts_generator$1(this, function(_state) {
|
|
327
|
+
switch(_state.label){
|
|
328
|
+
case 0:
|
|
329
|
+
_state.trys.push([
|
|
330
|
+
0,
|
|
331
|
+
2,
|
|
332
|
+
,
|
|
333
|
+
3
|
|
334
|
+
]);
|
|
335
|
+
return [
|
|
336
|
+
4,
|
|
337
|
+
runTransaction('readonly', function(store) {
|
|
338
|
+
return store.get(accountAddress);
|
|
339
|
+
})
|
|
340
|
+
];
|
|
341
|
+
case 1:
|
|
342
|
+
result = _state.sent();
|
|
343
|
+
return [
|
|
344
|
+
2,
|
|
345
|
+
result !== null && result !== void 0 ? result : null
|
|
346
|
+
];
|
|
347
|
+
case 2:
|
|
348
|
+
_state.sent();
|
|
349
|
+
// IndexedDB unavailable (private mode, corrupt DB, etc.) — behave as if empty.
|
|
350
|
+
return [
|
|
351
|
+
2,
|
|
352
|
+
null
|
|
353
|
+
];
|
|
354
|
+
case 3:
|
|
355
|
+
return [
|
|
356
|
+
2
|
|
357
|
+
];
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
return _getWalletState.apply(this, arguments);
|
|
362
|
+
}
|
|
363
|
+
function putWalletState(accountAddress, state) {
|
|
364
|
+
return _putWalletState.apply(this, arguments);
|
|
365
|
+
}
|
|
366
|
+
function _putWalletState() {
|
|
367
|
+
_putWalletState = _async_to_generator$1(function(accountAddress, state) {
|
|
368
|
+
var record;
|
|
369
|
+
return _ts_generator$1(this, function(_state) {
|
|
370
|
+
switch(_state.label){
|
|
371
|
+
case 0:
|
|
372
|
+
record = {
|
|
373
|
+
accountAddress: accountAddress,
|
|
374
|
+
shielded: state.shielded,
|
|
375
|
+
dust: state.dust,
|
|
376
|
+
updatedAt: Date.now()
|
|
377
|
+
};
|
|
378
|
+
_state.label = 1;
|
|
379
|
+
case 1:
|
|
380
|
+
_state.trys.push([
|
|
381
|
+
1,
|
|
382
|
+
3,
|
|
383
|
+
,
|
|
384
|
+
4
|
|
385
|
+
]);
|
|
386
|
+
return [
|
|
387
|
+
4,
|
|
388
|
+
runTransaction('readwrite', function(store) {
|
|
389
|
+
return store.put(record);
|
|
390
|
+
})
|
|
391
|
+
];
|
|
392
|
+
case 2:
|
|
393
|
+
_state.sent();
|
|
394
|
+
return [
|
|
395
|
+
3,
|
|
396
|
+
4
|
|
397
|
+
];
|
|
398
|
+
case 3:
|
|
399
|
+
_state.sent();
|
|
400
|
+
return [
|
|
401
|
+
3,
|
|
402
|
+
4
|
|
403
|
+
];
|
|
404
|
+
case 4:
|
|
405
|
+
return [
|
|
406
|
+
2
|
|
407
|
+
];
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
return _putWalletState.apply(this, arguments);
|
|
412
|
+
}
|
|
413
|
+
function deleteWalletState(accountAddress) {
|
|
414
|
+
return _deleteWalletState.apply(this, arguments);
|
|
415
|
+
}
|
|
416
|
+
function _deleteWalletState() {
|
|
417
|
+
_deleteWalletState = _async_to_generator$1(function(accountAddress) {
|
|
418
|
+
return _ts_generator$1(this, function(_state) {
|
|
419
|
+
switch(_state.label){
|
|
420
|
+
case 0:
|
|
421
|
+
_state.trys.push([
|
|
422
|
+
0,
|
|
423
|
+
2,
|
|
424
|
+
,
|
|
425
|
+
3
|
|
426
|
+
]);
|
|
427
|
+
return [
|
|
428
|
+
4,
|
|
429
|
+
runTransaction('readwrite', function(store) {
|
|
430
|
+
return store.delete(accountAddress);
|
|
431
|
+
})
|
|
432
|
+
];
|
|
433
|
+
case 1:
|
|
434
|
+
_state.sent();
|
|
435
|
+
return [
|
|
436
|
+
3,
|
|
437
|
+
3
|
|
438
|
+
];
|
|
439
|
+
case 2:
|
|
440
|
+
_state.sent();
|
|
441
|
+
return [
|
|
442
|
+
3,
|
|
443
|
+
3
|
|
444
|
+
];
|
|
445
|
+
case 3:
|
|
446
|
+
return [
|
|
447
|
+
2
|
|
448
|
+
];
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
return _deleteWalletState.apply(this, arguments);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function _array_like_to_array(arr, len) {
|
|
456
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
457
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
458
|
+
return arr2;
|
|
459
|
+
}
|
|
460
|
+
function _array_with_holes(arr) {
|
|
461
|
+
if (Array.isArray(arr)) return arr;
|
|
462
|
+
}
|
|
463
|
+
function _assert_this_initialized(self) {
|
|
464
|
+
if (self === void 0) {
|
|
465
|
+
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
|
466
|
+
}
|
|
467
|
+
return self;
|
|
468
|
+
}
|
|
469
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
470
|
+
try {
|
|
471
|
+
var info = gen[key](arg);
|
|
472
|
+
var value = info.value;
|
|
473
|
+
} catch (error) {
|
|
474
|
+
reject(error);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (info.done) {
|
|
478
|
+
resolve(value);
|
|
479
|
+
} else {
|
|
480
|
+
Promise.resolve(value).then(_next, _throw);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function _async_to_generator(fn) {
|
|
484
|
+
return function() {
|
|
485
|
+
var self = this, args = arguments;
|
|
486
|
+
return new Promise(function(resolve, reject) {
|
|
487
|
+
var gen = fn.apply(self, args);
|
|
488
|
+
function _next(value) {
|
|
489
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
490
|
+
}
|
|
491
|
+
function _throw(err) {
|
|
492
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
493
|
+
}
|
|
494
|
+
_next(undefined);
|
|
495
|
+
});
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function _call_super(_this, derived, args) {
|
|
499
|
+
derived = _get_prototype_of(derived);
|
|
500
|
+
return _possible_constructor_return(_this, _is_native_reflect_construct() ? Reflect.construct(derived, args || [], _get_prototype_of(_this).constructor) : derived.apply(_this, args));
|
|
501
|
+
}
|
|
502
|
+
function _class_call_check(instance, Constructor) {
|
|
503
|
+
if (!(instance instanceof Constructor)) {
|
|
504
|
+
throw new TypeError("Cannot call a class as a function");
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
function _defineProperties(target, props) {
|
|
508
|
+
for(var i = 0; i < props.length; i++){
|
|
509
|
+
var descriptor = props[i];
|
|
510
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
511
|
+
descriptor.configurable = true;
|
|
512
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
513
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function _create_class(Constructor, protoProps, staticProps) {
|
|
517
|
+
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
518
|
+
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
519
|
+
return Constructor;
|
|
520
|
+
}
|
|
521
|
+
function _define_property(obj, key, value) {
|
|
522
|
+
if (key in obj) {
|
|
523
|
+
Object.defineProperty(obj, key, {
|
|
524
|
+
value: value,
|
|
525
|
+
enumerable: true,
|
|
526
|
+
configurable: true,
|
|
527
|
+
writable: true
|
|
528
|
+
});
|
|
529
|
+
} else {
|
|
530
|
+
obj[key] = value;
|
|
531
|
+
}
|
|
532
|
+
return obj;
|
|
533
|
+
}
|
|
534
|
+
function _get_prototype_of(o) {
|
|
535
|
+
_get_prototype_of = Object.setPrototypeOf ? Object.getPrototypeOf : function getPrototypeOf(o) {
|
|
536
|
+
return o.__proto__ || Object.getPrototypeOf(o);
|
|
537
|
+
};
|
|
538
|
+
return _get_prototype_of(o);
|
|
539
|
+
}
|
|
540
|
+
function _inherits(subClass, superClass) {
|
|
541
|
+
if (typeof superClass !== "function" && superClass !== null) {
|
|
542
|
+
throw new TypeError("Super expression must either be null or a function");
|
|
543
|
+
}
|
|
544
|
+
subClass.prototype = Object.create(superClass && superClass.prototype, {
|
|
545
|
+
constructor: {
|
|
546
|
+
value: subClass,
|
|
547
|
+
writable: true,
|
|
548
|
+
configurable: true
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
if (superClass) _set_prototype_of(subClass, superClass);
|
|
552
|
+
}
|
|
553
|
+
function _instanceof(left, right) {
|
|
554
|
+
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
|
|
555
|
+
return !!right[Symbol.hasInstance](left);
|
|
556
|
+
} else {
|
|
557
|
+
return left instanceof right;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function _iterable_to_array_limit(arr, i) {
|
|
561
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
562
|
+
if (_i == null) return;
|
|
563
|
+
var _arr = [];
|
|
564
|
+
var _n = true;
|
|
565
|
+
var _d = false;
|
|
566
|
+
var _s, _e;
|
|
567
|
+
try {
|
|
568
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
569
|
+
_arr.push(_s.value);
|
|
570
|
+
if (i && _arr.length === i) break;
|
|
571
|
+
}
|
|
572
|
+
} catch (err) {
|
|
573
|
+
_d = true;
|
|
574
|
+
_e = err;
|
|
575
|
+
} finally{
|
|
576
|
+
try {
|
|
577
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
578
|
+
} finally{
|
|
579
|
+
if (_d) throw _e;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return _arr;
|
|
583
|
+
}
|
|
584
|
+
function _non_iterable_rest() {
|
|
585
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
586
|
+
}
|
|
587
|
+
function _object_spread(target) {
|
|
588
|
+
for(var i = 1; i < arguments.length; i++){
|
|
589
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
590
|
+
var ownKeys = Object.keys(source);
|
|
591
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
592
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
593
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
594
|
+
}));
|
|
595
|
+
}
|
|
596
|
+
ownKeys.forEach(function(key) {
|
|
597
|
+
_define_property(target, key, source[key]);
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
return target;
|
|
601
|
+
}
|
|
602
|
+
function ownKeys(object, enumerableOnly) {
|
|
603
|
+
var keys = Object.keys(object);
|
|
604
|
+
if (Object.getOwnPropertySymbols) {
|
|
605
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
606
|
+
keys.push.apply(keys, symbols);
|
|
607
|
+
}
|
|
608
|
+
return keys;
|
|
609
|
+
}
|
|
610
|
+
function _object_spread_props(target, source) {
|
|
611
|
+
source = source != null ? source : {};
|
|
612
|
+
if (Object.getOwnPropertyDescriptors) {
|
|
613
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
614
|
+
} else {
|
|
615
|
+
ownKeys(Object(source)).forEach(function(key) {
|
|
616
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
return target;
|
|
620
|
+
}
|
|
621
|
+
function _possible_constructor_return(self, call) {
|
|
622
|
+
if (call && (_type_of(call) === "object" || typeof call === "function")) {
|
|
623
|
+
return call;
|
|
624
|
+
}
|
|
625
|
+
return _assert_this_initialized(self);
|
|
626
|
+
}
|
|
627
|
+
function _set_prototype_of(o, p) {
|
|
628
|
+
_set_prototype_of = Object.setPrototypeOf || function setPrototypeOf(o, p) {
|
|
629
|
+
o.__proto__ = p;
|
|
630
|
+
return o;
|
|
631
|
+
};
|
|
632
|
+
return _set_prototype_of(o, p);
|
|
633
|
+
}
|
|
634
|
+
function _sliced_to_array(arr, i) {
|
|
635
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
636
|
+
}
|
|
637
|
+
function _type_of(obj) {
|
|
638
|
+
"@swc/helpers - typeof";
|
|
639
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
640
|
+
}
|
|
641
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
642
|
+
if (!o) return;
|
|
643
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
644
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
645
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
646
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
647
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
648
|
+
}
|
|
649
|
+
function _is_native_reflect_construct() {
|
|
650
|
+
try {
|
|
651
|
+
var result = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
|
|
652
|
+
} catch (_) {}
|
|
653
|
+
return (_is_native_reflect_construct = function() {
|
|
654
|
+
return !!result;
|
|
655
|
+
})();
|
|
656
|
+
}
|
|
657
|
+
function _ts_generator(thisArg, body) {
|
|
658
|
+
var f, y, t, g, _ = {
|
|
659
|
+
label: 0,
|
|
660
|
+
sent: function() {
|
|
661
|
+
if (t[0] & 1) throw t[1];
|
|
662
|
+
return t[1];
|
|
663
|
+
},
|
|
664
|
+
trys: [],
|
|
665
|
+
ops: []
|
|
666
|
+
};
|
|
667
|
+
return g = {
|
|
668
|
+
next: verb(0),
|
|
669
|
+
"throw": verb(1),
|
|
670
|
+
"return": verb(2)
|
|
671
|
+
}, typeof Symbol === "function" && (g[Symbol.iterator] = function() {
|
|
672
|
+
return this;
|
|
673
|
+
}), g;
|
|
674
|
+
function verb(n) {
|
|
675
|
+
return function(v) {
|
|
676
|
+
return step([
|
|
677
|
+
n,
|
|
678
|
+
v
|
|
679
|
+
]);
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
function step(op) {
|
|
683
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
684
|
+
while(_)try {
|
|
685
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
686
|
+
if (y = 0, t) op = [
|
|
687
|
+
op[0] & 2,
|
|
688
|
+
t.value
|
|
689
|
+
];
|
|
690
|
+
switch(op[0]){
|
|
691
|
+
case 0:
|
|
692
|
+
case 1:
|
|
693
|
+
t = op;
|
|
694
|
+
break;
|
|
695
|
+
case 4:
|
|
696
|
+
_.label++;
|
|
697
|
+
return {
|
|
698
|
+
value: op[1],
|
|
699
|
+
done: false
|
|
700
|
+
};
|
|
701
|
+
case 5:
|
|
702
|
+
_.label++;
|
|
703
|
+
y = op[1];
|
|
704
|
+
op = [
|
|
705
|
+
0
|
|
706
|
+
];
|
|
707
|
+
continue;
|
|
708
|
+
case 7:
|
|
709
|
+
op = _.ops.pop();
|
|
710
|
+
_.trys.pop();
|
|
711
|
+
continue;
|
|
712
|
+
default:
|
|
713
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
714
|
+
_ = 0;
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
718
|
+
_.label = op[1];
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
722
|
+
_.label = t[1];
|
|
723
|
+
t = op;
|
|
724
|
+
break;
|
|
725
|
+
}
|
|
726
|
+
if (t && _.label < t[2]) {
|
|
727
|
+
_.label = t[2];
|
|
728
|
+
_.ops.push(op);
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
if (t[2]) _.ops.pop();
|
|
732
|
+
_.trys.pop();
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
op = body.call(thisArg, _);
|
|
736
|
+
} catch (e) {
|
|
737
|
+
op = [
|
|
738
|
+
6,
|
|
739
|
+
e
|
|
740
|
+
];
|
|
741
|
+
y = 0;
|
|
742
|
+
} finally{
|
|
743
|
+
f = t = 0;
|
|
744
|
+
}
|
|
745
|
+
if (op[0] & 5) throw op[1];
|
|
746
|
+
return {
|
|
747
|
+
value: op[0] ? op[1] : void 0,
|
|
748
|
+
done: true
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
function _ts_values(o) {
|
|
753
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
754
|
+
if (m) return m.call(o);
|
|
755
|
+
if (o && typeof o.length === "number") return {
|
|
756
|
+
next: function() {
|
|
757
|
+
if (o && i >= o.length) o = void 0;
|
|
758
|
+
return {
|
|
759
|
+
value: o && o[i++],
|
|
760
|
+
done: !o
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
765
|
+
}
|
|
766
|
+
var DynamicMidnightWalletClient = /*#__PURE__*/ function(DynamicWalletClient) {
|
|
767
|
+
_inherits(DynamicMidnightWalletClient, DynamicWalletClient);
|
|
768
|
+
function DynamicMidnightWalletClient(param, internalOptions) {
|
|
769
|
+
var environmentId = param.environmentId, authToken = param.authToken, baseApiUrl = param.baseApiUrl, baseMPCRelayApiUrl = param.baseMPCRelayApiUrl, storageKey = param.storageKey, debug = param.debug, featureFlags = param.featureFlags, _param_authMode = param.authMode, authMode = _param_authMode === void 0 ? browser.AuthMode.HEADER : _param_authMode, sdkVersion = param.sdkVersion, forwardMPCClient = param.forwardMPCClient, logger = param.logger;
|
|
770
|
+
_class_call_check(this, DynamicMidnightWalletClient);
|
|
771
|
+
var _this;
|
|
772
|
+
_this = _call_super(this, DynamicMidnightWalletClient, [
|
|
773
|
+
{
|
|
774
|
+
environmentId: environmentId,
|
|
775
|
+
authToken: authToken,
|
|
776
|
+
baseApiUrl: baseApiUrl,
|
|
777
|
+
baseMPCRelayApiUrl: baseMPCRelayApiUrl,
|
|
778
|
+
storageKey: storageKey,
|
|
779
|
+
debug: debug,
|
|
780
|
+
featureFlags: featureFlags,
|
|
781
|
+
authMode: authMode,
|
|
782
|
+
sdkVersion: sdkVersion,
|
|
783
|
+
forwardMPCClient: forwardMPCClient,
|
|
784
|
+
logger: logger
|
|
785
|
+
},
|
|
786
|
+
internalOptions
|
|
787
|
+
]), _define_property(_this, "chainName", 'MIDNIGHT'), // Keep wallet alive between calls — avoids 90s re-sync per operation.
|
|
788
|
+
// `diagnosticSub` is the long-lived pending-tx log subscription attached
|
|
789
|
+
// at init time; we hold onto its handle so account-switch / resetCache
|
|
790
|
+
// paths can unsubscribe it and release the closure.
|
|
791
|
+
_define_property(_this, "cachedWallet", null), // Dedup concurrent init calls for the same account. Without this, two
|
|
792
|
+
// public methods invoked back-to-back (e.g. getPrivateBalance +
|
|
793
|
+
// submitTransaction) would both miss the cache, both run WalletFacade.init,
|
|
794
|
+
// and the first wallet's WebSocket + sync threads + diagnostic subscription
|
|
795
|
+
// would be leaked when the second overwrote `cachedWallet`.
|
|
796
|
+
_define_property(_this, "pendingInits", new Map());
|
|
797
|
+
return _this;
|
|
798
|
+
}
|
|
799
|
+
_create_class(DynamicMidnightWalletClient, [
|
|
800
|
+
{
|
|
801
|
+
key: "persistCachedWalletState",
|
|
802
|
+
value: /**
|
|
803
|
+
* Snapshot the current shielded + dust state to IndexedDB so the next
|
|
804
|
+
* init on this account resumes from this checkpoint. Fire-and-forget —
|
|
805
|
+
* callers shouldn't await this to avoid adding latency to user actions.
|
|
806
|
+
*
|
|
807
|
+
* Option A: ONLY persist when the wallet is in a clean resting state
|
|
808
|
+
* (no pending-tx entries, no pending UTXOs on any side). If the wallet
|
|
809
|
+
* has in-flight work, skip the write — we don't want dirty state to
|
|
810
|
+
* get baked into IndexedDB and trap the user on next reload.
|
|
811
|
+
*
|
|
812
|
+
* "Clean" == whatever's persisted can be restored without dragging
|
|
813
|
+
* stale pending entries forward. If a tx fails or a draft is abandoned,
|
|
814
|
+
* sync will reconcile on the next load based on what the chain actually
|
|
815
|
+
* shows, which is always authoritative.
|
|
816
|
+
*/ function persistCachedWalletState() {
|
|
817
|
+
var _this = this;
|
|
818
|
+
return _async_to_generator(function() {
|
|
819
|
+
var cached, _snapshot_pending_all, _snapshot_pending, _snapshot_unshielded, _snapshot_shielded, _snapshot_dust, snapshot, _snapshot_pending_all_length, pendingTxCount, _snapshot_unshielded_pendingCoins, _length, unshieldedPending, _snapshot_shielded_pendingCoins, _length1, shieldedPending, _snapshot_dust_pendingCoins, _length2, dustPending, isDirty, _ref, shielded, dust, err;
|
|
820
|
+
return _ts_generator(this, function(_state) {
|
|
821
|
+
switch(_state.label){
|
|
822
|
+
case 0:
|
|
823
|
+
// Capture a local reference up-front: the three `await` sites below yield
|
|
824
|
+
// to the event loop, during which a concurrent resetCache or account
|
|
825
|
+
// switch could null `this.cachedWallet`. Using the local snapshot avoids
|
|
826
|
+
// a TypeError-then-warn on every concurrent access, and the final
|
|
827
|
+
// identity check prevents writing a stopped wallet's state to IndexedDB
|
|
828
|
+
// (which would effectively undo the reset on the next init).
|
|
829
|
+
cached = _this.cachedWallet;
|
|
830
|
+
if (!cached) return [
|
|
831
|
+
2
|
|
832
|
+
];
|
|
833
|
+
_state.label = 1;
|
|
834
|
+
case 1:
|
|
835
|
+
_state.trys.push([
|
|
836
|
+
1,
|
|
837
|
+
5,
|
|
838
|
+
,
|
|
839
|
+
6
|
|
840
|
+
]);
|
|
841
|
+
return [
|
|
842
|
+
4,
|
|
843
|
+
new Promise(function(resolve, reject) {
|
|
844
|
+
// `let` is required: the setTimeout closure references `sub` before the
|
|
845
|
+
// `.subscribe()` call returns, and the callback itself can fire
|
|
846
|
+
// synchronously (TDZ hazard with const).
|
|
847
|
+
// eslint-disable-next-line prefer-const
|
|
848
|
+
var sub;
|
|
849
|
+
var tid = setTimeout(function() {
|
|
850
|
+
sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
|
|
851
|
+
reject(new Error('persist snapshot timeout'));
|
|
852
|
+
}, 5000);
|
|
853
|
+
sub = cached.wallet.state().subscribe(function(s) {
|
|
854
|
+
clearTimeout(tid);
|
|
855
|
+
queueMicrotask(function() {
|
|
856
|
+
return sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
|
|
857
|
+
});
|
|
858
|
+
resolve(s);
|
|
859
|
+
});
|
|
860
|
+
})
|
|
861
|
+
];
|
|
862
|
+
case 2:
|
|
863
|
+
snapshot = _state.sent();
|
|
864
|
+
pendingTxCount = (_snapshot_pending_all_length = snapshot === null || snapshot === void 0 ? void 0 : (_snapshot_pending = snapshot.pending) === null || _snapshot_pending === void 0 ? void 0 : (_snapshot_pending_all = _snapshot_pending.all) === null || _snapshot_pending_all === void 0 ? void 0 : _snapshot_pending_all.length) !== null && _snapshot_pending_all_length !== void 0 ? _snapshot_pending_all_length : 0;
|
|
865
|
+
unshieldedPending = (_length = ((_snapshot_unshielded_pendingCoins = snapshot === null || snapshot === void 0 ? void 0 : (_snapshot_unshielded = snapshot.unshielded) === null || _snapshot_unshielded === void 0 ? void 0 : _snapshot_unshielded.pendingCoins) !== null && _snapshot_unshielded_pendingCoins !== void 0 ? _snapshot_unshielded_pendingCoins : []).length) !== null && _length !== void 0 ? _length : 0;
|
|
866
|
+
shieldedPending = (_length1 = ((_snapshot_shielded_pendingCoins = snapshot === null || snapshot === void 0 ? void 0 : (_snapshot_shielded = snapshot.shielded) === null || _snapshot_shielded === void 0 ? void 0 : _snapshot_shielded.pendingCoins) !== null && _snapshot_shielded_pendingCoins !== void 0 ? _snapshot_shielded_pendingCoins : []).length) !== null && _length1 !== void 0 ? _length1 : 0;
|
|
867
|
+
dustPending = (_length2 = ((_snapshot_dust_pendingCoins = snapshot === null || snapshot === void 0 ? void 0 : (_snapshot_dust = snapshot.dust) === null || _snapshot_dust === void 0 ? void 0 : _snapshot_dust.pendingCoins) !== null && _snapshot_dust_pendingCoins !== void 0 ? _snapshot_dust_pendingCoins : []).length) !== null && _length2 !== void 0 ? _length2 : 0;
|
|
868
|
+
isDirty = pendingTxCount > 0 || unshieldedPending > 0 || shieldedPending > 0 || dustPending > 0;
|
|
869
|
+
if (isDirty) {
|
|
870
|
+
_this.logger.debug("[Midnight] persist skipped — state is dirty \xb7 " + "pendingTxs=".concat(pendingTxCount, " \xb7 unshieldedPending=").concat(unshieldedPending, " \xb7 ") + "shieldedPending=".concat(shieldedPending, " \xb7 dustPending=").concat(dustPending, ". ") + "IndexedDB retains last clean snapshot.");
|
|
871
|
+
return [
|
|
872
|
+
2
|
|
873
|
+
];
|
|
874
|
+
}
|
|
875
|
+
return [
|
|
876
|
+
4,
|
|
877
|
+
Promise.all([
|
|
878
|
+
cached.wallet.shielded.serializeState(),
|
|
879
|
+
cached.wallet.dust.serializeState()
|
|
880
|
+
])
|
|
881
|
+
];
|
|
882
|
+
case 3:
|
|
883
|
+
_ref = _sliced_to_array.apply(void 0, [
|
|
884
|
+
_state.sent(),
|
|
885
|
+
2
|
|
886
|
+
]), shielded = _ref[0], dust = _ref[1];
|
|
887
|
+
// If the cache was swapped or cleared while we were serializing, skip
|
|
888
|
+
// the write — otherwise we'd persist the old wallet's snapshot under
|
|
889
|
+
// its accountAddress after resetCache (which just deleted that entry)
|
|
890
|
+
// or under a newly-stopped account.
|
|
891
|
+
if (_this.cachedWallet !== cached) {
|
|
892
|
+
_this.logger.debug('[Midnight] persist skipped — cache changed during serialize');
|
|
893
|
+
return [
|
|
894
|
+
2
|
|
895
|
+
];
|
|
896
|
+
}
|
|
897
|
+
return [
|
|
898
|
+
4,
|
|
899
|
+
putWalletState(cached.accountAddress, {
|
|
900
|
+
shielded: shielded,
|
|
901
|
+
dust: dust
|
|
902
|
+
})
|
|
903
|
+
];
|
|
904
|
+
case 4:
|
|
905
|
+
_state.sent();
|
|
906
|
+
_this.logger.debug('[Midnight] persisted clean wallet state to IndexedDB');
|
|
907
|
+
return [
|
|
908
|
+
3,
|
|
909
|
+
6
|
|
910
|
+
];
|
|
911
|
+
case 5:
|
|
912
|
+
err = _state.sent();
|
|
913
|
+
_this.logger.warn('[Midnight] Failed to persist wallet state:', err);
|
|
914
|
+
return [
|
|
915
|
+
3,
|
|
916
|
+
6
|
|
917
|
+
];
|
|
918
|
+
case 6:
|
|
919
|
+
return [
|
|
920
|
+
2
|
|
921
|
+
];
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
})();
|
|
925
|
+
}
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
key: "createWalletAccount",
|
|
929
|
+
value: function createWalletAccount(param) {
|
|
930
|
+
var thresholdSignatureScheme = param.thresholdSignatureScheme, _param_password = param.password, password = _param_password === void 0 ? undefined : _param_password, onError = param.onError, signedSessionId = param.signedSessionId;
|
|
931
|
+
var _this = this;
|
|
932
|
+
return _async_to_generator(function() {
|
|
933
|
+
var ceremonyCeremonyCompleteResolver, serverAccountAddress, ceremonyCompletePromise, _ref, publicKeyHex, clientKeyShares, accountAddress, pubKeyBytes, roleKeys, shieldedSecretKeys, shieldedAddr, shieldedBech32m, dustBech32m, dustSecretKey, dustAddr, toHex, correctAddresses, wallet, addrError, error;
|
|
934
|
+
return _ts_generator(this, function(_state) {
|
|
935
|
+
switch(_state.label){
|
|
936
|
+
case 0:
|
|
937
|
+
_state.trys.push([
|
|
938
|
+
0,
|
|
939
|
+
11,
|
|
940
|
+
,
|
|
941
|
+
12
|
|
942
|
+
]);
|
|
943
|
+
ceremonyCompletePromise = new Promise(function(resolve) {
|
|
944
|
+
ceremonyCeremonyCompleteResolver = resolve;
|
|
945
|
+
});
|
|
946
|
+
return [
|
|
947
|
+
4,
|
|
948
|
+
_this.keyGen({
|
|
949
|
+
chainName: _this.chainName,
|
|
950
|
+
thresholdSignatureScheme: thresholdSignatureScheme,
|
|
951
|
+
onError: onError,
|
|
952
|
+
onCeremonyComplete: function(accountAddress, walletId) {
|
|
953
|
+
var chainConfig = browser.getMPCChainConfig(_this.chainName);
|
|
954
|
+
serverAccountAddress = accountAddress;
|
|
955
|
+
_this.initializeWalletMapEntry({
|
|
956
|
+
accountAddress: accountAddress,
|
|
957
|
+
walletId: walletId,
|
|
958
|
+
chainName: _this.chainName,
|
|
959
|
+
thresholdSignatureScheme: thresholdSignatureScheme,
|
|
960
|
+
derivationPath: JSON.stringify(Object.fromEntries(chainConfig.derivationPath.map(function(value, index) {
|
|
961
|
+
return [
|
|
962
|
+
index,
|
|
963
|
+
value
|
|
964
|
+
];
|
|
965
|
+
})))
|
|
966
|
+
});
|
|
967
|
+
ceremonyCeremonyCompleteResolver(undefined);
|
|
968
|
+
},
|
|
969
|
+
password: password,
|
|
970
|
+
signedSessionId: signedSessionId
|
|
971
|
+
})
|
|
972
|
+
];
|
|
973
|
+
case 1:
|
|
974
|
+
_ref = _state.sent(), publicKeyHex = _ref.rawPublicKey, clientKeyShares = _ref.clientKeyShares;
|
|
975
|
+
return [
|
|
976
|
+
4,
|
|
977
|
+
ceremonyCompletePromise
|
|
978
|
+
];
|
|
979
|
+
case 2:
|
|
980
|
+
_state.sent();
|
|
981
|
+
if (!publicKeyHex || !clientKeyShares) {
|
|
982
|
+
throw new Error(ERROR_KEYGEN_FAILED);
|
|
983
|
+
}
|
|
984
|
+
// Server uses ledger.addressFromKey() (Poseidon hash of public key) —
|
|
985
|
+
// we can't run it in the browser because the hashing is WASM-only.
|
|
986
|
+
accountAddress = serverAccountAddress;
|
|
987
|
+
return [
|
|
988
|
+
4,
|
|
989
|
+
_this.setClientKeySharesToStorage({
|
|
990
|
+
accountAddress: accountAddress,
|
|
991
|
+
clientKeyShares: clientKeyShares
|
|
992
|
+
})
|
|
993
|
+
];
|
|
994
|
+
case 3:
|
|
995
|
+
_state.sent();
|
|
996
|
+
return [
|
|
997
|
+
4,
|
|
998
|
+
_this.storeEncryptedBackupByWallet({
|
|
999
|
+
accountAddress: accountAddress,
|
|
1000
|
+
clientKeyShares: clientKeyShares,
|
|
1001
|
+
password: password,
|
|
1002
|
+
signedSessionId: signedSessionId
|
|
1003
|
+
})
|
|
1004
|
+
];
|
|
1005
|
+
case 4:
|
|
1006
|
+
_state.sent();
|
|
1007
|
+
pubKeyBytes = Buffer.from(publicKeyHex, 'hex');
|
|
1008
|
+
_state.label = 5;
|
|
1009
|
+
case 5:
|
|
1010
|
+
_state.trys.push([
|
|
1011
|
+
5,
|
|
1012
|
+
9,
|
|
1013
|
+
,
|
|
1014
|
+
10
|
|
1015
|
+
]);
|
|
1016
|
+
_this.logger.info('[Midnight] Computing shielded and dust addresses client-side');
|
|
1017
|
+
return [
|
|
1018
|
+
4,
|
|
1019
|
+
_this.getRoleKeys(accountAddress, {
|
|
1020
|
+
password: password,
|
|
1021
|
+
signedSessionId: signedSessionId
|
|
1022
|
+
})
|
|
1023
|
+
];
|
|
1024
|
+
case 6:
|
|
1025
|
+
roleKeys = _state.sent();
|
|
1026
|
+
shieldedSecretKeys = ledger__namespace.ZswapSecretKeys.fromSeed(new Uint8Array(Buffer.from(roleKeys.shielded, 'hex')));
|
|
1027
|
+
shieldedAddr = new walletSdkAddressFormat.ShieldedAddress(new walletSdkAddressFormat.ShieldedCoinPublicKey(Buffer.from(shieldedSecretKeys.coinPublicKey, 'hex')), new walletSdkAddressFormat.ShieldedEncryptionPublicKey(Buffer.from(shieldedSecretKeys.encryptionPublicKey, 'hex')));
|
|
1028
|
+
shieldedBech32m = walletSdkAddressFormat.MidnightBech32m.encode('preview', shieldedAddr).toString();
|
|
1029
|
+
try {
|
|
1030
|
+
dustSecretKey = ledger__namespace.DustSecretKey.fromSeed(new Uint8Array(Buffer.from(roleKeys.dust, 'hex')));
|
|
1031
|
+
dustAddr = new walletSdkAddressFormat.DustAddress(dustSecretKey.publicKey);
|
|
1032
|
+
dustBech32m = walletSdkAddressFormat.MidnightBech32m.encode('preview', dustAddr).toString();
|
|
1033
|
+
} catch (dustErr) {
|
|
1034
|
+
_this.logger.warn('[Midnight] Dust address encoding failed (skipping):', dustErr);
|
|
1035
|
+
}
|
|
1036
|
+
toHex = function(val) {
|
|
1037
|
+
if (typeof val === 'string') return val;
|
|
1038
|
+
if (_instanceof(val, Uint8Array)) return Buffer.from(val).toString('hex');
|
|
1039
|
+
if ((typeof val === "undefined" ? "undefined" : _type_of(val)) === 'bigint') return val.toString(16);
|
|
1040
|
+
return String(val);
|
|
1041
|
+
};
|
|
1042
|
+
correctAddresses = [
|
|
1043
|
+
{
|
|
1044
|
+
address: accountAddress,
|
|
1045
|
+
type: 'midnight_unshielded',
|
|
1046
|
+
publicKey: toHex(publicKeyHex)
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
address: shieldedBech32m,
|
|
1050
|
+
type: 'midnight_shielded',
|
|
1051
|
+
publicKey: toHex(shieldedSecretKeys.coinPublicKey)
|
|
1052
|
+
}
|
|
1053
|
+
];
|
|
1054
|
+
if (dustBech32m && dustSecretKey) {
|
|
1055
|
+
correctAddresses.push({
|
|
1056
|
+
address: dustBech32m,
|
|
1057
|
+
type: 'midnight_dust',
|
|
1058
|
+
publicKey: toHex(dustSecretKey.publicKey)
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
_this.logger.info('[Midnight] Updating additional addresses with correct derivation');
|
|
1062
|
+
return [
|
|
1063
|
+
4,
|
|
1064
|
+
_this.getWallet({
|
|
1065
|
+
accountAddress: accountAddress,
|
|
1066
|
+
password: password,
|
|
1067
|
+
signedSessionId: signedSessionId
|
|
1068
|
+
})
|
|
1069
|
+
];
|
|
1070
|
+
case 7:
|
|
1071
|
+
wallet = _state.sent();
|
|
1072
|
+
return [
|
|
1073
|
+
4,
|
|
1074
|
+
_this.apiClient.updateAdditionalAddresses({
|
|
1075
|
+
walletId: wallet.walletId,
|
|
1076
|
+
additionalAddresses: correctAddresses
|
|
1077
|
+
})
|
|
1078
|
+
];
|
|
1079
|
+
case 8:
|
|
1080
|
+
_state.sent();
|
|
1081
|
+
_this.logger.info('[Midnight] Additional addresses updated');
|
|
1082
|
+
return [
|
|
1083
|
+
3,
|
|
1084
|
+
10
|
|
1085
|
+
];
|
|
1086
|
+
case 9:
|
|
1087
|
+
addrError = _state.sent();
|
|
1088
|
+
// Non-fatal — wallet works without correct addresses, just display is wrong
|
|
1089
|
+
_this.logger.error('[Midnight] FAILED TO COMPUTE/UPDATE ADDRESSES:', addrError);
|
|
1090
|
+
_this.logger.warn('[Midnight] Failed to update additional addresses (non-fatal)', addrError);
|
|
1091
|
+
return [
|
|
1092
|
+
3,
|
|
1093
|
+
10
|
|
1094
|
+
];
|
|
1095
|
+
case 10:
|
|
1096
|
+
return [
|
|
1097
|
+
2,
|
|
1098
|
+
{
|
|
1099
|
+
accountAddress: accountAddress,
|
|
1100
|
+
publicKeyHex: publicKeyHex,
|
|
1101
|
+
rawPublicKey: new Uint8Array(pubKeyBytes)
|
|
1102
|
+
}
|
|
1103
|
+
];
|
|
1104
|
+
case 11:
|
|
1105
|
+
error = _state.sent();
|
|
1106
|
+
// Re-throw password mismatch errors without wrapping
|
|
1107
|
+
if (_instanceof(error, Error) && error.message === browser.ERROR_PASSWORD_MISMATCH) {
|
|
1108
|
+
throw error;
|
|
1109
|
+
}
|
|
1110
|
+
_this.logger.error(ERROR_CREATE_WALLET_ACCOUNT, error);
|
|
1111
|
+
throw new Error(ERROR_CREATE_WALLET_ACCOUNT);
|
|
1112
|
+
case 12:
|
|
1113
|
+
return [
|
|
1114
|
+
2
|
|
1115
|
+
];
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
})();
|
|
1119
|
+
}
|
|
1120
|
+
},
|
|
1121
|
+
{
|
|
1122
|
+
key: "signMessage",
|
|
1123
|
+
value: function signMessage(param) {
|
|
1124
|
+
var message = param.message, accountAddress = param.accountAddress, _param_password = param.password, password = _param_password === void 0 ? undefined : _param_password, signedSessionId = param.signedSessionId, mfaToken = param.mfaToken, context = param.context, onError = param.onError;
|
|
1125
|
+
var _this = this;
|
|
1126
|
+
return _async_to_generator(function() {
|
|
1127
|
+
var messageHex, resolvedContext, signature, error;
|
|
1128
|
+
return _ts_generator(this, function(_state) {
|
|
1129
|
+
switch(_state.label){
|
|
1130
|
+
case 0:
|
|
1131
|
+
if (!accountAddress) {
|
|
1132
|
+
throw new Error(ERROR_ACCOUNT_ADDRESS_REQUIRED);
|
|
1133
|
+
}
|
|
1134
|
+
return [
|
|
1135
|
+
4,
|
|
1136
|
+
_this.verifyPassword({
|
|
1137
|
+
accountAddress: accountAddress,
|
|
1138
|
+
password: password,
|
|
1139
|
+
signedSessionId: signedSessionId
|
|
1140
|
+
})
|
|
1141
|
+
];
|
|
1142
|
+
case 1:
|
|
1143
|
+
_state.sent();
|
|
1144
|
+
// The signing API expects hex; policy validation needs the original
|
|
1145
|
+
// bytes. Send hex on the wire, keep the original on the context.
|
|
1146
|
+
messageHex = Buffer.from(message).toString('hex');
|
|
1147
|
+
// Cast: SignMessageContext doesn't declare chain-specific fields.
|
|
1148
|
+
resolvedContext = _object_spread_props(_object_spread({}, context), {
|
|
1149
|
+
midnightMessage: message
|
|
1150
|
+
});
|
|
1151
|
+
_state.label = 2;
|
|
1152
|
+
case 2:
|
|
1153
|
+
_state.trys.push([
|
|
1154
|
+
2,
|
|
1155
|
+
4,
|
|
1156
|
+
,
|
|
1157
|
+
5
|
|
1158
|
+
]);
|
|
1159
|
+
return [
|
|
1160
|
+
4,
|
|
1161
|
+
_this.sign({
|
|
1162
|
+
message: messageHex,
|
|
1163
|
+
accountAddress: accountAddress,
|
|
1164
|
+
chainName: _this.chainName,
|
|
1165
|
+
password: password,
|
|
1166
|
+
signedSessionId: signedSessionId,
|
|
1167
|
+
mfaToken: mfaToken,
|
|
1168
|
+
context: resolvedContext,
|
|
1169
|
+
onError: onError
|
|
1170
|
+
})
|
|
1171
|
+
];
|
|
1172
|
+
case 3:
|
|
1173
|
+
signature = _state.sent();
|
|
1174
|
+
return [
|
|
1175
|
+
2,
|
|
1176
|
+
Buffer.from(signature).toString('base64')
|
|
1177
|
+
];
|
|
1178
|
+
case 4:
|
|
1179
|
+
error = _state.sent();
|
|
1180
|
+
_this.logger.error(ERROR_SIGN_MESSAGE, error);
|
|
1181
|
+
throw new Error(ERROR_SIGN_MESSAGE);
|
|
1182
|
+
case 5:
|
|
1183
|
+
return [
|
|
1184
|
+
2
|
|
1185
|
+
];
|
|
1186
|
+
}
|
|
1187
|
+
});
|
|
1188
|
+
})();
|
|
1189
|
+
}
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
key: "signTransaction",
|
|
1193
|
+
value: /**
|
|
1194
|
+
* Sign + finalize (prove) an unsigned Midnight transaction. Unshielded
|
|
1195
|
+
* segments are MPC-signed via BIP-340; shielded/dust proving is delegated
|
|
1196
|
+
* to the facade's proof server.
|
|
1197
|
+
*
|
|
1198
|
+
* Side effect: finalizeRecipe mutates wallet state (adds a pending-tx
|
|
1199
|
+
* entry). If the caller never calls submitTransaction, that entry sits
|
|
1200
|
+
* until the next re-sync drops it.
|
|
1201
|
+
*/ function signTransaction(param) {
|
|
1202
|
+
var senderAddress = param.senderAddress, transaction = param.transaction, _param_password = param.password, password = _param_password === void 0 ? undefined : _param_password, signedSessionId = param.signedSessionId, mfaToken = param.mfaToken, elevatedAccessToken = param.elevatedAccessToken, networkId = param.networkId, context = param.context, onError = param.onError;
|
|
1203
|
+
var _this = this;
|
|
1204
|
+
return _async_to_generator(function() {
|
|
1205
|
+
var resolvedNetworkId, wallet, txBytes, unsignedTx, signedRecipe, finalized, error;
|
|
1206
|
+
return _ts_generator(this, function(_state) {
|
|
1207
|
+
switch(_state.label){
|
|
1208
|
+
case 0:
|
|
1209
|
+
if (!senderAddress) {
|
|
1210
|
+
throw new Error(ERROR_ACCOUNT_ADDRESS_REQUIRED);
|
|
1211
|
+
}
|
|
1212
|
+
return [
|
|
1213
|
+
4,
|
|
1214
|
+
_this.verifyPassword({
|
|
1215
|
+
accountAddress: senderAddress,
|
|
1216
|
+
password: password,
|
|
1217
|
+
signedSessionId: signedSessionId
|
|
1218
|
+
})
|
|
1219
|
+
];
|
|
1220
|
+
case 1:
|
|
1221
|
+
_state.sent();
|
|
1222
|
+
if (networkId === undefined) {
|
|
1223
|
+
throw new Error(ERROR_NETWORK_ID_REQUIRED);
|
|
1224
|
+
}
|
|
1225
|
+
resolvedNetworkId = typeof networkId === 'number' ? networkId : getNetworkIdFromChainId(networkId);
|
|
1226
|
+
_state.label = 2;
|
|
1227
|
+
case 2:
|
|
1228
|
+
_state.trys.push([
|
|
1229
|
+
2,
|
|
1230
|
+
6,
|
|
1231
|
+
,
|
|
1232
|
+
7
|
|
1233
|
+
]);
|
|
1234
|
+
return [
|
|
1235
|
+
4,
|
|
1236
|
+
_this.initMidnightWallet('signTransaction', {
|
|
1237
|
+
accountAddress: senderAddress,
|
|
1238
|
+
password: password,
|
|
1239
|
+
signedSessionId: signedSessionId,
|
|
1240
|
+
mfaToken: mfaToken,
|
|
1241
|
+
elevatedAccessToken: elevatedAccessToken
|
|
1242
|
+
})
|
|
1243
|
+
];
|
|
1244
|
+
case 3:
|
|
1245
|
+
wallet = _state.sent().wallet;
|
|
1246
|
+
txBytes = Buffer.from(transaction, 'base64');
|
|
1247
|
+
unsignedTx = ledger__namespace.Transaction.deserialize('signature', 'pre-proof', 'pre-binding', new Uint8Array(txBytes));
|
|
1248
|
+
// Context carries the full unsigned tx so wallet-service policy can
|
|
1249
|
+
// validate against the whole transaction on each per-segment signature
|
|
1250
|
+
// (same pattern as BTC PSBT per-input context).
|
|
1251
|
+
return [
|
|
1252
|
+
4,
|
|
1253
|
+
_this.mpcSignIntents(unsignedTx, {
|
|
1254
|
+
accountAddress: senderAddress,
|
|
1255
|
+
password: password,
|
|
1256
|
+
signedSessionId: signedSessionId,
|
|
1257
|
+
mfaToken: mfaToken,
|
|
1258
|
+
elevatedAccessToken: elevatedAccessToken,
|
|
1259
|
+
fullTxBase64: transaction,
|
|
1260
|
+
networkId: resolvedNetworkId,
|
|
1261
|
+
baseContext: context,
|
|
1262
|
+
proofMarker: 'pre-proof'
|
|
1263
|
+
})
|
|
1264
|
+
];
|
|
1265
|
+
case 4:
|
|
1266
|
+
_state.sent();
|
|
1267
|
+
signedRecipe = {
|
|
1268
|
+
type: 'UNPROVEN_TRANSACTION',
|
|
1269
|
+
transaction: unsignedTx
|
|
1270
|
+
};
|
|
1271
|
+
_this.logger.info('[Midnight] signTransaction: finalizing (ZK prove)...');
|
|
1272
|
+
return [
|
|
1273
|
+
4,
|
|
1274
|
+
wallet.finalizeRecipe(signedRecipe)
|
|
1275
|
+
];
|
|
1276
|
+
case 5:
|
|
1277
|
+
finalized = _state.sent();
|
|
1278
|
+
return [
|
|
1279
|
+
2,
|
|
1280
|
+
Buffer.from(finalized.serialize()).toString('base64')
|
|
1281
|
+
];
|
|
1282
|
+
case 6:
|
|
1283
|
+
error = _state.sent();
|
|
1284
|
+
_this.logger.error(ERROR_SIGN_TRANSACTION, error);
|
|
1285
|
+
onError === null || onError === void 0 ? void 0 : onError(error);
|
|
1286
|
+
throw _instanceof(error, Error) ? error : new Error(ERROR_SIGN_TRANSACTION);
|
|
1287
|
+
case 7:
|
|
1288
|
+
return [
|
|
1289
|
+
2
|
|
1290
|
+
];
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
})();
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
key: "exportPrivateKey",
|
|
1298
|
+
value: function exportPrivateKey(param) {
|
|
1299
|
+
var accountAddress = param.accountAddress, _param_password = param.password, password = _param_password === void 0 ? undefined : _param_password, signedSessionId = param.signedSessionId, mfaToken = param.mfaToken, elevatedAccessToken = param.elevatedAccessToken;
|
|
1300
|
+
var _this = this;
|
|
1301
|
+
return _async_to_generator(function() {
|
|
1302
|
+
var mpcSigner, wallet, reconstructedKeyShare, exportId, data, keyExportRaw, exportStr, xprvBytes, error;
|
|
1303
|
+
return _ts_generator(this, function(_state) {
|
|
1304
|
+
switch(_state.label){
|
|
1305
|
+
case 0:
|
|
1306
|
+
// Matches the EVM/BTC exportPrivateKey pattern: verify the password OUTSIDE
|
|
1307
|
+
// the try-catch so a password-protected wallet can't be exported without
|
|
1308
|
+
// the right one AND the specific ERROR_PASSWORD_MISMATCH propagates to the
|
|
1309
|
+
// UI — wrapping it as ERROR_EXPORT_PRIVATE_KEY would hide which prompt to
|
|
1310
|
+
// show the user.
|
|
1311
|
+
return [
|
|
1312
|
+
4,
|
|
1313
|
+
_this.verifyPassword({
|
|
1314
|
+
accountAddress: accountAddress,
|
|
1315
|
+
password: password,
|
|
1316
|
+
signedSessionId: signedSessionId
|
|
1317
|
+
})
|
|
1318
|
+
];
|
|
1319
|
+
case 1:
|
|
1320
|
+
_state.sent();
|
|
1321
|
+
_state.label = 2;
|
|
1322
|
+
case 2:
|
|
1323
|
+
_state.trys.push([
|
|
1324
|
+
2,
|
|
1325
|
+
8,
|
|
1326
|
+
,
|
|
1327
|
+
9
|
|
1328
|
+
]);
|
|
1329
|
+
// Fetch the master xprv (not the wallet-path-derived key) so derivations
|
|
1330
|
+
// here match what the iframe's HDWallet produces from the same root.
|
|
1331
|
+
mpcSigner = browser.getMPCSigner({
|
|
1332
|
+
chainName: _this.chainName,
|
|
1333
|
+
baseRelayUrl: _this.baseMPCRelayApiUrl
|
|
1334
|
+
});
|
|
1335
|
+
return [
|
|
1336
|
+
4,
|
|
1337
|
+
_this.getWallet({
|
|
1338
|
+
accountAddress: accountAddress,
|
|
1339
|
+
password: password,
|
|
1340
|
+
walletOperation: browser.WalletOperation.EXPORT_PRIVATE_KEY,
|
|
1341
|
+
signedSessionId: signedSessionId
|
|
1342
|
+
})
|
|
1343
|
+
];
|
|
1344
|
+
case 3:
|
|
1345
|
+
wallet = _state.sent();
|
|
1346
|
+
return [
|
|
1347
|
+
4,
|
|
1348
|
+
_this.getReconstructedKeyShare(accountAddress, mpcSigner)
|
|
1349
|
+
];
|
|
1350
|
+
case 4:
|
|
1351
|
+
reconstructedKeyShare = _state.sent();
|
|
1352
|
+
return [
|
|
1353
|
+
4,
|
|
1354
|
+
_this.getExportId({
|
|
1355
|
+
chainName: _this.chainName,
|
|
1356
|
+
clientKeyShare: reconstructedKeyShare
|
|
1357
|
+
})
|
|
1358
|
+
];
|
|
1359
|
+
case 5:
|
|
1360
|
+
exportId = _state.sent();
|
|
1361
|
+
return [
|
|
1362
|
+
4,
|
|
1363
|
+
_this.apiClient.exportKey({
|
|
1364
|
+
walletId: wallet.walletId,
|
|
1365
|
+
exportId: exportId,
|
|
1366
|
+
mfaToken: mfaToken,
|
|
1367
|
+
elevatedAccessToken: elevatedAccessToken
|
|
1368
|
+
})
|
|
1369
|
+
];
|
|
1370
|
+
case 6:
|
|
1371
|
+
data = _state.sent();
|
|
1372
|
+
return [
|
|
1373
|
+
4,
|
|
1374
|
+
_this.performMPCExport(mpcSigner, reconstructedKeyShare, data.roomId, exportId, accountAddress, _this.chainName)
|
|
1375
|
+
];
|
|
1376
|
+
case 7:
|
|
1377
|
+
keyExportRaw = _state.sent();
|
|
1378
|
+
if (!keyExportRaw) {
|
|
1379
|
+
throw new Error('MPC export returned no key');
|
|
1380
|
+
}
|
|
1381
|
+
// keyExportRaw is a BIP32 xprv in base58; bytes 46-78 are the 32-byte
|
|
1382
|
+
// master private key per the xprv serialization format.
|
|
1383
|
+
exportStr = typeof keyExportRaw === 'string' ? keyExportRaw : String(keyExportRaw);
|
|
1384
|
+
xprvBytes = bs58.decode(exportStr);
|
|
1385
|
+
return [
|
|
1386
|
+
2,
|
|
1387
|
+
Buffer.from(xprvBytes.slice(46, 78)).toString('hex')
|
|
1388
|
+
];
|
|
1389
|
+
case 8:
|
|
1390
|
+
error = _state.sent();
|
|
1391
|
+
_this.logger.error(ERROR_EXPORT_PRIVATE_KEY, error);
|
|
1392
|
+
throw new Error(ERROR_EXPORT_PRIVATE_KEY);
|
|
1393
|
+
case 9:
|
|
1394
|
+
return [
|
|
1395
|
+
2
|
|
1396
|
+
];
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
})();
|
|
1400
|
+
}
|
|
1401
|
+
},
|
|
1402
|
+
{
|
|
1403
|
+
key: "importPrivateKey",
|
|
1404
|
+
value: function importPrivateKey(param) {
|
|
1405
|
+
var privateKey = param.privateKey, chainName = param.chainName, thresholdSignatureScheme = param.thresholdSignatureScheme, _param_password = param.password, password = _param_password === void 0 ? undefined : _param_password, signedSessionId = param.signedSessionId, onError = param.onError; param.publicAddressCheck; var legacyWalletId = param.legacyWalletId;
|
|
1406
|
+
var _this = this;
|
|
1407
|
+
return _async_to_generator(function() {
|
|
1408
|
+
var ceremonyCeremonyCompleteResolver, serverAccountAddress, ceremonyCompletePromise, formattedPrivateKey, _ref, rawPublicKey, clientKeyShares, accountAddress, pubKeyBytes, error;
|
|
1409
|
+
return _ts_generator(this, function(_state) {
|
|
1410
|
+
switch(_state.label){
|
|
1411
|
+
case 0:
|
|
1412
|
+
_state.trys.push([
|
|
1413
|
+
0,
|
|
1414
|
+
5,
|
|
1415
|
+
,
|
|
1416
|
+
6
|
|
1417
|
+
]);
|
|
1418
|
+
ceremonyCompletePromise = new Promise(function(resolve) {
|
|
1419
|
+
ceremonyCeremonyCompleteResolver = resolve;
|
|
1420
|
+
});
|
|
1421
|
+
formattedPrivateKey = privateKey.startsWith('0x') ? privateKey.slice(2) : privateKey;
|
|
1422
|
+
return [
|
|
1423
|
+
4,
|
|
1424
|
+
_this.importRawPrivateKey({
|
|
1425
|
+
chainName: chainName,
|
|
1426
|
+
thresholdSignatureScheme: thresholdSignatureScheme,
|
|
1427
|
+
privateKey: formattedPrivateKey,
|
|
1428
|
+
onCeremonyComplete: function(accountAddress, walletId) {
|
|
1429
|
+
serverAccountAddress = accountAddress;
|
|
1430
|
+
_this.initializeWalletMapEntry({
|
|
1431
|
+
accountAddress: accountAddress,
|
|
1432
|
+
walletId: walletId,
|
|
1433
|
+
chainName: _this.chainName,
|
|
1434
|
+
thresholdSignatureScheme: thresholdSignatureScheme
|
|
1435
|
+
});
|
|
1436
|
+
ceremonyCeremonyCompleteResolver(undefined);
|
|
1437
|
+
},
|
|
1438
|
+
onError: onError,
|
|
1439
|
+
legacyWalletId: legacyWalletId,
|
|
1440
|
+
password: password,
|
|
1441
|
+
signedSessionId: signedSessionId
|
|
1442
|
+
})
|
|
1443
|
+
];
|
|
1444
|
+
case 1:
|
|
1445
|
+
_ref = _state.sent(), rawPublicKey = _ref.rawPublicKey, clientKeyShares = _ref.clientKeyShares;
|
|
1446
|
+
return [
|
|
1447
|
+
4,
|
|
1448
|
+
ceremonyCompletePromise
|
|
1449
|
+
];
|
|
1450
|
+
case 2:
|
|
1451
|
+
_state.sent();
|
|
1452
|
+
if (!rawPublicKey || !clientKeyShares) {
|
|
1453
|
+
throw new Error(ERROR_IMPORT_PRIVATE_KEY);
|
|
1454
|
+
}
|
|
1455
|
+
accountAddress = serverAccountAddress;
|
|
1456
|
+
return [
|
|
1457
|
+
4,
|
|
1458
|
+
_this.setClientKeySharesToStorage({
|
|
1459
|
+
accountAddress: accountAddress,
|
|
1460
|
+
clientKeyShares: clientKeyShares
|
|
1461
|
+
})
|
|
1462
|
+
];
|
|
1463
|
+
case 3:
|
|
1464
|
+
_state.sent();
|
|
1465
|
+
return [
|
|
1466
|
+
4,
|
|
1467
|
+
_this.storeEncryptedBackupByWallet({
|
|
1468
|
+
accountAddress: accountAddress,
|
|
1469
|
+
clientKeyShares: clientKeyShares,
|
|
1470
|
+
password: password,
|
|
1471
|
+
signedSessionId: signedSessionId
|
|
1472
|
+
})
|
|
1473
|
+
];
|
|
1474
|
+
case 4:
|
|
1475
|
+
_state.sent();
|
|
1476
|
+
pubKeyBytes = Buffer.from(rawPublicKey, 'hex');
|
|
1477
|
+
return [
|
|
1478
|
+
2,
|
|
1479
|
+
{
|
|
1480
|
+
accountAddress: accountAddress,
|
|
1481
|
+
publicKeyHex: rawPublicKey,
|
|
1482
|
+
rawPublicKey: new Uint8Array(pubKeyBytes),
|
|
1483
|
+
clientKeyShares: clientKeyShares
|
|
1484
|
+
}
|
|
1485
|
+
];
|
|
1486
|
+
case 5:
|
|
1487
|
+
error = _state.sent();
|
|
1488
|
+
// Password mismatch must propagate so the UI can re-prompt — wrapping it
|
|
1489
|
+
// as ERROR_IMPORT_PRIVATE_KEY would hide which error the UI is meant to
|
|
1490
|
+
// recover from. Same pattern as createWalletAccount.
|
|
1491
|
+
if (_instanceof(error, Error) && error.message === browser.ERROR_PASSWORD_MISMATCH) {
|
|
1492
|
+
throw error;
|
|
1493
|
+
}
|
|
1494
|
+
_this.logger.error(ERROR_IMPORT_PRIVATE_KEY, error);
|
|
1495
|
+
onError === null || onError === void 0 ? void 0 : onError(error);
|
|
1496
|
+
throw new Error(ERROR_IMPORT_PRIVATE_KEY);
|
|
1497
|
+
case 6:
|
|
1498
|
+
return [
|
|
1499
|
+
2
|
|
1500
|
+
];
|
|
1501
|
+
}
|
|
1502
|
+
});
|
|
1503
|
+
})();
|
|
1504
|
+
}
|
|
1505
|
+
},
|
|
1506
|
+
{
|
|
1507
|
+
key: "getPublicKeyFromPrivateKey",
|
|
1508
|
+
value: function getPublicKeyFromPrivateKey(privateKeyHex) {
|
|
1509
|
+
try {
|
|
1510
|
+
var privateKeyBytes = Buffer.from(privateKeyHex, 'hex');
|
|
1511
|
+
if (privateKeyBytes.length !== 32) {
|
|
1512
|
+
throw new Error("Invalid private key length: ".concat(privateKeyBytes.length, ", expected 32"));
|
|
1513
|
+
}
|
|
1514
|
+
return ledger__namespace.signatureVerifyingKey(privateKeyHex);
|
|
1515
|
+
} catch (error) {
|
|
1516
|
+
// Debug-level only: this is a caller-input validation failure, not a
|
|
1517
|
+
// system error. The full error is re-thrown, so callers can surface or
|
|
1518
|
+
// inspect it themselves — no need to duplicate it at a higher level
|
|
1519
|
+
// where it could land in production logs.
|
|
1520
|
+
this.logger.debug('[Midnight] getPublicKeyFromPrivateKey failed', error);
|
|
1521
|
+
throw error;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
key: "getMidnightWallets",
|
|
1527
|
+
value: function getMidnightWallets() {
|
|
1528
|
+
var _this = this;
|
|
1529
|
+
return _async_to_generator(function() {
|
|
1530
|
+
var wallets, midnightWallets;
|
|
1531
|
+
return _ts_generator(this, function(_state) {
|
|
1532
|
+
switch(_state.label){
|
|
1533
|
+
case 0:
|
|
1534
|
+
return [
|
|
1535
|
+
4,
|
|
1536
|
+
_this.getWallets()
|
|
1537
|
+
];
|
|
1538
|
+
case 1:
|
|
1539
|
+
wallets = _state.sent();
|
|
1540
|
+
// chainName on the wallet-map entry is uppercase (matches this.chainName
|
|
1541
|
+
// and all other chain clients: 'EVM', 'SVM', 'BTC', …). Filtering on
|
|
1542
|
+
// lowercase 'midnight' here always returned an empty array.
|
|
1543
|
+
midnightWallets = wallets.filter(function(wallet) {
|
|
1544
|
+
return wallet.chainName === _this.chainName;
|
|
1545
|
+
});
|
|
1546
|
+
return [
|
|
1547
|
+
2,
|
|
1548
|
+
midnightWallets
|
|
1549
|
+
];
|
|
1550
|
+
}
|
|
1551
|
+
});
|
|
1552
|
+
})();
|
|
1553
|
+
}
|
|
1554
|
+
},
|
|
1555
|
+
{
|
|
1556
|
+
key: "getRoleKeys",
|
|
1557
|
+
value: /**
|
|
1558
|
+
* Get or derive Midnight role keys (cached in storage).
|
|
1559
|
+
* Calls existing exportKey internally - no new MPC flow.
|
|
1560
|
+
* Keys: unshielded (NightExternal), shielded (Zswap), dust.
|
|
1561
|
+
*/ function getRoleKeys(accountAddress, param) {
|
|
1562
|
+
var password = param.password, signedSessionId = param.signedSessionId, mfaToken = param.mfaToken, elevatedAccessToken = param.elevatedAccessToken;
|
|
1563
|
+
var _this = this;
|
|
1564
|
+
return _async_to_generator(function() {
|
|
1565
|
+
var _this_cachedWallet, mpcSigner, wallet, reconstructedKeyShare, exportId, data, keyExportRaw, exportStr, master, unshieldedNode, shieldedNode, dustNode, roleKeys;
|
|
1566
|
+
return _ts_generator(this, function(_state) {
|
|
1567
|
+
switch(_state.label){
|
|
1568
|
+
case 0:
|
|
1569
|
+
// In-memory cache: reuse role keys if the caller already booted this wallet
|
|
1570
|
+
// in the current session. initMidnightWallet sets this.cachedWallet after
|
|
1571
|
+
// its first call to getRoleKeys, so subsequent fresh-inits for the same
|
|
1572
|
+
// account can skip the expensive MPC export.
|
|
1573
|
+
if (((_this_cachedWallet = _this.cachedWallet) === null || _this_cachedWallet === void 0 ? void 0 : _this_cachedWallet.accountAddress) === accountAddress) {
|
|
1574
|
+
return [
|
|
1575
|
+
2,
|
|
1576
|
+
_this.cachedWallet.roleKeys
|
|
1577
|
+
];
|
|
1578
|
+
}
|
|
1579
|
+
// We deliberately do NOT persist role keys (private BIP32-derived keys
|
|
1580
|
+
// for shielded/dust/unshielded roles) to localStorage. Plaintext browser
|
|
1581
|
+
// storage of secret key material is a known exfiltration risk (XSS,
|
|
1582
|
+
// malicious extensions). The cost of MPC-re-exporting on each session
|
|
1583
|
+
// start (~few seconds) is acceptable vs. leaking the key material to
|
|
1584
|
+
// disk. A future hardening pass could add a password-derived AES-GCM
|
|
1585
|
+
// encryption layer to persist them securely across reloads.
|
|
1586
|
+
_this.logger.info('[Midnight] Exporting root key for role derivation', {
|
|
1587
|
+
accountAddress: accountAddress
|
|
1588
|
+
});
|
|
1589
|
+
mpcSigner = browser.getMPCSigner({
|
|
1590
|
+
chainName: _this.chainName,
|
|
1591
|
+
baseRelayUrl: _this.baseMPCRelayApiUrl
|
|
1592
|
+
});
|
|
1593
|
+
return [
|
|
1594
|
+
4,
|
|
1595
|
+
_this.getWallet({
|
|
1596
|
+
accountAddress: accountAddress,
|
|
1597
|
+
password: password,
|
|
1598
|
+
walletOperation: browser.WalletOperation.EXPORT_PRIVATE_KEY,
|
|
1599
|
+
signedSessionId: signedSessionId
|
|
1600
|
+
})
|
|
1601
|
+
];
|
|
1602
|
+
case 1:
|
|
1603
|
+
wallet = _state.sent();
|
|
1604
|
+
return [
|
|
1605
|
+
4,
|
|
1606
|
+
_this.getReconstructedKeyShare(accountAddress, mpcSigner)
|
|
1607
|
+
];
|
|
1608
|
+
case 2:
|
|
1609
|
+
reconstructedKeyShare = _state.sent();
|
|
1610
|
+
return [
|
|
1611
|
+
4,
|
|
1612
|
+
_this.getExportId({
|
|
1613
|
+
chainName: _this.chainName,
|
|
1614
|
+
clientKeyShare: reconstructedKeyShare
|
|
1615
|
+
})
|
|
1616
|
+
];
|
|
1617
|
+
case 3:
|
|
1618
|
+
exportId = _state.sent();
|
|
1619
|
+
return [
|
|
1620
|
+
4,
|
|
1621
|
+
_this.apiClient.exportKey({
|
|
1622
|
+
walletId: wallet.walletId,
|
|
1623
|
+
exportId: exportId,
|
|
1624
|
+
mfaToken: mfaToken,
|
|
1625
|
+
elevatedAccessToken: elevatedAccessToken
|
|
1626
|
+
})
|
|
1627
|
+
];
|
|
1628
|
+
case 4:
|
|
1629
|
+
data = _state.sent();
|
|
1630
|
+
return [
|
|
1631
|
+
4,
|
|
1632
|
+
_this.performMPCExport(mpcSigner, reconstructedKeyShare, data.roomId, exportId, accountAddress, _this.chainName)
|
|
1633
|
+
];
|
|
1634
|
+
case 5:
|
|
1635
|
+
keyExportRaw = _state.sent();
|
|
1636
|
+
if (!keyExportRaw) {
|
|
1637
|
+
throw new Error('MPC export returned no key');
|
|
1638
|
+
}
|
|
1639
|
+
// keyExportRaw is a base58 BIP32 xprv (e.g., xprv9s21ZrQH143K3...) — the MPC master xprv.
|
|
1640
|
+
// We derive role keys via BIP32 directly on this master so our derivations match what the
|
|
1641
|
+
// server does (BIP340.derivePrivateKeyFromXpriv) and what `verify-from-xprv.js` produces.
|
|
1642
|
+
exportStr = typeof keyExportRaw === 'string' ? keyExportRaw : String(keyExportRaw);
|
|
1643
|
+
master = bip32.HDKey.fromExtendedKey(exportStr);
|
|
1644
|
+
// Unshielded: NON-hardened to match the server's MPC signing path (Sodot's
|
|
1645
|
+
// derivePrivateKeyFromXpriv is non-hardened-only). If we used the hardened
|
|
1646
|
+
// Midnight-canonical path here, the UnshieldedWallet facade would track a
|
|
1647
|
+
// different address than the one the server signs for, and balances/txns
|
|
1648
|
+
// would silently diverge.
|
|
1649
|
+
// Shielded + Dust: hardened Midnight-canonical paths (m/44'/2400'/0'/role/0),
|
|
1650
|
+
// matching @midnight-ntwrk/wallet-sdk-hd. Those roles are signed entirely
|
|
1651
|
+
// client-side, so Sodot's non-hardened constraint doesn't apply.
|
|
1652
|
+
unshieldedNode = master.derive('m/44/2400/0/0/0');
|
|
1653
|
+
shieldedNode = master.derive("m/44'/2400'/0'/3/0");
|
|
1654
|
+
dustNode = master.derive("m/44'/2400'/0'/2/0");
|
|
1655
|
+
if (!unshieldedNode.privateKey || !shieldedNode.privateKey || !dustNode.privateKey) {
|
|
1656
|
+
throw new Error('BIP32 derivation returned null privateKey');
|
|
1657
|
+
}
|
|
1658
|
+
roleKeys = {
|
|
1659
|
+
unshielded: Buffer.from(unshieldedNode.privateKey).toString('hex'),
|
|
1660
|
+
shielded: Buffer.from(shieldedNode.privateKey).toString('hex'),
|
|
1661
|
+
dust: Buffer.from(dustNode.privateKey).toString('hex')
|
|
1662
|
+
};
|
|
1663
|
+
_this.logger.info('[Midnight] Role keys derived (in-memory only)', {
|
|
1664
|
+
accountAddress: accountAddress
|
|
1665
|
+
});
|
|
1666
|
+
return [
|
|
1667
|
+
2,
|
|
1668
|
+
roleKeys
|
|
1669
|
+
];
|
|
1670
|
+
}
|
|
1671
|
+
});
|
|
1672
|
+
})();
|
|
1673
|
+
}
|
|
1674
|
+
},
|
|
1675
|
+
{
|
|
1676
|
+
key: "initMidnightWallet",
|
|
1677
|
+
value: function initMidnightWallet(label, mpcParams) {
|
|
1678
|
+
var _this = this;
|
|
1679
|
+
return _async_to_generator(function() {
|
|
1680
|
+
var accountAddress, inFlight, initPromise;
|
|
1681
|
+
return _ts_generator(this, function(_state) {
|
|
1682
|
+
switch(_state.label){
|
|
1683
|
+
case 0:
|
|
1684
|
+
accountAddress = mpcParams.accountAddress;
|
|
1685
|
+
// If another init for this same account is in flight, return its promise
|
|
1686
|
+
// instead of kicking off a second, redundant WalletFacade.init.
|
|
1687
|
+
inFlight = _this.pendingInits.get(accountAddress);
|
|
1688
|
+
if (inFlight) {
|
|
1689
|
+
_this.logger.info("[Midnight] ".concat(label, ": joining in-flight init for ").concat(accountAddress));
|
|
1690
|
+
return [
|
|
1691
|
+
2,
|
|
1692
|
+
inFlight
|
|
1693
|
+
];
|
|
1694
|
+
}
|
|
1695
|
+
initPromise = _this.runMidnightWalletInit(label, mpcParams);
|
|
1696
|
+
_this.pendingInits.set(accountAddress, initPromise);
|
|
1697
|
+
_state.label = 1;
|
|
1698
|
+
case 1:
|
|
1699
|
+
_state.trys.push([
|
|
1700
|
+
1,
|
|
1701
|
+
,
|
|
1702
|
+
3,
|
|
1703
|
+
4
|
|
1704
|
+
]);
|
|
1705
|
+
return [
|
|
1706
|
+
4,
|
|
1707
|
+
initPromise
|
|
1708
|
+
];
|
|
1709
|
+
case 2:
|
|
1710
|
+
return [
|
|
1711
|
+
2,
|
|
1712
|
+
_state.sent()
|
|
1713
|
+
];
|
|
1714
|
+
case 3:
|
|
1715
|
+
_this.pendingInits.delete(accountAddress);
|
|
1716
|
+
return [
|
|
1717
|
+
7
|
|
1718
|
+
];
|
|
1719
|
+
case 4:
|
|
1720
|
+
return [
|
|
1721
|
+
2
|
|
1722
|
+
];
|
|
1723
|
+
}
|
|
1724
|
+
});
|
|
1725
|
+
})();
|
|
1726
|
+
}
|
|
1727
|
+
},
|
|
1728
|
+
{
|
|
1729
|
+
key: "runMidnightWalletInit",
|
|
1730
|
+
value: function runMidnightWalletInit(label, mpcParams) {
|
|
1731
|
+
var _this = this;
|
|
1732
|
+
return _async_to_generator(function() {
|
|
1733
|
+
var _this_cachedWallet, roleKeys, accountAddress, state, _this_cachedWallet_diagnosticSub, shieldedSecretKeys, dustSecretKey, unshieldedKeystore, config, savedState, wallet, syncStart, lastLogTime, fmtSide, state1, err, lastPendingCount, lastPendingSig, diagnosticSub;
|
|
1734
|
+
return _ts_generator(this, function(_state) {
|
|
1735
|
+
switch(_state.label){
|
|
1736
|
+
case 0:
|
|
1737
|
+
return [
|
|
1738
|
+
4,
|
|
1739
|
+
_this.getRoleKeys(mpcParams.accountAddress, mpcParams)
|
|
1740
|
+
];
|
|
1741
|
+
case 1:
|
|
1742
|
+
roleKeys = _state.sent();
|
|
1743
|
+
accountAddress = mpcParams.accountAddress;
|
|
1744
|
+
if (!(((_this_cachedWallet = _this.cachedWallet) === null || _this_cachedWallet === void 0 ? void 0 : _this_cachedWallet.accountAddress) === accountAddress)) return [
|
|
1745
|
+
3,
|
|
1746
|
+
3
|
|
1747
|
+
];
|
|
1748
|
+
_this.logger.info("[Midnight] ".concat(label, ": reusing cached wallet (background sync active)"));
|
|
1749
|
+
return [
|
|
1750
|
+
4,
|
|
1751
|
+
new Promise(function(resolve, reject) {
|
|
1752
|
+
// `sub` must be declared as `let` because the callback can fire
|
|
1753
|
+
// synchronously during `.subscribe()` (the cached wallet is already
|
|
1754
|
+
// synced, so the state observable emits immediately on subscription).
|
|
1755
|
+
// If we used `const sub = ...subscribe(...)`, the callback would hit
|
|
1756
|
+
// `sub` before the binding was initialized (TDZ error).
|
|
1757
|
+
// eslint-disable-next-line prefer-const
|
|
1758
|
+
var sub;
|
|
1759
|
+
var tid = setTimeout(function() {
|
|
1760
|
+
sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
|
|
1761
|
+
reject(new Error('Wallet re-sync timeout'));
|
|
1762
|
+
}, 30000);
|
|
1763
|
+
sub = _this.cachedWallet.wallet.state().subscribe(function(s) {
|
|
1764
|
+
if (s.isSynced) {
|
|
1765
|
+
clearTimeout(tid);
|
|
1766
|
+
// Defer to a microtask so `sub` is definitely assigned whether the
|
|
1767
|
+
// emission is synchronous or async.
|
|
1768
|
+
queueMicrotask(function() {
|
|
1769
|
+
return sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
|
|
1770
|
+
});
|
|
1771
|
+
resolve(s);
|
|
1772
|
+
}
|
|
1773
|
+
});
|
|
1774
|
+
})
|
|
1775
|
+
];
|
|
1776
|
+
case 2:
|
|
1777
|
+
state = _state.sent();
|
|
1778
|
+
return [
|
|
1779
|
+
2,
|
|
1780
|
+
_object_spread_props(_object_spread({}, _this.cachedWallet), {
|
|
1781
|
+
state: state
|
|
1782
|
+
})
|
|
1783
|
+
];
|
|
1784
|
+
case 3:
|
|
1785
|
+
if (!_this.cachedWallet) return [
|
|
1786
|
+
3,
|
|
1787
|
+
8
|
|
1788
|
+
];
|
|
1789
|
+
_this.logger.info("[Midnight] ".concat(label, ": stopping previous wallet (account changed)"));
|
|
1790
|
+
try {
|
|
1791
|
+
;
|
|
1792
|
+
(_this_cachedWallet_diagnosticSub = _this.cachedWallet.diagnosticSub) === null || _this_cachedWallet_diagnosticSub === void 0 ? void 0 : _this_cachedWallet_diagnosticSub.unsubscribe();
|
|
1793
|
+
} catch (e) {
|
|
1794
|
+
/* ignore */ }
|
|
1795
|
+
_state.label = 4;
|
|
1796
|
+
case 4:
|
|
1797
|
+
_state.trys.push([
|
|
1798
|
+
4,
|
|
1799
|
+
6,
|
|
1800
|
+
,
|
|
1801
|
+
7
|
|
1802
|
+
]);
|
|
1803
|
+
return [
|
|
1804
|
+
4,
|
|
1805
|
+
_this.cachedWallet.wallet.stop()
|
|
1806
|
+
];
|
|
1807
|
+
case 5:
|
|
1808
|
+
_state.sent();
|
|
1809
|
+
return [
|
|
1810
|
+
3,
|
|
1811
|
+
7
|
|
1812
|
+
];
|
|
1813
|
+
case 6:
|
|
1814
|
+
_state.sent();
|
|
1815
|
+
return [
|
|
1816
|
+
3,
|
|
1817
|
+
7
|
|
1818
|
+
];
|
|
1819
|
+
case 7:
|
|
1820
|
+
_this.cachedWallet = null;
|
|
1821
|
+
_state.label = 8;
|
|
1822
|
+
case 8:
|
|
1823
|
+
shieldedSecretKeys = ledger__namespace.ZswapSecretKeys.fromSeed(new Uint8Array(Buffer.from(roleKeys.shielded, 'hex')));
|
|
1824
|
+
dustSecretKey = ledger__namespace.DustSecretKey.fromSeed(new Uint8Array(Buffer.from(roleKeys.dust, 'hex')));
|
|
1825
|
+
unshieldedKeystore = walletSdkUnshieldedWallet.createKeystore(new Uint8Array(Buffer.from(roleKeys.unshielded, 'hex')), 'preview');
|
|
1826
|
+
config = {
|
|
1827
|
+
networkId: 'preview',
|
|
1828
|
+
costParameters: {
|
|
1829
|
+
feeBlocksMargin: 5
|
|
1830
|
+
},
|
|
1831
|
+
relayURL: new URL('wss://rpc.preview.midnight.network'),
|
|
1832
|
+
provingServerUrl: new URL('http://localhost:6300'),
|
|
1833
|
+
indexerClientConnection: {
|
|
1834
|
+
indexerHttpUrl: 'https://indexer.preview.midnight.network/api/v4/graphql',
|
|
1835
|
+
indexerWsUrl: 'wss://indexer.preview.midnight.network/api/v4/graphql/ws'
|
|
1836
|
+
},
|
|
1837
|
+
txHistoryStorage: new walletSdkUnshieldedWallet.NoOpTransactionHistoryStorage()
|
|
1838
|
+
};
|
|
1839
|
+
return [
|
|
1840
|
+
4,
|
|
1841
|
+
getWalletState(accountAddress)
|
|
1842
|
+
];
|
|
1843
|
+
case 9:
|
|
1844
|
+
savedState = _state.sent();
|
|
1845
|
+
if (savedState) {
|
|
1846
|
+
_this.logger.info("[Midnight] ".concat(label, ": found persisted state — resuming sync from checkpoint"));
|
|
1847
|
+
} else {
|
|
1848
|
+
_this.logger.info("[Midnight] ".concat(label, ": no persisted state — cold sync from genesis"));
|
|
1849
|
+
}
|
|
1850
|
+
_this.logger.info("[Midnight] ".concat(label, ": initializing wallet..."));
|
|
1851
|
+
return [
|
|
1852
|
+
4,
|
|
1853
|
+
walletSdkFacade.WalletFacade.init({
|
|
1854
|
+
configuration: config,
|
|
1855
|
+
shielded: function(cfg) {
|
|
1856
|
+
return savedState ? walletSdkShielded.ShieldedWallet(cfg).restore(savedState.shielded) : walletSdkShielded.ShieldedWallet(cfg).startWithSecretKeys(shieldedSecretKeys);
|
|
1857
|
+
},
|
|
1858
|
+
unshielded: function(cfg) {
|
|
1859
|
+
return walletSdkUnshieldedWallet.UnshieldedWallet(cfg).startWithPublicKey(walletSdkUnshieldedWallet.PublicKey.fromKeyStore(unshieldedKeystore));
|
|
1860
|
+
},
|
|
1861
|
+
dust: function(cfg) {
|
|
1862
|
+
return savedState ? walletSdkDustWallet.DustWallet(cfg).restore(savedState.dust) : walletSdkDustWallet.DustWallet(cfg).startWithSecretKey(dustSecretKey, ledger__namespace.LedgerParameters.initialParameters().dust);
|
|
1863
|
+
}
|
|
1864
|
+
})
|
|
1865
|
+
];
|
|
1866
|
+
case 10:
|
|
1867
|
+
wallet = _state.sent();
|
|
1868
|
+
return [
|
|
1869
|
+
4,
|
|
1870
|
+
wallet.start(shieldedSecretKeys, dustSecretKey)
|
|
1871
|
+
];
|
|
1872
|
+
case 11:
|
|
1873
|
+
_state.sent();
|
|
1874
|
+
_this.logger.debug("[Midnight] ".concat(label, ": syncing..."));
|
|
1875
|
+
syncStart = Date.now();
|
|
1876
|
+
lastLogTime = 0;
|
|
1877
|
+
// Per-side sync-progress summary. Fields are bigints; `gap` is how many
|
|
1878
|
+
// updates still need to be applied to reach the latest relevant index
|
|
1879
|
+
// (not the chain tip — `highestRelevantIndex` is what matters for balance).
|
|
1880
|
+
// Read the progress object the SDK itself uses for isSynced. For shielded
|
|
1881
|
+
// and dust it lives at `side.state.progress`; for unshielded it's
|
|
1882
|
+
// `side.progress`. Fall back across both to be safe.
|
|
1883
|
+
fmtSide = function(side) {
|
|
1884
|
+
var _side_state;
|
|
1885
|
+
var _side_state_progress;
|
|
1886
|
+
var p = (_side_state_progress = side === null || side === void 0 ? void 0 : (_side_state = side.state) === null || _side_state === void 0 ? void 0 : _side_state.progress) !== null && _side_state_progress !== void 0 ? _side_state_progress : side === null || side === void 0 ? void 0 : side.progress;
|
|
1887
|
+
if (!p) return 'n/a';
|
|
1888
|
+
var _p_appliedIndex;
|
|
1889
|
+
var applied = (_p_appliedIndex = p.appliedIndex) !== null && _p_appliedIndex !== void 0 ? _p_appliedIndex : 0n;
|
|
1890
|
+
var _p_highestRelevantIndex;
|
|
1891
|
+
var highestRelevant = (_p_highestRelevantIndex = p.highestRelevantIndex) !== null && _p_highestRelevantIndex !== void 0 ? _p_highestRelevantIndex : 0n;
|
|
1892
|
+
var _p_highestRelevantWalletIndex;
|
|
1893
|
+
var highestRelevantWallet = (_p_highestRelevantWalletIndex = p.highestRelevantWalletIndex) !== null && _p_highestRelevantWalletIndex !== void 0 ? _p_highestRelevantWalletIndex : 0n;
|
|
1894
|
+
var _p_highestIndex;
|
|
1895
|
+
var highest = (_p_highestIndex = p.highestIndex) !== null && _p_highestIndex !== void 0 ? _p_highestIndex : 0n;
|
|
1896
|
+
var connected = p.isConnected ? '✓' : '✗';
|
|
1897
|
+
var done = '?';
|
|
1898
|
+
if (typeof p.isStrictlyComplete === 'function') {
|
|
1899
|
+
done = p.isStrictlyComplete() ? '✓' : '✗';
|
|
1900
|
+
}
|
|
1901
|
+
return "done=".concat(done, " \xb7 applied=").concat(applied, " \xb7 hrw=").concat(highestRelevantWallet, " \xb7 hr=").concat(highestRelevant, " \xb7 tip=").concat(highest, " \xb7 ws=").concat(connected);
|
|
1902
|
+
};
|
|
1903
|
+
_state.label = 12;
|
|
1904
|
+
case 12:
|
|
1905
|
+
_state.trys.push([
|
|
1906
|
+
12,
|
|
1907
|
+
14,
|
|
1908
|
+
,
|
|
1909
|
+
19
|
|
1910
|
+
]);
|
|
1911
|
+
return [
|
|
1912
|
+
4,
|
|
1913
|
+
new Promise(function(resolve, reject) {
|
|
1914
|
+
// `let` + optional chaining + queueMicrotask — same TDZ-safe pattern used
|
|
1915
|
+
// in the cached-wallet, persist, and snapshot paths. Restoring from a
|
|
1916
|
+
// checkpoint that's already at chain tip makes `.subscribe(...)` fire the
|
|
1917
|
+
// callback synchronously before the `sub` binding initializes.
|
|
1918
|
+
// eslint-disable-next-line prefer-const
|
|
1919
|
+
var sub;
|
|
1920
|
+
var timeoutId = setTimeout(function() {
|
|
1921
|
+
sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
|
|
1922
|
+
reject(new Error('Wallet sync timeout'));
|
|
1923
|
+
}, 300000);
|
|
1924
|
+
sub = wallet.state().subscribe(function(s) {
|
|
1925
|
+
var now = Date.now();
|
|
1926
|
+
if (now - lastLogTime > 5000) {
|
|
1927
|
+
lastLogTime = now;
|
|
1928
|
+
_this.logger.debug("[Midnight] ".concat(label, ": sync @ ").concat(((now - syncStart) / 1000).toFixed(1), "s \xb7 ") + "isSynced=".concat(s.isSynced, " \xb7 ") + "shielded=".concat(fmtSide(s.shielded), " \xb7 ") + "unshielded=".concat(fmtSide(s.unshielded), " \xb7 ") + "dust=".concat(fmtSide(s.dust)));
|
|
1929
|
+
}
|
|
1930
|
+
if (s.isSynced) {
|
|
1931
|
+
clearTimeout(timeoutId);
|
|
1932
|
+
queueMicrotask(function() {
|
|
1933
|
+
return sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
|
|
1934
|
+
});
|
|
1935
|
+
_this.logger.debug("[Midnight] ".concat(label, ": synced in ").concat(((Date.now() - syncStart) / 1000).toFixed(1), "s \xb7 ") + "shielded=".concat(fmtSide(s.shielded), " \xb7 ") + "unshielded=".concat(fmtSide(s.unshielded), " \xb7 ") + "dust=".concat(fmtSide(s.dust)));
|
|
1936
|
+
resolve(s);
|
|
1937
|
+
}
|
|
1938
|
+
});
|
|
1939
|
+
})
|
|
1940
|
+
];
|
|
1941
|
+
case 13:
|
|
1942
|
+
state1 = _state.sent();
|
|
1943
|
+
return [
|
|
1944
|
+
3,
|
|
1945
|
+
19
|
|
1946
|
+
];
|
|
1947
|
+
case 14:
|
|
1948
|
+
err = _state.sent();
|
|
1949
|
+
_state.label = 15;
|
|
1950
|
+
case 15:
|
|
1951
|
+
_state.trys.push([
|
|
1952
|
+
15,
|
|
1953
|
+
17,
|
|
1954
|
+
,
|
|
1955
|
+
18
|
|
1956
|
+
]);
|
|
1957
|
+
return [
|
|
1958
|
+
4,
|
|
1959
|
+
wallet.stop()
|
|
1960
|
+
];
|
|
1961
|
+
case 16:
|
|
1962
|
+
_state.sent();
|
|
1963
|
+
return [
|
|
1964
|
+
3,
|
|
1965
|
+
18
|
|
1966
|
+
];
|
|
1967
|
+
case 17:
|
|
1968
|
+
_state.sent();
|
|
1969
|
+
return [
|
|
1970
|
+
3,
|
|
1971
|
+
18
|
|
1972
|
+
];
|
|
1973
|
+
case 18:
|
|
1974
|
+
throw err;
|
|
1975
|
+
case 19:
|
|
1976
|
+
// Diagnostic: track pending-tx transitions so we can observe TTL
|
|
1977
|
+
// auto-revert (entry flips from result=undefined → result.status='FAILURE'
|
|
1978
|
+
// then disappears on the next tick). Subscription handle is kept on the
|
|
1979
|
+
// cachedWallet so cleanup paths (account-switch, resetCache) can
|
|
1980
|
+
// unsubscribe it — otherwise its closure pins `this`, `wallet`, and the
|
|
1981
|
+
// full log-format state for the lifetime of the client.
|
|
1982
|
+
lastPendingCount = -1;
|
|
1983
|
+
lastPendingSig = '';
|
|
1984
|
+
diagnosticSub = wallet.state().subscribe(function(s) {
|
|
1985
|
+
var _s_pending;
|
|
1986
|
+
var _s_pending_all;
|
|
1987
|
+
var pending = (_s_pending_all = s === null || s === void 0 ? void 0 : (_s_pending = s.pending) === null || _s_pending === void 0 ? void 0 : _s_pending.all) !== null && _s_pending_all !== void 0 ? _s_pending_all : [];
|
|
1988
|
+
var sig = JSON.stringify(pending.map(function(p) {
|
|
1989
|
+
var _p_result;
|
|
1990
|
+
var _p_result_status;
|
|
1991
|
+
return {
|
|
1992
|
+
id: function() {
|
|
1993
|
+
try {
|
|
1994
|
+
var _p_tx_identifiers__slice, _p_tx_identifiers_;
|
|
1995
|
+
var _p_tx_identifiers__slice1;
|
|
1996
|
+
return (_p_tx_identifiers__slice1 = (_p_tx_identifiers_ = p.tx.identifiers()[0]) === null || _p_tx_identifiers_ === void 0 ? void 0 : (_p_tx_identifiers__slice = _p_tx_identifiers_.slice) === null || _p_tx_identifiers__slice === void 0 ? void 0 : _p_tx_identifiers__slice.call(_p_tx_identifiers_, 0, 12)) !== null && _p_tx_identifiers__slice1 !== void 0 ? _p_tx_identifiers__slice1 : '?';
|
|
1997
|
+
} catch (e) {
|
|
1998
|
+
return '?';
|
|
1999
|
+
}
|
|
2000
|
+
}(),
|
|
2001
|
+
resultStatus: (_p_result_status = (_p_result = p.result) === null || _p_result === void 0 ? void 0 : _p_result.status) !== null && _p_result_status !== void 0 ? _p_result_status : 'pending'
|
|
2002
|
+
};
|
|
2003
|
+
}));
|
|
2004
|
+
if (sig === lastPendingSig) return;
|
|
2005
|
+
lastPendingSig = sig;
|
|
2006
|
+
if (pending.length === 0 && lastPendingCount === 0) return;
|
|
2007
|
+
lastPendingCount = pending.length;
|
|
2008
|
+
_this.logger.debug("[Midnight] pending-tx list (".concat(pending.length, "):"), pending.map(function(p) {
|
|
2009
|
+
var _p_result;
|
|
2010
|
+
// Guard every date/serialize call — `p.creationTime` may be an
|
|
2011
|
+
// Effect DateTime (not a JS Date), `i.ttl` may be a bigint or
|
|
2012
|
+
// something else that new Date() can't handle. Fall back to
|
|
2013
|
+
// raw string representation if conversion fails.
|
|
2014
|
+
var safeDate = function(v) {
|
|
2015
|
+
try {
|
|
2016
|
+
var d = new Date(v);
|
|
2017
|
+
if (Number.isFinite(d.getTime())) return d.toISOString();
|
|
2018
|
+
return String(v);
|
|
2019
|
+
} catch (e) {
|
|
2020
|
+
return String(v);
|
|
2021
|
+
}
|
|
2022
|
+
};
|
|
2023
|
+
var intentTtls = [];
|
|
2024
|
+
try {
|
|
2025
|
+
var _p_tx_intents;
|
|
2026
|
+
var _p_tx_intents_values_toArray;
|
|
2027
|
+
intentTtls = ((_p_tx_intents_values_toArray = (_p_tx_intents = p.tx.intents) === null || _p_tx_intents === void 0 ? void 0 : _p_tx_intents.values().toArray()) !== null && _p_tx_intents_values_toArray !== void 0 ? _p_tx_intents_values_toArray : []).map(function(i) {
|
|
2028
|
+
if ((i === null || i === void 0 ? void 0 : i.ttl) == null) return 'none';
|
|
2029
|
+
// Midnight intent TTLs are typically seconds since epoch (may
|
|
2030
|
+
// also arrive as bigint or Date). Multiply to ms only if the
|
|
2031
|
+
// value is clearly in the seconds range (< 1e12).
|
|
2032
|
+
var asNumber = _type_of(i.ttl) === 'bigint' ? Number(i.ttl) : i.ttl;
|
|
2033
|
+
var ms = typeof asNumber === 'number' && asNumber < 1e12 ? asNumber * 1000 : asNumber;
|
|
2034
|
+
return safeDate(ms);
|
|
2035
|
+
});
|
|
2036
|
+
} catch (e) {
|
|
2037
|
+
/* ignore */ }
|
|
2038
|
+
var idShort = '?';
|
|
2039
|
+
try {
|
|
2040
|
+
var _p_tx_identifiers__slice, _p_tx_identifiers_;
|
|
2041
|
+
var _p_tx_identifiers__slice1;
|
|
2042
|
+
idShort = (_p_tx_identifiers__slice1 = (_p_tx_identifiers_ = p.tx.identifiers()[0]) === null || _p_tx_identifiers_ === void 0 ? void 0 : (_p_tx_identifiers__slice = _p_tx_identifiers_.slice) === null || _p_tx_identifiers__slice === void 0 ? void 0 : _p_tx_identifiers__slice.call(_p_tx_identifiers_, 0, 16)) !== null && _p_tx_identifiers__slice1 !== void 0 ? _p_tx_identifiers__slice1 : '?';
|
|
2043
|
+
} catch (e) {
|
|
2044
|
+
/* ignore */ }
|
|
2045
|
+
var _p_result_status;
|
|
2046
|
+
return {
|
|
2047
|
+
id: idShort,
|
|
2048
|
+
creationTime: p.creationTime == null ? 'n/a' : safeDate(p.creationTime),
|
|
2049
|
+
result: (_p_result_status = (_p_result = p.result) === null || _p_result === void 0 ? void 0 : _p_result.status) !== null && _p_result_status !== void 0 ? _p_result_status : 'pending',
|
|
2050
|
+
intentTtls: intentTtls
|
|
2051
|
+
};
|
|
2052
|
+
}));
|
|
2053
|
+
});
|
|
2054
|
+
_this.cachedWallet = {
|
|
2055
|
+
wallet: wallet,
|
|
2056
|
+
ledger: ledger__namespace,
|
|
2057
|
+
roleKeys: roleKeys,
|
|
2058
|
+
accountAddress: accountAddress,
|
|
2059
|
+
shieldedSecretKeys: shieldedSecretKeys,
|
|
2060
|
+
dustSecretKey: dustSecretKey,
|
|
2061
|
+
diagnosticSub: diagnosticSub
|
|
2062
|
+
};
|
|
2063
|
+
_this.logger.info("[Midnight] ".concat(label, ": wallet cached for future calls"));
|
|
2064
|
+
// Persist the freshly-synced state to IndexedDB so the next init can
|
|
2065
|
+
// resume. Routed through persistCachedWalletState so the same "clean
|
|
2066
|
+
// only" guard (Option A) applies — if this fresh sync somehow restored
|
|
2067
|
+
// dirty state (e.g., a lingering pending-tx entry from an earlier
|
|
2068
|
+
// session), we skip the write and keep IndexedDB at its last known-good.
|
|
2069
|
+
void _async_to_generator(function() {
|
|
2070
|
+
var err;
|
|
2071
|
+
return _ts_generator(this, function(_state) {
|
|
2072
|
+
switch(_state.label){
|
|
2073
|
+
case 0:
|
|
2074
|
+
_state.trys.push([
|
|
2075
|
+
0,
|
|
2076
|
+
2,
|
|
2077
|
+
,
|
|
2078
|
+
3
|
|
2079
|
+
]);
|
|
2080
|
+
return [
|
|
2081
|
+
4,
|
|
2082
|
+
_this.persistCachedWalletState()
|
|
2083
|
+
];
|
|
2084
|
+
case 1:
|
|
2085
|
+
_state.sent();
|
|
2086
|
+
return [
|
|
2087
|
+
3,
|
|
2088
|
+
3
|
|
2089
|
+
];
|
|
2090
|
+
case 2:
|
|
2091
|
+
err = _state.sent();
|
|
2092
|
+
_this.logger.warn('[Midnight] Failed to persist wallet state:', err);
|
|
2093
|
+
return [
|
|
2094
|
+
3,
|
|
2095
|
+
3
|
|
2096
|
+
];
|
|
2097
|
+
case 3:
|
|
2098
|
+
return [
|
|
2099
|
+
2
|
|
2100
|
+
];
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
2103
|
+
})();
|
|
2104
|
+
return [
|
|
2105
|
+
2,
|
|
2106
|
+
{
|
|
2107
|
+
wallet: wallet,
|
|
2108
|
+
state: state1,
|
|
2109
|
+
ledger: ledger__namespace,
|
|
2110
|
+
roleKeys: roleKeys,
|
|
2111
|
+
accountAddress: accountAddress,
|
|
2112
|
+
shieldedSecretKeys: shieldedSecretKeys,
|
|
2113
|
+
dustSecretKey: dustSecretKey
|
|
2114
|
+
}
|
|
2115
|
+
];
|
|
2116
|
+
}
|
|
2117
|
+
});
|
|
2118
|
+
})();
|
|
2119
|
+
}
|
|
2120
|
+
},
|
|
2121
|
+
{
|
|
2122
|
+
key: "getPrivateBalance",
|
|
2123
|
+
value: function getPrivateBalance(param) {
|
|
2124
|
+
var accountAddress = param.accountAddress, password = param.password, signedSessionId = param.signedSessionId, mfaToken = param.mfaToken, elevatedAccessToken = param.elevatedAccessToken;
|
|
2125
|
+
var _this = this;
|
|
2126
|
+
return _async_to_generator(function() {
|
|
2127
|
+
var _state_unshielded, _state_shielded, _state_dust_balance, _state_dust, _ref, state, addr, unshieldedByToken, _state_unshielded_balances, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, _step_value, tokenType, value, shieldedByToken, _state_shielded_balances, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, _step_value1, tokenType1, value1, _state_dust_balance1, result;
|
|
2128
|
+
return _ts_generator(this, function(_state) {
|
|
2129
|
+
switch(_state.label){
|
|
2130
|
+
case 0:
|
|
2131
|
+
return [
|
|
2132
|
+
4,
|
|
2133
|
+
_this.initMidnightWallet('getPrivateBalance', {
|
|
2134
|
+
accountAddress: accountAddress,
|
|
2135
|
+
password: password,
|
|
2136
|
+
signedSessionId: signedSessionId,
|
|
2137
|
+
mfaToken: mfaToken,
|
|
2138
|
+
elevatedAccessToken: elevatedAccessToken
|
|
2139
|
+
})
|
|
2140
|
+
];
|
|
2141
|
+
case 1:
|
|
2142
|
+
_ref = _state.sent(), state = _ref.state, addr = _ref.accountAddress;
|
|
2143
|
+
unshieldedByToken = {};
|
|
2144
|
+
_iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2145
|
+
try {
|
|
2146
|
+
for(_iterator = Object.entries((_state_unshielded_balances = (_state_unshielded = state.unshielded) === null || _state_unshielded === void 0 ? void 0 : _state_unshielded.balances) !== null && _state_unshielded_balances !== void 0 ? _state_unshielded_balances : {})[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
2147
|
+
_step_value = _sliced_to_array(_step.value, 2), tokenType = _step_value[0], value = _step_value[1];
|
|
2148
|
+
unshieldedByToken[tokenType] = value.toString();
|
|
2149
|
+
}
|
|
2150
|
+
} catch (err) {
|
|
2151
|
+
_didIteratorError = true;
|
|
2152
|
+
_iteratorError = err;
|
|
2153
|
+
} finally{
|
|
2154
|
+
try {
|
|
2155
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
2156
|
+
_iterator.return();
|
|
2157
|
+
}
|
|
2158
|
+
} finally{
|
|
2159
|
+
if (_didIteratorError) {
|
|
2160
|
+
throw _iteratorError;
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
shieldedByToken = {};
|
|
2165
|
+
_iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
2166
|
+
try {
|
|
2167
|
+
for(_iterator1 = Object.entries((_state_shielded_balances = (_state_shielded = state.shielded) === null || _state_shielded === void 0 ? void 0 : _state_shielded.balances) !== null && _state_shielded_balances !== void 0 ? _state_shielded_balances : {})[Symbol.iterator](); !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
2168
|
+
_step_value1 = _sliced_to_array(_step1.value, 2), tokenType1 = _step_value1[0], value1 = _step_value1[1];
|
|
2169
|
+
shieldedByToken[tokenType1] = value1.toString();
|
|
2170
|
+
}
|
|
2171
|
+
} catch (err) {
|
|
2172
|
+
_didIteratorError1 = true;
|
|
2173
|
+
_iteratorError1 = err;
|
|
2174
|
+
} finally{
|
|
2175
|
+
try {
|
|
2176
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
2177
|
+
_iterator1.return();
|
|
2178
|
+
}
|
|
2179
|
+
} finally{
|
|
2180
|
+
if (_didIteratorError1) {
|
|
2181
|
+
throw _iteratorError1;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
result = {
|
|
2186
|
+
unshielded: unshieldedByToken,
|
|
2187
|
+
shielded: shieldedByToken,
|
|
2188
|
+
dust: ((_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).toString(),
|
|
2189
|
+
address: addr
|
|
2190
|
+
};
|
|
2191
|
+
// Fire-and-forget snapshot so the next init resumes from here instead
|
|
2192
|
+
// of cold-syncing from genesis.
|
|
2193
|
+
void _this.persistCachedWalletState();
|
|
2194
|
+
return [
|
|
2195
|
+
2,
|
|
2196
|
+
result
|
|
2197
|
+
];
|
|
2198
|
+
}
|
|
2199
|
+
});
|
|
2200
|
+
})();
|
|
2201
|
+
}
|
|
2202
|
+
},
|
|
2203
|
+
{
|
|
2204
|
+
key: "registerDust",
|
|
2205
|
+
value: function registerDust(param) {
|
|
2206
|
+
var accountAddress = param.accountAddress, password = param.password, signedSessionId = param.signedSessionId, mfaToken = param.mfaToken, elevatedAccessToken = param.elevatedAccessToken;
|
|
2207
|
+
var _this = this;
|
|
2208
|
+
return _async_to_generator(function() {
|
|
2209
|
+
var _state_dust_balance, _state_dust, _state_unshielded, _state_dust_balance1, _state_dust1, _ref, wallet, state, ledger, roleKeys, _state_dust_balance2, existingDust, _state_unshielded_availableCoins, allUtxos, nightUtxos, _state_dust_balance3, _state_dust2, _state_dust_balance4, dustBalance, verifyingKey, recipe, finalized, submitId, submitIdStr, _state_dust_balance5;
|
|
2210
|
+
return _ts_generator(this, function(_state) {
|
|
2211
|
+
switch(_state.label){
|
|
2212
|
+
case 0:
|
|
2213
|
+
return [
|
|
2214
|
+
4,
|
|
2215
|
+
_this.initMidnightWallet('registerDust', {
|
|
2216
|
+
accountAddress: accountAddress,
|
|
2217
|
+
password: password,
|
|
2218
|
+
signedSessionId: signedSessionId,
|
|
2219
|
+
mfaToken: mfaToken,
|
|
2220
|
+
elevatedAccessToken: elevatedAccessToken
|
|
2221
|
+
})
|
|
2222
|
+
];
|
|
2223
|
+
case 1:
|
|
2224
|
+
_ref = _state.sent(), wallet = _ref.wallet, state = _ref.state, ledger = _ref.ledger, roleKeys = _ref.roleKeys;
|
|
2225
|
+
existingDust = (_state_dust_balance2 = (_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_balance2 !== void 0 ? _state_dust_balance2 : 0n;
|
|
2226
|
+
if (existingDust > 0n) {
|
|
2227
|
+
return [
|
|
2228
|
+
2,
|
|
2229
|
+
{
|
|
2230
|
+
dustBalance: existingDust.toString(),
|
|
2231
|
+
status: 'already_has_dust',
|
|
2232
|
+
message: "Wallet already has ".concat(existingDust.toString(), " dust. No action needed."),
|
|
2233
|
+
registeredCount: 0
|
|
2234
|
+
}
|
|
2235
|
+
];
|
|
2236
|
+
}
|
|
2237
|
+
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 : [];
|
|
2238
|
+
nightUtxos = allUtxos.filter(function(coin) {
|
|
2239
|
+
var _coin_meta;
|
|
2240
|
+
return ((_coin_meta = coin.meta) === null || _coin_meta === void 0 ? void 0 : _coin_meta.registeredForDustGeneration) !== true;
|
|
2241
|
+
});
|
|
2242
|
+
_this.logger.info('[Midnight] registerDust: UTXOs total:', allUtxos.length, 'unregistered:', nightUtxos.length);
|
|
2243
|
+
if (nightUtxos.length === 0 && allUtxos.length > 0) {
|
|
2244
|
+
dustBalance = ((_state_dust_balance4 = (_state_dust2 = state.dust) === null || _state_dust2 === void 0 ? void 0 : (_state_dust_balance3 = _state_dust2.balance) === null || _state_dust_balance3 === void 0 ? void 0 : _state_dust_balance3.call(_state_dust2, new Date())) !== null && _state_dust_balance4 !== void 0 ? _state_dust_balance4 : 0n).toString();
|
|
2245
|
+
return [
|
|
2246
|
+
2,
|
|
2247
|
+
{
|
|
2248
|
+
dustBalance: dustBalance,
|
|
2249
|
+
status: 'already_registered',
|
|
2250
|
+
message: "All ".concat(allUtxos.length, " NIGHT UTXO(s) are already registered for dust generation. ") + "Current dust balance is ".concat(dustBalance, ". Dust generation runs continuously on-chain — ") + "poll getPrivateBalance periodically to see it accumulate. If it stays at 0 for a long " + "time past the grace period (3h on preview), the NIGHT UTXO may be generating too " + "slowly to show up — fund the wallet with more NIGHT to increase the rate.",
|
|
2251
|
+
registeredCount: 0
|
|
2252
|
+
}
|
|
2253
|
+
];
|
|
2254
|
+
}
|
|
2255
|
+
if (nightUtxos.length === 0) {
|
|
2256
|
+
return [
|
|
2257
|
+
2,
|
|
2258
|
+
{
|
|
2259
|
+
dustBalance: '0',
|
|
2260
|
+
status: 'no_utxos',
|
|
2261
|
+
message: 'No NIGHT UTXOs in wallet. Fund the wallet with NIGHT from the preview faucet before registering for dust.',
|
|
2262
|
+
registeredCount: 0
|
|
2263
|
+
}
|
|
2264
|
+
];
|
|
2265
|
+
}
|
|
2266
|
+
_this.logger.debug("[Midnight] registerDust: registering ".concat(nightUtxos.length, " NIGHT UTXO(s) for dust generation..."));
|
|
2267
|
+
verifyingKey = ledger.signatureVerifyingKey(roleKeys.unshielded);
|
|
2268
|
+
_this.logger.debug('[Midnight] registerDust: building registration recipe...');
|
|
2269
|
+
return [
|
|
2270
|
+
4,
|
|
2271
|
+
wallet.registerNightUtxosForDustGeneration(nightUtxos, verifyingKey, function(payload) {
|
|
2272
|
+
return ledger.signData(roleKeys.unshielded, payload);
|
|
2273
|
+
})
|
|
2274
|
+
];
|
|
2275
|
+
case 2:
|
|
2276
|
+
recipe = _state.sent();
|
|
2277
|
+
_this.logger.debug('[Midnight] registerDust: proving + finalizing (proof server)...');
|
|
2278
|
+
return [
|
|
2279
|
+
4,
|
|
2280
|
+
wallet.finalizeRecipe(recipe)
|
|
2281
|
+
];
|
|
2282
|
+
case 3:
|
|
2283
|
+
finalized = _state.sent();
|
|
2284
|
+
_this.logger.debug('[Midnight] registerDust: broadcasting registration tx...');
|
|
2285
|
+
return [
|
|
2286
|
+
4,
|
|
2287
|
+
wallet.submitTransaction(finalized)
|
|
2288
|
+
];
|
|
2289
|
+
case 4:
|
|
2290
|
+
submitId = _state.sent();
|
|
2291
|
+
submitIdStr = DynamicMidnightWalletClient.toTxHashString(submitId);
|
|
2292
|
+
_this.logger.debug("[Midnight] registerDust: broadcast, txId=".concat(submitIdStr));
|
|
2293
|
+
// Do NOT block here waiting for dust. Dust generation is a continuous
|
|
2294
|
+
// on-chain process — it can take minutes to hours depending on NIGHT
|
|
2295
|
+
// value, grace period, and decay rate. Return immediately so the UI can
|
|
2296
|
+
// show "registration submitted" and the caller polls getPrivateBalance
|
|
2297
|
+
// to observe dust when it eventually appears.
|
|
2298
|
+
void _this.persistCachedWalletState();
|
|
2299
|
+
return [
|
|
2300
|
+
2,
|
|
2301
|
+
{
|
|
2302
|
+
dustBalance: ((_state_dust_balance5 = (_state_dust1 = state.dust) === null || _state_dust1 === void 0 ? void 0 : (_state_dust_balance1 = _state_dust1.balance) === null || _state_dust_balance1 === void 0 ? void 0 : _state_dust_balance1.call(_state_dust1, new Date())) !== null && _state_dust_balance5 !== void 0 ? _state_dust_balance5 : 0n).toString(),
|
|
2303
|
+
txId: submitIdStr,
|
|
2304
|
+
status: 'registered',
|
|
2305
|
+
message: "Registered ".concat(nightUtxos.length, " NIGHT UTXO(s) for dust generation (txId ").concat(submitIdStr.slice(0, 16), "…). ") + "Dust will accumulate continuously on-chain. Poll getPrivateBalance to see it arrive " + "(usually a few minutes after the registration tx confirms, depending on network parameters).",
|
|
2306
|
+
registeredCount: nightUtxos.length
|
|
2307
|
+
}
|
|
2308
|
+
];
|
|
2309
|
+
}
|
|
2310
|
+
});
|
|
2311
|
+
})();
|
|
2312
|
+
}
|
|
2313
|
+
},
|
|
2314
|
+
{
|
|
2315
|
+
key: "createTransferTransaction",
|
|
2316
|
+
value: /**
|
|
2317
|
+
* Build an unsigned Midnight transfer (unshielded, shielded, or mixed).
|
|
2318
|
+
* No MPC, no proof server, no broadcast — pair with signTransaction and
|
|
2319
|
+
* submitTransaction.
|
|
2320
|
+
*/ function createTransferTransaction(param) {
|
|
2321
|
+
var accountAddress = param.accountAddress, transfers = param.transfers, ttlMinutes = param.ttlMinutes, password = param.password, signedSessionId = param.signedSessionId, mfaToken = param.mfaToken, elevatedAccessToken = param.elevatedAccessToken;
|
|
2322
|
+
var _this = this;
|
|
2323
|
+
return _async_to_generator(function() {
|
|
2324
|
+
var _state_dust_balance, _state_dust, _ref, wallet, state, shieldedSecretKeys, dustSecretKey, _state_dust_balance1, dustBalance, nativeToken, requiredByKey, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, t, _t_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, ttlMs, recipe;
|
|
2325
|
+
return _ts_generator(this, function(_state) {
|
|
2326
|
+
switch(_state.label){
|
|
2327
|
+
case 0:
|
|
2328
|
+
if (!transfers || transfers.length === 0) {
|
|
2329
|
+
throw new Error('At least one transfer is required');
|
|
2330
|
+
}
|
|
2331
|
+
return [
|
|
2332
|
+
4,
|
|
2333
|
+
_this.initMidnightWallet('createTransferTransaction', {
|
|
2334
|
+
accountAddress: accountAddress,
|
|
2335
|
+
password: password,
|
|
2336
|
+
signedSessionId: signedSessionId,
|
|
2337
|
+
mfaToken: mfaToken,
|
|
2338
|
+
elevatedAccessToken: elevatedAccessToken
|
|
2339
|
+
})
|
|
2340
|
+
];
|
|
2341
|
+
case 1:
|
|
2342
|
+
_ref = _state.sent(), wallet = _ref.wallet, state = _ref.state, shieldedSecretKeys = _ref.shieldedSecretKeys, dustSecretKey = _ref.dustSecretKey;
|
|
2343
|
+
// Dust fee is required for every Midnight transaction regardless of the transfer type.
|
|
2344
|
+
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, new Date())) !== null && _state_dust_balance1 !== void 0 ? _state_dust_balance1 : 0n;
|
|
2345
|
+
if (dustBalance === 0n) {
|
|
2346
|
+
throw new Error('No dust balance — register dust first');
|
|
2347
|
+
}
|
|
2348
|
+
nativeToken = ledger__namespace.nativeToken().raw;
|
|
2349
|
+
// Aggregate per-(side, token) amounts so a single tx spending the same token
|
|
2350
|
+
// across multiple outputs gets checked against one combined total. The Map
|
|
2351
|
+
// value keeps the parsed components so we don't have to round-trip the key
|
|
2352
|
+
// through string-splitting — a token identifier containing `:` (Midnight
|
|
2353
|
+
// contract tokens can) would truncate if we split on ':' to recover it.
|
|
2354
|
+
requiredByKey = new Map();
|
|
2355
|
+
_iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2356
|
+
try {
|
|
2357
|
+
for(_iterator = transfers[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
2358
|
+
t = _step.value;
|
|
2359
|
+
;
|
|
2360
|
+
token = (_t_tokenType = t.tokenType) !== null && _t_tokenType !== void 0 ? _t_tokenType : nativeToken;
|
|
2361
|
+
// NUL-separated dedup key. The NUL byte cannot appear in a JS string
|
|
2362
|
+
// literal produced by any Midnight address/token encoder, so collisions
|
|
2363
|
+
// between (side, token) pairs are impossible regardless of token shape.
|
|
2364
|
+
key = "".concat(t.type, "\0").concat(token);
|
|
2365
|
+
existing = requiredByKey.get(key);
|
|
2366
|
+
;
|
|
2367
|
+
requiredByKey.set(key, {
|
|
2368
|
+
side: t.type,
|
|
2369
|
+
token: token,
|
|
2370
|
+
required: ((_existing_required = existing === null || existing === void 0 ? void 0 : existing.required) !== null && _existing_required !== void 0 ? _existing_required : 0n) + BigInt(t.amount)
|
|
2371
|
+
});
|
|
2372
|
+
}
|
|
2373
|
+
} catch (err) {
|
|
2374
|
+
_didIteratorError = true;
|
|
2375
|
+
_iteratorError = err;
|
|
2376
|
+
} finally{
|
|
2377
|
+
try {
|
|
2378
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
2379
|
+
_iterator.return();
|
|
2380
|
+
}
|
|
2381
|
+
} finally{
|
|
2382
|
+
if (_didIteratorError) {
|
|
2383
|
+
throw _iteratorError;
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
_iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
2388
|
+
try {
|
|
2389
|
+
for(_iterator1 = requiredByKey.values()[Symbol.iterator](); !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
2390
|
+
_step_value = _step1.value, side = _step_value.side, token1 = _step_value.token, required = _step_value.required;
|
|
2391
|
+
;
|
|
2392
|
+
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;
|
|
2393
|
+
;
|
|
2394
|
+
have = (_balances_token = balances === null || balances === void 0 ? void 0 : balances[token1]) !== null && _balances_token !== void 0 ? _balances_token : 0n;
|
|
2395
|
+
if (have < required) {
|
|
2396
|
+
throw new Error("Insufficient ".concat(side, " balance for token ").concat(token1, ": ").concat(have, " < ").concat(required));
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
} catch (err) {
|
|
2400
|
+
_didIteratorError1 = true;
|
|
2401
|
+
_iteratorError1 = err;
|
|
2402
|
+
} finally{
|
|
2403
|
+
try {
|
|
2404
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
2405
|
+
_iterator1.return();
|
|
2406
|
+
}
|
|
2407
|
+
} finally{
|
|
2408
|
+
if (_didIteratorError1) {
|
|
2409
|
+
throw _iteratorError1;
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
sdkTransfers = transfers.map(function(t) {
|
|
2414
|
+
var _t_tokenType;
|
|
2415
|
+
var token = (_t_tokenType = t.tokenType) !== null && _t_tokenType !== void 0 ? _t_tokenType : nativeToken;
|
|
2416
|
+
var receiverAddress;
|
|
2417
|
+
try {
|
|
2418
|
+
var parsed = walletSdkAddressFormat.MidnightBech32m.parse(t.recipientAddress);
|
|
2419
|
+
receiverAddress = t.type === 'unshielded' ? parsed.decode(walletSdkAddressFormat.UnshieldedAddress, 'preview') : parsed.decode(walletSdkAddressFormat.ShieldedAddress, 'preview');
|
|
2420
|
+
} catch (err) {
|
|
2421
|
+
throw new Error("Invalid ".concat(t.type, " recipient address ").concat(t.recipientAddress, ": ").concat(err.message));
|
|
2422
|
+
}
|
|
2423
|
+
_this.logger.info("[Midnight] createTransferTransaction: ".concat(t.type, " ").concat(t.amount, " of token ").concat(token.slice(0, 8), "… → ").concat(t.recipientAddress.slice(0, 20), "…"));
|
|
2424
|
+
return {
|
|
2425
|
+
type: t.type,
|
|
2426
|
+
outputs: [
|
|
2427
|
+
{
|
|
2428
|
+
amount: BigInt(t.amount),
|
|
2429
|
+
receiverAddress: receiverAddress,
|
|
2430
|
+
type: token
|
|
2431
|
+
}
|
|
2432
|
+
]
|
|
2433
|
+
};
|
|
2434
|
+
});
|
|
2435
|
+
// TTL defaults to 5 min. If the caller doesn't submit within this window,
|
|
2436
|
+
// the SDK's PendingTransactions.allFailed stream picks up the expired tx
|
|
2437
|
+
// and auto-reverts the reserved UTXOs back to available. Short enough
|
|
2438
|
+
// that abandoned drafts recover quickly; long enough for a normal
|
|
2439
|
+
// craft → sign (ZK prove) → submit path to complete comfortably.
|
|
2440
|
+
// Callers can override via ttlMinutes for relayer/batched-submit flows.
|
|
2441
|
+
ttlMs = (ttlMinutes !== null && ttlMinutes !== void 0 ? ttlMinutes : 5) * 60 * 1000;
|
|
2442
|
+
return [
|
|
2443
|
+
4,
|
|
2444
|
+
wallet.transferTransaction(sdkTransfers, {
|
|
2445
|
+
shieldedSecretKeys: shieldedSecretKeys,
|
|
2446
|
+
dustSecretKey: dustSecretKey
|
|
2447
|
+
}, {
|
|
2448
|
+
ttl: new Date(Date.now() + ttlMs)
|
|
2449
|
+
})
|
|
2450
|
+
];
|
|
2451
|
+
case 2:
|
|
2452
|
+
recipe = _state.sent();
|
|
2453
|
+
if (recipe.type !== 'UNPROVEN_TRANSACTION') {
|
|
2454
|
+
throw new Error("Unexpected recipe type from transferTransaction: ".concat(recipe.type));
|
|
2455
|
+
}
|
|
2456
|
+
return [
|
|
2457
|
+
2,
|
|
2458
|
+
{
|
|
2459
|
+
serializedTransaction: Buffer.from(recipe.transaction.serialize()).toString('base64')
|
|
2460
|
+
}
|
|
2461
|
+
];
|
|
2462
|
+
}
|
|
2463
|
+
});
|
|
2464
|
+
})();
|
|
2465
|
+
}
|
|
2466
|
+
},
|
|
2467
|
+
{
|
|
2468
|
+
key: "submitTransaction",
|
|
2469
|
+
value: /**
|
|
2470
|
+
* Broadcast a finalized Midnight transaction. Pure RPC — no MPC, no proof
|
|
2471
|
+
* server; reuses the cached wallet's node connection. Optionally blocks
|
|
2472
|
+
* until the unshielded balance for tokenType stabilizes.
|
|
2473
|
+
*/ function submitTransaction(param) {
|
|
2474
|
+
var accountAddress = param.accountAddress, transaction = param.transaction, _param_waitForBalance = param.waitForBalance, waitForBalance = _param_waitForBalance === void 0 ? false : _param_waitForBalance, tokenType = param.tokenType, balanceBefore = param.balanceBefore, password = param.password, signedSessionId = param.signedSessionId, mfaToken = param.mfaToken, elevatedAccessToken = param.elevatedAccessToken;
|
|
2475
|
+
var _this = this;
|
|
2476
|
+
return _async_to_generator(function() {
|
|
2477
|
+
var _updatedState_unshielded_balances, _updatedState_unshielded, wallet, txBytes, finalized, txIdentifier, txHash, balanceBeforeBig, updatedState, _updatedState_unshielded_balances_tokenType, balanceAfter;
|
|
2478
|
+
return _ts_generator(this, function(_state) {
|
|
2479
|
+
switch(_state.label){
|
|
2480
|
+
case 0:
|
|
2481
|
+
return [
|
|
2482
|
+
4,
|
|
2483
|
+
_this.initMidnightWallet('submitTransaction', {
|
|
2484
|
+
accountAddress: accountAddress,
|
|
2485
|
+
password: password,
|
|
2486
|
+
signedSessionId: signedSessionId,
|
|
2487
|
+
mfaToken: mfaToken,
|
|
2488
|
+
elevatedAccessToken: elevatedAccessToken
|
|
2489
|
+
})
|
|
2490
|
+
];
|
|
2491
|
+
case 1:
|
|
2492
|
+
wallet = _state.sent().wallet;
|
|
2493
|
+
txBytes = Buffer.from(transaction, 'base64');
|
|
2494
|
+
finalized = ledger__namespace.Transaction.deserialize('signature', 'proof', 'binding', new Uint8Array(txBytes));
|
|
2495
|
+
_this.logger.info('[Midnight] submitTransaction: broadcasting...');
|
|
2496
|
+
return [
|
|
2497
|
+
4,
|
|
2498
|
+
wallet.submitTransaction(finalized)
|
|
2499
|
+
];
|
|
2500
|
+
case 2:
|
|
2501
|
+
txIdentifier = _state.sent();
|
|
2502
|
+
txHash = DynamicMidnightWalletClient.toTxHashString(txIdentifier);
|
|
2503
|
+
_this.logger.info('[Midnight] submitTransaction: broadcast, txHash=', txHash);
|
|
2504
|
+
if (!waitForBalance) {
|
|
2505
|
+
void _this.persistCachedWalletState();
|
|
2506
|
+
return [
|
|
2507
|
+
2,
|
|
2508
|
+
{
|
|
2509
|
+
txHash: txHash
|
|
2510
|
+
}
|
|
2511
|
+
];
|
|
2512
|
+
}
|
|
2513
|
+
if (!tokenType || balanceBefore === undefined) {
|
|
2514
|
+
throw new Error('tokenType and balanceBefore are required when waitForBalance is true');
|
|
2515
|
+
}
|
|
2516
|
+
balanceBeforeBig = BigInt(balanceBefore);
|
|
2517
|
+
_this.logger.info('[Midnight] submitTransaction: waiting for balance to stabilize...');
|
|
2518
|
+
return [
|
|
2519
|
+
4,
|
|
2520
|
+
new Promise(function(resolve, reject) {
|
|
2521
|
+
// Same TDZ-safe pattern used at every other subscribe site in the
|
|
2522
|
+
// file: `let sub` + optional chaining so if `.subscribe()` throws
|
|
2523
|
+
// synchronously (before `sub` is initialized) the hardTimeout and
|
|
2524
|
+
// stable-timer closures don't hit a TDZ ReferenceError.
|
|
2525
|
+
// eslint-disable-next-line prefer-const
|
|
2526
|
+
var sub;
|
|
2527
|
+
var hardTimeout = setTimeout(function() {
|
|
2528
|
+
sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
|
|
2529
|
+
reject(new Error('Balance update timeout'));
|
|
2530
|
+
}, 120000);
|
|
2531
|
+
var lastState = null;
|
|
2532
|
+
var lastValue = null;
|
|
2533
|
+
var stableTimer = null;
|
|
2534
|
+
sub = wallet.state().subscribe(function(s) {
|
|
2535
|
+
var _s_unshielded_balances, _s_unshielded;
|
|
2536
|
+
var _s_unshielded_balances_tokenType;
|
|
2537
|
+
var current = (_s_unshielded_balances_tokenType = (_s_unshielded = s.unshielded) === null || _s_unshielded === void 0 ? void 0 : (_s_unshielded_balances = _s_unshielded.balances) === null || _s_unshielded_balances === void 0 ? void 0 : _s_unshielded_balances[tokenType]) !== null && _s_unshielded_balances_tokenType !== void 0 ? _s_unshielded_balances_tokenType : 0n;
|
|
2538
|
+
if (current === balanceBeforeBig) return;
|
|
2539
|
+
lastState = s;
|
|
2540
|
+
if (lastValue !== current) {
|
|
2541
|
+
lastValue = current;
|
|
2542
|
+
if (stableTimer) clearTimeout(stableTimer);
|
|
2543
|
+
stableTimer = setTimeout(function() {
|
|
2544
|
+
clearTimeout(hardTimeout);
|
|
2545
|
+
sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
|
|
2546
|
+
resolve(lastState);
|
|
2547
|
+
}, 3000);
|
|
2548
|
+
}
|
|
2549
|
+
});
|
|
2550
|
+
})
|
|
2551
|
+
];
|
|
2552
|
+
case 3:
|
|
2553
|
+
updatedState = _state.sent();
|
|
2554
|
+
balanceAfter = (_updatedState_unshielded_balances_tokenType = (_updatedState_unshielded = updatedState.unshielded) === null || _updatedState_unshielded === void 0 ? void 0 : (_updatedState_unshielded_balances = _updatedState_unshielded.balances) === null || _updatedState_unshielded_balances === void 0 ? void 0 : _updatedState_unshielded_balances[tokenType]) !== null && _updatedState_unshielded_balances_tokenType !== void 0 ? _updatedState_unshielded_balances_tokenType : 0n;
|
|
2555
|
+
void _this.persistCachedWalletState();
|
|
2556
|
+
return [
|
|
2557
|
+
2,
|
|
2558
|
+
{
|
|
2559
|
+
txHash: txHash,
|
|
2560
|
+
balanceAfter: balanceAfter.toString()
|
|
2561
|
+
}
|
|
2562
|
+
];
|
|
2563
|
+
}
|
|
2564
|
+
});
|
|
2565
|
+
})();
|
|
2566
|
+
}
|
|
2567
|
+
},
|
|
2568
|
+
{
|
|
2569
|
+
key: "revertTransaction",
|
|
2570
|
+
value: /**
|
|
2571
|
+
* Release UTXOs reserved by drafted or signed-but-never-submitted Midnight
|
|
2572
|
+
* transactions. Three mutually exclusive modes, resolved by priority below.
|
|
2573
|
+
*
|
|
2574
|
+
* Priority order:
|
|
2575
|
+
* 1. `resetCache` — nuclear: stops cached wallet, deletes the account's
|
|
2576
|
+
* persisted state from IndexedDB, forces next operation to cold-sync.
|
|
2577
|
+
* Use this when reference to specific pending txs is lost, or state
|
|
2578
|
+
* got into a stuck configuration that can't be reverted otherwise.
|
|
2579
|
+
* This is Midnight's documented workaround for "pending coins not
|
|
2580
|
+
* cleared after failed shielded transaction".
|
|
2581
|
+
* 2. `transaction` — revert one specific tx by its base64 serialization.
|
|
2582
|
+
* Accepts either UnprovenTransaction (step 1 output) or
|
|
2583
|
+
* FinalizedTransaction (step 2 output).
|
|
2584
|
+
* 3. `all` — iterate `state.pending.all` and revert each. Covers every
|
|
2585
|
+
* finalized tx the SDK knows about. Does NOT cover Step-1-only drafts
|
|
2586
|
+
* (those are never registered with the pending-tx service — by design
|
|
2587
|
+
* per Midnight, because unproven transactions hold secret-key material).
|
|
2588
|
+
*
|
|
2589
|
+
* Exactly one of the three must be truthy; otherwise this throws.
|
|
2590
|
+
*/ function revertTransaction(params) {
|
|
2591
|
+
var _this = this;
|
|
2592
|
+
return _async_to_generator(function() {
|
|
2593
|
+
return _ts_generator(this, function(_state) {
|
|
2594
|
+
if (params.resetCache) {
|
|
2595
|
+
return [
|
|
2596
|
+
2,
|
|
2597
|
+
_this.revertModeResetCache(params.accountAddress)
|
|
2598
|
+
];
|
|
2599
|
+
}
|
|
2600
|
+
if (params.transaction) {
|
|
2601
|
+
return [
|
|
2602
|
+
2,
|
|
2603
|
+
_this.revertModeSingleTransaction(params)
|
|
2604
|
+
];
|
|
2605
|
+
}
|
|
2606
|
+
if (params.all) {
|
|
2607
|
+
return [
|
|
2608
|
+
2,
|
|
2609
|
+
_this.revertModeAllPending(params)
|
|
2610
|
+
];
|
|
2611
|
+
}
|
|
2612
|
+
throw new Error('revertTransaction requires one of: transaction, all, resetCache');
|
|
2613
|
+
});
|
|
2614
|
+
})();
|
|
2615
|
+
}
|
|
2616
|
+
},
|
|
2617
|
+
{
|
|
2618
|
+
key: "revertModeResetCache",
|
|
2619
|
+
value: function revertModeResetCache(accountAddress) {
|
|
2620
|
+
var _this = this;
|
|
2621
|
+
return _async_to_generator(function() {
|
|
2622
|
+
var _this_cachedWallet, _this_cachedWallet_diagnosticSub, err;
|
|
2623
|
+
return _ts_generator(this, function(_state) {
|
|
2624
|
+
switch(_state.label){
|
|
2625
|
+
case 0:
|
|
2626
|
+
_this.logger.info('[Midnight] revertTransaction[resetCache]: stopping cached wallet and clearing persisted state...');
|
|
2627
|
+
_state.label = 1;
|
|
2628
|
+
case 1:
|
|
2629
|
+
_state.trys.push([
|
|
2630
|
+
1,
|
|
2631
|
+
8,
|
|
2632
|
+
,
|
|
2633
|
+
9
|
|
2634
|
+
]);
|
|
2635
|
+
if (!(((_this_cachedWallet = _this.cachedWallet) === null || _this_cachedWallet === void 0 ? void 0 : _this_cachedWallet.accountAddress) === accountAddress)) return [
|
|
2636
|
+
3,
|
|
2637
|
+
6
|
|
2638
|
+
];
|
|
2639
|
+
try {
|
|
2640
|
+
;
|
|
2641
|
+
(_this_cachedWallet_diagnosticSub = _this.cachedWallet.diagnosticSub) === null || _this_cachedWallet_diagnosticSub === void 0 ? void 0 : _this_cachedWallet_diagnosticSub.unsubscribe();
|
|
2642
|
+
} catch (e) {
|
|
2643
|
+
/* ignore */ }
|
|
2644
|
+
_state.label = 2;
|
|
2645
|
+
case 2:
|
|
2646
|
+
_state.trys.push([
|
|
2647
|
+
2,
|
|
2648
|
+
4,
|
|
2649
|
+
,
|
|
2650
|
+
5
|
|
2651
|
+
]);
|
|
2652
|
+
return [
|
|
2653
|
+
4,
|
|
2654
|
+
_this.cachedWallet.wallet.stop()
|
|
2655
|
+
];
|
|
2656
|
+
case 3:
|
|
2657
|
+
_state.sent();
|
|
2658
|
+
return [
|
|
2659
|
+
3,
|
|
2660
|
+
5
|
|
2661
|
+
];
|
|
2662
|
+
case 4:
|
|
2663
|
+
_state.sent();
|
|
2664
|
+
return [
|
|
2665
|
+
3,
|
|
2666
|
+
5
|
|
2667
|
+
];
|
|
2668
|
+
case 5:
|
|
2669
|
+
_this.cachedWallet = null;
|
|
2670
|
+
_state.label = 6;
|
|
2671
|
+
case 6:
|
|
2672
|
+
return [
|
|
2673
|
+
4,
|
|
2674
|
+
deleteWalletState(accountAddress)
|
|
2675
|
+
];
|
|
2676
|
+
case 7:
|
|
2677
|
+
_state.sent();
|
|
2678
|
+
return [
|
|
2679
|
+
3,
|
|
2680
|
+
9
|
|
2681
|
+
];
|
|
2682
|
+
case 8:
|
|
2683
|
+
err = _state.sent();
|
|
2684
|
+
throw new Error("resetCache failed: ".concat(err.message));
|
|
2685
|
+
case 9:
|
|
2686
|
+
_this.logger.info('[Midnight] revertTransaction[resetCache]: cache cleared');
|
|
2687
|
+
return [
|
|
2688
|
+
2,
|
|
2689
|
+
{
|
|
2690
|
+
reverted: true,
|
|
2691
|
+
count: 0,
|
|
2692
|
+
cacheCleared: true,
|
|
2693
|
+
message: 'Wallet cache reset. The cached wallet instance was stopped, the persisted IndexedDB entry for this account was deleted, and the next operation will cold-sync from chain.'
|
|
2694
|
+
}
|
|
2695
|
+
];
|
|
2696
|
+
}
|
|
2697
|
+
});
|
|
2698
|
+
})();
|
|
2699
|
+
}
|
|
2700
|
+
},
|
|
2701
|
+
{
|
|
2702
|
+
key: "revertModeSingleTransaction",
|
|
2703
|
+
value: function revertModeSingleTransaction(params) {
|
|
2704
|
+
var _this = this;
|
|
2705
|
+
return _async_to_generator(function() {
|
|
2706
|
+
var wallet, txBytes, tx;
|
|
2707
|
+
return _ts_generator(this, function(_state) {
|
|
2708
|
+
switch(_state.label){
|
|
2709
|
+
case 0:
|
|
2710
|
+
return [
|
|
2711
|
+
4,
|
|
2712
|
+
_this.initMidnightWallet('revertTransaction', {
|
|
2713
|
+
accountAddress: params.accountAddress,
|
|
2714
|
+
password: params.password,
|
|
2715
|
+
signedSessionId: params.signedSessionId,
|
|
2716
|
+
mfaToken: params.mfaToken,
|
|
2717
|
+
elevatedAccessToken: params.elevatedAccessToken
|
|
2718
|
+
})
|
|
2719
|
+
];
|
|
2720
|
+
case 1:
|
|
2721
|
+
wallet = _state.sent().wallet;
|
|
2722
|
+
txBytes = new Uint8Array(Buffer.from(params.transaction, 'base64'));
|
|
2723
|
+
tx = _this.deserializeAnyMidnightTx(txBytes);
|
|
2724
|
+
_this.logger.info('[Midnight] revertTransaction[single]: reverting specific tx...');
|
|
2725
|
+
return [
|
|
2726
|
+
4,
|
|
2727
|
+
wallet.revert(tx)
|
|
2728
|
+
];
|
|
2729
|
+
case 2:
|
|
2730
|
+
_state.sent();
|
|
2731
|
+
void _this.persistCachedWalletState();
|
|
2732
|
+
return [
|
|
2733
|
+
2,
|
|
2734
|
+
{
|
|
2735
|
+
reverted: true,
|
|
2736
|
+
count: 1,
|
|
2737
|
+
cacheCleared: false,
|
|
2738
|
+
message: 'Transaction reverted. Reserved UTXOs have been returned to the available set.'
|
|
2739
|
+
}
|
|
2740
|
+
];
|
|
2741
|
+
}
|
|
2742
|
+
});
|
|
2743
|
+
})();
|
|
2744
|
+
}
|
|
2745
|
+
},
|
|
2746
|
+
{
|
|
2747
|
+
key: "revertModeAllPending",
|
|
2748
|
+
value: function revertModeAllPending(params) {
|
|
2749
|
+
var _this = this;
|
|
2750
|
+
return _async_to_generator(function() {
|
|
2751
|
+
var _snapshot_pending, wallet, snapshot, _snapshot_pending_all, pending, reverted, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, item, err1, err;
|
|
2752
|
+
return _ts_generator(this, function(_state) {
|
|
2753
|
+
switch(_state.label){
|
|
2754
|
+
case 0:
|
|
2755
|
+
return [
|
|
2756
|
+
4,
|
|
2757
|
+
_this.initMidnightWallet('revertTransaction', {
|
|
2758
|
+
accountAddress: params.accountAddress,
|
|
2759
|
+
password: params.password,
|
|
2760
|
+
signedSessionId: params.signedSessionId,
|
|
2761
|
+
mfaToken: params.mfaToken,
|
|
2762
|
+
elevatedAccessToken: params.elevatedAccessToken
|
|
2763
|
+
})
|
|
2764
|
+
];
|
|
2765
|
+
case 1:
|
|
2766
|
+
wallet = _state.sent().wallet;
|
|
2767
|
+
_this.logger.info('[Midnight] revertTransaction[all]: reading pending-tx list...');
|
|
2768
|
+
return [
|
|
2769
|
+
4,
|
|
2770
|
+
_this.snapshotWalletState(wallet)
|
|
2771
|
+
];
|
|
2772
|
+
case 2:
|
|
2773
|
+
snapshot = _state.sent();
|
|
2774
|
+
pending = (_snapshot_pending_all = snapshot === null || snapshot === void 0 ? void 0 : (_snapshot_pending = snapshot.pending) === null || _snapshot_pending === void 0 ? void 0 : _snapshot_pending.all) !== null && _snapshot_pending_all !== void 0 ? _snapshot_pending_all : [];
|
|
2775
|
+
_this.logger.info("[Midnight] revertTransaction[all]: reverting ".concat(pending.length, " pending tx(s)..."));
|
|
2776
|
+
reverted = 0;
|
|
2777
|
+
_iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2778
|
+
_state.label = 3;
|
|
2779
|
+
case 3:
|
|
2780
|
+
_state.trys.push([
|
|
2781
|
+
3,
|
|
2782
|
+
10,
|
|
2783
|
+
11,
|
|
2784
|
+
12
|
|
2785
|
+
]);
|
|
2786
|
+
_iterator = pending[Symbol.iterator]();
|
|
2787
|
+
_state.label = 4;
|
|
2788
|
+
case 4:
|
|
2789
|
+
if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
|
|
2790
|
+
3,
|
|
2791
|
+
9
|
|
2792
|
+
];
|
|
2793
|
+
item = _step.value;
|
|
2794
|
+
_state.label = 5;
|
|
2795
|
+
case 5:
|
|
2796
|
+
_state.trys.push([
|
|
2797
|
+
5,
|
|
2798
|
+
7,
|
|
2799
|
+
,
|
|
2800
|
+
8
|
|
2801
|
+
]);
|
|
2802
|
+
return [
|
|
2803
|
+
4,
|
|
2804
|
+
wallet.revert(item.tx)
|
|
2805
|
+
];
|
|
2806
|
+
case 6:
|
|
2807
|
+
_state.sent();
|
|
2808
|
+
reverted += 1;
|
|
2809
|
+
return [
|
|
2810
|
+
3,
|
|
2811
|
+
8
|
|
2812
|
+
];
|
|
2813
|
+
case 7:
|
|
2814
|
+
err1 = _state.sent();
|
|
2815
|
+
_this.logger.warn('[Midnight] revertTransaction[all]: revert failed for one tx, continuing:', err1);
|
|
2816
|
+
return [
|
|
2817
|
+
3,
|
|
2818
|
+
8
|
|
2819
|
+
];
|
|
2820
|
+
case 8:
|
|
2821
|
+
_iteratorNormalCompletion = true;
|
|
2822
|
+
return [
|
|
2823
|
+
3,
|
|
2824
|
+
4
|
|
2825
|
+
];
|
|
2826
|
+
case 9:
|
|
2827
|
+
return [
|
|
2828
|
+
3,
|
|
2829
|
+
12
|
|
2830
|
+
];
|
|
2831
|
+
case 10:
|
|
2832
|
+
err = _state.sent();
|
|
2833
|
+
_didIteratorError = true;
|
|
2834
|
+
_iteratorError = err;
|
|
2835
|
+
return [
|
|
2836
|
+
3,
|
|
2837
|
+
12
|
|
2838
|
+
];
|
|
2839
|
+
case 11:
|
|
2840
|
+
try {
|
|
2841
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
2842
|
+
_iterator.return();
|
|
2843
|
+
}
|
|
2844
|
+
} finally{
|
|
2845
|
+
if (_didIteratorError) {
|
|
2846
|
+
throw _iteratorError;
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
return [
|
|
2850
|
+
7
|
|
2851
|
+
];
|
|
2852
|
+
case 12:
|
|
2853
|
+
void _this.persistCachedWalletState();
|
|
2854
|
+
return [
|
|
2855
|
+
2,
|
|
2856
|
+
{
|
|
2857
|
+
reverted: true,
|
|
2858
|
+
count: reverted,
|
|
2859
|
+
cacheCleared: false,
|
|
2860
|
+
message: reverted > 0 ? "Reverted ".concat(reverted, " pending transaction(s). Step-1-only drafts are not covered — use resetCache if UTXOs are still stuck.") : 'No pending transactions to revert. If UTXOs are still stuck, the tx was likely an unfinalized draft — use resetCache to clear local state.'
|
|
2861
|
+
}
|
|
2862
|
+
];
|
|
2863
|
+
}
|
|
2864
|
+
});
|
|
2865
|
+
})();
|
|
2866
|
+
}
|
|
2867
|
+
},
|
|
2868
|
+
{
|
|
2869
|
+
key: "deserializeAnyMidnightTx",
|
|
2870
|
+
value: // Try unproven first (more common abandonment point), fall back to finalized.
|
|
2871
|
+
function deserializeAnyMidnightTx(txBytes) {
|
|
2872
|
+
try {
|
|
2873
|
+
return ledger__namespace.Transaction.deserialize('signature', 'pre-proof', 'pre-binding', txBytes);
|
|
2874
|
+
} catch (e) {
|
|
2875
|
+
try {
|
|
2876
|
+
return ledger__namespace.Transaction.deserialize('signature', 'proof', 'binding', txBytes);
|
|
2877
|
+
} catch (err) {
|
|
2878
|
+
throw new Error("Could not deserialize transaction as either UnprovenTransaction or FinalizedTransaction: ".concat(err.message));
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
},
|
|
2883
|
+
{
|
|
2884
|
+
key: "snapshotWalletState",
|
|
2885
|
+
value: // Take a single snapshot of wallet state via the observable. Same
|
|
2886
|
+
// subscribe-once pattern we use elsewhere — no rxjs dependency needed.
|
|
2887
|
+
function snapshotWalletState(wallet) {
|
|
2888
|
+
return _async_to_generator(function() {
|
|
2889
|
+
return _ts_generator(this, function(_state) {
|
|
2890
|
+
return [
|
|
2891
|
+
2,
|
|
2892
|
+
new Promise(function(resolve, reject) {
|
|
2893
|
+
// eslint-disable-next-line prefer-const
|
|
2894
|
+
var sub;
|
|
2895
|
+
var tid = setTimeout(function() {
|
|
2896
|
+
sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
|
|
2897
|
+
reject(new Error('Timed out waiting for wallet state snapshot'));
|
|
2898
|
+
}, 10000);
|
|
2899
|
+
sub = wallet.state().subscribe(function(s) {
|
|
2900
|
+
clearTimeout(tid);
|
|
2901
|
+
queueMicrotask(function() {
|
|
2902
|
+
return sub === null || sub === void 0 ? void 0 : sub.unsubscribe();
|
|
2903
|
+
});
|
|
2904
|
+
resolve(s);
|
|
2905
|
+
});
|
|
2906
|
+
})
|
|
2907
|
+
];
|
|
2908
|
+
});
|
|
2909
|
+
})();
|
|
2910
|
+
}
|
|
2911
|
+
},
|
|
2912
|
+
{
|
|
2913
|
+
key: "mpcSignIntents",
|
|
2914
|
+
value: /**
|
|
2915
|
+
* Sign all unshielded intents in a transaction via MPC.
|
|
2916
|
+
* Mutates tx.intents in place with signatures attached.
|
|
2917
|
+
* No-op for pure shielded transactions (0 unshielded segments).
|
|
2918
|
+
*
|
|
2919
|
+
* Each per-segment sign call carries the full unsigned tx in context so
|
|
2920
|
+
* wallet-service policy sees the whole transaction once per signature
|
|
2921
|
+
* (same pattern as BTC's PSBT in per-input context).
|
|
2922
|
+
*/ function mpcSignIntents(tx, options) {
|
|
2923
|
+
var _this = this;
|
|
2924
|
+
return _async_to_generator(function() {
|
|
2925
|
+
var _tx_intents, accountAddress, password, signedSessionId, mfaToken, elevatedAccessToken, fullTxBase64, networkId, baseContext, _options_proofMarker, proofMarker, _tx_intents_keys_toArray, segments, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _loop, _iterator, _step, err;
|
|
2926
|
+
return _ts_generator(this, function(_state) {
|
|
2927
|
+
switch(_state.label){
|
|
2928
|
+
case 0:
|
|
2929
|
+
accountAddress = options.accountAddress, password = options.password, signedSessionId = options.signedSessionId, mfaToken = options.mfaToken, elevatedAccessToken = options.elevatedAccessToken, fullTxBase64 = options.fullTxBase64, networkId = options.networkId, baseContext = options.baseContext, _options_proofMarker = options.proofMarker, proofMarker = _options_proofMarker === void 0 ? 'pre-proof' : _options_proofMarker;
|
|
2930
|
+
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 : [];
|
|
2931
|
+
if (segments.length === 0) return [
|
|
2932
|
+
2
|
|
2933
|
+
];
|
|
2934
|
+
_this.logger.info("[Midnight] MPC signing ".concat(segments.length, " segment(s)..."));
|
|
2935
|
+
_iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2936
|
+
_state.label = 1;
|
|
2937
|
+
case 1:
|
|
2938
|
+
_state.trys.push([
|
|
2939
|
+
1,
|
|
2940
|
+
6,
|
|
2941
|
+
7,
|
|
2942
|
+
8
|
|
2943
|
+
]);
|
|
2944
|
+
_loop = function() {
|
|
2945
|
+
var segment, intent, cloned, sigData, hashBuffer, hash, signature, sigBytes, ledgerSig, sigs, sigs1;
|
|
2946
|
+
return _ts_generator(this, function(_state) {
|
|
2947
|
+
switch(_state.label){
|
|
2948
|
+
case 0:
|
|
2949
|
+
segment = _step.value;
|
|
2950
|
+
intent = tx.intents.get(segment);
|
|
2951
|
+
if (!intent) return [
|
|
2952
|
+
2,
|
|
2953
|
+
"continue"
|
|
2954
|
+
];
|
|
2955
|
+
cloned = ledger__namespace.Intent.deserialize('signature', proofMarker, 'pre-binding', intent.serialize());
|
|
2956
|
+
sigData = cloned.signatureData(segment);
|
|
2957
|
+
return [
|
|
2958
|
+
4,
|
|
2959
|
+
crypto.subtle.digest('SHA-256', sigData)
|
|
2960
|
+
];
|
|
2961
|
+
case 1:
|
|
2962
|
+
hashBuffer = _state.sent();
|
|
2963
|
+
hash = Array.from(new Uint8Array(hashBuffer)).map(function(b) {
|
|
2964
|
+
return b.toString(16).padStart(2, '0');
|
|
2965
|
+
}).join('');
|
|
2966
|
+
return [
|
|
2967
|
+
4,
|
|
2968
|
+
_this.sign({
|
|
2969
|
+
message: hash,
|
|
2970
|
+
accountAddress: accountAddress,
|
|
2971
|
+
chainName: _this.chainName,
|
|
2972
|
+
password: password,
|
|
2973
|
+
signedSessionId: signedSessionId,
|
|
2974
|
+
mfaToken: mfaToken,
|
|
2975
|
+
elevatedAccessToken: elevatedAccessToken,
|
|
2976
|
+
context: _object_spread_props(_object_spread({}, baseContext), {
|
|
2977
|
+
midnightTransaction: {
|
|
2978
|
+
serializedTransaction: fullTxBase64,
|
|
2979
|
+
networkId: networkId
|
|
2980
|
+
}
|
|
2981
|
+
})
|
|
2982
|
+
})
|
|
2983
|
+
];
|
|
2984
|
+
case 2:
|
|
2985
|
+
signature = _state.sent();
|
|
2986
|
+
sigBytes = _instanceof(signature, Uint8Array) ? signature : Buffer.from(signature, 'base64');
|
|
2987
|
+
ledgerSig = Buffer.from(sigBytes).toString('hex');
|
|
2988
|
+
if (cloned.fallibleUnshieldedOffer) {
|
|
2989
|
+
sigs = cloned.fallibleUnshieldedOffer.inputs.map(function(_, i) {
|
|
2990
|
+
var _cloned_fallibleUnshieldedOffer_signatures_at;
|
|
2991
|
+
return (_cloned_fallibleUnshieldedOffer_signatures_at = cloned.fallibleUnshieldedOffer.signatures.at(i)) !== null && _cloned_fallibleUnshieldedOffer_signatures_at !== void 0 ? _cloned_fallibleUnshieldedOffer_signatures_at : ledgerSig;
|
|
2992
|
+
});
|
|
2993
|
+
cloned.fallibleUnshieldedOffer = cloned.fallibleUnshieldedOffer.addSignatures(sigs);
|
|
2994
|
+
}
|
|
2995
|
+
if (cloned.guaranteedUnshieldedOffer) {
|
|
2996
|
+
sigs1 = cloned.guaranteedUnshieldedOffer.inputs.map(function(_, i) {
|
|
2997
|
+
var _cloned_guaranteedUnshieldedOffer_signatures_at;
|
|
2998
|
+
return (_cloned_guaranteedUnshieldedOffer_signatures_at = cloned.guaranteedUnshieldedOffer.signatures.at(i)) !== null && _cloned_guaranteedUnshieldedOffer_signatures_at !== void 0 ? _cloned_guaranteedUnshieldedOffer_signatures_at : ledgerSig;
|
|
2999
|
+
});
|
|
3000
|
+
cloned.guaranteedUnshieldedOffer = cloned.guaranteedUnshieldedOffer.addSignatures(sigs1);
|
|
3001
|
+
}
|
|
3002
|
+
tx.intents = tx.intents.set(segment, cloned);
|
|
3003
|
+
return [
|
|
3004
|
+
2
|
|
3005
|
+
];
|
|
3006
|
+
}
|
|
3007
|
+
});
|
|
3008
|
+
};
|
|
3009
|
+
_iterator = segments[Symbol.iterator]();
|
|
3010
|
+
_state.label = 2;
|
|
3011
|
+
case 2:
|
|
3012
|
+
if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
|
|
3013
|
+
3,
|
|
3014
|
+
5
|
|
3015
|
+
];
|
|
3016
|
+
return [
|
|
3017
|
+
5,
|
|
3018
|
+
_ts_values(_loop())
|
|
3019
|
+
];
|
|
3020
|
+
case 3:
|
|
3021
|
+
_state.sent();
|
|
3022
|
+
_state.label = 4;
|
|
3023
|
+
case 4:
|
|
3024
|
+
_iteratorNormalCompletion = true;
|
|
3025
|
+
return [
|
|
3026
|
+
3,
|
|
3027
|
+
2
|
|
3028
|
+
];
|
|
3029
|
+
case 5:
|
|
3030
|
+
return [
|
|
3031
|
+
3,
|
|
3032
|
+
8
|
|
3033
|
+
];
|
|
3034
|
+
case 6:
|
|
3035
|
+
err = _state.sent();
|
|
3036
|
+
_didIteratorError = true;
|
|
3037
|
+
_iteratorError = err;
|
|
3038
|
+
return [
|
|
3039
|
+
3,
|
|
3040
|
+
8
|
|
3041
|
+
];
|
|
3042
|
+
case 7:
|
|
3043
|
+
try {
|
|
3044
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
3045
|
+
_iterator.return();
|
|
3046
|
+
}
|
|
3047
|
+
} finally{
|
|
3048
|
+
if (_didIteratorError) {
|
|
3049
|
+
throw _iteratorError;
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
return [
|
|
3053
|
+
7
|
|
3054
|
+
];
|
|
3055
|
+
case 8:
|
|
3056
|
+
return [
|
|
3057
|
+
2
|
|
3058
|
+
];
|
|
3059
|
+
}
|
|
3060
|
+
});
|
|
3061
|
+
})();
|
|
3062
|
+
}
|
|
3063
|
+
},
|
|
3064
|
+
{
|
|
3065
|
+
key: "getWalletProvider",
|
|
3066
|
+
value: /**
|
|
3067
|
+
* Returns a Midnight.js-compatible WalletProvider for use with
|
|
3068
|
+
* midnight-js-contracts. Signing goes through MPC — no private key exposed.
|
|
3069
|
+
*/ function getWalletProvider(param) {
|
|
3070
|
+
var accountAddress = param.accountAddress, password = param.password, signedSessionId = param.signedSessionId, mfaToken = param.mfaToken, elevatedAccessToken = param.elevatedAccessToken;
|
|
3071
|
+
var _this = this;
|
|
3072
|
+
return _async_to_generator(function() {
|
|
3073
|
+
var _ref, wallet, state, shieldedSecretKeys, dustSecretKey, walletProvider;
|
|
3074
|
+
return _ts_generator(this, function(_state) {
|
|
3075
|
+
switch(_state.label){
|
|
3076
|
+
case 0:
|
|
3077
|
+
return [
|
|
3078
|
+
4,
|
|
3079
|
+
_this.initMidnightWallet('walletProvider', {
|
|
3080
|
+
accountAddress: accountAddress,
|
|
3081
|
+
password: password,
|
|
3082
|
+
signedSessionId: signedSessionId,
|
|
3083
|
+
mfaToken: mfaToken,
|
|
3084
|
+
elevatedAccessToken: elevatedAccessToken
|
|
3085
|
+
})
|
|
3086
|
+
];
|
|
3087
|
+
case 1:
|
|
3088
|
+
_ref = _state.sent(), wallet = _ref.wallet, state = _ref.state, shieldedSecretKeys = _ref.shieldedSecretKeys, dustSecretKey = _ref.dustSecretKey;
|
|
3089
|
+
walletProvider = {
|
|
3090
|
+
getCoinPublicKey: function() {
|
|
3091
|
+
return state.shielded.coinPublicKey.toHexString();
|
|
3092
|
+
},
|
|
3093
|
+
getEncryptionPublicKey: function() {
|
|
3094
|
+
return state.shielded.encryptionPublicKey.toHexString();
|
|
3095
|
+
},
|
|
3096
|
+
balanceTx: /*#__PURE__*/ function() {
|
|
3097
|
+
var _ref = _async_to_generator(function(tx, ttl) {
|
|
3098
|
+
var recipe, baseTxB64, balancingTxB64;
|
|
3099
|
+
return _ts_generator(this, function(_state) {
|
|
3100
|
+
switch(_state.label){
|
|
3101
|
+
case 0:
|
|
3102
|
+
_this.logger.info('[Midnight] walletProvider.balanceTx: balancing transaction...');
|
|
3103
|
+
return [
|
|
3104
|
+
4,
|
|
3105
|
+
wallet.balanceUnboundTransaction(tx, {
|
|
3106
|
+
shieldedSecretKeys: shieldedSecretKeys,
|
|
3107
|
+
dustSecretKey: dustSecretKey
|
|
3108
|
+
}, {
|
|
3109
|
+
ttl: ttl !== null && ttl !== void 0 ? ttl : new Date(Date.now() + 30 * 60 * 1000)
|
|
3110
|
+
})
|
|
3111
|
+
];
|
|
3112
|
+
case 1:
|
|
3113
|
+
recipe = _state.sent();
|
|
3114
|
+
// The proven base tx uses the 'proof' marker. networkId 0 matches
|
|
3115
|
+
// the 'preview' facade network this provider is configured for.
|
|
3116
|
+
baseTxB64 = Buffer.from(recipe.baseTransaction.serialize()).toString('base64');
|
|
3117
|
+
return [
|
|
3118
|
+
4,
|
|
3119
|
+
_this.mpcSignIntents(recipe.baseTransaction, {
|
|
3120
|
+
accountAddress: accountAddress,
|
|
3121
|
+
password: password,
|
|
3122
|
+
signedSessionId: signedSessionId,
|
|
3123
|
+
mfaToken: mfaToken,
|
|
3124
|
+
elevatedAccessToken: elevatedAccessToken,
|
|
3125
|
+
fullTxBase64: baseTxB64,
|
|
3126
|
+
networkId: 0,
|
|
3127
|
+
proofMarker: 'proof'
|
|
3128
|
+
})
|
|
3129
|
+
];
|
|
3130
|
+
case 2:
|
|
3131
|
+
_state.sent();
|
|
3132
|
+
if (!recipe.balancingTransaction) return [
|
|
3133
|
+
3,
|
|
3134
|
+
4
|
|
3135
|
+
];
|
|
3136
|
+
balancingTxB64 = Buffer.from(recipe.balancingTransaction.serialize()).toString('base64');
|
|
3137
|
+
return [
|
|
3138
|
+
4,
|
|
3139
|
+
_this.mpcSignIntents(recipe.balancingTransaction, {
|
|
3140
|
+
accountAddress: accountAddress,
|
|
3141
|
+
password: password,
|
|
3142
|
+
signedSessionId: signedSessionId,
|
|
3143
|
+
mfaToken: mfaToken,
|
|
3144
|
+
elevatedAccessToken: elevatedAccessToken,
|
|
3145
|
+
fullTxBase64: balancingTxB64,
|
|
3146
|
+
networkId: 0,
|
|
3147
|
+
proofMarker: 'pre-proof'
|
|
3148
|
+
})
|
|
3149
|
+
];
|
|
3150
|
+
case 3:
|
|
3151
|
+
_state.sent();
|
|
3152
|
+
_state.label = 4;
|
|
3153
|
+
case 4:
|
|
3154
|
+
_this.logger.info('[Midnight] walletProvider.balanceTx: finalizing...');
|
|
3155
|
+
return [
|
|
3156
|
+
2,
|
|
3157
|
+
wallet.finalizeRecipe(recipe)
|
|
3158
|
+
];
|
|
3159
|
+
}
|
|
3160
|
+
});
|
|
3161
|
+
});
|
|
3162
|
+
return function(tx, ttl) {
|
|
3163
|
+
return _ref.apply(this, arguments);
|
|
3164
|
+
};
|
|
3165
|
+
}(),
|
|
3166
|
+
submitTx: function(tx) {
|
|
3167
|
+
_this.logger.info('[Midnight] walletProvider.submitTx: submitting...');
|
|
3168
|
+
return wallet.submitTransaction(tx);
|
|
3169
|
+
}
|
|
3170
|
+
};
|
|
3171
|
+
return [
|
|
3172
|
+
2,
|
|
3173
|
+
{
|
|
3174
|
+
walletProvider: walletProvider,
|
|
3175
|
+
wallet: wallet,
|
|
3176
|
+
state: state,
|
|
3177
|
+
shieldedSecretKeys: shieldedSecretKeys,
|
|
3178
|
+
dustSecretKey: dustSecretKey
|
|
3179
|
+
}
|
|
3180
|
+
];
|
|
3181
|
+
}
|
|
3182
|
+
});
|
|
3183
|
+
})();
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
], [
|
|
3187
|
+
{
|
|
3188
|
+
key: "toTxHashString",
|
|
3189
|
+
value: /**
|
|
3190
|
+
* Coerce the value returned by `wallet.submitTransaction(...)` to a string.
|
|
3191
|
+
* Midnight's facade returns either a string txId, or a TransactionIdentifier
|
|
3192
|
+
* object with a `.toHex()` method. We normalize so callers always see a
|
|
3193
|
+
* string, and fall back to `String(...)` if neither shape matches.
|
|
3194
|
+
*/ function toTxHashString(txIdentifier) {
|
|
3195
|
+
if (typeof txIdentifier === 'string') return txIdentifier;
|
|
3196
|
+
var maybeHex = txIdentifier === null || txIdentifier === void 0 ? void 0 : txIdentifier.toHex;
|
|
3197
|
+
if (typeof maybeHex === 'function') return txIdentifier.toHex();
|
|
3198
|
+
return String(txIdentifier);
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
]);
|
|
3202
|
+
return DynamicMidnightWalletClient;
|
|
3203
|
+
}(browser.DynamicWalletClient);
|
|
3204
|
+
|
|
3205
|
+
/**
|
|
3206
|
+
* Address prefixes for Bech32m encoding (BIP-0350)
|
|
3207
|
+
*/ var ADDRESS_PREFIXES = {
|
|
3208
|
+
mainnet: 'mn_addr',
|
|
3209
|
+
preview: 'mn_addr_preview',
|
|
3210
|
+
preprod: 'mn_addr_preprod',
|
|
3211
|
+
undeployed: 'mn_addr_undeployed'
|
|
3212
|
+
};
|
|
3213
|
+
/**
|
|
3214
|
+
* Derives a Midnight unshielded address from a 32-byte address hash.
|
|
3215
|
+
* NOTE: Midnight addresses are Poseidon hashes of the public key, not the raw key.
|
|
3216
|
+
* The address hash should be computed server-side using ledger.addressFromKey().
|
|
3217
|
+
* If publicKeyHex is passed, it's assumed to already be the address hash.
|
|
3218
|
+
*
|
|
3219
|
+
* @param publicKeyHex - The 32-byte address hash (from addressFromKey) as hex string
|
|
3220
|
+
* @param network - The network. Defaults to preview
|
|
3221
|
+
* @returns Bech32m encoded address (e.g., mn_addr_preview1...)
|
|
3222
|
+
*/ var deriveMidnightAddress = function(param) {
|
|
3223
|
+
var publicKeyHex = param.publicKeyHex, _param_network = param.network, network = _param_network === void 0 ? 'preview' : _param_network;
|
|
3224
|
+
var bytes = Buffer.from(publicKeyHex, 'hex');
|
|
3225
|
+
if (bytes.length !== 32) {
|
|
3226
|
+
throw new Error("Invalid key length: ".concat(bytes.length, ", expected 32"));
|
|
3227
|
+
}
|
|
3228
|
+
var prefix = ADDRESS_PREFIXES[network];
|
|
3229
|
+
var words = bech32.bech32m.toWords(bytes);
|
|
3230
|
+
return bech32.bech32m.encode(prefix, words);
|
|
3231
|
+
};
|
|
3232
|
+
// `'mn_addr'` is a strict prefix of every other value in ADDRESS_PREFIXES,
|
|
3233
|
+
// so naive `startsWith` checks hit `mainnet` for any Midnight address. Always
|
|
3234
|
+
// check longest prefix first (descending).
|
|
3235
|
+
Object.entries(ADDRESS_PREFIXES).slice().sort(function(a, b) {
|
|
3236
|
+
return b[1].length - a[1].length;
|
|
3237
|
+
});
|
|
3238
|
+
|
|
3239
|
+
exports.sdkApiCore = sdkApiCore__namespace;
|
|
3240
|
+
exports.DynamicMidnightWalletClient = DynamicMidnightWalletClient;
|
|
3241
|
+
exports.MIDNIGHT_NETWORK_IDS = MIDNIGHT_NETWORK_IDS;
|
|
3242
|
+
exports.deriveMidnightAddress = deriveMidnightAddress;
|