@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/send-evm.js
CHANGED
|
@@ -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
|
|
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 <
|
|
18483
|
-
const { abi: abi2, address, args, functionName } =
|
|
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 } =
|
|
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 !==
|
|
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: {
|
package/scripts/dist/send-sol.js
CHANGED
|
@@ -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,
|