@glyphteck/veyl 0.51.0 → 0.55.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/LICENSE +22 -0
- package/dist/account.js +515 -562
- package/dist/auth.js +16 -9
- package/dist/cli.js +674 -694
- package/dist/index.js +674 -694
- package/docs/agents.md +1 -1
- package/docs/api.md +3 -3
- package/docs/validation.md +5 -5
- package/package.json +3 -2
- package/readme.md +28 -26
package/dist/account.js
CHANGED
|
@@ -6346,10 +6346,10 @@ var domains = Object.freeze({
|
|
|
6346
6346
|
var origins = Object.freeze({
|
|
6347
6347
|
root: `https://${domains.root}`,
|
|
6348
6348
|
rootDev: `https://${domains.rootDev}`,
|
|
6349
|
-
rootDevWeb: `https://${domains.rootDev}:3001`,
|
|
6350
6349
|
veyl: `https://${domains.veyl}`,
|
|
6351
6350
|
veylDev: `https://${domains.veylDev}`,
|
|
6352
|
-
veylDevWeb: `https://${domains.veylDev}:3000
|
|
6351
|
+
veylDevWeb: `https://${domains.veylDev}:3000`,
|
|
6352
|
+
veylDevFeatureWeb: `https://${domains.veylDev}:3001`
|
|
6353
6353
|
});
|
|
6354
6354
|
var appDomains = Object.freeze([
|
|
6355
6355
|
domains.veyl
|
|
@@ -7143,6 +7143,84 @@ function clearSettingsKey(key) {
|
|
|
7143
7143
|
cleanBytes(key);
|
|
7144
7144
|
}
|
|
7145
7145
|
|
|
7146
|
+
// ../../core/utils/number.js
|
|
7147
|
+
function nonNegativeNumber(value, fallback) {
|
|
7148
|
+
const next = Number(value);
|
|
7149
|
+
return Number.isFinite(next) && next >= 0 ? next : fallback;
|
|
7150
|
+
}
|
|
7151
|
+
function positiveNumber(value, fallback) {
|
|
7152
|
+
const next = Number(value);
|
|
7153
|
+
return Number.isFinite(next) && next > 0 ? next : fallback;
|
|
7154
|
+
}
|
|
7155
|
+
function positiveInt(value, fallback) {
|
|
7156
|
+
const next = Math.trunc(Number(value));
|
|
7157
|
+
return Number.isFinite(next) && next > 0 ? next : fallback;
|
|
7158
|
+
}
|
|
7159
|
+
function nonNegativeInt(value, fallback) {
|
|
7160
|
+
const next = Math.trunc(Number(value));
|
|
7161
|
+
return Number.isFinite(next) && next >= 0 ? next : fallback;
|
|
7162
|
+
}
|
|
7163
|
+
|
|
7164
|
+
// ../../core/utils/async.js
|
|
7165
|
+
function sleep(ms) {
|
|
7166
|
+
return new Promise((resolve) => setTimeout(resolve, nonNegativeNumber(ms, 0)));
|
|
7167
|
+
}
|
|
7168
|
+
function createOwnedWaits() {
|
|
7169
|
+
const waits = new Set;
|
|
7170
|
+
let closed = false;
|
|
7171
|
+
const wait = (schedule, cancel) => new Promise((resolve) => {
|
|
7172
|
+
if (closed) {
|
|
7173
|
+
resolve();
|
|
7174
|
+
return;
|
|
7175
|
+
}
|
|
7176
|
+
const pending = { cancel, id: null, resolve };
|
|
7177
|
+
pending.id = schedule(() => {
|
|
7178
|
+
waits.delete(pending);
|
|
7179
|
+
resolve();
|
|
7180
|
+
});
|
|
7181
|
+
waits.add(pending);
|
|
7182
|
+
});
|
|
7183
|
+
return {
|
|
7184
|
+
delay(ms) {
|
|
7185
|
+
return wait((done) => globalThis.setTimeout(done, nonNegativeNumber(ms, 0)), (id) => globalThis.clearTimeout(id));
|
|
7186
|
+
},
|
|
7187
|
+
idle({ timeout = 0, delay = 0 } = {}) {
|
|
7188
|
+
const requestIdle = globalThis.requestIdleCallback?.bind(globalThis);
|
|
7189
|
+
const cancelIdle = globalThis.cancelIdleCallback?.bind(globalThis);
|
|
7190
|
+
if (typeof requestIdle === "function" && typeof cancelIdle === "function") {
|
|
7191
|
+
return wait((done) => requestIdle(done, { timeout: nonNegativeNumber(timeout, 0) }), cancelIdle);
|
|
7192
|
+
}
|
|
7193
|
+
return wait((done) => globalThis.setTimeout(done, nonNegativeNumber(delay, 0)), (id) => globalThis.clearTimeout(id));
|
|
7194
|
+
},
|
|
7195
|
+
close() {
|
|
7196
|
+
if (closed)
|
|
7197
|
+
return;
|
|
7198
|
+
closed = true;
|
|
7199
|
+
for (const pending of waits) {
|
|
7200
|
+
pending.cancel(pending.id);
|
|
7201
|
+
pending.resolve();
|
|
7202
|
+
}
|
|
7203
|
+
waits.clear();
|
|
7204
|
+
}
|
|
7205
|
+
};
|
|
7206
|
+
}
|
|
7207
|
+
async function yieldToUi() {
|
|
7208
|
+
if (typeof requestAnimationFrame === "function") {
|
|
7209
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
7210
|
+
}
|
|
7211
|
+
await sleep(0);
|
|
7212
|
+
}
|
|
7213
|
+
function waitForIdle({ timeout = 0, delay = 0 } = {}) {
|
|
7214
|
+
return new Promise((resolve) => {
|
|
7215
|
+
const idle = globalThis.requestIdleCallback?.bind(globalThis);
|
|
7216
|
+
if (typeof idle === "function") {
|
|
7217
|
+
idle(() => resolve(), { timeout: nonNegativeNumber(timeout, 0) });
|
|
7218
|
+
return;
|
|
7219
|
+
}
|
|
7220
|
+
globalThis.setTimeout(resolve, nonNegativeNumber(delay, 0));
|
|
7221
|
+
});
|
|
7222
|
+
}
|
|
7223
|
+
|
|
7146
7224
|
// ../../core/crypto/sign.js
|
|
7147
7225
|
"use client";
|
|
7148
7226
|
var CHAT_SIGNING_SCOPE = "chat-actor-sign-v2";
|
|
@@ -8067,6 +8145,8 @@ async function openVaultAccountSession(vault, password, options = {}) {
|
|
|
8067
8145
|
mark(diag, "vault.unlock.decrypt.done", { elapsedMs: Date.now() - decryptStartedAt, source });
|
|
8068
8146
|
onStage?.("seed-decrypted");
|
|
8069
8147
|
notifySeedDecrypted(onSeedDecrypted, diag, source);
|
|
8148
|
+
await yieldToUi();
|
|
8149
|
+
requireCurrent(isCurrent);
|
|
8070
8150
|
onStage?.("deriving");
|
|
8071
8151
|
const deriveStartedAt = Date.now();
|
|
8072
8152
|
const registry = await openSecretRegistry(masterSeed, registryEnvelope);
|
|
@@ -8334,12 +8414,9 @@ function nextBanRefreshMs(banned, keys = ["full", "chat"], now = Date.now()) {
|
|
|
8334
8414
|
}
|
|
8335
8415
|
|
|
8336
8416
|
// ../../core/profile.js
|
|
8337
|
-
var BOT_PROFILE_MARKER = "
|
|
8417
|
+
var BOT_PROFILE_MARKER = "glyphteck";
|
|
8338
8418
|
function readBotMarker(profile) {
|
|
8339
|
-
|
|
8340
|
-
if (!bot)
|
|
8341
|
-
return null;
|
|
8342
|
-
return typeof bot === "string" ? bot : BOT_PROFILE_MARKER;
|
|
8419
|
+
return profile?.bot === BOT_PROFILE_MARKER ? BOT_PROFILE_MARKER : null;
|
|
8343
8420
|
}
|
|
8344
8421
|
function hasPeerKeys(profile) {
|
|
8345
8422
|
return !!(profile?.walletPK || profile?.chatPK);
|
|
@@ -9385,24 +9462,6 @@ async function makeSharedFileUploadPayload(data, { contentType = "application/oc
|
|
|
9385
9462
|
}
|
|
9386
9463
|
}
|
|
9387
9464
|
|
|
9388
|
-
// ../../core/utils/number.js
|
|
9389
|
-
function nonNegativeNumber(value, fallback) {
|
|
9390
|
-
const next = Number(value);
|
|
9391
|
-
return Number.isFinite(next) && next >= 0 ? next : fallback;
|
|
9392
|
-
}
|
|
9393
|
-
function positiveNumber(value, fallback) {
|
|
9394
|
-
const next = Number(value);
|
|
9395
|
-
return Number.isFinite(next) && next > 0 ? next : fallback;
|
|
9396
|
-
}
|
|
9397
|
-
function positiveInt(value, fallback) {
|
|
9398
|
-
const next = Math.trunc(Number(value));
|
|
9399
|
-
return Number.isFinite(next) && next > 0 ? next : fallback;
|
|
9400
|
-
}
|
|
9401
|
-
function nonNegativeInt(value, fallback) {
|
|
9402
|
-
const next = Math.trunc(Number(value));
|
|
9403
|
-
return Number.isFinite(next) && next >= 0 ? next : fallback;
|
|
9404
|
-
}
|
|
9405
|
-
|
|
9406
9465
|
// ../../core/chat/messages/files.js
|
|
9407
9466
|
function hasLocalFileRef(msg) {
|
|
9408
9467
|
if (!hasText(msg?.p) || !hasText(msg?.k)) {
|
|
@@ -12412,8 +12471,8 @@ var HIDDEN_TRANSFER_STATUSES = new Set(["TRANSFER_STATUS_EXPIRED", "TRANSFER_STA
|
|
|
12412
12471
|
var LIGHTNING_RECEIVE_DONE_STATUSES = new Set(["transfer_completed", "lightning_payment_received", "payment_preimage_recovered", "completed"]);
|
|
12413
12472
|
var WALLET_TRANSFER_TYPE_CODES = new Set([TRANSFER_TYPE_TRANSFER_CODE]);
|
|
12414
12473
|
var WALLET_TRANSFER_TYPES = new Set([TRANSFER_TYPE_TRANSFER]);
|
|
12415
|
-
var
|
|
12416
|
-
var
|
|
12474
|
+
var BITCOIN_PAYMENT_TRANSFER_TYPE_CODES = new Set([TRANSFER_TYPE_COOPERATIVE_EXIT_CODE]);
|
|
12475
|
+
var BITCOIN_PAYMENT_TRANSFER_TYPES = new Set([TRANSFER_TYPE_COOPERATIVE_EXIT]);
|
|
12417
12476
|
var BITCOIN_DEPOSIT_TYPE_CODES = new Set;
|
|
12418
12477
|
var BITCOIN_DEPOSIT_TYPES = new Set([TRANSFER_TYPE_BITCOIN_DEPOSIT]);
|
|
12419
12478
|
var FUNDING_TRANSFER_TYPE_CODES = new Set([TRANSFER_TYPE_UTXO_SWAP_CODE]);
|
|
@@ -12447,8 +12506,8 @@ function isPendingTransfer(tx) {
|
|
|
12447
12506
|
function isWalletTransfer(tx) {
|
|
12448
12507
|
return hasTransferType(tx, WALLET_TRANSFER_TYPES, WALLET_TRANSFER_TYPE_CODES);
|
|
12449
12508
|
}
|
|
12450
|
-
function
|
|
12451
|
-
return hasTransferType(tx,
|
|
12509
|
+
function isBitcoinPaymentTransfer(tx) {
|
|
12510
|
+
return hasTransferType(tx, BITCOIN_PAYMENT_TRANSFER_TYPES, BITCOIN_PAYMENT_TRANSFER_TYPE_CODES);
|
|
12452
12511
|
}
|
|
12453
12512
|
function isFundingTransfer(tx) {
|
|
12454
12513
|
if (hasTransferType(tx, BITCOIN_DEPOSIT_TYPES, BITCOIN_DEPOSIT_TYPE_CODES)) {
|
|
@@ -12464,7 +12523,7 @@ function isVisibleTransferStatus(tx) {
|
|
|
12464
12523
|
return !status || !HIDDEN_TRANSFER_STATUSES.has(status);
|
|
12465
12524
|
}
|
|
12466
12525
|
function isVisibleTransfer(tx) {
|
|
12467
|
-
return isVisibleTransferStatus(tx) && (isWalletTransfer(tx) || isFundingTransfer(tx) ||
|
|
12526
|
+
return isVisibleTransferStatus(tx) && (isWalletTransfer(tx) || isFundingTransfer(tx) || isBitcoinPaymentTransfer(tx));
|
|
12468
12527
|
}
|
|
12469
12528
|
function isClaimablePendingTransfer(tx, types = null) {
|
|
12470
12529
|
if (Array.isArray(types) && types.length && !types.includes(tx?.type)) {
|
|
@@ -15939,66 +15998,6 @@ function sameHead(a, b) {
|
|
|
15939
15998
|
return a?.from === b?.from && a?.cid === b?.cid;
|
|
15940
15999
|
}
|
|
15941
16000
|
|
|
15942
|
-
// ../../core/utils/async.js
|
|
15943
|
-
function sleep(ms) {
|
|
15944
|
-
return new Promise((resolve) => setTimeout(resolve, nonNegativeNumber(ms, 0)));
|
|
15945
|
-
}
|
|
15946
|
-
function createOwnedWaits() {
|
|
15947
|
-
const waits = new Set;
|
|
15948
|
-
let closed = false;
|
|
15949
|
-
const wait = (schedule, cancel) => new Promise((resolve) => {
|
|
15950
|
-
if (closed) {
|
|
15951
|
-
resolve();
|
|
15952
|
-
return;
|
|
15953
|
-
}
|
|
15954
|
-
const pending = { cancel, id: null, resolve };
|
|
15955
|
-
pending.id = schedule(() => {
|
|
15956
|
-
waits.delete(pending);
|
|
15957
|
-
resolve();
|
|
15958
|
-
});
|
|
15959
|
-
waits.add(pending);
|
|
15960
|
-
});
|
|
15961
|
-
return {
|
|
15962
|
-
delay(ms) {
|
|
15963
|
-
return wait((done) => globalThis.setTimeout(done, nonNegativeNumber(ms, 0)), (id) => globalThis.clearTimeout(id));
|
|
15964
|
-
},
|
|
15965
|
-
idle({ timeout = 0, delay = 0 } = {}) {
|
|
15966
|
-
const requestIdle = globalThis.requestIdleCallback?.bind(globalThis);
|
|
15967
|
-
const cancelIdle = globalThis.cancelIdleCallback?.bind(globalThis);
|
|
15968
|
-
if (typeof requestIdle === "function" && typeof cancelIdle === "function") {
|
|
15969
|
-
return wait((done) => requestIdle(done, { timeout: nonNegativeNumber(timeout, 0) }), cancelIdle);
|
|
15970
|
-
}
|
|
15971
|
-
return wait((done) => globalThis.setTimeout(done, nonNegativeNumber(delay, 0)), (id) => globalThis.clearTimeout(id));
|
|
15972
|
-
},
|
|
15973
|
-
close() {
|
|
15974
|
-
if (closed)
|
|
15975
|
-
return;
|
|
15976
|
-
closed = true;
|
|
15977
|
-
for (const pending of waits) {
|
|
15978
|
-
pending.cancel(pending.id);
|
|
15979
|
-
pending.resolve();
|
|
15980
|
-
}
|
|
15981
|
-
waits.clear();
|
|
15982
|
-
}
|
|
15983
|
-
};
|
|
15984
|
-
}
|
|
15985
|
-
async function yieldToUi() {
|
|
15986
|
-
if (typeof requestAnimationFrame === "function") {
|
|
15987
|
-
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
15988
|
-
}
|
|
15989
|
-
await sleep(0);
|
|
15990
|
-
}
|
|
15991
|
-
function waitForIdle({ timeout = 0, delay = 0 } = {}) {
|
|
15992
|
-
return new Promise((resolve) => {
|
|
15993
|
-
const idle = globalThis.requestIdleCallback?.bind(globalThis);
|
|
15994
|
-
if (typeof idle === "function") {
|
|
15995
|
-
idle(() => resolve(), { timeout: nonNegativeNumber(timeout, 0) });
|
|
15996
|
-
return;
|
|
15997
|
-
}
|
|
15998
|
-
globalThis.setTimeout(resolve, nonNegativeNumber(delay, 0));
|
|
15999
|
-
});
|
|
16000
|
-
}
|
|
16001
|
-
|
|
16002
16001
|
// ../../core/chat/messages/payloadlog.js
|
|
16003
16002
|
var CHAT_PAYLOAD_DIAG_ENV = globalThis.process?.env || {};
|
|
16004
16003
|
var CHAT_PAYLOAD_DIAG_DEV = typeof globalThis !== "undefined" && globalThis.__DEV__ === true || CHAT_PAYLOAD_DIAG_ENV?.NODE_ENV === "development";
|
|
@@ -21194,7 +21193,7 @@ var FEE_RATE_FALLBACKS = Object.freeze({
|
|
|
21194
21193
|
noPriority: Object.freeze(["low", "medium", "high"]),
|
|
21195
21194
|
average: Object.freeze(["medium", "low", "high"])
|
|
21196
21195
|
});
|
|
21197
|
-
var
|
|
21196
|
+
var BITCOIN_PAYMENT_FEE_FIELDS = Object.freeze({
|
|
21198
21197
|
FAST: Object.freeze({ user: "userFeeFast", l1: "l1BroadcastFeeFast" }),
|
|
21199
21198
|
MEDIUM: Object.freeze({ user: "userFeeMedium", l1: "l1BroadcastFeeMedium" }),
|
|
21200
21199
|
SLOW: Object.freeze({ user: "userFeeSlow", l1: "l1BroadcastFeeSlow" })
|
|
@@ -21240,12 +21239,12 @@ function getCurrencyAmountSats(amount) {
|
|
|
21240
21239
|
}
|
|
21241
21240
|
return value;
|
|
21242
21241
|
}
|
|
21243
|
-
function
|
|
21242
|
+
function getBitcoinPaymentFeeBreakdown(feeQuote, exitSpeed = DEFAULT_EXIT_SPEED) {
|
|
21244
21243
|
if (!feeQuote) {
|
|
21245
21244
|
return null;
|
|
21246
21245
|
}
|
|
21247
21246
|
const speed = getExitSpeed(exitSpeed);
|
|
21248
|
-
const fields =
|
|
21247
|
+
const fields = BITCOIN_PAYMENT_FEE_FIELDS[speed];
|
|
21249
21248
|
const userFeeSats = getCurrencyAmountSats(feeQuote[fields.user]);
|
|
21250
21249
|
const l1BroadcastFeeSats = getCurrencyAmountSats(feeQuote[fields.l1]);
|
|
21251
21250
|
return {
|
|
@@ -21257,16 +21256,16 @@ function getWithdrawalFeeBreakdown(feeQuote, exitSpeed = DEFAULT_EXIT_SPEED) {
|
|
|
21257
21256
|
expiresAt: feeQuote.expiresAt ?? null
|
|
21258
21257
|
};
|
|
21259
21258
|
}
|
|
21260
|
-
function
|
|
21261
|
-
return
|
|
21259
|
+
function getBitcoinPaymentFeeAmountSats(feeQuote, exitSpeed = DEFAULT_EXIT_SPEED) {
|
|
21260
|
+
return getBitcoinPaymentFeeBreakdown(feeQuote, exitSpeed)?.feeAmountSats ?? null;
|
|
21262
21261
|
}
|
|
21263
|
-
function
|
|
21262
|
+
function normalizeBitcoinPaymentFeeQuote(feeQuote) {
|
|
21264
21263
|
if (!feeQuote) {
|
|
21265
21264
|
return null;
|
|
21266
21265
|
}
|
|
21267
21266
|
const speeds = {};
|
|
21268
21267
|
for (const speed of EXIT_SPEEDS) {
|
|
21269
|
-
speeds[speed] =
|
|
21268
|
+
speeds[speed] = getBitcoinPaymentFeeBreakdown(feeQuote, speed);
|
|
21270
21269
|
}
|
|
21271
21270
|
return {
|
|
21272
21271
|
id: feeQuote.id ?? null,
|
|
@@ -23855,59 +23854,6 @@ async function createLightningInvoice(wallet, { amountSats = 0, memo, expirySeco
|
|
|
23855
23854
|
return { success: false, error };
|
|
23856
23855
|
}
|
|
23857
23856
|
}
|
|
23858
|
-
async function quoteLightningFees(wallet, { invoice, amountSats } = {}) {
|
|
23859
|
-
if (!wallet) {
|
|
23860
|
-
return { success: false, error: new Error("wallet not ready") };
|
|
23861
|
-
}
|
|
23862
|
-
const encodedInvoice = cleanText(invoice);
|
|
23863
|
-
if (!encodedInvoice) {
|
|
23864
|
-
return { success: false, error: new Error("lightning invoice required") };
|
|
23865
|
-
}
|
|
23866
|
-
try {
|
|
23867
|
-
const params = { encodedInvoice };
|
|
23868
|
-
if (amountSats != null) {
|
|
23869
|
-
params.amountSats = toSafeSats(amountSats);
|
|
23870
|
-
}
|
|
23871
|
-
const feeAmountSats = await wallet.getLightningSendFeeEstimate(params);
|
|
23872
|
-
return {
|
|
23873
|
-
success: true,
|
|
23874
|
-
feeAmountSats,
|
|
23875
|
-
fees: normalizeLightningFeeEstimate(feeAmountSats)
|
|
23876
|
-
};
|
|
23877
|
-
} catch (error) {
|
|
23878
|
-
return { success: false, error };
|
|
23879
|
-
}
|
|
23880
|
-
}
|
|
23881
|
-
async function sendLightningPayment(wallet, { invoice, maxFeeSats, preferSpark = false, amountSatsToSend, idempotencyKey } = {}) {
|
|
23882
|
-
if (!wallet) {
|
|
23883
|
-
return { success: false, error: new Error("wallet not ready") };
|
|
23884
|
-
}
|
|
23885
|
-
const encodedInvoice = cleanText(invoice);
|
|
23886
|
-
if (!encodedInvoice) {
|
|
23887
|
-
return { success: false, error: new Error("lightning invoice required") };
|
|
23888
|
-
}
|
|
23889
|
-
try {
|
|
23890
|
-
const params = {
|
|
23891
|
-
invoice: encodedInvoice,
|
|
23892
|
-
maxFeeSats: toSafeNonNegativeSats(maxFeeSats, "maxFeeSats"),
|
|
23893
|
-
preferSpark: !!preferSpark
|
|
23894
|
-
};
|
|
23895
|
-
if (amountSatsToSend != null) {
|
|
23896
|
-
params.amountSatsToSend = toSafeSats(amountSatsToSend, "amountSatsToSend");
|
|
23897
|
-
}
|
|
23898
|
-
if (idempotencyKey) {
|
|
23899
|
-
params.idempotencyKey = idempotencyKey;
|
|
23900
|
-
}
|
|
23901
|
-
const payment = await wallet.payLightningInvoice(params);
|
|
23902
|
-
return {
|
|
23903
|
-
success: true,
|
|
23904
|
-
payment,
|
|
23905
|
-
result: normalizeLightningPaymentResult(payment)
|
|
23906
|
-
};
|
|
23907
|
-
} catch (error) {
|
|
23908
|
-
return { success: false, error };
|
|
23909
|
-
}
|
|
23910
|
-
}
|
|
23911
23857
|
async function getLightningReceiveRequest(wallet, id) {
|
|
23912
23858
|
if (!wallet) {
|
|
23913
23859
|
return { success: false, error: new Error("wallet not ready") };
|
|
@@ -23948,17 +23894,9 @@ async function getLightningSendRequest(wallet, id) {
|
|
|
23948
23894
|
}
|
|
23949
23895
|
|
|
23950
23896
|
// ../../core/wallet/lightning.js
|
|
23951
|
-
function createLightning({ wallet
|
|
23897
|
+
function createLightning({ wallet }) {
|
|
23952
23898
|
return {
|
|
23953
23899
|
createLightningInvoice: (params) => createLightningInvoice(wallet, params),
|
|
23954
|
-
quoteLightningFees: (params) => quoteLightningFees(wallet, params),
|
|
23955
|
-
async sendLightningPayment(params) {
|
|
23956
|
-
const result = await sendLightningPayment(wallet, params);
|
|
23957
|
-
if (result.success) {
|
|
23958
|
-
await updateWalletData({ reason: "lightning-send" });
|
|
23959
|
-
}
|
|
23960
|
-
return result;
|
|
23961
|
-
},
|
|
23962
23900
|
getLightningReceiveRequest: (id) => getLightningReceiveRequest(wallet, id),
|
|
23963
23901
|
getLightningSendRequest: (id) => getLightningSendRequest(wallet, id)
|
|
23964
23902
|
};
|
|
@@ -24456,56 +24394,6 @@ function createWalletReadyDiag({ wallet, getBalanceState, getTransferState, diag
|
|
|
24456
24394
|
};
|
|
24457
24395
|
}
|
|
24458
24396
|
|
|
24459
|
-
// ../../core/wallet/privacy.js
|
|
24460
|
-
var PRIVACY_SYNC_DELAY_MS = WALLET_BOOT_CACHED_REFRESH_DELAY_MS * 2;
|
|
24461
|
-
function createWalletPrivacy({ wallet, ghostWallet, diag }) {
|
|
24462
|
-
let desiredPrivacy = ghostWallet === true;
|
|
24463
|
-
let revision = 0;
|
|
24464
|
-
let closed = false;
|
|
24465
|
-
let privacySync = Promise.resolve();
|
|
24466
|
-
const waits = createOwnedWaits();
|
|
24467
|
-
const setGhostWallet = (nextGhostWallet) => {
|
|
24468
|
-
const nextDesiredPrivacy = nextGhostWallet === true;
|
|
24469
|
-
if (revision > 0 && desiredPrivacy === nextDesiredPrivacy) {
|
|
24470
|
-
return privacySync;
|
|
24471
|
-
}
|
|
24472
|
-
desiredPrivacy = nextDesiredPrivacy;
|
|
24473
|
-
const requestRevision = ++revision;
|
|
24474
|
-
if (!wallet || typeof wallet.setPrivacyEnabled !== "function" || closed) {
|
|
24475
|
-
return privacySync;
|
|
24476
|
-
}
|
|
24477
|
-
privacySync = privacySync.catch(() => {}).then(async () => {
|
|
24478
|
-
await waits.delay(PRIVACY_SYNC_DELAY_MS);
|
|
24479
|
-
if (closed || requestRevision !== revision) {
|
|
24480
|
-
return;
|
|
24481
|
-
}
|
|
24482
|
-
await waits.idle({ timeout: PRIVACY_SYNC_DELAY_MS, delay: PRIVACY_SYNC_DELAY_MS });
|
|
24483
|
-
if (closed || requestRevision !== revision) {
|
|
24484
|
-
return;
|
|
24485
|
-
}
|
|
24486
|
-
const startedAt = Date.now();
|
|
24487
|
-
const desired = desiredPrivacy;
|
|
24488
|
-
markDiag(diag, "wallet.privacy.start", { ghostWallet: desired });
|
|
24489
|
-
await wallet.setPrivacyEnabled(desired);
|
|
24490
|
-
markDone(diag, "wallet.privacy", startedAt, { changed: true });
|
|
24491
|
-
}).catch((error) => {
|
|
24492
|
-
markDiag(diag, "wallet.privacy.error", { code: error?.code || "", message: error?.message || String(error) });
|
|
24493
|
-
});
|
|
24494
|
-
return privacySync;
|
|
24495
|
-
};
|
|
24496
|
-
setGhostWallet(ghostWallet);
|
|
24497
|
-
return {
|
|
24498
|
-
setGhostWallet,
|
|
24499
|
-
close() {
|
|
24500
|
-
if (closed)
|
|
24501
|
-
return;
|
|
24502
|
-
closed = true;
|
|
24503
|
-
revision += 1;
|
|
24504
|
-
waits.close();
|
|
24505
|
-
}
|
|
24506
|
-
};
|
|
24507
|
-
}
|
|
24508
|
-
|
|
24509
24397
|
// ../../core/wallet/spark.js
|
|
24510
24398
|
var import_bech32 = __toESM(require_dist(), 1);
|
|
24511
24399
|
var SPARK_ADDRESS_PREFIX = Object.freeze({
|
|
@@ -24550,7 +24438,58 @@ function walletPKtoSparkAddress(walletPK, network) {
|
|
|
24550
24438
|
return import_bech32.bech32m.encode(getSparkAddressPrefix(network), import_bech32.bech32m.toWords(payload), 1500);
|
|
24551
24439
|
}
|
|
24552
24440
|
|
|
24553
|
-
// ../../core/wallet/
|
|
24441
|
+
// ../../core/wallet/payment.js
|
|
24442
|
+
var PAYMENT_KIND_BITCOIN = "bitcoin";
|
|
24443
|
+
var PAYMENT_KIND_LIGHTNING = "lightning";
|
|
24444
|
+
var PAYMENT_KIND_SPARK = "spark";
|
|
24445
|
+
var PAYMENT_KIND_SPARK_INVOICE = "spark_invoice";
|
|
24446
|
+
var PAYMENT_STATE_PREPARED = "prepared";
|
|
24447
|
+
var PAYMENT_KINDS = new Set([
|
|
24448
|
+
PAYMENT_KIND_BITCOIN,
|
|
24449
|
+
PAYMENT_KIND_LIGHTNING,
|
|
24450
|
+
PAYMENT_KIND_SPARK,
|
|
24451
|
+
PAYMENT_KIND_SPARK_INVOICE
|
|
24452
|
+
]);
|
|
24453
|
+
var P2PKH_DUST_SATS = 546;
|
|
24454
|
+
var P2SH_DUST_SATS = 540;
|
|
24455
|
+
var P2WPKH_DUST_SATS = 294;
|
|
24456
|
+
var SEGWIT_SCRIPT_DUST_SATS = 330;
|
|
24457
|
+
function paymentKind(value) {
|
|
24458
|
+
const kind = cleanText(value).toLowerCase();
|
|
24459
|
+
if (!PAYMENT_KINDS.has(kind)) {
|
|
24460
|
+
throw new Error("unsupported payment kind");
|
|
24461
|
+
}
|
|
24462
|
+
return kind;
|
|
24463
|
+
}
|
|
24464
|
+
function paymentDestination(value) {
|
|
24465
|
+
const destination = cleanText(value);
|
|
24466
|
+
if (!destination) {
|
|
24467
|
+
throw new Error("payment destination required");
|
|
24468
|
+
}
|
|
24469
|
+
return destination;
|
|
24470
|
+
}
|
|
24471
|
+
function optionalPaymentAmount(value) {
|
|
24472
|
+
return value == null ? null : toSafeSats(value);
|
|
24473
|
+
}
|
|
24474
|
+
function normalizeIntent(intent = {}) {
|
|
24475
|
+
const kind = paymentKind(intent.kind);
|
|
24476
|
+
const destination = paymentDestination(intent.destination);
|
|
24477
|
+
const amountSats = optionalPaymentAmount(intent.amountSats);
|
|
24478
|
+
if ((kind === PAYMENT_KIND_BITCOIN || kind === PAYMENT_KIND_SPARK) && amountSats == null) {
|
|
24479
|
+
throw new Error("payment amount required");
|
|
24480
|
+
}
|
|
24481
|
+
return {
|
|
24482
|
+
kind,
|
|
24483
|
+
destination,
|
|
24484
|
+
amountSats,
|
|
24485
|
+
variableAmount: intent.variableAmount === true,
|
|
24486
|
+
preferSpark: intent.preferSpark === true,
|
|
24487
|
+
idempotencyKey: cleanText(intent.idempotencyKey) || null,
|
|
24488
|
+
maxFeeSats: intent.maxFeeSats == null ? null : toSafeNonNegativeSats(intent.maxFeeSats, "maxFeeSats"),
|
|
24489
|
+
exitSpeed: getExitSpeed(intent.exitSpeed),
|
|
24490
|
+
deductFeeFromAmount: intent.deductFeeFromAmount !== false
|
|
24491
|
+
};
|
|
24492
|
+
}
|
|
24554
24493
|
function firstSparkInvoiceError(result) {
|
|
24555
24494
|
return result?.invalidInvoices?.[0]?.error || result?.satsTransactionErrors?.[0]?.error || result?.tokenTransactionErrors?.[0]?.error || null;
|
|
24556
24495
|
}
|
|
@@ -24566,137 +24505,345 @@ function publicSparkInvoiceResult(transfer) {
|
|
|
24566
24505
|
amountSats: Number.isSafeInteger(transfer?.totalValue) ? transfer.totalValue : null
|
|
24567
24506
|
};
|
|
24568
24507
|
}
|
|
24569
|
-
function
|
|
24570
|
-
const
|
|
24571
|
-
|
|
24572
|
-
|
|
24573
|
-
|
|
24574
|
-
|
|
24508
|
+
function getBitcoinPaymentMinimumReceiveSats(addressInput) {
|
|
24509
|
+
const address = cleanText(addressInput).toLowerCase();
|
|
24510
|
+
if (address.startsWith("1"))
|
|
24511
|
+
return P2PKH_DUST_SATS;
|
|
24512
|
+
if (address.startsWith("3"))
|
|
24513
|
+
return P2SH_DUST_SATS;
|
|
24514
|
+
const p2wpkhLength = address.startsWith("bcrt1q") ? 44 : 42;
|
|
24515
|
+
if ((address.startsWith("bc1q") || address.startsWith("bcrt1q")) && address.length === p2wpkhLength) {
|
|
24516
|
+
return P2WPKH_DUST_SATS;
|
|
24517
|
+
}
|
|
24518
|
+
return SEGWIT_SCRIPT_DUST_SATS;
|
|
24519
|
+
}
|
|
24520
|
+
function getBitcoinPaymentAmounts(payment) {
|
|
24521
|
+
const amountSats = toSafeSats(payment?.amountSats);
|
|
24522
|
+
const feeAmountSats = toSafeNonNegativeSats(payment?.feeAmountSats ?? 0, "feeAmountSats");
|
|
24523
|
+
const deductFeeFromAmount = payment?.deductFeeFromAmount !== false;
|
|
24524
|
+
const receiveAmountSats = deductFeeFromAmount ? Math.max(0, amountSats - feeAmountSats) : amountSats;
|
|
24525
|
+
const sendAmountSats = deductFeeFromAmount ? amountSats : amountSats + feeAmountSats;
|
|
24526
|
+
return {
|
|
24527
|
+
sendAmountSats,
|
|
24528
|
+
receiveAmountSats,
|
|
24529
|
+
feeAmountSats
|
|
24530
|
+
};
|
|
24531
|
+
}
|
|
24532
|
+
function assertBitcoinPaymentReceivesFunds(payment) {
|
|
24533
|
+
const { receiveAmountSats } = getBitcoinPaymentAmounts(payment);
|
|
24534
|
+
const minimumReceiveAmountSats = getBitcoinPaymentMinimumReceiveSats(payment?.destination);
|
|
24535
|
+
if (receiveAmountSats < minimumReceiveAmountSats) {
|
|
24536
|
+
throw new Error(`bitcoin payment would create a dust output below ${minimumReceiveAmountSats} sats`);
|
|
24537
|
+
}
|
|
24538
|
+
}
|
|
24539
|
+
function bitcoinPaymentReview(intent, feeQuote) {
|
|
24540
|
+
const feeQuoteId = feeQuote?.id ?? null;
|
|
24541
|
+
const feeAmountSats = getBitcoinPaymentFeeAmountSats(feeQuote, intent.exitSpeed);
|
|
24542
|
+
if (!feeQuoteId || feeAmountSats == null) {
|
|
24543
|
+
throw new Error("bitcoin payment fee quote unavailable");
|
|
24544
|
+
}
|
|
24545
|
+
const sparkFees = normalizeBitcoinPaymentFeeQuote(feeQuote);
|
|
24546
|
+
const payment = {
|
|
24547
|
+
...intent,
|
|
24548
|
+
state: PAYMENT_STATE_PREPARED,
|
|
24549
|
+
feeQuote,
|
|
24550
|
+
feeQuoteId,
|
|
24551
|
+
feeAmountSats,
|
|
24552
|
+
fees: { spark: sparkFees },
|
|
24553
|
+
expiresAt: sparkFees?.expiresAt ?? feeQuote.expiresAt ?? null
|
|
24554
|
+
};
|
|
24555
|
+
assertBitcoinPaymentReceivesFunds(payment);
|
|
24556
|
+
return Object.freeze(payment);
|
|
24557
|
+
}
|
|
24558
|
+
function preparedPayment(intent, extra = {}) {
|
|
24559
|
+
return Object.freeze({
|
|
24560
|
+
...intent,
|
|
24561
|
+
state: PAYMENT_STATE_PREPARED,
|
|
24562
|
+
fees: null,
|
|
24563
|
+
expiresAt: null,
|
|
24564
|
+
...extra
|
|
24565
|
+
});
|
|
24566
|
+
}
|
|
24567
|
+
async function prepare(wallet, network, paymentInput) {
|
|
24568
|
+
if (!wallet) {
|
|
24569
|
+
throw new Error("wallet not ready");
|
|
24570
|
+
}
|
|
24571
|
+
const intent = normalizeIntent(paymentInput);
|
|
24572
|
+
if (intent.kind === PAYMENT_KIND_BITCOIN) {
|
|
24573
|
+
if (!isAddressOnNetwork(intent.destination, network)) {
|
|
24574
|
+
throw new Error(`refusing to pay - address is not a ${network} address`);
|
|
24575
24575
|
}
|
|
24576
|
-
const
|
|
24577
|
-
|
|
24578
|
-
|
|
24579
|
-
|
|
24580
|
-
|
|
24581
|
-
|
|
24582
|
-
|
|
24583
|
-
|
|
24584
|
-
|
|
24585
|
-
|
|
24586
|
-
|
|
24587
|
-
|
|
24588
|
-
|
|
24589
|
-
markDone(diag, "wallet.send.remember", rememberStartedAt, { hasTransfer: !!tx?.id });
|
|
24590
|
-
await refreshBalance?.();
|
|
24591
|
-
const refreshDelayMs = Number(sentTransferRefreshDelayMs);
|
|
24592
|
-
if (!closed && tx?.id && typeof refreshPendingTransfers === "function" && Number.isFinite(refreshDelayMs) && refreshDelayMs >= 0) {
|
|
24593
|
-
markDiag(diag, "wallet.send.refresh.schedule", { delayMs: refreshDelayMs });
|
|
24594
|
-
const timer = setTimeout(() => {
|
|
24595
|
-
refreshTimers.delete(timer);
|
|
24596
|
-
if (closed)
|
|
24597
|
-
return;
|
|
24598
|
-
markDiag(diag, "wallet.send.refresh.start", {});
|
|
24599
|
-
refreshPendingTransfers([tx.id]);
|
|
24600
|
-
}, refreshDelayMs);
|
|
24601
|
-
refreshTimers.add(timer);
|
|
24576
|
+
const feeQuote = await wallet.getWithdrawalFeeQuote({
|
|
24577
|
+
amountSats: intent.amountSats,
|
|
24578
|
+
withdrawalAddress: intent.destination
|
|
24579
|
+
});
|
|
24580
|
+
return bitcoinPaymentReview(intent, feeQuote);
|
|
24581
|
+
}
|
|
24582
|
+
if (intent.kind === PAYMENT_KIND_LIGHTNING) {
|
|
24583
|
+
let maxFeeSats = intent.maxFeeSats;
|
|
24584
|
+
let fees = maxFeeSats == null ? null : normalizeLightningFeeEstimate(maxFeeSats);
|
|
24585
|
+
if (maxFeeSats == null) {
|
|
24586
|
+
const estimateParams = { encodedInvoice: intent.destination };
|
|
24587
|
+
if (intent.variableAmount && intent.amountSats != null) {
|
|
24588
|
+
estimateParams.amountSats = intent.amountSats;
|
|
24602
24589
|
}
|
|
24603
|
-
|
|
24604
|
-
|
|
24605
|
-
} catch (error) {
|
|
24606
|
-
endSdkActivity?.();
|
|
24607
|
-
markError(diag, "wallet.send", startedAt, error);
|
|
24608
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
24609
|
-
throw new Error(`failed to send money: ${message}`, { cause: error });
|
|
24590
|
+
maxFeeSats = await wallet.getLightningSendFeeEstimate(estimateParams);
|
|
24591
|
+
fees = normalizeLightningFeeEstimate(maxFeeSats);
|
|
24610
24592
|
}
|
|
24593
|
+
return preparedPayment(intent, { maxFeeSats, fees });
|
|
24594
|
+
}
|
|
24595
|
+
return preparedPayment(intent);
|
|
24596
|
+
}
|
|
24597
|
+
function normalizePreparedPayment(paymentInput, network) {
|
|
24598
|
+
const intent = normalizeIntent(paymentInput);
|
|
24599
|
+
if (paymentInput?.state !== PAYMENT_STATE_PREPARED) {
|
|
24600
|
+
throw new Error("prepared payment required");
|
|
24601
|
+
}
|
|
24602
|
+
if (intent.kind !== PAYMENT_KIND_BITCOIN) {
|
|
24603
|
+
return preparedPayment(intent, {
|
|
24604
|
+
fees: paymentInput.fees || null,
|
|
24605
|
+
expiresAt: paymentInput.expiresAt ?? null
|
|
24606
|
+
});
|
|
24607
|
+
}
|
|
24608
|
+
if (!isAddressOnNetwork(intent.destination, network)) {
|
|
24609
|
+
throw new Error(`refusing to pay - address is not a ${network} address`);
|
|
24610
|
+
}
|
|
24611
|
+
const feeQuoteId = cleanText(paymentInput.feeQuoteId);
|
|
24612
|
+
if (!feeQuoteId) {
|
|
24613
|
+
throw new Error("bitcoin payment fee quote missing");
|
|
24614
|
+
}
|
|
24615
|
+
const payment = {
|
|
24616
|
+
...intent,
|
|
24617
|
+
state: PAYMENT_STATE_PREPARED,
|
|
24618
|
+
feeQuote: paymentInput.feeQuote || null,
|
|
24619
|
+
feeQuoteId,
|
|
24620
|
+
feeAmountSats: toSafeNonNegativeSats(paymentInput.feeAmountSats, "feeAmountSats"),
|
|
24621
|
+
fees: paymentInput.fees || null,
|
|
24622
|
+
expiresAt: paymentInput.expiresAt ?? null
|
|
24623
|
+
};
|
|
24624
|
+
assertBitcoinPaymentReceivesFunds(payment);
|
|
24625
|
+
return Object.freeze(payment);
|
|
24626
|
+
}
|
|
24627
|
+
async function sendBitcoin(wallet, walletPK, network, payment, rememberTransfer) {
|
|
24628
|
+
if (!isAddressOnNetwork(payment.destination, network)) {
|
|
24629
|
+
throw new Error(`refusing to pay - address is not a ${network} address`);
|
|
24630
|
+
}
|
|
24631
|
+
if (!payment.feeQuoteId || payment.feeAmountSats == null) {
|
|
24632
|
+
throw new Error("bitcoin payment review missing");
|
|
24633
|
+
}
|
|
24634
|
+
assertBitcoinPaymentReceivesFunds(payment);
|
|
24635
|
+
const tx = await wallet.withdraw({
|
|
24636
|
+
onchainAddress: payment.destination,
|
|
24637
|
+
amountSats: payment.amountSats,
|
|
24638
|
+
exitSpeed: payment.exitSpeed,
|
|
24639
|
+
feeQuoteId: payment.feeQuoteId,
|
|
24640
|
+
feeAmountSats: payment.feeAmountSats,
|
|
24641
|
+
deductFeeFromWithdrawalAmount: payment.deductFeeFromAmount
|
|
24642
|
+
});
|
|
24643
|
+
const transferId = tx?.transfer?.sparkId || tx?.transfer?.id || null;
|
|
24644
|
+
if (transferId) {
|
|
24645
|
+
const createdTime = tx?.createdAt || new Date().toISOString();
|
|
24646
|
+
await rememberTransfer?.({
|
|
24647
|
+
id: transferId,
|
|
24648
|
+
senderIdentityPublicKey: walletPK,
|
|
24649
|
+
receiverIdentityPublicKey: "",
|
|
24650
|
+
status: "TRANSFER_STATUS_SENDER_INITIATED",
|
|
24651
|
+
totalValue: payment.amountSats,
|
|
24652
|
+
createdTime,
|
|
24653
|
+
updatedTime: tx?.updatedAt || createdTime,
|
|
24654
|
+
type: "COOPERATIVE_EXIT",
|
|
24655
|
+
transferDirection: "OUTGOING"
|
|
24656
|
+
}, {
|
|
24657
|
+
pending: true,
|
|
24658
|
+
bitcoinAddress: payment.destination,
|
|
24659
|
+
bitcoinTxid: tx?.coopExitTxid || null
|
|
24660
|
+
});
|
|
24661
|
+
}
|
|
24662
|
+
return {
|
|
24663
|
+
id: transferId || tx?.id || tx?.coopExitTxid || null,
|
|
24664
|
+
result: tx
|
|
24611
24665
|
};
|
|
24612
|
-
|
|
24666
|
+
}
|
|
24667
|
+
function createPayment({
|
|
24668
|
+
wallet,
|
|
24669
|
+
walletPK,
|
|
24670
|
+
network,
|
|
24671
|
+
updateWalletData,
|
|
24672
|
+
refreshBalance = null,
|
|
24673
|
+
rememberTransfer,
|
|
24674
|
+
refreshPendingTransfers = null,
|
|
24675
|
+
sentTransferRefreshDelayMs = null,
|
|
24676
|
+
beginSdkActivity = null,
|
|
24677
|
+
diag = null
|
|
24678
|
+
}) {
|
|
24679
|
+
const refreshTimers = new Set;
|
|
24680
|
+
let closed = false;
|
|
24681
|
+
const assertReady = () => {
|
|
24613
24682
|
if (!wallet || closed) {
|
|
24614
24683
|
throw new Error("wallet not ready");
|
|
24615
24684
|
}
|
|
24616
|
-
|
|
24617
|
-
|
|
24618
|
-
|
|
24685
|
+
};
|
|
24686
|
+
const preparePayment = async (intent) => {
|
|
24687
|
+
assertReady();
|
|
24688
|
+
return prepare(wallet, network, intent);
|
|
24689
|
+
};
|
|
24690
|
+
const scheduleTransferRefresh = (id) => {
|
|
24691
|
+
const refreshDelayMs = Number(sentTransferRefreshDelayMs);
|
|
24692
|
+
if (closed || !id || typeof refreshPendingTransfers !== "function" || !Number.isFinite(refreshDelayMs) || refreshDelayMs < 0) {
|
|
24693
|
+
return;
|
|
24694
|
+
}
|
|
24695
|
+
markDiag(diag, "wallet.send.refresh.schedule", { delayMs: refreshDelayMs });
|
|
24696
|
+
const timer = setTimeout(() => {
|
|
24697
|
+
refreshTimers.delete(timer);
|
|
24698
|
+
if (closed)
|
|
24699
|
+
return;
|
|
24700
|
+
markDiag(diag, "wallet.send.refresh.start", {});
|
|
24701
|
+
refreshPendingTransfers([id]);
|
|
24702
|
+
}, refreshDelayMs);
|
|
24703
|
+
refreshTimers.add(timer);
|
|
24704
|
+
};
|
|
24705
|
+
const sendPayment = async (paymentInput) => {
|
|
24706
|
+
assertReady();
|
|
24707
|
+
if (paymentInput?.kind === PAYMENT_KIND_BITCOIN && paymentInput?.state !== PAYMENT_STATE_PREPARED) {
|
|
24708
|
+
throw new Error("bitcoin payment must be reviewed before sending");
|
|
24619
24709
|
}
|
|
24710
|
+
const payment = paymentInput?.state === PAYMENT_STATE_PREPARED ? normalizePreparedPayment(paymentInput, network) : await preparePayment(paymentInput);
|
|
24620
24711
|
const startedAt = Date.now();
|
|
24621
|
-
|
|
24622
|
-
|
|
24623
|
-
|
|
24624
|
-
|
|
24625
|
-
|
|
24626
|
-
|
|
24627
|
-
|
|
24628
|
-
|
|
24629
|
-
|
|
24630
|
-
const
|
|
24631
|
-
|
|
24632
|
-
|
|
24633
|
-
|
|
24634
|
-
|
|
24635
|
-
|
|
24712
|
+
const diagKind = payment.kind;
|
|
24713
|
+
markDiag(diag, "wallet.payment.start", { kind: diagKind });
|
|
24714
|
+
const endSdkActivity = typeof beginSdkActivity === "function" ? beginSdkActivity("payment") : null;
|
|
24715
|
+
try {
|
|
24716
|
+
let outcome;
|
|
24717
|
+
if (payment.kind === PAYMENT_KIND_SPARK) {
|
|
24718
|
+
if (sameText(payment.destination, walletPK)) {
|
|
24719
|
+
throw new Error("cannot send money to the same wallet");
|
|
24720
|
+
}
|
|
24721
|
+
const tx = await wallet.transfer({
|
|
24722
|
+
receiverSparkAddress: walletPKtoSparkAddress(payment.destination, network),
|
|
24723
|
+
amountSats: payment.amountSats
|
|
24724
|
+
});
|
|
24725
|
+
await rememberTransfer?.(tx, { pending: true });
|
|
24726
|
+
await refreshBalance?.();
|
|
24727
|
+
scheduleTransferRefresh(tx?.id);
|
|
24728
|
+
outcome = { id: tx?.id || null, result: tx };
|
|
24729
|
+
} else if (payment.kind === PAYMENT_KIND_LIGHTNING) {
|
|
24730
|
+
const sent = await wallet.payLightningInvoice({
|
|
24731
|
+
invoice: payment.destination,
|
|
24732
|
+
maxFeeSats: payment.maxFeeSats,
|
|
24733
|
+
preferSpark: payment.preferSpark,
|
|
24734
|
+
...payment.variableAmount && payment.amountSats != null ? { amountSatsToSend: payment.amountSats } : {},
|
|
24735
|
+
...payment.idempotencyKey ? { idempotencyKey: payment.idempotencyKey } : {}
|
|
24636
24736
|
});
|
|
24637
|
-
const result = normalizeLightningPaymentResult(
|
|
24737
|
+
const result = normalizeLightningPaymentResult(sent);
|
|
24638
24738
|
if (result?.kind === "spark" && result?.transfer) {
|
|
24639
24739
|
await rememberTransfer?.(result.transfer, { pending: true });
|
|
24640
24740
|
} else {
|
|
24641
|
-
await rememberTransfer?.(result?.id ||
|
|
24741
|
+
await rememberTransfer?.(result?.id || sent?.id);
|
|
24642
24742
|
await updateWalletData({ reason: "external-lightning-send" });
|
|
24643
24743
|
}
|
|
24644
|
-
|
|
24645
|
-
|
|
24646
|
-
id: result?.id || payment?.id || null,
|
|
24647
|
-
payment,
|
|
24648
|
-
result,
|
|
24649
|
-
fees
|
|
24650
|
-
};
|
|
24651
|
-
} catch (error) {
|
|
24652
|
-
markError(diag, "wallet.externalPay", startedAt, error, { type });
|
|
24653
|
-
throw error;
|
|
24654
|
-
}
|
|
24655
|
-
}
|
|
24656
|
-
if (type === "spark") {
|
|
24657
|
-
try {
|
|
24658
|
-
const safeAmountSats = amountSats != null ? toSafeSats(amountSats, "amountSats") : null;
|
|
24744
|
+
outcome = { id: result?.id || sent?.id || null, result, raw: sent };
|
|
24745
|
+
} else if (payment.kind === PAYMENT_KIND_SPARK_INVOICE) {
|
|
24659
24746
|
const result = await wallet.fulfillSparkInvoice([
|
|
24660
24747
|
{
|
|
24661
|
-
invoice:
|
|
24662
|
-
...
|
|
24748
|
+
invoice: payment.destination,
|
|
24749
|
+
...payment.amountSats != null ? { amount: BigInt(payment.amountSats) } : {}
|
|
24663
24750
|
}
|
|
24664
24751
|
]);
|
|
24665
24752
|
const error = firstSparkInvoiceError(result);
|
|
24666
|
-
if (error)
|
|
24753
|
+
if (error)
|
|
24667
24754
|
throw error;
|
|
24668
|
-
}
|
|
24669
24755
|
const success = firstSparkInvoiceSuccess(result);
|
|
24670
|
-
if (!success)
|
|
24756
|
+
if (!success)
|
|
24671
24757
|
throw new Error("failed to pay spark invoice");
|
|
24672
|
-
}
|
|
24673
24758
|
const id = success.id || success.txid || null;
|
|
24674
24759
|
await rememberTransfer?.(success.id ? success : id, { pending: true });
|
|
24675
|
-
|
|
24676
|
-
|
|
24677
|
-
|
|
24678
|
-
|
|
24679
|
-
|
|
24680
|
-
|
|
24681
|
-
markError(diag, "wallet.externalPay", startedAt, error, { type });
|
|
24682
|
-
throw error;
|
|
24760
|
+
outcome = { id, result: publicSparkInvoiceResult(success), raw: result };
|
|
24761
|
+
} else if (payment.kind === PAYMENT_KIND_BITCOIN) {
|
|
24762
|
+
outcome = await sendBitcoin(wallet, walletPK, network, payment, rememberTransfer);
|
|
24763
|
+
await updateWalletData({ reason: "bitcoin-payment" });
|
|
24764
|
+
} else {
|
|
24765
|
+
throw new Error("unsupported payment kind");
|
|
24683
24766
|
}
|
|
24767
|
+
endSdkActivity?.();
|
|
24768
|
+
markDone(diag, "wallet.payment", startedAt, { kind: diagKind, hasTransfer: !!outcome?.id });
|
|
24769
|
+
return Object.freeze({
|
|
24770
|
+
kind: payment.kind,
|
|
24771
|
+
destination: payment.destination,
|
|
24772
|
+
amountSats: payment.amountSats,
|
|
24773
|
+
id: outcome?.id || null,
|
|
24774
|
+
fees: payment.fees || null,
|
|
24775
|
+
result: outcome?.result || null,
|
|
24776
|
+
raw: outcome?.raw || null
|
|
24777
|
+
});
|
|
24778
|
+
} catch (error) {
|
|
24779
|
+
endSdkActivity?.();
|
|
24780
|
+
markError(diag, "wallet.payment", startedAt, error, { kind: diagKind });
|
|
24781
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
24782
|
+
throw new Error(`failed to send payment: ${message}`, { cause: error });
|
|
24684
24783
|
}
|
|
24685
|
-
throw new Error("unsupported invoice type");
|
|
24686
24784
|
};
|
|
24687
24785
|
return {
|
|
24688
|
-
|
|
24689
|
-
|
|
24786
|
+
preparePayment,
|
|
24787
|
+
sendPayment,
|
|
24690
24788
|
close() {
|
|
24691
24789
|
closed = true;
|
|
24692
|
-
for (const timer of refreshTimers)
|
|
24790
|
+
for (const timer of refreshTimers)
|
|
24693
24791
|
clearTimeout(timer);
|
|
24694
|
-
}
|
|
24695
24792
|
refreshTimers.clear();
|
|
24696
24793
|
}
|
|
24697
24794
|
};
|
|
24698
24795
|
}
|
|
24699
24796
|
|
|
24797
|
+
// ../../core/wallet/privacy.js
|
|
24798
|
+
var PRIVACY_SYNC_DELAY_MS = WALLET_BOOT_CACHED_REFRESH_DELAY_MS * 2;
|
|
24799
|
+
function createWalletPrivacy({ wallet, ghostWallet, diag }) {
|
|
24800
|
+
let desiredPrivacy = ghostWallet === true;
|
|
24801
|
+
let revision = 0;
|
|
24802
|
+
let closed = false;
|
|
24803
|
+
let privacySync = Promise.resolve();
|
|
24804
|
+
const waits = createOwnedWaits();
|
|
24805
|
+
const setGhostWallet = (nextGhostWallet) => {
|
|
24806
|
+
const nextDesiredPrivacy = nextGhostWallet === true;
|
|
24807
|
+
if (revision > 0 && desiredPrivacy === nextDesiredPrivacy) {
|
|
24808
|
+
return privacySync;
|
|
24809
|
+
}
|
|
24810
|
+
desiredPrivacy = nextDesiredPrivacy;
|
|
24811
|
+
const requestRevision = ++revision;
|
|
24812
|
+
if (!wallet || typeof wallet.setPrivacyEnabled !== "function" || closed) {
|
|
24813
|
+
return privacySync;
|
|
24814
|
+
}
|
|
24815
|
+
privacySync = privacySync.catch(() => {}).then(async () => {
|
|
24816
|
+
await waits.delay(PRIVACY_SYNC_DELAY_MS);
|
|
24817
|
+
if (closed || requestRevision !== revision) {
|
|
24818
|
+
return;
|
|
24819
|
+
}
|
|
24820
|
+
await waits.idle({ timeout: PRIVACY_SYNC_DELAY_MS, delay: PRIVACY_SYNC_DELAY_MS });
|
|
24821
|
+
if (closed || requestRevision !== revision) {
|
|
24822
|
+
return;
|
|
24823
|
+
}
|
|
24824
|
+
const startedAt = Date.now();
|
|
24825
|
+
const desired = desiredPrivacy;
|
|
24826
|
+
markDiag(diag, "wallet.privacy.start", { ghostWallet: desired });
|
|
24827
|
+
await wallet.setPrivacyEnabled(desired);
|
|
24828
|
+
markDone(diag, "wallet.privacy", startedAt, { changed: true });
|
|
24829
|
+
}).catch((error) => {
|
|
24830
|
+
markDiag(diag, "wallet.privacy.error", { code: error?.code || "", message: error?.message || String(error) });
|
|
24831
|
+
});
|
|
24832
|
+
return privacySync;
|
|
24833
|
+
};
|
|
24834
|
+
setGhostWallet(ghostWallet);
|
|
24835
|
+
return {
|
|
24836
|
+
setGhostWallet,
|
|
24837
|
+
close() {
|
|
24838
|
+
if (closed)
|
|
24839
|
+
return;
|
|
24840
|
+
closed = true;
|
|
24841
|
+
revision += 1;
|
|
24842
|
+
waits.close();
|
|
24843
|
+
}
|
|
24844
|
+
};
|
|
24845
|
+
}
|
|
24846
|
+
|
|
24700
24847
|
// ../../core/wallet/history.js
|
|
24701
24848
|
function transferPage(page, currentOffset, limit) {
|
|
24702
24849
|
const transfers = Array.isArray(page?.transfers) ? page.transfers : [];
|
|
@@ -24775,7 +24922,7 @@ function compareRecentTransfer(a, b) {
|
|
|
24775
24922
|
return String(b?.id || "").localeCompare(String(a?.id || ""));
|
|
24776
24923
|
}
|
|
24777
24924
|
function sameTransfer(left, right) {
|
|
24778
|
-
return left?.id === right?.id && left?.status === right?.status && txCreatedMs(left) === txCreatedMs(right) && txUpdatedMs(left) === txUpdatedMs(right) && left?.totalValue === right?.totalValue && left?.type === right?.type && left?.transferDirection === right?.transferDirection && left?.senderIdentityPublicKey === right?.senderIdentityPublicKey && left?.receiverIdentityPublicKey === right?.receiverIdentityPublicKey;
|
|
24925
|
+
return left?.id === right?.id && left?.status === right?.status && txCreatedMs(left) === txCreatedMs(right) && txUpdatedMs(left) === txUpdatedMs(right) && left?.totalValue === right?.totalValue && left?.type === right?.type && left?.transferDirection === right?.transferDirection && left?.senderIdentityPublicKey === right?.senderIdentityPublicKey && left?.receiverIdentityPublicKey === right?.receiverIdentityPublicKey && left?.bitcoinAddress === right?.bitcoinAddress && left?.bitcoinTxid === right?.bitcoinTxid;
|
|
24779
24926
|
}
|
|
24780
24927
|
function sameTransfers(a = [], b = []) {
|
|
24781
24928
|
if (a === b) {
|
|
@@ -24817,10 +24964,13 @@ function satValue(value) {
|
|
|
24817
24964
|
const amount = Number(value);
|
|
24818
24965
|
return Number.isFinite(amount) ? amount : 0;
|
|
24819
24966
|
}
|
|
24820
|
-
function compactTransfer(tx, { incoming = false } = {}) {
|
|
24967
|
+
function compactTransfer(tx, { incoming = false, bitcoinAddress = null, bitcoinTxid = null } = {}) {
|
|
24821
24968
|
if (!tx?.id) {
|
|
24822
24969
|
return null;
|
|
24823
24970
|
}
|
|
24971
|
+
const isBitcoinPayment = enumName(tx.type, TRANSFER_TYPE_BY_CODE) === "COOPERATIVE_EXIT";
|
|
24972
|
+
const storedBitcoinAddress = String(bitcoinAddress || tx.bitcoinAddress || "").trim() || null;
|
|
24973
|
+
const storedBitcoinTxid = String(bitcoinTxid || tx.bitcoinTxid || (isBitcoinPayment ? tx.userRequest?.coopExitTxid : "") || "").trim() || null;
|
|
24824
24974
|
return {
|
|
24825
24975
|
id: tx.id,
|
|
24826
24976
|
senderIdentityPublicKey: hexKey(tx.senderIdentityPublicKey),
|
|
@@ -24830,11 +24980,13 @@ function compactTransfer(tx, { incoming = false } = {}) {
|
|
|
24830
24980
|
createdTime: tx.createdTime,
|
|
24831
24981
|
updatedTime: tx.updatedTime,
|
|
24832
24982
|
type: enumName(tx.type, TRANSFER_TYPE_BY_CODE),
|
|
24833
|
-
transferDirection: tx.transferDirection || (incoming ? "INCOMING" : undefined)
|
|
24983
|
+
transferDirection: tx.transferDirection || (incoming ? "INCOMING" : undefined),
|
|
24984
|
+
...isBitcoinPayment && storedBitcoinAddress ? { bitcoinAddress: storedBitcoinAddress } : {},
|
|
24985
|
+
...isBitcoinPayment && storedBitcoinTxid ? { bitcoinTxid: storedBitcoinTxid } : {}
|
|
24834
24986
|
};
|
|
24835
24987
|
}
|
|
24836
24988
|
function compactRememberedTransfer(tx, options = {}) {
|
|
24837
|
-
const compact = compactTransfer(tx,
|
|
24989
|
+
const compact = compactTransfer(tx, options);
|
|
24838
24990
|
if (!compact) {
|
|
24839
24991
|
return null;
|
|
24840
24992
|
}
|
|
@@ -24908,12 +25060,30 @@ function getOldestAnyTransferMs(transfers = []) {
|
|
|
24908
25060
|
function shouldReplaceTransfer(current, next) {
|
|
24909
25061
|
return isPendingTransfer(current) && (!isPendingTransfer(next) || current?.status !== next?.status || txUpdatedMs(next) > txUpdatedMs(current));
|
|
24910
25062
|
}
|
|
25063
|
+
function mergeTransferDetails(current, next) {
|
|
25064
|
+
if (!current)
|
|
25065
|
+
return next;
|
|
25066
|
+
const base = shouldReplaceTransfer(current, next) ? next : current;
|
|
25067
|
+
const bitcoinAddress = next?.bitcoinAddress || current.bitcoinAddress || null;
|
|
25068
|
+
const bitcoinTxid = next?.bitcoinTxid || current.bitcoinTxid || null;
|
|
25069
|
+
if (base === current && bitcoinAddress === current?.bitcoinAddress && bitcoinTxid === current?.bitcoinTxid) {
|
|
25070
|
+
return current;
|
|
25071
|
+
}
|
|
25072
|
+
if (base === next && bitcoinAddress === next?.bitcoinAddress && bitcoinTxid === next?.bitcoinTxid) {
|
|
25073
|
+
return next;
|
|
25074
|
+
}
|
|
25075
|
+
return {
|
|
25076
|
+
...base,
|
|
25077
|
+
...bitcoinAddress ? { bitcoinAddress } : {},
|
|
25078
|
+
...bitcoinTxid ? { bitcoinTxid } : {}
|
|
25079
|
+
};
|
|
25080
|
+
}
|
|
24911
25081
|
function indexTransfersById(transfers = []) {
|
|
24912
25082
|
const byId = new Map;
|
|
24913
25083
|
for (const tx of Array.isArray(transfers) ? transfers : []) {
|
|
24914
25084
|
const id = String(tx?.id || "");
|
|
24915
25085
|
if (id) {
|
|
24916
|
-
byId.set(id, tx);
|
|
25086
|
+
byId.set(id, mergeTransferDetails(byId.get(id), tx));
|
|
24917
25087
|
}
|
|
24918
25088
|
}
|
|
24919
25089
|
return byId;
|
|
@@ -24925,7 +25095,7 @@ function hasMergeableTransferChanges(transfers = [], transferById = new Map) {
|
|
|
24925
25095
|
continue;
|
|
24926
25096
|
}
|
|
24927
25097
|
const current = transferById.get(id);
|
|
24928
|
-
if (!current ||
|
|
25098
|
+
if (!current || mergeTransferDetails(current, tx) !== current) {
|
|
24929
25099
|
return true;
|
|
24930
25100
|
}
|
|
24931
25101
|
}
|
|
@@ -24942,8 +25112,9 @@ function dedupeSortedTransfers(transfers = []) {
|
|
|
24942
25112
|
continue;
|
|
24943
25113
|
}
|
|
24944
25114
|
const current = byId.get(id);
|
|
24945
|
-
|
|
24946
|
-
|
|
25115
|
+
const merged = mergeTransferDetails(current, tx);
|
|
25116
|
+
if (!current || merged !== current) {
|
|
25117
|
+
byId.set(id, merged);
|
|
24947
25118
|
}
|
|
24948
25119
|
}
|
|
24949
25120
|
return sortRecentTransfers([...byId.values()]);
|
|
@@ -25011,11 +25182,14 @@ function mergeCompactedTransferPage(current = [], pageTransfers = [], position =
|
|
|
25011
25182
|
const updatedCurrent = replacements.size ? current.map((tx) => {
|
|
25012
25183
|
const id = String(tx?.id || "");
|
|
25013
25184
|
const replacement = id ? replacements.get(id) : null;
|
|
25014
|
-
if (!replacement
|
|
25185
|
+
if (!replacement) {
|
|
25015
25186
|
return tx;
|
|
25016
25187
|
}
|
|
25188
|
+
const merged = mergeTransferDetails(tx, replacement);
|
|
25189
|
+
if (merged === tx)
|
|
25190
|
+
return tx;
|
|
25017
25191
|
changed = true;
|
|
25018
|
-
return
|
|
25192
|
+
return merged;
|
|
25019
25193
|
}) : current;
|
|
25020
25194
|
if (!newItems.length) {
|
|
25021
25195
|
return changed ? updatedCurrent : current;
|
|
@@ -25038,7 +25212,8 @@ function mergeTransferPageForWallet(currentTransfers = [], pageTransfers = [], w
|
|
|
25038
25212
|
return mergeCompactedTransferPage(current, page, position);
|
|
25039
25213
|
}
|
|
25040
25214
|
function mergeRecentSnapshotForWallet(currentTransfers = [], latestTransfers = [], walletPK) {
|
|
25041
|
-
const
|
|
25215
|
+
const currentById = new Map(currentTransfers.map((tx) => [String(tx?.id || ""), tx]));
|
|
25216
|
+
const latest = mergeTransferPageForWallet([], latestTransfers, walletPK, "append").map((tx) => mergeTransferDetails(currentById.get(String(tx?.id || "")), tx));
|
|
25042
25217
|
if (!latest.length) {
|
|
25043
25218
|
return filterTransfersForWallet(currentTransfers, walletPK);
|
|
25044
25219
|
}
|
|
@@ -25167,7 +25342,7 @@ function pendingReconcileAgeMs(tx, now = Date.now(), firstSeenAt = null) {
|
|
|
25167
25342
|
return baseMs ? Math.max(0, now - baseMs) : null;
|
|
25168
25343
|
}
|
|
25169
25344
|
function getPendingTransferRetryMs(tx, ageMs, unresolvedCount = 0) {
|
|
25170
|
-
if (
|
|
25345
|
+
if (isBitcoinPaymentTransfer(tx)) {
|
|
25171
25346
|
if (!Number.isFinite(ageMs) || ageMs <= PENDING_TRANSFER_WARM_AGE_MS) {
|
|
25172
25347
|
return PENDING_TRANSFER_WARM_RETRY_MS;
|
|
25173
25348
|
}
|
|
@@ -27138,206 +27313,6 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
|
|
|
27138
27313
|
};
|
|
27139
27314
|
}
|
|
27140
27315
|
|
|
27141
|
-
// ../../core/wallet/withdrawops.js
|
|
27142
|
-
var P2PKH_DUST_SATS = 546;
|
|
27143
|
-
var P2SH_DUST_SATS = 540;
|
|
27144
|
-
var P2WPKH_DUST_SATS = 294;
|
|
27145
|
-
var SEGWIT_SCRIPT_DUST_SATS = 330;
|
|
27146
|
-
function getWithdrawalMinimumReceiveSats(onchainAddress) {
|
|
27147
|
-
const address = cleanText(onchainAddress).toLowerCase();
|
|
27148
|
-
if (address.startsWith("1"))
|
|
27149
|
-
return P2PKH_DUST_SATS;
|
|
27150
|
-
if (address.startsWith("3"))
|
|
27151
|
-
return P2SH_DUST_SATS;
|
|
27152
|
-
const p2wpkhLength = address.startsWith("bcrt1q") ? 44 : 42;
|
|
27153
|
-
if ((address.startsWith("bc1q") || address.startsWith("bcrt1q")) && address.length === p2wpkhLength) {
|
|
27154
|
-
return P2WPKH_DUST_SATS;
|
|
27155
|
-
}
|
|
27156
|
-
return SEGWIT_SCRIPT_DUST_SATS;
|
|
27157
|
-
}
|
|
27158
|
-
function getWithdrawalReviewAmounts(withdrawal) {
|
|
27159
|
-
const amountSats = toSafeSats(withdrawal?.amountSats);
|
|
27160
|
-
const feeAmountSats = toSafeNonNegativeSats(withdrawal?.feeAmountSats ?? 0, "feeAmountSats");
|
|
27161
|
-
const deductFeeFromWithdrawalAmount = withdrawal?.deductFeeFromWithdrawalAmount !== false;
|
|
27162
|
-
const receiveAmountSats = deductFeeFromWithdrawalAmount ? Math.max(0, amountSats - feeAmountSats) : amountSats;
|
|
27163
|
-
const sendAmountSats = deductFeeFromWithdrawalAmount ? amountSats : amountSats + feeAmountSats;
|
|
27164
|
-
return {
|
|
27165
|
-
sendAmountSats,
|
|
27166
|
-
receiveAmountSats,
|
|
27167
|
-
feeAmountSats
|
|
27168
|
-
};
|
|
27169
|
-
}
|
|
27170
|
-
function assertWithdrawalReceivesFunds(withdrawal) {
|
|
27171
|
-
const { receiveAmountSats } = getWithdrawalReviewAmounts(withdrawal);
|
|
27172
|
-
const minimumReceiveAmountSats = getWithdrawalMinimumReceiveSats(withdrawal?.onchainAddress);
|
|
27173
|
-
if (receiveAmountSats < minimumReceiveAmountSats) {
|
|
27174
|
-
throw new Error(`withdrawal would create a dust output below ${minimumReceiveAmountSats} sats`);
|
|
27175
|
-
}
|
|
27176
|
-
}
|
|
27177
|
-
function getWithdrawalReview({ feeQuote, exitSpeed, amountSats, onchainAddress, deductFeeFromWithdrawalAmount = true }) {
|
|
27178
|
-
const safeExitSpeed = getExitSpeed(exitSpeed);
|
|
27179
|
-
const feeQuoteId = feeQuote?.id ?? null;
|
|
27180
|
-
const feeAmountSats = getWithdrawalFeeAmountSats(feeQuote, safeExitSpeed);
|
|
27181
|
-
if (!feeQuoteId || feeAmountSats == null) {
|
|
27182
|
-
throw new Error("withdrawal fee quote unavailable");
|
|
27183
|
-
}
|
|
27184
|
-
const sparkFees = normalizeWithdrawalFeeQuote(feeQuote);
|
|
27185
|
-
const withdrawal = {
|
|
27186
|
-
kind: "cooperative_exit",
|
|
27187
|
-
onchainAddress,
|
|
27188
|
-
amountSats,
|
|
27189
|
-
exitSpeed: safeExitSpeed,
|
|
27190
|
-
deductFeeFromWithdrawalAmount,
|
|
27191
|
-
feeQuote,
|
|
27192
|
-
feeQuoteId,
|
|
27193
|
-
feeAmountSats,
|
|
27194
|
-
fees: {
|
|
27195
|
-
spark: sparkFees
|
|
27196
|
-
},
|
|
27197
|
-
sparkFees,
|
|
27198
|
-
expiresAt: sparkFees?.expiresAt ?? feeQuote.expiresAt ?? null
|
|
27199
|
-
};
|
|
27200
|
-
assertWithdrawalReceivesFunds(withdrawal);
|
|
27201
|
-
return withdrawal;
|
|
27202
|
-
}
|
|
27203
|
-
async function quoteWithdrawalFees(wallet, network, { onchainAddress, amountSats } = {}) {
|
|
27204
|
-
if (!wallet) {
|
|
27205
|
-
return { success: false, error: new Error("wallet not ready") };
|
|
27206
|
-
}
|
|
27207
|
-
const address = cleanText(onchainAddress);
|
|
27208
|
-
if (!isAddressOnNetwork(address, network)) {
|
|
27209
|
-
return { success: false, error: new Error(`refusing to withdraw - address is not a ${network} address`) };
|
|
27210
|
-
}
|
|
27211
|
-
try {
|
|
27212
|
-
const safeAmountSats = toSafeSats(amountSats);
|
|
27213
|
-
const feeQuote = await wallet.getWithdrawalFeeQuote({
|
|
27214
|
-
amountSats: safeAmountSats,
|
|
27215
|
-
withdrawalAddress: address
|
|
27216
|
-
});
|
|
27217
|
-
if (!feeQuote?.id) {
|
|
27218
|
-
throw new Error("withdrawal fee quote unavailable");
|
|
27219
|
-
}
|
|
27220
|
-
const sparkFees = normalizeWithdrawalFeeQuote(feeQuote);
|
|
27221
|
-
return {
|
|
27222
|
-
success: true,
|
|
27223
|
-
feeQuote,
|
|
27224
|
-
fees: {
|
|
27225
|
-
spark: sparkFees
|
|
27226
|
-
},
|
|
27227
|
-
sparkFees,
|
|
27228
|
-
amountSats: safeAmountSats,
|
|
27229
|
-
onchainAddress: address
|
|
27230
|
-
};
|
|
27231
|
-
} catch (error) {
|
|
27232
|
-
return { success: false, error };
|
|
27233
|
-
}
|
|
27234
|
-
}
|
|
27235
|
-
async function prepareWithdrawal(wallet, network, { onchainAddress, amountSats, exitSpeed = DEFAULT_EXIT_SPEED, deductFeeFromWithdrawalAmount = true } = {}) {
|
|
27236
|
-
const quoted = await quoteWithdrawalFees(wallet, network, { onchainAddress, amountSats });
|
|
27237
|
-
if (!quoted.success) {
|
|
27238
|
-
return quoted;
|
|
27239
|
-
}
|
|
27240
|
-
try {
|
|
27241
|
-
const withdrawal = getWithdrawalReview({
|
|
27242
|
-
feeQuote: quoted.feeQuote,
|
|
27243
|
-
exitSpeed,
|
|
27244
|
-
amountSats: quoted.amountSats,
|
|
27245
|
-
onchainAddress: quoted.onchainAddress,
|
|
27246
|
-
deductFeeFromWithdrawalAmount
|
|
27247
|
-
});
|
|
27248
|
-
return {
|
|
27249
|
-
success: true,
|
|
27250
|
-
withdrawal,
|
|
27251
|
-
...withdrawal
|
|
27252
|
-
};
|
|
27253
|
-
} catch (error) {
|
|
27254
|
-
return { success: false, error };
|
|
27255
|
-
}
|
|
27256
|
-
}
|
|
27257
|
-
async function withdrawFunds(wallet, network, { onchainAddress, amountSats, exitSpeed = DEFAULT_EXIT_SPEED, feeQuote = null, feeQuoteId = null, feeAmountSats = null, deductFeeFromWithdrawalAmount = true } = {}) {
|
|
27258
|
-
if (!wallet) {
|
|
27259
|
-
return { success: false, error: new Error("wallet not ready") };
|
|
27260
|
-
}
|
|
27261
|
-
const address = cleanText(onchainAddress);
|
|
27262
|
-
if (!isAddressOnNetwork(address, network)) {
|
|
27263
|
-
return { success: false, error: new Error(`refusing to withdraw - address is not a ${network} address`) };
|
|
27264
|
-
}
|
|
27265
|
-
try {
|
|
27266
|
-
const safeAmountSats = toSafeSats(amountSats);
|
|
27267
|
-
const safeExitSpeed = getExitSpeed(exitSpeed);
|
|
27268
|
-
let readyFeeQuote = feeQuote;
|
|
27269
|
-
if (!readyFeeQuote && (!feeQuoteId || feeAmountSats == null)) {
|
|
27270
|
-
const quoted = await quoteWithdrawalFees(wallet, network, {
|
|
27271
|
-
onchainAddress: address,
|
|
27272
|
-
amountSats: safeAmountSats
|
|
27273
|
-
});
|
|
27274
|
-
if (!quoted.success) {
|
|
27275
|
-
return quoted;
|
|
27276
|
-
}
|
|
27277
|
-
readyFeeQuote = quoted.feeQuote;
|
|
27278
|
-
}
|
|
27279
|
-
const readyFeeQuoteId = feeQuoteId || readyFeeQuote?.id;
|
|
27280
|
-
const readyFeeAmountSats = feeAmountSats == null ? getWithdrawalFeeAmountSats(readyFeeQuote, safeExitSpeed) : toSafeNonNegativeSats(feeAmountSats, "feeAmountSats");
|
|
27281
|
-
if (!readyFeeQuoteId || readyFeeAmountSats == null) {
|
|
27282
|
-
throw new Error("withdrawal fee quote unavailable");
|
|
27283
|
-
}
|
|
27284
|
-
assertWithdrawalReceivesFunds({
|
|
27285
|
-
onchainAddress: address,
|
|
27286
|
-
amountSats: safeAmountSats,
|
|
27287
|
-
feeAmountSats: readyFeeAmountSats,
|
|
27288
|
-
deductFeeFromWithdrawalAmount
|
|
27289
|
-
});
|
|
27290
|
-
const tx = await wallet.withdraw({
|
|
27291
|
-
onchainAddress: address,
|
|
27292
|
-
amountSats: safeAmountSats,
|
|
27293
|
-
exitSpeed: safeExitSpeed,
|
|
27294
|
-
feeQuoteId: readyFeeQuoteId,
|
|
27295
|
-
feeAmountSats: readyFeeAmountSats,
|
|
27296
|
-
deductFeeFromWithdrawalAmount
|
|
27297
|
-
});
|
|
27298
|
-
return {
|
|
27299
|
-
success: true,
|
|
27300
|
-
tx,
|
|
27301
|
-
feeQuote: readyFeeQuote,
|
|
27302
|
-
fees: readyFeeQuote ? {
|
|
27303
|
-
spark: normalizeWithdrawalFeeQuote(readyFeeQuote)
|
|
27304
|
-
} : null,
|
|
27305
|
-
feeAmountSats: readyFeeAmountSats
|
|
27306
|
-
};
|
|
27307
|
-
} catch (error) {
|
|
27308
|
-
return { success: false, error };
|
|
27309
|
-
}
|
|
27310
|
-
}
|
|
27311
|
-
async function confirmWithdrawal(wallet, network, withdrawal) {
|
|
27312
|
-
if (!withdrawal) {
|
|
27313
|
-
return { success: false, error: new Error("withdrawal review missing") };
|
|
27314
|
-
}
|
|
27315
|
-
return withdrawFunds(wallet, network, {
|
|
27316
|
-
onchainAddress: withdrawal.onchainAddress,
|
|
27317
|
-
amountSats: withdrawal.amountSats,
|
|
27318
|
-
exitSpeed: withdrawal.exitSpeed,
|
|
27319
|
-
feeQuoteId: withdrawal.feeQuoteId,
|
|
27320
|
-
feeAmountSats: withdrawal.feeAmountSats,
|
|
27321
|
-
deductFeeFromWithdrawalAmount: withdrawal.deductFeeFromWithdrawalAmount
|
|
27322
|
-
});
|
|
27323
|
-
}
|
|
27324
|
-
|
|
27325
|
-
// ../../core/wallet/withdraw.js
|
|
27326
|
-
function createWithdrawal({ wallet, network, updateWalletData }) {
|
|
27327
|
-
const refreshOnSuccess = async (result) => {
|
|
27328
|
-
if (result.success) {
|
|
27329
|
-
await updateWalletData({ reason: "withdraw" });
|
|
27330
|
-
}
|
|
27331
|
-
return result;
|
|
27332
|
-
};
|
|
27333
|
-
return {
|
|
27334
|
-
quoteWithdrawalFees: (params) => quoteWithdrawalFees(wallet, network, params),
|
|
27335
|
-
prepareWithdrawal: (params) => prepareWithdrawal(wallet, network, params),
|
|
27336
|
-
confirmWithdrawal: async (withdrawal) => refreshOnSuccess(await confirmWithdrawal(wallet, network, withdrawal)),
|
|
27337
|
-
withdrawFunds: async (params) => refreshOnSuccess(await withdrawFunds(wallet, network, params))
|
|
27338
|
-
};
|
|
27339
|
-
}
|
|
27340
|
-
|
|
27341
27316
|
// ../../core/wallet/session.js
|
|
27342
27317
|
var NOOP5 = () => {};
|
|
27343
27318
|
var ASYNC_NOOP = async () => null;
|
|
@@ -27396,18 +27371,12 @@ function createEmptyWalletSessionSnapshot(network, transferSnapshot = null, cach
|
|
|
27396
27371
|
requestTransferDiscoveryHotWindow: NOOP5,
|
|
27397
27372
|
claimDeposits: ASYNC_FALSE,
|
|
27398
27373
|
getFundingAddress: ASYNC_NOOP,
|
|
27399
|
-
|
|
27400
|
-
|
|
27374
|
+
preparePayment: WALLET_NOT_READY,
|
|
27375
|
+
sendPayment: WALLET_NOT_READY,
|
|
27401
27376
|
createLightningInvoice: WALLET_NOT_READY,
|
|
27402
|
-
quoteLightningFees: WALLET_NOT_READY,
|
|
27403
|
-
sendLightningPayment: WALLET_NOT_READY,
|
|
27404
27377
|
getLightningReceiveRequest: WALLET_NOT_READY,
|
|
27405
27378
|
getLightningSendRequest: WALLET_NOT_READY,
|
|
27406
|
-
|
|
27407
|
-
prepareWithdrawal: WALLET_NOT_READY,
|
|
27408
|
-
confirmWithdrawal: WALLET_NOT_READY,
|
|
27409
|
-
withdrawFunds: WALLET_NOT_READY,
|
|
27410
|
-
runWalletMutation: WALLET_NOT_READY
|
|
27379
|
+
runPayment: WALLET_NOT_READY
|
|
27411
27380
|
},
|
|
27412
27381
|
txValue: {
|
|
27413
27382
|
oldestTxMs: null,
|
|
@@ -27538,8 +27507,9 @@ function createWalletSession({
|
|
|
27538
27507
|
diag
|
|
27539
27508
|
});
|
|
27540
27509
|
const transferState = getTransferState();
|
|
27541
|
-
const
|
|
27510
|
+
const payment = createPayment({
|
|
27542
27511
|
wallet,
|
|
27512
|
+
walletPK,
|
|
27543
27513
|
network,
|
|
27544
27514
|
updateWalletData,
|
|
27545
27515
|
refreshBalance: balance.getBalance,
|
|
@@ -27549,28 +27519,15 @@ function createWalletSession({
|
|
|
27549
27519
|
beginSdkActivity: transferState.beginSdkActivity,
|
|
27550
27520
|
diag
|
|
27551
27521
|
});
|
|
27552
|
-
const lightning = createLightning({ wallet
|
|
27553
|
-
const withdrawal = createWithdrawal({ wallet, network, updateWalletData });
|
|
27522
|
+
const lightning = createLightning({ wallet });
|
|
27554
27523
|
const privacy = createWalletPrivacy({ wallet, ghostWallet, diag });
|
|
27555
27524
|
const mutations = createWalletMutationQueue();
|
|
27556
|
-
const sendMoneyWithSpark = (receiverWalletPK, ...args) => {
|
|
27557
|
-
if (sameText(receiverWalletPK, walletPK)) {
|
|
27558
|
-
throw new Error("cannot send money to the same wallet");
|
|
27559
|
-
}
|
|
27560
|
-
return send.sendMoneyWithSpark(receiverWalletPK, ...args);
|
|
27561
|
-
};
|
|
27562
27525
|
const mutationActions = {
|
|
27563
|
-
|
|
27564
|
-
|
|
27565
|
-
|
|
27566
|
-
|
|
27567
|
-
|
|
27568
|
-
runWalletMutation: (task) => mutations.run(() => task({
|
|
27569
|
-
sendMoneyWithSpark,
|
|
27570
|
-
payExternalInvoice: send.payExternalInvoice,
|
|
27571
|
-
sendLightningPayment: lightning.sendLightningPayment,
|
|
27572
|
-
confirmWithdrawal: withdrawal.confirmWithdrawal,
|
|
27573
|
-
withdrawFunds: withdrawal.withdrawFunds
|
|
27526
|
+
preparePayment: (...args) => mutations.run(() => payment.preparePayment(...args)),
|
|
27527
|
+
sendPayment: (...args) => mutations.run(() => payment.sendPayment(...args)),
|
|
27528
|
+
runPayment: (task) => mutations.run(() => task({
|
|
27529
|
+
preparePayment: payment.preparePayment,
|
|
27530
|
+
sendPayment: payment.sendPayment
|
|
27574
27531
|
}))
|
|
27575
27532
|
};
|
|
27576
27533
|
function buildSnapshot() {
|
|
@@ -27592,18 +27549,12 @@ function createWalletSession({
|
|
|
27592
27549
|
requestTransferDiscoveryHotWindow: polling.requestTransferDiscoveryHotWindow,
|
|
27593
27550
|
claimDeposits: claims.claimDeposits,
|
|
27594
27551
|
getFundingAddress: funding.getFundingAddress,
|
|
27595
|
-
|
|
27596
|
-
|
|
27552
|
+
preparePayment: mutationActions.preparePayment,
|
|
27553
|
+
sendPayment: mutationActions.sendPayment,
|
|
27597
27554
|
createLightningInvoice: lightning.createLightningInvoice,
|
|
27598
|
-
quoteLightningFees: lightning.quoteLightningFees,
|
|
27599
|
-
sendLightningPayment: mutationActions.sendLightningPayment,
|
|
27600
27555
|
getLightningReceiveRequest: lightning.getLightningReceiveRequest,
|
|
27601
27556
|
getLightningSendRequest: lightning.getLightningSendRequest,
|
|
27602
|
-
|
|
27603
|
-
prepareWithdrawal: withdrawal.prepareWithdrawal,
|
|
27604
|
-
confirmWithdrawal: mutationActions.confirmWithdrawal,
|
|
27605
|
-
withdrawFunds: mutationActions.withdrawFunds,
|
|
27606
|
-
runWalletMutation: mutationActions.runWalletMutation
|
|
27557
|
+
runPayment: mutationActions.runPayment
|
|
27607
27558
|
};
|
|
27608
27559
|
const txValue = {
|
|
27609
27560
|
oldestTxMs: currentTransferState.oldestLoadedMs,
|
|
@@ -27666,7 +27617,7 @@ function createWalletSession({
|
|
|
27666
27617
|
polling.close();
|
|
27667
27618
|
privacy.close();
|
|
27668
27619
|
mutations.close();
|
|
27669
|
-
|
|
27620
|
+
payment.close();
|
|
27670
27621
|
claims.close();
|
|
27671
27622
|
funding.close();
|
|
27672
27623
|
updateWalletData.close();
|
|
@@ -28009,7 +27960,7 @@ function coveredUnitsSince(oldestLoadedMs, unitMs, nowMs2 = Date.now()) {
|
|
|
28009
27960
|
}
|
|
28010
27961
|
var enrichTx = (tx) => {
|
|
28011
27962
|
const incoming = tx.transferDirection === "INCOMING";
|
|
28012
|
-
const
|
|
27963
|
+
const isBitcoinPayment = isBitcoinPaymentTransfer(tx);
|
|
28013
27964
|
const isFunding = isFundingTransfer(tx);
|
|
28014
27965
|
const pending = !isCompletedTransfer(tx);
|
|
28015
27966
|
const peerPK = incoming ? tx.senderIdentityPublicKey : tx.receiverIdentityPublicKey;
|
|
@@ -28021,10 +27972,10 @@ var enrichTx = (tx) => {
|
|
|
28021
27972
|
totalValue: tx.totalValue,
|
|
28022
27973
|
amount: incoming ? tx.totalValue : -tx.totalValue,
|
|
28023
27974
|
funding: isFunding,
|
|
28024
|
-
|
|
27975
|
+
bitcoin: isBitcoinPayment,
|
|
28025
27976
|
pending,
|
|
28026
27977
|
pendingCredit: pending && (incoming || isFunding),
|
|
28027
|
-
summaryPending: pending && (incoming || isFunding ||
|
|
27978
|
+
summaryPending: pending && (incoming || isFunding || isBitcoinPayment)
|
|
28028
27979
|
};
|
|
28029
27980
|
};
|
|
28030
27981
|
var aggregateTxs = (transfers, userPK) => {
|
|
@@ -28057,7 +28008,7 @@ var aggregateTxs = (transfers, userPK) => {
|
|
|
28057
28008
|
}
|
|
28058
28009
|
if (!firstDate || txDate < firstDate)
|
|
28059
28010
|
firstDate = txDate;
|
|
28060
|
-
if (tx.peerPK && tx.peerPK !== userPK && !tx.funding && !tx.
|
|
28011
|
+
if (tx.peerPK && tx.peerPK !== userPK && !tx.funding && !tx.bitcoin) {
|
|
28061
28012
|
const txMs = txDate.getTime();
|
|
28062
28013
|
if (!peerMap.has(tx.peerPK)) {
|
|
28063
28014
|
peerMap.set(tx.peerPK, {
|
|
@@ -28164,7 +28115,9 @@ function txSearchFields(tx) {
|
|
|
28164
28115
|
tx?.amount == null ? "" : String(tx.amount),
|
|
28165
28116
|
tx?.incoming ? "incoming received inflow" : "outgoing sent outflow",
|
|
28166
28117
|
tx?.funding ? "funded fund deposit" : "",
|
|
28167
|
-
tx?.
|
|
28118
|
+
tx?.bitcoin ? "bitcoin payment onchain on-chain l1" : "",
|
|
28119
|
+
tx?.bitcoinAddress,
|
|
28120
|
+
tx?.bitcoinTxid,
|
|
28168
28121
|
tx?.pending ? "pending" : "completed"
|
|
28169
28122
|
];
|
|
28170
28123
|
}
|
|
@@ -28380,7 +28333,7 @@ function getRecentWalletPeers(transfers, walletPK) {
|
|
|
28380
28333
|
continue;
|
|
28381
28334
|
}
|
|
28382
28335
|
const tx = enrichTx(raw);
|
|
28383
|
-
if (!isVisibleTransfer(tx) || tx.funding || tx.
|
|
28336
|
+
if (!isVisibleTransfer(tx) || tx.funding || tx.bitcoin || !tx.peerPK || tx.peerPK === walletPK) {
|
|
28384
28337
|
continue;
|
|
28385
28338
|
}
|
|
28386
28339
|
const txMs = tx.createdMs || 0;
|
|
@@ -28419,7 +28372,7 @@ function getPeerDataFromTransfers(transfers, walletPK, peerPK) {
|
|
|
28419
28372
|
if (rawPeerPK !== peerPK)
|
|
28420
28373
|
continue;
|
|
28421
28374
|
const tx = enrichTx(raw);
|
|
28422
|
-
if (tx.funding || tx.
|
|
28375
|
+
if (tx.funding || tx.bitcoin) {
|
|
28423
28376
|
continue;
|
|
28424
28377
|
}
|
|
28425
28378
|
txs.push(tx);
|