@lumenwipe/sdk 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +382 -0
- package/LICENSE +201 -0
- package/README.md +99 -12
- package/dist/index.cjs +485 -58
- package/dist/index.d.cts +611 -22
- package/dist/index.d.ts +611 -22
- package/dist/index.js +470 -57
- package/package.json +34 -16
package/dist/index.cjs
CHANGED
|
@@ -37,13 +37,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
37
37
|
// src/index.ts
|
|
38
38
|
var index_exports = {};
|
|
39
39
|
__export(index_exports, {
|
|
40
|
+
CloseDidNotConvergeError: () => CloseDidNotConvergeError,
|
|
41
|
+
CloseOutcomeUnknownError: () => CloseOutcomeUnknownError,
|
|
42
|
+
CloseStateChangedError: () => CloseStateChangedError,
|
|
43
|
+
CloseTransactionFailedError: () => CloseTransactionFailedError,
|
|
40
44
|
InsufficientSignatureWeightError: () => InsufficientSignatureWeightError,
|
|
41
45
|
LumenWipeAbortError: () => LumenWipeAbortError,
|
|
42
46
|
LumenWipeApiError: () => LumenWipeApiError,
|
|
43
47
|
LumenWipeClient: () => LumenWipeClient,
|
|
48
|
+
LumenWipeIntegratorClient: () => LumenWipeIntegratorClient,
|
|
49
|
+
LumenWipeInvalidNetworkError: () => LumenWipeInvalidNetworkError,
|
|
44
50
|
LumenWipeTimeoutError: () => LumenWipeTimeoutError,
|
|
51
|
+
STATE_CHANGED_REFETCHES: () => STATE_CHANGED_REFETCHES,
|
|
52
|
+
STATE_CHANGED_REFETCH_DELAY_MS: () => STATE_CHANGED_REFETCH_DELAY_MS,
|
|
53
|
+
SUBMISSION_GIVE_UP_MARGIN_SECONDS: () => SUBMISSION_GIVE_UP_MARGIN_SECONDS,
|
|
54
|
+
SUBMIT_RESEND_ATTEMPTS: () => SUBMIT_RESEND_ATTEMPTS,
|
|
55
|
+
SUBMIT_RESEND_DELAY_MS: () => SUBMIT_RESEND_DELAY_MS,
|
|
45
56
|
isApiErrorCode: () => isApiErrorCode,
|
|
46
|
-
|
|
57
|
+
isSubmitRefused: () => isSubmitRefused,
|
|
58
|
+
resolveSubmission: () => resolveSubmission,
|
|
59
|
+
runClose: () => runClose,
|
|
60
|
+
submissionClosesAccount: () => submissionClosesAccount
|
|
47
61
|
});
|
|
48
62
|
module.exports = __toCommonJS(index_exports);
|
|
49
63
|
|
|
@@ -89,6 +103,12 @@ var LumenWipeTimeoutError = class extends Error {
|
|
|
89
103
|
this.name = "LumenWipeTimeoutError";
|
|
90
104
|
}
|
|
91
105
|
};
|
|
106
|
+
var LumenWipeInvalidNetworkError = class extends Error {
|
|
107
|
+
constructor() {
|
|
108
|
+
super('LumenWipe network must be "mainnet" or "testnet"');
|
|
109
|
+
this.name = "LumenWipeInvalidNetworkError";
|
|
110
|
+
}
|
|
111
|
+
};
|
|
92
112
|
var LumenWipeAbortError = class extends Error {
|
|
93
113
|
constructor(reason) {
|
|
94
114
|
super("LumenWipe API request was aborted");
|
|
@@ -98,7 +118,7 @@ var LumenWipeAbortError = class extends Error {
|
|
|
98
118
|
};
|
|
99
119
|
|
|
100
120
|
// src/version.ts
|
|
101
|
-
var SDK_VERSION = "0.
|
|
121
|
+
var SDK_VERSION = "0.9.0";
|
|
102
122
|
|
|
103
123
|
// src/http.ts
|
|
104
124
|
var MAX_DELAY_MS = 3e4;
|
|
@@ -113,11 +133,11 @@ var HttpTransport = class {
|
|
|
113
133
|
this.retry = retry;
|
|
114
134
|
}
|
|
115
135
|
async request(method, path, body, opts) {
|
|
116
|
-
var _a, _b;
|
|
136
|
+
var _a, _b, _c;
|
|
117
137
|
const attempts = opts.retry === "never" ? 1 : Math.max(1, Math.floor((_b = (_a = this.retry) == null ? void 0 : _a.attempts) != null ? _b : 1));
|
|
118
138
|
for (let attempt = 1; ; attempt++) {
|
|
119
139
|
try {
|
|
120
|
-
return await this.once(method, path, body, opts.signal);
|
|
140
|
+
return await this.once(method, path, body, (_c = opts.bearer) != null ? _c : this.apiKey, opts.signal);
|
|
121
141
|
} catch (e) {
|
|
122
142
|
if (attempt >= attempts || !this.shouldRetry(opts.retry, e)) throw e;
|
|
123
143
|
await this.sleep(this.delayMs(e, attempt), opts.signal);
|
|
@@ -153,12 +173,11 @@ var HttpTransport = class {
|
|
|
153
173
|
signal == null ? void 0 : signal.addEventListener("abort", onAbort, { once: true });
|
|
154
174
|
});
|
|
155
175
|
}
|
|
156
|
-
async once(method, path, body, callerSignal) {
|
|
176
|
+
async once(method, path, body, bearer, callerSignal) {
|
|
157
177
|
if (callerSignal == null ? void 0 : callerSignal.aborted) throw new LumenWipeAbortError(callerSignal.reason);
|
|
158
|
-
const headers = {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
};
|
|
178
|
+
const headers = {};
|
|
179
|
+
if (bearer !== void 0) headers.Authorization = `Bearer ${bearer}`;
|
|
180
|
+
headers["X-LumenWipe-SDK"] = `sdk/${SDK_VERSION}`;
|
|
162
181
|
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
163
182
|
const controller = new AbortController();
|
|
164
183
|
let timedOut = false;
|
|
@@ -202,7 +221,16 @@ var HttpTransport = class {
|
|
|
202
221
|
}
|
|
203
222
|
};
|
|
204
223
|
|
|
224
|
+
// src/url.ts
|
|
225
|
+
var SLASH = 47;
|
|
226
|
+
function withoutTrailingSlashes(url) {
|
|
227
|
+
let end = url.length;
|
|
228
|
+
while (end > 0 && url.charCodeAt(end - 1) === SLASH) end--;
|
|
229
|
+
return url.slice(0, end);
|
|
230
|
+
}
|
|
231
|
+
|
|
205
232
|
// src/client.ts
|
|
233
|
+
var NETWORKS = ["mainnet", "testnet"];
|
|
206
234
|
function isPlaintextRemote(baseUrl) {
|
|
207
235
|
try {
|
|
208
236
|
const url = new URL(baseUrl);
|
|
@@ -222,7 +250,7 @@ var LumenWipeClient = class {
|
|
|
222
250
|
throw new Error("No fetch implementation available; pass one via options.fetch.");
|
|
223
251
|
}
|
|
224
252
|
this.http = new HttpTransport(
|
|
225
|
-
options.baseUrl
|
|
253
|
+
withoutTrailingSlashes(options.baseUrl),
|
|
226
254
|
options.apiKey,
|
|
227
255
|
(_b = options.timeout) != null ? _b : 3e4,
|
|
228
256
|
resolved,
|
|
@@ -237,44 +265,78 @@ var LumenWipeClient = class {
|
|
|
237
265
|
}
|
|
238
266
|
}
|
|
239
267
|
resolveNetwork(network) {
|
|
240
|
-
|
|
241
|
-
if (
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
268
|
+
const resolved = network != null ? network : this.defaultNetwork;
|
|
269
|
+
if (resolved === void 0) {
|
|
270
|
+
if (!this.warnedDefaultNetwork) {
|
|
271
|
+
this.warnedDefaultNetwork = true;
|
|
272
|
+
this.log(
|
|
273
|
+
'LumenWipe: no network given, defaulting to "testnet". Pass `network` to the client or the call to silence this.'
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
return "testnet";
|
|
247
277
|
}
|
|
248
|
-
|
|
278
|
+
if (!NETWORKS.includes(resolved)) throw new LumenWipeInvalidNetworkError();
|
|
279
|
+
return resolved;
|
|
280
|
+
}
|
|
281
|
+
/** The `/v1/{network}` path for `rest`. The network is checked before any request is built. */
|
|
282
|
+
v1(network, rest) {
|
|
283
|
+
return `/v1/${encodeURIComponent(this.resolveNetwork(network))}${rest}`;
|
|
249
284
|
}
|
|
250
285
|
health(options) {
|
|
251
286
|
return this.http.request("GET", "/health", void 0, __spreadProps(__spreadValues({}, options), {
|
|
252
287
|
retry: "transient"
|
|
253
288
|
}));
|
|
254
289
|
}
|
|
255
|
-
|
|
290
|
+
/** Whether Stellar RPC is reachable on every network, and the days left on the exchange registry.
|
|
291
|
+
* A degraded service answers 503, thrown as a `LumenWipeApiError` whose `body` is a
|
|
292
|
+
* `HealthDeepResponse` with `status: "error"`. Never retried: the 503 is the answer. */
|
|
293
|
+
healthDeep(options) {
|
|
294
|
+
return this.http.request("GET", "/health/deep", void 0, __spreadProps(__spreadValues({}, options), {
|
|
295
|
+
retry: "never"
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
/** The exchange registry the API closes against. Do not rely on it past `validUntil`. */
|
|
299
|
+
getExchangeRegistry(options) {
|
|
300
|
+
return this.http.request(
|
|
301
|
+
"GET",
|
|
302
|
+
"/v1/config/exchange-registry",
|
|
303
|
+
void 0,
|
|
304
|
+
__spreadProps(__spreadValues({}, options), { retry: "transient" })
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
async getAccount(address, network, options) {
|
|
256
308
|
return this.http.request(
|
|
257
309
|
"GET",
|
|
258
|
-
|
|
310
|
+
this.v1(network, `/account/${encodeURIComponent(address)}`),
|
|
259
311
|
void 0,
|
|
260
312
|
__spreadProps(__spreadValues({}, options), { retry: "transient" })
|
|
261
313
|
);
|
|
262
314
|
}
|
|
263
|
-
getPaths(params, network, options) {
|
|
315
|
+
async getPaths(params, network, options) {
|
|
264
316
|
const query = new URLSearchParams({ fromAsset: params.fromAsset, amount: params.amount });
|
|
265
317
|
return this.http.request(
|
|
266
318
|
"GET",
|
|
267
|
-
|
|
319
|
+
this.v1(network, `/paths?${query.toString()}`),
|
|
268
320
|
void 0,
|
|
269
321
|
__spreadProps(__spreadValues({}, options), { retry: "transient" })
|
|
270
322
|
);
|
|
271
323
|
}
|
|
272
|
-
closePlan(body, network, options) {
|
|
324
|
+
async closePlan(body, network, options) {
|
|
325
|
+
return this.http.request("POST", this.v1(network, "/close/plan"), body, __spreadProps(__spreadValues({}, options), {
|
|
326
|
+
retry: "throttle"
|
|
327
|
+
}));
|
|
328
|
+
}
|
|
329
|
+
/** Plans up to 20 accounts into one shared destination in one call. Read-only: no decisions are
|
|
330
|
+
* applied, so plan each account with `closePlan` before closing it. An address that cannot be
|
|
331
|
+
* read or planned comes back as a `blocked` plan, never as a missing entry. */
|
|
332
|
+
async batchPlan(body, network, options) {
|
|
273
333
|
return this.http.request(
|
|
274
334
|
"POST",
|
|
275
|
-
|
|
335
|
+
this.v1(network, "/close/batch-plan"),
|
|
276
336
|
body,
|
|
277
|
-
__spreadProps(__spreadValues({}, options), {
|
|
337
|
+
__spreadProps(__spreadValues({}, options), {
|
|
338
|
+
retry: "throttle"
|
|
339
|
+
})
|
|
278
340
|
);
|
|
279
341
|
}
|
|
280
342
|
/**
|
|
@@ -284,34 +346,68 @@ var LumenWipeClient = class {
|
|
|
284
346
|
* submit the returned transactions in `order`, wait for confirmation, then call this
|
|
285
347
|
* again until `requiresAnotherCall` is false.
|
|
286
348
|
*/
|
|
287
|
-
closeTransactions(body, network, options) {
|
|
349
|
+
async closeTransactions(body, network, options) {
|
|
288
350
|
return this.http.request(
|
|
289
351
|
"POST",
|
|
290
|
-
|
|
352
|
+
this.v1(network, "/close/transactions"),
|
|
291
353
|
body,
|
|
292
354
|
__spreadProps(__spreadValues({}, options), { retry: "throttle" })
|
|
293
355
|
);
|
|
294
356
|
}
|
|
295
|
-
submit(signedXdr, network, options) {
|
|
357
|
+
async submit(signedXdr, network, options) {
|
|
296
358
|
return this.http.request(
|
|
297
359
|
"POST",
|
|
298
|
-
|
|
360
|
+
this.v1(network, "/submit"),
|
|
299
361
|
{ signedXdr },
|
|
300
362
|
__spreadProps(__spreadValues({}, options), { retry: "never" })
|
|
301
363
|
);
|
|
302
364
|
}
|
|
303
|
-
|
|
365
|
+
/** Looks a transaction up by hash and sends nothing. `not_found` is not "did not land": the
|
|
366
|
+
* transaction may be queued, or older than the window the response names. */
|
|
367
|
+
async getTransaction(hash, network, options) {
|
|
368
|
+
return this.http.request(
|
|
369
|
+
"GET",
|
|
370
|
+
this.v1(network, `/transactions/${encodeURIComponent(hash)}`),
|
|
371
|
+
void 0,
|
|
372
|
+
__spreadProps(__spreadValues({}, options), { retry: "transient" })
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
/** A close's phase and remaining work, from live state. Informational: a close is finished only
|
|
376
|
+
* when the hash of the merge you signed is confirmed by `getTransaction`. */
|
|
377
|
+
async closeStatus(source, network, options) {
|
|
378
|
+
const query = new URLSearchParams({ source });
|
|
304
379
|
return this.http.request(
|
|
305
380
|
"GET",
|
|
306
|
-
|
|
381
|
+
this.v1(network, `/close/status?${query.toString()}`),
|
|
307
382
|
void 0,
|
|
308
383
|
__spreadProps(__spreadValues({}, options), { retry: "transient" })
|
|
309
384
|
);
|
|
310
385
|
}
|
|
311
|
-
|
|
386
|
+
/** The XLM confirmed close transactions delivered to `destination`, read from each one's ledger
|
|
387
|
+
* result: a merge into it, or the relay account's forward of a merged balance. A hash that
|
|
388
|
+
* cannot be read as delivered is an `unavailable` entry, not an error, and `totalStroops` is
|
|
389
|
+
* set only when every entry is `delivered`. Pass 1 to 20 distinct hashes. */
|
|
390
|
+
async closeDelivered(destination, hashes, network, options) {
|
|
391
|
+
const query = new URLSearchParams({ destination, hashes: hashes.join(",") });
|
|
392
|
+
return this.http.request(
|
|
393
|
+
"GET",
|
|
394
|
+
this.v1(network, `/close/delivered?${query.toString()}`),
|
|
395
|
+
void 0,
|
|
396
|
+
__spreadProps(__spreadValues({}, options), { retry: "transient" })
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
async mediatorCheck(address, network, options) {
|
|
400
|
+
return this.http.request(
|
|
401
|
+
"GET",
|
|
402
|
+
this.v1(network, `/mediator/check/${encodeURIComponent(address)}`),
|
|
403
|
+
void 0,
|
|
404
|
+
__spreadProps(__spreadValues({}, options), { retry: "transient" })
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
async mediatorSign(transaction, network, options) {
|
|
312
408
|
return this.http.request(
|
|
313
409
|
"POST",
|
|
314
|
-
|
|
410
|
+
this.v1(network, "/mediator/sign"),
|
|
315
411
|
{
|
|
316
412
|
transaction
|
|
317
413
|
},
|
|
@@ -321,10 +417,10 @@ var LumenWipeClient = class {
|
|
|
321
417
|
/** Wraps a wind-down transaction (its own fee already zero) in a signed CAP-15 fee-bump
|
|
322
418
|
* envelope for an account that cannot pay its own fee (architecture.md §8.1). The caller
|
|
323
419
|
* submits the returned XDR through `submit()`, exactly like any other close transaction. */
|
|
324
|
-
feeBumpSponsor(transaction, network, options) {
|
|
420
|
+
async feeBumpSponsor(transaction, network, options) {
|
|
325
421
|
return this.http.request(
|
|
326
422
|
"POST",
|
|
327
|
-
|
|
423
|
+
this.v1(network, "/fee-bump/sponsor"),
|
|
328
424
|
{
|
|
329
425
|
transaction
|
|
330
426
|
},
|
|
@@ -333,24 +429,125 @@ var LumenWipeClient = class {
|
|
|
333
429
|
}
|
|
334
430
|
/** Every live SEP-41 allowance the account has granted (architecture.md §12). Independent of
|
|
335
431
|
* closing an account - a standalone security utility. */
|
|
336
|
-
getAllowances(address, network, options) {
|
|
432
|
+
async getAllowances(address, network, options) {
|
|
337
433
|
return this.http.request(
|
|
338
434
|
"GET",
|
|
339
|
-
|
|
435
|
+
this.v1(network, `/allowances/${encodeURIComponent(address)}`),
|
|
340
436
|
void 0,
|
|
341
437
|
__spreadProps(__spreadValues({}, options), { retry: "transient" })
|
|
342
438
|
);
|
|
343
439
|
}
|
|
344
440
|
/** Builds the unsigned `approve(owner, spender, 0, 0)` transaction that revokes one allowance.
|
|
345
441
|
* The caller signs and submits it through `submit()`, like any other transaction here. */
|
|
346
|
-
revokeAllowance(params, network, options) {
|
|
442
|
+
async revokeAllowance(params, network, options) {
|
|
347
443
|
return this.http.request(
|
|
348
444
|
"POST",
|
|
349
|
-
|
|
445
|
+
this.v1(network, "/allowances/revoke"),
|
|
350
446
|
params,
|
|
351
447
|
__spreadProps(__spreadValues({}, options), { retry: "throttle" })
|
|
352
448
|
);
|
|
353
449
|
}
|
|
450
|
+
/** Accounts closed and XLM recovered through LumenWipe on the network. */
|
|
451
|
+
async getStats(network, options) {
|
|
452
|
+
return this.http.request("GET", this.v1(network, "/stats"), void 0, __spreadProps(__spreadValues({}, options), {
|
|
453
|
+
retry: "transient"
|
|
454
|
+
}));
|
|
455
|
+
}
|
|
456
|
+
/** The totals, the most recent closes and the daily activity on the network. */
|
|
457
|
+
async getStatsFeed(network, options) {
|
|
458
|
+
return this.http.request("GET", this.v1(network, "/stats/feed"), void 0, __spreadProps(__spreadValues({}, options), {
|
|
459
|
+
retry: "transient"
|
|
460
|
+
}));
|
|
461
|
+
}
|
|
462
|
+
/** Counts a confirmed close that was submitted outside this API. The API re-reads the
|
|
463
|
+
* transaction and counts it only if it built it, it confirmed and it merged an account;
|
|
464
|
+
* `counted` is false for a hash already counted. Closes sent through `submit()` are counted
|
|
465
|
+
* without this. */
|
|
466
|
+
async recordMerge(txHash, network, options) {
|
|
467
|
+
return this.http.request(
|
|
468
|
+
"POST",
|
|
469
|
+
this.v1(network, "/stats/merges"),
|
|
470
|
+
{ txHash },
|
|
471
|
+
__spreadProps(__spreadValues({}, options), { retry: "throttle" })
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
// src/integrator-client.ts
|
|
477
|
+
var LumenWipeIntegratorClient = class {
|
|
478
|
+
constructor(options) {
|
|
479
|
+
var _a, _b, _c;
|
|
480
|
+
const resolved = (_a = options.fetch) != null ? _a : globalThis.fetch;
|
|
481
|
+
if (!resolved) {
|
|
482
|
+
throw new Error("No fetch implementation available; pass one via options.fetch.");
|
|
483
|
+
}
|
|
484
|
+
this.http = new HttpTransport(
|
|
485
|
+
withoutTrailingSlashes(options.baseUrl),
|
|
486
|
+
void 0,
|
|
487
|
+
(_b = options.timeout) != null ? _b : 3e4,
|
|
488
|
+
resolved,
|
|
489
|
+
options.retry
|
|
490
|
+
);
|
|
491
|
+
if (isPlaintextRemote(options.baseUrl)) {
|
|
492
|
+
((_c = options.logger) != null ? _c : ((message) => console.warn(message)))(
|
|
493
|
+
"LumenWipe: baseUrl uses http:// for a non-local host. Session tokens and new keys travel in clear text; use https://."
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/** A SEP-53 message for `address` (G...) to sign, valid until `expiresAt`. */
|
|
498
|
+
challenge(address, options) {
|
|
499
|
+
return this.http.request(
|
|
500
|
+
"POST",
|
|
501
|
+
"/v1/integrator/auth/challenge",
|
|
502
|
+
{ address },
|
|
503
|
+
__spreadProps(__spreadValues({}, options), { retry: "throttle" })
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
/** Exchanges the signed challenge for a session token. */
|
|
507
|
+
session(request, options) {
|
|
508
|
+
return this.http.request(
|
|
509
|
+
"POST",
|
|
510
|
+
"/v1/integrator/auth/session",
|
|
511
|
+
{ address: request.address, message: request.message, signature: request.signature },
|
|
512
|
+
__spreadProps(__spreadValues({}, options), { retry: "throttle" })
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
/** The wallet's keys, each with its own request counts, and the wallet's total counts. */
|
|
516
|
+
listKeys(token, options) {
|
|
517
|
+
return this.http.request("GET", "/v1/integrator/keys", void 0, __spreadProps(__spreadValues({}, options), {
|
|
518
|
+
retry: "transient",
|
|
519
|
+
bearer: token
|
|
520
|
+
}));
|
|
521
|
+
}
|
|
522
|
+
/** Creates a key, optionally named and limited to some networks. The raw `key` is in this
|
|
523
|
+
* response only and can never be read again. */
|
|
524
|
+
createKey(token, request = {}, options) {
|
|
525
|
+
return this.http.request(
|
|
526
|
+
"POST",
|
|
527
|
+
"/v1/integrator/keys",
|
|
528
|
+
{ label: request.label, networks: request.networks },
|
|
529
|
+
__spreadProps(__spreadValues({}, options), { retry: "never", bearer: token })
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
/** Revokes one of the wallet's keys by its `id`. */
|
|
533
|
+
revokeKey(token, id, options) {
|
|
534
|
+
return this.http.request(
|
|
535
|
+
"POST",
|
|
536
|
+
`/v1/integrator/keys/${encodeURIComponent(id)}/revoke`,
|
|
537
|
+
void 0,
|
|
538
|
+
__spreadProps(__spreadValues({}, options), { retry: "never", bearer: token })
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
/** Revokes a key and issues its replacement, keeping its label and networks. The new raw `key`
|
|
542
|
+
* is in this response only. */
|
|
543
|
+
rotateKey(token, id, options) {
|
|
544
|
+
return this.http.request(
|
|
545
|
+
"POST",
|
|
546
|
+
`/v1/integrator/keys/${encodeURIComponent(id)}/rotate`,
|
|
547
|
+
void 0,
|
|
548
|
+
__spreadProps(__spreadValues({}, options), { retry: "never", bearer: token })
|
|
549
|
+
);
|
|
550
|
+
}
|
|
354
551
|
};
|
|
355
552
|
|
|
356
553
|
// src/close-engine.ts
|
|
@@ -363,33 +560,200 @@ var InsufficientSignatureWeightError = class extends Error {
|
|
|
363
560
|
this.name = "InsufficientSignatureWeightError";
|
|
364
561
|
}
|
|
365
562
|
};
|
|
563
|
+
var STATE_CHANGED_REFETCHES = 2;
|
|
564
|
+
var STATE_CHANGED_REFETCH_DELAY_MS = 3e3;
|
|
565
|
+
var defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
566
|
+
var CloseStateChangedError = class extends Error {
|
|
567
|
+
constructor(apiError) {
|
|
568
|
+
super(
|
|
569
|
+
"Your account changed while the close was being prepared, so nothing was submitted. Plan the close again and review it before continuing."
|
|
570
|
+
);
|
|
571
|
+
this.apiError = apiError;
|
|
572
|
+
this.name = "CloseStateChangedError";
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
function hasCode(error, code) {
|
|
576
|
+
return typeof error === "object" && error !== null && error.code === code;
|
|
577
|
+
}
|
|
578
|
+
var SUBMIT_RESEND_ATTEMPTS = 2;
|
|
579
|
+
var SUBMIT_RESEND_DELAY_MS = 5e3;
|
|
580
|
+
var SUBMISSION_GIVE_UP_MARGIN_SECONDS = 120;
|
|
581
|
+
var CloseOutcomeUnknownError = class extends Error {
|
|
582
|
+
constructor(submission) {
|
|
583
|
+
super(
|
|
584
|
+
"This transaction is not confirmed yet. Your account may already be closed, so check again before doing anything else."
|
|
585
|
+
);
|
|
586
|
+
this.submission = submission;
|
|
587
|
+
this.name = "CloseOutcomeUnknownError";
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
var CloseTransactionFailedError = class extends Error {
|
|
591
|
+
constructor(submission, message) {
|
|
592
|
+
super(message);
|
|
593
|
+
this.submission = submission;
|
|
594
|
+
this.name = "CloseTransactionFailedError";
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
function isSubmitRefused(error) {
|
|
598
|
+
const status = error == null ? void 0 : error.status;
|
|
599
|
+
return typeof status === "number" && status >= 400 && status < 500;
|
|
600
|
+
}
|
|
601
|
+
async function resolveSubmission(submission, lookup, now = Date.now) {
|
|
602
|
+
let found;
|
|
603
|
+
try {
|
|
604
|
+
found = await lookup(submission.hash);
|
|
605
|
+
} catch (e) {
|
|
606
|
+
return { outcome: "unknown" };
|
|
607
|
+
}
|
|
608
|
+
if (found.hash !== submission.hash) return { outcome: "unknown" };
|
|
609
|
+
if (found.status === "confirmed") return { outcome: "confirmed" };
|
|
610
|
+
if (found.status === "failed") return { outcome: "failed", message: found.message };
|
|
611
|
+
if (submission.validUntil === null) return { outcome: "unknown" };
|
|
612
|
+
const elapsed = Math.min(now() / 1e3, found.searched.latestLedgerCloseTime);
|
|
613
|
+
return elapsed > submission.validUntil + SUBMISSION_GIVE_UP_MARGIN_SECONDS ? { outcome: "expired" } : { outcome: "unknown" };
|
|
614
|
+
}
|
|
615
|
+
var submissionClosesAccount = (submission) => submission.covers.some((step) => step === "MERGE" || step === "CLOSE_ACCOUNT");
|
|
616
|
+
var ROUND_MARGIN = 5;
|
|
617
|
+
var HARD_MAX_ROUNDS = 200;
|
|
618
|
+
var CloseDidNotConvergeError = class extends Error {
|
|
619
|
+
constructor(rounds, bound, reason) {
|
|
620
|
+
super(
|
|
621
|
+
reason === "empty_batch" ? "The close stopped because the service reported more steps but had nothing to sign. Check your account, then try again - the close picks up where it stopped." : `The close needed more steps than expected (${rounds} of at most ${bound}). Check your account, then try again - the close picks up where it stopped.`
|
|
622
|
+
);
|
|
623
|
+
this.rounds = rounds;
|
|
624
|
+
this.bound = bound;
|
|
625
|
+
this.reason = reason;
|
|
626
|
+
this.name = "CloseDidNotConvergeError";
|
|
627
|
+
}
|
|
628
|
+
};
|
|
366
629
|
async function runClose(deps, resume) {
|
|
367
|
-
var _a, _b;
|
|
368
|
-
|
|
630
|
+
var _a, _b, _c, _d, _e, _f;
|
|
631
|
+
let bound = 0;
|
|
632
|
+
let planHash = resume ? void 0 : deps.planHash;
|
|
633
|
+
const run = __spreadProps(__spreadValues({}, deps), {
|
|
634
|
+
onConfirmed: (tx, hash) => {
|
|
635
|
+
var _a2;
|
|
636
|
+
planHash = void 0;
|
|
637
|
+
(_a2 = deps.onConfirmed) == null ? void 0 : _a2.call(deps, tx, hash);
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
if ((_a = deps.reconcile) == null ? void 0 : _a.unresolved) {
|
|
641
|
+
const settled = await settleLeftOver(deps.reconcile, deps.reconcile.unresolved);
|
|
642
|
+
if (settled === "closed") return { mergeConfirmed: true };
|
|
643
|
+
if (settled === "confirmed") planHash = void 0;
|
|
644
|
+
}
|
|
645
|
+
let firstRound = 0;
|
|
369
646
|
if (resume) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
647
|
+
const at = resumedPosition(resume);
|
|
648
|
+
firstRound = at.round + 1;
|
|
649
|
+
await signOrThrow(run, resume.tx, resume.xdr, resume.queue, at);
|
|
650
|
+
await processTxs(run, resume.queue, __spreadProps(__spreadValues({}, at), { txIndex: at.txIndex + 1 }));
|
|
651
|
+
if (!resume.requiresAnotherCall) return resultOf(run);
|
|
373
652
|
}
|
|
374
|
-
for (let round = 0; round < maxRounds; round++) {
|
|
375
|
-
(
|
|
376
|
-
|
|
653
|
+
for (let round = 0; round < ((_b = run.maxRounds) != null ? _b : Math.max(bound, 1)); round++) {
|
|
654
|
+
(_c = run.onProgress) == null ? void 0 : _c.call(run, "Preparing transactions\u2026");
|
|
655
|
+
report(run, () => ({ phase: "preparing", round: firstRound + round }));
|
|
656
|
+
const batch = await fetchRound(run, planHash).catch(async (error) => {
|
|
657
|
+
if (await mergeExplainsMissingAccount(run, error)) return null;
|
|
658
|
+
throw error;
|
|
659
|
+
});
|
|
660
|
+
if (!batch) return resultOf(run);
|
|
377
661
|
const txs = [...batch.transactions].sort((a, b) => a.order - b.order);
|
|
378
|
-
|
|
379
|
-
if (
|
|
662
|
+
const { requiresAnotherCall, steps } = batch.remaining;
|
|
663
|
+
if (requiresAnotherCall && txs.length === 0) {
|
|
664
|
+
throw new CloseDidNotConvergeError(round + 1, (_d = run.maxRounds) != null ? _d : bound, "empty_batch");
|
|
665
|
+
}
|
|
666
|
+
await processTxs(run, txs, {
|
|
667
|
+
round: firstRound + round,
|
|
668
|
+
txIndex: 0,
|
|
669
|
+
txCount: txs.length,
|
|
670
|
+
moreRoundsExpected: requiresAnotherCall
|
|
671
|
+
});
|
|
672
|
+
if (!requiresAnotherCall) return resultOf(run);
|
|
673
|
+
bound = Math.max(bound, Math.min(HARD_MAX_ROUNDS, round + 1 + steps + ROUND_MARGIN));
|
|
380
674
|
}
|
|
381
|
-
throw new
|
|
675
|
+
throw new CloseDidNotConvergeError((_e = run.maxRounds) != null ? _e : bound, (_f = run.maxRounds) != null ? _f : bound, "round_limit");
|
|
676
|
+
}
|
|
677
|
+
function resultOf(run) {
|
|
678
|
+
return { mergeConfirmed: run.confirmedMerge !== void 0 };
|
|
382
679
|
}
|
|
383
|
-
async function
|
|
680
|
+
async function settleLeftOver(reconcile, submission) {
|
|
681
|
+
var _a, _b;
|
|
682
|
+
const settled = await resolveSubmission(submission, reconcile.lookup, reconcile.now);
|
|
683
|
+
if (settled.outcome === "unknown") throw new CloseOutcomeUnknownError(submission);
|
|
684
|
+
await ((_a = reconcile.onSubmission) == null ? void 0 : _a.call(reconcile, null));
|
|
685
|
+
if (settled.outcome === "failed") {
|
|
686
|
+
throw new CloseTransactionFailedError(submission, settled.message);
|
|
687
|
+
}
|
|
688
|
+
if (settled.outcome === "expired") return "expired";
|
|
689
|
+
(_b = reconcile.onResolved) == null ? void 0 : _b.call(reconcile, submission);
|
|
690
|
+
return submissionClosesAccount(submission) ? "closed" : "confirmed";
|
|
691
|
+
}
|
|
692
|
+
async function mergeExplainsMissingAccount(run, error) {
|
|
693
|
+
if (!hasCode(error, "account_not_found") || !run.reconcile || !run.confirmedMerge) return false;
|
|
694
|
+
const settled = await resolveSubmission(
|
|
695
|
+
run.confirmedMerge,
|
|
696
|
+
run.reconcile.lookup,
|
|
697
|
+
run.reconcile.now
|
|
698
|
+
);
|
|
699
|
+
return settled.outcome === "confirmed";
|
|
700
|
+
}
|
|
701
|
+
async function fetchRound(deps, planHash) {
|
|
702
|
+
var _a;
|
|
703
|
+
for (let attempt = 0; ; attempt++) {
|
|
704
|
+
if (attempt > 0) await ((_a = deps.sleep) != null ? _a : defaultSleep)(STATE_CHANGED_REFETCH_DELAY_MS);
|
|
705
|
+
try {
|
|
706
|
+
return await deps.getTransactions(planHash === void 0 ? {} : { planHash });
|
|
707
|
+
} catch (error) {
|
|
708
|
+
if (!hasCode(error, "state_changed")) throw error;
|
|
709
|
+
if (planHash !== void 0 || attempt === STATE_CHANGED_REFETCHES) {
|
|
710
|
+
throw new CloseStateChangedError(error);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function resumedPosition(resume) {
|
|
716
|
+
var _a, _b, _c;
|
|
717
|
+
const txIndex = (_a = resume.txIndex) != null ? _a : 0;
|
|
718
|
+
return {
|
|
719
|
+
round: (_b = resume.round) != null ? _b : 0,
|
|
720
|
+
txIndex,
|
|
721
|
+
txCount: (_c = resume.txCount) != null ? _c : txIndex + 1 + resume.queue.length,
|
|
722
|
+
moreRoundsExpected: resume.requiresAnotherCall
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
function report(deps, build) {
|
|
726
|
+
if (!deps.onProgressEvent) return;
|
|
727
|
+
try {
|
|
728
|
+
const returned = deps.onProgressEvent(build());
|
|
729
|
+
if (returned instanceof Promise) returned.catch(() => {
|
|
730
|
+
});
|
|
731
|
+
} catch (e) {
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
function reportTx(deps, phase, at, covers) {
|
|
735
|
+
report(deps, () => ({
|
|
736
|
+
phase,
|
|
737
|
+
round: at.round,
|
|
738
|
+
txIndex: at.txIndex,
|
|
739
|
+
txCount: at.txCount,
|
|
740
|
+
covers: [...covers],
|
|
741
|
+
moreRoundsExpected: at.moreRoundsExpected
|
|
742
|
+
}));
|
|
743
|
+
}
|
|
744
|
+
async function processTxs(deps, txs, start) {
|
|
384
745
|
for (let i = 0; i < txs.length; i++) {
|
|
385
746
|
const tx = txs[i];
|
|
747
|
+
const at = __spreadProps(__spreadValues({}, start), { txIndex: start.txIndex + i });
|
|
748
|
+
reportTx(deps, "verifying", at, tx.covers);
|
|
386
749
|
await deps.verify(tx);
|
|
387
|
-
await signOrThrow(deps, tx, tx.xdr, txs.slice(i + 1),
|
|
750
|
+
await signOrThrow(deps, tx, tx.xdr, txs.slice(i + 1), at);
|
|
388
751
|
}
|
|
389
752
|
}
|
|
390
|
-
async function signOrThrow(deps, tx, xdr, queue,
|
|
391
|
-
var _a;
|
|
753
|
+
async function signOrThrow(deps, tx, xdr, queue, at) {
|
|
754
|
+
var _a, _b, _c, _d, _e, _f;
|
|
392
755
|
const required = deps.requiredWeight(tx);
|
|
756
|
+
reportTx(deps, "awaiting-signature", at, tx.covers);
|
|
393
757
|
const { xdr: signedXdr, weight } = await deps.sign(tx, xdr);
|
|
394
758
|
if (weight < required) {
|
|
395
759
|
throw new InsufficientSignatureWeightError({
|
|
@@ -398,19 +762,82 @@ async function signOrThrow(deps, tx, xdr, queue, requiresAnotherCall) {
|
|
|
398
762
|
requiredWeight: required,
|
|
399
763
|
accumulatedWeight: weight,
|
|
400
764
|
queue,
|
|
401
|
-
requiresAnotherCall
|
|
765
|
+
requiresAnotherCall: at.moreRoundsExpected,
|
|
766
|
+
round: at.round,
|
|
767
|
+
txIndex: at.txIndex,
|
|
768
|
+
txCount: at.txCount
|
|
402
769
|
});
|
|
403
770
|
}
|
|
404
|
-
|
|
405
|
-
|
|
771
|
+
reportTx(deps, "submitting", at, tx.covers);
|
|
772
|
+
const finalXdr = deps.prepare ? await deps.prepare(tx, signedXdr) : signedXdr;
|
|
773
|
+
if (!deps.reconcile) {
|
|
774
|
+
(_a = deps.onConfirmed) == null ? void 0 : _a.call(deps, tx, await deps.submit(tx, finalXdr));
|
|
775
|
+
reportTx(deps, "confirmed", at, tx.covers);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
const submission = openSubmission(deps.reconcile, tx, finalXdr);
|
|
779
|
+
await ((_c = (_b = deps.reconcile).onSubmission) == null ? void 0 : _c.call(_b, submission));
|
|
780
|
+
await submitUntilSettled(deps, deps.reconcile, tx, finalXdr, submission);
|
|
781
|
+
await ((_e = (_d = deps.reconcile).onSubmission) == null ? void 0 : _e.call(_d, null));
|
|
782
|
+
if (submissionClosesAccount(submission)) deps.confirmedMerge = submission;
|
|
783
|
+
(_f = deps.onConfirmed) == null ? void 0 : _f.call(deps, __spreadProps(__spreadValues({}, tx), { covers: submission.covers }), submission.hash);
|
|
784
|
+
reportTx(deps, "confirmed", at, submission.covers);
|
|
785
|
+
}
|
|
786
|
+
function openSubmission(reconcile, tx, finalXdr) {
|
|
787
|
+
const claimed = tx.covers.filter((s) => s !== "MERGE" && s !== "CLOSE_ACCOUNT");
|
|
788
|
+
return __spreadValues(__spreadValues({
|
|
789
|
+
hash: reconcile.hashOf(finalXdr),
|
|
790
|
+
covers: reconcile.closesAccount(finalXdr) ? [...claimed, "MERGE", "CLOSE_ACCOUNT"] : claimed,
|
|
791
|
+
validUntil: reconcile.validUntil(finalXdr)
|
|
792
|
+
}, tx.coversTargets && { coversTargets: tx.coversTargets }), tx.needsSponsoredFee && { needsSponsoredFee: true });
|
|
793
|
+
}
|
|
794
|
+
async function submitUntilSettled(deps, reconcile, tx, finalXdr, submission) {
|
|
795
|
+
var _a, _b, _c, _d;
|
|
796
|
+
for (let attempt = 0; attempt <= SUBMIT_RESEND_ATTEMPTS; attempt++) {
|
|
797
|
+
if (attempt > 0) {
|
|
798
|
+
(_a = deps.onProgress) == null ? void 0 : _a.call(deps, "Still waiting for the network to confirm\u2026");
|
|
799
|
+
await ((_b = deps.sleep) != null ? _b : defaultSleep)(SUBMIT_RESEND_DELAY_MS);
|
|
800
|
+
}
|
|
801
|
+
try {
|
|
802
|
+
if (await deps.submit(tx, finalXdr) === submission.hash) return;
|
|
803
|
+
} catch (error) {
|
|
804
|
+
if (!isSubmitRefused(error)) continue;
|
|
805
|
+
if (attempt === 0) {
|
|
806
|
+
await ((_c = reconcile.onSubmission) == null ? void 0 : _c.call(reconcile, null));
|
|
807
|
+
throw error;
|
|
808
|
+
}
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
const settled = await resolveSubmission(submission, reconcile.lookup, reconcile.now);
|
|
813
|
+
if (settled.outcome === "confirmed") return;
|
|
814
|
+
if (settled.outcome === "failed") {
|
|
815
|
+
await ((_d = reconcile.onSubmission) == null ? void 0 : _d.call(reconcile, null));
|
|
816
|
+
throw new CloseTransactionFailedError(submission, settled.message);
|
|
817
|
+
}
|
|
818
|
+
throw new CloseOutcomeUnknownError(submission);
|
|
406
819
|
}
|
|
407
820
|
// Annotate the CommonJS export names for ESM import in node:
|
|
408
821
|
0 && (module.exports = {
|
|
822
|
+
CloseDidNotConvergeError,
|
|
823
|
+
CloseOutcomeUnknownError,
|
|
824
|
+
CloseStateChangedError,
|
|
825
|
+
CloseTransactionFailedError,
|
|
409
826
|
InsufficientSignatureWeightError,
|
|
410
827
|
LumenWipeAbortError,
|
|
411
828
|
LumenWipeApiError,
|
|
412
829
|
LumenWipeClient,
|
|
830
|
+
LumenWipeIntegratorClient,
|
|
831
|
+
LumenWipeInvalidNetworkError,
|
|
413
832
|
LumenWipeTimeoutError,
|
|
833
|
+
STATE_CHANGED_REFETCHES,
|
|
834
|
+
STATE_CHANGED_REFETCH_DELAY_MS,
|
|
835
|
+
SUBMISSION_GIVE_UP_MARGIN_SECONDS,
|
|
836
|
+
SUBMIT_RESEND_ATTEMPTS,
|
|
837
|
+
SUBMIT_RESEND_DELAY_MS,
|
|
414
838
|
isApiErrorCode,
|
|
415
|
-
|
|
839
|
+
isSubmitRefused,
|
|
840
|
+
resolveSubmission,
|
|
841
|
+
runClose,
|
|
842
|
+
submissionClosesAccount
|
|
416
843
|
});
|