@rozoai/checkout 0.1.15 → 0.1.17
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 +63 -1
- package/package.json +1 -1
- package/scripts/dist/cli.js +1267 -126
- package/scripts/dist/create-order.js +341 -186
- package/scripts/dist/quote.js +9 -0
- package/scripts/dist/receipt.js +1195 -0
- package/scripts/dist/resume.js +1486 -0
- package/scripts/dist/send-evm.js +502 -215
- package/scripts/dist/send-sol.js +324 -229
- package/scripts/dist/status.js +405 -49
package/scripts/dist/cli.js
CHANGED
|
@@ -3475,6 +3475,9 @@ function carry(digits) {
|
|
|
3475
3475
|
}
|
|
3476
3476
|
return `1${out2.join("")}`;
|
|
3477
3477
|
}
|
|
3478
|
+
function fromGwei(gwei, unit = "wei") {
|
|
3479
|
+
return from(gwei, exponents.gwei - exponents[unit]);
|
|
3480
|
+
}
|
|
3478
3481
|
var exponents, InvalidDecimalNumberError, InvalidDecimalsError;
|
|
3479
3482
|
var init_Value = __esm({
|
|
3480
3483
|
"node_modules/viem/_esm/utils/unit/Value.js"() {
|
|
@@ -20541,7 +20544,7 @@ var require_dist = __commonJS({
|
|
|
20541
20544
|
}
|
|
20542
20545
|
}
|
|
20543
20546
|
}
|
|
20544
|
-
function*
|
|
20547
|
+
function* run9(value, struct2, options = {}) {
|
|
20545
20548
|
const { path: path7 = [], branch = [value], coerce: coerce2 = false, mask: mask2 = false } = options;
|
|
20546
20549
|
const ctx = { path: path7, branch, mask: mask2 };
|
|
20547
20550
|
if (coerce2) {
|
|
@@ -20554,7 +20557,7 @@ var require_dist = __commonJS({
|
|
|
20554
20557
|
yield [failure, void 0];
|
|
20555
20558
|
}
|
|
20556
20559
|
for (let [k, v, s] of struct2.entries(value, ctx)) {
|
|
20557
|
-
const ts =
|
|
20560
|
+
const ts = run9(v, s, {
|
|
20558
20561
|
path: k === void 0 ? path7 : [...path7, k],
|
|
20559
20562
|
branch: k === void 0 ? branch : [...branch, v],
|
|
20560
20563
|
coerce: coerce2,
|
|
@@ -20682,7 +20685,7 @@ var require_dist = __commonJS({
|
|
|
20682
20685
|
return !result[0];
|
|
20683
20686
|
}
|
|
20684
20687
|
function validate7(value, struct2, options = {}) {
|
|
20685
|
-
const tuples =
|
|
20688
|
+
const tuples = run9(value, struct2, options);
|
|
20686
20689
|
const tuple2 = shiftIterator(tuples);
|
|
20687
20690
|
if (tuple2[0]) {
|
|
20688
20691
|
const error = new StructError(tuple2[0], function* () {
|
|
@@ -21095,7 +21098,7 @@ var require_dist = __commonJS({
|
|
|
21095
21098
|
validator(value, ctx) {
|
|
21096
21099
|
const failures = [];
|
|
21097
21100
|
for (const S of Structs) {
|
|
21098
|
-
const [...tuples] =
|
|
21101
|
+
const [...tuples] = run9(value, S, ctx);
|
|
21099
21102
|
const [first] = tuples;
|
|
21100
21103
|
if (!first[0]) {
|
|
21101
21104
|
return [];
|
|
@@ -41511,7 +41514,36 @@ function redact(text) {
|
|
|
41511
41514
|
);
|
|
41512
41515
|
return s;
|
|
41513
41516
|
}
|
|
41517
|
+
var PublicHash = class {
|
|
41518
|
+
constructor(value) {
|
|
41519
|
+
this.value = value;
|
|
41520
|
+
}
|
|
41521
|
+
toJSON() {
|
|
41522
|
+
return this.value;
|
|
41523
|
+
}
|
|
41524
|
+
};
|
|
41525
|
+
var TX_HASH_VALUE = /^(0x[0-9a-fA-F]{64}|[1-9A-HJ-NP-Za-km-z]{86,88})$/;
|
|
41526
|
+
function publicHash(value) {
|
|
41527
|
+
return typeof value === "string" && TX_HASH_VALUE.test(value) ? new PublicHash(value) : value;
|
|
41528
|
+
}
|
|
41529
|
+
var PUBLIC_PAY_PAGE_HOSTS = /* @__PURE__ */ new Set(["invoice.rozo.ai", "checkout.rozo.ai"]);
|
|
41530
|
+
var PAY_PAGE_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
41531
|
+
function publicPayPage(value) {
|
|
41532
|
+
if (typeof value !== "string") return value;
|
|
41533
|
+
let u;
|
|
41534
|
+
try {
|
|
41535
|
+
u = new URL(value);
|
|
41536
|
+
} catch {
|
|
41537
|
+
return value;
|
|
41538
|
+
}
|
|
41539
|
+
if (u.protocol !== "https:" || u.username || u.password || u.port || !PUBLIC_PAY_PAGE_HOSTS.has(u.hostname)) return value;
|
|
41540
|
+
if (u.pathname !== "/checkout") return value;
|
|
41541
|
+
const id = u.searchParams.get("id");
|
|
41542
|
+
if (!id || !PAY_PAGE_ID.test(id)) return value;
|
|
41543
|
+
return new PublicHash(`https://${u.hostname}/checkout?id=${id.toLowerCase()}`);
|
|
41544
|
+
}
|
|
41514
41545
|
function redactDeep(value) {
|
|
41546
|
+
if (value instanceof PublicHash) return value.value;
|
|
41515
41547
|
if (typeof value === "string") return redact(value);
|
|
41516
41548
|
if (Array.isArray(value)) return value.map(redactDeep);
|
|
41517
41549
|
if (value && typeof value === "object") {
|
|
@@ -41753,8 +41785,61 @@ function normalizeUtmSource(value) {
|
|
|
41753
41785
|
return s;
|
|
41754
41786
|
}
|
|
41755
41787
|
|
|
41788
|
+
// scripts/src/lib/ids.mjs
|
|
41789
|
+
var IdError = class extends Error {
|
|
41790
|
+
constructor(code, message) {
|
|
41791
|
+
super(message);
|
|
41792
|
+
this.code = code;
|
|
41793
|
+
}
|
|
41794
|
+
};
|
|
41795
|
+
var PL_RE = /(pl_[0-9a-zA-Z]+)/;
|
|
41796
|
+
var SESSION_RE = /(paymentSession_[A-Za-z0-9_-]+)/;
|
|
41797
|
+
var UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
41798
|
+
function extractLinkId(urlOrId) {
|
|
41799
|
+
if (typeof urlOrId !== "string" || !urlOrId.trim()) {
|
|
41800
|
+
throw new IdError("BAD_LINK", "A Coinbase payment link URL or id is required.");
|
|
41801
|
+
}
|
|
41802
|
+
const s = urlOrId.trim();
|
|
41803
|
+
const session = s.match(SESSION_RE);
|
|
41804
|
+
if (session) return { linkId: session[1], kind: "payment_session" };
|
|
41805
|
+
const link = s.match(PL_RE);
|
|
41806
|
+
if (link) return { linkId: link[1], kind: "payment_link" };
|
|
41807
|
+
if (/commerce\.coinbase\.com\/pay\//.test(s)) {
|
|
41808
|
+
throw new IdError(
|
|
41809
|
+
"LEGACY_COMMERCE_URL",
|
|
41810
|
+
"commerce.coinbase.com/pay/<uuid> uses the legacy protocol; this skill does not handle it."
|
|
41811
|
+
);
|
|
41812
|
+
}
|
|
41813
|
+
throw new IdError(
|
|
41814
|
+
"BAD_LINK",
|
|
41815
|
+
"Could not find a `pl_*` or `paymentSession_*` id in the supplied value."
|
|
41816
|
+
);
|
|
41817
|
+
}
|
|
41818
|
+
function assertRozoPaymentId(value) {
|
|
41819
|
+
const s = String(value || "").trim();
|
|
41820
|
+
if (!UUID_RE.test(s)) {
|
|
41821
|
+
throw new IdError("BAD_ROZO_PAYMENT_ID", "rozoPaymentId must be a UUID.");
|
|
41822
|
+
}
|
|
41823
|
+
return s;
|
|
41824
|
+
}
|
|
41825
|
+
function isRozoPaymentId(value) {
|
|
41826
|
+
return UUID_RE.test(String(value || "").trim());
|
|
41827
|
+
}
|
|
41828
|
+
function maskAddress2(address) {
|
|
41829
|
+
const s = String(address ?? "").trim();
|
|
41830
|
+
if (!s) return "(none)";
|
|
41831
|
+
if (s.length <= 12) return s;
|
|
41832
|
+
return `${s.slice(0, 6)}...${s.slice(-4)}`;
|
|
41833
|
+
}
|
|
41834
|
+
function maskMemo(memo) {
|
|
41835
|
+
const s = String(memo ?? "").trim();
|
|
41836
|
+
if (!s) return null;
|
|
41837
|
+
if (s.length <= 8) return s;
|
|
41838
|
+
return `${s.slice(0, 4)}...${s.slice(-3)}`;
|
|
41839
|
+
}
|
|
41840
|
+
|
|
41756
41841
|
// scripts/src/lib/cli-args.mjs
|
|
41757
|
-
var COMMANDS = ["pay", "quote", "status", "help", "version"];
|
|
41842
|
+
var COMMANDS = ["pay", "quote", "status", "receipt", "resume", "help", "version"];
|
|
41758
41843
|
var CHAIN_ALIASES = {
|
|
41759
41844
|
ethereum: "1",
|
|
41760
41845
|
eth: "1",
|
|
@@ -41970,6 +42055,30 @@ function parseCliArgs(argv) {
|
|
|
41970
42055
|
if (!target) throw new CliError("MISSING_TARGET", "Usage: rozo-checkout quote <coinbase-link>");
|
|
41971
42056
|
return { command, target, json, ...utmSource ? { utmSource } : {} };
|
|
41972
42057
|
}
|
|
42058
|
+
if (command === "receipt") {
|
|
42059
|
+
if (!target) {
|
|
42060
|
+
throw new CliError(
|
|
42061
|
+
"MISSING_TARGET",
|
|
42062
|
+
"Usage: rozo-checkout receipt <rozoPaymentId | coinbase-link>"
|
|
42063
|
+
);
|
|
42064
|
+
}
|
|
42065
|
+
if (flags.provider !== void 0 && !["coinbase", "bitrefill"].includes(String(flags.provider))) {
|
|
42066
|
+
throw new CliError("BAD_VALUE", "--provider must be coinbase or bitrefill.");
|
|
42067
|
+
}
|
|
42068
|
+
return { command, target, json, provider: flags.provider };
|
|
42069
|
+
}
|
|
42070
|
+
if (command === "resume") {
|
|
42071
|
+
if (!target) {
|
|
42072
|
+
throw new CliError("MISSING_TARGET", "Usage: rozo-checkout resume <rozoPaymentId>");
|
|
42073
|
+
}
|
|
42074
|
+
if (!isRozoPaymentId(target)) {
|
|
42075
|
+
throw new CliError(
|
|
42076
|
+
"BAD_VALUE",
|
|
42077
|
+
"resume takes the order id (rozoPaymentId, a UUID) that pay printed, not a payment link."
|
|
42078
|
+
);
|
|
42079
|
+
}
|
|
42080
|
+
return { command, target, json, yes: flags.yes === true };
|
|
42081
|
+
}
|
|
41973
42082
|
if (command === "status") {
|
|
41974
42083
|
if (!target) {
|
|
41975
42084
|
throw new CliError(
|
|
@@ -42087,6 +42196,8 @@ USAGE
|
|
|
42087
42196
|
npx @rozoai/checkout pay --bitrefill-invoice <id> --to <0x\u2026> --amount <USDC> --expires-at <ISO> --with <coin>
|
|
42088
42197
|
npx @rozoai/checkout quote <coinbase-link>
|
|
42089
42198
|
npx @rozoai/checkout status <rozoPaymentId | coinbase-link>
|
|
42199
|
+
npx @rozoai/checkout receipt <rozoPaymentId | coinbase-link>
|
|
42200
|
+
npx @rozoai/checkout resume <rozoPaymentId>
|
|
42090
42201
|
|
|
42091
42202
|
COINS (--with)
|
|
42092
42203
|
usdt-solana usdc-solana usdt-bnb usdc-bnb
|
|
@@ -42139,6 +42250,31 @@ EXAMPLES
|
|
|
42139
42250
|
npx @rozoai/checkout pay pl_01... --with usdt-solana --email you@example.com
|
|
42140
42251
|
npx @rozoai/checkout pay --bitrefill-invoice 8f3k2 --to 0xAbC\u2026123 --amount 7.90 --expires-at 2026-10-01T12:00:00Z --with usdc-stellar
|
|
42141
42252
|
npx @rozoai/checkout status 11111111-2222-4333-8444-555555555555
|
|
42253
|
+
npx @rozoai/checkout receipt 11111111-2222-4333-8444-555555555555 --json
|
|
42254
|
+
|
|
42255
|
+
RECEIPT
|
|
42256
|
+
receipt reads the order once (nothing is created or sent) and reports three
|
|
42257
|
+
layers separately: did your money reach Rozo, was the merchant invoice paid,
|
|
42258
|
+
and was the service delivered (always "unknown": Rozo cannot see OpenRouter
|
|
42259
|
+
credits). Exit 0 only when the merchant invoice is settled; 3 when it is
|
|
42260
|
+
still in flight or unknown; 1 when it expired unpaid or needs a human.
|
|
42261
|
+
In --json output from status and receipt, read paymentOutcome and
|
|
42262
|
+
nextAction. The older "success" field means the query worked, not that the
|
|
42263
|
+
invoice is paid.
|
|
42264
|
+
|
|
42265
|
+
RESUME
|
|
42266
|
+
resume picks up an order you created earlier and did not pay yet. It reads
|
|
42267
|
+
the order (nothing is created or sent), and if it is still unpaid and has
|
|
42268
|
+
enough time left it prints the hosted payment page and, after you confirm
|
|
42269
|
+
(or with --yes), the exact deposit instructions again. A paid order is
|
|
42270
|
+
reported as paid (exit 0); an expired one says so and how to start over.
|
|
42271
|
+
|
|
42272
|
+
PRIVACY
|
|
42273
|
+
Each order carries an anonymous install id: a random UUID created on first
|
|
42274
|
+
run and stored in ~/.rozo-checkout/prefs.json. If OPENROUTER_API_KEY is set,
|
|
42275
|
+
a one-way salted sha256 hash of it is sent too; the key itself is never
|
|
42276
|
+
sent, logged or stored. Delete prefs.json
|
|
42277
|
+
to reset the id; set ROZO_CHECKOUT_ANON_ID=off to send neither.
|
|
42142
42278
|
|
|
42143
42279
|
Creating an order moves no money; an unfunded order simply expires. Nothing is
|
|
42144
42280
|
paid until you confirm, and --send is required before anything is signed.
|
|
@@ -42561,7 +42697,42 @@ function prefsPath() {
|
|
|
42561
42697
|
const root = process.env.ROZO_CHECKOUT_STATE_DIR ? path2.dirname(stateRoot()) : path2.join(os2.homedir(), ".rozo-checkout");
|
|
42562
42698
|
return path2.join(root, "prefs.json");
|
|
42563
42699
|
}
|
|
42564
|
-
var ALLOWED = ["lastPayerAddress", "lastAddressFamily", "lastPreset", "updatedAt"];
|
|
42700
|
+
var ALLOWED = ["lastPayerAddress", "lastAddressFamily", "lastPreset", "installId", "updatedAt"];
|
|
42701
|
+
var INSTALL_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
42702
|
+
var LOCK_WAIT_MS2 = 2e3;
|
|
42703
|
+
var LOCK_STALE_MS2 = 1e4;
|
|
42704
|
+
function sleepSync2(ms) {
|
|
42705
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
42706
|
+
}
|
|
42707
|
+
function withPrefsLock(fn) {
|
|
42708
|
+
const lock = `${prefsPath()}.lock`;
|
|
42709
|
+
fs2.mkdirSync(path2.dirname(lock), { recursive: true, mode: 448 });
|
|
42710
|
+
const deadline = Date.now() + LOCK_WAIT_MS2;
|
|
42711
|
+
for (; ; ) {
|
|
42712
|
+
try {
|
|
42713
|
+
fs2.closeSync(fs2.openSync(lock, "wx", 384));
|
|
42714
|
+
break;
|
|
42715
|
+
} catch (err) {
|
|
42716
|
+
if (err.code !== "EEXIST") throw err;
|
|
42717
|
+
}
|
|
42718
|
+
try {
|
|
42719
|
+
if (Date.now() - fs2.statSync(lock).mtimeMs > LOCK_STALE_MS2) {
|
|
42720
|
+
fs2.rmSync(lock, { force: true });
|
|
42721
|
+
continue;
|
|
42722
|
+
}
|
|
42723
|
+
} catch {
|
|
42724
|
+
continue;
|
|
42725
|
+
}
|
|
42726
|
+
if (Date.now() > deadline) return void 0;
|
|
42727
|
+
sleepSync2(10);
|
|
42728
|
+
}
|
|
42729
|
+
try {
|
|
42730
|
+
return fn();
|
|
42731
|
+
} finally {
|
|
42732
|
+
fs2.rmSync(lock, { force: true });
|
|
42733
|
+
}
|
|
42734
|
+
}
|
|
42735
|
+
var USER_FIELDS = ["lastPayerAddress", "lastAddressFamily", "lastPreset"];
|
|
42565
42736
|
function readPrefs() {
|
|
42566
42737
|
let raw;
|
|
42567
42738
|
try {
|
|
@@ -42583,21 +42754,26 @@ function readPrefs() {
|
|
|
42583
42754
|
}
|
|
42584
42755
|
return Object.keys(out2).length ? out2 : null;
|
|
42585
42756
|
}
|
|
42586
|
-
function savePrefs(update) {
|
|
42587
|
-
const existing = readPrefs() || {};
|
|
42588
|
-
const next = { ...existing };
|
|
42589
|
-
for (const k of ALLOWED) {
|
|
42590
|
-
if (k === "updatedAt") continue;
|
|
42591
|
-
const v = update?.[k];
|
|
42592
|
-
if (typeof v === "string" && v.trim()) next[k] = v.trim();
|
|
42593
|
-
}
|
|
42594
|
-
next.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
42757
|
+
function savePrefs(update, { installIdIfAbsent } = {}) {
|
|
42595
42758
|
try {
|
|
42596
|
-
|
|
42759
|
+
const saved = withPrefsLock(() => {
|
|
42760
|
+
const existing = readPrefs() || {};
|
|
42761
|
+
const next = { ...existing };
|
|
42762
|
+
for (const k of USER_FIELDS) {
|
|
42763
|
+
const v = update?.[k];
|
|
42764
|
+
if (typeof v === "string" && v.trim()) next[k] = v.trim();
|
|
42765
|
+
}
|
|
42766
|
+
if (!INSTALL_ID_RE.test(String(existing.installId ?? "")) && INSTALL_ID_RE.test(String(installIdIfAbsent ?? ""))) {
|
|
42767
|
+
next.installId = installIdIfAbsent;
|
|
42768
|
+
}
|
|
42769
|
+
next.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
42770
|
+
writeAtomic(prefsPath(), next);
|
|
42771
|
+
return next;
|
|
42772
|
+
});
|
|
42773
|
+
return saved ?? null;
|
|
42597
42774
|
} catch {
|
|
42598
42775
|
return null;
|
|
42599
42776
|
}
|
|
42600
|
-
return next;
|
|
42601
42777
|
}
|
|
42602
42778
|
|
|
42603
42779
|
// scripts/src/lib/blacklist.mjs
|
|
@@ -42723,59 +42899,6 @@ function assertNotBlacklisted(targets, blacklist) {
|
|
|
42723
42899
|
return true;
|
|
42724
42900
|
}
|
|
42725
42901
|
|
|
42726
|
-
// scripts/src/lib/ids.mjs
|
|
42727
|
-
var IdError = class extends Error {
|
|
42728
|
-
constructor(code, message) {
|
|
42729
|
-
super(message);
|
|
42730
|
-
this.code = code;
|
|
42731
|
-
}
|
|
42732
|
-
};
|
|
42733
|
-
var PL_RE = /(pl_[0-9a-zA-Z]+)/;
|
|
42734
|
-
var SESSION_RE = /(paymentSession_[A-Za-z0-9_-]+)/;
|
|
42735
|
-
var UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
42736
|
-
function extractLinkId(urlOrId) {
|
|
42737
|
-
if (typeof urlOrId !== "string" || !urlOrId.trim()) {
|
|
42738
|
-
throw new IdError("BAD_LINK", "A Coinbase payment link URL or id is required.");
|
|
42739
|
-
}
|
|
42740
|
-
const s = urlOrId.trim();
|
|
42741
|
-
const session = s.match(SESSION_RE);
|
|
42742
|
-
if (session) return { linkId: session[1], kind: "payment_session" };
|
|
42743
|
-
const link = s.match(PL_RE);
|
|
42744
|
-
if (link) return { linkId: link[1], kind: "payment_link" };
|
|
42745
|
-
if (/commerce\.coinbase\.com\/pay\//.test(s)) {
|
|
42746
|
-
throw new IdError(
|
|
42747
|
-
"LEGACY_COMMERCE_URL",
|
|
42748
|
-
"commerce.coinbase.com/pay/<uuid> uses the legacy protocol; this skill does not handle it."
|
|
42749
|
-
);
|
|
42750
|
-
}
|
|
42751
|
-
throw new IdError(
|
|
42752
|
-
"BAD_LINK",
|
|
42753
|
-
"Could not find a `pl_*` or `paymentSession_*` id in the supplied value."
|
|
42754
|
-
);
|
|
42755
|
-
}
|
|
42756
|
-
function assertRozoPaymentId(value) {
|
|
42757
|
-
const s = String(value || "").trim();
|
|
42758
|
-
if (!UUID_RE.test(s)) {
|
|
42759
|
-
throw new IdError("BAD_ROZO_PAYMENT_ID", "rozoPaymentId must be a UUID.");
|
|
42760
|
-
}
|
|
42761
|
-
return s;
|
|
42762
|
-
}
|
|
42763
|
-
function isRozoPaymentId(value) {
|
|
42764
|
-
return UUID_RE.test(String(value || "").trim());
|
|
42765
|
-
}
|
|
42766
|
-
function maskAddress2(address) {
|
|
42767
|
-
const s = String(address ?? "").trim();
|
|
42768
|
-
if (!s) return "(none)";
|
|
42769
|
-
if (s.length <= 12) return s;
|
|
42770
|
-
return `${s.slice(0, 6)}...${s.slice(-4)}`;
|
|
42771
|
-
}
|
|
42772
|
-
function maskMemo(memo) {
|
|
42773
|
-
const s = String(memo ?? "").trim();
|
|
42774
|
-
if (!s) return null;
|
|
42775
|
-
if (s.length <= 8) return s;
|
|
42776
|
-
return `${s.slice(0, 4)}...${s.slice(-3)}`;
|
|
42777
|
-
}
|
|
42778
|
-
|
|
42779
42902
|
// scripts/src/lib/expiry.mjs
|
|
42780
42903
|
var MINUTE = 6e4;
|
|
42781
42904
|
var MARGINS_MS = {
|
|
@@ -50830,6 +50953,12 @@ function formatUnits(value, decimals) {
|
|
|
50830
50953
|
return format(value, decimals);
|
|
50831
50954
|
}
|
|
50832
50955
|
|
|
50956
|
+
// node_modules/viem/_esm/utils/unit/parseGwei.js
|
|
50957
|
+
init_Value();
|
|
50958
|
+
function parseGwei(ether, unit = "wei") {
|
|
50959
|
+
return fromGwei(ether, unit);
|
|
50960
|
+
}
|
|
50961
|
+
|
|
50833
50962
|
// node_modules/viem/_esm/utils/unit/parseUnits.js
|
|
50834
50963
|
init_Value();
|
|
50835
50964
|
function parseUnits(value, decimals) {
|
|
@@ -50973,7 +51102,7 @@ init_createBatchScheduler();
|
|
|
50973
51102
|
init_stringify();
|
|
50974
51103
|
async function multicall(client, parameters) {
|
|
50975
51104
|
const { account, authorizationList, allowFailure = true, blockHash, blockNumber, blockOverrides, blockTag, requireCanonical, stateOverride } = parameters;
|
|
50976
|
-
const
|
|
51105
|
+
const contracts2 = parameters.contracts;
|
|
50977
51106
|
const batch = typeof client.batch?.multicall === "object" ? client.batch.multicall : {};
|
|
50978
51107
|
const batchSize = parameters.batchSize ?? batch.batchSize ?? 1024;
|
|
50979
51108
|
const deployless = parameters.deployless ?? batch.deployless ?? false;
|
|
@@ -50994,8 +51123,8 @@ async function multicall(client, parameters) {
|
|
|
50994
51123
|
const chunkedCalls = [[]];
|
|
50995
51124
|
let currentChunk = 0;
|
|
50996
51125
|
let currentChunkSize = 0;
|
|
50997
|
-
for (let i = 0; i <
|
|
50998
|
-
const { abi: abi2, address, args, functionName } =
|
|
51126
|
+
for (let i = 0; i < contracts2.length; i++) {
|
|
51127
|
+
const { abi: abi2, address, args, functionName } = contracts2[i];
|
|
50999
51128
|
try {
|
|
51000
51129
|
const callData = encodeFunctionData({ abi: abi2, args, functionName });
|
|
51001
51130
|
currentChunkSize += (callData.length - 2) / 2;
|
|
@@ -51089,7 +51218,7 @@ async function multicall(client, parameters) {
|
|
|
51089
51218
|
for (let j = 0; j < aggregate3Result.length; j++) {
|
|
51090
51219
|
const { returnData, success } = aggregate3Result[j];
|
|
51091
51220
|
const { callData } = batches[i][j];
|
|
51092
|
-
const { abi: abi2, address, functionName, args } =
|
|
51221
|
+
const { abi: abi2, address, functionName, args } = contracts2[results.length];
|
|
51093
51222
|
try {
|
|
51094
51223
|
if (callData === "0x")
|
|
51095
51224
|
throw new AbiDecodingZeroDataError();
|
|
@@ -51116,7 +51245,7 @@ async function multicall(client, parameters) {
|
|
|
51116
51245
|
}
|
|
51117
51246
|
}
|
|
51118
51247
|
}
|
|
51119
|
-
if (results.length !==
|
|
51248
|
+
if (results.length !== contracts2.length)
|
|
51120
51249
|
throw new BaseError2("multicall results mismatch");
|
|
51121
51250
|
return results;
|
|
51122
51251
|
}
|
|
@@ -54557,6 +54686,44 @@ function postJson(url, body, opts) {
|
|
|
54557
54686
|
return request("POST", url, { ...opts, body });
|
|
54558
54687
|
}
|
|
54559
54688
|
|
|
54689
|
+
// scripts/src/lib/identity.mjs
|
|
54690
|
+
import crypto7 from "node:crypto";
|
|
54691
|
+
var ACCOUNT_HASH_PREFIX = "rozo-acct-v1:";
|
|
54692
|
+
function isValidInstallId(value) {
|
|
54693
|
+
return typeof value === "string" && INSTALL_ID_RE.test(value);
|
|
54694
|
+
}
|
|
54695
|
+
function attributionDisabled(env = process.env) {
|
|
54696
|
+
const v = String(env.ROZO_CHECKOUT_ANON_ID ?? "").trim().toLowerCase();
|
|
54697
|
+
return ["off", "0", "false", "no"].includes(v);
|
|
54698
|
+
}
|
|
54699
|
+
function getOrCreateInstallId() {
|
|
54700
|
+
const existing = readPrefs()?.installId;
|
|
54701
|
+
if (isValidInstallId(existing)) return existing;
|
|
54702
|
+
const saved = savePrefs({}, { installIdIfAbsent: crypto7.randomUUID().toLowerCase() });
|
|
54703
|
+
if (isValidInstallId(saved?.installId)) return saved.installId;
|
|
54704
|
+
const onDisk = readPrefs()?.installId;
|
|
54705
|
+
return isValidInstallId(onDisk) ? onDisk : crypto7.randomUUID().toLowerCase();
|
|
54706
|
+
}
|
|
54707
|
+
function hashAccount(raw) {
|
|
54708
|
+
return crypto7.createHash("sha256").update(`${ACCOUNT_HASH_PREFIX}${raw}`, "utf8").digest("hex");
|
|
54709
|
+
}
|
|
54710
|
+
function rawApiKey(env) {
|
|
54711
|
+
const v = typeof env.OPENROUTER_API_KEY === "string" ? env.OPENROUTER_API_KEY.trim() : "";
|
|
54712
|
+
return v && v.length <= 1024 ? v : null;
|
|
54713
|
+
}
|
|
54714
|
+
function attributionIdentity(env = process.env) {
|
|
54715
|
+
if (attributionDisabled(env)) return {};
|
|
54716
|
+
const out2 = {};
|
|
54717
|
+
try {
|
|
54718
|
+
const id = getOrCreateInstallId();
|
|
54719
|
+
if (isValidInstallId(id)) out2.install_id = id;
|
|
54720
|
+
} catch {
|
|
54721
|
+
}
|
|
54722
|
+
const raw = rawApiKey(env);
|
|
54723
|
+
if (raw) out2.account_hash = hashAccount(raw);
|
|
54724
|
+
return out2;
|
|
54725
|
+
}
|
|
54726
|
+
|
|
54560
54727
|
// scripts/src/lib/api.mjs
|
|
54561
54728
|
var PKG_VERSION = (() => {
|
|
54562
54729
|
const requireFrom = createRequire(import.meta.url);
|
|
@@ -54572,10 +54739,16 @@ var PKG_VERSION = (() => {
|
|
|
54572
54739
|
var MPP_BASE = process.env.ROZO_CHECKOUT_MPP_BASE || "https://apiserver.mpprouter.dev/v1/services/rozo-agent-api";
|
|
54573
54740
|
var CLIENT_LABEL = `rozo-checkout-cli/${PKG_VERSION}`;
|
|
54574
54741
|
var ATTRIBUTION_CLIENT = `rozo-checkout-skill/${PKG_VERSION}`;
|
|
54575
|
-
function buildAttribution({ utmSource } = {}) {
|
|
54742
|
+
function buildAttribution({ utmSource, identity } = {}) {
|
|
54576
54743
|
const raw = utmSource !== void 0 ? utmSource : process.env.ROZO_CHECKOUT_UTM_SOURCE;
|
|
54577
54744
|
const utm_source = normalizeUtmSource(raw);
|
|
54578
|
-
|
|
54745
|
+
const ids = identity ?? attributionIdentity();
|
|
54746
|
+
return {
|
|
54747
|
+
client: ATTRIBUTION_CLIENT,
|
|
54748
|
+
...utm_source ? { utm_source } : {},
|
|
54749
|
+
...ids.install_id ? { install_id: ids.install_id } : {},
|
|
54750
|
+
...ids.account_hash ? { account_hash: ids.account_hash } : {}
|
|
54751
|
+
};
|
|
54579
54752
|
}
|
|
54580
54753
|
var INTENTS_BASE = process.env.ROZO_CHECKOUT_INTENTS_BASE || "https://intentapiv4.rozo.ai/functions/v1/payment-api";
|
|
54581
54754
|
async function quoteInvoice({ url, linkId }) {
|
|
@@ -54961,9 +55134,6 @@ function classifyStatus({
|
|
|
54961
55134
|
{ escalate: true }
|
|
54962
55135
|
);
|
|
54963
55136
|
}
|
|
54964
|
-
if (!bitrefill && (routerStatus === "paid" || coinbase?.settled === true)) {
|
|
54965
|
-
return mk("settled", "Coinbase invoice settled by the funder wallet.", { terminal: true });
|
|
54966
|
-
}
|
|
54967
55137
|
if (routerStatus === "failed_pay_invoice" || routerStatus === "failed_insufficient_balance") {
|
|
54968
55138
|
return mk(
|
|
54969
55139
|
"stuck_after_payment",
|
|
@@ -54971,6 +55141,9 @@ function classifyStatus({
|
|
|
54971
55141
|
{ terminal: false, escalate: true }
|
|
54972
55142
|
);
|
|
54973
55143
|
}
|
|
55144
|
+
if (!bitrefill && (routerStatus === "paid" || coinbase?.settled === true)) {
|
|
55145
|
+
return mk("settled", "Coinbase invoice settled by the funder wallet.", { terminal: true });
|
|
55146
|
+
}
|
|
54974
55147
|
if (moneyDetected && receipt && receipt.state === "underpaid") {
|
|
54975
55148
|
return mk(
|
|
54976
55149
|
"underpaid",
|
|
@@ -55228,6 +55401,17 @@ async function main2(argv) {
|
|
|
55228
55401
|
expiresAt: created.expiresAt ?? null,
|
|
55229
55402
|
expiresIn: Number.isFinite(remainingMs) ? formatRemaining(remainingMs) : null
|
|
55230
55403
|
},
|
|
55404
|
+
// Machine-readable form of the guidance below. The live order keeps
|
|
55405
|
+
// its own coin; a different coin is only possible once it has
|
|
55406
|
+
// expired unfunded (then pay on the same link creates a new order).
|
|
55407
|
+
nextAction: {
|
|
55408
|
+
type: "choose_method",
|
|
55409
|
+
canRetryQuery: true,
|
|
55410
|
+
canCreateOrder: false,
|
|
55411
|
+
canSend: false,
|
|
55412
|
+
payExistingWith: { chainId: existing.chainId ?? null, tokenSymbol: existing.tokenSymbol ?? null },
|
|
55413
|
+
switchPossibleAfter: created.expiresAt ?? null
|
|
55414
|
+
},
|
|
55231
55415
|
guidance: `Either pay the existing order with ${existing.tokenSymbol ?? "its own coin"} (re-run with --chain ${existing.chainId} --token ${existing.tokenSymbol}), or wait ${Number.isFinite(remainingMs) ? formatRemaining(remainingMs) : "for it to expire"} for it to expire and then create a new one. Unpaid orders cost nothing.`
|
|
55232
55416
|
},
|
|
55233
55417
|
EXIT_ERROR
|
|
@@ -55841,6 +56025,241 @@ async function run3(argv = process.argv.slice(2)) {
|
|
|
55841
56025
|
return main3(argv);
|
|
55842
56026
|
}
|
|
55843
56027
|
|
|
56028
|
+
// scripts/src/lib/receipt.mjs
|
|
56029
|
+
var SCHEMA_VERSION = 1;
|
|
56030
|
+
var PAYMENT_OUTCOMES = Object.freeze([
|
|
56031
|
+
"awaiting_payment",
|
|
56032
|
+
"processing",
|
|
56033
|
+
"settled",
|
|
56034
|
+
"expired_unfunded",
|
|
56035
|
+
"needs_attention",
|
|
56036
|
+
"unknown"
|
|
56037
|
+
]);
|
|
56038
|
+
var NEXT_ACTION_TYPES = Object.freeze([
|
|
56039
|
+
"pay",
|
|
56040
|
+
"check_status",
|
|
56041
|
+
"choose_method",
|
|
56042
|
+
"request_new_invoice",
|
|
56043
|
+
"contact_support",
|
|
56044
|
+
"none"
|
|
56045
|
+
]);
|
|
56046
|
+
var STATE_TO_OUTCOME = Object.freeze({
|
|
56047
|
+
awaiting_deposit: "awaiting_payment",
|
|
56048
|
+
payin_detected: "processing",
|
|
56049
|
+
payin_confirmed: "processing",
|
|
56050
|
+
bridging: "processing",
|
|
56051
|
+
paying_coinbase: "processing",
|
|
56052
|
+
settled: "settled",
|
|
56053
|
+
expired_unfunded: "expired_unfunded",
|
|
56054
|
+
invoice_expired: "expired_unfunded",
|
|
56055
|
+
deadline_unknown: "needs_attention",
|
|
56056
|
+
underpaid: "needs_attention",
|
|
56057
|
+
stuck_after_payment: "needs_attention",
|
|
56058
|
+
unknown: "unknown"
|
|
56059
|
+
});
|
|
56060
|
+
var SERVICE_DELIVERY_NOTE = {
|
|
56061
|
+
coinbase: "The invoice is paid. Arrival of the OpenRouter credits (or other goods) is not independently verified by Rozo; check the merchant account.",
|
|
56062
|
+
bitrefill: "USDC reached the Bitrefill invoice address. Delivery of the Bitrefill order is not independently verified by Rozo; check the Bitrefill order page."
|
|
56063
|
+
};
|
|
56064
|
+
function merchantSettlementProved(result) {
|
|
56065
|
+
if (result?.provider === "bitrefill") return result?.state === "settled";
|
|
56066
|
+
return result?.backend?.coinbaseSettled === true;
|
|
56067
|
+
}
|
|
56068
|
+
function hasLocalSend(result) {
|
|
56069
|
+
return Boolean(result?.localSend && result.localSend.status);
|
|
56070
|
+
}
|
|
56071
|
+
function paymentOutcomeFor(result) {
|
|
56072
|
+
if (!result) return "unknown";
|
|
56073
|
+
if (result.escalate) return "needs_attention";
|
|
56074
|
+
if (result.unknown) return "unknown";
|
|
56075
|
+
let outcome = Object.prototype.hasOwnProperty.call(STATE_TO_OUTCOME, result.state) ? STATE_TO_OUTCOME[result.state] : "unknown";
|
|
56076
|
+
if (outcome === "settled" && !merchantSettlementProved(result)) outcome = "processing";
|
|
56077
|
+
if (outcome === "settled" && result.provider !== "bitrefill") {
|
|
56078
|
+
const router = result.backend?.routerStatus ?? null;
|
|
56079
|
+
if (router && router.startsWith("failed_")) return "needs_attention";
|
|
56080
|
+
if (router && router !== "paid") outcome = "processing";
|
|
56081
|
+
}
|
|
56082
|
+
if (!result.authoritativeView && (outcome === "awaiting_payment" || outcome === "expired_unfunded")) {
|
|
56083
|
+
return "unknown";
|
|
56084
|
+
}
|
|
56085
|
+
if ((result.moneyDetected || hasLocalSend(result)) && outcome === "expired_unfunded") {
|
|
56086
|
+
return "needs_attention";
|
|
56087
|
+
}
|
|
56088
|
+
if (result.moneyDetected && outcome === "awaiting_payment") return "processing";
|
|
56089
|
+
return outcome;
|
|
56090
|
+
}
|
|
56091
|
+
function nextActionFor(result, outcome = paymentOutcomeFor(result)) {
|
|
56092
|
+
const locked = Boolean(result?.moneyDetected) || hasLocalSend(result);
|
|
56093
|
+
const base = { canRetryQuery: true, canCreateOrder: false, canSend: false };
|
|
56094
|
+
const bitrefill = result?.provider === "bitrefill";
|
|
56095
|
+
switch (outcome) {
|
|
56096
|
+
case "awaiting_payment":
|
|
56097
|
+
if (locked) {
|
|
56098
|
+
return {
|
|
56099
|
+
...base,
|
|
56100
|
+
type: "check_status",
|
|
56101
|
+
message: "A send was already recorded for this order. Do NOT pay again; poll status until the pay-in shows up."
|
|
56102
|
+
};
|
|
56103
|
+
}
|
|
56104
|
+
if (!result?.sendWindow?.ok) {
|
|
56105
|
+
const code = result?.sendWindow?.code ?? "SEND_WINDOW_UNKNOWN";
|
|
56106
|
+
if (code === "LINK_NO_LONGER_PAYABLE") {
|
|
56107
|
+
return {
|
|
56108
|
+
...base,
|
|
56109
|
+
type: "request_new_invoice",
|
|
56110
|
+
reason: code,
|
|
56111
|
+
message: "The merchant link no longer accepts payment. Do NOT fund this order; get a new payment link."
|
|
56112
|
+
};
|
|
56113
|
+
}
|
|
56114
|
+
if (code === "EXPIRED" || code === "EXPIRY_MARGIN") {
|
|
56115
|
+
return {
|
|
56116
|
+
...base,
|
|
56117
|
+
type: "check_status",
|
|
56118
|
+
reason: code,
|
|
56119
|
+
message: "Too little time is left to pay this order safely. Do NOT fund it. Once it shows expired_unfunded, run pay again on the same link for a new order."
|
|
56120
|
+
};
|
|
56121
|
+
}
|
|
56122
|
+
return {
|
|
56123
|
+
...base,
|
|
56124
|
+
type: "check_status",
|
|
56125
|
+
reason: code,
|
|
56126
|
+
message: "Whether this order can still be paid could not be proved. Do NOT fund it on a guess; retry."
|
|
56127
|
+
};
|
|
56128
|
+
}
|
|
56129
|
+
return {
|
|
56130
|
+
...base,
|
|
56131
|
+
type: "pay",
|
|
56132
|
+
canSend: true,
|
|
56133
|
+
message: "Send the exact amount from the order deposit block, once."
|
|
56134
|
+
};
|
|
56135
|
+
case "processing":
|
|
56136
|
+
return {
|
|
56137
|
+
...base,
|
|
56138
|
+
type: "check_status",
|
|
56139
|
+
message: "Funds are moving. Do NOT pay again; poll status."
|
|
56140
|
+
};
|
|
56141
|
+
case "settled":
|
|
56142
|
+
return { ...base, type: "none", canRetryQuery: false, message: "Nothing to do." };
|
|
56143
|
+
case "expired_unfunded":
|
|
56144
|
+
if (bitrefill) {
|
|
56145
|
+
return {
|
|
56146
|
+
...base,
|
|
56147
|
+
type: "request_new_invoice",
|
|
56148
|
+
message: "Nothing was funded. Create a fresh Bitrefill invoice and pay that one."
|
|
56149
|
+
};
|
|
56150
|
+
}
|
|
56151
|
+
return {
|
|
56152
|
+
...base,
|
|
56153
|
+
type: "choose_method",
|
|
56154
|
+
canCreateOrder: true,
|
|
56155
|
+
message: "Nothing arrived for this order. If your own wallet shows no send to the old deposit address and no pending Lightning payment, run pay again on the same link: it creates a new order, and you may pick a different coin. Never fund the old deposit address."
|
|
56156
|
+
};
|
|
56157
|
+
case "needs_attention":
|
|
56158
|
+
if (result?.state === "deadline_unknown" && !locked) {
|
|
56159
|
+
return {
|
|
56160
|
+
...base,
|
|
56161
|
+
type: "request_new_invoice",
|
|
56162
|
+
message: "The invoice deadline is unknown here, so the order is not payable. Re-run pay with the invoice details, or create a fresh invoice."
|
|
56163
|
+
};
|
|
56164
|
+
}
|
|
56165
|
+
return {
|
|
56166
|
+
...base,
|
|
56167
|
+
type: "contact_support",
|
|
56168
|
+
message: "Do NOT pay again and do NOT create a new order. Keep linkId, rozoPaymentId and every tx hash, and contact Rozo support."
|
|
56169
|
+
};
|
|
56170
|
+
default:
|
|
56171
|
+
return {
|
|
56172
|
+
...base,
|
|
56173
|
+
type: "check_status",
|
|
56174
|
+
message: "The order state could not be established. This is not evidence that nothing was paid. Retry with the rozoPaymentId before doing anything else."
|
|
56175
|
+
};
|
|
56176
|
+
}
|
|
56177
|
+
}
|
|
56178
|
+
function sourcePaymentLayer(result) {
|
|
56179
|
+
const payin = result?.payin || {};
|
|
56180
|
+
let status;
|
|
56181
|
+
if (result?.unknown || !result?.authoritativeView) status = "unknown";
|
|
56182
|
+
else if (payin.confirmedAt) status = "confirmed";
|
|
56183
|
+
else if (result?.moneyDetected) status = "detected";
|
|
56184
|
+
else status = "none";
|
|
56185
|
+
return {
|
|
56186
|
+
status,
|
|
56187
|
+
evidence: status === "unknown" ? null : "rozo-intents",
|
|
56188
|
+
chain: payin.chain ?? null,
|
|
56189
|
+
expected: payin.expected ?? null,
|
|
56190
|
+
received: payin.received ?? null,
|
|
56191
|
+
txHash: payin.txHash ?? null,
|
|
56192
|
+
confirmedAt: payin.confirmedAt ?? null,
|
|
56193
|
+
localSend: result?.localSend ? {
|
|
56194
|
+
status: result.localSend.status,
|
|
56195
|
+
txHash: result.localSend.txHash ?? null,
|
|
56196
|
+
claimedAt: result.localSend.claimedAt ?? null,
|
|
56197
|
+
// A wallet broadcast proves the payer sent, not that Rozo received.
|
|
56198
|
+
evidence: "local-wallet"
|
|
56199
|
+
} : null
|
|
56200
|
+
};
|
|
56201
|
+
}
|
|
56202
|
+
function merchantSettlementLayer(result) {
|
|
56203
|
+
if (result?.provider === "bitrefill") {
|
|
56204
|
+
const proved = result?.state === "settled";
|
|
56205
|
+
return {
|
|
56206
|
+
status: proved ? "confirmed" : result?.unknown ? "unknown" : "pending",
|
|
56207
|
+
evidence: proved ? "rozo-payout" : null,
|
|
56208
|
+
provider: "bitrefill",
|
|
56209
|
+
txHash: result?.payout?.txHash ?? null,
|
|
56210
|
+
confirmedAt: result?.payout?.confirmedAt ?? null
|
|
56211
|
+
};
|
|
56212
|
+
}
|
|
56213
|
+
const backend = result?.backend || {};
|
|
56214
|
+
let status;
|
|
56215
|
+
if (backend.routerStatus && backend.routerStatus.startsWith("failed_")) status = "unknown";
|
|
56216
|
+
else if (backend.coinbaseSettled === true) status = "confirmed";
|
|
56217
|
+
else if (backend.coinbaseSettled === null || backend.coinbaseSettled === void 0) {
|
|
56218
|
+
status = backend.routerStatus === "paid" || result?.unknown ? "unknown" : "pending";
|
|
56219
|
+
} else status = "pending";
|
|
56220
|
+
return {
|
|
56221
|
+
status,
|
|
56222
|
+
evidence: status === "confirmed" ? "coinbase" : null,
|
|
56223
|
+
provider: "coinbase",
|
|
56224
|
+
coinbaseStatus: backend.coinbaseStatus ?? null,
|
|
56225
|
+
routerStatus: backend.routerStatus ?? null,
|
|
56226
|
+
// The router reporting `paid` is recorded, but it is not settlement proof.
|
|
56227
|
+
routerReportsPaid: backend.routerStatus === "paid",
|
|
56228
|
+
// Coinbase may read settled because someone else paid the link.
|
|
56229
|
+
routerFailed: Boolean(backend.routerStatus && backend.routerStatus.startsWith("failed_")),
|
|
56230
|
+
payoutTxHash: result?.payout?.txHash ?? null
|
|
56231
|
+
};
|
|
56232
|
+
}
|
|
56233
|
+
function serviceDeliveryLayer(result, merchant) {
|
|
56234
|
+
return {
|
|
56235
|
+
status: "unknown",
|
|
56236
|
+
evidence: null,
|
|
56237
|
+
note: merchant.status === "confirmed" ? SERVICE_DELIVERY_NOTE[result?.provider === "bitrefill" ? "bitrefill" : "coinbase"] : "Not applicable until the merchant invoice is settled."
|
|
56238
|
+
};
|
|
56239
|
+
}
|
|
56240
|
+
function buildReceipt(result, { observedAt = (/* @__PURE__ */ new Date()).toISOString() } = {}) {
|
|
56241
|
+
const paymentOutcome = paymentOutcomeFor(result);
|
|
56242
|
+
const merchantSettlement = merchantSettlementLayer(result);
|
|
56243
|
+
return {
|
|
56244
|
+
schemaVersion: SCHEMA_VERSION,
|
|
56245
|
+
orderId: result?.rozoPaymentId ?? null,
|
|
56246
|
+
linkId: result?.linkId ?? null,
|
|
56247
|
+
provider: result?.provider ?? null,
|
|
56248
|
+
observedAt,
|
|
56249
|
+
paymentOutcome,
|
|
56250
|
+
state: result?.state ?? "unknown",
|
|
56251
|
+
sourcePayment: sourcePaymentLayer(result),
|
|
56252
|
+
merchantSettlement,
|
|
56253
|
+
serviceDelivery: serviceDeliveryLayer(result, merchantSettlement),
|
|
56254
|
+
nextAction: nextActionFor(result, paymentOutcome)
|
|
56255
|
+
};
|
|
56256
|
+
}
|
|
56257
|
+
function receiptExitCode(outcome) {
|
|
56258
|
+
if (outcome === "settled") return 0;
|
|
56259
|
+
if (outcome === "expired_unfunded" || outcome === "needs_attention") return 1;
|
|
56260
|
+
return 3;
|
|
56261
|
+
}
|
|
56262
|
+
|
|
55844
56263
|
// scripts/src/status.mjs
|
|
55845
56264
|
var POLL_INTERVAL_MS = 1e4;
|
|
55846
56265
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -55855,6 +56274,42 @@ function resolveProvider(explicit, rozoPaymentId) {
|
|
|
55855
56274
|
}
|
|
55856
56275
|
return null;
|
|
55857
56276
|
}
|
|
56277
|
+
function localSendFor(id, read = readState) {
|
|
56278
|
+
if (!id || !isRozoPaymentId(id)) return null;
|
|
56279
|
+
let state;
|
|
56280
|
+
try {
|
|
56281
|
+
state = read(id);
|
|
56282
|
+
} catch (err) {
|
|
56283
|
+
return { status: "unreadable", txHash: null, claimedAt: null, error: err?.code ?? "STATE_UNREADABLE" };
|
|
56284
|
+
}
|
|
56285
|
+
const send = state?.send;
|
|
56286
|
+
if (!send || !send.status) return null;
|
|
56287
|
+
return { status: send.status, txHash: publicHash(send.txHash ?? null), claimedAt: send.claimedAt ?? null };
|
|
56288
|
+
}
|
|
56289
|
+
function sendWindowFor({ provider, chainId, payment, status, bitrefillExpiry, now = Date.now() }) {
|
|
56290
|
+
if (provider === "bitrefill") {
|
|
56291
|
+
const deadline = bitrefillExpiry ?? null;
|
|
56292
|
+
const expiry2 = checkExpiry({
|
|
56293
|
+
now,
|
|
56294
|
+
chainId,
|
|
56295
|
+
intentExpiresAt: payment?.expiresAt ?? deadline,
|
|
56296
|
+
coinbaseExpiry: deadline
|
|
56297
|
+
});
|
|
56298
|
+
return expiry2.ok ? { ok: true, code: null, minutesOfSlack: Math.floor(expiry2.msOfSlack / 6e4), deadlineMs: expiry2.effectiveDeadlineMs } : { ok: false, code: expiry2.code, reason: expiry2.reason };
|
|
56299
|
+
}
|
|
56300
|
+
if (!status) {
|
|
56301
|
+
return { ok: false, code: "LINK_PAYABILITY_UNKNOWN", reason: "The Coinbase link state could not be read." };
|
|
56302
|
+
}
|
|
56303
|
+
const payable = checkPayable(status, now);
|
|
56304
|
+
if (!payable.ok) return { ok: false, code: payable.code, reason: payable.reason };
|
|
56305
|
+
const expiry = checkExpiry({
|
|
56306
|
+
now,
|
|
56307
|
+
chainId,
|
|
56308
|
+
intentExpiresAt: payment?.expiresAt ?? status?.rozoPayment?.expiresAt,
|
|
56309
|
+
coinbaseExpiry: status?.coinbase?.preApprovalExpiry
|
|
56310
|
+
});
|
|
56311
|
+
return expiry.ok ? { ok: true, code: null, minutesOfSlack: Math.floor(expiry.msOfSlack / 6e4), deadlineMs: expiry.effectiveDeadlineMs } : { ok: false, code: expiry.code, reason: expiry.reason };
|
|
56312
|
+
}
|
|
55858
56313
|
async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
55859
56314
|
let provider = resolveProvider(explicitProvider, rozoPaymentId);
|
|
55860
56315
|
let prefetched = null;
|
|
@@ -55952,7 +56407,7 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
55952
56407
|
expected: source.amount ? formatAmount(source) : null,
|
|
55953
56408
|
received: source.amountReceived ?? null,
|
|
55954
56409
|
receipt: verdict.receipt,
|
|
55955
|
-
txHash: source.txHash ?? null,
|
|
56410
|
+
txHash: publicHash(source.txHash ?? null),
|
|
55956
56411
|
confirmedAt: source.confirmedAt ?? null,
|
|
55957
56412
|
senderAddressMasked: source.senderAddress ? maskAddress2(source.senderAddress) : null,
|
|
55958
56413
|
chain: source.chainId ? chainName(source.chainId) : null
|
|
@@ -55971,9 +56426,11 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
55971
56426
|
};
|
|
55972
56427
|
})(),
|
|
55973
56428
|
payout: {
|
|
55974
|
-
txHash: payment?.destination?.txHash ?? status?.rozoPayment?.destination?.txHash ?? null,
|
|
56429
|
+
txHash: publicHash(payment?.destination?.txHash ?? status?.rozoPayment?.destination?.txHash ?? null),
|
|
55975
56430
|
confirmedAt: payment?.destination?.confirmedAt ?? status?.rozoPayment?.destination?.confirmedAt ?? null
|
|
55976
56431
|
},
|
|
56432
|
+
localSend: localSendFor(id),
|
|
56433
|
+
sendWindow: state === "awaiting_deposit" ? sendWindowFor({ provider, chainId: source.chainId, payment, status, bitrefillExpiry }) : null,
|
|
55977
56434
|
errors: [statusError, paymentError].filter(Boolean)
|
|
55978
56435
|
};
|
|
55979
56436
|
}
|
|
@@ -56022,9 +56479,16 @@ async function main4(argv) {
|
|
|
56022
56479
|
const guidance = statusGuidance(result);
|
|
56023
56480
|
const unresolved = watch && !result.terminal && !result.escalate && !result.unknown;
|
|
56024
56481
|
const failed = result.escalate || result.unknown || !result.authoritativeView;
|
|
56482
|
+
const paymentOutcome = paymentOutcomeFor(result);
|
|
56025
56483
|
emit(
|
|
56026
56484
|
{
|
|
56485
|
+
// `success` keeps its historical meaning (the query produced a clean
|
|
56486
|
+
// answer). It is NOT "the invoice is paid": read paymentOutcome.
|
|
56027
56487
|
success: !failed,
|
|
56488
|
+
schemaVersion: SCHEMA_VERSION,
|
|
56489
|
+
operationOk: !result.unknown,
|
|
56490
|
+
paymentOutcome,
|
|
56491
|
+
nextAction: nextActionFor(result, paymentOutcome),
|
|
56028
56492
|
step: "status",
|
|
56029
56493
|
...result,
|
|
56030
56494
|
history,
|
|
@@ -56040,6 +56504,275 @@ async function run4(argv = process.argv.slice(2)) {
|
|
|
56040
56504
|
return main4(argv);
|
|
56041
56505
|
}
|
|
56042
56506
|
|
|
56507
|
+
// scripts/src/receipt.mjs
|
|
56508
|
+
async function main5(argv) {
|
|
56509
|
+
const args = parseArgs(argv);
|
|
56510
|
+
const rozoPaymentId = args["rozo-payment-id"] || (isRozoPaymentId(args._[0]) ? args._[0] : null);
|
|
56511
|
+
const linkId = args["link-id"] || (!rozoPaymentId ? args._[0] : null);
|
|
56512
|
+
const provider = args.provider ? String(args.provider) : null;
|
|
56513
|
+
if (provider && provider !== "bitrefill" && provider !== "coinbase") {
|
|
56514
|
+
usage("--provider must be coinbase or bitrefill");
|
|
56515
|
+
}
|
|
56516
|
+
if (provider === "bitrefill" && !rozoPaymentId) {
|
|
56517
|
+
usage("A Bitrefill order is tracked by --rozo-payment-id <uuid>.");
|
|
56518
|
+
}
|
|
56519
|
+
if (!rozoPaymentId && !linkId) {
|
|
56520
|
+
usage("Required: --rozo-payment-id <uuid> and/or --link-id <pl_* | paymentSession_*>");
|
|
56521
|
+
}
|
|
56522
|
+
const result = await snapshot({ rozoPaymentId, linkId, provider });
|
|
56523
|
+
const receipt = buildReceipt(result);
|
|
56524
|
+
const exitCode = receiptExitCode(receipt.paymentOutcome);
|
|
56525
|
+
emit(
|
|
56526
|
+
{
|
|
56527
|
+
success: receipt.paymentOutcome === "settled",
|
|
56528
|
+
operationOk: !result.unknown,
|
|
56529
|
+
step: "receipt",
|
|
56530
|
+
receipt,
|
|
56531
|
+
errors: result.errors,
|
|
56532
|
+
...exitCode === 0 ? {} : { support: SUPPORT }
|
|
56533
|
+
},
|
|
56534
|
+
exitCode
|
|
56535
|
+
);
|
|
56536
|
+
}
|
|
56537
|
+
async function run5(argv = process.argv.slice(2)) {
|
|
56538
|
+
return main5(argv);
|
|
56539
|
+
}
|
|
56540
|
+
|
|
56541
|
+
// scripts/src/resume.mjs
|
|
56542
|
+
var PAID_STATES = /* @__PURE__ */ new Set([
|
|
56543
|
+
"payin_detected",
|
|
56544
|
+
"payin_confirmed",
|
|
56545
|
+
"bridging",
|
|
56546
|
+
"paying_coinbase",
|
|
56547
|
+
"settled"
|
|
56548
|
+
]);
|
|
56549
|
+
var EXPIRED_STATES = /* @__PURE__ */ new Set(["expired_unfunded", "invoice_expired"]);
|
|
56550
|
+
function localPaymentLink(id) {
|
|
56551
|
+
try {
|
|
56552
|
+
return readState(id)?.paymentLink ?? null;
|
|
56553
|
+
} catch {
|
|
56554
|
+
return null;
|
|
56555
|
+
}
|
|
56556
|
+
}
|
|
56557
|
+
function resumeVerdict(snap) {
|
|
56558
|
+
const base = {
|
|
56559
|
+
rozoPaymentId: snap.rozoPaymentId,
|
|
56560
|
+
linkId: snap.linkId ?? null,
|
|
56561
|
+
provider: snap.provider,
|
|
56562
|
+
state: snap.state,
|
|
56563
|
+
moneyDetected: Boolean(snap.moneyDetected)
|
|
56564
|
+
};
|
|
56565
|
+
if (snap.escalate) {
|
|
56566
|
+
return { ...base, resumable: false, outcome: "needs_attention", code: "ORDER_NEEDS_ATTENTION", exitCode: EXIT_ERROR, message: statusGuidance(snap) };
|
|
56567
|
+
}
|
|
56568
|
+
if (snap.unknown || !snap.authoritativeView) {
|
|
56569
|
+
return { ...base, resumable: false, outcome: "unknown", code: "ORDER_STATE_UNKNOWN", exitCode: EXIT_ERROR, message: statusGuidance(snap) };
|
|
56570
|
+
}
|
|
56571
|
+
if (snap.moneyDetected || PAID_STATES.has(snap.state)) {
|
|
56572
|
+
const done = snap.state === "settled";
|
|
56573
|
+
return {
|
|
56574
|
+
...base,
|
|
56575
|
+
resumable: false,
|
|
56576
|
+
outcome: done ? "paid" : "paid_in_progress",
|
|
56577
|
+
code: "ORDER_ALREADY_PAID",
|
|
56578
|
+
exitCode: EXIT_OK,
|
|
56579
|
+
message: done ? "This order is already paid and settled. Nothing to resume. Do not pay again." : `A payment for this order was already detected (state: ${snap.state}). Do NOT pay again. Follow it with: rozo-checkout status ${snap.rozoPaymentId} --watch`
|
|
56580
|
+
};
|
|
56581
|
+
}
|
|
56582
|
+
if (EXPIRED_STATES.has(snap.state)) {
|
|
56583
|
+
return {
|
|
56584
|
+
...base,
|
|
56585
|
+
resumable: false,
|
|
56586
|
+
outcome: "expired",
|
|
56587
|
+
code: "ORDER_EXPIRED",
|
|
56588
|
+
exitCode: EXIT_ERROR,
|
|
56589
|
+
message: snap.provider === "bitrefill" ? "This order expired before any funds arrived. Nothing was lost. Create a fresh Bitrefill invoice and run pay again." : EXPIRED_UNFUNDED_GUIDANCE
|
|
56590
|
+
};
|
|
56591
|
+
}
|
|
56592
|
+
if (snap.localSend) {
|
|
56593
|
+
return {
|
|
56594
|
+
...base,
|
|
56595
|
+
resumable: false,
|
|
56596
|
+
outcome: "sent_locally",
|
|
56597
|
+
code: "ALREADY_SENT",
|
|
56598
|
+
exitCode: EXIT_ERROR,
|
|
56599
|
+
message: `This machine already recorded a send for this order (${snap.localSend.status}). Do NOT pay again. Check it with: rozo-checkout status ${snap.rozoPaymentId}`
|
|
56600
|
+
};
|
|
56601
|
+
}
|
|
56602
|
+
if (snap.state !== "awaiting_deposit") {
|
|
56603
|
+
return {
|
|
56604
|
+
...base,
|
|
56605
|
+
resumable: false,
|
|
56606
|
+
outcome: "not_payable",
|
|
56607
|
+
code: "ORDER_NOT_RESUMABLE",
|
|
56608
|
+
exitCode: EXIT_ERROR,
|
|
56609
|
+
message: `This order is in state "${snap.state}" and cannot be paid now. ${statusGuidance(snap)}`
|
|
56610
|
+
};
|
|
56611
|
+
}
|
|
56612
|
+
if (!snap.sendWindow?.ok) {
|
|
56613
|
+
return {
|
|
56614
|
+
...base,
|
|
56615
|
+
resumable: false,
|
|
56616
|
+
outcome: "expired",
|
|
56617
|
+
code: snap.sendWindow?.code ?? "SEND_WINDOW_CLOSED",
|
|
56618
|
+
exitCode: EXIT_ERROR,
|
|
56619
|
+
message: `${snap.sendWindow?.reason ?? "Not enough time is left to pay this order safely."} Do not fund it. Let it expire unfunded, then create a new order for the same link.`
|
|
56620
|
+
};
|
|
56621
|
+
}
|
|
56622
|
+
return { ...base, resumable: true, outcome: "awaiting_deposit", code: null, exitCode: EXIT_OK, message: null };
|
|
56623
|
+
}
|
|
56624
|
+
async function main6(argv) {
|
|
56625
|
+
const args = parseArgs(argv);
|
|
56626
|
+
const rozoPaymentId = args["rozo-payment-id"] || args._[0];
|
|
56627
|
+
if (!rozoPaymentId || rozoPaymentId === true) usage("Required: <rozoPaymentId> (the order id printed by pay)");
|
|
56628
|
+
if (!isRozoPaymentId(String(rozoPaymentId))) {
|
|
56629
|
+
usage("resume takes the order id (rozoPaymentId, a UUID) printed by pay, not a payment link.");
|
|
56630
|
+
}
|
|
56631
|
+
const id = String(rozoPaymentId);
|
|
56632
|
+
const confirmed = Boolean(args.confirm);
|
|
56633
|
+
let blacklist;
|
|
56634
|
+
try {
|
|
56635
|
+
blacklist = loadBlacklist();
|
|
56636
|
+
} catch (err) {
|
|
56637
|
+
throw new SkillError("BLACKLIST_UNAVAILABLE", `Compromised-address list unusable: ${err.message} Refusing to proceed.`);
|
|
56638
|
+
}
|
|
56639
|
+
const snap = await snapshot({ rozoPaymentId: id });
|
|
56640
|
+
const verdict = resumeVerdict(snap);
|
|
56641
|
+
if (!verdict.resumable) {
|
|
56642
|
+
emit(
|
|
56643
|
+
{
|
|
56644
|
+
success: verdict.exitCode === EXIT_OK,
|
|
56645
|
+
step: "resume",
|
|
56646
|
+
resumable: false,
|
|
56647
|
+
outcome: verdict.outcome,
|
|
56648
|
+
rozoPaymentId: id,
|
|
56649
|
+
linkId: verdict.linkId,
|
|
56650
|
+
state: verdict.state,
|
|
56651
|
+
moneyDetected: verdict.moneyDetected,
|
|
56652
|
+
...verdict.exitCode === EXIT_OK ? { message: verdict.message } : { error: { code: verdict.code, message: verdict.message } },
|
|
56653
|
+
payin: snap.payin,
|
|
56654
|
+
...verdict.exitCode === EXIT_OK ? {} : { support: SUPPORT }
|
|
56655
|
+
},
|
|
56656
|
+
verdict.exitCode
|
|
56657
|
+
);
|
|
56658
|
+
}
|
|
56659
|
+
const payment = await getPayment(id);
|
|
56660
|
+
const source = payment?.source || {};
|
|
56661
|
+
const guard = reuseGuard({ payment, requested: { chainId: source.chainId, tokenSymbol: source.tokenSymbol }, reused: true });
|
|
56662
|
+
if (!guard.ok) {
|
|
56663
|
+
emit(
|
|
56664
|
+
{
|
|
56665
|
+
success: false,
|
|
56666
|
+
step: "resume",
|
|
56667
|
+
resumable: false,
|
|
56668
|
+
outcome: guard.moneyDetected ? "paid_in_progress" : "not_payable",
|
|
56669
|
+
rozoPaymentId: id,
|
|
56670
|
+
moneyDetected: guard.moneyDetected,
|
|
56671
|
+
error: { code: guard.code, message: guard.reason },
|
|
56672
|
+
guidance: guard.moneyDetected ? "A payment for this order was detected. Do NOT pay again." : "This order cannot be paid as it stands. Do not fund it.",
|
|
56673
|
+
support: SUPPORT
|
|
56674
|
+
},
|
|
56675
|
+
EXIT_ERROR
|
|
56676
|
+
);
|
|
56677
|
+
}
|
|
56678
|
+
const finalWindow = checkExpiry({
|
|
56679
|
+
now: Date.now(),
|
|
56680
|
+
chainId: source.chainId,
|
|
56681
|
+
intentExpiresAt: payment?.expiresAt ?? snap.sendWindow?.deadlineMs,
|
|
56682
|
+
coinbaseExpiry: snap.sendWindow?.deadlineMs
|
|
56683
|
+
});
|
|
56684
|
+
if (!finalWindow.ok) {
|
|
56685
|
+
emit(
|
|
56686
|
+
{
|
|
56687
|
+
success: false,
|
|
56688
|
+
step: "resume",
|
|
56689
|
+
resumable: false,
|
|
56690
|
+
outcome: "expired",
|
|
56691
|
+
rozoPaymentId: id,
|
|
56692
|
+
moneyDetected: false,
|
|
56693
|
+
error: { code: finalWindow.code, message: finalWindow.reason },
|
|
56694
|
+
guidance: "Not enough time is left to pay this order safely. Do not fund it; let it expire unfunded and create a new order.",
|
|
56695
|
+
support: SUPPORT
|
|
56696
|
+
},
|
|
56697
|
+
EXIT_ERROR
|
|
56698
|
+
);
|
|
56699
|
+
}
|
|
56700
|
+
const lightning = String(source.chainId) === "lightning";
|
|
56701
|
+
if (!lightning) {
|
|
56702
|
+
try {
|
|
56703
|
+
assertNotBlacklisted(
|
|
56704
|
+
[{ address: source.receiverAddress, family: chainFamily(source.chainId), role: "deposit address" }],
|
|
56705
|
+
blacklist
|
|
56706
|
+
);
|
|
56707
|
+
} catch (err) {
|
|
56708
|
+
emit(
|
|
56709
|
+
{
|
|
56710
|
+
success: false,
|
|
56711
|
+
step: "resume",
|
|
56712
|
+
resumable: false,
|
|
56713
|
+
rozoPaymentId: id,
|
|
56714
|
+
error: { code: err.code, message: err.message },
|
|
56715
|
+
guidance: "Do NOT send anything. Report this to the operator immediately.",
|
|
56716
|
+
support: SUPPORT
|
|
56717
|
+
},
|
|
56718
|
+
EXIT_ERROR
|
|
56719
|
+
);
|
|
56720
|
+
}
|
|
56721
|
+
}
|
|
56722
|
+
const expiresAt = payment?.expiresAt ?? snap.expiry?.expiresAt ?? null;
|
|
56723
|
+
const msRemaining = expiresAt ? Date.parse(expiresAt) - Date.now() : NaN;
|
|
56724
|
+
const expiresIn = Number.isFinite(msRemaining) ? formatRemaining(msRemaining) : null;
|
|
56725
|
+
const paymentLink = payment?.paymentLink ?? localPaymentLink(id);
|
|
56726
|
+
const bolt11 = source.lnInvoice ?? payment?.lnInvoice ?? null;
|
|
56727
|
+
emit({
|
|
56728
|
+
success: true,
|
|
56729
|
+
step: "resume",
|
|
56730
|
+
resumable: true,
|
|
56731
|
+
outcome: "awaiting_deposit",
|
|
56732
|
+
confirmed,
|
|
56733
|
+
rozoPaymentId: id,
|
|
56734
|
+
linkId: snap.linkId ?? payment?.orderId ?? null,
|
|
56735
|
+
provider: snap.provider,
|
|
56736
|
+
state: snap.state,
|
|
56737
|
+
// Hosted pay page for this order: a human can open it and pay from there.
|
|
56738
|
+
// Shown in full only for Rozo's own pay-page hosts; anything else is
|
|
56739
|
+
// reduced to its host by the normal output redaction.
|
|
56740
|
+
paymentLink: publicPayPage(paymentLink),
|
|
56741
|
+
deposit: confirmed ? {
|
|
56742
|
+
chainId: source.chainId,
|
|
56743
|
+
chain: chainName(source.chainId),
|
|
56744
|
+
tokenSymbol: source.tokenSymbol,
|
|
56745
|
+
tokenAddress: source.tokenAddress || null,
|
|
56746
|
+
receiverAddress: lightning ? null : source.receiverAddress,
|
|
56747
|
+
receiverMemo: source.receiverMemo ?? null,
|
|
56748
|
+
receiverMemoType: source.receiverMemo ? STELLAR_MEMO_TYPE : null,
|
|
56749
|
+
amount: source.amount,
|
|
56750
|
+
amountUnit: source.amountUnit ?? null,
|
|
56751
|
+
isSats: isSatsUnit(source.amountUnit),
|
|
56752
|
+
lnInvoice: bolt11 || null,
|
|
56753
|
+
payTo: guard.deposit.payTo,
|
|
56754
|
+
expiresAt,
|
|
56755
|
+
expiresIn
|
|
56756
|
+
} : null,
|
|
56757
|
+
depositWithheld: !confirmed,
|
|
56758
|
+
display: {
|
|
56759
|
+
chain: chainName(source.chainId),
|
|
56760
|
+
token: source.tokenSymbol,
|
|
56761
|
+
amount: formatAmount(source),
|
|
56762
|
+
isSats: isSatsUnit(source.amountUnit),
|
|
56763
|
+
payToMasked: maskAddress2(guard.deposit.payTo),
|
|
56764
|
+
receiverMemoMasked: maskMemo(source.receiverMemo),
|
|
56765
|
+
hasMemo: Boolean(source.receiverMemo),
|
|
56766
|
+
memoType: source.receiverMemo ? STELLAR_MEMO_TYPE : null
|
|
56767
|
+
},
|
|
56768
|
+
expiry: { expiresAt, expiresIn, minutesOfSlack: snap.sendWindow?.minutesOfSlack ?? null },
|
|
56769
|
+
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."
|
|
56770
|
+
});
|
|
56771
|
+
}
|
|
56772
|
+
async function run6(argv = process.argv.slice(2)) {
|
|
56773
|
+
return main6(argv);
|
|
56774
|
+
}
|
|
56775
|
+
|
|
56043
56776
|
// scripts/src/lib/passphrase.mjs
|
|
56044
56777
|
import readline from "node:readline";
|
|
56045
56778
|
function promptPassphrase(prompt = " Keystore passphrase: ") {
|
|
@@ -56279,6 +57012,290 @@ function broadcastOutcome({ receiptStatus, executionError = null, receiptSeen =
|
|
|
56279
57012
|
};
|
|
56280
57013
|
}
|
|
56281
57014
|
|
|
57015
|
+
// scripts/src/lib/gas.mjs
|
|
57016
|
+
var EVM_FEE_COIN = Object.freeze({
|
|
57017
|
+
1: { symbol: "ETH", network: "Ethereum" },
|
|
57018
|
+
56: { symbol: "BNB", network: "BNB Chain" },
|
|
57019
|
+
137: { symbol: "POL", network: "Polygon" },
|
|
57020
|
+
8453: { symbol: "ETH", network: "Base" }
|
|
57021
|
+
});
|
|
57022
|
+
var DEFAULT_GAS_BUFFER_PCT = 20;
|
|
57023
|
+
var MAX_GAS_BUFFER_PCT = 200;
|
|
57024
|
+
function gasBufferPct(env = process.env) {
|
|
57025
|
+
const raw = env?.ROZO_CHECKOUT_GAS_BUFFER_PCT;
|
|
57026
|
+
if (raw === void 0 || raw === "") return DEFAULT_GAS_BUFFER_PCT;
|
|
57027
|
+
const n = Number(raw);
|
|
57028
|
+
if (!Number.isInteger(n) || n < 0 || n > MAX_GAS_BUFFER_PCT) return DEFAULT_GAS_BUFFER_PCT;
|
|
57029
|
+
return n;
|
|
57030
|
+
}
|
|
57031
|
+
function formatAtomic(atomic, decimals) {
|
|
57032
|
+
const neg = atomic < 0n;
|
|
57033
|
+
const v = neg ? -atomic : atomic;
|
|
57034
|
+
const base = 10n ** BigInt(decimals);
|
|
57035
|
+
const whole = v / base;
|
|
57036
|
+
const frac = (v % base).toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
57037
|
+
return `${neg ? "-" : ""}${whole}${frac ? `.${frac}` : ""}`;
|
|
57038
|
+
}
|
|
57039
|
+
function evmFeeRequirement({ gasEstimate, maxFeePerGas, gasPrice, bufferPct = DEFAULT_GAS_BUFFER_PCT }) {
|
|
57040
|
+
const perGas = typeof maxFeePerGas === "bigint" ? maxFeePerGas : typeof gasPrice === "bigint" ? gasPrice : null;
|
|
57041
|
+
if (typeof gasEstimate !== "bigint" || perGas === null) return null;
|
|
57042
|
+
const gasLimit = (gasEstimate * BigInt(100 + bufferPct) + 99n) / 100n;
|
|
57043
|
+
return { gasLimit, perGas, pricing: typeof maxFeePerGas === "bigint" ? "eip1559" : "legacy", fee: gasLimit * perGas };
|
|
57044
|
+
}
|
|
57045
|
+
function assessFee({ balance, required, decimals, symbol, network }) {
|
|
57046
|
+
const ok = required === null ? balance > 0n : balance >= required;
|
|
57047
|
+
const details = {
|
|
57048
|
+
network,
|
|
57049
|
+
feeCoin: symbol,
|
|
57050
|
+
balance: formatAtomic(balance, decimals),
|
|
57051
|
+
required: required === null ? null : formatAtomic(required, decimals),
|
|
57052
|
+
shortfall: required === null || ok ? null : formatAtomic(required - balance, decimals)
|
|
57053
|
+
};
|
|
57054
|
+
return { ok, details };
|
|
57055
|
+
}
|
|
57056
|
+
function insufficient(details, extra) {
|
|
57057
|
+
const need = details.required ? `about ${details.required} ${details.feeCoin}` : `some ${details.feeCoin}`;
|
|
57058
|
+
return new SkillError(
|
|
57059
|
+
"INSUFFICIENT_GAS",
|
|
57060
|
+
`This wallet holds ${details.balance} ${details.feeCoin} on ${details.network} but needs ${need} for the network fee${details.shortfall ? ` (short by ${details.shortfall} ${details.feeCoin})` : ""}. Nothing was signed. Add ${details.feeCoin} on ${details.network} to this wallet and re-run, or pay with a different coin.` + (extra ? ` ${extra}` : ""),
|
|
57061
|
+
details
|
|
57062
|
+
);
|
|
57063
|
+
}
|
|
57064
|
+
function unavailable(network, err) {
|
|
57065
|
+
return new SkillError(
|
|
57066
|
+
"GAS_CHECK_UNAVAILABLE",
|
|
57067
|
+
`Could not check the network fee on ${network}: ${redact(err?.shortMessage || err?.message || "unknown error")}. This is not evidence of a shortfall. Nothing was signed. Retry, pass --rpc <url>, or pay by hand from your wallet.`,
|
|
57068
|
+
{ network }
|
|
57069
|
+
);
|
|
57070
|
+
}
|
|
57071
|
+
async function estimateFees(client) {
|
|
57072
|
+
try {
|
|
57073
|
+
return await client.estimateFeesPerGas();
|
|
57074
|
+
} catch (err) {
|
|
57075
|
+
if (err?.name !== "Eip1559FeesNotSupportedError") throw err;
|
|
57076
|
+
return client.estimateFeesPerGas({ type: "legacy" });
|
|
57077
|
+
}
|
|
57078
|
+
}
|
|
57079
|
+
var OP_STACK_GAS_PRICE_ORACLE = "0x420000000000000000000000000000000000000F";
|
|
57080
|
+
var OP_STACK_CHAIN_IDS = /* @__PURE__ */ new Set([8453]);
|
|
57081
|
+
async function evmGasPrecheck({
|
|
57082
|
+
client,
|
|
57083
|
+
chainId,
|
|
57084
|
+
from: from15,
|
|
57085
|
+
to,
|
|
57086
|
+
data,
|
|
57087
|
+
value = 0n,
|
|
57088
|
+
bufferPct = gasBufferPct(),
|
|
57089
|
+
estimateL1Fee: estimateL1Fee2 = null
|
|
57090
|
+
}) {
|
|
57091
|
+
const coin = EVM_FEE_COIN[Number(chainId)] ?? { symbol: "native coin", network: `chain ${chainId}` };
|
|
57092
|
+
let balance;
|
|
57093
|
+
try {
|
|
57094
|
+
balance = await client.getBalance({ address: from15 });
|
|
57095
|
+
} catch (err) {
|
|
57096
|
+
throw unavailable(coin.network, err);
|
|
57097
|
+
}
|
|
57098
|
+
let gasEstimate;
|
|
57099
|
+
let fees;
|
|
57100
|
+
try {
|
|
57101
|
+
gasEstimate = await client.estimateGas({ account: from15, to, data, value });
|
|
57102
|
+
fees = await estimateFees(client);
|
|
57103
|
+
} catch (err) {
|
|
57104
|
+
if (balance === 0n) {
|
|
57105
|
+
throw insufficient(
|
|
57106
|
+
assessFee({ balance, required: null, decimals: 18, symbol: coin.symbol, network: coin.network }).details
|
|
57107
|
+
);
|
|
57108
|
+
}
|
|
57109
|
+
throw unavailable(coin.network, err);
|
|
57110
|
+
}
|
|
57111
|
+
const req = evmFeeRequirement({
|
|
57112
|
+
gasEstimate,
|
|
57113
|
+
maxFeePerGas: fees?.maxFeePerGas,
|
|
57114
|
+
gasPrice: fees?.gasPrice,
|
|
57115
|
+
bufferPct
|
|
57116
|
+
});
|
|
57117
|
+
if (!req) throw unavailable(coin.network, new Error("the node returned no fee price"));
|
|
57118
|
+
let l1Fee = 0n;
|
|
57119
|
+
if (OP_STACK_CHAIN_IDS.has(Number(chainId))) {
|
|
57120
|
+
if (typeof estimateL1Fee2 !== "function") {
|
|
57121
|
+
throw unavailable(coin.network, new Error("no L1 data fee estimator for this OP-stack chain"));
|
|
57122
|
+
}
|
|
57123
|
+
try {
|
|
57124
|
+
l1Fee = (await estimateL1Fee2() * BigInt(100 + bufferPct) + 99n) / 100n;
|
|
57125
|
+
} catch (err) {
|
|
57126
|
+
throw unavailable(coin.network, err);
|
|
57127
|
+
}
|
|
57128
|
+
}
|
|
57129
|
+
const required = req.fee + l1Fee + value;
|
|
57130
|
+
const { ok, details } = assessFee({ balance, required, decimals: 18, symbol: coin.symbol, network: coin.network });
|
|
57131
|
+
const full = {
|
|
57132
|
+
...details,
|
|
57133
|
+
gasLimit: req.gasLimit.toString(),
|
|
57134
|
+
pricing: req.pricing,
|
|
57135
|
+
bufferPct,
|
|
57136
|
+
l1DataFee: OP_STACK_CHAIN_IDS.has(Number(chainId)) ? formatAtomic(l1Fee, 18) : null
|
|
57137
|
+
};
|
|
57138
|
+
if (!ok) throw insufficient(full);
|
|
57139
|
+
return full;
|
|
57140
|
+
}
|
|
57141
|
+
var LAMPORTS_DECIMALS = 9;
|
|
57142
|
+
async function solFeePrecheck({ connection, payer, message }) {
|
|
57143
|
+
const network = "Solana";
|
|
57144
|
+
let balance;
|
|
57145
|
+
let fee;
|
|
57146
|
+
let rentMin;
|
|
57147
|
+
try {
|
|
57148
|
+
balance = BigInt(await connection.getBalance(payer, "confirmed"));
|
|
57149
|
+
const res = await connection.getFeeForMessage(message, "confirmed");
|
|
57150
|
+
if (res?.value === null || res?.value === void 0) {
|
|
57151
|
+
throw new Error("the node returned no fee for this transaction (stale blockhash?)");
|
|
57152
|
+
}
|
|
57153
|
+
fee = BigInt(res.value);
|
|
57154
|
+
rentMin = BigInt(await connection.getMinimumBalanceForRentExemption(0));
|
|
57155
|
+
} catch (err) {
|
|
57156
|
+
if (balance === 0n) {
|
|
57157
|
+
throw insufficient(
|
|
57158
|
+
assessFee({ balance, required: null, decimals: LAMPORTS_DECIMALS, symbol: "SOL", network }).details
|
|
57159
|
+
);
|
|
57160
|
+
}
|
|
57161
|
+
throw unavailable(network, err);
|
|
57162
|
+
}
|
|
57163
|
+
const remaining = balance - fee;
|
|
57164
|
+
const required = remaining === 0n ? fee : fee + rentMin;
|
|
57165
|
+
const { ok, details } = assessFee({ balance, required, decimals: LAMPORTS_DECIMALS, symbol: "SOL", network });
|
|
57166
|
+
const full = { ...details, fee: formatAtomic(fee, LAMPORTS_DECIMALS), rentExemptMinimum: formatAtomic(rentMin, LAMPORTS_DECIMALS) };
|
|
57167
|
+
if (!ok) {
|
|
57168
|
+
throw insufficient(
|
|
57169
|
+
full,
|
|
57170
|
+
balance >= fee ? "Solana also requires a wallet to keep a small minimum balance after paying the fee." : null
|
|
57171
|
+
);
|
|
57172
|
+
}
|
|
57173
|
+
return full;
|
|
57174
|
+
}
|
|
57175
|
+
|
|
57176
|
+
// node_modules/viem/_esm/op-stack/contracts.js
|
|
57177
|
+
var contracts = {
|
|
57178
|
+
gasPriceOracle: { address: "0x420000000000000000000000000000000000000F" },
|
|
57179
|
+
l1Block: { address: "0x4200000000000000000000000000000000000015" },
|
|
57180
|
+
l2CrossDomainMessenger: {
|
|
57181
|
+
address: "0x4200000000000000000000000000000000000007"
|
|
57182
|
+
},
|
|
57183
|
+
l2Erc721Bridge: { address: "0x4200000000000000000000000000000000000014" },
|
|
57184
|
+
l2StandardBridge: { address: "0x4200000000000000000000000000000000000010" },
|
|
57185
|
+
l2ToL1MessagePasser: {
|
|
57186
|
+
address: "0x4200000000000000000000000000000000000016"
|
|
57187
|
+
}
|
|
57188
|
+
};
|
|
57189
|
+
|
|
57190
|
+
// node_modules/viem/_esm/op-stack/abis.js
|
|
57191
|
+
var gasPriceOracleAbi = [
|
|
57192
|
+
{ inputs: [], stateMutability: "nonpayable", type: "constructor" },
|
|
57193
|
+
{
|
|
57194
|
+
inputs: [],
|
|
57195
|
+
name: "DECIMALS",
|
|
57196
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
57197
|
+
stateMutability: "view",
|
|
57198
|
+
type: "function"
|
|
57199
|
+
},
|
|
57200
|
+
{
|
|
57201
|
+
inputs: [],
|
|
57202
|
+
name: "baseFee",
|
|
57203
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
57204
|
+
stateMutability: "view",
|
|
57205
|
+
type: "function"
|
|
57206
|
+
},
|
|
57207
|
+
{
|
|
57208
|
+
inputs: [],
|
|
57209
|
+
name: "decimals",
|
|
57210
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
57211
|
+
stateMutability: "pure",
|
|
57212
|
+
type: "function"
|
|
57213
|
+
},
|
|
57214
|
+
{
|
|
57215
|
+
inputs: [],
|
|
57216
|
+
name: "gasPrice",
|
|
57217
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
57218
|
+
stateMutability: "view",
|
|
57219
|
+
type: "function"
|
|
57220
|
+
},
|
|
57221
|
+
{
|
|
57222
|
+
inputs: [{ internalType: "bytes", name: "_data", type: "bytes" }],
|
|
57223
|
+
name: "getL1Fee",
|
|
57224
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
57225
|
+
stateMutability: "view",
|
|
57226
|
+
type: "function"
|
|
57227
|
+
},
|
|
57228
|
+
{
|
|
57229
|
+
inputs: [{ internalType: "bytes", name: "_data", type: "bytes" }],
|
|
57230
|
+
name: "getL1GasUsed",
|
|
57231
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
57232
|
+
stateMutability: "view",
|
|
57233
|
+
type: "function"
|
|
57234
|
+
},
|
|
57235
|
+
{
|
|
57236
|
+
inputs: [],
|
|
57237
|
+
name: "l1BaseFee",
|
|
57238
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
57239
|
+
stateMutability: "view",
|
|
57240
|
+
type: "function"
|
|
57241
|
+
},
|
|
57242
|
+
{
|
|
57243
|
+
inputs: [],
|
|
57244
|
+
name: "overhead",
|
|
57245
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
57246
|
+
stateMutability: "view",
|
|
57247
|
+
type: "function"
|
|
57248
|
+
},
|
|
57249
|
+
{
|
|
57250
|
+
inputs: [],
|
|
57251
|
+
name: "scalar",
|
|
57252
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
57253
|
+
stateMutability: "view",
|
|
57254
|
+
type: "function"
|
|
57255
|
+
},
|
|
57256
|
+
{
|
|
57257
|
+
inputs: [],
|
|
57258
|
+
name: "version",
|
|
57259
|
+
outputs: [{ internalType: "string", name: "", type: "string" }],
|
|
57260
|
+
stateMutability: "view",
|
|
57261
|
+
type: "function"
|
|
57262
|
+
}
|
|
57263
|
+
];
|
|
57264
|
+
|
|
57265
|
+
// node_modules/viem/_esm/op-stack/actions/estimateL1Fee.js
|
|
57266
|
+
init_getChainContractAddress();
|
|
57267
|
+
async function estimateL1Fee(client, args) {
|
|
57268
|
+
const { chain = client.chain, gasPriceOracleAddress: gasPriceOracleAddress_ } = args;
|
|
57269
|
+
const gasPriceOracleAddress = (() => {
|
|
57270
|
+
if (gasPriceOracleAddress_)
|
|
57271
|
+
return gasPriceOracleAddress_;
|
|
57272
|
+
if (chain)
|
|
57273
|
+
return getChainContractAddress({
|
|
57274
|
+
chain,
|
|
57275
|
+
contract: "gasPriceOracle"
|
|
57276
|
+
});
|
|
57277
|
+
return contracts.gasPriceOracle.address;
|
|
57278
|
+
})();
|
|
57279
|
+
const transaction = serializeTransaction({
|
|
57280
|
+
...args,
|
|
57281
|
+
chainId: chain?.id ?? 1,
|
|
57282
|
+
type: "eip1559",
|
|
57283
|
+
// Set upper-limit-ish stub values. Shouldn't affect the estimate too much as we are
|
|
57284
|
+
// tweaking dust bytes here (as opposed to long `data` bytes).
|
|
57285
|
+
// See: https://github.com/ethereum-optimism/optimism/blob/54d02df55523c9e1b4b38ed082c12a42087323a0/packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L242-L248.
|
|
57286
|
+
gas: args.data ? 300000n : 21000n,
|
|
57287
|
+
maxFeePerGas: parseGwei("5"),
|
|
57288
|
+
maxPriorityFeePerGas: parseGwei("1"),
|
|
57289
|
+
nonce: 1
|
|
57290
|
+
});
|
|
57291
|
+
return readContract(client, {
|
|
57292
|
+
abi: gasPriceOracleAbi,
|
|
57293
|
+
address: gasPriceOracleAddress,
|
|
57294
|
+
functionName: "getL1Fee",
|
|
57295
|
+
args: [transaction]
|
|
57296
|
+
});
|
|
57297
|
+
}
|
|
57298
|
+
|
|
56282
57299
|
// node_modules/viem/_esm/accounts/privateKeyToAccount.js
|
|
56283
57300
|
init_secp256k1();
|
|
56284
57301
|
init_toHex();
|
|
@@ -56472,7 +57489,7 @@ function chainDef(chainId, rpcUrl) {
|
|
|
56472
57489
|
rpcUrls: { default: { http: [rpcUrl] } }
|
|
56473
57490
|
});
|
|
56474
57491
|
}
|
|
56475
|
-
async function
|
|
57492
|
+
async function main7(argv) {
|
|
56476
57493
|
const args = parseArgs(argv);
|
|
56477
57494
|
const rozoPaymentId = assertRozoPaymentId(args["rozo-payment-id"] || args._[0]);
|
|
56478
57495
|
assertNoTrackedDotEnv();
|
|
@@ -56550,6 +57567,25 @@ async function main5(argv) {
|
|
|
56550
57567
|
`Sender holds ${balance} atomic units of ${source.tokenSymbol} on ${chainName(chainId)}; ${amountAtomic} are required.`
|
|
56551
57568
|
);
|
|
56552
57569
|
}
|
|
57570
|
+
const data = encodeFunctionData({
|
|
57571
|
+
abi: ERC20_TRANSFER_ABI,
|
|
57572
|
+
functionName: "transfer",
|
|
57573
|
+
args: [to, amountAtomic]
|
|
57574
|
+
});
|
|
57575
|
+
const networkFee = await evmGasPrecheck({
|
|
57576
|
+
client: pub,
|
|
57577
|
+
chainId,
|
|
57578
|
+
from: sender,
|
|
57579
|
+
to: tokenAddress,
|
|
57580
|
+
data,
|
|
57581
|
+
estimateL1Fee: OP_STACK_CHAIN_IDS.has(chainId) ? () => estimateL1Fee(pub, {
|
|
57582
|
+
account: sender,
|
|
57583
|
+
to: tokenAddress,
|
|
57584
|
+
data,
|
|
57585
|
+
chain,
|
|
57586
|
+
gasPriceOracleAddress: OP_STACK_GAS_PRICE_ORACLE
|
|
57587
|
+
}) : null
|
|
57588
|
+
});
|
|
56553
57589
|
if (dryRun) {
|
|
56554
57590
|
emit({
|
|
56555
57591
|
success: true,
|
|
@@ -56565,6 +57601,7 @@ async function main5(argv) {
|
|
|
56565
57601
|
toMasked: maskAddress2(to),
|
|
56566
57602
|
fromMasked: maskAddress2(sender)
|
|
56567
57603
|
},
|
|
57604
|
+
networkFee,
|
|
56568
57605
|
confirmedAt: state.confirmation?.confirmedAt ?? null,
|
|
56569
57606
|
keySource,
|
|
56570
57607
|
// Key NAMES only — a value from a .env is never echoed.
|
|
@@ -56574,11 +57611,6 @@ async function main5(argv) {
|
|
|
56574
57611
|
});
|
|
56575
57612
|
}
|
|
56576
57613
|
const wallet = createWalletClient({ account, chain, transport: http(rpcUrl) });
|
|
56577
|
-
const data = encodeFunctionData({
|
|
56578
|
-
abi: ERC20_TRANSFER_ABI,
|
|
56579
|
-
functionName: "transfer",
|
|
56580
|
-
args: [to, amountAtomic]
|
|
56581
|
-
});
|
|
56582
57614
|
const request2 = await wallet.prepareTransactionRequest({
|
|
56583
57615
|
to: tokenAddress,
|
|
56584
57616
|
data,
|
|
@@ -56637,7 +57669,7 @@ async function main5(argv) {
|
|
|
56637
57669
|
code: landed === false ? "BROADCAST_FAILED" : "BROADCAST_AMBIGUOUS",
|
|
56638
57670
|
message: redact(err?.shortMessage || err?.message || "broadcast failed")
|
|
56639
57671
|
},
|
|
56640
|
-
signedTxHash: expectedTxHash,
|
|
57672
|
+
signedTxHash: publicHash(expectedTxHash),
|
|
56641
57673
|
nonceBefore,
|
|
56642
57674
|
foundOnChain: landed,
|
|
56643
57675
|
guidance: landed === false ? "Nothing appears to have been broadcast, but this order is now locked against a second automated send. Verify the signed hash on a block explorer first." : "The signed transaction may already be in flight. Do NOT resend. Look up the signed hash above on a block explorer and poll status.js."
|
|
@@ -56661,7 +57693,7 @@ async function main5(argv) {
|
|
|
56661
57693
|
confirmed: false,
|
|
56662
57694
|
rozoPaymentId,
|
|
56663
57695
|
linkId: state.linkId,
|
|
56664
|
-
txHash,
|
|
57696
|
+
txHash: publicHash(txHash),
|
|
56665
57697
|
guidance: "Broadcast but not confirmed within the wait window. Do NOT resend. Poll status.js; receipt truth is the backend confirmedAt/amountReceived, not this response."
|
|
56666
57698
|
},
|
|
56667
57699
|
EXIT_UNCONFIRMED
|
|
@@ -56682,7 +57714,7 @@ async function main5(argv) {
|
|
|
56682
57714
|
confirmed: succeeded,
|
|
56683
57715
|
rozoPaymentId,
|
|
56684
57716
|
linkId: state.linkId,
|
|
56685
|
-
txHash,
|
|
57717
|
+
txHash: publicHash(txHash),
|
|
56686
57718
|
blockNumber: receipt.blockNumber?.toString?.() ?? null,
|
|
56687
57719
|
...succeeded ? {} : { error: { code: outcome.code, message: "The transfer reverted on chain." } },
|
|
56688
57720
|
sent: {
|
|
@@ -56700,8 +57732,8 @@ async function main5(argv) {
|
|
|
56700
57732
|
outcome.exitCode
|
|
56701
57733
|
);
|
|
56702
57734
|
}
|
|
56703
|
-
async function
|
|
56704
|
-
return
|
|
57735
|
+
async function run7(argv = process.argv.slice(2)) {
|
|
57736
|
+
return main7(argv);
|
|
56705
57737
|
}
|
|
56706
57738
|
|
|
56707
57739
|
// scripts/src/send-sol.mjs
|
|
@@ -56771,7 +57803,7 @@ function createTransferCheckedInstruction(source, mint, destination, owner, amou
|
|
|
56771
57803
|
var MEMO_PROGRAM_ID = new import_web32.PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
|
|
56772
57804
|
var MAINNET_GENESIS = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d";
|
|
56773
57805
|
var B58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
56774
|
-
async function
|
|
57806
|
+
async function main8(argv) {
|
|
56775
57807
|
const args = parseArgs(argv);
|
|
56776
57808
|
const rozoPaymentId = assertRozoPaymentId(args["rozo-payment-id"] || args._[0]);
|
|
56777
57809
|
assertNoTrackedDotEnv();
|
|
@@ -56868,6 +57900,34 @@ async function main6(argv) {
|
|
|
56868
57900
|
);
|
|
56869
57901
|
}
|
|
56870
57902
|
}
|
|
57903
|
+
const tx = new import_web32.Transaction();
|
|
57904
|
+
tx.add(
|
|
57905
|
+
createTransferCheckedInstruction(
|
|
57906
|
+
fromAta,
|
|
57907
|
+
mint,
|
|
57908
|
+
toAta,
|
|
57909
|
+
keypair.publicKey,
|
|
57910
|
+
amountAtomic,
|
|
57911
|
+
mintInfo.decimals
|
|
57912
|
+
)
|
|
57913
|
+
);
|
|
57914
|
+
if (source.receiverMemo) {
|
|
57915
|
+
tx.add(
|
|
57916
|
+
new import_web32.TransactionInstruction({
|
|
57917
|
+
keys: [],
|
|
57918
|
+
programId: MEMO_PROGRAM_ID,
|
|
57919
|
+
data: Buffer.from(String(source.receiverMemo), "utf8")
|
|
57920
|
+
})
|
|
57921
|
+
);
|
|
57922
|
+
}
|
|
57923
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
|
|
57924
|
+
tx.recentBlockhash = blockhash;
|
|
57925
|
+
tx.feePayer = keypair.publicKey;
|
|
57926
|
+
const networkFee = await solFeePrecheck({
|
|
57927
|
+
connection,
|
|
57928
|
+
payer: keypair.publicKey,
|
|
57929
|
+
message: tx.compileMessage()
|
|
57930
|
+
});
|
|
56871
57931
|
if (dryRun) {
|
|
56872
57932
|
emit({
|
|
56873
57933
|
success: true,
|
|
@@ -56884,6 +57944,7 @@ async function main6(argv) {
|
|
|
56884
57944
|
toIsTokenAccount,
|
|
56885
57945
|
withMemo: Boolean(source.receiverMemo)
|
|
56886
57946
|
},
|
|
57947
|
+
networkFee,
|
|
56887
57948
|
confirmedAt: state.confirmation?.confirmedAt ?? null,
|
|
56888
57949
|
keySource,
|
|
56889
57950
|
// Key NAMES only — a value from a .env is never echoed.
|
|
@@ -56892,29 +57953,6 @@ async function main6(argv) {
|
|
|
56892
57953
|
note: "Nothing was signed or broadcast. Add --send (without --dry-run) to execute."
|
|
56893
57954
|
});
|
|
56894
57955
|
}
|
|
56895
|
-
const tx = new import_web32.Transaction();
|
|
56896
|
-
tx.add(
|
|
56897
|
-
createTransferCheckedInstruction(
|
|
56898
|
-
fromAta,
|
|
56899
|
-
mint,
|
|
56900
|
-
toAta,
|
|
56901
|
-
keypair.publicKey,
|
|
56902
|
-
amountAtomic,
|
|
56903
|
-
mintInfo.decimals
|
|
56904
|
-
)
|
|
56905
|
-
);
|
|
56906
|
-
if (source.receiverMemo) {
|
|
56907
|
-
tx.add(
|
|
56908
|
-
new import_web32.TransactionInstruction({
|
|
56909
|
-
keys: [],
|
|
56910
|
-
programId: MEMO_PROGRAM_ID,
|
|
56911
|
-
data: Buffer.from(String(source.receiverMemo), "utf8")
|
|
56912
|
-
})
|
|
56913
|
-
);
|
|
56914
|
-
}
|
|
56915
|
-
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
|
|
56916
|
-
tx.recentBlockhash = blockhash;
|
|
56917
|
-
tx.feePayer = keypair.publicKey;
|
|
56918
57956
|
tx.sign(keypair);
|
|
56919
57957
|
const signature = tx.signatures[0]?.signature ? base58Encode(tx.signatures[0].signature) : null;
|
|
56920
57958
|
await finalPayabilityCheck({
|
|
@@ -56966,7 +58004,7 @@ async function main6(argv) {
|
|
|
56966
58004
|
code: landed ? "BROADCAST_AMBIGUOUS" : "BROADCAST_FAILED",
|
|
56967
58005
|
message: redact(err?.message || "broadcast failed")
|
|
56968
58006
|
},
|
|
56969
|
-
signature,
|
|
58007
|
+
signature: publicHash(signature),
|
|
56970
58008
|
guidance: landed ? "The signed transaction may already be on chain. Do NOT resend. Check the signature on an explorer and poll status.js." : "Nothing appears to have landed, but this order is now locked against a second automated send. Verify on chain before doing anything else."
|
|
56971
58009
|
},
|
|
56972
58010
|
EXIT_ERROR
|
|
@@ -57003,7 +58041,7 @@ async function main6(argv) {
|
|
|
57003
58041
|
confirmed: false,
|
|
57004
58042
|
rozoPaymentId,
|
|
57005
58043
|
linkId: state.linkId,
|
|
57006
|
-
txHash: sent,
|
|
58044
|
+
txHash: publicHash(sent),
|
|
57007
58045
|
error: {
|
|
57008
58046
|
code: broadcastOutcome({ executionError }).code,
|
|
57009
58047
|
message: `The transaction landed but failed on chain: ${redact(JSON.stringify(executionError))}`
|
|
@@ -57022,7 +58060,7 @@ async function main6(argv) {
|
|
|
57022
58060
|
confirmed: false,
|
|
57023
58061
|
rozoPaymentId,
|
|
57024
58062
|
linkId: state.linkId,
|
|
57025
|
-
txHash: sent,
|
|
58063
|
+
txHash: publicHash(sent),
|
|
57026
58064
|
guidance: "Broadcast but not confirmed within the wait window. Do NOT resend. Poll status.js; receipt truth is the backend confirmedAt/amountReceived."
|
|
57027
58065
|
},
|
|
57028
58066
|
EXIT_UNCONFIRMED
|
|
@@ -57036,7 +58074,7 @@ async function main6(argv) {
|
|
|
57036
58074
|
confirmed: true,
|
|
57037
58075
|
rozoPaymentId,
|
|
57038
58076
|
linkId: state.linkId,
|
|
57039
|
-
txHash: sent,
|
|
58077
|
+
txHash: publicHash(sent),
|
|
57040
58078
|
sent: {
|
|
57041
58079
|
chain: chainName(900),
|
|
57042
58080
|
tokenSymbol: source.tokenSymbol,
|
|
@@ -57064,8 +58102,8 @@ function base58Encode(buf) {
|
|
|
57064
58102
|
}
|
|
57065
58103
|
return out2;
|
|
57066
58104
|
}
|
|
57067
|
-
async function
|
|
57068
|
-
return
|
|
58105
|
+
async function run8(argv = process.argv.slice(2)) {
|
|
58106
|
+
return main8(argv);
|
|
57069
58107
|
}
|
|
57070
58108
|
|
|
57071
58109
|
// scripts/src/cli.mjs
|
|
@@ -57268,6 +58306,105 @@ async function cmdStatus(opts) {
|
|
|
57268
58306
|
out();
|
|
57269
58307
|
return exitCode;
|
|
57270
58308
|
}
|
|
58309
|
+
var LAYER_LABEL = {
|
|
58310
|
+
confirmed: green("confirmed"),
|
|
58311
|
+
detected: yellow("detected"),
|
|
58312
|
+
pending: yellow("pending"),
|
|
58313
|
+
none: dim("none"),
|
|
58314
|
+
unknown: yellow("unknown")
|
|
58315
|
+
};
|
|
58316
|
+
async function cmdReceipt(opts) {
|
|
58317
|
+
const argv = targetToArgs(opts.target);
|
|
58318
|
+
if (opts.provider) argv.push("--provider", opts.provider);
|
|
58319
|
+
if (!opts.json) out(dim(" Reading the order\u2026 (no money moves)"));
|
|
58320
|
+
const { payload, exitCode } = await step(run5, argv);
|
|
58321
|
+
if (opts.json) {
|
|
58322
|
+
printJson(payload);
|
|
58323
|
+
return exitCode;
|
|
58324
|
+
}
|
|
58325
|
+
if (payload.error) {
|
|
58326
|
+
printError(payload);
|
|
58327
|
+
printSupport();
|
|
58328
|
+
return exitCode;
|
|
58329
|
+
}
|
|
58330
|
+
const r = payload.receipt;
|
|
58331
|
+
out();
|
|
58332
|
+
out(` Outcome ${outcomeLabel(r.paymentOutcome)} ${dim(`(${r.state})`)}`);
|
|
58333
|
+
out(` Your payment ${LAYER_LABEL[r.sourcePayment.status] ?? r.sourcePayment.status}` + (r.sourcePayment.txHash ? ` ${r.sourcePayment.txHash}` : ""));
|
|
58334
|
+
if (r.sourcePayment.localSend && r.sourcePayment.status !== "confirmed") {
|
|
58335
|
+
out(dim(` this machine sent it (${r.sourcePayment.localSend.status})` + (r.sourcePayment.localSend.txHash ? `: ${r.sourcePayment.localSend.txHash}` : "")));
|
|
58336
|
+
}
|
|
58337
|
+
out(` Merchant invoice ${LAYER_LABEL[r.merchantSettlement.status] ?? r.merchantSettlement.status}` + (r.merchantSettlement.coinbaseStatus ? ` ${dim(r.merchantSettlement.coinbaseStatus)}` : ""));
|
|
58338
|
+
out(` Service delivered ${LAYER_LABEL.unknown}`);
|
|
58339
|
+
if (r.merchantSettlement.status === "confirmed") out(` ${dim(r.serviceDelivery.note)}`);
|
|
58340
|
+
out();
|
|
58341
|
+
out(` Next: ${r.nextAction.type === "contact_support" ? red(r.nextAction.message) : r.nextAction.message}`);
|
|
58342
|
+
if (payload.support) printSupport();
|
|
58343
|
+
out();
|
|
58344
|
+
return exitCode;
|
|
58345
|
+
}
|
|
58346
|
+
async function cmdResume(opts) {
|
|
58347
|
+
if (!opts.json) out(dim(" Reading the order\u2026 (no money moves)"));
|
|
58348
|
+
const first = await step(run6, [opts.target]);
|
|
58349
|
+
let payload = first.payload;
|
|
58350
|
+
let exitCode = first.exitCode;
|
|
58351
|
+
if (payload.resumable && !opts.json) {
|
|
58352
|
+
out();
|
|
58353
|
+
out(` Order ${payload.rozoPaymentId} ${dim("(unpaid)")}`);
|
|
58354
|
+
out(` You send ${bold(payload.display?.amount)} on ${bold(payload.display?.chain)}`);
|
|
58355
|
+
out(` To ${payload.display?.payToMasked} ${dim("(full address shown after you confirm)")}`);
|
|
58356
|
+
if (payload.display?.hasMemo) {
|
|
58357
|
+
out(` Memo ${payload.display.receiverMemoMasked} ${dim(`(${payload.display.memoType})`)}`);
|
|
58358
|
+
}
|
|
58359
|
+
if (payload.expiry?.expiresIn) out(` Expires in ${bold(payload.expiry.expiresIn)}`);
|
|
58360
|
+
if (payload.paymentLink) out(` Pay page ${payload.paymentLink}`);
|
|
58361
|
+
out();
|
|
58362
|
+
}
|
|
58363
|
+
if (payload.resumable) {
|
|
58364
|
+
let go = opts.yes;
|
|
58365
|
+
if (!go && !opts.json && process.stdin.isTTY) {
|
|
58366
|
+
go = await askYesNo(` ${bold("Show the deposit details to pay this now?")} [y/N] `);
|
|
58367
|
+
}
|
|
58368
|
+
if (go) {
|
|
58369
|
+
const second = await step(run6, [opts.target, "--confirm"]);
|
|
58370
|
+
payload = second.payload;
|
|
58371
|
+
exitCode = second.exitCode;
|
|
58372
|
+
}
|
|
58373
|
+
}
|
|
58374
|
+
if (opts.json) {
|
|
58375
|
+
printJson(payload);
|
|
58376
|
+
return exitCode;
|
|
58377
|
+
}
|
|
58378
|
+
if (payload.error) {
|
|
58379
|
+
printError(payload);
|
|
58380
|
+
printMoneyWarning(payload);
|
|
58381
|
+
printSupport();
|
|
58382
|
+
out();
|
|
58383
|
+
return exitCode;
|
|
58384
|
+
}
|
|
58385
|
+
if (!payload.resumable) {
|
|
58386
|
+
out();
|
|
58387
|
+
out(` State ${stateLabel(payload.state)}`);
|
|
58388
|
+
out(` ${payload.message}`);
|
|
58389
|
+
out();
|
|
58390
|
+
return exitCode;
|
|
58391
|
+
}
|
|
58392
|
+
if (payload.deposit) {
|
|
58393
|
+
printDeposit(payload.deposit, opts);
|
|
58394
|
+
out(dim(` Then: rozo-checkout status ${payload.rozoPaymentId} --watch`));
|
|
58395
|
+
out();
|
|
58396
|
+
} else {
|
|
58397
|
+
out(dim(" Deposit details withheld. Open the pay page above, or re-run with --yes."));
|
|
58398
|
+
out();
|
|
58399
|
+
}
|
|
58400
|
+
return exitCode;
|
|
58401
|
+
}
|
|
58402
|
+
function outcomeLabel(outcome) {
|
|
58403
|
+
if (outcome === "settled") return green(bold(outcome));
|
|
58404
|
+
if (outcome === "needs_attention" || outcome === "unknown") return red(bold(outcome));
|
|
58405
|
+
if (outcome === "expired_unfunded") return yellow(bold(outcome));
|
|
58406
|
+
return bold(outcome);
|
|
58407
|
+
}
|
|
57271
58408
|
function presetFor(chainId, tokenSymbol) {
|
|
57272
58409
|
const hit = PICKER_OPTIONS.find(
|
|
57273
58410
|
(o) => o.chainId === String(chainId) && o.tokenSymbol === String(tokenSymbol).toUpperCase()
|
|
@@ -57452,7 +58589,7 @@ async function cmdPay(opts) {
|
|
|
57452
58589
|
}
|
|
57453
58590
|
if (opts.send) {
|
|
57454
58591
|
const family = chainFamily(deposit.chainId);
|
|
57455
|
-
const sender = family === "evm" ?
|
|
58592
|
+
const sender = family === "evm" ? run7 : family === "solana" ? run8 : null;
|
|
57456
58593
|
if (!sender) {
|
|
57457
58594
|
const payload = formatFailure({
|
|
57458
58595
|
code: "SEND_UNSUPPORTED_CHAIN",
|
|
@@ -57564,7 +58701,7 @@ function printDeposit(deposit, opts) {
|
|
|
57564
58701
|
out(dim(" Send it exactly once."));
|
|
57565
58702
|
out();
|
|
57566
58703
|
}
|
|
57567
|
-
async function
|
|
58704
|
+
async function main9() {
|
|
57568
58705
|
let opts;
|
|
57569
58706
|
try {
|
|
57570
58707
|
opts = parseCliArgs(process.argv.slice(2));
|
|
@@ -57593,6 +58730,10 @@ async function main7() {
|
|
|
57593
58730
|
return cmdQuote(opts);
|
|
57594
58731
|
case "status":
|
|
57595
58732
|
return cmdStatus(opts);
|
|
58733
|
+
case "receipt":
|
|
58734
|
+
return cmdReceipt(opts);
|
|
58735
|
+
case "resume":
|
|
58736
|
+
return cmdResume(opts);
|
|
57596
58737
|
case "pay":
|
|
57597
58738
|
try {
|
|
57598
58739
|
return await cmdPay(opts);
|
|
@@ -57613,7 +58754,7 @@ async function main7() {
|
|
|
57613
58754
|
return EXIT_USAGE;
|
|
57614
58755
|
}
|
|
57615
58756
|
}
|
|
57616
|
-
|
|
58757
|
+
main9().then((code) => process.exit(code ?? EXIT_OK)).catch((err) => {
|
|
57617
58758
|
const payload = formatFailure(err);
|
|
57618
58759
|
if (process.argv.includes("--json")) printJson(payload);
|
|
57619
58760
|
else {
|