@rozoai/checkout 0.1.14 → 0.1.16
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 +132 -0
- 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/llms.txt +8 -0
- package/package.json +1 -1
- package/scripts/dist/cli.js +800 -62
- package/scripts/dist/create-order.js +20 -0
- package/scripts/dist/quote.js +9 -0
- package/scripts/dist/receipt.js +1195 -0
- package/scripts/dist/send-evm.js +300 -13
- package/scripts/dist/send-sol.js +122 -27
- package/scripts/dist/status.js +381 -8
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* run8(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 = run8(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 = run8(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] = run8(value, S, ctx);
|
|
21099
21102
|
const [first] = tuples;
|
|
21100
21103
|
if (!first[0]) {
|
|
21101
21104
|
return [];
|
|
@@ -41511,7 +41514,20 @@ 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
|
+
}
|
|
41514
41529
|
function redactDeep(value) {
|
|
41530
|
+
if (value instanceof PublicHash) return value.value;
|
|
41515
41531
|
if (typeof value === "string") return redact(value);
|
|
41516
41532
|
if (Array.isArray(value)) return value.map(redactDeep);
|
|
41517
41533
|
if (value && typeof value === "object") {
|
|
@@ -41754,7 +41770,7 @@ function normalizeUtmSource(value) {
|
|
|
41754
41770
|
}
|
|
41755
41771
|
|
|
41756
41772
|
// scripts/src/lib/cli-args.mjs
|
|
41757
|
-
var COMMANDS = ["pay", "quote", "status", "help", "version"];
|
|
41773
|
+
var COMMANDS = ["pay", "quote", "status", "receipt", "help", "version"];
|
|
41758
41774
|
var CHAIN_ALIASES = {
|
|
41759
41775
|
ethereum: "1",
|
|
41760
41776
|
eth: "1",
|
|
@@ -41970,6 +41986,18 @@ function parseCliArgs(argv) {
|
|
|
41970
41986
|
if (!target) throw new CliError("MISSING_TARGET", "Usage: rozo-checkout quote <coinbase-link>");
|
|
41971
41987
|
return { command, target, json, ...utmSource ? { utmSource } : {} };
|
|
41972
41988
|
}
|
|
41989
|
+
if (command === "receipt") {
|
|
41990
|
+
if (!target) {
|
|
41991
|
+
throw new CliError(
|
|
41992
|
+
"MISSING_TARGET",
|
|
41993
|
+
"Usage: rozo-checkout receipt <rozoPaymentId | coinbase-link>"
|
|
41994
|
+
);
|
|
41995
|
+
}
|
|
41996
|
+
if (flags.provider !== void 0 && !["coinbase", "bitrefill"].includes(String(flags.provider))) {
|
|
41997
|
+
throw new CliError("BAD_VALUE", "--provider must be coinbase or bitrefill.");
|
|
41998
|
+
}
|
|
41999
|
+
return { command, target, json, provider: flags.provider };
|
|
42000
|
+
}
|
|
41973
42001
|
if (command === "status") {
|
|
41974
42002
|
if (!target) {
|
|
41975
42003
|
throw new CliError(
|
|
@@ -42087,6 +42115,7 @@ USAGE
|
|
|
42087
42115
|
npx @rozoai/checkout pay --bitrefill-invoice <id> --to <0x\u2026> --amount <USDC> --expires-at <ISO> --with <coin>
|
|
42088
42116
|
npx @rozoai/checkout quote <coinbase-link>
|
|
42089
42117
|
npx @rozoai/checkout status <rozoPaymentId | coinbase-link>
|
|
42118
|
+
npx @rozoai/checkout receipt <rozoPaymentId | coinbase-link>
|
|
42090
42119
|
|
|
42091
42120
|
COINS (--with)
|
|
42092
42121
|
usdt-solana usdc-solana usdt-bnb usdc-bnb
|
|
@@ -42139,6 +42168,17 @@ EXAMPLES
|
|
|
42139
42168
|
npx @rozoai/checkout pay pl_01... --with usdt-solana --email you@example.com
|
|
42140
42169
|
npx @rozoai/checkout pay --bitrefill-invoice 8f3k2 --to 0xAbC\u2026123 --amount 7.90 --expires-at 2026-10-01T12:00:00Z --with usdc-stellar
|
|
42141
42170
|
npx @rozoai/checkout status 11111111-2222-4333-8444-555555555555
|
|
42171
|
+
npx @rozoai/checkout receipt 11111111-2222-4333-8444-555555555555 --json
|
|
42172
|
+
|
|
42173
|
+
RECEIPT
|
|
42174
|
+
receipt reads the order once (nothing is created or sent) and reports three
|
|
42175
|
+
layers separately: did your money reach Rozo, was the merchant invoice paid,
|
|
42176
|
+
and was the service delivered (always "unknown": Rozo cannot see OpenRouter
|
|
42177
|
+
credits). Exit 0 only when the merchant invoice is settled; 3 when it is
|
|
42178
|
+
still in flight or unknown; 1 when it expired unpaid or needs a human.
|
|
42179
|
+
In --json output from status and receipt, read paymentOutcome and
|
|
42180
|
+
nextAction. The older "success" field means the query worked, not that the
|
|
42181
|
+
invoice is paid.
|
|
42142
42182
|
|
|
42143
42183
|
Creating an order moves no money; an unfunded order simply expires. Nothing is
|
|
42144
42184
|
paid until you confirm, and --send is required before anything is signed.
|
|
@@ -50830,6 +50870,12 @@ function formatUnits(value, decimals) {
|
|
|
50830
50870
|
return format(value, decimals);
|
|
50831
50871
|
}
|
|
50832
50872
|
|
|
50873
|
+
// node_modules/viem/_esm/utils/unit/parseGwei.js
|
|
50874
|
+
init_Value();
|
|
50875
|
+
function parseGwei(ether, unit = "wei") {
|
|
50876
|
+
return fromGwei(ether, unit);
|
|
50877
|
+
}
|
|
50878
|
+
|
|
50833
50879
|
// node_modules/viem/_esm/utils/unit/parseUnits.js
|
|
50834
50880
|
init_Value();
|
|
50835
50881
|
function parseUnits(value, decimals) {
|
|
@@ -50973,7 +51019,7 @@ init_createBatchScheduler();
|
|
|
50973
51019
|
init_stringify();
|
|
50974
51020
|
async function multicall(client, parameters) {
|
|
50975
51021
|
const { account, authorizationList, allowFailure = true, blockHash, blockNumber, blockOverrides, blockTag, requireCanonical, stateOverride } = parameters;
|
|
50976
|
-
const
|
|
51022
|
+
const contracts2 = parameters.contracts;
|
|
50977
51023
|
const batch = typeof client.batch?.multicall === "object" ? client.batch.multicall : {};
|
|
50978
51024
|
const batchSize = parameters.batchSize ?? batch.batchSize ?? 1024;
|
|
50979
51025
|
const deployless = parameters.deployless ?? batch.deployless ?? false;
|
|
@@ -50994,8 +51040,8 @@ async function multicall(client, parameters) {
|
|
|
50994
51040
|
const chunkedCalls = [[]];
|
|
50995
51041
|
let currentChunk = 0;
|
|
50996
51042
|
let currentChunkSize = 0;
|
|
50997
|
-
for (let i = 0; i <
|
|
50998
|
-
const { abi: abi2, address, args, functionName } =
|
|
51043
|
+
for (let i = 0; i < contracts2.length; i++) {
|
|
51044
|
+
const { abi: abi2, address, args, functionName } = contracts2[i];
|
|
50999
51045
|
try {
|
|
51000
51046
|
const callData = encodeFunctionData({ abi: abi2, args, functionName });
|
|
51001
51047
|
currentChunkSize += (callData.length - 2) / 2;
|
|
@@ -51089,7 +51135,7 @@ async function multicall(client, parameters) {
|
|
|
51089
51135
|
for (let j = 0; j < aggregate3Result.length; j++) {
|
|
51090
51136
|
const { returnData, success } = aggregate3Result[j];
|
|
51091
51137
|
const { callData } = batches[i][j];
|
|
51092
|
-
const { abi: abi2, address, functionName, args } =
|
|
51138
|
+
const { abi: abi2, address, functionName, args } = contracts2[results.length];
|
|
51093
51139
|
try {
|
|
51094
51140
|
if (callData === "0x")
|
|
51095
51141
|
throw new AbiDecodingZeroDataError();
|
|
@@ -51116,7 +51162,7 @@ async function multicall(client, parameters) {
|
|
|
51116
51162
|
}
|
|
51117
51163
|
}
|
|
51118
51164
|
}
|
|
51119
|
-
if (results.length !==
|
|
51165
|
+
if (results.length !== contracts2.length)
|
|
51120
51166
|
throw new BaseError2("multicall results mismatch");
|
|
51121
51167
|
return results;
|
|
51122
51168
|
}
|
|
@@ -54961,9 +55007,6 @@ function classifyStatus({
|
|
|
54961
55007
|
{ escalate: true }
|
|
54962
55008
|
);
|
|
54963
55009
|
}
|
|
54964
|
-
if (!bitrefill && (routerStatus === "paid" || coinbase?.settled === true)) {
|
|
54965
|
-
return mk("settled", "Coinbase invoice settled by the funder wallet.", { terminal: true });
|
|
54966
|
-
}
|
|
54967
55010
|
if (routerStatus === "failed_pay_invoice" || routerStatus === "failed_insufficient_balance") {
|
|
54968
55011
|
return mk(
|
|
54969
55012
|
"stuck_after_payment",
|
|
@@ -54971,6 +55014,9 @@ function classifyStatus({
|
|
|
54971
55014
|
{ terminal: false, escalate: true }
|
|
54972
55015
|
);
|
|
54973
55016
|
}
|
|
55017
|
+
if (!bitrefill && (routerStatus === "paid" || coinbase?.settled === true)) {
|
|
55018
|
+
return mk("settled", "Coinbase invoice settled by the funder wallet.", { terminal: true });
|
|
55019
|
+
}
|
|
54974
55020
|
if (moneyDetected && receipt && receipt.state === "underpaid") {
|
|
54975
55021
|
return mk(
|
|
54976
55022
|
"underpaid",
|
|
@@ -55001,7 +55047,7 @@ function classifyStatus({
|
|
|
55001
55047
|
}
|
|
55002
55048
|
const exp = payment?.expiresAt ? Date.parse(payment.expiresAt) : NaN;
|
|
55003
55049
|
if (Number.isFinite(exp) && exp < now) {
|
|
55004
|
-
return mk("expired_unfunded", "The order expired before any funds arrived.
|
|
55050
|
+
return mk("expired_unfunded", "The order expired before any funds arrived. This order can no longer be paid.", {
|
|
55005
55051
|
terminal: true
|
|
55006
55052
|
});
|
|
55007
55053
|
}
|
|
@@ -55027,7 +55073,7 @@ function classifyStatus({
|
|
|
55027
55073
|
case "payment_expired":
|
|
55028
55074
|
return moneyDetected ? mk("stuck_after_payment", "Order expired AFTER funds arrived \u2014 escalate immediately.", {
|
|
55029
55075
|
escalate: true
|
|
55030
|
-
}) : mk("expired_unfunded", "Order expired unfunded.
|
|
55076
|
+
}) : mk("expired_unfunded", "Order expired unfunded. This order can no longer be paid.", { terminal: true });
|
|
55031
55077
|
case "payment_bounced":
|
|
55032
55078
|
case "payment_refunded":
|
|
55033
55079
|
return mk("stuck_after_payment", `Order ended as ${payment.status} \u2014 escalate.`, {
|
|
@@ -55228,6 +55274,17 @@ async function main2(argv) {
|
|
|
55228
55274
|
expiresAt: created.expiresAt ?? null,
|
|
55229
55275
|
expiresIn: Number.isFinite(remainingMs) ? formatRemaining(remainingMs) : null
|
|
55230
55276
|
},
|
|
55277
|
+
// Machine-readable form of the guidance below. The live order keeps
|
|
55278
|
+
// its own coin; a different coin is only possible once it has
|
|
55279
|
+
// expired unfunded (then pay on the same link creates a new order).
|
|
55280
|
+
nextAction: {
|
|
55281
|
+
type: "choose_method",
|
|
55282
|
+
canRetryQuery: true,
|
|
55283
|
+
canCreateOrder: false,
|
|
55284
|
+
canSend: false,
|
|
55285
|
+
payExistingWith: { chainId: existing.chainId ?? null, tokenSymbol: existing.tokenSymbol ?? null },
|
|
55286
|
+
switchPossibleAfter: created.expiresAt ?? null
|
|
55287
|
+
},
|
|
55231
55288
|
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
55289
|
},
|
|
55233
55290
|
EXIT_ERROR
|
|
@@ -55841,6 +55898,241 @@ async function run3(argv = process.argv.slice(2)) {
|
|
|
55841
55898
|
return main3(argv);
|
|
55842
55899
|
}
|
|
55843
55900
|
|
|
55901
|
+
// scripts/src/lib/receipt.mjs
|
|
55902
|
+
var SCHEMA_VERSION = 1;
|
|
55903
|
+
var PAYMENT_OUTCOMES = Object.freeze([
|
|
55904
|
+
"awaiting_payment",
|
|
55905
|
+
"processing",
|
|
55906
|
+
"settled",
|
|
55907
|
+
"expired_unfunded",
|
|
55908
|
+
"needs_attention",
|
|
55909
|
+
"unknown"
|
|
55910
|
+
]);
|
|
55911
|
+
var NEXT_ACTION_TYPES = Object.freeze([
|
|
55912
|
+
"pay",
|
|
55913
|
+
"check_status",
|
|
55914
|
+
"choose_method",
|
|
55915
|
+
"request_new_invoice",
|
|
55916
|
+
"contact_support",
|
|
55917
|
+
"none"
|
|
55918
|
+
]);
|
|
55919
|
+
var STATE_TO_OUTCOME = Object.freeze({
|
|
55920
|
+
awaiting_deposit: "awaiting_payment",
|
|
55921
|
+
payin_detected: "processing",
|
|
55922
|
+
payin_confirmed: "processing",
|
|
55923
|
+
bridging: "processing",
|
|
55924
|
+
paying_coinbase: "processing",
|
|
55925
|
+
settled: "settled",
|
|
55926
|
+
expired_unfunded: "expired_unfunded",
|
|
55927
|
+
invoice_expired: "expired_unfunded",
|
|
55928
|
+
deadline_unknown: "needs_attention",
|
|
55929
|
+
underpaid: "needs_attention",
|
|
55930
|
+
stuck_after_payment: "needs_attention",
|
|
55931
|
+
unknown: "unknown"
|
|
55932
|
+
});
|
|
55933
|
+
var SERVICE_DELIVERY_NOTE = {
|
|
55934
|
+
coinbase: "The invoice is paid. Arrival of the OpenRouter credits (or other goods) is not independently verified by Rozo; check the merchant account.",
|
|
55935
|
+
bitrefill: "USDC reached the Bitrefill invoice address. Delivery of the Bitrefill order is not independently verified by Rozo; check the Bitrefill order page."
|
|
55936
|
+
};
|
|
55937
|
+
function merchantSettlementProved(result) {
|
|
55938
|
+
if (result?.provider === "bitrefill") return result?.state === "settled";
|
|
55939
|
+
return result?.backend?.coinbaseSettled === true;
|
|
55940
|
+
}
|
|
55941
|
+
function hasLocalSend(result) {
|
|
55942
|
+
return Boolean(result?.localSend && result.localSend.status);
|
|
55943
|
+
}
|
|
55944
|
+
function paymentOutcomeFor(result) {
|
|
55945
|
+
if (!result) return "unknown";
|
|
55946
|
+
if (result.escalate) return "needs_attention";
|
|
55947
|
+
if (result.unknown) return "unknown";
|
|
55948
|
+
let outcome = Object.prototype.hasOwnProperty.call(STATE_TO_OUTCOME, result.state) ? STATE_TO_OUTCOME[result.state] : "unknown";
|
|
55949
|
+
if (outcome === "settled" && !merchantSettlementProved(result)) outcome = "processing";
|
|
55950
|
+
if (outcome === "settled" && result.provider !== "bitrefill") {
|
|
55951
|
+
const router = result.backend?.routerStatus ?? null;
|
|
55952
|
+
if (router && router.startsWith("failed_")) return "needs_attention";
|
|
55953
|
+
if (router && router !== "paid") outcome = "processing";
|
|
55954
|
+
}
|
|
55955
|
+
if (!result.authoritativeView && (outcome === "awaiting_payment" || outcome === "expired_unfunded")) {
|
|
55956
|
+
return "unknown";
|
|
55957
|
+
}
|
|
55958
|
+
if ((result.moneyDetected || hasLocalSend(result)) && outcome === "expired_unfunded") {
|
|
55959
|
+
return "needs_attention";
|
|
55960
|
+
}
|
|
55961
|
+
if (result.moneyDetected && outcome === "awaiting_payment") return "processing";
|
|
55962
|
+
return outcome;
|
|
55963
|
+
}
|
|
55964
|
+
function nextActionFor(result, outcome = paymentOutcomeFor(result)) {
|
|
55965
|
+
const locked = Boolean(result?.moneyDetected) || hasLocalSend(result);
|
|
55966
|
+
const base = { canRetryQuery: true, canCreateOrder: false, canSend: false };
|
|
55967
|
+
const bitrefill = result?.provider === "bitrefill";
|
|
55968
|
+
switch (outcome) {
|
|
55969
|
+
case "awaiting_payment":
|
|
55970
|
+
if (locked) {
|
|
55971
|
+
return {
|
|
55972
|
+
...base,
|
|
55973
|
+
type: "check_status",
|
|
55974
|
+
message: "A send was already recorded for this order. Do NOT pay again; poll status until the pay-in shows up."
|
|
55975
|
+
};
|
|
55976
|
+
}
|
|
55977
|
+
if (!result?.sendWindow?.ok) {
|
|
55978
|
+
const code = result?.sendWindow?.code ?? "SEND_WINDOW_UNKNOWN";
|
|
55979
|
+
if (code === "LINK_NO_LONGER_PAYABLE") {
|
|
55980
|
+
return {
|
|
55981
|
+
...base,
|
|
55982
|
+
type: "request_new_invoice",
|
|
55983
|
+
reason: code,
|
|
55984
|
+
message: "The merchant link no longer accepts payment. Do NOT fund this order; get a new payment link."
|
|
55985
|
+
};
|
|
55986
|
+
}
|
|
55987
|
+
if (code === "EXPIRED" || code === "EXPIRY_MARGIN") {
|
|
55988
|
+
return {
|
|
55989
|
+
...base,
|
|
55990
|
+
type: "check_status",
|
|
55991
|
+
reason: code,
|
|
55992
|
+
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."
|
|
55993
|
+
};
|
|
55994
|
+
}
|
|
55995
|
+
return {
|
|
55996
|
+
...base,
|
|
55997
|
+
type: "check_status",
|
|
55998
|
+
reason: code,
|
|
55999
|
+
message: "Whether this order can still be paid could not be proved. Do NOT fund it on a guess; retry."
|
|
56000
|
+
};
|
|
56001
|
+
}
|
|
56002
|
+
return {
|
|
56003
|
+
...base,
|
|
56004
|
+
type: "pay",
|
|
56005
|
+
canSend: true,
|
|
56006
|
+
message: "Send the exact amount from the order deposit block, once."
|
|
56007
|
+
};
|
|
56008
|
+
case "processing":
|
|
56009
|
+
return {
|
|
56010
|
+
...base,
|
|
56011
|
+
type: "check_status",
|
|
56012
|
+
message: "Funds are moving. Do NOT pay again; poll status."
|
|
56013
|
+
};
|
|
56014
|
+
case "settled":
|
|
56015
|
+
return { ...base, type: "none", canRetryQuery: false, message: "Nothing to do." };
|
|
56016
|
+
case "expired_unfunded":
|
|
56017
|
+
if (bitrefill) {
|
|
56018
|
+
return {
|
|
56019
|
+
...base,
|
|
56020
|
+
type: "request_new_invoice",
|
|
56021
|
+
message: "Nothing was funded. Create a fresh Bitrefill invoice and pay that one."
|
|
56022
|
+
};
|
|
56023
|
+
}
|
|
56024
|
+
return {
|
|
56025
|
+
...base,
|
|
56026
|
+
type: "choose_method",
|
|
56027
|
+
canCreateOrder: true,
|
|
56028
|
+
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."
|
|
56029
|
+
};
|
|
56030
|
+
case "needs_attention":
|
|
56031
|
+
if (result?.state === "deadline_unknown" && !locked) {
|
|
56032
|
+
return {
|
|
56033
|
+
...base,
|
|
56034
|
+
type: "request_new_invoice",
|
|
56035
|
+
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."
|
|
56036
|
+
};
|
|
56037
|
+
}
|
|
56038
|
+
return {
|
|
56039
|
+
...base,
|
|
56040
|
+
type: "contact_support",
|
|
56041
|
+
message: "Do NOT pay again and do NOT create a new order. Keep linkId, rozoPaymentId and every tx hash, and contact Rozo support."
|
|
56042
|
+
};
|
|
56043
|
+
default:
|
|
56044
|
+
return {
|
|
56045
|
+
...base,
|
|
56046
|
+
type: "check_status",
|
|
56047
|
+
message: "The order state could not be established. This is not evidence that nothing was paid. Retry with the rozoPaymentId before doing anything else."
|
|
56048
|
+
};
|
|
56049
|
+
}
|
|
56050
|
+
}
|
|
56051
|
+
function sourcePaymentLayer(result) {
|
|
56052
|
+
const payin = result?.payin || {};
|
|
56053
|
+
let status;
|
|
56054
|
+
if (result?.unknown || !result?.authoritativeView) status = "unknown";
|
|
56055
|
+
else if (payin.confirmedAt) status = "confirmed";
|
|
56056
|
+
else if (result?.moneyDetected) status = "detected";
|
|
56057
|
+
else status = "none";
|
|
56058
|
+
return {
|
|
56059
|
+
status,
|
|
56060
|
+
evidence: status === "unknown" ? null : "rozo-intents",
|
|
56061
|
+
chain: payin.chain ?? null,
|
|
56062
|
+
expected: payin.expected ?? null,
|
|
56063
|
+
received: payin.received ?? null,
|
|
56064
|
+
txHash: payin.txHash ?? null,
|
|
56065
|
+
confirmedAt: payin.confirmedAt ?? null,
|
|
56066
|
+
localSend: result?.localSend ? {
|
|
56067
|
+
status: result.localSend.status,
|
|
56068
|
+
txHash: result.localSend.txHash ?? null,
|
|
56069
|
+
claimedAt: result.localSend.claimedAt ?? null,
|
|
56070
|
+
// A wallet broadcast proves the payer sent, not that Rozo received.
|
|
56071
|
+
evidence: "local-wallet"
|
|
56072
|
+
} : null
|
|
56073
|
+
};
|
|
56074
|
+
}
|
|
56075
|
+
function merchantSettlementLayer(result) {
|
|
56076
|
+
if (result?.provider === "bitrefill") {
|
|
56077
|
+
const proved = result?.state === "settled";
|
|
56078
|
+
return {
|
|
56079
|
+
status: proved ? "confirmed" : result?.unknown ? "unknown" : "pending",
|
|
56080
|
+
evidence: proved ? "rozo-payout" : null,
|
|
56081
|
+
provider: "bitrefill",
|
|
56082
|
+
txHash: result?.payout?.txHash ?? null,
|
|
56083
|
+
confirmedAt: result?.payout?.confirmedAt ?? null
|
|
56084
|
+
};
|
|
56085
|
+
}
|
|
56086
|
+
const backend = result?.backend || {};
|
|
56087
|
+
let status;
|
|
56088
|
+
if (backend.routerStatus && backend.routerStatus.startsWith("failed_")) status = "unknown";
|
|
56089
|
+
else if (backend.coinbaseSettled === true) status = "confirmed";
|
|
56090
|
+
else if (backend.coinbaseSettled === null || backend.coinbaseSettled === void 0) {
|
|
56091
|
+
status = backend.routerStatus === "paid" || result?.unknown ? "unknown" : "pending";
|
|
56092
|
+
} else status = "pending";
|
|
56093
|
+
return {
|
|
56094
|
+
status,
|
|
56095
|
+
evidence: status === "confirmed" ? "coinbase" : null,
|
|
56096
|
+
provider: "coinbase",
|
|
56097
|
+
coinbaseStatus: backend.coinbaseStatus ?? null,
|
|
56098
|
+
routerStatus: backend.routerStatus ?? null,
|
|
56099
|
+
// The router reporting `paid` is recorded, but it is not settlement proof.
|
|
56100
|
+
routerReportsPaid: backend.routerStatus === "paid",
|
|
56101
|
+
// Coinbase may read settled because someone else paid the link.
|
|
56102
|
+
routerFailed: Boolean(backend.routerStatus && backend.routerStatus.startsWith("failed_")),
|
|
56103
|
+
payoutTxHash: result?.payout?.txHash ?? null
|
|
56104
|
+
};
|
|
56105
|
+
}
|
|
56106
|
+
function serviceDeliveryLayer(result, merchant) {
|
|
56107
|
+
return {
|
|
56108
|
+
status: "unknown",
|
|
56109
|
+
evidence: null,
|
|
56110
|
+
note: merchant.status === "confirmed" ? SERVICE_DELIVERY_NOTE[result?.provider === "bitrefill" ? "bitrefill" : "coinbase"] : "Not applicable until the merchant invoice is settled."
|
|
56111
|
+
};
|
|
56112
|
+
}
|
|
56113
|
+
function buildReceipt(result, { observedAt = (/* @__PURE__ */ new Date()).toISOString() } = {}) {
|
|
56114
|
+
const paymentOutcome = paymentOutcomeFor(result);
|
|
56115
|
+
const merchantSettlement = merchantSettlementLayer(result);
|
|
56116
|
+
return {
|
|
56117
|
+
schemaVersion: SCHEMA_VERSION,
|
|
56118
|
+
orderId: result?.rozoPaymentId ?? null,
|
|
56119
|
+
linkId: result?.linkId ?? null,
|
|
56120
|
+
provider: result?.provider ?? null,
|
|
56121
|
+
observedAt,
|
|
56122
|
+
paymentOutcome,
|
|
56123
|
+
state: result?.state ?? "unknown",
|
|
56124
|
+
sourcePayment: sourcePaymentLayer(result),
|
|
56125
|
+
merchantSettlement,
|
|
56126
|
+
serviceDelivery: serviceDeliveryLayer(result, merchantSettlement),
|
|
56127
|
+
nextAction: nextActionFor(result, paymentOutcome)
|
|
56128
|
+
};
|
|
56129
|
+
}
|
|
56130
|
+
function receiptExitCode(outcome) {
|
|
56131
|
+
if (outcome === "settled") return 0;
|
|
56132
|
+
if (outcome === "expired_unfunded" || outcome === "needs_attention") return 1;
|
|
56133
|
+
return 3;
|
|
56134
|
+
}
|
|
56135
|
+
|
|
55844
56136
|
// scripts/src/status.mjs
|
|
55845
56137
|
var POLL_INTERVAL_MS = 1e4;
|
|
55846
56138
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -55855,6 +56147,42 @@ function resolveProvider(explicit, rozoPaymentId) {
|
|
|
55855
56147
|
}
|
|
55856
56148
|
return null;
|
|
55857
56149
|
}
|
|
56150
|
+
function localSendFor(id, read = readState) {
|
|
56151
|
+
if (!id || !isRozoPaymentId(id)) return null;
|
|
56152
|
+
let state;
|
|
56153
|
+
try {
|
|
56154
|
+
state = read(id);
|
|
56155
|
+
} catch (err) {
|
|
56156
|
+
return { status: "unreadable", txHash: null, claimedAt: null, error: err?.code ?? "STATE_UNREADABLE" };
|
|
56157
|
+
}
|
|
56158
|
+
const send = state?.send;
|
|
56159
|
+
if (!send || !send.status) return null;
|
|
56160
|
+
return { status: send.status, txHash: publicHash(send.txHash ?? null), claimedAt: send.claimedAt ?? null };
|
|
56161
|
+
}
|
|
56162
|
+
function sendWindowFor({ provider, chainId, payment, status, bitrefillExpiry, now = Date.now() }) {
|
|
56163
|
+
if (provider === "bitrefill") {
|
|
56164
|
+
const deadline = bitrefillExpiry ?? null;
|
|
56165
|
+
const expiry2 = checkExpiry({
|
|
56166
|
+
now,
|
|
56167
|
+
chainId,
|
|
56168
|
+
intentExpiresAt: payment?.expiresAt ?? deadline,
|
|
56169
|
+
coinbaseExpiry: deadline
|
|
56170
|
+
});
|
|
56171
|
+
return expiry2.ok ? { ok: true, code: null, minutesOfSlack: Math.floor(expiry2.msOfSlack / 6e4) } : { ok: false, code: expiry2.code, reason: expiry2.reason };
|
|
56172
|
+
}
|
|
56173
|
+
if (!status) {
|
|
56174
|
+
return { ok: false, code: "LINK_PAYABILITY_UNKNOWN", reason: "The Coinbase link state could not be read." };
|
|
56175
|
+
}
|
|
56176
|
+
const payable = checkPayable(status, now);
|
|
56177
|
+
if (!payable.ok) return { ok: false, code: payable.code, reason: payable.reason };
|
|
56178
|
+
const expiry = checkExpiry({
|
|
56179
|
+
now,
|
|
56180
|
+
chainId,
|
|
56181
|
+
intentExpiresAt: payment?.expiresAt ?? status?.rozoPayment?.expiresAt,
|
|
56182
|
+
coinbaseExpiry: status?.coinbase?.preApprovalExpiry
|
|
56183
|
+
});
|
|
56184
|
+
return expiry.ok ? { ok: true, code: null, minutesOfSlack: Math.floor(expiry.msOfSlack / 6e4) } : { ok: false, code: expiry.code, reason: expiry.reason };
|
|
56185
|
+
}
|
|
55858
56186
|
async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
55859
56187
|
let provider = resolveProvider(explicitProvider, rozoPaymentId);
|
|
55860
56188
|
let prefetched = null;
|
|
@@ -55952,7 +56280,7 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
55952
56280
|
expected: source.amount ? formatAmount(source) : null,
|
|
55953
56281
|
received: source.amountReceived ?? null,
|
|
55954
56282
|
receipt: verdict.receipt,
|
|
55955
|
-
txHash: source.txHash ?? null,
|
|
56283
|
+
txHash: publicHash(source.txHash ?? null),
|
|
55956
56284
|
confirmedAt: source.confirmedAt ?? null,
|
|
55957
56285
|
senderAddressMasked: source.senderAddress ? maskAddress2(source.senderAddress) : null,
|
|
55958
56286
|
chain: source.chainId ? chainName(source.chainId) : null
|
|
@@ -55971,12 +56299,31 @@ async function snapshot({ rozoPaymentId, linkId, provider: explicitProvider }) {
|
|
|
55971
56299
|
};
|
|
55972
56300
|
})(),
|
|
55973
56301
|
payout: {
|
|
55974
|
-
txHash: payment?.destination?.txHash ?? status?.rozoPayment?.destination?.txHash ?? null,
|
|
56302
|
+
txHash: publicHash(payment?.destination?.txHash ?? status?.rozoPayment?.destination?.txHash ?? null),
|
|
55975
56303
|
confirmedAt: payment?.destination?.confirmedAt ?? status?.rozoPayment?.destination?.confirmedAt ?? null
|
|
55976
56304
|
},
|
|
56305
|
+
localSend: localSendFor(id),
|
|
56306
|
+
sendWindow: state === "awaiting_deposit" ? sendWindowFor({ provider, chainId: source.chainId, payment, status, bitrefillExpiry }) : null,
|
|
55977
56307
|
errors: [statusError, paymentError].filter(Boolean)
|
|
55978
56308
|
};
|
|
55979
56309
|
}
|
|
56310
|
+
var EXPIRED_UNFUNDED_GUIDANCE = "This order expired before any funds arrived. First check your own wallet: if anything was sent to the old deposit address, or a Lightning payment is still pending, do not pay again; contact support with the linkId and rozoPaymentId (the router cannot see a pending Lightning payment). If nothing was sent, re-run the same pay on the same link with the same --with: it creates a new order for this link. If pay answers PAYMENT_EXPIRED with retryable: true, the old order cannot be verified yet; wait a few minutes and retry. If it answers LINK_USED_OR_EXPIRED, or PAYMENT_EXPIRED with confirmed: true, this link cannot be paid any more; get a new payment link from the merchant (OpenRouter). If it answers ORDER_ALREADY_ACTIVE, a payment was found: do not pay again; contact support.";
|
|
56311
|
+
function statusGuidance(result) {
|
|
56312
|
+
if (result.escalate) {
|
|
56313
|
+
return "MONEY DETECTED and the order is not on a healthy path. Do NOT pay again and do NOT create a new order for this link. Preserve linkId, rozoPaymentId and every tx hash, then escalate to the operator for manual reconciliation.";
|
|
56314
|
+
}
|
|
56315
|
+
if (result.unknown) {
|
|
56316
|
+
return "The order state could not be established. This is NOT evidence that nothing was paid \u2014 do not create a new order and do not send again on the strength of it. Retry, or pass --rozo-payment-id so the authoritative pay-in view can be read.";
|
|
56317
|
+
}
|
|
56318
|
+
if (!result.authoritativeView) {
|
|
56319
|
+
return "Only the fulfilment view was readable; the pay-in view is unavailable, so the money-detected rule cannot be enforced. Pass --rozo-payment-id for a complete answer.";
|
|
56320
|
+
}
|
|
56321
|
+
if ((result.state === "expired_unfunded" || result.state === "invoice_expired") && result.provider === "bitrefill") {
|
|
56322
|
+
return "Nothing was funded, so nothing was lost. Create a fresh Bitrefill invoice and run rozo-checkout pay --bitrefill-invoice <id> --to <0x\u2026> --amount <USDC> --with <coin>";
|
|
56323
|
+
}
|
|
56324
|
+
if (result.state === "expired_unfunded") return EXPIRED_UNFUNDED_GUIDANCE;
|
|
56325
|
+
return result.terminal ? "Done." : "Still in flight. Poll again in ~10s.";
|
|
56326
|
+
}
|
|
55980
56327
|
async function main4(argv) {
|
|
55981
56328
|
const args = parseArgs(argv);
|
|
55982
56329
|
const rozoPaymentId = args["rozo-payment-id"] || (isRozoPaymentId(args._[0]) ? args._[0] : null);
|
|
@@ -56002,12 +56349,19 @@ async function main4(argv) {
|
|
|
56002
56349
|
if (next.state !== result.state) history.push({ at: (/* @__PURE__ */ new Date()).toISOString(), state: next.state });
|
|
56003
56350
|
result = next;
|
|
56004
56351
|
}
|
|
56005
|
-
const guidance =
|
|
56352
|
+
const guidance = statusGuidance(result);
|
|
56006
56353
|
const unresolved = watch && !result.terminal && !result.escalate && !result.unknown;
|
|
56007
56354
|
const failed = result.escalate || result.unknown || !result.authoritativeView;
|
|
56355
|
+
const paymentOutcome = paymentOutcomeFor(result);
|
|
56008
56356
|
emit(
|
|
56009
56357
|
{
|
|
56358
|
+
// `success` keeps its historical meaning (the query produced a clean
|
|
56359
|
+
// answer). It is NOT "the invoice is paid": read paymentOutcome.
|
|
56010
56360
|
success: !failed,
|
|
56361
|
+
schemaVersion: SCHEMA_VERSION,
|
|
56362
|
+
operationOk: !result.unknown,
|
|
56363
|
+
paymentOutcome,
|
|
56364
|
+
nextAction: nextActionFor(result, paymentOutcome),
|
|
56011
56365
|
step: "status",
|
|
56012
56366
|
...result,
|
|
56013
56367
|
history,
|
|
@@ -56023,6 +56377,40 @@ async function run4(argv = process.argv.slice(2)) {
|
|
|
56023
56377
|
return main4(argv);
|
|
56024
56378
|
}
|
|
56025
56379
|
|
|
56380
|
+
// scripts/src/receipt.mjs
|
|
56381
|
+
async function main5(argv) {
|
|
56382
|
+
const args = parseArgs(argv);
|
|
56383
|
+
const rozoPaymentId = args["rozo-payment-id"] || (isRozoPaymentId(args._[0]) ? args._[0] : null);
|
|
56384
|
+
const linkId = args["link-id"] || (!rozoPaymentId ? args._[0] : null);
|
|
56385
|
+
const provider = args.provider ? String(args.provider) : null;
|
|
56386
|
+
if (provider && provider !== "bitrefill" && provider !== "coinbase") {
|
|
56387
|
+
usage("--provider must be coinbase or bitrefill");
|
|
56388
|
+
}
|
|
56389
|
+
if (provider === "bitrefill" && !rozoPaymentId) {
|
|
56390
|
+
usage("A Bitrefill order is tracked by --rozo-payment-id <uuid>.");
|
|
56391
|
+
}
|
|
56392
|
+
if (!rozoPaymentId && !linkId) {
|
|
56393
|
+
usage("Required: --rozo-payment-id <uuid> and/or --link-id <pl_* | paymentSession_*>");
|
|
56394
|
+
}
|
|
56395
|
+
const result = await snapshot({ rozoPaymentId, linkId, provider });
|
|
56396
|
+
const receipt = buildReceipt(result);
|
|
56397
|
+
const exitCode = receiptExitCode(receipt.paymentOutcome);
|
|
56398
|
+
emit(
|
|
56399
|
+
{
|
|
56400
|
+
success: receipt.paymentOutcome === "settled",
|
|
56401
|
+
operationOk: !result.unknown,
|
|
56402
|
+
step: "receipt",
|
|
56403
|
+
receipt,
|
|
56404
|
+
errors: result.errors,
|
|
56405
|
+
...exitCode === 0 ? {} : { support: SUPPORT }
|
|
56406
|
+
},
|
|
56407
|
+
exitCode
|
|
56408
|
+
);
|
|
56409
|
+
}
|
|
56410
|
+
async function run5(argv = process.argv.slice(2)) {
|
|
56411
|
+
return main5(argv);
|
|
56412
|
+
}
|
|
56413
|
+
|
|
56026
56414
|
// scripts/src/lib/passphrase.mjs
|
|
56027
56415
|
import readline from "node:readline";
|
|
56028
56416
|
function promptPassphrase(prompt = " Keystore passphrase: ") {
|
|
@@ -56262,6 +56650,290 @@ function broadcastOutcome({ receiptStatus, executionError = null, receiptSeen =
|
|
|
56262
56650
|
};
|
|
56263
56651
|
}
|
|
56264
56652
|
|
|
56653
|
+
// scripts/src/lib/gas.mjs
|
|
56654
|
+
var EVM_FEE_COIN = Object.freeze({
|
|
56655
|
+
1: { symbol: "ETH", network: "Ethereum" },
|
|
56656
|
+
56: { symbol: "BNB", network: "BNB Chain" },
|
|
56657
|
+
137: { symbol: "POL", network: "Polygon" },
|
|
56658
|
+
8453: { symbol: "ETH", network: "Base" }
|
|
56659
|
+
});
|
|
56660
|
+
var DEFAULT_GAS_BUFFER_PCT = 20;
|
|
56661
|
+
var MAX_GAS_BUFFER_PCT = 200;
|
|
56662
|
+
function gasBufferPct(env = process.env) {
|
|
56663
|
+
const raw = env?.ROZO_CHECKOUT_GAS_BUFFER_PCT;
|
|
56664
|
+
if (raw === void 0 || raw === "") return DEFAULT_GAS_BUFFER_PCT;
|
|
56665
|
+
const n = Number(raw);
|
|
56666
|
+
if (!Number.isInteger(n) || n < 0 || n > MAX_GAS_BUFFER_PCT) return DEFAULT_GAS_BUFFER_PCT;
|
|
56667
|
+
return n;
|
|
56668
|
+
}
|
|
56669
|
+
function formatAtomic(atomic, decimals) {
|
|
56670
|
+
const neg = atomic < 0n;
|
|
56671
|
+
const v = neg ? -atomic : atomic;
|
|
56672
|
+
const base = 10n ** BigInt(decimals);
|
|
56673
|
+
const whole = v / base;
|
|
56674
|
+
const frac = (v % base).toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
56675
|
+
return `${neg ? "-" : ""}${whole}${frac ? `.${frac}` : ""}`;
|
|
56676
|
+
}
|
|
56677
|
+
function evmFeeRequirement({ gasEstimate, maxFeePerGas, gasPrice, bufferPct = DEFAULT_GAS_BUFFER_PCT }) {
|
|
56678
|
+
const perGas = typeof maxFeePerGas === "bigint" ? maxFeePerGas : typeof gasPrice === "bigint" ? gasPrice : null;
|
|
56679
|
+
if (typeof gasEstimate !== "bigint" || perGas === null) return null;
|
|
56680
|
+
const gasLimit = (gasEstimate * BigInt(100 + bufferPct) + 99n) / 100n;
|
|
56681
|
+
return { gasLimit, perGas, pricing: typeof maxFeePerGas === "bigint" ? "eip1559" : "legacy", fee: gasLimit * perGas };
|
|
56682
|
+
}
|
|
56683
|
+
function assessFee({ balance, required, decimals, symbol, network }) {
|
|
56684
|
+
const ok = required === null ? balance > 0n : balance >= required;
|
|
56685
|
+
const details = {
|
|
56686
|
+
network,
|
|
56687
|
+
feeCoin: symbol,
|
|
56688
|
+
balance: formatAtomic(balance, decimals),
|
|
56689
|
+
required: required === null ? null : formatAtomic(required, decimals),
|
|
56690
|
+
shortfall: required === null || ok ? null : formatAtomic(required - balance, decimals)
|
|
56691
|
+
};
|
|
56692
|
+
return { ok, details };
|
|
56693
|
+
}
|
|
56694
|
+
function insufficient(details, extra) {
|
|
56695
|
+
const need = details.required ? `about ${details.required} ${details.feeCoin}` : `some ${details.feeCoin}`;
|
|
56696
|
+
return new SkillError(
|
|
56697
|
+
"INSUFFICIENT_GAS",
|
|
56698
|
+
`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}` : ""),
|
|
56699
|
+
details
|
|
56700
|
+
);
|
|
56701
|
+
}
|
|
56702
|
+
function unavailable(network, err) {
|
|
56703
|
+
return new SkillError(
|
|
56704
|
+
"GAS_CHECK_UNAVAILABLE",
|
|
56705
|
+
`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.`,
|
|
56706
|
+
{ network }
|
|
56707
|
+
);
|
|
56708
|
+
}
|
|
56709
|
+
async function estimateFees(client) {
|
|
56710
|
+
try {
|
|
56711
|
+
return await client.estimateFeesPerGas();
|
|
56712
|
+
} catch (err) {
|
|
56713
|
+
if (err?.name !== "Eip1559FeesNotSupportedError") throw err;
|
|
56714
|
+
return client.estimateFeesPerGas({ type: "legacy" });
|
|
56715
|
+
}
|
|
56716
|
+
}
|
|
56717
|
+
var OP_STACK_GAS_PRICE_ORACLE = "0x420000000000000000000000000000000000000F";
|
|
56718
|
+
var OP_STACK_CHAIN_IDS = /* @__PURE__ */ new Set([8453]);
|
|
56719
|
+
async function evmGasPrecheck({
|
|
56720
|
+
client,
|
|
56721
|
+
chainId,
|
|
56722
|
+
from: from15,
|
|
56723
|
+
to,
|
|
56724
|
+
data,
|
|
56725
|
+
value = 0n,
|
|
56726
|
+
bufferPct = gasBufferPct(),
|
|
56727
|
+
estimateL1Fee: estimateL1Fee2 = null
|
|
56728
|
+
}) {
|
|
56729
|
+
const coin = EVM_FEE_COIN[Number(chainId)] ?? { symbol: "native coin", network: `chain ${chainId}` };
|
|
56730
|
+
let balance;
|
|
56731
|
+
try {
|
|
56732
|
+
balance = await client.getBalance({ address: from15 });
|
|
56733
|
+
} catch (err) {
|
|
56734
|
+
throw unavailable(coin.network, err);
|
|
56735
|
+
}
|
|
56736
|
+
let gasEstimate;
|
|
56737
|
+
let fees;
|
|
56738
|
+
try {
|
|
56739
|
+
gasEstimate = await client.estimateGas({ account: from15, to, data, value });
|
|
56740
|
+
fees = await estimateFees(client);
|
|
56741
|
+
} catch (err) {
|
|
56742
|
+
if (balance === 0n) {
|
|
56743
|
+
throw insufficient(
|
|
56744
|
+
assessFee({ balance, required: null, decimals: 18, symbol: coin.symbol, network: coin.network }).details
|
|
56745
|
+
);
|
|
56746
|
+
}
|
|
56747
|
+
throw unavailable(coin.network, err);
|
|
56748
|
+
}
|
|
56749
|
+
const req = evmFeeRequirement({
|
|
56750
|
+
gasEstimate,
|
|
56751
|
+
maxFeePerGas: fees?.maxFeePerGas,
|
|
56752
|
+
gasPrice: fees?.gasPrice,
|
|
56753
|
+
bufferPct
|
|
56754
|
+
});
|
|
56755
|
+
if (!req) throw unavailable(coin.network, new Error("the node returned no fee price"));
|
|
56756
|
+
let l1Fee = 0n;
|
|
56757
|
+
if (OP_STACK_CHAIN_IDS.has(Number(chainId))) {
|
|
56758
|
+
if (typeof estimateL1Fee2 !== "function") {
|
|
56759
|
+
throw unavailable(coin.network, new Error("no L1 data fee estimator for this OP-stack chain"));
|
|
56760
|
+
}
|
|
56761
|
+
try {
|
|
56762
|
+
l1Fee = (await estimateL1Fee2() * BigInt(100 + bufferPct) + 99n) / 100n;
|
|
56763
|
+
} catch (err) {
|
|
56764
|
+
throw unavailable(coin.network, err);
|
|
56765
|
+
}
|
|
56766
|
+
}
|
|
56767
|
+
const required = req.fee + l1Fee + value;
|
|
56768
|
+
const { ok, details } = assessFee({ balance, required, decimals: 18, symbol: coin.symbol, network: coin.network });
|
|
56769
|
+
const full = {
|
|
56770
|
+
...details,
|
|
56771
|
+
gasLimit: req.gasLimit.toString(),
|
|
56772
|
+
pricing: req.pricing,
|
|
56773
|
+
bufferPct,
|
|
56774
|
+
l1DataFee: OP_STACK_CHAIN_IDS.has(Number(chainId)) ? formatAtomic(l1Fee, 18) : null
|
|
56775
|
+
};
|
|
56776
|
+
if (!ok) throw insufficient(full);
|
|
56777
|
+
return full;
|
|
56778
|
+
}
|
|
56779
|
+
var LAMPORTS_DECIMALS = 9;
|
|
56780
|
+
async function solFeePrecheck({ connection, payer, message }) {
|
|
56781
|
+
const network = "Solana";
|
|
56782
|
+
let balance;
|
|
56783
|
+
let fee;
|
|
56784
|
+
let rentMin;
|
|
56785
|
+
try {
|
|
56786
|
+
balance = BigInt(await connection.getBalance(payer, "confirmed"));
|
|
56787
|
+
const res = await connection.getFeeForMessage(message, "confirmed");
|
|
56788
|
+
if (res?.value === null || res?.value === void 0) {
|
|
56789
|
+
throw new Error("the node returned no fee for this transaction (stale blockhash?)");
|
|
56790
|
+
}
|
|
56791
|
+
fee = BigInt(res.value);
|
|
56792
|
+
rentMin = BigInt(await connection.getMinimumBalanceForRentExemption(0));
|
|
56793
|
+
} catch (err) {
|
|
56794
|
+
if (balance === 0n) {
|
|
56795
|
+
throw insufficient(
|
|
56796
|
+
assessFee({ balance, required: null, decimals: LAMPORTS_DECIMALS, symbol: "SOL", network }).details
|
|
56797
|
+
);
|
|
56798
|
+
}
|
|
56799
|
+
throw unavailable(network, err);
|
|
56800
|
+
}
|
|
56801
|
+
const remaining = balance - fee;
|
|
56802
|
+
const required = remaining === 0n ? fee : fee + rentMin;
|
|
56803
|
+
const { ok, details } = assessFee({ balance, required, decimals: LAMPORTS_DECIMALS, symbol: "SOL", network });
|
|
56804
|
+
const full = { ...details, fee: formatAtomic(fee, LAMPORTS_DECIMALS), rentExemptMinimum: formatAtomic(rentMin, LAMPORTS_DECIMALS) };
|
|
56805
|
+
if (!ok) {
|
|
56806
|
+
throw insufficient(
|
|
56807
|
+
full,
|
|
56808
|
+
balance >= fee ? "Solana also requires a wallet to keep a small minimum balance after paying the fee." : null
|
|
56809
|
+
);
|
|
56810
|
+
}
|
|
56811
|
+
return full;
|
|
56812
|
+
}
|
|
56813
|
+
|
|
56814
|
+
// node_modules/viem/_esm/op-stack/contracts.js
|
|
56815
|
+
var contracts = {
|
|
56816
|
+
gasPriceOracle: { address: "0x420000000000000000000000000000000000000F" },
|
|
56817
|
+
l1Block: { address: "0x4200000000000000000000000000000000000015" },
|
|
56818
|
+
l2CrossDomainMessenger: {
|
|
56819
|
+
address: "0x4200000000000000000000000000000000000007"
|
|
56820
|
+
},
|
|
56821
|
+
l2Erc721Bridge: { address: "0x4200000000000000000000000000000000000014" },
|
|
56822
|
+
l2StandardBridge: { address: "0x4200000000000000000000000000000000000010" },
|
|
56823
|
+
l2ToL1MessagePasser: {
|
|
56824
|
+
address: "0x4200000000000000000000000000000000000016"
|
|
56825
|
+
}
|
|
56826
|
+
};
|
|
56827
|
+
|
|
56828
|
+
// node_modules/viem/_esm/op-stack/abis.js
|
|
56829
|
+
var gasPriceOracleAbi = [
|
|
56830
|
+
{ inputs: [], stateMutability: "nonpayable", type: "constructor" },
|
|
56831
|
+
{
|
|
56832
|
+
inputs: [],
|
|
56833
|
+
name: "DECIMALS",
|
|
56834
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
56835
|
+
stateMutability: "view",
|
|
56836
|
+
type: "function"
|
|
56837
|
+
},
|
|
56838
|
+
{
|
|
56839
|
+
inputs: [],
|
|
56840
|
+
name: "baseFee",
|
|
56841
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
56842
|
+
stateMutability: "view",
|
|
56843
|
+
type: "function"
|
|
56844
|
+
},
|
|
56845
|
+
{
|
|
56846
|
+
inputs: [],
|
|
56847
|
+
name: "decimals",
|
|
56848
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
56849
|
+
stateMutability: "pure",
|
|
56850
|
+
type: "function"
|
|
56851
|
+
},
|
|
56852
|
+
{
|
|
56853
|
+
inputs: [],
|
|
56854
|
+
name: "gasPrice",
|
|
56855
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
56856
|
+
stateMutability: "view",
|
|
56857
|
+
type: "function"
|
|
56858
|
+
},
|
|
56859
|
+
{
|
|
56860
|
+
inputs: [{ internalType: "bytes", name: "_data", type: "bytes" }],
|
|
56861
|
+
name: "getL1Fee",
|
|
56862
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
56863
|
+
stateMutability: "view",
|
|
56864
|
+
type: "function"
|
|
56865
|
+
},
|
|
56866
|
+
{
|
|
56867
|
+
inputs: [{ internalType: "bytes", name: "_data", type: "bytes" }],
|
|
56868
|
+
name: "getL1GasUsed",
|
|
56869
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
56870
|
+
stateMutability: "view",
|
|
56871
|
+
type: "function"
|
|
56872
|
+
},
|
|
56873
|
+
{
|
|
56874
|
+
inputs: [],
|
|
56875
|
+
name: "l1BaseFee",
|
|
56876
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
56877
|
+
stateMutability: "view",
|
|
56878
|
+
type: "function"
|
|
56879
|
+
},
|
|
56880
|
+
{
|
|
56881
|
+
inputs: [],
|
|
56882
|
+
name: "overhead",
|
|
56883
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
56884
|
+
stateMutability: "view",
|
|
56885
|
+
type: "function"
|
|
56886
|
+
},
|
|
56887
|
+
{
|
|
56888
|
+
inputs: [],
|
|
56889
|
+
name: "scalar",
|
|
56890
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
56891
|
+
stateMutability: "view",
|
|
56892
|
+
type: "function"
|
|
56893
|
+
},
|
|
56894
|
+
{
|
|
56895
|
+
inputs: [],
|
|
56896
|
+
name: "version",
|
|
56897
|
+
outputs: [{ internalType: "string", name: "", type: "string" }],
|
|
56898
|
+
stateMutability: "view",
|
|
56899
|
+
type: "function"
|
|
56900
|
+
}
|
|
56901
|
+
];
|
|
56902
|
+
|
|
56903
|
+
// node_modules/viem/_esm/op-stack/actions/estimateL1Fee.js
|
|
56904
|
+
init_getChainContractAddress();
|
|
56905
|
+
async function estimateL1Fee(client, args) {
|
|
56906
|
+
const { chain = client.chain, gasPriceOracleAddress: gasPriceOracleAddress_ } = args;
|
|
56907
|
+
const gasPriceOracleAddress = (() => {
|
|
56908
|
+
if (gasPriceOracleAddress_)
|
|
56909
|
+
return gasPriceOracleAddress_;
|
|
56910
|
+
if (chain)
|
|
56911
|
+
return getChainContractAddress({
|
|
56912
|
+
chain,
|
|
56913
|
+
contract: "gasPriceOracle"
|
|
56914
|
+
});
|
|
56915
|
+
return contracts.gasPriceOracle.address;
|
|
56916
|
+
})();
|
|
56917
|
+
const transaction = serializeTransaction({
|
|
56918
|
+
...args,
|
|
56919
|
+
chainId: chain?.id ?? 1,
|
|
56920
|
+
type: "eip1559",
|
|
56921
|
+
// Set upper-limit-ish stub values. Shouldn't affect the estimate too much as we are
|
|
56922
|
+
// tweaking dust bytes here (as opposed to long `data` bytes).
|
|
56923
|
+
// See: https://github.com/ethereum-optimism/optimism/blob/54d02df55523c9e1b4b38ed082c12a42087323a0/packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L242-L248.
|
|
56924
|
+
gas: args.data ? 300000n : 21000n,
|
|
56925
|
+
maxFeePerGas: parseGwei("5"),
|
|
56926
|
+
maxPriorityFeePerGas: parseGwei("1"),
|
|
56927
|
+
nonce: 1
|
|
56928
|
+
});
|
|
56929
|
+
return readContract(client, {
|
|
56930
|
+
abi: gasPriceOracleAbi,
|
|
56931
|
+
address: gasPriceOracleAddress,
|
|
56932
|
+
functionName: "getL1Fee",
|
|
56933
|
+
args: [transaction]
|
|
56934
|
+
});
|
|
56935
|
+
}
|
|
56936
|
+
|
|
56265
56937
|
// node_modules/viem/_esm/accounts/privateKeyToAccount.js
|
|
56266
56938
|
init_secp256k1();
|
|
56267
56939
|
init_toHex();
|
|
@@ -56455,7 +57127,7 @@ function chainDef(chainId, rpcUrl) {
|
|
|
56455
57127
|
rpcUrls: { default: { http: [rpcUrl] } }
|
|
56456
57128
|
});
|
|
56457
57129
|
}
|
|
56458
|
-
async function
|
|
57130
|
+
async function main6(argv) {
|
|
56459
57131
|
const args = parseArgs(argv);
|
|
56460
57132
|
const rozoPaymentId = assertRozoPaymentId(args["rozo-payment-id"] || args._[0]);
|
|
56461
57133
|
assertNoTrackedDotEnv();
|
|
@@ -56533,6 +57205,25 @@ async function main5(argv) {
|
|
|
56533
57205
|
`Sender holds ${balance} atomic units of ${source.tokenSymbol} on ${chainName(chainId)}; ${amountAtomic} are required.`
|
|
56534
57206
|
);
|
|
56535
57207
|
}
|
|
57208
|
+
const data = encodeFunctionData({
|
|
57209
|
+
abi: ERC20_TRANSFER_ABI,
|
|
57210
|
+
functionName: "transfer",
|
|
57211
|
+
args: [to, amountAtomic]
|
|
57212
|
+
});
|
|
57213
|
+
const networkFee = await evmGasPrecheck({
|
|
57214
|
+
client: pub,
|
|
57215
|
+
chainId,
|
|
57216
|
+
from: sender,
|
|
57217
|
+
to: tokenAddress,
|
|
57218
|
+
data,
|
|
57219
|
+
estimateL1Fee: OP_STACK_CHAIN_IDS.has(chainId) ? () => estimateL1Fee(pub, {
|
|
57220
|
+
account: sender,
|
|
57221
|
+
to: tokenAddress,
|
|
57222
|
+
data,
|
|
57223
|
+
chain,
|
|
57224
|
+
gasPriceOracleAddress: OP_STACK_GAS_PRICE_ORACLE
|
|
57225
|
+
}) : null
|
|
57226
|
+
});
|
|
56536
57227
|
if (dryRun) {
|
|
56537
57228
|
emit({
|
|
56538
57229
|
success: true,
|
|
@@ -56548,6 +57239,7 @@ async function main5(argv) {
|
|
|
56548
57239
|
toMasked: maskAddress2(to),
|
|
56549
57240
|
fromMasked: maskAddress2(sender)
|
|
56550
57241
|
},
|
|
57242
|
+
networkFee,
|
|
56551
57243
|
confirmedAt: state.confirmation?.confirmedAt ?? null,
|
|
56552
57244
|
keySource,
|
|
56553
57245
|
// Key NAMES only — a value from a .env is never echoed.
|
|
@@ -56557,11 +57249,6 @@ async function main5(argv) {
|
|
|
56557
57249
|
});
|
|
56558
57250
|
}
|
|
56559
57251
|
const wallet = createWalletClient({ account, chain, transport: http(rpcUrl) });
|
|
56560
|
-
const data = encodeFunctionData({
|
|
56561
|
-
abi: ERC20_TRANSFER_ABI,
|
|
56562
|
-
functionName: "transfer",
|
|
56563
|
-
args: [to, amountAtomic]
|
|
56564
|
-
});
|
|
56565
57252
|
const request2 = await wallet.prepareTransactionRequest({
|
|
56566
57253
|
to: tokenAddress,
|
|
56567
57254
|
data,
|
|
@@ -56620,7 +57307,7 @@ async function main5(argv) {
|
|
|
56620
57307
|
code: landed === false ? "BROADCAST_FAILED" : "BROADCAST_AMBIGUOUS",
|
|
56621
57308
|
message: redact(err?.shortMessage || err?.message || "broadcast failed")
|
|
56622
57309
|
},
|
|
56623
|
-
signedTxHash: expectedTxHash,
|
|
57310
|
+
signedTxHash: publicHash(expectedTxHash),
|
|
56624
57311
|
nonceBefore,
|
|
56625
57312
|
foundOnChain: landed,
|
|
56626
57313
|
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."
|
|
@@ -56644,7 +57331,7 @@ async function main5(argv) {
|
|
|
56644
57331
|
confirmed: false,
|
|
56645
57332
|
rozoPaymentId,
|
|
56646
57333
|
linkId: state.linkId,
|
|
56647
|
-
txHash,
|
|
57334
|
+
txHash: publicHash(txHash),
|
|
56648
57335
|
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."
|
|
56649
57336
|
},
|
|
56650
57337
|
EXIT_UNCONFIRMED
|
|
@@ -56665,7 +57352,7 @@ async function main5(argv) {
|
|
|
56665
57352
|
confirmed: succeeded,
|
|
56666
57353
|
rozoPaymentId,
|
|
56667
57354
|
linkId: state.linkId,
|
|
56668
|
-
txHash,
|
|
57355
|
+
txHash: publicHash(txHash),
|
|
56669
57356
|
blockNumber: receipt.blockNumber?.toString?.() ?? null,
|
|
56670
57357
|
...succeeded ? {} : { error: { code: outcome.code, message: "The transfer reverted on chain." } },
|
|
56671
57358
|
sent: {
|
|
@@ -56683,8 +57370,8 @@ async function main5(argv) {
|
|
|
56683
57370
|
outcome.exitCode
|
|
56684
57371
|
);
|
|
56685
57372
|
}
|
|
56686
|
-
async function
|
|
56687
|
-
return
|
|
57373
|
+
async function run6(argv = process.argv.slice(2)) {
|
|
57374
|
+
return main6(argv);
|
|
56688
57375
|
}
|
|
56689
57376
|
|
|
56690
57377
|
// scripts/src/send-sol.mjs
|
|
@@ -56754,7 +57441,7 @@ function createTransferCheckedInstruction(source, mint, destination, owner, amou
|
|
|
56754
57441
|
var MEMO_PROGRAM_ID = new import_web32.PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
|
|
56755
57442
|
var MAINNET_GENESIS = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d";
|
|
56756
57443
|
var B58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
56757
|
-
async function
|
|
57444
|
+
async function main7(argv) {
|
|
56758
57445
|
const args = parseArgs(argv);
|
|
56759
57446
|
const rozoPaymentId = assertRozoPaymentId(args["rozo-payment-id"] || args._[0]);
|
|
56760
57447
|
assertNoTrackedDotEnv();
|
|
@@ -56851,6 +57538,34 @@ async function main6(argv) {
|
|
|
56851
57538
|
);
|
|
56852
57539
|
}
|
|
56853
57540
|
}
|
|
57541
|
+
const tx = new import_web32.Transaction();
|
|
57542
|
+
tx.add(
|
|
57543
|
+
createTransferCheckedInstruction(
|
|
57544
|
+
fromAta,
|
|
57545
|
+
mint,
|
|
57546
|
+
toAta,
|
|
57547
|
+
keypair.publicKey,
|
|
57548
|
+
amountAtomic,
|
|
57549
|
+
mintInfo.decimals
|
|
57550
|
+
)
|
|
57551
|
+
);
|
|
57552
|
+
if (source.receiverMemo) {
|
|
57553
|
+
tx.add(
|
|
57554
|
+
new import_web32.TransactionInstruction({
|
|
57555
|
+
keys: [],
|
|
57556
|
+
programId: MEMO_PROGRAM_ID,
|
|
57557
|
+
data: Buffer.from(String(source.receiverMemo), "utf8")
|
|
57558
|
+
})
|
|
57559
|
+
);
|
|
57560
|
+
}
|
|
57561
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
|
|
57562
|
+
tx.recentBlockhash = blockhash;
|
|
57563
|
+
tx.feePayer = keypair.publicKey;
|
|
57564
|
+
const networkFee = await solFeePrecheck({
|
|
57565
|
+
connection,
|
|
57566
|
+
payer: keypair.publicKey,
|
|
57567
|
+
message: tx.compileMessage()
|
|
57568
|
+
});
|
|
56854
57569
|
if (dryRun) {
|
|
56855
57570
|
emit({
|
|
56856
57571
|
success: true,
|
|
@@ -56867,6 +57582,7 @@ async function main6(argv) {
|
|
|
56867
57582
|
toIsTokenAccount,
|
|
56868
57583
|
withMemo: Boolean(source.receiverMemo)
|
|
56869
57584
|
},
|
|
57585
|
+
networkFee,
|
|
56870
57586
|
confirmedAt: state.confirmation?.confirmedAt ?? null,
|
|
56871
57587
|
keySource,
|
|
56872
57588
|
// Key NAMES only — a value from a .env is never echoed.
|
|
@@ -56875,29 +57591,6 @@ async function main6(argv) {
|
|
|
56875
57591
|
note: "Nothing was signed or broadcast. Add --send (without --dry-run) to execute."
|
|
56876
57592
|
});
|
|
56877
57593
|
}
|
|
56878
|
-
const tx = new import_web32.Transaction();
|
|
56879
|
-
tx.add(
|
|
56880
|
-
createTransferCheckedInstruction(
|
|
56881
|
-
fromAta,
|
|
56882
|
-
mint,
|
|
56883
|
-
toAta,
|
|
56884
|
-
keypair.publicKey,
|
|
56885
|
-
amountAtomic,
|
|
56886
|
-
mintInfo.decimals
|
|
56887
|
-
)
|
|
56888
|
-
);
|
|
56889
|
-
if (source.receiverMemo) {
|
|
56890
|
-
tx.add(
|
|
56891
|
-
new import_web32.TransactionInstruction({
|
|
56892
|
-
keys: [],
|
|
56893
|
-
programId: MEMO_PROGRAM_ID,
|
|
56894
|
-
data: Buffer.from(String(source.receiverMemo), "utf8")
|
|
56895
|
-
})
|
|
56896
|
-
);
|
|
56897
|
-
}
|
|
56898
|
-
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
|
|
56899
|
-
tx.recentBlockhash = blockhash;
|
|
56900
|
-
tx.feePayer = keypair.publicKey;
|
|
56901
57594
|
tx.sign(keypair);
|
|
56902
57595
|
const signature = tx.signatures[0]?.signature ? base58Encode(tx.signatures[0].signature) : null;
|
|
56903
57596
|
await finalPayabilityCheck({
|
|
@@ -56949,7 +57642,7 @@ async function main6(argv) {
|
|
|
56949
57642
|
code: landed ? "BROADCAST_AMBIGUOUS" : "BROADCAST_FAILED",
|
|
56950
57643
|
message: redact(err?.message || "broadcast failed")
|
|
56951
57644
|
},
|
|
56952
|
-
signature,
|
|
57645
|
+
signature: publicHash(signature),
|
|
56953
57646
|
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."
|
|
56954
57647
|
},
|
|
56955
57648
|
EXIT_ERROR
|
|
@@ -56986,7 +57679,7 @@ async function main6(argv) {
|
|
|
56986
57679
|
confirmed: false,
|
|
56987
57680
|
rozoPaymentId,
|
|
56988
57681
|
linkId: state.linkId,
|
|
56989
|
-
txHash: sent,
|
|
57682
|
+
txHash: publicHash(sent),
|
|
56990
57683
|
error: {
|
|
56991
57684
|
code: broadcastOutcome({ executionError }).code,
|
|
56992
57685
|
message: `The transaction landed but failed on chain: ${redact(JSON.stringify(executionError))}`
|
|
@@ -57005,7 +57698,7 @@ async function main6(argv) {
|
|
|
57005
57698
|
confirmed: false,
|
|
57006
57699
|
rozoPaymentId,
|
|
57007
57700
|
linkId: state.linkId,
|
|
57008
|
-
txHash: sent,
|
|
57701
|
+
txHash: publicHash(sent),
|
|
57009
57702
|
guidance: "Broadcast but not confirmed within the wait window. Do NOT resend. Poll status.js; receipt truth is the backend confirmedAt/amountReceived."
|
|
57010
57703
|
},
|
|
57011
57704
|
EXIT_UNCONFIRMED
|
|
@@ -57019,7 +57712,7 @@ async function main6(argv) {
|
|
|
57019
57712
|
confirmed: true,
|
|
57020
57713
|
rozoPaymentId,
|
|
57021
57714
|
linkId: state.linkId,
|
|
57022
|
-
txHash: sent,
|
|
57715
|
+
txHash: publicHash(sent),
|
|
57023
57716
|
sent: {
|
|
57024
57717
|
chain: chainName(900),
|
|
57025
57718
|
tokenSymbol: source.tokenSymbol,
|
|
@@ -57047,8 +57740,8 @@ function base58Encode(buf) {
|
|
|
57047
57740
|
}
|
|
57048
57741
|
return out2;
|
|
57049
57742
|
}
|
|
57050
|
-
async function
|
|
57051
|
-
return
|
|
57743
|
+
async function run7(argv = process.argv.slice(2)) {
|
|
57744
|
+
return main7(argv);
|
|
57052
57745
|
}
|
|
57053
57746
|
|
|
57054
57747
|
// scripts/src/cli.mjs
|
|
@@ -57251,6 +57944,49 @@ async function cmdStatus(opts) {
|
|
|
57251
57944
|
out();
|
|
57252
57945
|
return exitCode;
|
|
57253
57946
|
}
|
|
57947
|
+
var LAYER_LABEL = {
|
|
57948
|
+
confirmed: green("confirmed"),
|
|
57949
|
+
detected: yellow("detected"),
|
|
57950
|
+
pending: yellow("pending"),
|
|
57951
|
+
none: dim("none"),
|
|
57952
|
+
unknown: yellow("unknown")
|
|
57953
|
+
};
|
|
57954
|
+
async function cmdReceipt(opts) {
|
|
57955
|
+
const argv = targetToArgs(opts.target);
|
|
57956
|
+
if (opts.provider) argv.push("--provider", opts.provider);
|
|
57957
|
+
if (!opts.json) out(dim(" Reading the order\u2026 (no money moves)"));
|
|
57958
|
+
const { payload, exitCode } = await step(run5, argv);
|
|
57959
|
+
if (opts.json) {
|
|
57960
|
+
printJson(payload);
|
|
57961
|
+
return exitCode;
|
|
57962
|
+
}
|
|
57963
|
+
if (payload.error) {
|
|
57964
|
+
printError(payload);
|
|
57965
|
+
printSupport();
|
|
57966
|
+
return exitCode;
|
|
57967
|
+
}
|
|
57968
|
+
const r = payload.receipt;
|
|
57969
|
+
out();
|
|
57970
|
+
out(` Outcome ${outcomeLabel(r.paymentOutcome)} ${dim(`(${r.state})`)}`);
|
|
57971
|
+
out(` Your payment ${LAYER_LABEL[r.sourcePayment.status] ?? r.sourcePayment.status}` + (r.sourcePayment.txHash ? ` ${r.sourcePayment.txHash}` : ""));
|
|
57972
|
+
if (r.sourcePayment.localSend && r.sourcePayment.status !== "confirmed") {
|
|
57973
|
+
out(dim(` this machine sent it (${r.sourcePayment.localSend.status})` + (r.sourcePayment.localSend.txHash ? `: ${r.sourcePayment.localSend.txHash}` : "")));
|
|
57974
|
+
}
|
|
57975
|
+
out(` Merchant invoice ${LAYER_LABEL[r.merchantSettlement.status] ?? r.merchantSettlement.status}` + (r.merchantSettlement.coinbaseStatus ? ` ${dim(r.merchantSettlement.coinbaseStatus)}` : ""));
|
|
57976
|
+
out(` Service delivered ${LAYER_LABEL.unknown}`);
|
|
57977
|
+
if (r.merchantSettlement.status === "confirmed") out(` ${dim(r.serviceDelivery.note)}`);
|
|
57978
|
+
out();
|
|
57979
|
+
out(` Next: ${r.nextAction.type === "contact_support" ? red(r.nextAction.message) : r.nextAction.message}`);
|
|
57980
|
+
if (payload.support) printSupport();
|
|
57981
|
+
out();
|
|
57982
|
+
return exitCode;
|
|
57983
|
+
}
|
|
57984
|
+
function outcomeLabel(outcome) {
|
|
57985
|
+
if (outcome === "settled") return green(bold(outcome));
|
|
57986
|
+
if (outcome === "needs_attention" || outcome === "unknown") return red(bold(outcome));
|
|
57987
|
+
if (outcome === "expired_unfunded") return yellow(bold(outcome));
|
|
57988
|
+
return bold(outcome);
|
|
57989
|
+
}
|
|
57254
57990
|
function presetFor(chainId, tokenSymbol) {
|
|
57255
57991
|
const hit = PICKER_OPTIONS.find(
|
|
57256
57992
|
(o) => o.chainId === String(chainId) && o.tokenSymbol === String(tokenSymbol).toUpperCase()
|
|
@@ -57435,7 +58171,7 @@ async function cmdPay(opts) {
|
|
|
57435
58171
|
}
|
|
57436
58172
|
if (opts.send) {
|
|
57437
58173
|
const family = chainFamily(deposit.chainId);
|
|
57438
|
-
const sender = family === "evm" ?
|
|
58174
|
+
const sender = family === "evm" ? run6 : family === "solana" ? run7 : null;
|
|
57439
58175
|
if (!sender) {
|
|
57440
58176
|
const payload = formatFailure({
|
|
57441
58177
|
code: "SEND_UNSUPPORTED_CHAIN",
|
|
@@ -57547,7 +58283,7 @@ function printDeposit(deposit, opts) {
|
|
|
57547
58283
|
out(dim(" Send it exactly once."));
|
|
57548
58284
|
out();
|
|
57549
58285
|
}
|
|
57550
|
-
async function
|
|
58286
|
+
async function main8() {
|
|
57551
58287
|
let opts;
|
|
57552
58288
|
try {
|
|
57553
58289
|
opts = parseCliArgs(process.argv.slice(2));
|
|
@@ -57576,6 +58312,8 @@ async function main7() {
|
|
|
57576
58312
|
return cmdQuote(opts);
|
|
57577
58313
|
case "status":
|
|
57578
58314
|
return cmdStatus(opts);
|
|
58315
|
+
case "receipt":
|
|
58316
|
+
return cmdReceipt(opts);
|
|
57579
58317
|
case "pay":
|
|
57580
58318
|
try {
|
|
57581
58319
|
return await cmdPay(opts);
|
|
@@ -57596,7 +58334,7 @@ async function main7() {
|
|
|
57596
58334
|
return EXIT_USAGE;
|
|
57597
58335
|
}
|
|
57598
58336
|
}
|
|
57599
|
-
|
|
58337
|
+
main8().then((code) => process.exit(code ?? EXIT_OK)).catch((err) => {
|
|
57600
58338
|
const payload = formatFailure(err);
|
|
57601
58339
|
if (process.argv.includes("--json")) printJson(payload);
|
|
57602
58340
|
else {
|