@rozoai/checkout 0.1.22 → 0.1.24
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/README.md +7 -2
- package/docs/QUICKSTART.es.md +1 -1
- package/docs/QUICKSTART.ja.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/docs/QUICKSTART.zh.md +1 -1
- package/docs/README.es.md +1 -1
- package/docs/README.ja.md +1 -1
- package/docs/README.zh.md +1 -1
- package/llms.txt +10 -2
- package/package.json +1 -1
- package/scripts/dist/cli.js +432 -57
- package/scripts/dist/create-order.js +304 -29
- package/scripts/dist/quote.js +104 -8
- package/scripts/dist/receipt.js +56 -9
- package/scripts/dist/resume.js +176 -20
- package/scripts/dist/send-evm.js +132 -7
- package/scripts/dist/send-sol.js +132 -7
- package/scripts/dist/status.js +56 -9
package/scripts/dist/quote.js
CHANGED
|
@@ -301,9 +301,15 @@ var MPP_BASE = process.env.ROZO_CHECKOUT_MPP_BASE || "https://apiserver.mpproute
|
|
|
301
301
|
var CLIENT_LABEL = `rozo-checkout-cli/${PKG_VERSION}`;
|
|
302
302
|
var ATTRIBUTION_CLIENT = `rozo-checkout-skill/${PKG_VERSION}`;
|
|
303
303
|
var INTENTS_BASE = process.env.ROZO_CHECKOUT_INTENTS_BASE || "https://intentapiv4.rozo.ai/functions/v1/payment-api";
|
|
304
|
-
async function quoteInvoice({ url, linkId }) {
|
|
304
|
+
async function quoteInvoice({ url, linkId, beta }) {
|
|
305
305
|
const body = url ? { url } : { payment_id: linkId };
|
|
306
|
-
return postJson(`${MPP_BASE}/quote-invoice`, body);
|
|
306
|
+
return postJson(withBeta(`${MPP_BASE}/quote-invoice`, beta), body);
|
|
307
|
+
}
|
|
308
|
+
function withBeta(url, beta) {
|
|
309
|
+
if (!beta) return url;
|
|
310
|
+
const u = new URL(url);
|
|
311
|
+
u.searchParams.set("beta", beta);
|
|
312
|
+
return u.toString();
|
|
307
313
|
}
|
|
308
314
|
function snapshotFromQuote(quote) {
|
|
309
315
|
const cb = quote?.coinbasePayment || quote?.paymentLink || null;
|
|
@@ -333,7 +339,8 @@ var CHAIN_NAMES = {
|
|
|
333
339
|
42161: "Arbitrum",
|
|
334
340
|
900: "Solana",
|
|
335
341
|
1500: "Stellar",
|
|
336
|
-
lightning: "Bitcoin Lightning"
|
|
342
|
+
lightning: "Bitcoin Lightning",
|
|
343
|
+
9133: "Zcash"
|
|
337
344
|
};
|
|
338
345
|
var SUPPORTED_SOURCES = [
|
|
339
346
|
{ chainId: "1", chain: "Ethereum", tokens: ["USDC", "USDT", "ETH"] },
|
|
@@ -343,7 +350,9 @@ var SUPPORTED_SOURCES = [
|
|
|
343
350
|
{ chainId: "8453", chain: "Base", tokens: ["USDC", "ETH"] },
|
|
344
351
|
{ chainId: "42161", chain: "Arbitrum", tokens: ["USDC", "USDT", "ETH"] },
|
|
345
352
|
{ chainId: "1500", chain: "Stellar", tokens: ["USDC"] },
|
|
346
|
-
{ chainId: "lightning", chain: "Bitcoin Lightning", tokens: ["BTC"] }
|
|
353
|
+
{ chainId: "lightning", chain: "Bitcoin Lightning", tokens: ["BTC"] },
|
|
354
|
+
// ZEC (Beta): shielded orders only, offered per invoice by the router (see lib/zec.mjs).
|
|
355
|
+
{ chainId: "9133", chain: "Zcash", tokens: ["ZEC"] }
|
|
347
356
|
];
|
|
348
357
|
var NATIVE_COINS = Object.freeze({
|
|
349
358
|
"1:ETH": { chainId: "1", symbol: "ETH", decimals: 18 },
|
|
@@ -353,7 +362,7 @@ var NATIVE_COINS = Object.freeze({
|
|
|
353
362
|
"137:POL": { chainId: "137", symbol: "POL", decimals: 18 },
|
|
354
363
|
"900:SOL": { chainId: "900", symbol: "SOL", decimals: 9 }
|
|
355
364
|
});
|
|
356
|
-
var NATIVE_MAX_USD =
|
|
365
|
+
var NATIVE_MAX_USD = 3500;
|
|
357
366
|
var NATIVE_MIN_USD_FALLBACK = Object.freeze({ "1:ETH": 1 });
|
|
358
367
|
function decimalsKey(chainId, tokenSymbol) {
|
|
359
368
|
return `${String(chainId).trim()}:${String(tokenSymbol || "").trim().toUpperCase()}`;
|
|
@@ -434,7 +443,9 @@ var MARGINS_MS = {
|
|
|
434
443
|
42161: 10 * MINUTE,
|
|
435
444
|
900: 5 * MINUTE,
|
|
436
445
|
1500: 10 * MINUTE,
|
|
437
|
-
lightning: 10 * MINUTE
|
|
446
|
+
lightning: 10 * MINUTE,
|
|
447
|
+
// Zcash: 75 s blocks, 1 to 2 confirmations, then the hub advances the merchant.
|
|
448
|
+
9133: 10 * MINUTE
|
|
438
449
|
};
|
|
439
450
|
var BOLT11_MIN_VALIDITY_MS = 10 * MINUTE;
|
|
440
451
|
var DEFAULT_MARGIN_MS = 10 * MINUTE;
|
|
@@ -454,6 +465,69 @@ function parseDeadline(value) {
|
|
|
454
465
|
return Number.isFinite(t) ? t : null;
|
|
455
466
|
}
|
|
456
467
|
|
|
468
|
+
// scripts/src/lib/zec.mjs
|
|
469
|
+
var ZEC_CHAIN_ID = "9133";
|
|
470
|
+
var ZEC_SYMBOL = "ZEC";
|
|
471
|
+
var ZEC_SOURCE_META_KEY = "ZEC@9133";
|
|
472
|
+
var ZEC_BETA_PARAM = "zec,zec_shielded";
|
|
473
|
+
var ZEC_SHIELDED_MAX_USD = 1200;
|
|
474
|
+
var ZEC_MIN_USD = 5;
|
|
475
|
+
var ZEC_TWO_CONF_FROM_USD = 110;
|
|
476
|
+
function isZecSource(chainId, tokenSymbol) {
|
|
477
|
+
return String(chainId ?? "").trim() === ZEC_CHAIN_ID && String(tokenSymbol ?? "").trim().toUpperCase() === ZEC_SYMBOL;
|
|
478
|
+
}
|
|
479
|
+
function zecRequiredConfirmations(usd) {
|
|
480
|
+
const n = Number(usd);
|
|
481
|
+
return Number.isFinite(n) && n >= ZEC_TWO_CONF_FROM_USD ? 2 : 1;
|
|
482
|
+
}
|
|
483
|
+
function zecShieldedOfferFor({ supportedSources, sourceMeta, callerPaysUsd }) {
|
|
484
|
+
const list = supportedSources && typeof supportedSources === "object" ? supportedSources[ZEC_CHAIN_ID] : null;
|
|
485
|
+
if (!Array.isArray(list) || !list.map((t) => String(t).toUpperCase()).includes(ZEC_SYMBOL)) {
|
|
486
|
+
return {
|
|
487
|
+
ok: false,
|
|
488
|
+
code: "ZEC_NOT_OFFERED",
|
|
489
|
+
reason: "ZEC is not offered for this invoice. Pay with USDC or USDT instead.",
|
|
490
|
+
requiredConfirmations: null
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
const meta = sourceMeta && typeof sourceMeta === "object" ? sourceMeta[ZEC_SOURCE_META_KEY] : null;
|
|
494
|
+
const available = meta && typeof meta === "object" && (meta.shieldedAvailable === true || meta.shielded_available === true);
|
|
495
|
+
if (!available) {
|
|
496
|
+
return {
|
|
497
|
+
ok: false,
|
|
498
|
+
code: "ZEC_SHIELDED_NOT_AVAILABLE",
|
|
499
|
+
reason: "Paying from a shielded balance is not available for this invoice right now.",
|
|
500
|
+
requiredConfirmations: null
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
const usd = Number(callerPaysUsd);
|
|
504
|
+
if (!Number.isFinite(usd) || usd <= 0) {
|
|
505
|
+
return {
|
|
506
|
+
ok: false,
|
|
507
|
+
code: "ZEC_AMOUNT_UNKNOWN",
|
|
508
|
+
reason: "The quote carries no USD amount, so the shielded ZEC limits cannot be checked. Pay with USDC or USDT instead.",
|
|
509
|
+
requiredConfirmations: null
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
if (usd > ZEC_SHIELDED_MAX_USD) {
|
|
513
|
+
return {
|
|
514
|
+
ok: false,
|
|
515
|
+
code: "ZEC_AMOUNT_TOO_HIGH",
|
|
516
|
+
reason: `Shielded ZEC is limited to $${ZEC_SHIELDED_MAX_USD} per payment during the beta; this invoice is $${usd}. Pay with USDC or USDT instead.`,
|
|
517
|
+
requiredConfirmations: null
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
if (usd < ZEC_MIN_USD) {
|
|
521
|
+
return {
|
|
522
|
+
ok: false,
|
|
523
|
+
code: "ZEC_AMOUNT_TOO_LOW",
|
|
524
|
+
reason: `ZEC payments need an invoice of at least $${ZEC_MIN_USD}; this one is $${usd}. Pay with another coin.`,
|
|
525
|
+
requiredConfirmations: null
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
return { ok: true, code: null, reason: null, requiredConfirmations: zecRequiredConfirmations(usd) };
|
|
529
|
+
}
|
|
530
|
+
|
|
457
531
|
// scripts/src/lib/guards.mjs
|
|
458
532
|
function normalizeDecimal(v) {
|
|
459
533
|
if (v === null || v === void 0) return null;
|
|
@@ -529,7 +603,7 @@ async function main(argv) {
|
|
|
529
603
|
chosenSource = { chainId, tokenSymbol };
|
|
530
604
|
}
|
|
531
605
|
const now = Date.now();
|
|
532
|
-
const quote = await quoteInvoice({ url: String(url) });
|
|
606
|
+
const quote = await quoteInvoice({ url: String(url), beta: ZEC_BETA_PARAM });
|
|
533
607
|
const snapshot = snapshotFromQuote(quote);
|
|
534
608
|
const payability = derivePayable(snapshot, now);
|
|
535
609
|
const nativeOffered = Object.values(NATIVE_COINS).map((c) => ({
|
|
@@ -544,6 +618,20 @@ async function main(argv) {
|
|
|
544
618
|
callerPaysUsd: snapshot.callerPays ?? snapshot.original
|
|
545
619
|
})
|
|
546
620
|
})).filter((c) => c.offer.ok).map((c) => ({ chainId: c.chainId, tokenSymbol: c.tokenSymbol, chain: c.chain, minUsd: c.offer.minUsd }));
|
|
621
|
+
const zecOffer = zecShieldedOfferFor({
|
|
622
|
+
supportedSources: quote?.supportedSources,
|
|
623
|
+
sourceMeta: quote?.sourceMeta,
|
|
624
|
+
callerPaysUsd: snapshot.callerPays ?? snapshot.original
|
|
625
|
+
});
|
|
626
|
+
const zecShielded = {
|
|
627
|
+
offered: zecOffer.ok,
|
|
628
|
+
code: zecOffer.code,
|
|
629
|
+
reason: zecOffer.reason,
|
|
630
|
+
requiredConfirmations: zecOffer.requiredConfirmations,
|
|
631
|
+
maxUsd: ZEC_SHIELDED_MAX_USD,
|
|
632
|
+
note: "ZEC (Beta) is paid from a shielded wallet, exact amount in one transaction; the CLI shows the address, amount and payment URI and never sends. Underpayments are not refunded automatically."
|
|
633
|
+
};
|
|
634
|
+
const zecRequested = chosenSource && isZecSource(chosenSource.chainId, chosenSource.tokenSymbol) ? zecOffer : null;
|
|
547
635
|
const nativeRequested = chosenSource && isNativeSource(chosenSource.chainId, chosenSource.tokenSymbol) ? nativeOfferFor({
|
|
548
636
|
...chosenSource,
|
|
549
637
|
supportedSources: quote?.supportedSources,
|
|
@@ -572,7 +660,8 @@ async function main(argv) {
|
|
|
572
660
|
// and the native coins it makes payable for this invoice.
|
|
573
661
|
routerSupportedSources: quote?.supportedSources && typeof quote.supportedSources === "object" ? quote.supportedSources : null,
|
|
574
662
|
nativeOffered,
|
|
575
|
-
|
|
663
|
+
zecShielded,
|
|
664
|
+
nativeNote: "Native coins are exact-out: the coin amount is fixed at order creation and its price is locked only until quoteExpiresAt. Up to $3,500 per invoice; native ETH on Ethereum needs $1.",
|
|
576
665
|
quoteReceipt: quote?.quoteReceipt ?? null,
|
|
577
666
|
quoteReceiptTtlSeconds: 60,
|
|
578
667
|
quoteReceiptNote: "Short-lived (~60s). create-order.js takes its own fresh quote; do not carry this over.",
|
|
@@ -589,6 +678,13 @@ async function main(argv) {
|
|
|
589
678
|
payload.error = { code: nativeRequested.code, message: nativeRequested.reason };
|
|
590
679
|
emit(payload, EXIT_ERROR);
|
|
591
680
|
}
|
|
681
|
+
if (zecRequested && !zecRequested.ok) {
|
|
682
|
+
payload.success = false;
|
|
683
|
+
payload.nextStep = null;
|
|
684
|
+
payload.error = { code: zecRequested.code, message: zecRequested.reason };
|
|
685
|
+
emit(payload, EXIT_ERROR);
|
|
686
|
+
}
|
|
687
|
+
if (zecRequested) payload.nextStep = "create-order.js --url <url> --chain 9133 --token ZEC --shielded";
|
|
592
688
|
if (snapshot.callerPays && snapshot.original && normalizeDecimal(snapshot.callerPays) !== normalizeDecimal(snapshot.original)) {
|
|
593
689
|
payload.warnings = [
|
|
594
690
|
`callerPays (${snapshot.callerPays}) differs from the invoice amount (${snapshot.original}). This flow is supposed to charge the full invoice \u2014 do not proceed until that is explained.`
|
package/scripts/dist/receipt.js
CHANGED
|
@@ -360,7 +360,8 @@ var CHAIN_NAMES = {
|
|
|
360
360
|
42161: "Arbitrum",
|
|
361
361
|
900: "Solana",
|
|
362
362
|
1500: "Stellar",
|
|
363
|
-
lightning: "Bitcoin Lightning"
|
|
363
|
+
lightning: "Bitcoin Lightning",
|
|
364
|
+
9133: "Zcash"
|
|
364
365
|
};
|
|
365
366
|
var NATIVE_COINS = Object.freeze({
|
|
366
367
|
"1:ETH": { chainId: "1", symbol: "ETH", decimals: 18 },
|
|
@@ -393,7 +394,9 @@ var DECIMALS = {
|
|
|
393
394
|
"42161:ETH": 18,
|
|
394
395
|
"56:BNB": 18,
|
|
395
396
|
"137:POL": 18,
|
|
396
|
-
"900:SOL": 9
|
|
397
|
+
"900:SOL": 9,
|
|
398
|
+
// Zcash: zatoshi.
|
|
399
|
+
"9133:ZEC": 8
|
|
397
400
|
};
|
|
398
401
|
var AmountError = class extends Error {
|
|
399
402
|
constructor(code, message) {
|
|
@@ -490,6 +493,32 @@ function isNativeSource(chainId, tokenSymbol) {
|
|
|
490
493
|
return Object.prototype.hasOwnProperty.call(NATIVE_COINS, decimalsKey(chainId, tokenSymbol));
|
|
491
494
|
}
|
|
492
495
|
|
|
496
|
+
// scripts/src/lib/zec.mjs
|
|
497
|
+
var ZEC_CHAIN_ID = "9133";
|
|
498
|
+
var ZEC_SYMBOL = "ZEC";
|
|
499
|
+
var ZEC_PAYIN_STATES = /* @__PURE__ */ new Set(["awaiting", "seen", "confirming", "eligible", "expired", "reorged"]);
|
|
500
|
+
function isZecSource(chainId, tokenSymbol) {
|
|
501
|
+
return String(chainId ?? "").trim() === ZEC_CHAIN_ID && String(tokenSymbol ?? "").trim().toUpperCase() === ZEC_SYMBOL;
|
|
502
|
+
}
|
|
503
|
+
function zecStatusOf(source) {
|
|
504
|
+
const z = source?.zec;
|
|
505
|
+
if (!z || typeof z !== "object") return null;
|
|
506
|
+
const receive = z.receive === "shielded" || z.receive === "transparent" ? z.receive : null;
|
|
507
|
+
const payinState = ZEC_PAYIN_STATES.has(z.payinState) ? z.payinState : null;
|
|
508
|
+
const confirmations = Number.isInteger(z.confirmations) && z.confirmations >= 0 ? z.confirmations : null;
|
|
509
|
+
const requiredConfirmations = z.requiredConfirmations === 1 || z.requiredConfirmations === 2 ? z.requiredConfirmations : null;
|
|
510
|
+
const quoteExpiresAt = typeof z.quoteExpiresAt === "string" && z.quoteExpiresAt ? z.quoteExpiresAt : null;
|
|
511
|
+
const receivedZat = typeof z.receivedZat === "string" && /^\d+$/.test(z.receivedZat) ? z.receivedZat : null;
|
|
512
|
+
return { receive, payinState, confirmations, requiredConfirmations, quoteExpiresAt, receivedZat };
|
|
513
|
+
}
|
|
514
|
+
function zecMoneySeen(zec) {
|
|
515
|
+
if (!zec) return false;
|
|
516
|
+
if (zec.payinState === "seen" || zec.payinState === "confirming" || zec.payinState === "eligible") return true;
|
|
517
|
+
if (zec.receivedZat !== null && zec.receivedZat !== "0") return true;
|
|
518
|
+
if (zec.payinState === null) return true;
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
|
|
493
522
|
// scripts/src/lib/guards.mjs
|
|
494
523
|
var NATIVE_PROGRESS_STATUSES = /* @__PURE__ */ new Set([
|
|
495
524
|
"payment_payin_completed",
|
|
@@ -712,7 +741,9 @@ var MARGINS_MS = {
|
|
|
712
741
|
42161: 10 * MINUTE,
|
|
713
742
|
900: 5 * MINUTE,
|
|
714
743
|
1500: 10 * MINUTE,
|
|
715
|
-
lightning: 10 * MINUTE
|
|
744
|
+
lightning: 10 * MINUTE,
|
|
745
|
+
// Zcash: 75 s blocks, 1 to 2 confirmations, then the hub advances the merchant.
|
|
746
|
+
9133: 10 * MINUTE
|
|
716
747
|
};
|
|
717
748
|
var BOLT11_MIN_VALIDITY_MS = 10 * MINUTE;
|
|
718
749
|
var DEFAULT_MARGIN_MS = 10 * MINUTE;
|
|
@@ -1202,13 +1233,15 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1202
1233
|
});
|
|
1203
1234
|
const source = payment?.source || status?.rozoPayment?.source || {};
|
|
1204
1235
|
const native = isNativeSource(source.chainId, source.tokenSymbol);
|
|
1236
|
+
const isZec = isZecSource(source.chainId, source.tokenSymbol);
|
|
1237
|
+
const zec = isZec ? zecStatusOf(source) : null;
|
|
1205
1238
|
let localState = null;
|
|
1206
1239
|
try {
|
|
1207
1240
|
localState = id && isRozoPaymentId(id) ? readState(id) : null;
|
|
1208
1241
|
} catch {
|
|
1209
1242
|
localState = null;
|
|
1210
1243
|
}
|
|
1211
|
-
const quoteExpiresAt = priceLockFor({
|
|
1244
|
+
const quoteExpiresAt = isZec ? zec?.quoteExpiresAt ?? null : priceLockFor({
|
|
1212
1245
|
native,
|
|
1213
1246
|
payment: payment || status?.rozoPayment || null,
|
|
1214
1247
|
others: [localState?.confirmation?.quoteExpiresAt, localState?.quoteExpiresAt]
|
|
@@ -1217,6 +1250,17 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1217
1250
|
let state = verdict.state;
|
|
1218
1251
|
let terminal = verdict.terminal;
|
|
1219
1252
|
let detail = verdict.detail;
|
|
1253
|
+
const zecMoney = Boolean(zec && zecMoneySeen(zec));
|
|
1254
|
+
let escalate = verdict.escalate;
|
|
1255
|
+
if (zecMoney && state === "expired_unfunded") {
|
|
1256
|
+
state = "stuck_after_payment";
|
|
1257
|
+
terminal = false;
|
|
1258
|
+
escalate = true;
|
|
1259
|
+
detail = "The order expired but the custody rail reports a ZEC pay-in. Do not pay again; escalate for manual reconciliation.";
|
|
1260
|
+
} else if (zecMoney && state === "awaiting_deposit") {
|
|
1261
|
+
state = "payin_detected";
|
|
1262
|
+
detail = zec.payinState === "seen" || zec.payinState === "confirming" ? `Shielded pay-in ${zec.payinState}: ${zec.confirmations ?? 0} of ${zec.requiredConfirmations ?? "?"} confirmations.` : `The custody rail reports a ZEC pay-in (${zec.payinState ?? "state unreadable"}). Do not pay again.`;
|
|
1263
|
+
}
|
|
1220
1264
|
if (provider === "bitrefill") {
|
|
1221
1265
|
let local = null;
|
|
1222
1266
|
try {
|
|
@@ -1246,9 +1290,9 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1246
1290
|
linkId: linkId || status?.pl_id || null,
|
|
1247
1291
|
state,
|
|
1248
1292
|
unknown: verdict.unknown,
|
|
1249
|
-
moneyDetected: verdict.moneyDetected,
|
|
1293
|
+
moneyDetected: verdict.moneyDetected || zecMoney,
|
|
1250
1294
|
terminal,
|
|
1251
|
-
escalate
|
|
1295
|
+
escalate,
|
|
1252
1296
|
detail,
|
|
1253
1297
|
backend: {
|
|
1254
1298
|
paymentStatus: payment?.status ?? status?.rozoPayment?.status ?? null,
|
|
@@ -1263,7 +1307,9 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1263
1307
|
txHash: publicHash(source.txHash ?? null),
|
|
1264
1308
|
confirmedAt: source.confirmedAt ?? null,
|
|
1265
1309
|
senderAddressMasked: source.senderAddress ? maskAddress(source.senderAddress) : null,
|
|
1266
|
-
chain: source.chainId ? chainName(source.chainId) : null
|
|
1310
|
+
chain: source.chainId ? chainName(source.chainId) : null,
|
|
1311
|
+
// Shielded ZEC only: the custody rail's own view of the pay-in.
|
|
1312
|
+
...zec ? { zec } : {}
|
|
1267
1313
|
},
|
|
1268
1314
|
expiry: (() => {
|
|
1269
1315
|
if (provider === "bitrefill" && !bitrefillExpiry) {
|
|
@@ -1279,7 +1325,7 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1279
1325
|
msRemaining: Number.isFinite(ms) ? ms : null,
|
|
1280
1326
|
// Native orders: the order stays open after the price lock, but a
|
|
1281
1327
|
// payment after it is valued at the arrival price. Pay before this.
|
|
1282
|
-
...native ? {
|
|
1328
|
+
...native || isZec ? {
|
|
1283
1329
|
quoteExpiresAt: quoteExpiresAt ?? null,
|
|
1284
1330
|
priceLockExpiresIn: Number.isFinite(lockMs) ? formatRemaining(lockMs) : null
|
|
1285
1331
|
} : {}
|
|
@@ -1290,7 +1336,8 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1290
1336
|
confirmedAt: payment?.destination?.confirmedAt ?? status?.rozoPayment?.destination?.confirmedAt ?? null
|
|
1291
1337
|
},
|
|
1292
1338
|
localSend: localSendFor(id),
|
|
1293
|
-
|
|
1339
|
+
// Never a send window once the custody rail has seen a ZEC pay-in.
|
|
1340
|
+
sendWindow: state === "awaiting_deposit" && !zecMoney ? sendWindowFor({ provider, chainId: source.chainId, payment, status, bitrefillExpiry, quoteExpiresAt }) : null,
|
|
1294
1341
|
errors: [statusError, paymentError].filter(Boolean)
|
|
1295
1342
|
};
|
|
1296
1343
|
}
|
package/scripts/dist/resume.js
CHANGED
|
@@ -382,7 +382,8 @@ var CHAIN_NAMES = {
|
|
|
382
382
|
42161: "Arbitrum",
|
|
383
383
|
900: "Solana",
|
|
384
384
|
1500: "Stellar",
|
|
385
|
-
lightning: "Bitcoin Lightning"
|
|
385
|
+
lightning: "Bitcoin Lightning",
|
|
386
|
+
9133: "Zcash"
|
|
386
387
|
};
|
|
387
388
|
var CHAIN_FAMILY = {
|
|
388
389
|
1: "evm",
|
|
@@ -392,7 +393,9 @@ var CHAIN_FAMILY = {
|
|
|
392
393
|
42161: "evm",
|
|
393
394
|
900: "solana",
|
|
394
395
|
1500: "stellar",
|
|
395
|
-
lightning: "lightning"
|
|
396
|
+
lightning: "lightning",
|
|
397
|
+
// Zcash: the CLI creates shielded orders only (pool Unified Address); it never signs.
|
|
398
|
+
9133: "zcash"
|
|
396
399
|
};
|
|
397
400
|
var NATIVE_COINS = Object.freeze({
|
|
398
401
|
"1:ETH": { chainId: "1", symbol: "ETH", decimals: 18 },
|
|
@@ -425,7 +428,9 @@ var DECIMALS = {
|
|
|
425
428
|
"42161:ETH": 18,
|
|
426
429
|
"56:BNB": 18,
|
|
427
430
|
"137:POL": 18,
|
|
428
|
-
"900:SOL": 9
|
|
431
|
+
"900:SOL": 9,
|
|
432
|
+
// Zcash: zatoshi.
|
|
433
|
+
"9133:ZEC": 8
|
|
429
434
|
};
|
|
430
435
|
var AmountError = class extends Error {
|
|
431
436
|
constructor(code, message) {
|
|
@@ -526,6 +531,96 @@ function isNativeSource(chainId, tokenSymbol) {
|
|
|
526
531
|
return Object.prototype.hasOwnProperty.call(NATIVE_COINS, decimalsKey(chainId, tokenSymbol));
|
|
527
532
|
}
|
|
528
533
|
|
|
534
|
+
// scripts/src/lib/zec.mjs
|
|
535
|
+
var ZEC_CHAIN_ID = "9133";
|
|
536
|
+
var ZEC_SYMBOL = "ZEC";
|
|
537
|
+
var ZEC_PAYIN_STATES = /* @__PURE__ */ new Set(["awaiting", "seen", "confirming", "eligible", "expired", "reorged"]);
|
|
538
|
+
var UA_RE = /^u1[02-9ac-hj-np-z]{100,400}$/;
|
|
539
|
+
function isZecSource(chainId, tokenSymbol) {
|
|
540
|
+
return String(chainId ?? "").trim() === ZEC_CHAIN_ID && String(tokenSymbol ?? "").trim().toUpperCase() === ZEC_SYMBOL;
|
|
541
|
+
}
|
|
542
|
+
function isUnifiedAddress(value) {
|
|
543
|
+
return typeof value === "string" && UA_RE.test(value.trim());
|
|
544
|
+
}
|
|
545
|
+
function zecStatusOf(source) {
|
|
546
|
+
const z = source?.zec;
|
|
547
|
+
if (!z || typeof z !== "object") return null;
|
|
548
|
+
const receive = z.receive === "shielded" || z.receive === "transparent" ? z.receive : null;
|
|
549
|
+
const payinState = ZEC_PAYIN_STATES.has(z.payinState) ? z.payinState : null;
|
|
550
|
+
const confirmations = Number.isInteger(z.confirmations) && z.confirmations >= 0 ? z.confirmations : null;
|
|
551
|
+
const requiredConfirmations = z.requiredConfirmations === 1 || z.requiredConfirmations === 2 ? z.requiredConfirmations : null;
|
|
552
|
+
const quoteExpiresAt = typeof z.quoteExpiresAt === "string" && z.quoteExpiresAt ? z.quoteExpiresAt : null;
|
|
553
|
+
const receivedZat = typeof z.receivedZat === "string" && /^\d+$/.test(z.receivedZat) ? z.receivedZat : null;
|
|
554
|
+
return { receive, payinState, confirmations, requiredConfirmations, quoteExpiresAt, receivedZat };
|
|
555
|
+
}
|
|
556
|
+
function zecMoneySeen(zec) {
|
|
557
|
+
if (!zec) return false;
|
|
558
|
+
if (zec.payinState === "seen" || zec.payinState === "confirming" || zec.payinState === "eligible") return true;
|
|
559
|
+
if (zec.receivedZat !== null && zec.receivedZat !== "0") return true;
|
|
560
|
+
if (zec.payinState === null) return true;
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
function parsePaymentUri(uri) {
|
|
564
|
+
if (typeof uri !== "string") return null;
|
|
565
|
+
const m = /^zcash:([0-9a-z]+)\?([^#]*)$/i.exec(uri.trim());
|
|
566
|
+
if (!m) return null;
|
|
567
|
+
const address = m[1];
|
|
568
|
+
const out = { address, amount: null, memo: null };
|
|
569
|
+
const seen = /* @__PURE__ */ new Set();
|
|
570
|
+
for (const part of m[2].split("&")) {
|
|
571
|
+
if (!part) return null;
|
|
572
|
+
const eq = part.indexOf("=");
|
|
573
|
+
if (eq <= 0) return null;
|
|
574
|
+
const key = part.slice(0, eq);
|
|
575
|
+
const value = part.slice(eq + 1);
|
|
576
|
+
if (seen.has(key)) return null;
|
|
577
|
+
seen.add(key);
|
|
578
|
+
if (key === "amount") {
|
|
579
|
+
if (!/^\d+(\.\d{1,8})?$/.test(value)) return null;
|
|
580
|
+
out.amount = value;
|
|
581
|
+
} else if (key === "memo") {
|
|
582
|
+
if (!/^[A-Za-z0-9_-]{1,684}$/.test(value)) return null;
|
|
583
|
+
out.memo = value;
|
|
584
|
+
} else {
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (out.amount === null) return null;
|
|
589
|
+
return out;
|
|
590
|
+
}
|
|
591
|
+
function sameDecimal(a, b) {
|
|
592
|
+
const norm = (v) => {
|
|
593
|
+
const s = String(v ?? "").trim();
|
|
594
|
+
if (!/^\d+(\.\d+)?$/.test(s)) return null;
|
|
595
|
+
const [w, f = ""] = s.split(".");
|
|
596
|
+
const frac = f.replace(/0+$/, "");
|
|
597
|
+
return `${w.replace(/^0+(?=\d)/, "")}${frac ? `.${frac}` : ""}`;
|
|
598
|
+
};
|
|
599
|
+
const na = norm(a);
|
|
600
|
+
return na !== null && na === norm(b);
|
|
601
|
+
}
|
|
602
|
+
function validateShieldedDeposit(source) {
|
|
603
|
+
const address = typeof source?.receiverAddress === "string" ? source.receiverAddress.trim() : "";
|
|
604
|
+
if (!isUnifiedAddress(address)) {
|
|
605
|
+
return { ok: false, code: "ZEC_ADDRESS_INVALID", reason: "The order did not return a mainnet Unified Address (u1...). Refusing to show it as payable." };
|
|
606
|
+
}
|
|
607
|
+
const uri = typeof source?.paymentUri === "string" ? source.paymentUri.trim() : "";
|
|
608
|
+
if (!uri) {
|
|
609
|
+
return { ok: false, code: "ZEC_PAYMENT_URI_MISSING", reason: "The order carries no payment URI yet. Nothing is payable until it appears." };
|
|
610
|
+
}
|
|
611
|
+
const parsed = parsePaymentUri(uri);
|
|
612
|
+
if (!parsed) {
|
|
613
|
+
return { ok: false, code: "ZEC_PAYMENT_URI_INVALID", reason: "The payment URI is not a single-payment ZIP-321 URI. Refusing to show it." };
|
|
614
|
+
}
|
|
615
|
+
if (parsed.address !== address) {
|
|
616
|
+
return { ok: false, code: "ZEC_PAYMENT_URI_MISMATCH", reason: "The payment URI names a different address than the order. Refusing to show it." };
|
|
617
|
+
}
|
|
618
|
+
if (!sameDecimal(parsed.amount, source?.amount)) {
|
|
619
|
+
return { ok: false, code: "ZEC_PAYMENT_URI_MISMATCH", reason: "The payment URI amount differs from the order amount. Refusing to show it." };
|
|
620
|
+
}
|
|
621
|
+
return { ok: true, code: null, reason: null, address, paymentUri: uri, memo: parsed.memo };
|
|
622
|
+
}
|
|
623
|
+
|
|
529
624
|
// scripts/src/lib/guards.mjs
|
|
530
625
|
var UNPAID_STATUS = "payment_unpaid";
|
|
531
626
|
var NATIVE_PROGRESS_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -586,6 +681,11 @@ function validateDepositInstructions(source) {
|
|
|
586
681
|
reason: "The live payments response returned no deposit address."
|
|
587
682
|
};
|
|
588
683
|
}
|
|
684
|
+
if (family === "zcash" && zecStatusOf(source)?.receive === "shielded") {
|
|
685
|
+
const zec = validateShieldedDeposit(source);
|
|
686
|
+
if (!zec.ok) return { ok: false, code: zec.code, reason: zec.reason };
|
|
687
|
+
return { ok: true, code: null, reason: null, family, amountAtomic, payTo: zec.address, memo: null, paymentUri: zec.paymentUri };
|
|
688
|
+
}
|
|
589
689
|
if (family === "stellar" && !memo) {
|
|
590
690
|
return {
|
|
591
691
|
ok: false,
|
|
@@ -618,13 +718,14 @@ function reuseGuard({ payment, requested, reused = false }) {
|
|
|
618
718
|
const hasTx = source.txHash !== null && source.txHash !== void 0 && source.txHash !== "";
|
|
619
719
|
const signal = receiptSignal(source);
|
|
620
720
|
const hasConfirm = source.confirmedAt !== null && source.confirmedAt !== void 0 && source.confirmedAt !== "";
|
|
621
|
-
|
|
721
|
+
const zecSeen = isZecSource(source.chainId, source.tokenSymbol) && zecMoneySeen(zecStatusOf(source));
|
|
722
|
+
if (hasTx || signal.money || hasConfirm || zecSeen) {
|
|
622
723
|
return {
|
|
623
724
|
ok: false,
|
|
624
725
|
code: "ORDER_ALREADY_FUNDED",
|
|
625
|
-
reason: signal.unparsable ? "This order reports an amountReceived that cannot be read. It is treated as funded until a human confirms otherwise \u2014 do NOT pay again." : "This Coinbase link already has a funded Rozo order (it may have been paid elsewhere). Do NOT pay again \u2014 escalate for manual reconciliation.",
|
|
726
|
+
reason: signal.unparsable ? "This order reports an amountReceived that cannot be read. It is treated as funded until a human confirms otherwise \u2014 do NOT pay again." : zecSeen && !hasTx && !signal.money && !hasConfirm ? "A shielded ZEC payment to this order has already been seen on the network. Do NOT pay again; watch it with status instead." : "This Coinbase link already has a funded Rozo order (it may have been paid elsewhere). Do NOT pay again \u2014 escalate for manual reconciliation.",
|
|
626
727
|
moneyDetected: true,
|
|
627
|
-
evidence: { ...evidence, receipt: signal.receipt, receiptUnparsable: signal.unparsable }
|
|
728
|
+
evidence: { ...evidence, receipt: signal.receipt, receiptUnparsable: signal.unparsable, ...zecSeen ? { zec: zecStatusOf(source) } : {} }
|
|
628
729
|
};
|
|
629
730
|
}
|
|
630
731
|
if (payment?.status !== UNPAID_STATUS) {
|
|
@@ -652,6 +753,28 @@ function reuseGuard({ payment, requested, reused = false }) {
|
|
|
652
753
|
evidence
|
|
653
754
|
};
|
|
654
755
|
}
|
|
756
|
+
if (isZecSource(gotChain, gotToken)) {
|
|
757
|
+
const zec = zecStatusOf(source);
|
|
758
|
+
const wantReceive = requested?.shielded === true ? "shielded" : "transparent";
|
|
759
|
+
if (!zec || zec.receive !== wantReceive) {
|
|
760
|
+
return {
|
|
761
|
+
ok: false,
|
|
762
|
+
code: "ZEC_RECEIVE_MODE_MISMATCH",
|
|
763
|
+
reason: `This link already has a ZEC order that is paid ${zec?.receive ?? "in an unknown way"}, but you asked for ${wantReceive}. Nothing new was created. Pay that order the way it was made, or wait for it to expire unfunded.`,
|
|
764
|
+
moneyDetected: false,
|
|
765
|
+
evidence: { ...evidence, zec }
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
if (zec.payinState === "expired" || zec.payinState === "reorged") {
|
|
769
|
+
return {
|
|
770
|
+
ok: false,
|
|
771
|
+
code: "ZEC_QUOTE_EXPIRED",
|
|
772
|
+
reason: `The ZEC price lock on this order has ${zec.payinState === "reorged" ? "been disturbed by a reorg" : "expired"} with nothing received. Do not pay it; re-run pay to get a fresh order.`,
|
|
773
|
+
moneyDetected: false,
|
|
774
|
+
evidence: { ...evidence, zec }
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
}
|
|
655
778
|
const deposit = validateDepositInstructions(source);
|
|
656
779
|
if (!deposit.ok) {
|
|
657
780
|
return {
|
|
@@ -989,7 +1112,9 @@ var MARGINS_MS = {
|
|
|
989
1112
|
42161: 10 * MINUTE,
|
|
990
1113
|
900: 5 * MINUTE,
|
|
991
1114
|
1500: 10 * MINUTE,
|
|
992
|
-
lightning: 10 * MINUTE
|
|
1115
|
+
lightning: 10 * MINUTE,
|
|
1116
|
+
// Zcash: 75 s blocks, 1 to 2 confirmations, then the hub advances the merchant.
|
|
1117
|
+
9133: 10 * MINUTE
|
|
993
1118
|
};
|
|
994
1119
|
var BOLT11_MIN_VALIDITY_MS = 10 * MINUTE;
|
|
995
1120
|
var DEFAULT_MARGIN_MS = 10 * MINUTE;
|
|
@@ -1276,13 +1401,15 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1276
1401
|
});
|
|
1277
1402
|
const source = payment?.source || status?.rozoPayment?.source || {};
|
|
1278
1403
|
const native = isNativeSource(source.chainId, source.tokenSymbol);
|
|
1404
|
+
const isZec = isZecSource(source.chainId, source.tokenSymbol);
|
|
1405
|
+
const zec = isZec ? zecStatusOf(source) : null;
|
|
1279
1406
|
let localState = null;
|
|
1280
1407
|
try {
|
|
1281
1408
|
localState = id && isRozoPaymentId(id) ? readState(id) : null;
|
|
1282
1409
|
} catch {
|
|
1283
1410
|
localState = null;
|
|
1284
1411
|
}
|
|
1285
|
-
const quoteExpiresAt = priceLockFor({
|
|
1412
|
+
const quoteExpiresAt = isZec ? zec?.quoteExpiresAt ?? null : priceLockFor({
|
|
1286
1413
|
native,
|
|
1287
1414
|
payment: payment || status?.rozoPayment || null,
|
|
1288
1415
|
others: [localState?.confirmation?.quoteExpiresAt, localState?.quoteExpiresAt]
|
|
@@ -1291,6 +1418,17 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1291
1418
|
let state = verdict.state;
|
|
1292
1419
|
let terminal = verdict.terminal;
|
|
1293
1420
|
let detail = verdict.detail;
|
|
1421
|
+
const zecMoney = Boolean(zec && zecMoneySeen(zec));
|
|
1422
|
+
let escalate = verdict.escalate;
|
|
1423
|
+
if (zecMoney && state === "expired_unfunded") {
|
|
1424
|
+
state = "stuck_after_payment";
|
|
1425
|
+
terminal = false;
|
|
1426
|
+
escalate = true;
|
|
1427
|
+
detail = "The order expired but the custody rail reports a ZEC pay-in. Do not pay again; escalate for manual reconciliation.";
|
|
1428
|
+
} else if (zecMoney && state === "awaiting_deposit") {
|
|
1429
|
+
state = "payin_detected";
|
|
1430
|
+
detail = zec.payinState === "seen" || zec.payinState === "confirming" ? `Shielded pay-in ${zec.payinState}: ${zec.confirmations ?? 0} of ${zec.requiredConfirmations ?? "?"} confirmations.` : `The custody rail reports a ZEC pay-in (${zec.payinState ?? "state unreadable"}). Do not pay again.`;
|
|
1431
|
+
}
|
|
1294
1432
|
if (provider === "bitrefill") {
|
|
1295
1433
|
let local = null;
|
|
1296
1434
|
try {
|
|
@@ -1320,9 +1458,9 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1320
1458
|
linkId: linkId || status?.pl_id || null,
|
|
1321
1459
|
state,
|
|
1322
1460
|
unknown: verdict.unknown,
|
|
1323
|
-
moneyDetected: verdict.moneyDetected,
|
|
1461
|
+
moneyDetected: verdict.moneyDetected || zecMoney,
|
|
1324
1462
|
terminal,
|
|
1325
|
-
escalate
|
|
1463
|
+
escalate,
|
|
1326
1464
|
detail,
|
|
1327
1465
|
backend: {
|
|
1328
1466
|
paymentStatus: payment?.status ?? status?.rozoPayment?.status ?? null,
|
|
@@ -1337,7 +1475,9 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1337
1475
|
txHash: publicHash(source.txHash ?? null),
|
|
1338
1476
|
confirmedAt: source.confirmedAt ?? null,
|
|
1339
1477
|
senderAddressMasked: source.senderAddress ? maskAddress(source.senderAddress) : null,
|
|
1340
|
-
chain: source.chainId ? chainName(source.chainId) : null
|
|
1478
|
+
chain: source.chainId ? chainName(source.chainId) : null,
|
|
1479
|
+
// Shielded ZEC only: the custody rail's own view of the pay-in.
|
|
1480
|
+
...zec ? { zec } : {}
|
|
1341
1481
|
},
|
|
1342
1482
|
expiry: (() => {
|
|
1343
1483
|
if (provider === "bitrefill" && !bitrefillExpiry) {
|
|
@@ -1353,7 +1493,7 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1353
1493
|
msRemaining: Number.isFinite(ms) ? ms : null,
|
|
1354
1494
|
// Native orders: the order stays open after the price lock, but a
|
|
1355
1495
|
// payment after it is valued at the arrival price. Pay before this.
|
|
1356
|
-
...native ? {
|
|
1496
|
+
...native || isZec ? {
|
|
1357
1497
|
quoteExpiresAt: quoteExpiresAt ?? null,
|
|
1358
1498
|
priceLockExpiresIn: Number.isFinite(lockMs) ? formatRemaining(lockMs) : null
|
|
1359
1499
|
} : {}
|
|
@@ -1364,7 +1504,8 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
1364
1504
|
confirmedAt: payment?.destination?.confirmedAt ?? status?.rozoPayment?.destination?.confirmedAt ?? null
|
|
1365
1505
|
},
|
|
1366
1506
|
localSend: localSendFor(id),
|
|
1367
|
-
|
|
1507
|
+
// Never a send window once the custody rail has seen a ZEC pay-in.
|
|
1508
|
+
sendWindow: state === "awaiting_deposit" && !zecMoney ? sendWindowFor({ provider, chainId: source.chainId, payment, status, bitrefillExpiry, quoteExpiresAt }) : null,
|
|
1368
1509
|
errors: [statusError, paymentError].filter(Boolean)
|
|
1369
1510
|
};
|
|
1370
1511
|
}
|
|
@@ -1506,7 +1647,12 @@ async function main(argv) {
|
|
|
1506
1647
|
}
|
|
1507
1648
|
const payment = await getPayment(id);
|
|
1508
1649
|
const source = payment?.source || {};
|
|
1509
|
-
const
|
|
1650
|
+
const resumedShielded = isZecSource(source.chainId, source.tokenSymbol) && zecStatusOf(source)?.receive === "shielded";
|
|
1651
|
+
const guard = reuseGuard({
|
|
1652
|
+
payment,
|
|
1653
|
+
requested: { chainId: source.chainId, tokenSymbol: source.tokenSymbol, ...resumedShielded ? { shielded: true } : {} },
|
|
1654
|
+
reused: true
|
|
1655
|
+
});
|
|
1510
1656
|
if (!guard.ok) {
|
|
1511
1657
|
emit(
|
|
1512
1658
|
{
|
|
@@ -1524,13 +1670,16 @@ async function main(argv) {
|
|
|
1524
1670
|
);
|
|
1525
1671
|
}
|
|
1526
1672
|
const native = isNativeSource(source.chainId, source.tokenSymbol);
|
|
1527
|
-
const
|
|
1673
|
+
const isZec = isZecSource(source.chainId, source.tokenSymbol);
|
|
1674
|
+
const zecStatus = isZec ? zecStatusOf(source) : null;
|
|
1675
|
+
const zec = Boolean(zecStatus && zecStatus.receive === "shielded");
|
|
1676
|
+
const quoteExpiresAt = isZec ? zecStatus?.quoteExpiresAt ?? null : priceLockFor({ native, payment, others: [snap.expiry?.quoteExpiresAt] });
|
|
1528
1677
|
const finalWindow = checkExpiry({
|
|
1529
1678
|
now: Date.now(),
|
|
1530
1679
|
chainId: source.chainId,
|
|
1531
1680
|
intentExpiresAt: payment?.expiresAt ?? snap.sendWindow?.deadlineMs,
|
|
1532
1681
|
coinbaseExpiry: snap.sendWindow?.deadlineMs,
|
|
1533
|
-
...native ? { quoteExpiresAt } : {}
|
|
1682
|
+
...native || isZec ? { quoteExpiresAt } : {}
|
|
1534
1683
|
});
|
|
1535
1684
|
if (!finalWindow.ok) {
|
|
1536
1685
|
emit(
|
|
@@ -1605,8 +1754,14 @@ async function main(argv) {
|
|
|
1605
1754
|
expiresAt,
|
|
1606
1755
|
expiresIn,
|
|
1607
1756
|
native,
|
|
1608
|
-
exactOut: native || lightning,
|
|
1609
|
-
quoteExpiresAt: native ? quoteExpiresAt : null
|
|
1757
|
+
exactOut: native || lightning || zec,
|
|
1758
|
+
quoteExpiresAt: native || zec ? quoteExpiresAt : null,
|
|
1759
|
+
...zec ? {
|
|
1760
|
+
shielded: true,
|
|
1761
|
+
paymentUri: guard.deposit.paymentUri,
|
|
1762
|
+
requiredConfirmations: zecStatus.requiredConfirmations,
|
|
1763
|
+
payinState: zecStatus.payinState
|
|
1764
|
+
} : {}
|
|
1610
1765
|
} : null,
|
|
1611
1766
|
depositWithheld: !confirmed,
|
|
1612
1767
|
display: {
|
|
@@ -1619,12 +1774,13 @@ async function main(argv) {
|
|
|
1619
1774
|
hasMemo: Boolean(source.receiverMemo),
|
|
1620
1775
|
memoType: source.receiverMemo ? STELLAR_MEMO_TYPE : null,
|
|
1621
1776
|
native,
|
|
1622
|
-
priceLockedUntil: native ? quoteExpiresAt : null
|
|
1777
|
+
priceLockedUntil: native || zec ? quoteExpiresAt : null,
|
|
1778
|
+
...zec ? { shielded: true } : {}
|
|
1623
1779
|
},
|
|
1624
1780
|
expiry: {
|
|
1625
1781
|
expiresAt,
|
|
1626
1782
|
expiresIn,
|
|
1627
|
-
...native ? { quoteExpiresAt } : {},
|
|
1783
|
+
...native || zec ? { quoteExpiresAt } : {},
|
|
1628
1784
|
minutesOfSlack: snap.sendWindow?.minutesOfSlack ?? null
|
|
1629
1785
|
},
|
|
1630
1786
|
note: confirmed ? "Send exactly once, exactly this amount, on this chain. Then: rozo-checkout status <id> --watch" : "Deposit address withheld. Confirm the chain, token and amount with the payer, then re-run with --confirm, or open paymentLink."
|