@candledottv/cli 0.13.18 → 0.13.19
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 +11 -3
- package/dist/index.js +871 -237
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -532,7 +532,7 @@ var init_terminal_theme = __esm(() => {
|
|
|
532
532
|
});
|
|
533
533
|
|
|
534
534
|
// src/version.ts
|
|
535
|
-
var CLI_VERSION = "0.13.
|
|
535
|
+
var CLI_VERSION = "0.13.19";
|
|
536
536
|
|
|
537
537
|
// src/presentation.ts
|
|
538
538
|
function terminalStyle(deps, stream, machine = false) {
|
|
@@ -10551,8 +10551,8 @@ function createSolanaRpc(url, fetchFn, sleep) {
|
|
|
10551
10551
|
const r = await read("simulateTransaction", [
|
|
10552
10552
|
txBase64,
|
|
10553
10553
|
withMinContextSlot({
|
|
10554
|
-
sigVerify: false,
|
|
10555
|
-
replaceRecentBlockhash:
|
|
10554
|
+
sigVerify: opts?.sigVerify ?? false,
|
|
10555
|
+
replaceRecentBlockhash: !opts?.sigVerify,
|
|
10556
10556
|
commitment: "finalized",
|
|
10557
10557
|
encoding: "base64",
|
|
10558
10558
|
accounts: { encoding: "base64", addresses }
|
|
@@ -10686,6 +10686,7 @@ function withContextFloor(rpc, floor) {
|
|
|
10686
10686
|
const finalized = (opts) => {
|
|
10687
10687
|
const base = required(opts);
|
|
10688
10688
|
return {
|
|
10689
|
+
...opts?.sigVerify !== undefined ? { sigVerify: opts.sigVerify } : {},
|
|
10689
10690
|
get minContextSlot() {
|
|
10690
10691
|
return base.minContextSlot;
|
|
10691
10692
|
},
|
|
@@ -12448,6 +12449,7 @@ var init_errors2 = __esm(() => {
|
|
|
12448
12449
|
"VAULT_FIELD_UNKNOWN",
|
|
12449
12450
|
"VAULT_KDF_OUT_OF_BOUNDS",
|
|
12450
12451
|
"VAULT_UNLOCK_FAILED",
|
|
12452
|
+
"VAULT_TERMINAL_REQUIRED",
|
|
12451
12453
|
"VAULT_BLOB_TAMPERED",
|
|
12452
12454
|
"VAULT_INDEX_INVALID",
|
|
12453
12455
|
"VAULT_OLDER_COPY",
|
|
@@ -15328,24 +15330,44 @@ function teeWalletList(rows) {
|
|
|
15328
15330
|
return rows.map(describeWallet).join("; ");
|
|
15329
15331
|
}
|
|
15330
15332
|
async function completeTradingWallet(ctx, row, appId, scope, chain2 = "solana", key) {
|
|
15333
|
+
const inspected = await inspectTradingWallet(ctx, row, appId, scope, chain2, key, true);
|
|
15334
|
+
if (inspected.failures[0])
|
|
15335
|
+
throw inspected.failures[0];
|
|
15336
|
+
return inspected.row;
|
|
15337
|
+
}
|
|
15338
|
+
async function inspectTradingWallet(ctx, row, appId, scope, chain2 = "solana", key, firstOnly = false) {
|
|
15339
|
+
const failures = [];
|
|
15331
15340
|
if (rowChain(row) !== chain2)
|
|
15332
|
-
|
|
15341
|
+
failures.push(chainMismatch(`TEE wallet ${describeWallet(row)}`, rowChain(row), chain2));
|
|
15342
|
+
if (firstOnly && failures.length)
|
|
15343
|
+
return { failures };
|
|
15333
15344
|
if (!row.active)
|
|
15334
|
-
|
|
15345
|
+
failures.push(new TradingError("TEE_WALLET_INACTIVE", `The payer must be a verified-active ${chainName(chain2)} TEE wallet.`));
|
|
15346
|
+
if (firstOnly && failures.length)
|
|
15347
|
+
return { failures };
|
|
15335
15348
|
if (scope === "launch:write" && row.allowLaunch !== true)
|
|
15336
|
-
|
|
15349
|
+
failures.push(new TradingError("LAUNCH_NOT_ALLOWED", `The account owner must turn on allowLaunch for wallet ${row.id}: run candle wallets allow-launch ${row.id} on a profile signed in with candle auth login. An agent key cannot.`));
|
|
15350
|
+
if (firstOnly && failures.length)
|
|
15351
|
+
return { failures };
|
|
15337
15352
|
if (!appId || !row.privyWalletId)
|
|
15338
|
-
|
|
15353
|
+
failures.push(new TradingError("SIGNER_UNAVAILABLE", "The server did not return its relay public identifiers."));
|
|
15354
|
+
if (firstOnly && failures.length)
|
|
15355
|
+
return { failures };
|
|
15339
15356
|
const signer = await localSignerFor(ctx.deps, row.id, row.signerQuorumId);
|
|
15340
15357
|
if (!signer)
|
|
15341
|
-
|
|
15358
|
+
failures.push(new TradingError("SIGNER_UNAVAILABLE", await signerUnavailableMessage(ctx, row, key)));
|
|
15359
|
+
if (failures.length || !signer || !row.privyWalletId)
|
|
15360
|
+
return { failures };
|
|
15342
15361
|
return {
|
|
15343
|
-
|
|
15344
|
-
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15362
|
+
failures,
|
|
15363
|
+
row: {
|
|
15364
|
+
id: row.id,
|
|
15365
|
+
address: row.address,
|
|
15366
|
+
privyWalletId: row.privyWalletId,
|
|
15367
|
+
appId,
|
|
15368
|
+
signer: signer.pem,
|
|
15369
|
+
chain: row.chain === "evm" ? "evm" : "solana"
|
|
15370
|
+
}
|
|
15349
15371
|
};
|
|
15350
15372
|
}
|
|
15351
15373
|
async function signerUnavailableMessage(ctx, row, key) {
|
|
@@ -15378,7 +15400,18 @@ async function tradingWallet(ctx, key, name, scope, chain2 = "solana") {
|
|
|
15378
15400
|
return { ...wallet, scopes };
|
|
15379
15401
|
}
|
|
15380
15402
|
async function tradingPayer(ctx, key, name, scope = "swap:write", chain2 = "solana") {
|
|
15381
|
-
const
|
|
15403
|
+
const listing = await listTradingWallets(ctx, key, scope);
|
|
15404
|
+
const selected = await selectTradingPayer(ctx, key, name, listing, chain2);
|
|
15405
|
+
if (selected.kind === "embedded")
|
|
15406
|
+
return selected;
|
|
15407
|
+
const wallet = await completeTradingWallet(ctx, selected.row, listing.appId, scope, chain2, {
|
|
15408
|
+
apiKey: key,
|
|
15409
|
+
keyPrefix: listing.keyPrefix
|
|
15410
|
+
});
|
|
15411
|
+
return { kind: "tee", wallet, scopes: listing.scopes };
|
|
15412
|
+
}
|
|
15413
|
+
async function selectTradingPayer(ctx, key, name, listing, chain2 = "solana") {
|
|
15414
|
+
const { rows, scopes, keyPrefix } = listing;
|
|
15382
15415
|
const discovered = embeddedSchema.parse(await request(ctx, key, "/api/v1/agent/wallets/embedded"));
|
|
15383
15416
|
const wallets = discovered.wallets;
|
|
15384
15417
|
const embeddedOn = {
|
|
@@ -15397,14 +15430,7 @@ async function tradingPayer(ctx, key, name, scope = "swap:write", chain2 = "sola
|
|
|
15397
15430
|
].join("; ");
|
|
15398
15431
|
if (name === undefined) {
|
|
15399
15432
|
if (onChain.length === 1 && !embedded)
|
|
15400
|
-
return {
|
|
15401
|
-
kind: "tee",
|
|
15402
|
-
wallet: await completeTradingWallet(ctx, onChain[0], appId, scope, chain2, {
|
|
15403
|
-
apiKey: key,
|
|
15404
|
-
keyPrefix
|
|
15405
|
-
}),
|
|
15406
|
-
scopes
|
|
15407
|
-
};
|
|
15433
|
+
return { kind: "tee", row: onChain[0] };
|
|
15408
15434
|
if (onChain.length === 0 && embedded)
|
|
15409
15435
|
return asEmbedded();
|
|
15410
15436
|
if (onChain.length === 0 && deniedAddress !== undefined)
|
|
@@ -15413,14 +15439,7 @@ async function tradingPayer(ctx, key, name, scope = "swap:write", chain2 = "sola
|
|
|
15413
15439
|
}
|
|
15414
15440
|
const matches = rows.filter((row) => matchesName(row, name));
|
|
15415
15441
|
if (matches.length === 1)
|
|
15416
|
-
return {
|
|
15417
|
-
kind: "tee",
|
|
15418
|
-
wallet: await completeTradingWallet(ctx, matches[0], appId, scope, chain2, {
|
|
15419
|
-
apiKey: key,
|
|
15420
|
-
keyPrefix
|
|
15421
|
-
}),
|
|
15422
|
-
scopes
|
|
15423
|
-
};
|
|
15442
|
+
return { kind: "tee", row: matches[0] };
|
|
15424
15443
|
if (matches.length === 0) {
|
|
15425
15444
|
if (embedded !== undefined && sameAddress(embedded, name))
|
|
15426
15445
|
return asEmbedded();
|
|
@@ -16949,8 +16968,431 @@ var init_solana_verify = __esm(() => {
|
|
|
16949
16968
|
]);
|
|
16950
16969
|
});
|
|
16951
16970
|
|
|
16952
|
-
// src/
|
|
16971
|
+
// src/trading-checks.ts
|
|
16953
16972
|
import { randomUUID } from "node:crypto";
|
|
16973
|
+
function failure2(id, error) {
|
|
16974
|
+
if (error instanceof TradingUsage)
|
|
16975
|
+
throw error;
|
|
16976
|
+
return {
|
|
16977
|
+
id,
|
|
16978
|
+
status: "fail",
|
|
16979
|
+
code: error instanceof TradingError ? error.code : "INVALID_RESPONSE",
|
|
16980
|
+
message: error instanceof Error ? describeRpcFailure(error) : "The check could not complete.",
|
|
16981
|
+
...error instanceof TradingError && error.suggestion ? { suggestion: error.suggestion } : {}
|
|
16982
|
+
};
|
|
16983
|
+
}
|
|
16984
|
+
function balanceProblem(value) {
|
|
16985
|
+
return /InsufficientFunds(?:ForFee)?|AccountNotFound|insufficient[_ ](?:funds|balance|lamports)/i.test(value instanceof Error ? `${value instanceof TradingError ? value.code : ""} ${value.message}` : String(value));
|
|
16986
|
+
}
|
|
16987
|
+
function needsBalance(id) {
|
|
16988
|
+
return {
|
|
16989
|
+
id,
|
|
16990
|
+
status: "skipped",
|
|
16991
|
+
needs: "balance",
|
|
16992
|
+
message: "This check needs balance; fund the wallet and check again."
|
|
16993
|
+
};
|
|
16994
|
+
}
|
|
16995
|
+
async function diagnosticListing(ctx, key) {
|
|
16996
|
+
try {
|
|
16997
|
+
return await listTradingWallets(ctx, key);
|
|
16998
|
+
} catch (error) {
|
|
16999
|
+
if (error instanceof exports_external.ZodError)
|
|
17000
|
+
throw new TradingError("INVALID_RESPONSE", "Trading wallet discovery returned a malformed page.");
|
|
17001
|
+
throw error;
|
|
17002
|
+
}
|
|
17003
|
+
}
|
|
17004
|
+
async function readWalletRecords(ctx, key) {
|
|
17005
|
+
const rows = [];
|
|
17006
|
+
const seen = new Set;
|
|
17007
|
+
let cursor;
|
|
17008
|
+
for (let page = 0;page < 25; page++) {
|
|
17009
|
+
const result = exports_external.object({
|
|
17010
|
+
page: exports_external.array(linkedRow),
|
|
17011
|
+
isDone: exports_external.boolean(),
|
|
17012
|
+
continueCursor: exports_external.string().nullable().optional()
|
|
17013
|
+
}).safeParse(await request(ctx, key, `/api/v1/agent/wallets?limit=100${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`));
|
|
17014
|
+
if (!result.success)
|
|
17015
|
+
throw new TradingError("INVALID_RESPONSE", "Wallet records did not return a valid page.");
|
|
17016
|
+
rows.push(...result.data.page);
|
|
17017
|
+
if (result.data.isDone)
|
|
17018
|
+
return rows;
|
|
17019
|
+
cursor = result.data.continueCursor ?? undefined;
|
|
17020
|
+
if (!cursor || seen.has(cursor))
|
|
17021
|
+
break;
|
|
17022
|
+
seen.add(cursor);
|
|
17023
|
+
}
|
|
17024
|
+
throw new TradingError("INVALID_RESPONSE", "Wallet records did not complete.");
|
|
17025
|
+
}
|
|
17026
|
+
function walletRecordCheck(row, trading, records) {
|
|
17027
|
+
const found = records.filter((record) => record.address === row.address);
|
|
17028
|
+
const differences = {};
|
|
17029
|
+
const other = found[0];
|
|
17030
|
+
const count = trading.filter((record) => record.address === row.address).length;
|
|
17031
|
+
if (count !== 1 || found.length !== 1)
|
|
17032
|
+
differences.addressCount = { trading: count, wallets: found.length };
|
|
17033
|
+
for (const [left, right] of [
|
|
17034
|
+
["id", "_id"],
|
|
17035
|
+
["signerQuorumId", "signerQuorumId"],
|
|
17036
|
+
["chain", "chain"],
|
|
17037
|
+
["label", "label"],
|
|
17038
|
+
["address", "address"]
|
|
17039
|
+
]) {
|
|
17040
|
+
if (!other || row[left] !== other[right])
|
|
17041
|
+
differences[left] = { trading: row[left] ?? null, wallets: other?.[right] ?? null };
|
|
17042
|
+
}
|
|
17043
|
+
if (!other || !["ember-tee", "ember-hot"].includes(other.profile ?? ""))
|
|
17044
|
+
differences.profile = { trading: "TEE", wallets: other?.profile ?? null };
|
|
17045
|
+
if (other?.revokedAt !== undefined)
|
|
17046
|
+
differences.revokedAt = { trading: null, wallets: other.revokedAt };
|
|
17047
|
+
return Object.keys(differences).length ? {
|
|
17048
|
+
id: "record",
|
|
17049
|
+
status: "fail",
|
|
17050
|
+
code: "WALLET_RECORD_MISMATCH",
|
|
17051
|
+
message: `Trading and wallet records differ: ${JSON.stringify(differences)}.`,
|
|
17052
|
+
differences
|
|
17053
|
+
} : pass("record");
|
|
17054
|
+
}
|
|
17055
|
+
async function inspectReport(ctx, key, row, listing, records, payable, name) {
|
|
17056
|
+
const checks = [pass("key"), payable ? failure2("payable", payable) : pass("payable")];
|
|
17057
|
+
checks.push(listing.scopes.includes("swap:write") ? pass("scope") : failure2("scope", new TradingError("SCOPE_MISSING", "The bound key needs swap:write.")));
|
|
17058
|
+
const report = {
|
|
17059
|
+
...row ? { id: row.id, address: row.address, label: row.label } : { name },
|
|
17060
|
+
ok: false,
|
|
17061
|
+
checks
|
|
17062
|
+
};
|
|
17063
|
+
if (!row) {
|
|
17064
|
+
checks.push(...["record", "active", "signer"].map((id) => skipped(id, "payable")));
|
|
17065
|
+
return { report };
|
|
17066
|
+
}
|
|
17067
|
+
checks.push(walletRecordCheck(row, listing.rows, records));
|
|
17068
|
+
const inspected = await inspectTradingWallet(ctx, row, listing.appId, "swap:write", "solana", {
|
|
17069
|
+
apiKey: key,
|
|
17070
|
+
keyPrefix: listing.keyPrefix
|
|
17071
|
+
});
|
|
17072
|
+
const active = inspected.failures.filter((error) => error.code !== "SIGNER_UNAVAILABLE");
|
|
17073
|
+
checks.push(...active.length ? active.map((error) => failure2("active", error)) : [pass("active")]);
|
|
17074
|
+
const signer = inspected.failures.find((error) => error.code === "SIGNER_UNAVAILABLE");
|
|
17075
|
+
checks.push(signer ? failure2("signer", signer) : pass("signer"));
|
|
17076
|
+
return { report, wallet: inspected.row };
|
|
17077
|
+
}
|
|
17078
|
+
function printWalletReports(ctx, reports, swap = false) {
|
|
17079
|
+
for (const report of reports)
|
|
17080
|
+
report.ok = !report.checks.some((check) => check.status === "fail");
|
|
17081
|
+
const failed = reports.filter((report) => !report.ok).length;
|
|
17082
|
+
const summary = { total: reports.length, pass: reports.length - failed, fail: failed };
|
|
17083
|
+
if (ctx.json) {
|
|
17084
|
+
ctx.deps.stdout.write(`${JSON.stringify({
|
|
17085
|
+
success: failed === 0,
|
|
17086
|
+
dryRun: true,
|
|
17087
|
+
wallets: reports,
|
|
17088
|
+
summary,
|
|
17089
|
+
...swap ? { checks: reports[0]?.checks, quote: reports[0]?.quote } : {}
|
|
17090
|
+
})}
|
|
17091
|
+
`);
|
|
17092
|
+
} else {
|
|
17093
|
+
for (const report of reports) {
|
|
17094
|
+
const first = report.checks.find((check) => check.status === "fail");
|
|
17095
|
+
ctx.deps.stdout.write(`${safeText(report.label ?? report.name ?? report.id ?? "wallet")} ${safeText(report.address ?? "")} ${report.ok ? "PASS" : "FAIL"}${first ? ` ${first.id}/${first.code}` : ""}
|
|
17096
|
+
`);
|
|
17097
|
+
if (reports.length === 1) {
|
|
17098
|
+
for (const check of report.checks)
|
|
17099
|
+
ctx.deps.stdout.write(` ${check.id} ${check.status}${check.code ? ` ${check.code}` : ""}${check.message ? ` ${safeText(check.message)}` : ""}${check.because ? ` (${check.because})` : ""}
|
|
17100
|
+
`);
|
|
17101
|
+
if (report.quote)
|
|
17102
|
+
ctx.deps.stdout.write(` quote ${safeText(JSON.stringify(report.quote))}
|
|
17103
|
+
`);
|
|
17104
|
+
}
|
|
17105
|
+
}
|
|
17106
|
+
if (reports.length > 1)
|
|
17107
|
+
ctx.deps.stdout.write(`${summary.pass} pass, ${summary.fail} fail
|
|
17108
|
+
`);
|
|
17109
|
+
}
|
|
17110
|
+
return failed ? 1 : 0;
|
|
17111
|
+
}
|
|
17112
|
+
async function buildCheck(ctx, key, wallet, report, from, to, amount, slippage, rpcUrl, sign3) {
|
|
17113
|
+
const checks = report.checks;
|
|
17114
|
+
const blocked = checks.find((check) => check.status === "fail");
|
|
17115
|
+
if (blocked) {
|
|
17116
|
+
checks.push(skipped("quote", blocked.id), skipped("spend", "quote"));
|
|
17117
|
+
if (sign3)
|
|
17118
|
+
checks.push(skipped("sign", "spend"));
|
|
17119
|
+
return;
|
|
17120
|
+
}
|
|
17121
|
+
const solana = lazySolanaClient(ctx, rpcUrl);
|
|
17122
|
+
let transaction;
|
|
17123
|
+
let quote;
|
|
17124
|
+
let amountRaw;
|
|
17125
|
+
let minimumRaw;
|
|
17126
|
+
let feeRaw;
|
|
17127
|
+
const fromBase = baseAsset(from);
|
|
17128
|
+
const toBase = baseAsset(to);
|
|
17129
|
+
const kind = fromBase && toBase ? "swap" : "trade";
|
|
17130
|
+
try {
|
|
17131
|
+
const decimals = await decimalsFor(ctx, from, solana);
|
|
17132
|
+
const outDecimals = await decimalsFor(ctx, to, solana);
|
|
17133
|
+
amountRaw = rawAmount(amount, decimals);
|
|
17134
|
+
if (BigInt(amountRaw) > BigInt(Number.MAX_SAFE_INTEGER))
|
|
17135
|
+
throw new TradingError("INVALID_AMOUNT", "Amount exceeds the venue's exact integer range.");
|
|
17136
|
+
const payer = { type: "linked", linkedWalletId: wallet.id };
|
|
17137
|
+
const clientTradeId = `swap-${randomUUID()}`;
|
|
17138
|
+
const built = kind === "swap" ? await request(ctx, key, "/api/v1/agent/swap/build", {
|
|
17139
|
+
clientTradeId,
|
|
17140
|
+
from,
|
|
17141
|
+
to,
|
|
17142
|
+
amountRaw,
|
|
17143
|
+
maxSlippageBps: slippage,
|
|
17144
|
+
payer
|
|
17145
|
+
}) : await request(ctx, key, "/api/v1/trade/agent/build", {
|
|
17146
|
+
clientTradeId,
|
|
17147
|
+
chain: "solana",
|
|
17148
|
+
mint: fromBase ? to : from,
|
|
17149
|
+
side: fromBase ? "buy" : "sell",
|
|
17150
|
+
quoteAsset: (fromBase ?? toBase)?.toLowerCase(),
|
|
17151
|
+
amountRaw,
|
|
17152
|
+
maxSlippageBps: slippage,
|
|
17153
|
+
payer
|
|
17154
|
+
});
|
|
17155
|
+
const parsed = swapBuildSchema.safeParse(kind === "swap" ? built.payload : built);
|
|
17156
|
+
if (!parsed.success || built.job || built.status === "executed")
|
|
17157
|
+
throw new TradingError("INVALID_RESPONSE", "Candle did not return a complete built quote.");
|
|
17158
|
+
const data = parsed.data;
|
|
17159
|
+
if (kind === "swap" && (data.venue !== "jupiter" || data.recipient !== wallet.address))
|
|
17160
|
+
throw new TradingError("INVALID_RESPONSE", "A base swap must use Jupiter and return to its payer.");
|
|
17161
|
+
if (kind === "trade" && (built.chain !== "solana" || built.walletAddress !== wallet.address))
|
|
17162
|
+
throw new TradingError("INVALID_RESPONSE", "The token build does not name the requested Solana payer.");
|
|
17163
|
+
const artifacts = kind === "swap" ? data : data.artifacts;
|
|
17164
|
+
if (!artifacts)
|
|
17165
|
+
throw new TradingError("INVALID_RESPONSE", "Missing quote artifacts.");
|
|
17166
|
+
if (kind === "trade" && data.artifacts?.quoteAsset !== (fromBase ?? toBase)?.toLowerCase())
|
|
17167
|
+
throw new TradingError("PAIR_UNSUPPORTED", "The token settles in a different quote asset.");
|
|
17168
|
+
if (data.expiresAt <= ctx.deps.now())
|
|
17169
|
+
throw new TradingError("QUOTE_EXPIRED", "The quote expired; build a fresh check.");
|
|
17170
|
+
minimumRaw = !fromBase && artifacts.venue === "curve" ? (BigInt(data.minOutRaw) > BigInt(data.fee.feeRaw) ? BigInt(data.minOutRaw) - BigInt(data.fee.feeRaw) : 0n).toString() : data.minOutRaw;
|
|
17171
|
+
quote = {
|
|
17172
|
+
venue: (kind === "trade" ? data.artifacts?.quoteSource : undefined) ?? artifacts.venue,
|
|
17173
|
+
priceImpactPct: artifacts.priceImpactPct ?? null,
|
|
17174
|
+
fee: data.fee,
|
|
17175
|
+
minimumReceived: `${decimalAmount(minimumRaw, outDecimals)} ${to}`,
|
|
17176
|
+
minOutRaw: data.minOutRaw,
|
|
17177
|
+
minimumReceivedRaw: minimumRaw,
|
|
17178
|
+
tokenRisks: artifacts.tokenRisks ?? []
|
|
17179
|
+
};
|
|
17180
|
+
const tx = kind === "swap" ? data.transactionsBase64?.[0] : data.artifacts?.transactionBase64;
|
|
17181
|
+
if (!tx || kind === "swap" && data.transactionsBase64?.length !== 1)
|
|
17182
|
+
throw new TradingError("INVALID_RESPONSE", "A TEE swap must contain exactly one same-chain transaction.");
|
|
17183
|
+
transaction = tx;
|
|
17184
|
+
feeRaw = BigInt(data.fee.feeRaw);
|
|
17185
|
+
report.quote = quote;
|
|
17186
|
+
checks.push(pass("quote"));
|
|
17187
|
+
} catch (error) {
|
|
17188
|
+
checks.push(balanceProblem(error) ? needsBalance("quote") : failure2("quote", error), skipped("spend", "quote"));
|
|
17189
|
+
if (sign3)
|
|
17190
|
+
checks.push(skipped("sign", "spend"));
|
|
17191
|
+
return;
|
|
17192
|
+
}
|
|
17193
|
+
const assetOf2 = (asset) => asset === "SOL" ? "SOL" : BASES[asset]?.mint ?? asset;
|
|
17194
|
+
let reader;
|
|
17195
|
+
try {
|
|
17196
|
+
reader = await solana();
|
|
17197
|
+
const problem = await tradingRead(ctx, reader, () => solanaSpendProblem(transaction, wallet.address, reader.rpc, {
|
|
17198
|
+
kind: "swap",
|
|
17199
|
+
spend: assetOf2(from),
|
|
17200
|
+
maxSpendRaw: BigInt(amountRaw) + (kind === "trade" && fromBase ? feeRaw : 0n),
|
|
17201
|
+
receive: { asset: assetOf2(to), minRaw: BigInt(minimumRaw) }
|
|
17202
|
+
}));
|
|
17203
|
+
if (problem) {
|
|
17204
|
+
checks.push(balanceProblem(problem) ? needsBalance("spend") : failure2("spend", new TradingError("INVALID_RESPONSE", `The transaction Candle built does not match the quote (${safeText(problem)}); nothing was signed.`)));
|
|
17205
|
+
if (sign3)
|
|
17206
|
+
checks.push(skipped("sign", "spend"));
|
|
17207
|
+
return;
|
|
17208
|
+
}
|
|
17209
|
+
checks.push(pass("spend"));
|
|
17210
|
+
} catch (error) {
|
|
17211
|
+
checks.push(balanceProblem(error) ? needsBalance("spend") : failure2("spend", error));
|
|
17212
|
+
if (sign3)
|
|
17213
|
+
checks.push(skipped("sign", "spend"));
|
|
17214
|
+
return;
|
|
17215
|
+
}
|
|
17216
|
+
if (!sign3)
|
|
17217
|
+
return;
|
|
17218
|
+
try {
|
|
17219
|
+
const signed = await relaySign(ctx, key, wallet, transaction);
|
|
17220
|
+
const decoded = decodeTransaction(decodeStrictBase64(signed));
|
|
17221
|
+
const original = decodeTransaction(decodeStrictBase64(transaction));
|
|
17222
|
+
if (decoded.message.staticKeys[0] !== wallet.address || !Buffer.from(decoded.message.bytes).equals(Buffer.from(original.message.bytes)) || !ed25519.verify(decoded.signatures[0], decoded.message.bytes, base58.decode(wallet.address)))
|
|
17223
|
+
throw new TradingError("SIGNATURE_INVALID", "The relay did not return the payer's signature over the built message.");
|
|
17224
|
+
checks.push(pass("sign"));
|
|
17225
|
+
try {
|
|
17226
|
+
const simulation = await tradingRead(ctx, reader, () => reader.rpc.simulateTransaction(signed, [], { sigVerify: true }));
|
|
17227
|
+
const problem = simulation.err == null ? undefined : JSON.stringify(simulation.err);
|
|
17228
|
+
if (!problem)
|
|
17229
|
+
checks.push(pass("simulation"));
|
|
17230
|
+
else if (balanceProblem(problem))
|
|
17231
|
+
checks.push(needsBalance("simulation"));
|
|
17232
|
+
else if (/BlockhashNotFound|blockhash not found|blockhash.*expir|block.?height.*exceed/i.test(problem))
|
|
17233
|
+
checks.push({ id: "simulation", status: "skipped", message: "The blockhash expired; run a fresh check." });
|
|
17234
|
+
else
|
|
17235
|
+
checks.push({
|
|
17236
|
+
...failure2("simulation", new TradingError("SIGN_FAILED", "Signed simulation failed.")),
|
|
17237
|
+
cause: problem
|
|
17238
|
+
});
|
|
17239
|
+
} catch (error) {
|
|
17240
|
+
const problem = error instanceof Error ? error.message : String(error);
|
|
17241
|
+
if (balanceProblem(problem))
|
|
17242
|
+
checks.push(needsBalance("simulation"));
|
|
17243
|
+
else if (/BlockhashNotFound|blockhash not found|blockhash.*expir|block.?height.*exceed/i.test(problem))
|
|
17244
|
+
checks.push({ id: "simulation", status: "skipped", message: "The blockhash expired; run a fresh check." });
|
|
17245
|
+
else if (error instanceof TradingError && error.code === "RPC_RATE_LIMITED")
|
|
17246
|
+
checks.push(failure2("simulation", error));
|
|
17247
|
+
else
|
|
17248
|
+
checks.push({
|
|
17249
|
+
...failure2("simulation", new TradingError("SIGN_FAILED", "Signed simulation could not complete.")),
|
|
17250
|
+
cause: error instanceof TradingError ? error.code : "RPC_FAILED"
|
|
17251
|
+
});
|
|
17252
|
+
}
|
|
17253
|
+
} catch (error) {
|
|
17254
|
+
checks.push({
|
|
17255
|
+
...failure2("sign", new TradingError("SIGN_FAILED", "The signer did not produce a verified signature.")),
|
|
17256
|
+
cause: error instanceof TradingError ? error.code : "INVALID_RESPONSE"
|
|
17257
|
+
});
|
|
17258
|
+
}
|
|
17259
|
+
}
|
|
17260
|
+
async function teeCheck(args, ctx) {
|
|
17261
|
+
const parsed = parseArgs(args, { valueFlags: ["--label-prefix", "--max", "--rpc-url"], booleanFlags: ["--sign"] });
|
|
17262
|
+
if ("error" in parsed) {
|
|
17263
|
+
writeUsageFailure(ctx.deps, parsed.error, ctx.json);
|
|
17264
|
+
return 2;
|
|
17265
|
+
}
|
|
17266
|
+
const max = Number(parsed.values["--max"] ?? "10");
|
|
17267
|
+
if (!parsed.positionals.length && parsed.values["--label-prefix"] === undefined || !Number.isSafeInteger(max) || max < 1) {
|
|
17268
|
+
writeUsageFailure(ctx.deps, "Usage: candle tee check <address|id|label>... [--label-prefix <p>] [--sign] [--max <n>] [--rpc-url <url>]", ctx.json);
|
|
17269
|
+
return 2;
|
|
17270
|
+
}
|
|
17271
|
+
try {
|
|
17272
|
+
const key = await tradingKey(ctx);
|
|
17273
|
+
const listing = await diagnosticListing(ctx, key);
|
|
17274
|
+
const names = [...new Set(parsed.positionals)];
|
|
17275
|
+
const prefix = parsed.values["--label-prefix"];
|
|
17276
|
+
if (prefix !== undefined) {
|
|
17277
|
+
const selected2 = listing.rows.filter((row) => row.label?.startsWith(prefix));
|
|
17278
|
+
if (!selected2.length)
|
|
17279
|
+
throw new TradingError("TEE_WALLET_REQUIRED", `No TEE wallet matches this prefix. Bound to this key: ${listing.rows.map(describeWallet).join("; ") || "none"}.`);
|
|
17280
|
+
for (const row of selected2)
|
|
17281
|
+
names.push(row.id);
|
|
17282
|
+
}
|
|
17283
|
+
const selected = new Set;
|
|
17284
|
+
const targets = names.filter((name) => {
|
|
17285
|
+
const matches = listing.rows.filter((row) => matchesName(row, name));
|
|
17286
|
+
const id = matches.length === 1 ? matches[0].id : `name:${name}`;
|
|
17287
|
+
if (selected.has(id))
|
|
17288
|
+
return false;
|
|
17289
|
+
selected.add(id);
|
|
17290
|
+
return true;
|
|
17291
|
+
});
|
|
17292
|
+
const sign3 = parsed.booleans.has("--sign");
|
|
17293
|
+
if (sign3 && targets.length > max)
|
|
17294
|
+
throw new TradingError("VALIDATION_FAILED", `Signing ${targets.length} wallets exceeds the batch cap ${max}; raise it explicitly with --max.`);
|
|
17295
|
+
const records = await readWalletRecords(ctx, key);
|
|
17296
|
+
const reports = [];
|
|
17297
|
+
for (const name of targets) {
|
|
17298
|
+
const matches = listing.rows.filter((row) => matchesName(row, name));
|
|
17299
|
+
const payable = matches.length === 1 ? undefined : new TradingError("TEE_WALLET_REQUIRED", `"${safeText(name)}" matches ${matches.length} TEE wallets. Bound to this key: ${listing.rows.map(describeWallet).join("; ") || "no TEE wallets bound to this key"}.`);
|
|
17300
|
+
const { report, wallet } = await inspectReport(ctx, key, matches.length === 1 ? matches[0] : undefined, listing, records, payable, name);
|
|
17301
|
+
if (sign3 && wallet)
|
|
17302
|
+
await buildCheck(ctx, key, wallet, report, "SOL", "USDC", "0.000001", 50, parsed.values["--rpc-url"], true);
|
|
17303
|
+
else
|
|
17304
|
+
report.checks.push(skipped("sign", sign3 ? report.checks.find((check) => check.status === "fail")?.id ?? "signer" : "--sign not set"));
|
|
17305
|
+
reports.push(report);
|
|
17306
|
+
}
|
|
17307
|
+
return printWalletReports(ctx, reports);
|
|
17308
|
+
} catch (error) {
|
|
17309
|
+
return tradingFailure(ctx, error);
|
|
17310
|
+
}
|
|
17311
|
+
}
|
|
17312
|
+
async function swapCheck(args, ctx) {
|
|
17313
|
+
const parsed = parseArgs(args, {
|
|
17314
|
+
valueFlags: ["--amount", "--percent", "--wallet", "--to", "--client-trade-id", "--slippage-bps", "--rpc-url"],
|
|
17315
|
+
booleanFlags: ["--dry-run", "--yes", "--wait"]
|
|
17316
|
+
});
|
|
17317
|
+
if ("error" in parsed) {
|
|
17318
|
+
writeUsageFailure(ctx.deps, parsed.error, ctx.json);
|
|
17319
|
+
return 2;
|
|
17320
|
+
}
|
|
17321
|
+
try {
|
|
17322
|
+
if (parsed.booleans.has("--yes") || parsed.values["--client-trade-id"] !== undefined || parsed.values["--percent"] !== undefined)
|
|
17323
|
+
throw new TradingError("VALIDATION_FAILED", "--dry-run refuses --yes, --client-trade-id and --percent.");
|
|
17324
|
+
if (parsed.positionals.length !== 2 || !parsed.values["--amount"]) {
|
|
17325
|
+
writeUsageFailure(ctx.deps, "Usage: candle swap <from> <to> --amount <n> [--wallet <w>] --dry-run", ctx.json);
|
|
17326
|
+
return 2;
|
|
17327
|
+
}
|
|
17328
|
+
const slippage = Number(parsed.values["--slippage-bps"] ?? "50");
|
|
17329
|
+
if (!Number.isInteger(slippage) || slippage < 0 || slippage > 5000) {
|
|
17330
|
+
writeUsageFailure(ctx.deps, "Slippage must be an integer from 0 to 5000 bps.", ctx.json);
|
|
17331
|
+
return 2;
|
|
17332
|
+
}
|
|
17333
|
+
const from = classifyAsset(parsed.positionals[0]);
|
|
17334
|
+
const to = classifyAsset(parsed.positionals[1]);
|
|
17335
|
+
if (from.chain !== "solana" || to.chain !== "solana")
|
|
17336
|
+
throw new TradingError("VALIDATION_FAILED", "Dry run checks Solana TEE pairs; Hood and bridge pairs are unsupported.");
|
|
17337
|
+
if (parsed.values["--to"] !== undefined || parsed.booleans.has("--wait")) {
|
|
17338
|
+
writeUsageFailure(ctx.deps, "--to and --wait are for a bridge.", ctx.json);
|
|
17339
|
+
return 2;
|
|
17340
|
+
}
|
|
17341
|
+
if (from.asset === to.asset || !from.base && !to.base)
|
|
17342
|
+
throw new TradingError("PAIR_UNSUPPORTED", "Choose distinct assets with SOL, USDC or CNDL on one side.");
|
|
17343
|
+
rawAmount(parsed.values["--amount"], 18);
|
|
17344
|
+
const key = await tradingKey(ctx);
|
|
17345
|
+
const listing = await diagnosticListing(ctx, key);
|
|
17346
|
+
let row;
|
|
17347
|
+
let payable;
|
|
17348
|
+
try {
|
|
17349
|
+
const selected = await selectTradingPayer(ctx, key, parsed.values["--wallet"], listing);
|
|
17350
|
+
if (selected.kind === "embedded")
|
|
17351
|
+
throw new TradingError("VALIDATION_FAILED", "dry run checks TEE payers");
|
|
17352
|
+
row = selected.row;
|
|
17353
|
+
} catch (error) {
|
|
17354
|
+
if (error instanceof TradingError && error.code === "VALIDATION_FAILED")
|
|
17355
|
+
throw error;
|
|
17356
|
+
if (!(error instanceof TradingError))
|
|
17357
|
+
throw error;
|
|
17358
|
+
payable = error;
|
|
17359
|
+
}
|
|
17360
|
+
const records = await readWalletRecords(ctx, key);
|
|
17361
|
+
const { report, wallet } = await inspectReport(ctx, key, row, listing, records, payable, parsed.values["--wallet"]);
|
|
17362
|
+
if (wallet)
|
|
17363
|
+
await buildCheck(ctx, key, wallet, report, from.asset, to.asset, parsed.values["--amount"], slippage, parsed.values["--rpc-url"], false);
|
|
17364
|
+
else
|
|
17365
|
+
report.checks.push(skipped("quote", report.checks.find((check) => check.status === "fail")?.id ?? "signer"), skipped("spend", "quote"));
|
|
17366
|
+
return printWalletReports(ctx, [report], true);
|
|
17367
|
+
} catch (error) {
|
|
17368
|
+
return tradingFailure(ctx, error);
|
|
17369
|
+
}
|
|
17370
|
+
}
|
|
17371
|
+
var pass = (id) => ({ id, status: "pass" }), skipped = (id, because) => ({ id, status: "skipped", because }), linkedRow;
|
|
17372
|
+
var init_trading_checks = __esm(() => {
|
|
17373
|
+
init_ed25519();
|
|
17374
|
+
init_esm();
|
|
17375
|
+
init_zod();
|
|
17376
|
+
init_args();
|
|
17377
|
+
init_swap();
|
|
17378
|
+
init_render();
|
|
17379
|
+
init_solana_alt();
|
|
17380
|
+
init_solana_endpoint();
|
|
17381
|
+
init_solana_verify();
|
|
17382
|
+
init_trading();
|
|
17383
|
+
linkedRow = exports_external.object({
|
|
17384
|
+
_id: exports_external.string(),
|
|
17385
|
+
address: exports_external.string(),
|
|
17386
|
+
chain: exports_external.string(),
|
|
17387
|
+
label: exports_external.string().optional(),
|
|
17388
|
+
signerQuorumId: exports_external.string().optional(),
|
|
17389
|
+
profile: exports_external.string().optional(),
|
|
17390
|
+
revokedAt: exports_external.number().optional()
|
|
17391
|
+
});
|
|
17392
|
+
});
|
|
17393
|
+
|
|
17394
|
+
// src/commands/swap.ts
|
|
17395
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
16954
17396
|
function tradingFailure(ctx, error, id) {
|
|
16955
17397
|
if (error instanceof TradingUsage) {
|
|
16956
17398
|
writeUsageFailure(ctx.deps, error.message, ctx.json);
|
|
@@ -17184,6 +17626,8 @@ async function assertDeferredExecuteSupported(ctx, key, id) {
|
|
|
17184
17626
|
throw new TradingError("INVALID_RESPONSE", "The execute route answered for a trade that does not exist.");
|
|
17185
17627
|
}
|
|
17186
17628
|
async function swap(args, ctx) {
|
|
17629
|
+
if (args.includes("--dry-run"))
|
|
17630
|
+
return swapCheck(args, ctx);
|
|
17187
17631
|
const parsed = parseArgs(args, {
|
|
17188
17632
|
valueFlags: ["--amount", "--percent", "--wallet", "--to", "--client-trade-id", "--slippage-bps", "--rpc-url"],
|
|
17189
17633
|
booleanFlags: ["--yes", "--wait"]
|
|
@@ -17193,7 +17637,7 @@ async function swap(args, ctx) {
|
|
|
17193
17637
|
return 2;
|
|
17194
17638
|
}
|
|
17195
17639
|
const flags = parsed.values;
|
|
17196
|
-
const id = flags["--client-trade-id"] ?? `swap-${
|
|
17640
|
+
const id = flags["--client-trade-id"] ?? `swap-${randomUUID2()}`;
|
|
17197
17641
|
const slippage = Number(flags["--slippage-bps"] ?? "50");
|
|
17198
17642
|
if (!Number.isInteger(slippage) || slippage < 0 || slippage > AGENT_MAX_SLIPPAGE_BPS) {
|
|
17199
17643
|
writeUsageFailure(ctx.deps, SLIPPAGE_RANGE_MESSAGE, ctx.json);
|
|
@@ -17414,17 +17858,17 @@ async function hoodDecimals(asset, rpc) {
|
|
|
17414
17858
|
return decimals;
|
|
17415
17859
|
}
|
|
17416
17860
|
async function recordTradedToken(ctx, wallet, token) {
|
|
17417
|
-
const
|
|
17861
|
+
const skipped2 = (reason) => `Notice: the sealed EVM record was not updated for ${token} (${reason}). The leg landed. A later sweep still finds this token with --token ${token}, or with --from-block.`;
|
|
17418
17862
|
const append = ctx.deps.appendEvmRecord;
|
|
17419
17863
|
let notice2;
|
|
17420
17864
|
if (!append)
|
|
17421
|
-
notice2 =
|
|
17865
|
+
notice2 = skipped2("this CLI build has no sealed EVM record writer");
|
|
17422
17866
|
else {
|
|
17423
17867
|
try {
|
|
17424
17868
|
const outcome = await append(ctx, { kind: "token", wallet: toChecksumAddress(wallet), token });
|
|
17425
|
-
notice2 = outcome.appended ? outcome.notice :
|
|
17869
|
+
notice2 = outcome.appended ? outcome.notice : skipped2(outcome.notice);
|
|
17426
17870
|
} catch (error) {
|
|
17427
|
-
notice2 =
|
|
17871
|
+
notice2 = skipped2(error instanceof Error ? error.message : "the append failed");
|
|
17428
17872
|
}
|
|
17429
17873
|
}
|
|
17430
17874
|
if (notice2)
|
|
@@ -17872,6 +18316,7 @@ var init_swap = __esm(() => {
|
|
|
17872
18316
|
init_solana_lite();
|
|
17873
18317
|
init_solana_verify();
|
|
17874
18318
|
init_trading();
|
|
18319
|
+
init_trading_checks();
|
|
17875
18320
|
SLIPPAGE_RANGE_MESSAGE = `maxSlippageBps must be an integer in bps, between 0 and ${AGENT_MAX_SLIPPAGE_BPS}`;
|
|
17876
18321
|
settlementSchema = exports_external.object({
|
|
17877
18322
|
state: exports_external.enum(["settled", "pending", "failed", "uncertain"]),
|
|
@@ -17925,11 +18370,11 @@ function writeScreenFailure(deps, result, context, json) {
|
|
|
17925
18370
|
`)}
|
|
17926
18371
|
`);
|
|
17927
18372
|
}
|
|
17928
|
-
function writeScreenLocalFailure(deps,
|
|
18373
|
+
function writeScreenLocalFailure(deps, failure3, json) {
|
|
17929
18374
|
if (json)
|
|
17930
|
-
writeLocalFailure(deps,
|
|
18375
|
+
writeLocalFailure(deps, failure3, json);
|
|
17931
18376
|
else
|
|
17932
|
-
writeScreenMessage(deps,
|
|
18377
|
+
writeScreenMessage(deps, failure3.message, failure3.suggestion);
|
|
17933
18378
|
}
|
|
17934
18379
|
function writeScreenUsageFailure(deps, message, json, screen) {
|
|
17935
18380
|
if (json)
|
|
@@ -18033,14 +18478,14 @@ async function readSignerRoles(rpc, addresses, opts) {
|
|
|
18033
18478
|
let requests = 0;
|
|
18034
18479
|
let rateLimited = 0;
|
|
18035
18480
|
let settled = 0;
|
|
18036
|
-
let
|
|
18481
|
+
let skipped2 = 0;
|
|
18037
18482
|
let next = 0;
|
|
18038
18483
|
let active = 0;
|
|
18039
18484
|
const progress = () => {
|
|
18040
18485
|
opts.onProgress?.({
|
|
18041
|
-
completed: settled +
|
|
18486
|
+
completed: settled + skipped2,
|
|
18042
18487
|
settled,
|
|
18043
|
-
skipped,
|
|
18488
|
+
skipped: skipped2,
|
|
18044
18489
|
total: planned,
|
|
18045
18490
|
refusedGroups: failed.size,
|
|
18046
18491
|
elapsedMs: opts.now() - startedAt
|
|
@@ -18185,7 +18630,7 @@ async function readSignerRoles(rpc, addresses, opts) {
|
|
|
18185
18630
|
while (active < cap && next < tasks.length) {
|
|
18186
18631
|
const task = tasks[next++];
|
|
18187
18632
|
if (failed.has(task.group.id)) {
|
|
18188
|
-
|
|
18633
|
+
skipped2 += 1;
|
|
18189
18634
|
skippedAny = true;
|
|
18190
18635
|
continue;
|
|
18191
18636
|
}
|
|
@@ -29067,7 +29512,7 @@ async function askForOwnPassphrase(ctx, promptText) {
|
|
|
29067
29512
|
function requireTty(ctx, what) {
|
|
29068
29513
|
if (ctx.deps.isTTY.stdin && ctx.deps.isTTY.stdout)
|
|
29069
29514
|
return true;
|
|
29070
|
-
writeVaultFailure(ctx, new VaultError("
|
|
29515
|
+
writeVaultFailure(ctx, new VaultError("VAULT_TERMINAL_REQUIRED", `${what} needs a terminal: this CLI reads a vault passphrase or a security key PIN from a hidden prompt and from nowhere else.`, {
|
|
29071
29516
|
suggestion: "There is no environment variable and no flag that supplies one."
|
|
29072
29517
|
}));
|
|
29073
29518
|
return false;
|
|
@@ -29076,7 +29521,7 @@ function refusePromptsWithoutTerminal(ctx, what) {
|
|
|
29076
29521
|
if (ctx.deps.isTTY.stdin && ctx.deps.isTTY.stderr)
|
|
29077
29522
|
return ctx;
|
|
29078
29523
|
const refuse2 = async () => {
|
|
29079
|
-
throw tagNoTerminal(new VaultError("
|
|
29524
|
+
throw tagNoTerminal(new VaultError("VAULT_TERMINAL_REQUIRED", `${what} needs a terminal here: this unlock reads a vault passphrase, a security key PIN or a choice from a prompt, and standard input and standard error must both be a terminal for that. Standard output may be redirected.`, {
|
|
29080
29525
|
suggestion: "Run it from a terminal, or pass --factor touch-id or --factor passkey to unlock with a factor that reads no prompt. No environment variable or flag supplies a passphrase or PIN."
|
|
29081
29526
|
}));
|
|
29082
29527
|
};
|
|
@@ -29860,9 +30305,9 @@ async function reconcileGrant(ctx, entry, opts = {}) {
|
|
|
29860
30305
|
if (!stranded.complete) {
|
|
29861
30306
|
return { kind: "unreadable", reason: stranded.incompleteReason ?? "the stranded-import read was incomplete" };
|
|
29862
30307
|
}
|
|
29863
|
-
const
|
|
29864
|
-
if (
|
|
29865
|
-
return { kind: "strand-final", account: wallets.account, failure:
|
|
30308
|
+
const failure3 = stranded.failures.find((candidate) => sameWalletAddress(candidate.address, entry.address));
|
|
30309
|
+
if (failure3?.stage === "convex_link") {
|
|
30310
|
+
return { kind: "strand-final", account: wallets.account, failure: failure3 };
|
|
29866
30311
|
}
|
|
29867
30312
|
return { kind: "unresolved", account: wallets.account };
|
|
29868
30313
|
}
|
|
@@ -33994,23 +34439,23 @@ async function walletsImport(args, ctx) {
|
|
|
33994
34439
|
deps
|
|
33995
34440
|
});
|
|
33996
34441
|
if (!flow.ok) {
|
|
33997
|
-
const
|
|
33998
|
-
if (
|
|
33999
|
-
writeScreenFailure(deps,
|
|
34442
|
+
const failure3 = flow.failure;
|
|
34443
|
+
if (failure3.kind === "api") {
|
|
34444
|
+
writeScreenFailure(deps, failure3.response, { apiUrl, authType: "key" }, json);
|
|
34000
34445
|
return 1;
|
|
34001
34446
|
}
|
|
34002
|
-
if (
|
|
34447
|
+
if (failure3.kind === "signer-store") {
|
|
34003
34448
|
writeScreenLocalFailure(deps, {
|
|
34004
34449
|
code: "SIGNER_STORE_FAILED",
|
|
34005
|
-
message: `Could not store the new signer in the ${deps.backend} store: ${
|
|
34450
|
+
message: `Could not store the new signer in the ${deps.backend} store: ${failure3.error instanceof Error ? failure3.error.message : failure3.error}`,
|
|
34006
34451
|
suggestion: "Nothing was imported. Unlock the store (or fix the error above) and run the command again."
|
|
34007
34452
|
}, json);
|
|
34008
34453
|
return 1;
|
|
34009
34454
|
}
|
|
34010
34455
|
writeScreenLocalFailure(deps, {
|
|
34011
34456
|
code: "SIGNER_COMMIT_FAILED",
|
|
34012
|
-
message: `Wallet ${
|
|
34013
|
-
suggestion: `The signer is not lost: it is in the ${deps.backend} store under "${
|
|
34457
|
+
message: `Wallet ${failure3.walletId} was imported, but its signer could not be stored under the wallet's own ` + `ref: ${failure3.error instanceof Error ? failure3.error.message : failure3.error}`,
|
|
34458
|
+
suggestion: `The signer is not lost: it is in the ${deps.backend} store under "${failure3.pendingRef}". Copy it to ` + `"${walletSignerRef(failure3.walletId)}", or revoke the wallet with: candle wallets revoke ${failure3.walletId}`
|
|
34014
34459
|
}, json);
|
|
34015
34460
|
return 1;
|
|
34016
34461
|
}
|
|
@@ -35486,8 +35931,8 @@ function releaseActiveTee(active) {
|
|
|
35486
35931
|
if (active.source === "vault")
|
|
35487
35932
|
releaseResolvedTee(active.resolved);
|
|
35488
35933
|
}
|
|
35489
|
-
function writeCommitFailure(ctx,
|
|
35490
|
-
writeLocalFailure(ctx.deps, { code:
|
|
35934
|
+
function writeCommitFailure(ctx, failure3, consequence) {
|
|
35935
|
+
writeLocalFailure(ctx.deps, { code: failure3.code, message: failure3.message, suggestion: consequence }, ctx.json);
|
|
35491
35936
|
}
|
|
35492
35937
|
async function openExistingTeeStore(ctx, parsed) {
|
|
35493
35938
|
const { deps, json } = ctx;
|
|
@@ -35788,13 +36233,13 @@ async function teeEnable(args, ctx) {
|
|
|
35788
36233
|
vaultDestination: vault
|
|
35789
36234
|
});
|
|
35790
36235
|
if (!flow.ok) {
|
|
35791
|
-
const
|
|
35792
|
-
if (
|
|
35793
|
-
writeFailure(deps,
|
|
36236
|
+
const failure3 = flow.failure;
|
|
36237
|
+
if (failure3.kind === "api")
|
|
36238
|
+
writeFailure(deps, failure3.response, { apiUrl, authType: "key" }, json);
|
|
35794
36239
|
else {
|
|
35795
36240
|
writeLocalFailure(deps, {
|
|
35796
|
-
code:
|
|
35797
|
-
message: `${address}: ${
|
|
36241
|
+
code: failure3.kind === "signer-store" ? "SIGNER_STORE_FAILED" : "SIGNER_COMMIT_FAILED",
|
|
36242
|
+
message: `${address}: ${failure3.error instanceof Error ? failure3.error.message : failure3.error}`,
|
|
35798
36243
|
suggestion: "The key is still sealed locally. Fix the error above and run enable again."
|
|
35799
36244
|
}, json);
|
|
35800
36245
|
}
|
|
@@ -40333,7 +40778,7 @@ var init_market_disclosure = __esm(() => {
|
|
|
40333
40778
|
});
|
|
40334
40779
|
|
|
40335
40780
|
// ../mcp/src/orchestrate.ts
|
|
40336
|
-
import { randomUUID as
|
|
40781
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
40337
40782
|
function requireApiKey(cfg) {
|
|
40338
40783
|
if (!cfg.apiKey) {
|
|
40339
40784
|
throw new Error("CANDLE_AGENT_API_KEY is required for this tool. Set it in the environment or MCP client config.");
|
|
@@ -40446,7 +40891,7 @@ async function readPaperInventory(cfg, doFetch, extra) {
|
|
|
40446
40891
|
}
|
|
40447
40892
|
async function executeTrade(args, cfg, doFetch) {
|
|
40448
40893
|
const apiKey = requireApiKey(cfg);
|
|
40449
|
-
const clientTradeId = args.clientTradeId ??
|
|
40894
|
+
const clientTradeId = args.clientTradeId ?? randomUUID4();
|
|
40450
40895
|
if (args.amount !== undefined && args.percent !== undefined) {
|
|
40451
40896
|
return errText("pass exactly one of amount or percent, not both", { clientTradeId });
|
|
40452
40897
|
}
|
|
@@ -40595,7 +41040,7 @@ async function executeTrade(args, cfg, doFetch) {
|
|
|
40595
41040
|
}
|
|
40596
41041
|
async function executeLaunchAndSeed(args, cfg, doFetch, imageDir, imageFs, opts = {}) {
|
|
40597
41042
|
const apiKey = requireApiKey(cfg);
|
|
40598
|
-
const clientLaunchId = args.clientLaunchId ??
|
|
41043
|
+
const clientLaunchId = args.clientLaunchId ?? randomUUID4();
|
|
40599
41044
|
const socialsProblem = socialsRefusal(args.socials);
|
|
40600
41045
|
if (socialsProblem)
|
|
40601
41046
|
return { text: imageRefusalBody(socialsProblem, { clientLaunchId }), isError: true };
|
|
@@ -40824,7 +41269,26 @@ async function resolveToken(args, cfg, doFetch) {
|
|
|
40824
41269
|
return read.err;
|
|
40825
41270
|
return { text: JSON.stringify({ success: true, chain: chain2, mint: args.mint, market: read.market }, null, 2) };
|
|
40826
41271
|
}
|
|
40827
|
-
|
|
41272
|
+
function teePayers(body) {
|
|
41273
|
+
const page = body;
|
|
41274
|
+
if (!Array.isArray(page?.page))
|
|
41275
|
+
return;
|
|
41276
|
+
const paused = page.paused === true;
|
|
41277
|
+
const scopes = Array.isArray(page.scopes) ? page.scopes.filter((s) => typeof s === "string") : [];
|
|
41278
|
+
const spendAuthorized = TEE_SPEND_SCOPES.some((scope) => scopes.includes(scope));
|
|
41279
|
+
const wallets2 = page.page.map((row2) => ({
|
|
41280
|
+
id: String(row2.id),
|
|
41281
|
+
address: String(row2.address),
|
|
41282
|
+
chain: String(row2.chain),
|
|
41283
|
+
...typeof row2.label === "string" ? { label: row2.label } : {},
|
|
41284
|
+
active: row2.active === true,
|
|
41285
|
+
allowLaunch: row2.allowLaunch === true,
|
|
41286
|
+
spendable: row2.active === true && !paused && spendAuthorized
|
|
41287
|
+
}));
|
|
41288
|
+
return { paused, scopes, wallets: wallets2, complete: page.isDone === true };
|
|
41289
|
+
}
|
|
41290
|
+
async function executionStatus(cfg, doFetch, options = {}) {
|
|
41291
|
+
const readTee = options.teeWallets !== false;
|
|
40828
41292
|
const apiKey = requireApiKey(cfg);
|
|
40829
41293
|
const get = async (path) => {
|
|
40830
41294
|
const res = await doFetch(`${base(cfg)}${path}`, { method: "GET", headers: headers(apiKey) });
|
|
@@ -40838,14 +41302,17 @@ async function executionStatus(cfg, doFetch) {
|
|
|
40838
41302
|
}
|
|
40839
41303
|
return { ok: res.ok, status: res.status, body };
|
|
40840
41304
|
};
|
|
40841
|
-
const [wallets2, tier, limits, risk] = await Promise.all([
|
|
41305
|
+
const [wallets2, trading, tier, limits, risk] = await Promise.all([
|
|
40842
41306
|
get("/api/v1/agent/wallets/embedded"),
|
|
41307
|
+
readTee ? get("/api/v1/agent/wallets/trading") : Promise.resolve(undefined),
|
|
40843
41308
|
get("/api/v1/agent/tier"),
|
|
40844
41309
|
get("/api/v1/agent/keys/self/limits"),
|
|
40845
41310
|
get("/api/v1/agent/keys/self/risk")
|
|
40846
41311
|
]);
|
|
41312
|
+
const tee = trading?.ok ? teePayers(trading.body) : undefined;
|
|
40847
41313
|
const unreadable2 = [
|
|
40848
41314
|
["wallets", wallets2],
|
|
41315
|
+
...trading ? [["tee", tee ? trading : { ok: false }]] : [],
|
|
40849
41316
|
["tier", tier],
|
|
40850
41317
|
["limits", limits],
|
|
40851
41318
|
["risk", risk]
|
|
@@ -40862,6 +41329,11 @@ async function executionStatus(cfg, doFetch) {
|
|
|
40862
41329
|
updateAvailable: updateAvailable() ?? undefined,
|
|
40863
41330
|
unreadable: unreadable2.length > 0 ? unreadable2.map(([name]) => name) : undefined,
|
|
40864
41331
|
wallets: wallets2.body,
|
|
41332
|
+
tee: tee?.wallets,
|
|
41333
|
+
teeScopes: tee ? tee.scopes : undefined,
|
|
41334
|
+
teePaused: tee ? tee.paused : undefined,
|
|
41335
|
+
teeComplete: tee ? tee.complete : undefined,
|
|
41336
|
+
teeError: trading && !tee ? trading.body : undefined,
|
|
40865
41337
|
tier: tier.body,
|
|
40866
41338
|
limits: limits.body,
|
|
40867
41339
|
risk: risk.body
|
|
@@ -40869,7 +41341,7 @@ async function executionStatus(cfg, doFetch) {
|
|
|
40869
41341
|
...unreadable2.length > 0 ? { isError: true } : {}
|
|
40870
41342
|
};
|
|
40871
41343
|
}
|
|
40872
|
-
var SOLANA_SEED_CEILINGS, MAIN_PAYER, SWEEP_BASE_ASSETS, STOP_CLEARED_BY = "An account owner reviews the profile and resumes it from the agent console; an API key cannot resume it.";
|
|
41344
|
+
var SOLANA_SEED_CEILINGS, MAIN_PAYER, SWEEP_BASE_ASSETS, TEE_SPEND_SCOPES, STOP_CLEARED_BY = "An account owner reviews the profile and resumes it from the agent console; an API key cannot resume it.";
|
|
40873
41345
|
var init_orchestrate = __esm(() => {
|
|
40874
41346
|
init_convert();
|
|
40875
41347
|
init_image();
|
|
@@ -40885,6 +41357,7 @@ var init_orchestrate = __esm(() => {
|
|
|
40885
41357
|
solana: ["USDC", "CNDL", "SOL"],
|
|
40886
41358
|
hood: ["USDG", "ETH"]
|
|
40887
41359
|
};
|
|
41360
|
+
TEE_SPEND_SCOPES = ["swap:write", "transfer:write", "launch:write"];
|
|
40888
41361
|
});
|
|
40889
41362
|
|
|
40890
41363
|
// ../mcp/src/hyperliquid.ts
|
|
@@ -44605,6 +45078,9 @@ function planTableRows(table) {
|
|
|
44605
45078
|
row2("Uploads per minute", (e) => count(e.limits.uploadsPerMin)),
|
|
44606
45079
|
row2("Linked wallets", (e) => count(e.limits.linkedWallets))
|
|
44607
45080
|
];
|
|
45081
|
+
if (plans.some((e) => typeof e.limits.labReplaysPerDay === "number")) {
|
|
45082
|
+
rows.push(row2("Strategy lab replays", (e) => typeof e.limits.labReplaysPerDay === "number" ? `${count(e.limits.labReplaysPerDay)} a day` : "-"));
|
|
45083
|
+
}
|
|
44608
45084
|
const known = Object.keys(PLAN_CAPABILITY_LABELS);
|
|
44609
45085
|
const served = new Set;
|
|
44610
45086
|
for (const entry of plans)
|
|
@@ -44649,13 +45125,14 @@ var init_plans = __esm(() => {
|
|
|
44649
45125
|
createLinkedWallets: "Create linked wallets",
|
|
44650
45126
|
importLinkedWallets: "Import linked wallets",
|
|
44651
45127
|
limitOrders: "Limit orders",
|
|
44652
|
-
quant: "Quant deep analyses (Max)"
|
|
45128
|
+
quant: "Quant deep analyses (Max)",
|
|
45129
|
+
strategyLab: "Strategy lab (saved strategies stay Max)"
|
|
44653
45130
|
};
|
|
44654
45131
|
PLAN_LABELS = { free: "Free", believer: "Believer", pro: "Pro", max: "Max" };
|
|
44655
45132
|
});
|
|
44656
45133
|
|
|
44657
45134
|
// ../mcp/src/token-metadata.ts
|
|
44658
|
-
import { randomUUID as
|
|
45135
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
44659
45136
|
function editSocialsSchema() {
|
|
44660
45137
|
const shape = {};
|
|
44661
45138
|
for (const key of SOCIAL_KEYS) {
|
|
@@ -44706,7 +45183,7 @@ function tokenEditBody(args) {
|
|
|
44706
45183
|
return { body };
|
|
44707
45184
|
}
|
|
44708
45185
|
function tokenEditRequest(base2, apiKey, args, body) {
|
|
44709
|
-
const key = typeof args.idempotencyKey === "string" && args.idempotencyKey ? args.idempotencyKey :
|
|
45186
|
+
const key = typeof args.idempotencyKey === "string" && args.idempotencyKey ? args.idempotencyKey : randomUUID5();
|
|
44710
45187
|
return {
|
|
44711
45188
|
url: `${base2}/api/v1/tokens/${encodeURIComponent(String(args.chain))}/${encodeURIComponent(String(args.mint))}/metadata`,
|
|
44712
45189
|
init: {
|
|
@@ -44934,7 +45411,7 @@ __export(exports_tools, {
|
|
|
44934
45411
|
OPERATION_NOT_FOUND_RULE: () => OPERATION_NOT_FOUND_RULE,
|
|
44935
45412
|
LAB_TOOL_NAMES: () => LAB_TOOL_NAMES
|
|
44936
45413
|
});
|
|
44937
|
-
import { randomUUID as
|
|
45414
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
44938
45415
|
function resolveToolAllowlist(env) {
|
|
44939
45416
|
const raw = env.CANDLE_MCP_TOOLS?.trim();
|
|
44940
45417
|
if (!raw)
|
|
@@ -45035,6 +45512,26 @@ function buildRequest(name, args, cfg) {
|
|
|
45035
45512
|
init: { method: "GET", headers: jsonHeaders() }
|
|
45036
45513
|
};
|
|
45037
45514
|
}
|
|
45515
|
+
case "candle_quote": {
|
|
45516
|
+
const apiKey = requireApiKey2(cfg);
|
|
45517
|
+
const { chain: chain2, mint, side, amountRaw, quoteAsset, poolKey, includeRoundTrip } = args;
|
|
45518
|
+
return {
|
|
45519
|
+
url: `${base2}/api/v1/trade/agent/quote`,
|
|
45520
|
+
init: {
|
|
45521
|
+
method: "POST",
|
|
45522
|
+
headers: jsonHeaders(apiKey),
|
|
45523
|
+
body: JSON.stringify({
|
|
45524
|
+
chain: chain2,
|
|
45525
|
+
mint,
|
|
45526
|
+
side,
|
|
45527
|
+
amountRaw,
|
|
45528
|
+
...quoteAsset !== undefined && quoteAsset !== "" ? { quoteAsset } : {},
|
|
45529
|
+
...poolKey !== undefined ? { poolKey } : {},
|
|
45530
|
+
...includeRoundTrip !== undefined ? { includeRoundTrip } : {}
|
|
45531
|
+
})
|
|
45532
|
+
}
|
|
45533
|
+
};
|
|
45534
|
+
}
|
|
45038
45535
|
case "candle_get_price_history": {
|
|
45039
45536
|
const { chain: chain2, mint, interval, limit, to } = args;
|
|
45040
45537
|
const query2 = new URLSearchParams;
|
|
@@ -45272,6 +45769,28 @@ function buildRequest(name, args, cfg) {
|
|
|
45272
45769
|
init: { method: "GET", headers: jsonHeaders(apiKey) }
|
|
45273
45770
|
};
|
|
45274
45771
|
}
|
|
45772
|
+
case "candle_get_trading_wallets": {
|
|
45773
|
+
const apiKey = requireApiKey2(cfg);
|
|
45774
|
+
const { cursor } = args;
|
|
45775
|
+
const query2 = cursor === undefined ? "" : `?cursor=${encodeURIComponent(cursor)}`;
|
|
45776
|
+
return {
|
|
45777
|
+
url: `${base2}/api/v1/agent/wallets/trading${query2}`,
|
|
45778
|
+
init: { method: "GET", headers: jsonHeaders(apiKey) }
|
|
45779
|
+
};
|
|
45780
|
+
}
|
|
45781
|
+
case "candle_get_spendable_balances": {
|
|
45782
|
+
const apiKey = requireApiKey2(cfg);
|
|
45783
|
+
const { cursor, mints } = args;
|
|
45784
|
+
const params = new URLSearchParams;
|
|
45785
|
+
if (cursor !== undefined)
|
|
45786
|
+
params.set("cursor", cursor);
|
|
45787
|
+
if (mints !== undefined && mints.length > 0)
|
|
45788
|
+
params.set("mints", mints.join(","));
|
|
45789
|
+
return {
|
|
45790
|
+
url: `${base2}/api/v1/agent/wallets/self/balances${params.size ? `?${params}` : ""}`,
|
|
45791
|
+
init: { method: "GET", headers: jsonHeaders(apiKey) }
|
|
45792
|
+
};
|
|
45793
|
+
}
|
|
45275
45794
|
case "candle_get_profile_wallets": {
|
|
45276
45795
|
const apiKey = requireApiKey2(cfg);
|
|
45277
45796
|
const { keyPrefix } = args;
|
|
@@ -45402,7 +45921,7 @@ async function getOperation(args, cfg, doFetch) {
|
|
|
45402
45921
|
};
|
|
45403
45922
|
}
|
|
45404
45923
|
async function swap2(args, cfg, doFetch) {
|
|
45405
|
-
const clientSwapId = typeof args.clientSwapId === "string" && args.clientSwapId.length > 0 ? args.clientSwapId :
|
|
45924
|
+
const clientSwapId = typeof args.clientSwapId === "string" && args.clientSwapId.length > 0 ? args.clientSwapId : randomUUID6();
|
|
45406
45925
|
const { url, init } = buildRequest("candle_swap", { ...args, clientSwapId }, cfg);
|
|
45407
45926
|
let ok;
|
|
45408
45927
|
let body;
|
|
@@ -45577,6 +46096,12 @@ function registerToolSubset(server, options) {
|
|
|
45577
46096
|
description: "Read Candle and indexed external markets, including indexed-but-not-routable tokens. No key needed. " + EXTERNAL_MARKET_DISCLOSURE + " " + "Read candleLaunched, launchpad, venue and trade.routable; jupiterOk and discovery flags are distinct. " + "Routability is stored eligibility, not a quote or permission. General quotes use POST /api/v1/trade/agent/quote. " + "Curve quotes and lifecycle describe Candle launches. MARKET_NOT_FOUND is a legacy code: read " + "error.routing.reason, error.discovery and sibling error.retryable. A curve-only 404 does not mean untradeable.",
|
|
45578
46097
|
inputSchema: getMarketShape
|
|
45579
46098
|
}, async (args) => callAndRelay("candle_get_market", args, getConfig(), doFetch));
|
|
46099
|
+
register("candle_quote", {
|
|
46100
|
+
title: "Quote a trade",
|
|
46101
|
+
description: "Price a buy or sell before making it. Moves nothing. Needs an API key with the swap:write scope: a read-only key (account:read) is refused, so use candle_get_market for state instead. " + "Returns amountOut (base units), venue, quoteAsset, quoteDecimals and quotedAt; Hood answers also carry fee and price impact. " + "route is present only on some answers, so do not assume it. A quote is a snapshot (Hood quotes are cached for 10 seconds), not a promise of the fill. " + "Limited to 600 quotes a minute per key, and counted against the key's own limit. Pass amountRaw in base units; candle_trade and candle_swap take decimals.",
|
|
46102
|
+
inputSchema: quoteShape,
|
|
46103
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
46104
|
+
}, async (args) => callAndRelay("candle_quote", args, getConfig(), doFetch));
|
|
45580
46105
|
register("candle_token_forensics", {
|
|
45581
46106
|
title: "Token forensics",
|
|
45582
46107
|
description: "Gate a buy before making it: who launched it (resolved on-chain; pump.fun's shared updateAuthority is never the developer; when no developer is on chain, deployer.attribution names the launchpad or issuer instead, e.g. launched via stonk.fun or issued by xStocks), their went-to-zero rate and last coins, who bought in the deploy window (the creator's own wallets are marked disclosed; strangers in the same slot are the bundle signal), same-funder insider share, same-funder deployer cluster, and safety.summary with six sourced flags (mintAuthority, freezeAuthority, tokenExtensions, lpLock, sellability, liquidityDrain). Refuse an unprompted buy when flagged; incomplete or unknown is not clearance. deployer.priorLaunches is counted (pump.fun curves for a curve creator; record.count says the source); deployer.launcher is the wallet that sent the launch, often a launch tool's signer, never the developer; launch.deployerLaunches is that wallet's inclusive informational count, never a warning. Every measurement carries a coverage note -- 'unavailable' is not 'clean'. No key needed.\n\nMARKET_NOT_FOUND means Candle has no market for that token and this could not run. That is also not 'clean': report that you could not check it, rather than reporting the token as safe. That refusal now carries error.coverage -- covered:false, a reason ('external_launchpad' when the token launched somewhere else, 'unknown_mint' when nobody has indexed it), the launchpad when known, and every check that consequently did not run. Read it instead of guessing. Most of the feed, and any Solana mint Jupiter's index knows, now answers with a partial report instead. On Hood, a token Candle did not launch names its launch account (deployer.method cvc_launch_account) for pons.family and pools.trade launches, with no record of earlier coins; other Hood launchpads return no developer.",
|
|
@@ -45767,6 +46292,16 @@ function registerToolSubset(server, options) {
|
|
|
45767
46292
|
description: "The account's EMBEDDED wallets, one per chain, with their delegation state. These are " + "the wallets candle_trade, candle_swap and candle_transfer spend from, so this is how an " + "agent finds its own funding addresses. Reads only; moves nothing. Not the same as the " + "account's LINKED wallets, which are the owner's own wallets and are not spent from here. " + "Balances are not included: read a specific one with the market and balance endpoints.",
|
|
45768
46293
|
inputSchema: {}
|
|
45769
46294
|
}, async () => callAndRelay("candle_get_wallets", {}, getConfig(), doFetch));
|
|
46295
|
+
register("candle_get_trading_wallets", {
|
|
46296
|
+
title: "List the TEE wallets bound to this key",
|
|
46297
|
+
description: "The TEE wallets bound to THIS key, on Solana (`chain: solana`) and Hood (`chain: evm`): the wallets " + "it may pay from beside the embedded ones in candle_get_wallets. Each row has its id, address, label, " + "`active` (true when the wallet's signing authority is verified active; a false row cannot pay) and " + "`allowLaunch`. The body also carries this key's `scopes` and `paused` (true while the owner has paused " + "the key, which then spends from nothing; spending also needs one of the spend scopes — " + "swap:write, transfer:write or launch:write — and `scopes` says which this key holds). A TEE wallet is signed on your own machine with the key " + "signer: the perps tools here and the candle CLI (`candle swap --wallet <id|address|label>`) pay from " + "it, while candle_trade and candle_swap spend the embedded wallet. Answers for any scope and on a " + "paused key. Paged: pass `continueCursor` back as `cursor` until `isDone`. Reads only; moves nothing.",
|
|
46298
|
+
inputSchema: tradingWalletsShape
|
|
46299
|
+
}, async (args) => callAndRelay("candle_get_trading_wallets", args, getConfig(), doFetch));
|
|
46300
|
+
register("candle_get_spendable_balances", {
|
|
46301
|
+
title: "Read the balances of every wallet this key may spend from",
|
|
46302
|
+
description: "Every wallet THIS key may spend from, and no other, with its balances: the embedded wallets when " + "the owner allows this key to spend them, the TEE wallets bound to it with an active signing " + "authority, and the linked wallets its wallet scope admits. Each row has `kind` (embedded, tee or " + "linked), `chain` (solana or evm), `address`, and `id` and `label` for a linked or TEE wallet. " + "`balances` always holds the base assets (SOL, USDC and CNDL on Solana; ETH and USDG on Hood) plus " + "the `mints` you name (at most 10), each in raw units with the slot or block it was read at. A read " + "that failed is in `unavailable`, never reported as zero. A key with no spend scope (swap:write, " + "transfer:write or launch:write) can spend from nothing and gets an empty page, not an error. " + "`complete: false` means the account's wallet list was cut off. 50 wallets a page: pass " + "`continueCursor` back as `cursor` until `isDone`. Needs no account:read; candle_get_portfolio is " + "the whole-account read. Reads only; moves nothing.",
|
|
46303
|
+
inputSchema: spendableBalancesShape
|
|
46304
|
+
}, async (args) => callAndRelay("candle_get_spendable_balances", args, getConfig(), doFetch));
|
|
45770
46305
|
register("candle_get_profile_wallets", {
|
|
45771
46306
|
title: "Read which wallets an agent profile can spend from",
|
|
45772
46307
|
description: "An agent profile (API key) either spends from EVERY wallet on its account or only from " + "the ones assigned to it. Read walletScope before drawing any conclusion from the list: " + "an empty list means 'every wallet' under scope 'all' and 'none at all' under 'selected'. " + "Reads only; moves nothing.",
|
|
@@ -45823,10 +46358,12 @@ function registerToolSubset(server, options) {
|
|
|
45823
46358
|
});
|
|
45824
46359
|
register("candle_execution_status", {
|
|
45825
46360
|
title: "Can this key execute right now",
|
|
45826
|
-
description: "One call before trading: the embedded wallets to spend from, the
|
|
46361
|
+
description: "One call before trading: the embedded wallets to spend from, the TEE wallets bound to this " + "key, the tier that decides what may be traded, this key's own spend limits, and its loss " + "limits and stop state (risk: lossLimits, state, stopped, tripped). `wallets` is the embedded " + "read. `tee` lists the first page of TEE wallets with `chain` (solana or evm), `active` and " + "`spendable` (active, the key not paused — `teePaused` says whether it is — and the key " + "holding a spend scope: swap:write, transfer:write or launch:write; `teeScopes` lists the " + "key's scopes, so a read-only key shows its wallets with `spendable: false`); " + "`teeComplete: false` means more, which candle_get_trading_wallets lists. candle_trade and " + "candle_swap spend the embedded wallet; a TEE wallet is signed locally (the perps tools, the " + "candle CLI's --wallet). A stopped key reports ready: false and a top-level stopped naming " + "the bound that broke; every trade answers RISK_LIMIT_REACHED until an account owner resumes " + "it, so stop trading and say why rather than retrying. Any scope can read its own. Reads " + "only; moves nothing. Call it when " + "a run starts, or after an authorization error, rather than inferring readiness from a " + "failed trade. If a read could not be completed the tool says which one and does NOT " + "claim the account is unready: an unreachable endpoint and a missing tier are different " + "problems with different fixes.",
|
|
45827
46362
|
inputSchema: {}
|
|
45828
46363
|
}, async () => {
|
|
45829
|
-
const result = await executionStatus(getConfig(), doFetch
|
|
46364
|
+
const result = await executionStatus(getConfig(), doFetch, {
|
|
46365
|
+
teeWallets: options.embeddedExecutionStatus !== true
|
|
46366
|
+
});
|
|
45830
46367
|
return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
|
|
45831
46368
|
});
|
|
45832
46369
|
register("candle_swap", {
|
|
@@ -46097,7 +46634,7 @@ async function registerEligibleProfileTool(server, env = process.env, doFetch =
|
|
|
46097
46634
|
}
|
|
46098
46635
|
return eligible;
|
|
46099
46636
|
}
|
|
46100
|
-
var VAMP_CANDIDATES_DESCRIPTION, localProfileTools, STAKING_DISCLOSURE = "No instant unstake. Read pool.withdrawalTimelockSeconds for the wait. Solana rewards accrue to the holding wallet. sCNDL in any eligible account wallet counts toward Pro and earns the account's Hood staking reward, except a wallet another account also claims, wallets past the 200-wallet cap, and an account with no EVM wallet. CNDL in a pending withdrawal counts toward nothing for Pro.", stakingWriteShape, TOOL_NAMES, LAB_TOOL_NAMES, PROFILE_EDIT_TOOL_NAMES, isProfileEditTool = (name) => PROFILE_EDIT_TOOL_NAMES.includes(name), OPERATION_NOT_FOUND_RULE, getMarketShape, priceHistoryShape, TOKEN_EVENTS_LIMIT_MESSAGE = "limit must be a whole number from 1 to 1000", tokenEventsLimit, TOKEN_EVENTS_KIND_NOTE = "kept back from the request and applied to the returned page by this server, so a filtered page can be shorter than limit while older events of other kinds remain: page with nextCursor as before until it is null", tokenCardShape, tokenEventsShape, tokenForensicsShape, FEED_LIMIT_MESSAGE = "limit must be a whole number from 1 to 200", feedLimit, getFeedShape, reportActivityShape, getAgentProfileShape, listAgentsShape, createPostShape, deletePostShape, listAgentPostsShape, getAgentPositionsShape, getOperationShape, vampCandidatesShape, getFeesShape, atomicPayerShape, atomicLaunchDryRunShape, claimFeesShape, resolveTokenShape, profileWalletsShape, profilePnlShape, profileTradesShape, noArgsShape, setProfileWalletsShape, swapShape, tradeShape, tradeExecuteShape, tokenImageUrl, tokenBannerUrl, localImageShape, localLaunchTokenShape, localLaunchAndSeedShape, IMAGE_PATH_DISCLOSURE, transferShape, sweepShape, PROFILE_PNL_HYPERLIQUID;
|
|
46637
|
+
var VAMP_CANDIDATES_DESCRIPTION, localProfileTools, STAKING_DISCLOSURE = "No instant unstake. Read pool.withdrawalTimelockSeconds for the wait. Solana rewards accrue to the holding wallet. sCNDL in any eligible account wallet counts toward Pro and earns the account's Hood staking reward, except a wallet another account also claims, wallets past the 200-wallet cap, and an account with no EVM wallet. CNDL in a pending withdrawal counts toward nothing for Pro.", stakingWriteShape, TOOL_NAMES, LAB_TOOL_NAMES, PROFILE_EDIT_TOOL_NAMES, isProfileEditTool = (name) => PROFILE_EDIT_TOOL_NAMES.includes(name), OPERATION_NOT_FOUND_RULE, getMarketShape, quoteShape, priceHistoryShape, TOKEN_EVENTS_LIMIT_MESSAGE = "limit must be a whole number from 1 to 1000", tokenEventsLimit, TOKEN_EVENTS_KIND_NOTE = "kept back from the request and applied to the returned page by this server, so a filtered page can be shorter than limit while older events of other kinds remain: page with nextCursor as before until it is null", tokenCardShape, tokenEventsShape, tokenForensicsShape, FEED_LIMIT_MESSAGE = "limit must be a whole number from 1 to 200", feedLimit, getFeedShape, reportActivityShape, getAgentProfileShape, listAgentsShape, createPostShape, deletePostShape, listAgentPostsShape, getAgentPositionsShape, getOperationShape, vampCandidatesShape, getFeesShape, atomicPayerShape, atomicLaunchDryRunShape, claimFeesShape, resolveTokenShape, tradingWalletsShape, spendableBalancesShape, profileWalletsShape, profilePnlShape, profileTradesShape, noArgsShape, setProfileWalletsShape, swapShape, tradeShape, tradeExecuteShape, tokenImageUrl, tokenBannerUrl, localImageShape, localLaunchTokenShape, localLaunchAndSeedShape, IMAGE_PATH_DISCLOSURE, transferShape, sweepShape, PROFILE_PNL_HYPERLIQUID;
|
|
46101
46638
|
var init_tools = __esm(() => {
|
|
46102
46639
|
init_zod();
|
|
46103
46640
|
init_client2();
|
|
@@ -46126,6 +46663,7 @@ var init_tools = __esm(() => {
|
|
|
46126
46663
|
TOOL_NAMES = [
|
|
46127
46664
|
"candle_launch_token",
|
|
46128
46665
|
"candle_get_market",
|
|
46666
|
+
"candle_quote",
|
|
46129
46667
|
"candle_get_feed",
|
|
46130
46668
|
"candle_vamp_candidates",
|
|
46131
46669
|
"candle_token_events",
|
|
@@ -46169,6 +46707,8 @@ var init_tools = __esm(() => {
|
|
|
46169
46707
|
"candle_transfer",
|
|
46170
46708
|
"candle_sweep",
|
|
46171
46709
|
"candle_get_wallets",
|
|
46710
|
+
"candle_get_trading_wallets",
|
|
46711
|
+
"candle_get_spendable_balances",
|
|
46172
46712
|
"candle_get_profile_wallets",
|
|
46173
46713
|
"candle_set_profile_wallets",
|
|
46174
46714
|
"candle_get_profile_pnl",
|
|
@@ -46232,6 +46772,15 @@ var init_tools = __esm(() => {
|
|
|
46232
46772
|
chain: exports_external.string().describe('"solana" or "hood"'),
|
|
46233
46773
|
mint: exports_external.string().describe("Token mint (solana) or contract address (hood)")
|
|
46234
46774
|
};
|
|
46775
|
+
quoteShape = {
|
|
46776
|
+
chain: exports_external.enum(["solana", "hood"]),
|
|
46777
|
+
mint: exports_external.string().describe("Token mint (solana) or contract address (hood)"),
|
|
46778
|
+
side: exports_external.enum(["buy", "sell"]),
|
|
46779
|
+
amountRaw: exports_external.string().describe("Positive integer in base units: the quote asset's for a buy, the token's for a sell"),
|
|
46780
|
+
quoteAsset: exports_external.string().optional().describe('Asset to price in, e.g. "sol" or "usdc" on Solana, "eth" or "usdg" on Hood'),
|
|
46781
|
+
poolKey: exports_external.unknown().optional().describe("Hood only: a pool hint for a pool nobody has indexed yet"),
|
|
46782
|
+
includeRoundTrip: exports_external.boolean().optional().describe("Hood only: also return the buy-then-sell cost in basis points")
|
|
46783
|
+
};
|
|
46235
46784
|
priceHistoryShape = {
|
|
46236
46785
|
chain: exports_external.string().optional().describe('"solana" (the default). Hood is refused with the REST route that serves it'),
|
|
46237
46786
|
mint: exports_external.string().describe("Token mint"),
|
|
@@ -46273,7 +46822,7 @@ var init_tools = __esm(() => {
|
|
|
46273
46822
|
return /^[0-9]+$/.test(trimmed) ? Number(trimmed) : value;
|
|
46274
46823
|
}, exports_external.number({ invalid_type_error: FEED_LIMIT_MESSAGE }).int(FEED_LIMIT_MESSAGE).min(1, FEED_LIMIT_MESSAGE).max(200, FEED_LIMIT_MESSAGE).optional().describe("Max rows to return, an integer from 1 to 200."));
|
|
46275
46824
|
getFeedShape = {
|
|
46276
|
-
bucket: exports_external.enum(["new", "graduated", "onfire", "bluechip", "finalstretch"]).describe(
|
|
46825
|
+
bucket: exports_external.enum(["new", "graduated", "onfire", "bluechip", "finalstretch"]).describe('finalstretch: pump.fun tokens at 50% or more of their bonding curve, most complete first, at most the 200 most complete (limit 200 reads all of them); no delta, read the whole list. Narrow it to tokens near graduation with where {"progress":{"gte":0.9}}. Where it is not switched on it is refused as an unknown bucket: use another bucket. bluechip is deprecated and will be removed.'),
|
|
46277
46826
|
chain: exports_external.string().optional().describe("Optional chain filter"),
|
|
46278
46827
|
where: exports_external.string().optional().describe('JSON filter, e.g. {"marketCap":{"lt":150000},"liquidityUsd":{"gte":25000},"mintAuthorityDisabled":{"eq":true}}. ' + "Comparators: eq, ne, lt, lte, gt, gte, present. An ABSENT field satisfies none of them except " + "present:false, so a filter for mintAuthorityDisabled eq true returns only tokens that actually say " + "true, never ones where the flag is simply missing. Use present:false to find the tokens with no data."),
|
|
46279
46828
|
sort: exports_external.string().optional().describe('Sort as "field" or "field:asc" / "field:desc". A bare field means desc.'),
|
|
@@ -46349,6 +46898,13 @@ var init_tools = __esm(() => {
|
|
|
46349
46898
|
resolveTokenShape = {
|
|
46350
46899
|
mint: exports_external.string().describe("Token mint (Solana, base58) or contract address (Hood, 0x-prefixed)")
|
|
46351
46900
|
};
|
|
46901
|
+
tradingWalletsShape = {
|
|
46902
|
+
cursor: exports_external.string().optional().describe("The continueCursor of the previous page. Omit for the first page")
|
|
46903
|
+
};
|
|
46904
|
+
spendableBalancesShape = {
|
|
46905
|
+
cursor: exports_external.string().optional().describe("The continueCursor of the previous page. Omit for the first page"),
|
|
46906
|
+
mints: exports_external.array(exports_external.string()).max(10).optional().describe("Up to 10 extra tokens to read beside the base assets: Solana mints or Hood (0x) token addresses. " + "Each is read on the wallets of its own chain")
|
|
46907
|
+
};
|
|
46352
46908
|
profileWalletsShape = {
|
|
46353
46909
|
keyPrefix: exports_external.string().describe("The profile's API key prefix, as listed by candle keys list or in the dashboard")
|
|
46354
46910
|
};
|
|
@@ -76191,7 +76747,7 @@ var HELP = {
|
|
|
76191
76747
|
group: "Trade",
|
|
76192
76748
|
intro: "Quote, swap and bridge on Solana or Hood; read an operation",
|
|
76193
76749
|
summary: "Quote, confirm and swap on Solana or Hood, bridge between them; read an operation by id",
|
|
76194
|
-
description: "Swaps run through a TEE wallet's bound key: the quote is shown and confirmed before anything is sent. The first buy after a launch is this command, unless the launch carried it with candle launch --buy. The assets decide the chain (ETH, USDG or a 0x token is Hood; SOL, USDC, CNDL or a mint is Solana) and a named wallet must be on it. A Hood TEE wallet signs one leg at a time: approve, Permit2, trade, then the fee, each only after the one before it landed. SOL or USDC to ETH or USDG, or back, is a bridge through Relay from a TEE wallet into this key's own TEE wallet on the other chain (--to names it when there are several); Candle charges no fee, only configured raw caps and USD limits apply (unset means unlimited, zero still binds), and this machine checks Relay's deposit before it is signed. The bridge prints its deposit and returns while Relay fills; --wait follows it for up to ten minutes. While it is open, a sweep of either wallet refuses BRIDGE_IN_FLIGHT for two hours. Vault to vault: fund the Solana TEE wallet from the vault, bridge, then disable and sweep the Hood TEE wallet into its vault. Every plan can trade Candle-launched tokens and base assets, and buy or sell a token that was not launched on Candle. Each trade pays the plan's agent fee on top; a base-pair swap or a bridge between this key's own wallets pays none. candle plans shows the fees in force. With --json, stdout is one JSON value for every swap, trade, replay and status read: kind, status, clientTradeId, signature (the primary transaction, or null), signatures (a list) and raw (the original response); every human line is on stderr. The keys older versions printed stay beside them.",
|
|
76750
|
+
description: "Swaps run through a TEE wallet's bound key: the quote is shown and confirmed before anything is sent. The first buy after a launch is this command, unless the launch carried it with candle launch --buy. The assets decide the chain (ETH, USDG or a 0x token is Hood; SOL, USDC, CNDL or a mint is Solana) and a named wallet must be on it. A Hood TEE wallet signs one leg at a time: approve, Permit2, trade, then the fee, each only after the one before it landed. SOL or USDC to ETH or USDG, or back, is a bridge through Relay from a TEE wallet into this key's own TEE wallet on the other chain (--to names it when there are several); Candle charges no fee, only configured raw caps and USD limits apply (unset means unlimited, zero still binds), and this machine checks Relay's deposit before it is signed. The bridge prints its deposit and returns while Relay fills; --wait follows it for up to ten minutes. While it is open, a sweep of either wallet refuses BRIDGE_IN_FLIGHT for two hours. Vault to vault: fund the Solana TEE wallet from the vault, bridge, then disable and sweep the Hood TEE wallet into its vault. Every plan can trade Candle-launched tokens and base assets, and buy or sell a token that was not launched on Candle. Each trade pays the plan's agent fee on top; a base-pair swap or a bridge between this key's own wallets pays none. candle plans shows the fees in force. With --dry-run, stdout reports per-check results and the quote; nothing is signed or sent. Otherwise with --json, stdout is one JSON value for every swap, trade, replay and status read: kind, status, clientTradeId, signature (the primary transaction, or null), signatures (a list) and raw (the original response); every human line is on stderr. The keys older versions printed stay beside them.",
|
|
76195
76751
|
usage: ["candle swap <from> <to> [flags]", "candle swap status <id>"],
|
|
76196
76752
|
rows: [
|
|
76197
76753
|
{
|
|
@@ -76203,7 +76759,14 @@ var HELP = {
|
|
|
76203
76759
|
description: "Read an operation without resending it; --wait follows a bridge until it ends"
|
|
76204
76760
|
}
|
|
76205
76761
|
],
|
|
76762
|
+
flags: [
|
|
76763
|
+
{
|
|
76764
|
+
invocation: "--dry-run",
|
|
76765
|
+
description: "Check a Solana TEE payer, build the quote and check spend, then stop. Never signs, sends, prompts or saves an operation. Refuses --yes, --percent and --client-trade-id; embedded, Hood and bridge payers are unsupported"
|
|
76766
|
+
}
|
|
76767
|
+
],
|
|
76206
76768
|
examples: [
|
|
76769
|
+
"candle swap SOL USDC --amount 0.001 --wallet AgentOne --dry-run --json",
|
|
76207
76770
|
"candle swap SOL USDC --amount 0.5 --wallet AgentOne",
|
|
76208
76771
|
"candle swap USDC SOL --percent 100 --wallet AgentOne",
|
|
76209
76772
|
"candle swap ETH USDG --amount 0.05 --wallet HoodOne",
|
|
@@ -76504,9 +77067,10 @@ var HELP = {
|
|
|
76504
77067
|
group: "Trade",
|
|
76505
77068
|
intro: "Move funds out of a TEE wallet to your wallets or its vault",
|
|
76506
77069
|
summary: "Move funds out of a TEE wallet to your own wallets or its vault, through its bound key",
|
|
76507
|
-
description: "Moves one asset out of a TEE wallet this machine can sign for. The bound key must be a Withdraw key (transfer:bound); from that wallet, Candle allows only its own pinned vault or another of the account's wallets you linked while signed in or marked trusted, and, where the server has TEE_WITHDRAWALS_ENABLED on, an address on the account's withdrawal allowlist (named by its label, id or address). To a linked wallet or an approved address only configured raw caps and USD limits apply (unset means unlimited, zero still binds), and the asset must be a base asset; to the vault any token and max are allowed. Candle builds the transaction, this machine approves the relay, Privy signs, Candle broadcasts. The destination and its kind are shown before you confirm. The asset decides the chain: ETH, USDG or --token is Hood, from a Hood TEE wallet, where this machine checks the one leg moves exactly what you confirmed before it is signed, and ETH max leaves the gas reserve.",
|
|
77070
|
+
description: "Moves one asset out of a TEE wallet this machine can sign for. The bound key must be a Withdraw key (transfer:bound); from that wallet, Candle allows only its own pinned vault or another of the account's wallets you linked while signed in or marked trusted, and, where the server has TEE_WITHDRAWALS_ENABLED on, an address on the account's withdrawal allowlist (named by its label, id or address). To a linked wallet or an approved address only configured raw caps and USD limits apply (unset means unlimited, zero still binds), and the asset must be a base asset; to the vault any token and max are allowed. Candle builds the transaction, this machine approves the relay, Privy signs, Candle broadcasts. The destination and its kind are shown before you confirm. candle transfer status <id> reads a transfer by its id or your --client-id: built, expired, submitted with its signature, unconfirmed (check the chain or candle activity before using a new client id), or failed (kept for 24 hours; older transfers are in candle activity). The asset decides the chain: ETH, USDG or --token is Hood, from a Hood TEE wallet, where this machine checks the one leg moves exactly what you confirmed before it is signed, and ETH max leaves the gas reserve.",
|
|
76508
77071
|
usage: [
|
|
76509
|
-
"candle transfer --to <address|wallet name|allowlist label|vault> --asset <SOL|USDC|CNDL|ETH|USDG>|--mint <mint>|--token <0x...> --amount <decimal|max> [--wallet <tee>] [--yes] [--json]"
|
|
77072
|
+
"candle transfer --to <address|wallet name|allowlist label|vault> --asset <SOL|USDC|CNDL|ETH|USDG>|--mint <mint>|--token <0x...> --amount <decimal|max> [--wallet <tee>] [--client-id <id>] [--yes] [--json]",
|
|
77073
|
+
"candle transfer status <transfer id|client id> [--json]"
|
|
76510
77074
|
],
|
|
76511
77075
|
rows: [],
|
|
76512
77076
|
flags: [
|
|
@@ -76526,10 +77090,15 @@ var HELP = {
|
|
|
76526
77090
|
invocation: "--rpc-url <url>",
|
|
76527
77091
|
description: "Your own Solana RPC, or on Hood the EVM RPC a --token's decimals are read over (optional)"
|
|
76528
77092
|
},
|
|
77093
|
+
{
|
|
77094
|
+
invocation: "--client-id <id>",
|
|
77095
|
+
description: "Your own id for this transfer (1 to 128 of letters, digits, . _ : -). An id already used for a built or sent transfer is refused with IDEMPOTENCY_CONFLICT naming that transfer, so a retry never sends twice; read it with candle transfer status <id>"
|
|
77096
|
+
},
|
|
76529
77097
|
{ invocation: "--yes", description: "Skip the confirmation prompt (the destination is still printed)" }
|
|
76530
77098
|
],
|
|
76531
77099
|
examples: [
|
|
76532
|
-
"candle transfer --to vault --asset SOL --amount max --wallet AgentOne",
|
|
77100
|
+
"candle transfer --to vault --asset SOL --amount max --wallet AgentOne --client-id sweep-2026-11-07",
|
|
77101
|
+
"candle transfer status sweep-2026-11-07",
|
|
76533
77102
|
"candle transfer --to treasury --asset USDC --amount 250 --wallet AgentOne --yes --json",
|
|
76534
77103
|
"candle transfer --to vault --asset ETH --amount max --wallet HoodAgent"
|
|
76535
77104
|
],
|
|
@@ -77328,6 +77897,10 @@ var HELP = {
|
|
|
77328
77897
|
description: "A dedicated, capped Solana wallet for one agent. The key is generated and sealed on this machine; enable delegates it to this profile's API key with a sweep vault pinned, and sweep moves everything back to that vault, signed locally.",
|
|
77329
77898
|
usage: ["candle tee <subcommand> [flags]"],
|
|
77330
77899
|
rows: [
|
|
77900
|
+
{
|
|
77901
|
+
invocation: "check <address|id|label>... [--label-prefix <p>] [--sign] [--max <n>] [--rpc-url <url>]",
|
|
77902
|
+
description: "Check payable membership, record parity and this machine's signer without spending. --sign builds a tiny SOL/USDC quote, checks spend, signs and simulates; never broadcasts (default cap 10). Needs swap:write"
|
|
77903
|
+
},
|
|
77331
77904
|
{
|
|
77332
77905
|
invocation: "new [--label <name>]",
|
|
77333
77906
|
description: "Add a Solana key to an existing tee-wallets.enc. With no store, refuses: candle vault new-key --chain solana, then candle vault promote --from <label> --to-key <prefix>. --factor does not apply"
|
|
@@ -77390,6 +77963,8 @@ var HELP = {
|
|
|
77390
77963
|
}
|
|
77391
77964
|
],
|
|
77392
77965
|
examples: [
|
|
77966
|
+
"candle tee check --label-prefix pool- --json",
|
|
77967
|
+
"candle tee check AgentOne --sign --rpc-url <url>",
|
|
77393
77968
|
"candle tee new --label AgentOne",
|
|
77394
77969
|
"candle tee status AgentOneAddress",
|
|
77395
77970
|
"candle tee sweep AgentOneAddress",
|
|
@@ -77956,13 +78531,14 @@ class AgentProfiles {
|
|
|
77956
78531
|
// src/core/cards.ts
|
|
77957
78532
|
init_render();
|
|
77958
78533
|
init_canonical_json();
|
|
77959
|
-
import { createHash as createHash3, randomBytes as randomBytes5, randomUUID as
|
|
78534
|
+
import { createHash as createHash3, randomBytes as randomBytes5, randomUUID as randomUUID3 } from "node:crypto";
|
|
77960
78535
|
|
|
77961
78536
|
// src/core/classify.ts
|
|
77962
78537
|
var READ = { class: "read" };
|
|
77963
78538
|
var card = (kind, idKey = "coreOpId") => ({ class: "card", kind, idKey });
|
|
77964
78539
|
var TOOL_CLASSES = {
|
|
77965
78540
|
candle_get_market: READ,
|
|
78541
|
+
candle_quote: READ,
|
|
77966
78542
|
candle_get_feed: READ,
|
|
77967
78543
|
candle_vamp_candidates: READ,
|
|
77968
78544
|
candle_token_events: READ,
|
|
@@ -77986,6 +78562,8 @@ var TOOL_CLASSES = {
|
|
|
77986
78562
|
candle_get_agent_positions: READ,
|
|
77987
78563
|
candle_get_plans: READ,
|
|
77988
78564
|
candle_get_wallets: READ,
|
|
78565
|
+
candle_get_trading_wallets: READ,
|
|
78566
|
+
candle_get_spendable_balances: READ,
|
|
77989
78567
|
candle_get_profile_wallets: READ,
|
|
77990
78568
|
candle_get_profile_pnl: READ,
|
|
77991
78569
|
candle_get_profile_trades: READ,
|
|
@@ -79820,8 +80398,8 @@ class CardBook {
|
|
|
79820
80398
|
if (idKey === "clientId")
|
|
79821
80399
|
return `0x${randomBytes5(16).toString("hex")}`;
|
|
79822
80400
|
if (idKey === "clientDepositId")
|
|
79823
|
-
return `deposit-${
|
|
79824
|
-
return
|
|
80401
|
+
return `deposit-${randomUUID3()}`;
|
|
80402
|
+
return randomUUID3();
|
|
79825
80403
|
}
|
|
79826
80404
|
view(card2) {
|
|
79827
80405
|
return JSON.parse(JSON.stringify(card2));
|
|
@@ -80680,18 +81258,18 @@ async function readJournal(path) {
|
|
|
80680
81258
|
const tornTail = text2.length > 0 && !text2.endsWith(`
|
|
80681
81259
|
`);
|
|
80682
81260
|
const rows = [];
|
|
80683
|
-
let
|
|
81261
|
+
let skipped2 = 0;
|
|
80684
81262
|
for (const line of text2.split(`
|
|
80685
81263
|
`)) {
|
|
80686
81264
|
if (line.trim() === "")
|
|
80687
81265
|
continue;
|
|
80688
81266
|
const row2 = parseRow(line);
|
|
80689
81267
|
if (row2 === undefined)
|
|
80690
|
-
|
|
81268
|
+
skipped2++;
|
|
80691
81269
|
else
|
|
80692
81270
|
rows.push(row2);
|
|
80693
81271
|
}
|
|
80694
|
-
return { rows, skipped, tornTail };
|
|
81272
|
+
return { rows, skipped: skipped2, tornTail };
|
|
80695
81273
|
}
|
|
80696
81274
|
function parseRow(line) {
|
|
80697
81275
|
let value;
|
|
@@ -80752,10 +81330,10 @@ function registerLaunchHandlers(server, deps) {
|
|
|
80752
81330
|
server.handle("launch.dryRun", async (params, { client }) => deps.cards.dryRun({ cardId: params.cardId, factor: parseFactor(params.factor) }, client.clientId));
|
|
80753
81331
|
}
|
|
80754
81332
|
function coded(error, fallback) {
|
|
80755
|
-
const
|
|
80756
|
-
if (
|
|
81333
|
+
const failure3 = error;
|
|
81334
|
+
if (failure3 !== null && typeof failure3 === "object" && "rpcCode" in failure3)
|
|
80757
81335
|
return error;
|
|
80758
|
-
return refused(typeof
|
|
81336
|
+
return refused(typeof failure3?.code === "string" ? failure3.code : fallback, typeof failure3?.message === "string" ? failure3.message : String(error));
|
|
80759
81337
|
}
|
|
80760
81338
|
|
|
80761
81339
|
// src/commands/core.ts
|
|
@@ -81159,7 +81737,7 @@ ${text2}` : text2;
|
|
|
81159
81737
|
const sleep = this.deps.sleep ?? defaultSleep;
|
|
81160
81738
|
let tooMany = 0;
|
|
81161
81739
|
let lost = 0;
|
|
81162
|
-
let
|
|
81740
|
+
let failure3;
|
|
81163
81741
|
while (!poll.stopped) {
|
|
81164
81742
|
const answer2 = await client.events(poll.turnId, poll.next, { signal: poll.abort.signal });
|
|
81165
81743
|
if (poll.stopped)
|
|
@@ -81194,7 +81772,7 @@ ${text2}` : text2;
|
|
|
81194
81772
|
if (refusal2.code === "too_many_polls") {
|
|
81195
81773
|
tooMany++;
|
|
81196
81774
|
if (tooMany > TOO_MANY_POLLS_RETRIES) {
|
|
81197
|
-
|
|
81775
|
+
failure3 = "too_many_polls";
|
|
81198
81776
|
break;
|
|
81199
81777
|
}
|
|
81200
81778
|
await sleep(TOO_MANY_POLLS_WAIT_MS);
|
|
@@ -81203,13 +81781,13 @@ ${text2}` : text2;
|
|
|
81203
81781
|
if (refusal2.shape === "transport") {
|
|
81204
81782
|
lost++;
|
|
81205
81783
|
if (lost > LOST_POLL_RETRIES) {
|
|
81206
|
-
|
|
81784
|
+
failure3 = "QUANT_UNREACHABLE";
|
|
81207
81785
|
break;
|
|
81208
81786
|
}
|
|
81209
81787
|
await sleep(Math.min(1000 * lost, 5000));
|
|
81210
81788
|
continue;
|
|
81211
81789
|
}
|
|
81212
|
-
|
|
81790
|
+
failure3 = refusal2.code;
|
|
81213
81791
|
break;
|
|
81214
81792
|
}
|
|
81215
81793
|
if (this.poll !== poll)
|
|
@@ -81220,11 +81798,11 @@ ${text2}` : text2;
|
|
|
81220
81798
|
this.ended.delete(this.ended.values().next().value);
|
|
81221
81799
|
if (this.turn?.turnId === poll.turnId)
|
|
81222
81800
|
this.turn.running = false;
|
|
81223
|
-
if (
|
|
81801
|
+
if (failure3 !== undefined) {
|
|
81224
81802
|
this.publish({
|
|
81225
81803
|
turnId: poll.turnId,
|
|
81226
81804
|
kind: "poll_failed",
|
|
81227
|
-
data: { code:
|
|
81805
|
+
data: { code: failure3 },
|
|
81228
81806
|
seq: poll.next,
|
|
81229
81807
|
origin: poll.origin
|
|
81230
81808
|
});
|
|
@@ -83261,8 +83839,8 @@ async function prepare(params, call2, deps) {
|
|
|
83261
83839
|
try {
|
|
83262
83840
|
preview = await deps.reads.previewVault(parsed, wallet3);
|
|
83263
83841
|
} catch (error) {
|
|
83264
|
-
const
|
|
83265
|
-
throw refused(typeof
|
|
83842
|
+
const failure3 = error;
|
|
83843
|
+
throw refused(typeof failure3.code === "string" ? failure3.code : "TRANSFER_PREVIEW_FAILED", `${terminalText(typeof failure3.message === "string" ? failure3.message : String(error))} Nothing was prepared.`);
|
|
83266
83844
|
}
|
|
83267
83845
|
} else {
|
|
83268
83846
|
const withdraw = deps.trading.withdrawState();
|
|
@@ -84636,8 +85214,8 @@ async function readTargetSigner(ctx, deviceToken, keyPrefix) {
|
|
|
84636
85214
|
}
|
|
84637
85215
|
async function preflightToKey(ctx, raw, opts) {
|
|
84638
85216
|
const { deps, json } = ctx;
|
|
84639
|
-
const refuse3 = (
|
|
84640
|
-
writeLocalFailure(deps,
|
|
85217
|
+
const refuse3 = (failure3) => {
|
|
85218
|
+
writeLocalFailure(deps, failure3, json);
|
|
84641
85219
|
return { ok: false, exit: 1 };
|
|
84642
85220
|
};
|
|
84643
85221
|
const deviceToken = await resolveDeviceToken(deps, ctx.profile);
|
|
@@ -84789,8 +85367,8 @@ async function rebindPromoted(ctx, deviceToken, toKeyPrefix, wallets2, pinnedSpk
|
|
|
84789
85367
|
if (!committed.ok) {
|
|
84790
85368
|
run2.ok = false;
|
|
84791
85369
|
if ("missing" in committed) {
|
|
84792
|
-
const
|
|
84793
|
-
run2.failure = { ...
|
|
85370
|
+
const failure3 = missingSignerFailure(committed.missing);
|
|
85371
|
+
run2.failure = { ...failure3, status: 409, chunk: at2 + 1 };
|
|
84794
85372
|
} else if ("changed" in committed) {
|
|
84795
85373
|
run2.failure = { ...committed.changed, status: 409, chunk: at2 + 1 };
|
|
84796
85374
|
} else {
|
|
@@ -85574,8 +86152,8 @@ async function moveToLiveKey(ctx, deviceToken, source, toKeyRaw) {
|
|
|
85574
86152
|
for (const chunk of chunkWallets(ids)) {
|
|
85575
86153
|
const preview = await postRebind(ctx, deviceToken, { dryRun: true, toKeyPrefix: target.keyPrefix, wallets: chunk });
|
|
85576
86154
|
if (!preview.ok) {
|
|
85577
|
-
const
|
|
85578
|
-
writeScreenLocalFailure(deps,
|
|
86155
|
+
const failure3 = rebindFailureDetails(preview, {});
|
|
86156
|
+
writeScreenLocalFailure(deps, failure3, json);
|
|
85579
86157
|
return 1;
|
|
85580
86158
|
}
|
|
85581
86159
|
const shown = preview.body;
|
|
@@ -90489,7 +91067,7 @@ init_secret_store();
|
|
|
90489
91067
|
init_key_output();
|
|
90490
91068
|
init_keys();
|
|
90491
91069
|
init_tee_rebind();
|
|
90492
|
-
import { randomUUID as
|
|
91070
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
90493
91071
|
|
|
90494
91072
|
// src/commands/token-pages.ts
|
|
90495
91073
|
init_esm();
|
|
@@ -90499,7 +91077,7 @@ init_deps();
|
|
|
90499
91077
|
init_human_output();
|
|
90500
91078
|
init_presentation();
|
|
90501
91079
|
init_render();
|
|
90502
|
-
import { createHash as createHash5, randomUUID as
|
|
91080
|
+
import { createHash as createHash5, randomUUID as randomUUID7 } from "node:crypto";
|
|
90503
91081
|
var EXIT_OUTCOME_UNKNOWN = 3;
|
|
90504
91082
|
var EXIT_REVISION_CONFLICT = 4;
|
|
90505
91083
|
var TOKEN_PAGE_LIMITS = { thesis: 280, tokenomics: 5000, lore: 5000, update: 1000, comment: 500 };
|
|
@@ -90582,8 +91160,8 @@ function writeApiFailure(ctx, result, authType) {
|
|
|
90582
91160
|
writeRefusal(ctx, refusal3);
|
|
90583
91161
|
return 1;
|
|
90584
91162
|
}
|
|
90585
|
-
const
|
|
90586
|
-
writeFailure(ctx.deps,
|
|
91163
|
+
const failure3 = result;
|
|
91164
|
+
writeFailure(ctx.deps, failure3.status === 404 ? { ...failure3, message: REFUSALS.not_found } : failure3, { apiUrl: ctx.apiUrl, authType }, ctx.json);
|
|
90587
91165
|
return 1;
|
|
90588
91166
|
}
|
|
90589
91167
|
function dataOf(result) {
|
|
@@ -90646,7 +91224,7 @@ function writeUnknown(ctx, idempotencyKey) {
|
|
|
90646
91224
|
function idempotencyKeyOf(values, usage4) {
|
|
90647
91225
|
const given2 = values["--idempotency-key"];
|
|
90648
91226
|
if (given2 === undefined)
|
|
90649
|
-
return
|
|
91227
|
+
return randomUUID7();
|
|
90650
91228
|
if (given2.length < 1 || given2.length > 128)
|
|
90651
91229
|
return usage4("--idempotency-key must be 1 to 128 characters.");
|
|
90652
91230
|
return given2;
|
|
@@ -91362,7 +91940,7 @@ async function keysRotate(args, ctx) {
|
|
|
91362
91940
|
writeScreenUsageFailure(deps, "--idempotency-key must be 1 to 128 characters.", json);
|
|
91363
91941
|
return 2;
|
|
91364
91942
|
}
|
|
91365
|
-
const idempotencyKey = keyFlag ??
|
|
91943
|
+
const idempotencyKey = keyFlag ?? randomUUID8();
|
|
91366
91944
|
let graceSeconds = now ? 0 : DEFAULT_GRACE_SECONDS;
|
|
91367
91945
|
if (graceFlag !== undefined) {
|
|
91368
91946
|
const grace = parseGrace(graceFlag);
|
|
@@ -91920,7 +92498,7 @@ async function client(ctx) {
|
|
|
91920
92498
|
}, ctx.json);
|
|
91921
92499
|
}
|
|
91922
92500
|
}
|
|
91923
|
-
function
|
|
92501
|
+
function failure3(answer2, ctx, statusRoute = false) {
|
|
91924
92502
|
const { refusal: refusal3 } = answer2;
|
|
91925
92503
|
if (ctx.json && refusal3.body !== undefined)
|
|
91926
92504
|
ctx.deps.stdout.write(`${JSON.stringify(refusal3.body)}
|
|
@@ -91988,7 +92566,7 @@ async function labStatus(args, ctx) {
|
|
|
91988
92566
|
return 1;
|
|
91989
92567
|
const answer2 = await connected.lab.status();
|
|
91990
92568
|
if (!answer2.ok)
|
|
91991
|
-
return
|
|
92569
|
+
return failure3(answer2, ctx, true);
|
|
91992
92570
|
if (ctx.json)
|
|
91993
92571
|
ctx.deps.stdout.write(`${JSON.stringify(answer2.body)}
|
|
91994
92572
|
`);
|
|
@@ -92033,7 +92611,7 @@ async function labReplay(args, ctx) {
|
|
|
92033
92611
|
const id = parsed2.positionals[0];
|
|
92034
92612
|
const answer2 = action === "get" ? await connected2.lab.replayGet(id) : await connected2.lab.replayCancel(id);
|
|
92035
92613
|
if (!answer2.ok)
|
|
92036
|
-
return
|
|
92614
|
+
return failure3(answer2, ctx);
|
|
92037
92615
|
if (action === "get")
|
|
92038
92616
|
return replayOutput(answer2.body, ctx);
|
|
92039
92617
|
if (ctx.json)
|
|
@@ -92099,7 +92677,7 @@ ${REPLAY_USAGE}`);
|
|
|
92099
92677
|
};
|
|
92100
92678
|
const started = await connected.lab.replay(body);
|
|
92101
92679
|
if (!started.ok)
|
|
92102
|
-
return
|
|
92680
|
+
return failure3(started, ctx);
|
|
92103
92681
|
if (parsed.booleans.has("--no-wait")) {
|
|
92104
92682
|
if (ctx.json)
|
|
92105
92683
|
ctx.deps.stdout.write(`${JSON.stringify(started.body)}
|
|
@@ -92109,7 +92687,7 @@ ${REPLAY_USAGE}`);
|
|
|
92109
92687
|
return 0;
|
|
92110
92688
|
}
|
|
92111
92689
|
const result = await connected.lab.waitForReplay(started.body.job_id);
|
|
92112
|
-
return result.ok ? replayOutput(result.body, ctx) :
|
|
92690
|
+
return result.ok ? replayOutput(result.body, ctx) : failure3(result, ctx);
|
|
92113
92691
|
}
|
|
92114
92692
|
async function report(args, ctx, kind) {
|
|
92115
92693
|
const flags = kind === "runners" ? ["--hours", "--min-wallets"] : ["--days", "--min-n"];
|
|
@@ -92134,7 +92712,7 @@ async function report(args, ctx, kind) {
|
|
|
92134
92712
|
return 1;
|
|
92135
92713
|
const answer2 = kind === "runners" ? await connected.lab.runners(q) : await connected.lab.edges(q);
|
|
92136
92714
|
if (!answer2.ok)
|
|
92137
|
-
return
|
|
92715
|
+
return failure3(answer2, ctx);
|
|
92138
92716
|
if (ctx.json)
|
|
92139
92717
|
ctx.deps.stdout.write(`${JSON.stringify(answer2.body)}
|
|
92140
92718
|
`);
|
|
@@ -92172,7 +92750,7 @@ init_zod();
|
|
|
92172
92750
|
init_args();
|
|
92173
92751
|
init_client();
|
|
92174
92752
|
init_evm_lite();
|
|
92175
|
-
import { randomUUID as
|
|
92753
|
+
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
92176
92754
|
|
|
92177
92755
|
// src/fee-wallet.ts
|
|
92178
92756
|
init_solana_lite();
|
|
@@ -92727,7 +93305,7 @@ async function launch(args, ctx) {
|
|
|
92727
93305
|
const flags = parsed.values;
|
|
92728
93306
|
if (flags["--quote-asset"])
|
|
92729
93307
|
flags["--quote-asset"] = flags["--quote-asset"].toLowerCase();
|
|
92730
|
-
const id = flags["--client-trade-id"] ?? `launch-${
|
|
93308
|
+
const id = flags["--client-trade-id"] ?? `launch-${randomUUID9()}`;
|
|
92731
93309
|
const embedded = EMBEDDED_WALLET_NAMES.has(flags["--wallet"] ?? "");
|
|
92732
93310
|
const dryRun = parsed.booleans.has("--dry-run");
|
|
92733
93311
|
const check = parsed.booleans.has("--check");
|
|
@@ -93716,7 +94294,7 @@ init_render();
|
|
|
93716
94294
|
init_solana_endpoint();
|
|
93717
94295
|
init_solana_lite();
|
|
93718
94296
|
init_trading();
|
|
93719
|
-
import { randomUUID as
|
|
94297
|
+
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
93720
94298
|
init_swap();
|
|
93721
94299
|
var LP_SCOPE = "lp:write";
|
|
93722
94300
|
var CONFIRM_POLL_MS3 = 2000;
|
|
@@ -94019,7 +94597,7 @@ async function lpAdd(args, ctx) {
|
|
|
94019
94597
|
if ("error" in parsed)
|
|
94020
94598
|
return usage5(ctx, parsed.error);
|
|
94021
94599
|
const flags = parsed.values;
|
|
94022
|
-
const id = flags["--client-trade-id"] ?? `lp-${
|
|
94600
|
+
const id = flags["--client-trade-id"] ?? `lp-${randomUUID10()}`;
|
|
94023
94601
|
if (parsed.positionals.length !== 2 || !flags["--amount"] || !flags["--wallet"] || !validClientId(id))
|
|
94024
94602
|
return usage5(ctx, "Usage: candle lp add <pool> --amount <decimal> <token> --wallet <tee> [--position <nft-mint>] [--slippage-bps 100] [--client-trade-id <id>] [--rpc-url <url>] [--yes]");
|
|
94025
94603
|
try {
|
|
@@ -94055,7 +94633,7 @@ async function lpRemove(args, ctx) {
|
|
|
94055
94633
|
if ("error" in parsed)
|
|
94056
94634
|
return usage5(ctx, parsed.error);
|
|
94057
94635
|
const flags = parsed.values;
|
|
94058
|
-
const id = flags["--client-trade-id"] ?? `lp-${
|
|
94636
|
+
const id = flags["--client-trade-id"] ?? `lp-${randomUUID10()}`;
|
|
94059
94637
|
const percent2 = Number(flags["--percent"]);
|
|
94060
94638
|
if (parsed.positionals.length !== 1 || !flags["--percent"] || !/^\d+(\.\d{1,2})?$/.test(flags["--percent"]) || !(percent2 > 0 && percent2 <= 100) || !validClientId(id))
|
|
94061
94639
|
return usage5(ctx, "Usage: candle lp remove <position> --percent <0.01-100> [--wallet <tee>] [--slippage-bps 100] [--client-trade-id <id>] [--rpc-url <url>] [--yes]");
|
|
@@ -94087,7 +94665,7 @@ async function lpClaim(args, ctx) {
|
|
|
94087
94665
|
if ("error" in parsed)
|
|
94088
94666
|
return usage5(ctx, parsed.error);
|
|
94089
94667
|
const flags = parsed.values;
|
|
94090
|
-
const id = flags["--client-trade-id"] ?? `lp-${
|
|
94668
|
+
const id = flags["--client-trade-id"] ?? `lp-${randomUUID10()}`;
|
|
94091
94669
|
if (parsed.positionals.length !== 1 || !validClientId(id))
|
|
94092
94670
|
return usage5(ctx, "Usage: candle lp claim <position> [--wallet <tee>] [--client-trade-id <id>] [--rpc-url <url>] [--yes]");
|
|
94093
94671
|
try {
|
|
@@ -94121,6 +94699,7 @@ var MCP_TOOL_NAMES = [
|
|
|
94121
94699
|
"candle_launch_token",
|
|
94122
94700
|
"candle_launch_and_seed",
|
|
94123
94701
|
"candle_get_market",
|
|
94702
|
+
"candle_quote",
|
|
94124
94703
|
"candle_get_feed",
|
|
94125
94704
|
"candle_vamp_candidates",
|
|
94126
94705
|
"candle_token_events",
|
|
@@ -94162,6 +94741,8 @@ var MCP_TOOL_NAMES = [
|
|
|
94162
94741
|
"candle_transfer",
|
|
94163
94742
|
"candle_sweep",
|
|
94164
94743
|
"candle_get_wallets",
|
|
94744
|
+
"candle_get_trading_wallets",
|
|
94745
|
+
"candle_get_spendable_balances",
|
|
94165
94746
|
"candle_get_profile_wallets",
|
|
94166
94747
|
"candle_set_profile_wallets",
|
|
94167
94748
|
"candle_get_profile_pnl",
|
|
@@ -94338,7 +94919,7 @@ init_bridge();
|
|
|
94338
94919
|
init_client();
|
|
94339
94920
|
init_evm_lite();
|
|
94340
94921
|
init_human_output();
|
|
94341
|
-
import { randomUUID as
|
|
94922
|
+
import { randomUUID as randomUUID11, sign as sign4 } from "node:crypto";
|
|
94342
94923
|
|
|
94343
94924
|
// src/hyperliquid.ts
|
|
94344
94925
|
init_sha3();
|
|
@@ -98251,7 +98832,7 @@ async function perpsDeposit(args, ctx) {
|
|
|
98251
98832
|
if (parsed.positionals.length !== 2 || !amount || !DEC_RE3.test(amount) || !isDepositAsset(asset))
|
|
98252
98833
|
return usage8(ctx, DEPOSIT_USAGE);
|
|
98253
98834
|
const { values } = parsed;
|
|
98254
|
-
const id = values["--id"] ?? `deposit-${
|
|
98835
|
+
const id = values["--id"] ?? `deposit-${randomUUID11()}`;
|
|
98255
98836
|
if (!DEPOSIT_ID_RE.test(id))
|
|
98256
98837
|
return usage8(ctx, "--id must be 1 to 128 letters, digits, '.', '_', ':' or '-'.");
|
|
98257
98838
|
const slippageBps = Number(values["--slippage-bps"] ?? "100");
|
|
@@ -99390,8 +99971,8 @@ async function read2(ctx, argv, command) {
|
|
|
99390
99971
|
writeUsageFailure(ctx.deps, error.message, ctx.json);
|
|
99391
99972
|
return 2;
|
|
99392
99973
|
}
|
|
99393
|
-
const
|
|
99394
|
-
writeLocalFailure(ctx.deps,
|
|
99974
|
+
const failure4 = { code: error.code, message: error.message, retryable: error.retryable };
|
|
99975
|
+
writeLocalFailure(ctx.deps, failure4, ctx.json);
|
|
99395
99976
|
return 1;
|
|
99396
99977
|
}
|
|
99397
99978
|
return tradingFailure(ctx, error);
|
|
@@ -99405,7 +99986,7 @@ var perpsCandles = (argv, ctx) => read2(ctx, argv, "candles");
|
|
|
99405
99986
|
// src/commands/perps-withdraw.ts
|
|
99406
99987
|
init_args();
|
|
99407
99988
|
init_human_output();
|
|
99408
|
-
import { randomUUID as
|
|
99989
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
99409
99990
|
|
|
99410
99991
|
// src/hyperliquid-withdraw.ts
|
|
99411
99992
|
var CANDLE_PERPS_WITHDRAW_PINS = {
|
|
@@ -99649,7 +100230,7 @@ async function perpsWithdraw(args, ctx) {
|
|
|
99649
100230
|
const amountMicros = parseWithdrawAmount(amount);
|
|
99650
100231
|
if (amountMicros === null)
|
|
99651
100232
|
return usage10(ctx, "The amount must be a positive USDC decimal with at most 6 decimals.");
|
|
99652
|
-
const id = parsed.values["--id"] ?? `withdraw-${
|
|
100233
|
+
const id = parsed.values["--id"] ?? `withdraw-${randomUUID12()}`;
|
|
99653
100234
|
if (!ID_RE.test(id))
|
|
99654
100235
|
return usage10(ctx, "--id must be 1 to 128 letters, digits, '.', '_', ':' or '-'.");
|
|
99655
100236
|
if (!refuseEnvPassphrase(ctx))
|
|
@@ -99788,7 +100369,7 @@ init_render();
|
|
|
99788
100369
|
init_secret_store();
|
|
99789
100370
|
init_solana_lite();
|
|
99790
100371
|
init_errors2();
|
|
99791
|
-
import { generateKeyPairSync as generateKeyPairSync3, randomUUID as
|
|
100372
|
+
import { generateKeyPairSync as generateKeyPairSync3, randomUUID as randomUUID14 } from "node:crypto";
|
|
99792
100373
|
|
|
99793
100374
|
// src/x402/codec.ts
|
|
99794
100375
|
var PAYMENT_REQUIRED_HEADER = "PAYMENT-REQUIRED";
|
|
@@ -100398,7 +100979,7 @@ init_deps();
|
|
|
100398
100979
|
init_render();
|
|
100399
100980
|
init_solana_endpoint();
|
|
100400
100981
|
init_solana_lite();
|
|
100401
|
-
import { randomUUID as
|
|
100982
|
+
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
100402
100983
|
|
|
100403
100984
|
// src/x402/pay.ts
|
|
100404
100985
|
init_client();
|
|
@@ -100746,7 +101327,7 @@ async function runPaidRead(ctx, input) {
|
|
|
100746
101327
|
blockhash: blockhashFor(ctx),
|
|
100747
101328
|
env: ctx.deps.env,
|
|
100748
101329
|
now: ctx.deps.now,
|
|
100749
|
-
newId:
|
|
101330
|
+
newId: randomUUID13
|
|
100750
101331
|
});
|
|
100751
101332
|
const line = paymentLine(paid);
|
|
100752
101333
|
if (line && !ctx.json)
|
|
@@ -101358,7 +101939,7 @@ async function runPlanPayment(ctx, run2) {
|
|
|
101358
101939
|
const transaction = serializePartiallySigned(transfer, signer.sign(transfer.message));
|
|
101359
101940
|
const at3 = new Date(deps.now()).toISOString();
|
|
101360
101941
|
const pending = {
|
|
101361
|
-
id:
|
|
101942
|
+
id: randomUUID14(),
|
|
101362
101943
|
plan: run2.plan,
|
|
101363
101944
|
renew,
|
|
101364
101945
|
path,
|
|
@@ -101669,7 +102250,8 @@ var PLAN_CAPABILITY_LABELS2 = {
|
|
|
101669
102250
|
createLinkedWallets: "Create linked wallets",
|
|
101670
102251
|
importLinkedWallets: "Import linked wallets",
|
|
101671
102252
|
limitOrders: "Limit orders",
|
|
101672
|
-
quant: "Quant deep analyses (Max)"
|
|
102253
|
+
quant: "Quant deep analyses (Max)",
|
|
102254
|
+
strategyLab: "Strategy lab (saved strategies stay Max)"
|
|
101673
102255
|
};
|
|
101674
102256
|
var PLAN_LABELS2 = { free: "Free", believer: "Believer", pro: "Pro", max: "Max" };
|
|
101675
102257
|
function planLabel2(plan2) {
|
|
@@ -101698,6 +102280,9 @@ function planTableRows2(table) {
|
|
|
101698
102280
|
row2("Uploads per minute", (e) => count2(e.limits.uploadsPerMin)),
|
|
101699
102281
|
row2("Linked wallets", (e) => count2(e.limits.linkedWallets))
|
|
101700
102282
|
];
|
|
102283
|
+
if (plans.some((e) => typeof e.limits.labReplaysPerDay === "number")) {
|
|
102284
|
+
rows.push(row2("Strategy lab replays", (e) => typeof e.limits.labReplaysPerDay === "number" ? `${count2(e.limits.labReplaysPerDay)} a day` : "-"));
|
|
102285
|
+
}
|
|
101701
102286
|
const known = Object.keys(PLAN_CAPABILITY_LABELS2);
|
|
101702
102287
|
const served = new Set;
|
|
101703
102288
|
for (const entry of plans)
|
|
@@ -103280,12 +103865,12 @@ async function startEvmRead(addresses, rpcUrl, fetchFn) {
|
|
|
103280
103865
|
try {
|
|
103281
103866
|
chainId = await rpc.chainId();
|
|
103282
103867
|
} catch (error) {
|
|
103283
|
-
const
|
|
103868
|
+
const failure4 = message(error);
|
|
103284
103869
|
return {
|
|
103285
103870
|
balances: async () => ({
|
|
103286
103871
|
byAddress: new Map(unique.map((address) => [address, { wei: null, usdg: null }])),
|
|
103287
103872
|
unavailable: unique,
|
|
103288
|
-
failure:
|
|
103873
|
+
failure: failure4
|
|
103289
103874
|
})
|
|
103290
103875
|
};
|
|
103291
103876
|
}
|
|
@@ -103297,7 +103882,7 @@ async function startEvmRead(addresses, rpcUrl, fetchFn) {
|
|
|
103297
103882
|
return {
|
|
103298
103883
|
balances: async () => {
|
|
103299
103884
|
const byAddress = new Map;
|
|
103300
|
-
let
|
|
103885
|
+
let failure4;
|
|
103301
103886
|
let next = 0;
|
|
103302
103887
|
await Promise.all(Array.from({ length: Math.min(EVM_READS_IN_FLIGHT, unique.length) }, async () => {
|
|
103303
103888
|
while (next < unique.length) {
|
|
@@ -103306,7 +103891,7 @@ async function startEvmRead(addresses, rpcUrl, fetchFn) {
|
|
|
103306
103891
|
try {
|
|
103307
103892
|
return await call2();
|
|
103308
103893
|
} catch (error) {
|
|
103309
|
-
|
|
103894
|
+
failure4 ??= message(error);
|
|
103310
103895
|
return null;
|
|
103311
103896
|
}
|
|
103312
103897
|
};
|
|
@@ -103319,7 +103904,7 @@ async function startEvmRead(addresses, rpcUrl, fetchFn) {
|
|
|
103319
103904
|
const read3 = byAddress.get(address);
|
|
103320
103905
|
return read3 === undefined || read3.wei === null || read3.usdg === null;
|
|
103321
103906
|
});
|
|
103322
|
-
return { byAddress, unavailable: unavailable6, ...
|
|
103907
|
+
return { byAddress, unavailable: unavailable6, ...failure4 === undefined ? {} : { failure: failure4 } };
|
|
103323
103908
|
}
|
|
103324
103909
|
};
|
|
103325
103910
|
}
|
|
@@ -103328,13 +103913,13 @@ async function readOwnRpc(addresses, solana, ctx) {
|
|
|
103328
103913
|
const unique = [...new Set(addresses)];
|
|
103329
103914
|
const lamports = new Map;
|
|
103330
103915
|
let rateLimited = false;
|
|
103331
|
-
let
|
|
103916
|
+
let failure4;
|
|
103332
103917
|
const noteFailure = (error) => {
|
|
103333
103918
|
if (isRateLimited(error)) {
|
|
103334
103919
|
rateLimited = true;
|
|
103335
|
-
|
|
103920
|
+
failure4 ??= rateLimitedReadFailure(ctx, error);
|
|
103336
103921
|
} else
|
|
103337
|
-
|
|
103922
|
+
failure4 ??= describeRpcFailure(error);
|
|
103338
103923
|
};
|
|
103339
103924
|
for (let at3 = 0;at3 < unique.length; at3 += CHUNK) {
|
|
103340
103925
|
const chunk = unique.slice(at3, at3 + CHUNK);
|
|
@@ -103396,7 +103981,7 @@ async function readOwnRpc(addresses, solana, ctx) {
|
|
|
103396
103981
|
if (sol === null || held === null)
|
|
103397
103982
|
unavailable6.push(address);
|
|
103398
103983
|
}
|
|
103399
|
-
return { byAddress, unavailable: unavailable6, ...
|
|
103984
|
+
return { byAddress, unavailable: unavailable6, ...failure4 === undefined ? {} : { failure: failure4 } };
|
|
103400
103985
|
}
|
|
103401
103986
|
function solanaHoldings(read3, prices) {
|
|
103402
103987
|
if (read3 === undefined || read3.lamports === null && read3.tokens === null)
|
|
@@ -103826,17 +104411,17 @@ async function profileUse(args, ctx) {
|
|
|
103826
104411
|
let account = profile.account;
|
|
103827
104412
|
let username = profile.username;
|
|
103828
104413
|
if (apiKey) {
|
|
103829
|
-
const { account: live, username: liveUsername, failure:
|
|
104414
|
+
const { account: live, username: liveUsername, failure: failure4 } = await fetchAccount(deps, apiUrl, apiKey);
|
|
103830
104415
|
if (live) {
|
|
103831
104416
|
account = live;
|
|
103832
104417
|
username = liveUsername;
|
|
103833
104418
|
await deps.updateProfile(name2, { account: live, username: liveUsername, accountCachedAt: deps.now() });
|
|
103834
104419
|
} else {
|
|
103835
104420
|
if (json)
|
|
103836
|
-
notice(deps, "stderr", "warning", `Could not refresh the account for ${name2} (${
|
|
104421
|
+
notice(deps, "stderr", "warning", `Could not refresh the account for ${name2} (${failure4}); keeping the cached value.
|
|
103837
104422
|
`.trim());
|
|
103838
104423
|
else
|
|
103839
|
-
notice(deps, "stderr", "warning", `Could not refresh the account for ${name2} (${
|
|
104424
|
+
notice(deps, "stderr", "warning", `Could not refresh the account for ${name2} (${failure4}); keeping the cached value.`);
|
|
103840
104425
|
}
|
|
103841
104426
|
} else {
|
|
103842
104427
|
if (json)
|
|
@@ -104170,8 +104755,8 @@ async function quantAsk(args, ctx) {
|
|
|
104170
104755
|
if (turnId === undefined) {
|
|
104171
104756
|
if (reason === "interrupted")
|
|
104172
104757
|
return await stopped(ctx, client2, undefined, reason, timeoutS);
|
|
104173
|
-
const
|
|
104174
|
-
writeScreenLocalFailure(ctx.deps,
|
|
104758
|
+
const failure4 = refusal3?.status === 402 && offer ? offerFailure(offer.status, offer.headers, offer.body) : turnRefusal(refusal3 ?? { code: "BAD_RESPONSE", status: 0, shape: "transport" });
|
|
104759
|
+
writeScreenLocalFailure(ctx.deps, failure4, ctx.json);
|
|
104175
104760
|
return 1;
|
|
104176
104761
|
}
|
|
104177
104762
|
if (stop.signal.aborted) {
|
|
@@ -105303,7 +105888,7 @@ init_presentation();
|
|
|
105303
105888
|
init_render();
|
|
105304
105889
|
init_trading();
|
|
105305
105890
|
init_swap();
|
|
105306
|
-
import { randomUUID as
|
|
105891
|
+
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
105307
105892
|
var withdrawal = exports_external.object({ withdrawal: exports_external.string(), amountRaw: exports_external.string(), endTime: exports_external.string(), claimable: exports_external.boolean() }).passthrough();
|
|
105308
105893
|
var positionsSchema = exports_external.object({
|
|
105309
105894
|
success: exports_external.literal(true),
|
|
@@ -105358,7 +105943,7 @@ async function runStake(action, args, ctx) {
|
|
|
105358
105943
|
return 2;
|
|
105359
105944
|
}
|
|
105360
105945
|
const { values, positionals, booleans } = parsed;
|
|
105361
|
-
const id = values["--client-stake-id"] ?? `stake-${
|
|
105946
|
+
const id = values["--client-stake-id"] ?? `stake-${randomUUID15()}`;
|
|
105362
105947
|
const all = booleans.has("--all");
|
|
105363
105948
|
if (!validClientId(id) || id.length > 128 || (action === "positions" ? positionals.length !== 0 : action === "finish" ? positionals.length > 1 : action === "claim" ? all ? positionals.length !== 0 : positionals.length !== 1 : positionals.length !== 1)) {
|
|
105364
105949
|
writeUsageFailure(ctx.deps, usage12, ctx.json);
|
|
@@ -106448,7 +107033,7 @@ init_args();
|
|
|
106448
107033
|
init_client();
|
|
106449
107034
|
init_deps();
|
|
106450
107035
|
init_human_output();
|
|
106451
|
-
import { randomUUID as
|
|
107036
|
+
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
106452
107037
|
init_render();
|
|
106453
107038
|
var EXIT_OUTCOME_UNKNOWN2 = 3;
|
|
106454
107039
|
var EDIT_FIELDS = launchFlagFields.filter((field) => field.key === "description" || field.key.startsWith("socials."));
|
|
@@ -106574,7 +107159,7 @@ async function tokenEdit(args, ctx) {
|
|
|
106574
107159
|
if (given2 !== undefined && (given2.length < 1 || given2.length > 128)) {
|
|
106575
107160
|
return usage12("--idempotency-key must be 1 to 128 characters.");
|
|
106576
107161
|
}
|
|
106577
|
-
const idempotencyKey = given2 ??
|
|
107162
|
+
const idempotencyKey = given2 ?? randomUUID16();
|
|
106578
107163
|
const apiKey = await resolveApiKey(ctx.deps, ctx.profile);
|
|
106579
107164
|
if (!apiKey) {
|
|
106580
107165
|
writeLocalFailure(ctx.deps, {
|
|
@@ -106599,17 +107184,17 @@ async function tokenEdit(args, ctx) {
|
|
|
106599
107184
|
return EXIT_OUTCOME_UNKNOWN2;
|
|
106600
107185
|
}
|
|
106601
107186
|
if (!result.ok) {
|
|
106602
|
-
const
|
|
107187
|
+
const failure4 = { apiUrl: ctx.apiUrl, authType: "key" };
|
|
106603
107188
|
if (!unsettledCommit && !mayHaveCommitted(result)) {
|
|
106604
|
-
writeFailure(ctx.deps, result,
|
|
107189
|
+
writeFailure(ctx.deps, result, failure4, ctx.json);
|
|
106605
107190
|
return 1;
|
|
106606
107191
|
}
|
|
106607
107192
|
const retry = `Rerun the same command with --idempotency-key ${idempotencyKey}: Candle returns the first result instead of editing twice.`;
|
|
106608
107193
|
if (ctx.json) {
|
|
106609
|
-
ctx.deps.stdout.write(`${JSON.stringify({ ...errorEnvelope(result,
|
|
107194
|
+
ctx.deps.stdout.write(`${JSON.stringify({ ...errorEnvelope(result, failure4), idempotencyKey, details: { idempotencyKey }, retryCommand: retry })}
|
|
106610
107195
|
`);
|
|
106611
107196
|
} else {
|
|
106612
|
-
writeFailure(ctx.deps, result,
|
|
107197
|
+
writeFailure(ctx.deps, result, failure4, false);
|
|
106613
107198
|
ctx.deps.stderr.write(`Idempotency key: ${idempotencyKey}
|
|
106614
107199
|
${retry}
|
|
106615
107200
|
`);
|
|
@@ -106709,7 +107294,9 @@ init_render();
|
|
|
106709
107294
|
init_solana_verify();
|
|
106710
107295
|
init_trading();
|
|
106711
107296
|
init_swap();
|
|
106712
|
-
var USAGE18 =
|
|
107297
|
+
var USAGE18 = `Usage: candle transfer --to <address|wallet name|allowlist label|vault> --asset <SOL|USDC|CNDL|ETH|USDG>|--mint <mint>|--token <0x...> --amount <decimal|max> [--wallet <name>] [--client-id <id>] [--rpc-url <url>] [--yes] [--json]
|
|
107298
|
+
candle transfer status <transfer id|client id> [--json]`;
|
|
107299
|
+
var CLIENT_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
106713
107300
|
var TRANSFER_ASSETS = [...Object.keys(BASES), ...Object.keys(HOOD_BASES)];
|
|
106714
107301
|
var BASE58_ADDRESS = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
106715
107302
|
async function readLinkedWallets(ctx, key) {
|
|
@@ -106842,9 +107429,26 @@ async function confirmTransfer(ctx, lines, yes) {
|
|
|
106842
107429
|
throw new TradingError("CONFIRMATION_REQUIRED", "Run interactively to confirm, or use --yes for an ordinary transfer prompt.");
|
|
106843
107430
|
return (await ctx.deps.promptLine("Proceed? [y/N] ")).trim().toLowerCase() === "y";
|
|
106844
107431
|
}
|
|
107432
|
+
async function transferStatus(args, ctx) {
|
|
107433
|
+
const parsed = parseArgs(args, { valueFlags: [], booleanFlags: [] });
|
|
107434
|
+
if ("error" in parsed || parsed.positionals.length !== 1 || !CLIENT_ID.test(parsed.positionals[0] ?? "")) {
|
|
107435
|
+
writeUsageFailure(ctx.deps, "Usage: candle transfer status <transfer id|client id> [--json]", ctx.json);
|
|
107436
|
+
return 2;
|
|
107437
|
+
}
|
|
107438
|
+
try {
|
|
107439
|
+
const key = await tradingKey(ctx);
|
|
107440
|
+
const id = parsed.positionals[0];
|
|
107441
|
+
const found = await request(ctx, key, `/api/v1/agent/transfer/${encodeURIComponent(id)}`);
|
|
107442
|
+
return printTradingResult(ctx, found);
|
|
107443
|
+
} catch (error) {
|
|
107444
|
+
return tradingFailure(ctx, error);
|
|
107445
|
+
}
|
|
107446
|
+
}
|
|
106845
107447
|
async function transfer(args, ctx) {
|
|
107448
|
+
if (args[0] === "status")
|
|
107449
|
+
return await transferStatus(args.slice(1), ctx);
|
|
106846
107450
|
const parsed = parseArgs(args, {
|
|
106847
|
-
valueFlags: ["--wallet", "--to", "--asset", "--mint", "--token", "--amount", "--rpc-url"],
|
|
107451
|
+
valueFlags: ["--wallet", "--to", "--asset", "--mint", "--token", "--amount", "--rpc-url", "--client-id"],
|
|
106848
107452
|
booleanFlags: ["--yes"]
|
|
106849
107453
|
});
|
|
106850
107454
|
if ("error" in parsed) {
|
|
@@ -106853,7 +107457,7 @@ async function transfer(args, ctx) {
|
|
|
106853
107457
|
}
|
|
106854
107458
|
const flags2 = parsed.values;
|
|
106855
107459
|
const asset = flags2["--asset"]?.toUpperCase();
|
|
106856
|
-
if (parsed.positionals.length !== 0 || !flags2["--to"] || !flags2["--amount"] || [flags2["--asset"], flags2["--mint"], flags2["--token"]].filter(Boolean).length !== 1 || asset !== undefined && !TRANSFER_ASSETS.includes(asset) || flags2["--mint"] !== undefined && !BASE58_ADDRESS.test(flags2["--mint"]) || flags2["--token"] !== undefined && !checkEvmAddress(flags2["--token"]).ok) {
|
|
107460
|
+
if (parsed.positionals.length !== 0 || !flags2["--to"] || !flags2["--amount"] || [flags2["--asset"], flags2["--mint"], flags2["--token"]].filter(Boolean).length !== 1 || asset !== undefined && !TRANSFER_ASSETS.includes(asset) || flags2["--mint"] !== undefined && !BASE58_ADDRESS.test(flags2["--mint"]) || flags2["--token"] !== undefined && !checkEvmAddress(flags2["--token"]).ok || flags2["--client-id"] !== undefined && !CLIENT_ID.test(flags2["--client-id"])) {
|
|
106857
107461
|
writeUsageFailure(ctx.deps, USAGE18, ctx.json);
|
|
106858
107462
|
return 2;
|
|
106859
107463
|
}
|
|
@@ -106900,7 +107504,8 @@ async function transfer(args, ctx) {
|
|
|
106900
107504
|
chain: "solana",
|
|
106901
107505
|
...asset ? { asset } : { mint: flags2["--mint"] },
|
|
106902
107506
|
amountRaw,
|
|
106903
|
-
to: destination.address
|
|
107507
|
+
to: destination.address,
|
|
107508
|
+
...flags2["--client-id"] !== undefined ? { clientTransferId: flags2["--client-id"] } : {}
|
|
106904
107509
|
});
|
|
106905
107510
|
const transferId = built.transferId;
|
|
106906
107511
|
const unsigned = built.unsignedTransactionsBase64;
|
|
@@ -106936,6 +107541,7 @@ async function transfer(args, ctx) {
|
|
|
106936
107541
|
const receipt = {
|
|
106937
107542
|
...result,
|
|
106938
107543
|
transferId,
|
|
107544
|
+
...flags2["--client-id"] !== undefined ? { clientTransferId: flags2["--client-id"] } : {},
|
|
106939
107545
|
walletId: wallet3.id,
|
|
106940
107546
|
wallet: safeText(wallet3.address),
|
|
106941
107547
|
destination,
|
|
@@ -106992,7 +107598,8 @@ async function hoodTransfer(ctx, args) {
|
|
|
106992
107598
|
chain: "hood",
|
|
106993
107599
|
...asset ? { asset } : { mint: tokenAddress },
|
|
106994
107600
|
amountRaw: requested,
|
|
106995
|
-
to: destination.address
|
|
107601
|
+
to: destination.address,
|
|
107602
|
+
...flags2["--client-id"] !== undefined ? { clientTransferId: flags2["--client-id"] } : {}
|
|
106996
107603
|
});
|
|
106997
107604
|
const parsed = sequencedSchema.safeParse(built);
|
|
106998
107605
|
if (!parsed.success)
|
|
@@ -107029,6 +107636,7 @@ async function hoodTransfer(ctx, args) {
|
|
|
107029
107636
|
const receipt = {
|
|
107030
107637
|
...run2.final,
|
|
107031
107638
|
transferId,
|
|
107639
|
+
...flags2["--client-id"] !== undefined ? { clientTransferId: flags2["--client-id"] } : {},
|
|
107032
107640
|
chain: "hood",
|
|
107033
107641
|
walletId: wallet3.id,
|
|
107034
107642
|
wallet: safeText(wallet3.address),
|
|
@@ -107701,10 +108309,10 @@ candle update`);
|
|
|
107701
108309
|
} catch (error) {
|
|
107702
108310
|
steps.fail("installing");
|
|
107703
108311
|
await discard(deps, helper.tmpPath);
|
|
107704
|
-
const
|
|
108312
|
+
const failure4 = notWritable(dir, error);
|
|
107705
108313
|
writeScreenLocalFailure(deps, {
|
|
107706
|
-
...
|
|
107707
|
-
message: `${
|
|
108314
|
+
...failure4,
|
|
108315
|
+
message: `${failure4.message} candle ${target.version} was installed to ${realExec}, but its security key helper could not be renamed to ${helper.target}.`
|
|
107708
108316
|
}, json);
|
|
107709
108317
|
return 1;
|
|
107710
108318
|
}
|
|
@@ -108635,8 +109243,8 @@ async function vaultDemoteBatch(args, ctx) {
|
|
|
108635
109243
|
row2.label,
|
|
108636
109244
|
row2.destination ?? `none (${row2.destinationNote})`
|
|
108637
109245
|
]), []);
|
|
108638
|
-
for (const
|
|
108639
|
-
ctx.deps[display].write(`Not demoted: ${
|
|
109246
|
+
for (const skipped2 of outcomes) {
|
|
109247
|
+
ctx.deps[display].write(`Not demoted: ${skipped2.address}: ${skipped2.reason}
|
|
108640
109248
|
`);
|
|
108641
109249
|
}
|
|
108642
109250
|
if (!yes) {
|
|
@@ -108744,12 +109352,12 @@ async function demoteOne(ctx, session, row2, values, booleans) {
|
|
|
108744
109352
|
}
|
|
108745
109353
|
}
|
|
108746
109354
|
outcome.results = results;
|
|
108747
|
-
const
|
|
108748
|
-
if (
|
|
108749
|
-
if (typeof
|
|
108750
|
-
outcome.code =
|
|
108751
|
-
if (typeof
|
|
108752
|
-
outcome.reason =
|
|
109355
|
+
const failure4 = [...results].reverse().find((doc) => typeof doc === "object" && doc !== null && doc.ok === false);
|
|
109356
|
+
if (failure4 !== undefined && exitCode !== 0) {
|
|
109357
|
+
if (typeof failure4.code === "string")
|
|
109358
|
+
outcome.code = failure4.code;
|
|
109359
|
+
if (typeof failure4.message === "string")
|
|
109360
|
+
outcome.reason = failure4.message;
|
|
108753
109361
|
}
|
|
108754
109362
|
}
|
|
108755
109363
|
if (thrown !== undefined)
|
|
@@ -110407,6 +111015,7 @@ init_solana_endpoint();
|
|
|
110407
111015
|
init_errors2();
|
|
110408
111016
|
init_evm_tee();
|
|
110409
111017
|
init_evm_transfer();
|
|
111018
|
+
init_format();
|
|
110410
111019
|
|
|
110411
111020
|
// src/vault/funding-receipts.ts
|
|
110412
111021
|
init_errors2();
|
|
@@ -110591,6 +111200,8 @@ async function vaultFund(args, ctx) {
|
|
|
110591
111200
|
if (parsed.values["--from"] !== undefined) {
|
|
110592
111201
|
return usage(ctx, "--from applies to an external wallet only; a TEE wallet is funded from its pinned vault key.");
|
|
110593
111202
|
}
|
|
111203
|
+
if (isRetiredTee(teeEntry))
|
|
111204
|
+
return refuseRetiredFund(ctx, teeAddress);
|
|
110594
111205
|
const reconciled = await reconcileFundingReceipts(vault, [teeAddress, ...teeEntry.tee?.vaultDestination ? [teeEntry.tee.vaultDestination] : []], solana.rpc, ctx);
|
|
110595
111206
|
if (reconciled !== null)
|
|
110596
111207
|
return reconciled;
|
|
@@ -110712,6 +111323,8 @@ async function vaultFundEvm(ctx, parsed, teeAddress) {
|
|
|
110712
111323
|
}, ctx.json);
|
|
110713
111324
|
return 1;
|
|
110714
111325
|
}
|
|
111326
|
+
if (isRetiredTee(teeEntry))
|
|
111327
|
+
return refuseRetiredFund(ctx, teeEntry.address);
|
|
110715
111328
|
if (teeEntry.tee?.remoteAuthority !== "verified-active" || teeEntry.tee.stopRequestedAt !== undefined) {
|
|
110716
111329
|
writeLocalFailure(ctx.deps, {
|
|
110717
111330
|
code: "TEE_WALLET_NOT_VERIFIED",
|
|
@@ -110746,6 +111359,14 @@ async function vaultFundEvm(ctx, parsed, teeAddress) {
|
|
|
110746
111359
|
}, client2.rpc);
|
|
110747
111360
|
});
|
|
110748
111361
|
}
|
|
111362
|
+
function refuseRetiredFund(ctx, address) {
|
|
111363
|
+
writeLocalFailure(ctx.deps, {
|
|
111364
|
+
code: "TEE_WALLET_STOPPED",
|
|
111365
|
+
message: `${address} is a retired TEE wallet; a retired address is never refunded.`,
|
|
111366
|
+
suggestion: "Nothing was signed. Create a new wallet to fund: candle tee new"
|
|
111367
|
+
}, ctx.json);
|
|
111368
|
+
return 1;
|
|
111369
|
+
}
|
|
110749
111370
|
|
|
110750
111371
|
// src/commands/vault-import-legacy.ts
|
|
110751
111372
|
init_args();
|
|
@@ -110957,14 +111578,14 @@ async function vaultImportLegacy(args, ctx) {
|
|
|
110957
111578
|
const digest = sourceDigest(vault.file.vaultId, new TextEncoder().encode(legacyRaw));
|
|
110958
111579
|
const already = new Set(vault.index.entries.map((entry) => entry.address));
|
|
110959
111580
|
const toMigrate = legacyEntries.filter((entry) => !already.has(entry.address));
|
|
110960
|
-
const
|
|
111581
|
+
const skipped2 = legacyEntries.length - toMigrate.length;
|
|
110961
111582
|
if (toMigrate.length === 0) {
|
|
110962
111583
|
await recordMigrationSidecar(vaultPath, vault, fromPath, digest, deps.now());
|
|
110963
111584
|
return reportDone(ctx, {
|
|
110964
111585
|
path: fromPath,
|
|
110965
111586
|
vaultPath,
|
|
110966
111587
|
migrated: 0,
|
|
110967
|
-
skipped,
|
|
111588
|
+
skipped: skipped2,
|
|
110968
111589
|
addresses: [],
|
|
110969
111590
|
digest,
|
|
110970
111591
|
idempotent: true
|
|
@@ -111004,7 +111625,7 @@ async function vaultImportLegacy(args, ctx) {
|
|
|
111004
111625
|
path: fromPath,
|
|
111005
111626
|
vaultPath,
|
|
111006
111627
|
migrated: newEntries.length,
|
|
111007
|
-
skipped,
|
|
111628
|
+
skipped: skipped2,
|
|
111008
111629
|
addresses: newEntries.map((entry) => ({
|
|
111009
111630
|
address: entry.address,
|
|
111010
111631
|
lifecycle: entry.tee?.lifecycle ?? "local-candidate",
|
|
@@ -111136,7 +111757,7 @@ async function readLamports(addresses, solana, ctx) {
|
|
|
111136
111757
|
const { rpc } = solana;
|
|
111137
111758
|
const lamports = new Map;
|
|
111138
111759
|
const unavailable6 = [];
|
|
111139
|
-
let
|
|
111760
|
+
let failure4;
|
|
111140
111761
|
let rateLimited = false;
|
|
111141
111762
|
for (let at3 = 0;at3 < addresses.length; at3 += CHUNK2) {
|
|
111142
111763
|
const chunk = addresses.slice(at3, at3 + CHUNK2);
|
|
@@ -111152,12 +111773,12 @@ async function readLamports(addresses, solana, ctx) {
|
|
|
111152
111773
|
unavailable6.push(...chunk);
|
|
111153
111774
|
if (isRateLimited(error)) {
|
|
111154
111775
|
rateLimited = true;
|
|
111155
|
-
|
|
111776
|
+
failure4 ??= rateLimitedReadFailure(ctx, error);
|
|
111156
111777
|
} else
|
|
111157
|
-
|
|
111778
|
+
failure4 ??= describeRpcFailure(error);
|
|
111158
111779
|
}
|
|
111159
111780
|
}
|
|
111160
|
-
return { lamports, unavailable: unavailable6, ...
|
|
111781
|
+
return { lamports, unavailable: unavailable6, ...failure4 === undefined ? {} : { failure: failure4 } };
|
|
111161
111782
|
}
|
|
111162
111783
|
async function readEvmBalances(addresses, rpcUrl, fetchFn) {
|
|
111163
111784
|
const rpc = createEvmRpc(rpcUrl, fetchFn);
|
|
@@ -111256,17 +111877,17 @@ async function vaultList(args, ctx) {
|
|
|
111256
111877
|
}
|
|
111257
111878
|
let lamports = new Map;
|
|
111258
111879
|
let unavailable6 = [];
|
|
111259
|
-
let
|
|
111880
|
+
let failure4;
|
|
111260
111881
|
if (balances && solana.length > 0 && solanaClient !== undefined) {
|
|
111261
111882
|
const outcome = await readLamports(solana.map((entry) => entry.address), solanaClient, ctx);
|
|
111262
111883
|
lamports = outcome.lamports;
|
|
111263
111884
|
unavailable6 = outcome.unavailable;
|
|
111264
|
-
|
|
111885
|
+
failure4 = outcome.failure;
|
|
111265
111886
|
}
|
|
111266
111887
|
const solanaComplete = unavailable6.length === 0;
|
|
111267
111888
|
const totalLamports = [...lamports.values()].reduce((sum, value) => sum + value, 0n);
|
|
111268
111889
|
if (!solanaComplete) {
|
|
111269
|
-
notice(deps, "stderr", "warning", `${unavailable6.length} ${unavailable6.length === 1 ? "address" : "addresses"} could not be read: ${
|
|
111890
|
+
notice(deps, "stderr", "warning", `${unavailable6.length} ${unavailable6.length === 1 ? "address" : "addresses"} could not be read: ${failure4 ?? "the RPC did not answer"}. Narrow with a filter, or use your own endpoint with --rpc-url.
|
|
111270
111891
|
`.trim());
|
|
111271
111892
|
}
|
|
111272
111893
|
const evm = balances ? matched.filter((entry) => entry.chain === "evm") : [];
|
|
@@ -111498,8 +112119,8 @@ function unreadableRoomLine(reason) {
|
|
|
111498
112119
|
`;
|
|
111499
112120
|
}
|
|
111500
112121
|
var RESTORED_SENTENCE = "The vault entry is back as it was: nothing left this machine.";
|
|
111501
|
-
function restoresPreImportEntry(
|
|
111502
|
-
return
|
|
112122
|
+
function restoresPreImportEntry(failure4) {
|
|
112123
|
+
return failure4.stage === "init" && failure4.status !== undefined && failure4.status !== 0;
|
|
111503
112124
|
}
|
|
111504
112125
|
function restoreRefusedFailure(init, error) {
|
|
111505
112126
|
if (error instanceof VaultError && error.code === "VAULT_CHANGED") {
|
|
@@ -111777,7 +112398,7 @@ async function promoteEvmInPlace(input, subjectLabel, sweepTo) {
|
|
|
111777
112398
|
});
|
|
111778
112399
|
const code = imported.exit;
|
|
111779
112400
|
if (imported.failure !== undefined) {
|
|
111780
|
-
let
|
|
112401
|
+
let failure4 = {
|
|
111781
112402
|
code: imported.failure.code,
|
|
111782
112403
|
message: imported.failure.message,
|
|
111783
112404
|
...imported.failure.suggestion !== undefined ? { suggestion: imported.failure.suggestion } : {}
|
|
@@ -111785,12 +112406,12 @@ async function promoteEvmInPlace(input, subjectLabel, sweepTo) {
|
|
|
111785
112406
|
if (restoresPreImportEntry(imported.failure)) {
|
|
111786
112407
|
try {
|
|
111787
112408
|
vault = hold(await commitVault(vault, { index: preImportIndex }, ctx.deps));
|
|
111788
|
-
|
|
112409
|
+
failure4 = { ...failure4, message: `${failure4.message} ${RESTORED_SENTENCE}` };
|
|
111789
112410
|
} catch (error) {
|
|
111790
|
-
|
|
112411
|
+
failure4 = restoreRefusedFailure(failure4, error);
|
|
111791
112412
|
}
|
|
111792
112413
|
}
|
|
111793
|
-
writeLocalFailure(ctx.deps,
|
|
112414
|
+
writeLocalFailure(ctx.deps, failure4, ctx.json);
|
|
111794
112415
|
return code;
|
|
111795
112416
|
}
|
|
111796
112417
|
let rebound;
|
|
@@ -112236,7 +112857,7 @@ async function promoteInPlace(ctx, parsed, subjectLabel, toKey) {
|
|
|
112236
112857
|
});
|
|
112237
112858
|
const code = imported.exit;
|
|
112238
112859
|
if (imported.failure !== undefined) {
|
|
112239
|
-
let
|
|
112860
|
+
let failure4 = {
|
|
112240
112861
|
code: imported.failure.code,
|
|
112241
112862
|
message: imported.failure.message,
|
|
112242
112863
|
...imported.failure.suggestion !== undefined ? { suggestion: imported.failure.suggestion } : {}
|
|
@@ -112244,12 +112865,12 @@ async function promoteInPlace(ctx, parsed, subjectLabel, toKey) {
|
|
|
112244
112865
|
if (restoresPreImportEntry(imported.failure)) {
|
|
112245
112866
|
try {
|
|
112246
112867
|
vault = hold(await commitVault(vault, { index: preImportIndex }, ctx.deps));
|
|
112247
|
-
|
|
112868
|
+
failure4 = { ...failure4, message: `${failure4.message} ${RESTORED_SENTENCE}` };
|
|
112248
112869
|
} catch (error) {
|
|
112249
|
-
|
|
112870
|
+
failure4 = restoreRefusedFailure(failure4, error);
|
|
112250
112871
|
}
|
|
112251
112872
|
}
|
|
112252
|
-
writeLocalFailure(ctx.deps,
|
|
112873
|
+
writeLocalFailure(ctx.deps, failure4, ctx.json);
|
|
112253
112874
|
return code;
|
|
112254
112875
|
}
|
|
112255
112876
|
let rebound;
|
|
@@ -112291,10 +112912,10 @@ async function promoteInPlace(ctx, parsed, subjectLabel, toKey) {
|
|
|
112291
112912
|
async function resumePromote(ctx, vault, entry, reopen, path, hold, confirmations) {
|
|
112292
112913
|
const announceGrant = confirmations.announceGrant ?? true;
|
|
112293
112914
|
const report2 = confirmations.report ?? "write";
|
|
112294
|
-
const fail4 = (
|
|
112915
|
+
const fail4 = (failure4, exit) => {
|
|
112295
112916
|
if (report2 === "write")
|
|
112296
|
-
writeLocalFailure(ctx.deps,
|
|
112297
|
-
return { exit, ...report2 === "return" ? { failure:
|
|
112917
|
+
writeLocalFailure(ctx.deps, failure4, ctx.json);
|
|
112918
|
+
return { exit, ...report2 === "return" ? { failure: failure4 } : {} };
|
|
112298
112919
|
};
|
|
112299
112920
|
const apiKey = await resolveApiKey(ctx.deps, ctx.profile);
|
|
112300
112921
|
if (!apiKey) {
|
|
@@ -112440,10 +113061,10 @@ async function runTeeImport(ctx, opts) {
|
|
|
112440
113061
|
const closeReopened = opts.closeReopened ?? true;
|
|
112441
113062
|
const apiKey = await resolveApiKey(ctx.deps, ctx.profile);
|
|
112442
113063
|
if (!apiKey) {
|
|
112443
|
-
const
|
|
113064
|
+
const failure4 = { code: "NO_API_KEY", message: "No API key available.", suggestion: "Run: candle keys create" };
|
|
112444
113065
|
if (report2 === "write")
|
|
112445
|
-
writeLocalFailure(ctx.deps,
|
|
112446
|
-
return { exit: 1, ...report2 === "return" ? { failure:
|
|
113066
|
+
writeLocalFailure(ctx.deps, failure4, ctx.json);
|
|
113067
|
+
return { exit: 1, ...report2 === "return" ? { failure: failure4 } : {} };
|
|
112447
113068
|
}
|
|
112448
113069
|
opts.onImport?.();
|
|
112449
113070
|
const flow = await runImportFlow({
|
|
@@ -112459,28 +113080,28 @@ async function runTeeImport(ctx, opts) {
|
|
|
112459
113080
|
...opts.keySigner !== undefined ? { keySigner: opts.keySigner } : {}
|
|
112460
113081
|
});
|
|
112461
113082
|
if (!flow.ok) {
|
|
112462
|
-
const
|
|
112463
|
-
if (
|
|
113083
|
+
const failure4 = flow.failure;
|
|
113084
|
+
if (failure4.kind === "api") {
|
|
112464
113085
|
const render2 = { apiUrl: ctx.apiUrl, authType: "key" };
|
|
112465
113086
|
if (report2 === "write") {
|
|
112466
|
-
writeFailure(ctx.deps,
|
|
113087
|
+
writeFailure(ctx.deps, failure4.response, render2, ctx.json);
|
|
112467
113088
|
return { exit: 1 };
|
|
112468
113089
|
}
|
|
112469
|
-
const suggestion = suggestionFor(
|
|
113090
|
+
const suggestion = suggestionFor(failure4.response, render2);
|
|
112470
113091
|
return {
|
|
112471
113092
|
exit: 1,
|
|
112472
113093
|
failure: {
|
|
112473
|
-
code: errorEnvelope(
|
|
112474
|
-
message: renderError(
|
|
113094
|
+
code: errorEnvelope(failure4.response, render2).code,
|
|
113095
|
+
message: renderError(failure4.response, render2),
|
|
112475
113096
|
...suggestion ? { suggestion } : {},
|
|
112476
|
-
stage:
|
|
112477
|
-
status:
|
|
113097
|
+
stage: failure4.stage,
|
|
113098
|
+
status: failure4.response.status
|
|
112478
113099
|
}
|
|
112479
113100
|
};
|
|
112480
113101
|
}
|
|
112481
113102
|
const local = {
|
|
112482
|
-
code:
|
|
112483
|
-
message: `${opts.address}: ${
|
|
113103
|
+
code: failure4.kind === "signer-store" ? "SIGNER_STORE_FAILED" : "SIGNER_COMMIT_FAILED",
|
|
113104
|
+
message: `${opts.address}: ${failure4.error instanceof Error ? failure4.error.message : String(failure4.error)}`,
|
|
112484
113105
|
suggestion: "The vault entry is import-pending. Fix the error and re-run promote to resume."
|
|
112485
113106
|
};
|
|
112486
113107
|
if (report2 === "write")
|
|
@@ -112744,15 +113365,15 @@ async function preflightBatch(index, rows, opts) {
|
|
|
112744
113365
|
const failures = [];
|
|
112745
113366
|
const pinnedBy = new Map;
|
|
112746
113367
|
const promotedBy = new Map;
|
|
112747
|
-
const refuse3 = (row2,
|
|
113368
|
+
const refuse3 = (row2, failure4) => {
|
|
112748
113369
|
failures.push({
|
|
112749
113370
|
line: row2.line,
|
|
112750
113371
|
label: row2.label,
|
|
112751
113372
|
destination: row2.destination,
|
|
112752
|
-
code:
|
|
112753
|
-
message:
|
|
112754
|
-
...
|
|
112755
|
-
...
|
|
113373
|
+
code: failure4.code,
|
|
113374
|
+
message: failure4.message,
|
|
113375
|
+
...failure4.suggestion !== undefined ? { suggestion: failure4.suggestion } : {},
|
|
113376
|
+
...failure4.why !== undefined ? { why: failure4.why } : {}
|
|
112756
113377
|
});
|
|
112757
113378
|
};
|
|
112758
113379
|
const refuseWith = (row2, error, why) => {
|
|
@@ -113195,8 +113816,8 @@ ${table}
|
|
|
113195
113816
|
rateLimited: 0,
|
|
113196
113817
|
elapsedMs: 0
|
|
113197
113818
|
};
|
|
113198
|
-
const
|
|
113199
|
-
const rebindable = rebindPhase === undefined ? undefined : splitRebindable(
|
|
113819
|
+
const skipped2 = planned.map((item) => skippedResult(item));
|
|
113820
|
+
const rebindable = rebindPhase === undefined ? undefined : splitRebindable(skipped2);
|
|
113200
113821
|
if (rebindPhase === undefined || rebindable === undefined || rebindable.wallets.length === 0) {
|
|
113201
113822
|
deps.stderr.write(`
|
|
113202
113823
|
${table}
|
|
@@ -113206,7 +113827,7 @@ ${table}
|
|
|
113206
113827
|
return finish(ctx, {
|
|
113207
113828
|
file: pairsFile,
|
|
113208
113829
|
rows: rows.length,
|
|
113209
|
-
keys:
|
|
113830
|
+
keys: skipped2,
|
|
113210
113831
|
destinations: destinations2,
|
|
113211
113832
|
exit: 0,
|
|
113212
113833
|
controlledBy,
|
|
@@ -113227,11 +113848,11 @@ ${renderControlledBy(controlledBy, n)}
|
|
|
113227
113848
|
if (typed.trim().toLowerCase() !== CONFIRM_WORD) {
|
|
113228
113849
|
throw new VaultError("PROMOTE_NOT_ACKNOWLEDGED", `The acknowledgement is the word ${CONFIRM_WORD}; nothing was moved, and nothing was written.`, { suggestion: `Run the command again and type ${CONFIRM_WORD} at the prompt.` });
|
|
113229
113850
|
}
|
|
113230
|
-
const rebound2 = await runRebindPhase(ctx, rebindPhase,
|
|
113851
|
+
const rebound2 = await runRebindPhase(ctx, rebindPhase, skipped2);
|
|
113231
113852
|
return finish(ctx, {
|
|
113232
113853
|
file: pairsFile,
|
|
113233
113854
|
rows: rows.length,
|
|
113234
|
-
keys:
|
|
113855
|
+
keys: skipped2,
|
|
113235
113856
|
destinations: destinations2,
|
|
113236
113857
|
exit: 0,
|
|
113237
113858
|
controlledBy,
|
|
@@ -113304,24 +113925,24 @@ ${renderControlledBy(controlledBy, acting.length)}
|
|
|
113304
113925
|
keySigner: keySignerImport(rebindPhase?.toKey)
|
|
113305
113926
|
});
|
|
113306
113927
|
if (imported.failure !== undefined || imported.submitted === undefined) {
|
|
113307
|
-
let
|
|
113928
|
+
let failure4 = imported.failure ?? {
|
|
113308
113929
|
code: "VAULT_WRITE_FAILED",
|
|
113309
113930
|
message: "The import did not complete."
|
|
113310
113931
|
};
|
|
113311
|
-
if (restoresPreImportEntry(
|
|
113932
|
+
if (restoresPreImportEntry(failure4)) {
|
|
113312
113933
|
try {
|
|
113313
113934
|
current = hold(await commitVault(current, { index: preImportIndex }, deps));
|
|
113314
|
-
|
|
113935
|
+
failure4 = { ...failure4, message: `${failure4.message} ${RESTORED_SENTENCE}` };
|
|
113315
113936
|
} catch (error) {
|
|
113316
|
-
|
|
113937
|
+
failure4 = restoreRefusedFailure(failure4, error);
|
|
113317
113938
|
}
|
|
113318
113939
|
}
|
|
113319
113940
|
stopped2 = {
|
|
113320
113941
|
line: row3.line,
|
|
113321
113942
|
exit: imported.exit,
|
|
113322
|
-
code:
|
|
113323
|
-
message:
|
|
113324
|
-
...
|
|
113943
|
+
code: failure4.code,
|
|
113944
|
+
message: failure4.message,
|
|
113945
|
+
...failure4.suggestion !== undefined ? { suggestion: failure4.suggestion } : {}
|
|
113325
113946
|
};
|
|
113326
113947
|
break;
|
|
113327
113948
|
}
|
|
@@ -113490,7 +114111,7 @@ async function verifySubjectSecret(vault, subject) {
|
|
|
113490
114111
|
}
|
|
113491
114112
|
async function reconcileForPreflight(ctx, row2, subject, resolved) {
|
|
113492
114113
|
const { onDisk } = resolved;
|
|
113493
|
-
const refuse3 = (
|
|
114114
|
+
const refuse3 = (failure4) => ({ ok: false, failure: failure4 });
|
|
113494
114115
|
const apiKey = await resolveApiKey(ctx.deps, ctx.profile);
|
|
113495
114116
|
if (!apiKey) {
|
|
113496
114117
|
return refuse3({
|
|
@@ -113616,11 +114237,11 @@ function renderBatchTable(planned, opts) {
|
|
|
113616
114237
|
return renderHumanTable(headers2, rows, opts.style ?? terminalStyle({ env: {}, isTTY: { stdin: false, stdout: false, stderr: false } }, "stderr"), headers2.map((_, i) => i === 0 || i === 1 || i === 3 || i === 4 || i === 5 ? { id: true } : i === 2 ? { priority: 2 } : { align: "right", priority: 1 }));
|
|
113617
114238
|
}
|
|
113618
114239
|
function renderFooter(opts) {
|
|
113619
|
-
const
|
|
114240
|
+
const skipped2 = opts.planned.filter((item) => item.kind === "skip").length;
|
|
113620
114241
|
const resumes = opts.planned.filter((item) => item.kind === "resume").length;
|
|
113621
114242
|
const promotes = opts.planned.filter((item) => item.kind === "promote").length;
|
|
113622
114243
|
const lines = [
|
|
113623
|
-
`${opts.planned.length} rows from ${opts.file}: ${
|
|
114244
|
+
`${opts.planned.length} rows from ${opts.file}: ${skipped2} already promoted (skipped), ${resumes} to resume, ${promotes} to promote.`
|
|
113624
114245
|
];
|
|
113625
114246
|
if (opts.destinations.length > 0) {
|
|
113626
114247
|
lines.push(`${opts.destinations.length} destination${opts.destinations.length === 1 ? "" : "s"}, in this order: ${opts.destinations.map((d) => `${destinationCell(d.label, d.address)} ${d.keys} key${d.keys === 1 ? "" : "s"}`).join(" · ")}`);
|
|
@@ -113670,7 +114291,7 @@ ${opts.landed} of ${opts.acting} rows landed and ARE in the vault; the vault is
|
|
|
113670
114291
|
function finish(ctx, opts) {
|
|
113671
114292
|
const promoted = opts.keys.filter((r) => r.state === "promoted").length;
|
|
113672
114293
|
const resumed = opts.keys.filter((r) => r.state === "resumed").length;
|
|
113673
|
-
const
|
|
114294
|
+
const skipped2 = opts.keys.filter((r) => r.state === "skipped").length;
|
|
113674
114295
|
const unverified = opts.keys.filter((r) => r.state !== "skipped" && r.remoteAuthority !== "verified-active");
|
|
113675
114296
|
const rebindFailed = opts.rebound?.run !== undefined && !opts.rebound.run.ok;
|
|
113676
114297
|
const exit = rebindFailed ? Math.max(opts.exit, 1) : opts.exit;
|
|
@@ -113683,7 +114304,7 @@ function finish(ctx, opts) {
|
|
|
113683
114304
|
rows: opts.rows,
|
|
113684
114305
|
promoted,
|
|
113685
114306
|
resumed,
|
|
113686
|
-
skipped,
|
|
114307
|
+
skipped: skipped2,
|
|
113687
114308
|
destinations: opts.destinations.map(({ label, address, keys }) => ({ label, address, keys })),
|
|
113688
114309
|
keys: opts.rebound === undefined ? opts.keys : keysWithRebind(opts.keys, opts.rebound.phase, opts.rebound.run),
|
|
113689
114310
|
controlledBy: controlledByJson(opts.controlledBy),
|
|
@@ -113695,7 +114316,7 @@ function finish(ctx, opts) {
|
|
|
113695
114316
|
writeFields(ctx.deps, "stdout", [
|
|
113696
114317
|
["Promoted", promoted],
|
|
113697
114318
|
["Resumed", resumed],
|
|
113698
|
-
["Already promoted (skipped)",
|
|
114319
|
+
["Already promoted (skipped)", skipped2],
|
|
113699
114320
|
["Commit", "one unlock, each committed on its own"]
|
|
113700
114321
|
]);
|
|
113701
114322
|
if (unverified.length > 0) {
|
|
@@ -114775,10 +115396,12 @@ function describeEnvelope(envelope, facts) {
|
|
|
114775
115396
|
init_args();
|
|
114776
115397
|
init_deps();
|
|
114777
115398
|
init_evm_lite();
|
|
115399
|
+
init_render();
|
|
114778
115400
|
init_solana_endpoint();
|
|
114779
115401
|
init_errors2();
|
|
114780
115402
|
init_evm_tee();
|
|
114781
115403
|
init_evm_transfer();
|
|
115404
|
+
init_format();
|
|
114782
115405
|
init_promote_support();
|
|
114783
115406
|
init_store();
|
|
114784
115407
|
init_vault_support();
|
|
@@ -114834,6 +115457,15 @@ async function vaultTransfer(args, ctx) {
|
|
|
114834
115457
|
return usage(ctx, `No vault key or promoted wallet matches --from ${fromLabel}.`);
|
|
114835
115458
|
}
|
|
114836
115459
|
assertTransferSigner(fromEntry);
|
|
115460
|
+
const retiredDestination = vault.index.entries.find((entry) => isRetiredTee(entry) && entry.address.toLowerCase() === to.toLowerCase());
|
|
115461
|
+
if (retiredDestination !== undefined) {
|
|
115462
|
+
writeLocalFailure(ctx.deps, {
|
|
115463
|
+
code: "TEE_WALLET_STOPPED",
|
|
115464
|
+
message: `${to} is a retired TEE wallet (${retiredDestination.label}); a retired address is never refunded.`,
|
|
115465
|
+
suggestion: "Nothing was signed. Send to a live wallet, or create one: candle tee new"
|
|
115466
|
+
}, ctx.json);
|
|
115467
|
+
return 1;
|
|
115468
|
+
}
|
|
114837
115469
|
if (fromEntry.chain === "evm") {
|
|
114838
115470
|
const evmRpc = resolveEvmRpcUrl(rpcUrlFlag, ctx.deps.env[EVM_RPC_URL_ENV], "--rpc-url");
|
|
114839
115471
|
if ("error" in evmRpc)
|
|
@@ -115266,8 +115898,8 @@ async function setAllowLaunch(args, ctx, enabled) {
|
|
|
115266
115898
|
for (const message of new Set(notEnforced.map((item) => item.message))) {
|
|
115267
115899
|
notice(deps, "stderr", "danger", `${message} (ALLOW_LAUNCH_NOT_ENFORCED)`);
|
|
115268
115900
|
}
|
|
115269
|
-
for (const
|
|
115270
|
-
notice(deps, "stderr", "danger", `Not changed: ${
|
|
115901
|
+
for (const failure4 of failed) {
|
|
115902
|
+
notice(deps, "stderr", "danger", `Not changed: ${failure4.id}: ${failure4.message}${failure4.code ? ` (${failure4.code})` : ""}.`);
|
|
115271
115903
|
}
|
|
115272
115904
|
return failed.length > 0 || notEnforced.length > 0 ? 1 : 0;
|
|
115273
115905
|
}
|
|
@@ -115525,7 +116157,7 @@ init_solana_alt();
|
|
|
115525
116157
|
init_solana_lite();
|
|
115526
116158
|
init_trading();
|
|
115527
116159
|
init_swap();
|
|
115528
|
-
import { randomUUID as
|
|
116160
|
+
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
115529
116161
|
var USAGE21 = "Usage: candle wallet close-empty [--wallet embedded|<tee address|label>] [--keep <mint>]... [--client-trade-id <id>] [--preview] [--yes] [--json]";
|
|
115530
116162
|
var BASE58_ADDRESS2 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
115531
116163
|
var LISTED_ROWS = 50;
|
|
@@ -115813,7 +116445,7 @@ async function walletsCloseEmpty(args, ctx) {
|
|
|
115813
116445
|
return 2;
|
|
115814
116446
|
}
|
|
115815
116447
|
const named = parsed.values["--client-trade-id"];
|
|
115816
|
-
const id = named ?? `close-${
|
|
116448
|
+
const id = named ?? `close-${randomUUID17()}`;
|
|
115817
116449
|
if (parsed.positionals.length !== 0 || !validClientId(id) || !lifted.keep.every((m) => BASE58_ADDRESS2.test(m))) {
|
|
115818
116450
|
writeUsageFailure(ctx.deps, USAGE21, ctx.json);
|
|
115819
116451
|
return 2;
|
|
@@ -116091,11 +116723,11 @@ async function verifyProfileAccount(ctx, config) {
|
|
|
116091
116723
|
const apiKey = await deps.store.get(profileSecretRef(profile, "apiKey"));
|
|
116092
116724
|
if (!apiKey)
|
|
116093
116725
|
return { ok: true, skipped: "no stored key" };
|
|
116094
|
-
const { account: live, failure:
|
|
116726
|
+
const { account: live, failure: failure4 } = await fetchAccount(deps, ctx.apiUrl, apiKey);
|
|
116095
116727
|
if (!live) {
|
|
116096
116728
|
return {
|
|
116097
116729
|
ok: true,
|
|
116098
|
-
warning: `Could not verify the account for ${profile} (${
|
|
116730
|
+
warning: `Could not verify the account for ${profile} (${failure4}); proceeding on the cached value ${cached}.`
|
|
116099
116731
|
};
|
|
116100
116732
|
}
|
|
116101
116733
|
if (live !== cached) {
|
|
@@ -116128,6 +116760,7 @@ init_profiles();
|
|
|
116128
116760
|
init_release();
|
|
116129
116761
|
init_render();
|
|
116130
116762
|
init_secret_store();
|
|
116763
|
+
init_trading_checks();
|
|
116131
116764
|
|
|
116132
116765
|
// src/update-notice.ts
|
|
116133
116766
|
init_client();
|
|
@@ -116375,6 +117008,7 @@ var COMMANDS = {
|
|
|
116375
117008
|
tee: {
|
|
116376
117009
|
subcommands: {
|
|
116377
117010
|
new: teeNew,
|
|
117011
|
+
check: teeCheck,
|
|
116378
117012
|
enable: teeEnable,
|
|
116379
117013
|
fund: teeFund,
|
|
116380
117014
|
status: teeStatus,
|