@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.
@@ -3441,6 +3441,9 @@ function carry(digits) {
3441
3441
  }
3442
3442
  return `1${out.join("")}`;
3443
3443
  }
3444
+ function fromGwei(gwei, unit = "wei") {
3445
+ return from(gwei, exponents.gwei - exponents[unit]);
3446
+ }
3444
3447
  var exponents, InvalidDecimalNumberError, InvalidDecimalsError;
3445
3448
  var init_Value = __esm({
3446
3449
  "node_modules/viem/_esm/utils/unit/Value.js"() {
@@ -10006,7 +10009,20 @@ function redact(text) {
10006
10009
  );
10007
10010
  return s;
10008
10011
  }
10012
+ var PublicHash = class {
10013
+ constructor(value) {
10014
+ this.value = value;
10015
+ }
10016
+ toJSON() {
10017
+ return this.value;
10018
+ }
10019
+ };
10020
+ var TX_HASH_VALUE = /^(0x[0-9a-fA-F]{64}|[1-9A-HJ-NP-Za-km-z]{86,88})$/;
10021
+ function publicHash(value) {
10022
+ return typeof value === "string" && TX_HASH_VALUE.test(value) ? new PublicHash(value) : value;
10023
+ }
10009
10024
  function redactDeep(value) {
10025
+ if (value instanceof PublicHash) return value.value;
10010
10026
  if (typeof value === "string") return redact(value);
10011
10027
  if (Array.isArray(value)) return value.map(redactDeep);
10012
10028
  if (value && typeof value === "object") {
@@ -18315,6 +18331,12 @@ function formatUnits(value, decimals) {
18315
18331
  return format(value, decimals);
18316
18332
  }
18317
18333
 
18334
+ // node_modules/viem/_esm/utils/unit/parseGwei.js
18335
+ init_Value();
18336
+ function parseGwei(ether, unit = "wei") {
18337
+ return fromGwei(ether, unit);
18338
+ }
18339
+
18318
18340
  // node_modules/viem/_esm/utils/unit/parseUnits.js
18319
18341
  init_Value();
18320
18342
  function parseUnits(value, decimals) {
@@ -18458,7 +18480,7 @@ init_createBatchScheduler();
18458
18480
  init_stringify();
18459
18481
  async function multicall(client, parameters) {
18460
18482
  const { account, authorizationList, allowFailure = true, blockHash, blockNumber, blockOverrides, blockTag, requireCanonical, stateOverride } = parameters;
18461
- const contracts = parameters.contracts;
18483
+ const contracts2 = parameters.contracts;
18462
18484
  const batch = typeof client.batch?.multicall === "object" ? client.batch.multicall : {};
18463
18485
  const batchSize = parameters.batchSize ?? batch.batchSize ?? 1024;
18464
18486
  const deployless = parameters.deployless ?? batch.deployless ?? false;
@@ -18479,8 +18501,8 @@ async function multicall(client, parameters) {
18479
18501
  const chunkedCalls = [[]];
18480
18502
  let currentChunk = 0;
18481
18503
  let currentChunkSize = 0;
18482
- for (let i = 0; i < contracts.length; i++) {
18483
- const { abi: abi2, address, args, functionName } = contracts[i];
18504
+ for (let i = 0; i < contracts2.length; i++) {
18505
+ const { abi: abi2, address, args, functionName } = contracts2[i];
18484
18506
  try {
18485
18507
  const callData = encodeFunctionData({ abi: abi2, args, functionName });
18486
18508
  currentChunkSize += (callData.length - 2) / 2;
@@ -18574,7 +18596,7 @@ async function multicall(client, parameters) {
18574
18596
  for (let j = 0; j < aggregate3Result.length; j++) {
18575
18597
  const { returnData, success } = aggregate3Result[j];
18576
18598
  const { callData } = batches[i][j];
18577
- const { abi: abi2, address, functionName, args } = contracts[results.length];
18599
+ const { abi: abi2, address, functionName, args } = contracts2[results.length];
18578
18600
  try {
18579
18601
  if (callData === "0x")
18580
18602
  throw new AbiDecodingZeroDataError();
@@ -18601,7 +18623,7 @@ async function multicall(client, parameters) {
18601
18623
  }
18602
18624
  }
18603
18625
  }
18604
- if (results.length !== contracts.length)
18626
+ if (results.length !== contracts2.length)
18605
18627
  throw new BaseError2("multicall results mismatch");
18606
18628
  return results;
18607
18629
  }
@@ -22802,6 +22824,256 @@ function broadcastOutcome({ receiptStatus, executionError = null, receiptSeen =
22802
22824
  };
22803
22825
  }
22804
22826
 
22827
+ // scripts/src/lib/gas.mjs
22828
+ var EVM_FEE_COIN = Object.freeze({
22829
+ 1: { symbol: "ETH", network: "Ethereum" },
22830
+ 56: { symbol: "BNB", network: "BNB Chain" },
22831
+ 137: { symbol: "POL", network: "Polygon" },
22832
+ 8453: { symbol: "ETH", network: "Base" }
22833
+ });
22834
+ var DEFAULT_GAS_BUFFER_PCT = 20;
22835
+ var MAX_GAS_BUFFER_PCT = 200;
22836
+ function gasBufferPct(env = process.env) {
22837
+ const raw = env?.ROZO_CHECKOUT_GAS_BUFFER_PCT;
22838
+ if (raw === void 0 || raw === "") return DEFAULT_GAS_BUFFER_PCT;
22839
+ const n = Number(raw);
22840
+ if (!Number.isInteger(n) || n < 0 || n > MAX_GAS_BUFFER_PCT) return DEFAULT_GAS_BUFFER_PCT;
22841
+ return n;
22842
+ }
22843
+ function formatAtomic(atomic, decimals) {
22844
+ const neg = atomic < 0n;
22845
+ const v = neg ? -atomic : atomic;
22846
+ const base = 10n ** BigInt(decimals);
22847
+ const whole = v / base;
22848
+ const frac = (v % base).toString().padStart(decimals, "0").replace(/0+$/, "");
22849
+ return `${neg ? "-" : ""}${whole}${frac ? `.${frac}` : ""}`;
22850
+ }
22851
+ function evmFeeRequirement({ gasEstimate, maxFeePerGas, gasPrice, bufferPct = DEFAULT_GAS_BUFFER_PCT }) {
22852
+ const perGas = typeof maxFeePerGas === "bigint" ? maxFeePerGas : typeof gasPrice === "bigint" ? gasPrice : null;
22853
+ if (typeof gasEstimate !== "bigint" || perGas === null) return null;
22854
+ const gasLimit = (gasEstimate * BigInt(100 + bufferPct) + 99n) / 100n;
22855
+ return { gasLimit, perGas, pricing: typeof maxFeePerGas === "bigint" ? "eip1559" : "legacy", fee: gasLimit * perGas };
22856
+ }
22857
+ function assessFee({ balance, required, decimals, symbol, network }) {
22858
+ const ok = required === null ? balance > 0n : balance >= required;
22859
+ const details = {
22860
+ network,
22861
+ feeCoin: symbol,
22862
+ balance: formatAtomic(balance, decimals),
22863
+ required: required === null ? null : formatAtomic(required, decimals),
22864
+ shortfall: required === null || ok ? null : formatAtomic(required - balance, decimals)
22865
+ };
22866
+ return { ok, details };
22867
+ }
22868
+ function insufficient(details, extra) {
22869
+ const need = details.required ? `about ${details.required} ${details.feeCoin}` : `some ${details.feeCoin}`;
22870
+ return new SkillError(
22871
+ "INSUFFICIENT_GAS",
22872
+ `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}` : ""),
22873
+ details
22874
+ );
22875
+ }
22876
+ function unavailable(network, err) {
22877
+ return new SkillError(
22878
+ "GAS_CHECK_UNAVAILABLE",
22879
+ `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.`,
22880
+ { network }
22881
+ );
22882
+ }
22883
+ async function estimateFees(client) {
22884
+ try {
22885
+ return await client.estimateFeesPerGas();
22886
+ } catch (err) {
22887
+ if (err?.name !== "Eip1559FeesNotSupportedError") throw err;
22888
+ return client.estimateFeesPerGas({ type: "legacy" });
22889
+ }
22890
+ }
22891
+ var OP_STACK_GAS_PRICE_ORACLE = "0x420000000000000000000000000000000000000F";
22892
+ var OP_STACK_CHAIN_IDS = /* @__PURE__ */ new Set([8453]);
22893
+ async function evmGasPrecheck({
22894
+ client,
22895
+ chainId,
22896
+ from: from15,
22897
+ to,
22898
+ data,
22899
+ value = 0n,
22900
+ bufferPct = gasBufferPct(),
22901
+ estimateL1Fee: estimateL1Fee2 = null
22902
+ }) {
22903
+ const coin = EVM_FEE_COIN[Number(chainId)] ?? { symbol: "native coin", network: `chain ${chainId}` };
22904
+ let balance;
22905
+ try {
22906
+ balance = await client.getBalance({ address: from15 });
22907
+ } catch (err) {
22908
+ throw unavailable(coin.network, err);
22909
+ }
22910
+ let gasEstimate;
22911
+ let fees;
22912
+ try {
22913
+ gasEstimate = await client.estimateGas({ account: from15, to, data, value });
22914
+ fees = await estimateFees(client);
22915
+ } catch (err) {
22916
+ if (balance === 0n) {
22917
+ throw insufficient(
22918
+ assessFee({ balance, required: null, decimals: 18, symbol: coin.symbol, network: coin.network }).details
22919
+ );
22920
+ }
22921
+ throw unavailable(coin.network, err);
22922
+ }
22923
+ const req = evmFeeRequirement({
22924
+ gasEstimate,
22925
+ maxFeePerGas: fees?.maxFeePerGas,
22926
+ gasPrice: fees?.gasPrice,
22927
+ bufferPct
22928
+ });
22929
+ if (!req) throw unavailable(coin.network, new Error("the node returned no fee price"));
22930
+ let l1Fee = 0n;
22931
+ if (OP_STACK_CHAIN_IDS.has(Number(chainId))) {
22932
+ if (typeof estimateL1Fee2 !== "function") {
22933
+ throw unavailable(coin.network, new Error("no L1 data fee estimator for this OP-stack chain"));
22934
+ }
22935
+ try {
22936
+ l1Fee = (await estimateL1Fee2() * BigInt(100 + bufferPct) + 99n) / 100n;
22937
+ } catch (err) {
22938
+ throw unavailable(coin.network, err);
22939
+ }
22940
+ }
22941
+ const required = req.fee + l1Fee + value;
22942
+ const { ok, details } = assessFee({ balance, required, decimals: 18, symbol: coin.symbol, network: coin.network });
22943
+ const full = {
22944
+ ...details,
22945
+ gasLimit: req.gasLimit.toString(),
22946
+ pricing: req.pricing,
22947
+ bufferPct,
22948
+ l1DataFee: OP_STACK_CHAIN_IDS.has(Number(chainId)) ? formatAtomic(l1Fee, 18) : null
22949
+ };
22950
+ if (!ok) throw insufficient(full);
22951
+ return full;
22952
+ }
22953
+
22954
+ // node_modules/viem/_esm/op-stack/contracts.js
22955
+ var contracts = {
22956
+ gasPriceOracle: { address: "0x420000000000000000000000000000000000000F" },
22957
+ l1Block: { address: "0x4200000000000000000000000000000000000015" },
22958
+ l2CrossDomainMessenger: {
22959
+ address: "0x4200000000000000000000000000000000000007"
22960
+ },
22961
+ l2Erc721Bridge: { address: "0x4200000000000000000000000000000000000014" },
22962
+ l2StandardBridge: { address: "0x4200000000000000000000000000000000000010" },
22963
+ l2ToL1MessagePasser: {
22964
+ address: "0x4200000000000000000000000000000000000016"
22965
+ }
22966
+ };
22967
+
22968
+ // node_modules/viem/_esm/op-stack/abis.js
22969
+ var gasPriceOracleAbi = [
22970
+ { inputs: [], stateMutability: "nonpayable", type: "constructor" },
22971
+ {
22972
+ inputs: [],
22973
+ name: "DECIMALS",
22974
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
22975
+ stateMutability: "view",
22976
+ type: "function"
22977
+ },
22978
+ {
22979
+ inputs: [],
22980
+ name: "baseFee",
22981
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
22982
+ stateMutability: "view",
22983
+ type: "function"
22984
+ },
22985
+ {
22986
+ inputs: [],
22987
+ name: "decimals",
22988
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
22989
+ stateMutability: "pure",
22990
+ type: "function"
22991
+ },
22992
+ {
22993
+ inputs: [],
22994
+ name: "gasPrice",
22995
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
22996
+ stateMutability: "view",
22997
+ type: "function"
22998
+ },
22999
+ {
23000
+ inputs: [{ internalType: "bytes", name: "_data", type: "bytes" }],
23001
+ name: "getL1Fee",
23002
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
23003
+ stateMutability: "view",
23004
+ type: "function"
23005
+ },
23006
+ {
23007
+ inputs: [{ internalType: "bytes", name: "_data", type: "bytes" }],
23008
+ name: "getL1GasUsed",
23009
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
23010
+ stateMutability: "view",
23011
+ type: "function"
23012
+ },
23013
+ {
23014
+ inputs: [],
23015
+ name: "l1BaseFee",
23016
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
23017
+ stateMutability: "view",
23018
+ type: "function"
23019
+ },
23020
+ {
23021
+ inputs: [],
23022
+ name: "overhead",
23023
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
23024
+ stateMutability: "view",
23025
+ type: "function"
23026
+ },
23027
+ {
23028
+ inputs: [],
23029
+ name: "scalar",
23030
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
23031
+ stateMutability: "view",
23032
+ type: "function"
23033
+ },
23034
+ {
23035
+ inputs: [],
23036
+ name: "version",
23037
+ outputs: [{ internalType: "string", name: "", type: "string" }],
23038
+ stateMutability: "view",
23039
+ type: "function"
23040
+ }
23041
+ ];
23042
+
23043
+ // node_modules/viem/_esm/op-stack/actions/estimateL1Fee.js
23044
+ init_getChainContractAddress();
23045
+ async function estimateL1Fee(client, args) {
23046
+ const { chain = client.chain, gasPriceOracleAddress: gasPriceOracleAddress_ } = args;
23047
+ const gasPriceOracleAddress = (() => {
23048
+ if (gasPriceOracleAddress_)
23049
+ return gasPriceOracleAddress_;
23050
+ if (chain)
23051
+ return getChainContractAddress({
23052
+ chain,
23053
+ contract: "gasPriceOracle"
23054
+ });
23055
+ return contracts.gasPriceOracle.address;
23056
+ })();
23057
+ const transaction = serializeTransaction({
23058
+ ...args,
23059
+ chainId: chain?.id ?? 1,
23060
+ type: "eip1559",
23061
+ // Set upper-limit-ish stub values. Shouldn't affect the estimate too much as we are
23062
+ // tweaking dust bytes here (as opposed to long `data` bytes).
23063
+ // See: https://github.com/ethereum-optimism/optimism/blob/54d02df55523c9e1b4b38ed082c12a42087323a0/packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L242-L248.
23064
+ gas: args.data ? 300000n : 21000n,
23065
+ maxFeePerGas: parseGwei("5"),
23066
+ maxPriorityFeePerGas: parseGwei("1"),
23067
+ nonce: 1
23068
+ });
23069
+ return readContract(client, {
23070
+ abi: gasPriceOracleAbi,
23071
+ address: gasPriceOracleAddress,
23072
+ functionName: "getL1Fee",
23073
+ args: [transaction]
23074
+ });
23075
+ }
23076
+
22805
23077
  // node_modules/viem/_esm/accounts/privateKeyToAccount.js
22806
23078
  init_secp256k1();
22807
23079
  init_toHex();
@@ -23073,6 +23345,25 @@ async function main(argv) {
23073
23345
  `Sender holds ${balance} atomic units of ${source.tokenSymbol} on ${chainName(chainId)}; ${amountAtomic} are required.`
23074
23346
  );
23075
23347
  }
23348
+ const data = encodeFunctionData({
23349
+ abi: ERC20_TRANSFER_ABI,
23350
+ functionName: "transfer",
23351
+ args: [to, amountAtomic]
23352
+ });
23353
+ const networkFee = await evmGasPrecheck({
23354
+ client: pub,
23355
+ chainId,
23356
+ from: sender,
23357
+ to: tokenAddress,
23358
+ data,
23359
+ estimateL1Fee: OP_STACK_CHAIN_IDS.has(chainId) ? () => estimateL1Fee(pub, {
23360
+ account: sender,
23361
+ to: tokenAddress,
23362
+ data,
23363
+ chain,
23364
+ gasPriceOracleAddress: OP_STACK_GAS_PRICE_ORACLE
23365
+ }) : null
23366
+ });
23076
23367
  if (dryRun) {
23077
23368
  emit({
23078
23369
  success: true,
@@ -23088,6 +23379,7 @@ async function main(argv) {
23088
23379
  toMasked: maskAddress(to),
23089
23380
  fromMasked: maskAddress(sender)
23090
23381
  },
23382
+ networkFee,
23091
23383
  confirmedAt: state.confirmation?.confirmedAt ?? null,
23092
23384
  keySource,
23093
23385
  // Key NAMES only — a value from a .env is never echoed.
@@ -23097,11 +23389,6 @@ async function main(argv) {
23097
23389
  });
23098
23390
  }
23099
23391
  const wallet = createWalletClient({ account, chain, transport: http(rpcUrl) });
23100
- const data = encodeFunctionData({
23101
- abi: ERC20_TRANSFER_ABI,
23102
- functionName: "transfer",
23103
- args: [to, amountAtomic]
23104
- });
23105
23392
  const request2 = await wallet.prepareTransactionRequest({
23106
23393
  to: tokenAddress,
23107
23394
  data,
@@ -23160,7 +23447,7 @@ async function main(argv) {
23160
23447
  code: landed === false ? "BROADCAST_FAILED" : "BROADCAST_AMBIGUOUS",
23161
23448
  message: redact(err?.shortMessage || err?.message || "broadcast failed")
23162
23449
  },
23163
- signedTxHash: expectedTxHash,
23450
+ signedTxHash: publicHash(expectedTxHash),
23164
23451
  nonceBefore,
23165
23452
  foundOnChain: landed,
23166
23453
  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."
@@ -23184,7 +23471,7 @@ async function main(argv) {
23184
23471
  confirmed: false,
23185
23472
  rozoPaymentId,
23186
23473
  linkId: state.linkId,
23187
- txHash,
23474
+ txHash: publicHash(txHash),
23188
23475
  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."
23189
23476
  },
23190
23477
  EXIT_UNCONFIRMED
@@ -23205,7 +23492,7 @@ async function main(argv) {
23205
23492
  confirmed: succeeded,
23206
23493
  rozoPaymentId,
23207
23494
  linkId: state.linkId,
23208
- txHash,
23495
+ txHash: publicHash(txHash),
23209
23496
  blockNumber: receipt.blockNumber?.toString?.() ?? null,
23210
23497
  ...succeeded ? {} : { error: { code: outcome.code, message: "The transfer reverted on chain." } },
23211
23498
  sent: {
@@ -32177,7 +32177,20 @@ function redact(text) {
32177
32177
  );
32178
32178
  return s;
32179
32179
  }
32180
+ var PublicHash = class {
32181
+ constructor(value) {
32182
+ this.value = value;
32183
+ }
32184
+ toJSON() {
32185
+ return this.value;
32186
+ }
32187
+ };
32188
+ var TX_HASH_VALUE = /^(0x[0-9a-fA-F]{64}|[1-9A-HJ-NP-Za-km-z]{86,88})$/;
32189
+ function publicHash(value) {
32190
+ return typeof value === "string" && TX_HASH_VALUE.test(value) ? new PublicHash(value) : value;
32191
+ }
32180
32192
  function redactDeep(value) {
32193
+ if (value instanceof PublicHash) return value.value;
32181
32194
  if (typeof value === "string") return redact(value);
32182
32195
  if (Array.isArray(value)) return value.map(redactDeep);
32183
32196
  if (value && typeof value === "object") {
@@ -33988,6 +34001,82 @@ function broadcastOutcome({ receiptStatus, executionError = null, receiptSeen =
33988
34001
  };
33989
34002
  }
33990
34003
 
34004
+ // scripts/src/lib/gas.mjs
34005
+ var EVM_FEE_COIN = Object.freeze({
34006
+ 1: { symbol: "ETH", network: "Ethereum" },
34007
+ 56: { symbol: "BNB", network: "BNB Chain" },
34008
+ 137: { symbol: "POL", network: "Polygon" },
34009
+ 8453: { symbol: "ETH", network: "Base" }
34010
+ });
34011
+ function formatAtomic(atomic, decimals) {
34012
+ const neg = atomic < 0n;
34013
+ const v = neg ? -atomic : atomic;
34014
+ const base = 10n ** BigInt(decimals);
34015
+ const whole = v / base;
34016
+ const frac = (v % base).toString().padStart(decimals, "0").replace(/0+$/, "");
34017
+ return `${neg ? "-" : ""}${whole}${frac ? `.${frac}` : ""}`;
34018
+ }
34019
+ function assessFee({ balance, required, decimals, symbol, network }) {
34020
+ const ok = required === null ? balance > 0n : balance >= required;
34021
+ const details = {
34022
+ network,
34023
+ feeCoin: symbol,
34024
+ balance: formatAtomic(balance, decimals),
34025
+ required: required === null ? null : formatAtomic(required, decimals),
34026
+ shortfall: required === null || ok ? null : formatAtomic(required - balance, decimals)
34027
+ };
34028
+ return { ok, details };
34029
+ }
34030
+ function insufficient(details, extra) {
34031
+ const need = details.required ? `about ${details.required} ${details.feeCoin}` : `some ${details.feeCoin}`;
34032
+ return new SkillError(
34033
+ "INSUFFICIENT_GAS",
34034
+ `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}` : ""),
34035
+ details
34036
+ );
34037
+ }
34038
+ function unavailable(network, err) {
34039
+ return new SkillError(
34040
+ "GAS_CHECK_UNAVAILABLE",
34041
+ `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.`,
34042
+ { network }
34043
+ );
34044
+ }
34045
+ var LAMPORTS_DECIMALS = 9;
34046
+ async function solFeePrecheck({ connection, payer, message }) {
34047
+ const network = "Solana";
34048
+ let balance;
34049
+ let fee;
34050
+ let rentMin;
34051
+ try {
34052
+ balance = BigInt(await connection.getBalance(payer, "confirmed"));
34053
+ const res = await connection.getFeeForMessage(message, "confirmed");
34054
+ if (res?.value === null || res?.value === void 0) {
34055
+ throw new Error("the node returned no fee for this transaction (stale blockhash?)");
34056
+ }
34057
+ fee = BigInt(res.value);
34058
+ rentMin = BigInt(await connection.getMinimumBalanceForRentExemption(0));
34059
+ } catch (err) {
34060
+ if (balance === 0n) {
34061
+ throw insufficient(
34062
+ assessFee({ balance, required: null, decimals: LAMPORTS_DECIMALS, symbol: "SOL", network }).details
34063
+ );
34064
+ }
34065
+ throw unavailable(network, err);
34066
+ }
34067
+ const remaining = balance - fee;
34068
+ const required = remaining === 0n ? fee : fee + rentMin;
34069
+ const { ok, details } = assessFee({ balance, required, decimals: LAMPORTS_DECIMALS, symbol: "SOL", network });
34070
+ const full = { ...details, fee: formatAtomic(fee, LAMPORTS_DECIMALS), rentExemptMinimum: formatAtomic(rentMin, LAMPORTS_DECIMALS) };
34071
+ if (!ok) {
34072
+ throw insufficient(
34073
+ full,
34074
+ balance >= fee ? "Solana also requires a wallet to keep a small minimum balance after paying the fee." : null
34075
+ );
34076
+ }
34077
+ return full;
34078
+ }
34079
+
33991
34080
  // scripts/src/send-sol.mjs
33992
34081
  var import_web32 = __toESM(require_index_cjs(), 1);
33993
34082
 
@@ -34152,6 +34241,34 @@ async function main(argv) {
34152
34241
  );
34153
34242
  }
34154
34243
  }
34244
+ const tx = new import_web32.Transaction();
34245
+ tx.add(
34246
+ createTransferCheckedInstruction(
34247
+ fromAta,
34248
+ mint,
34249
+ toAta,
34250
+ keypair.publicKey,
34251
+ amountAtomic,
34252
+ mintInfo.decimals
34253
+ )
34254
+ );
34255
+ if (source.receiverMemo) {
34256
+ tx.add(
34257
+ new import_web32.TransactionInstruction({
34258
+ keys: [],
34259
+ programId: MEMO_PROGRAM_ID,
34260
+ data: Buffer.from(String(source.receiverMemo), "utf8")
34261
+ })
34262
+ );
34263
+ }
34264
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
34265
+ tx.recentBlockhash = blockhash;
34266
+ tx.feePayer = keypair.publicKey;
34267
+ const networkFee = await solFeePrecheck({
34268
+ connection,
34269
+ payer: keypair.publicKey,
34270
+ message: tx.compileMessage()
34271
+ });
34155
34272
  if (dryRun) {
34156
34273
  emit({
34157
34274
  success: true,
@@ -34168,6 +34285,7 @@ async function main(argv) {
34168
34285
  toIsTokenAccount,
34169
34286
  withMemo: Boolean(source.receiverMemo)
34170
34287
  },
34288
+ networkFee,
34171
34289
  confirmedAt: state.confirmation?.confirmedAt ?? null,
34172
34290
  keySource,
34173
34291
  // Key NAMES only — a value from a .env is never echoed.
@@ -34176,29 +34294,6 @@ async function main(argv) {
34176
34294
  note: "Nothing was signed or broadcast. Add --send (without --dry-run) to execute."
34177
34295
  });
34178
34296
  }
34179
- const tx = new import_web32.Transaction();
34180
- tx.add(
34181
- createTransferCheckedInstruction(
34182
- fromAta,
34183
- mint,
34184
- toAta,
34185
- keypair.publicKey,
34186
- amountAtomic,
34187
- mintInfo.decimals
34188
- )
34189
- );
34190
- if (source.receiverMemo) {
34191
- tx.add(
34192
- new import_web32.TransactionInstruction({
34193
- keys: [],
34194
- programId: MEMO_PROGRAM_ID,
34195
- data: Buffer.from(String(source.receiverMemo), "utf8")
34196
- })
34197
- );
34198
- }
34199
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
34200
- tx.recentBlockhash = blockhash;
34201
- tx.feePayer = keypair.publicKey;
34202
34297
  tx.sign(keypair);
34203
34298
  const signature = tx.signatures[0]?.signature ? base58Encode(tx.signatures[0].signature) : null;
34204
34299
  await finalPayabilityCheck({
@@ -34250,7 +34345,7 @@ async function main(argv) {
34250
34345
  code: landed ? "BROADCAST_AMBIGUOUS" : "BROADCAST_FAILED",
34251
34346
  message: redact(err?.message || "broadcast failed")
34252
34347
  },
34253
- signature,
34348
+ signature: publicHash(signature),
34254
34349
  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."
34255
34350
  },
34256
34351
  EXIT_ERROR
@@ -34287,7 +34382,7 @@ async function main(argv) {
34287
34382
  confirmed: false,
34288
34383
  rozoPaymentId,
34289
34384
  linkId: state.linkId,
34290
- txHash: sent,
34385
+ txHash: publicHash(sent),
34291
34386
  error: {
34292
34387
  code: broadcastOutcome({ executionError }).code,
34293
34388
  message: `The transaction landed but failed on chain: ${redact(JSON.stringify(executionError))}`
@@ -34306,7 +34401,7 @@ async function main(argv) {
34306
34401
  confirmed: false,
34307
34402
  rozoPaymentId,
34308
34403
  linkId: state.linkId,
34309
- txHash: sent,
34404
+ txHash: publicHash(sent),
34310
34405
  guidance: "Broadcast but not confirmed within the wait window. Do NOT resend. Poll status.js; receipt truth is the backend confirmedAt/amountReceived."
34311
34406
  },
34312
34407
  EXIT_UNCONFIRMED
@@ -34320,7 +34415,7 @@ async function main(argv) {
34320
34415
  confirmed: true,
34321
34416
  rozoPaymentId,
34322
34417
  linkId: state.linkId,
34323
- txHash: sent,
34418
+ txHash: publicHash(sent),
34324
34419
  sent: {
34325
34420
  chain: chainName(900),
34326
34421
  tokenSymbol: source.tokenSymbol,