@candledottv/cli 0.11.30 → 0.11.32
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/dist/index.js +570 -101
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10972,6 +10972,7 @@ var init_errors2 = __esm(() => {
|
|
|
10972
10972
|
"EVM_RECIPIENT_IS_TOKEN",
|
|
10973
10973
|
"EVM_TRANSFER_REVERTED",
|
|
10974
10974
|
"TRANSFER_CHAIN_MISMATCH",
|
|
10975
|
+
"TRANSFER_INSUFFICIENT_FUNDS",
|
|
10975
10976
|
"SOLANA_COMMAND_EVM_KEY",
|
|
10976
10977
|
"RPC_RATE_LIMITED",
|
|
10977
10978
|
"EVM_RECORD_ENTRY_TOO_LONG",
|
|
@@ -12707,24 +12708,6 @@ function decodeArgs(data, types2, base = 4) {
|
|
|
12707
12708
|
return;
|
|
12708
12709
|
}
|
|
12709
12710
|
}
|
|
12710
|
-
function v2CallOk(data, value, terms) {
|
|
12711
|
-
const selector = selectorOf(data);
|
|
12712
|
-
if (selector === V2_SWAP_ETH_FOR_TOKENS_SELECTOR) {
|
|
12713
|
-
const args = decodeArgs(data, ["uint", ADDRESSES, "address", "uint"]);
|
|
12714
|
-
if (!args || terms.inputToken !== undefined)
|
|
12715
|
-
return false;
|
|
12716
|
-
const path = asList(args[1]);
|
|
12717
|
-
return path.length >= 2 && sameEvmAddress(asAddress(path[0]), HOOD_WETH_ADDRESS) && terms.outputToken !== undefined && sameEvmAddress(asAddress(path[path.length - 1]), terms.outputToken) && asNumber(args[0]) >= terms.minOutRaw && sameEvmAddress(asAddress(args[2]), terms.wallet);
|
|
12718
|
-
}
|
|
12719
|
-
if (selector === V2_SWAP_TOKENS_FOR_ETH_SELECTOR) {
|
|
12720
|
-
const args = decodeArgs(data, ["uint", "uint", ADDRESSES, "address", "uint"]);
|
|
12721
|
-
if (!args || terms.inputToken === undefined || value !== 0n)
|
|
12722
|
-
return false;
|
|
12723
|
-
const path = asList(args[2]);
|
|
12724
|
-
return asNumber(args[0]) <= terms.amountRaw && asNumber(args[1]) >= terms.minOutRaw && terms.outputToken === undefined && path.length >= 2 && sameEvmAddress(asAddress(path[0]), terms.inputToken) && sameEvmAddress(asAddress(path[path.length - 1]), HOOD_WETH_ADDRESS) && sameEvmAddress(asAddress(args[3]), terms.wallet);
|
|
12725
|
-
}
|
|
12726
|
-
return false;
|
|
12727
|
-
}
|
|
12728
12711
|
function v3SwapOf(call) {
|
|
12729
12712
|
const selector = selectorOf(call);
|
|
12730
12713
|
if (selector === V3_EXACT_INPUT_SINGLE_SELECTOR) {
|
|
@@ -12905,9 +12888,7 @@ function kyberCallOk(data, terms) {
|
|
|
12905
12888
|
const amount = asNumber(desc[7]);
|
|
12906
12889
|
return sameEvmAddress(asAddress(desc[0]), terms.inputToken ?? KYBER_NATIVE) && sameEvmAddress(asAddress(desc[1]), terms.outputToken ?? KYBER_NATIVE) && asNumber(desc[8]) >= terms.minOutRaw && sameEvmAddress(asAddress(desc[6]), terms.wallet) && amount === terms.amountRaw && asList(desc[3]).reduce((sum, part) => sum + asNumber(part), 0n) <= amount && asList(desc[4]).length === 0 && asList(desc[5]).every((part) => asNumber(part) === 0n) && asBytes(desc[10]).length === 0;
|
|
12907
12890
|
}
|
|
12908
|
-
function dexCallOk(to, data,
|
|
12909
|
-
if (sameEvmAddress(to, HOOD_UNIV2_ROUTER))
|
|
12910
|
-
return v2CallOk(data, value, terms);
|
|
12891
|
+
function dexCallOk(to, data, terms) {
|
|
12911
12892
|
if (sameEvmAddress(to, HOOD_UNIV3_ROUTER))
|
|
12912
12893
|
return v3CallOk(data, terms);
|
|
12913
12894
|
if (sameEvmAddress(to, HOOD_V4_UNIVERSAL_ROUTER))
|
|
@@ -13052,7 +13033,7 @@ function hoodTradeLegCheck(terms) {
|
|
|
13052
13033
|
if (terms.inputToken !== undefined ? value !== 0n : value > terms.amountRaw)
|
|
13053
13034
|
return false;
|
|
13054
13035
|
if (terms.venue === "dex")
|
|
13055
|
-
return dexCallOk(leg.to, data,
|
|
13036
|
+
return dexCallOk(leg.to, data, {
|
|
13056
13037
|
wallet: terms.wallet,
|
|
13057
13038
|
...terms.inputToken !== undefined ? { inputToken: terms.inputToken } : {},
|
|
13058
13039
|
amountRaw: terms.amountRaw,
|
|
@@ -13088,7 +13069,7 @@ async function registeredCurveFor(rpc, token) {
|
|
|
13088
13069
|
}
|
|
13089
13070
|
return;
|
|
13090
13071
|
}
|
|
13091
|
-
var HOOD_CURVE_FACTORY = "0xD08f8489F026957EdeD3Bb8Ef8A9d151A7B1DF22", HOOD_CURVE_REGISTRIES,
|
|
13072
|
+
var HOOD_CURVE_FACTORY = "0xD08f8489F026957EdeD3Bb8Ef8A9d151A7B1DF22", HOOD_CURVE_REGISTRIES, HOOD_UNIV3_ROUTER = "0xCaf681a66D020601342297493863E78C959E5cb2", HOOD_V4_UNIVERSAL_ROUTER = "0x8876789976decbfcbbbe364623c63652db8c0904", HOOD_KYBER_ROUTER = "0x6131B5fae19EA4f9D964eAc0408E4408b66337b5", HOOD_PERMIT2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3", HOOD_SPLITTER_FACTORY = "0x9318bD328Ac32DBAe08bbD85B634E6B6B06683BF", KYBER_NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", ADDRESS_THIS = "0x0000000000000000000000000000000000000002", DEX_SPENDERS, CREATE_CURVE_SELECTOR = "0xe5ab9610", ERC20_APPROVE_SELECTOR = "0x095ea7b3", PERMIT2_APPROVE_SELECTOR = "0x87517c45", CURVE_BUY_SELECTOR = "0xd96a094a", CURVE_BUY_WITH_QUOTE_SELECTOR = "0x6818735c", CURVE_SELL_SELECTOR = "0xd79875eb", V3_MULTICALL_SELECTOR = "0x5ae401dc", V3_EXACT_INPUT_SINGLE_SELECTOR = "0x04e45aaf", V3_EXACT_INPUT_SELECTOR = "0xb858183f", V3_UNWRAP_WETH9_SELECTOR = "0x49404b7c", V3_REFUND_ETH_SELECTOR = "0x12210e8a", UNIVERSAL_EXECUTE_SELECTOR = "0x3593564c", KYBER_SWAP_SELECTOR = "0xe21fd0e9", KYBER_SWAP_GENERIC_SELECTOR = "0x59e50fed", KYBER_SWAP_SIMPLE_SELECTOR = "0x8af033fb", SPLITTER_DEV_SELECTOR = "0x91cca3db", SPLITTER_IS_SPLITTER_SELECTOR = "0xa4c597d1", CURVE_FOR_BASE_TOKEN_SELECTOR = "0x446b9253", SPLITTER_PLATFORM_VAULT_SELECTOR = "0x20f094f8", CURVE_CREATED_TOPIC = "0xed3a740542131b3621499825e40efbcaf096e3b1608de865c08a2109378bb36b", LAUNCH_FEE_MAX_BPS = 1000n, MAX_ABI_ARRAY = 64, V3_SINGLE, V3_PATH, KYBER_DESC, KYBER_EXECUTION, V4_POOL_KEY, V4_SWAP_PARAMS, asBytes = (value) => value, asAddress = (value) => value, asNumber = (value) => value, asList = (value) => value, routedInput = (terms) => terms.inputToken ?? HOOD_WETH_ADDRESS, routedOutput = (terms) => terms.outputToken ?? HOOD_WETH_ADDRESS, UR_V3_SWAP_EXACT_IN = 0, UR_WRAP_ETH = 11, UR_UNWRAP_WETH = 12, UR_V4_SWAP = 16, V4_SWAP_EXACT_IN_SINGLE = 6, V4_SETTLE = 11, V4_SETTLE_ALL = 12, V4_TAKE = 14, V4_TAKE_ALL = 15, NATIVE_CURRENCY = "0x0000000000000000000000000000000000000000";
|
|
13092
13073
|
var init_hood_legs = __esm(() => {
|
|
13093
13074
|
init_evm_lite();
|
|
13094
13075
|
HOOD_CURVE_REGISTRIES = [
|
|
@@ -13096,8 +13077,7 @@ var init_hood_legs = __esm(() => {
|
|
|
13096
13077
|
"0x75934c16162075583435358f9F042D8D6E42Bd5C",
|
|
13097
13078
|
"0xFD54D094ad93349CA316bA309A3C8573E3D6C0cd"
|
|
13098
13079
|
];
|
|
13099
|
-
DEX_SPENDERS = [
|
|
13100
|
-
ADDRESSES = { array: "address" };
|
|
13080
|
+
DEX_SPENDERS = [HOOD_UNIV3_ROUTER, HOOD_KYBER_ROUTER, HOOD_PERMIT2];
|
|
13101
13081
|
V3_SINGLE = { tuple: ["address", "address", "uint", "address", "uint", "uint", "uint"] };
|
|
13102
13082
|
V3_PATH = { tuple: ["bytes", "address", "uint", "uint"] };
|
|
13103
13083
|
KYBER_DESC = {
|
|
@@ -17598,6 +17578,7 @@ function parseMintAccount(mint, owner, data) {
|
|
|
17598
17578
|
tokenProgram: owner,
|
|
17599
17579
|
token2022: owner === TOKEN_2022_PROGRAM_ID,
|
|
17600
17580
|
decimals,
|
|
17581
|
+
associatedTokenAccountSize: owner === TOKEN_2022_PROGRAM_ID ? TLV_START + 4 : ACCOUNT_TYPE_OFFSET2,
|
|
17601
17582
|
nonTransferable: false,
|
|
17602
17583
|
defaultFrozen: false,
|
|
17603
17584
|
paused: false,
|
|
@@ -17622,6 +17603,7 @@ function parseMintAccount(mint, owner, data) {
|
|
|
17622
17603
|
throw new MintReadError(`${mint} has an invalid mint extension length or a duplicate extension`);
|
|
17623
17604
|
}
|
|
17624
17605
|
seen.add(type);
|
|
17606
|
+
profile.associatedTokenAccountSize += REQUIRED_ACCOUNT_TLV_SIZE.get(type) ?? 0;
|
|
17625
17607
|
const requireLength = (expected) => {
|
|
17626
17608
|
if (length !== expected)
|
|
17627
17609
|
throw new MintReadError(`${mint} has an invalid extension ${type} length`);
|
|
@@ -17911,11 +17893,17 @@ function classifyTokenSendFailure(input) {
|
|
|
17911
17893
|
return "TOKEN_2022_HOOK_REFUSED";
|
|
17912
17894
|
return;
|
|
17913
17895
|
}
|
|
17914
|
-
var MintReadError, MINT_DECIMALS_OFFSET = 44, MINT_IS_INITIALIZED_OFFSET = 45, MINT_BASE_SIZE2 = 82, ACCOUNT_TYPE_OFFSET2 = 165, TLV_START = 166, MULTISIG_SIZE2 = 355, EXT_TRANSFER_FEE_CONFIG = 1, EXT_DEFAULT_ACCOUNT_STATE = 6, EXT_NON_TRANSFERABLE = 9, EXT_PERMANENT_DELEGATE = 12, EXT_TRANSFER_HOOK = 14, EXT_PAUSABLE = 26, EXECUTE_DISCRIMINATOR, EXTRA_ACCOUNT_METAS_SEED, EXTRA_ACCOUNT_META_SIZE = 35, HookResolutionError;
|
|
17896
|
+
var MintReadError, MINT_DECIMALS_OFFSET = 44, MINT_IS_INITIALIZED_OFFSET = 45, MINT_BASE_SIZE2 = 82, ACCOUNT_TYPE_OFFSET2 = 165, TLV_START = 166, MULTISIG_SIZE2 = 355, EXT_TRANSFER_FEE_CONFIG = 1, EXT_DEFAULT_ACCOUNT_STATE = 6, EXT_NON_TRANSFERABLE = 9, EXT_PERMANENT_DELEGATE = 12, EXT_TRANSFER_HOOK = 14, EXT_PAUSABLE = 26, REQUIRED_ACCOUNT_TLV_SIZE, EXECUTE_DISCRIMINATOR, EXTRA_ACCOUNT_METAS_SEED, EXTRA_ACCOUNT_META_SIZE = 35, HookResolutionError;
|
|
17915
17897
|
var init_token_2022 = __esm(() => {
|
|
17916
17898
|
init_solana_lite();
|
|
17917
17899
|
MintReadError = class MintReadError extends Error {
|
|
17918
17900
|
};
|
|
17901
|
+
REQUIRED_ACCOUNT_TLV_SIZE = new Map([
|
|
17902
|
+
[EXT_TRANSFER_FEE_CONFIG, 4 + 8],
|
|
17903
|
+
[EXT_NON_TRANSFERABLE, 4],
|
|
17904
|
+
[EXT_TRANSFER_HOOK, 4 + 1],
|
|
17905
|
+
[EXT_PAUSABLE, 4]
|
|
17906
|
+
]);
|
|
17919
17907
|
EXECUTE_DISCRIMINATOR = new Uint8Array([105, 37, 101, 197, 75, 251, 102, 26]);
|
|
17920
17908
|
EXTRA_ACCOUNT_METAS_SEED = new TextEncoder().encode("extra-account-metas");
|
|
17921
17909
|
HookResolutionError = class HookResolutionError extends Error {
|
|
@@ -19215,6 +19203,12 @@ function describeBiometry(report) {
|
|
|
19215
19203
|
suggestion: "Unlock the Mac with its password to reset Touch ID, then retry. Nothing was written and no other factor is substituted."
|
|
19216
19204
|
};
|
|
19217
19205
|
case "not-interactive":
|
|
19206
|
+
if (report.laError?.name === "systemCancel") {
|
|
19207
|
+
return {
|
|
19208
|
+
message: `Touch ID did not complete${la}: ${SYSTEM_CANCEL_WORDS}${reason}. This Mac ${report.biometryType && report.biometryType !== "none" ? `has ${biometryTypeWord(report.biometryType)}` : "may have Touch ID"}.`,
|
|
19209
|
+
suggestion: SYSTEM_CANCEL_SUGGESTION
|
|
19210
|
+
};
|
|
19211
|
+
}
|
|
19218
19212
|
return {
|
|
19219
19213
|
message: `Touch ID is not available from this session${la}: this Mac ${report.biometryType && report.biometryType !== "none" ? `has ${biometryTypeWord(report.biometryType)}, but` : "may have Touch ID, but"} no prompt can be shown to a process outside the interactive login session (SSH, a background agent, the lid closed with no display)${reason}.`,
|
|
19220
19214
|
suggestion: NOT_INTERACTIVE_SUGGESTION
|
|
@@ -19277,6 +19271,11 @@ function translateEnclaveFailure(code, message) {
|
|
|
19277
19271
|
DOMAIN_NOT_ASSOCIATED: `macOS did not associate the helper with cli.candle.tv: ${message}. ${AASA_REQUIREMENT}`,
|
|
19278
19272
|
NO_CREDENTIAL: `No synced passkey with this envelope's credential id is available to this Mac or this Apple account: ${message}.`
|
|
19279
19273
|
};
|
|
19274
|
+
if (code === "NOT_INTERACTIVE" && /\bsystemCancel\b/.test(message)) {
|
|
19275
|
+
return new VaultError("VAULT_FACTOR_UNAVAILABLE", `Touch ID did not complete: ${SYSTEM_CANCEL_WORDS}: ${message}.`, {
|
|
19276
|
+
suggestion: SYSTEM_CANCEL_SUGGESTION
|
|
19277
|
+
});
|
|
19278
|
+
}
|
|
19280
19279
|
const suggestion = {
|
|
19281
19280
|
BIOMETRY_UNAVAILABLE: "Open the lid, or use a keyboard with Touch ID, or unlock the Mac with its password first. Nothing was derived and no other factor was tried; the passphrase still opens the vault.",
|
|
19282
19281
|
NOT_INTERACTIVE: NOT_INTERACTIVE_SUGGESTION,
|
|
@@ -19491,7 +19490,7 @@ async function deleteEnclaveKey(deps, session, opts) {
|
|
|
19491
19490
|
return false;
|
|
19492
19491
|
}
|
|
19493
19492
|
}
|
|
19494
|
-
var ENCLAVE_HELPER_ENV = "CANDLE_ENCLAVE_HELPER", CODESIGN_PATH = "/usr/bin/codesign", ENCLAVE_HELPER_TIMEOUT_MS = 120000, CODESIGN_TIMEOUT_MS = 30000, AASA_URL = "https://cli.candle.tv/.well-known/apple-app-site-association", AASA_REQUIREMENT, ENCLAVE_INSTALL_SUGGESTION = "Install a release build of the CLI that ships the signed helper (the darwin tarball and Homebrew place candle-enclave.app beside candle), or set CANDLE_ENCLAVE_HELPER to the path of a signed candle-enclave.app. No other factor is substituted.", NOT_INTERACTIVE_SUGGESTION = "Run the command from a Terminal window inside the logged-in session on that Mac (not over SSH, not from a background agent, not with the lid closed and no display), then retry. Nothing was derived and no other factor is substituted; the passphrase still opens the vault.";
|
|
19493
|
+
var ENCLAVE_HELPER_ENV = "CANDLE_ENCLAVE_HELPER", CODESIGN_PATH = "/usr/bin/codesign", ENCLAVE_HELPER_TIMEOUT_MS = 120000, CODESIGN_TIMEOUT_MS = 30000, AASA_URL = "https://cli.candle.tv/.well-known/apple-app-site-association", AASA_REQUIREMENT, ENCLAVE_INSTALL_SUGGESTION = "Install a release build of the CLI that ships the signed helper (the darwin tarball and Homebrew place candle-enclave.app beside candle), or set CANDLE_ENCLAVE_HELPER to the path of a signed candle-enclave.app. No other factor is substituted.", NOT_INTERACTIVE_SUGGESTION = "Run the command from a Terminal window inside the logged-in session on that Mac (not over SSH, not from a background agent, not with the lid closed and no display), then retry. Nothing was derived and no other factor is substituted; the passphrase still opens the vault.", SYSTEM_CANCEL_SUGGESTION = "Retry with the Terminal window in front, and leave it in front until the prompt is answered. If it is cancelled again, run the command from a Terminal window inside the logged-in session on that Mac (not over SSH, not from a background agent, not with the lid closed and no display). Nothing was derived and no other factor is substituted; the passphrase still opens the vault.", SYSTEM_CANCEL_WORDS = "the system cancelled the prompt (for example another window took focus, or this is not the logged-in session: SSH, a background agent, the lid closed with no display)";
|
|
19495
19494
|
var init_enclave = __esm(() => {
|
|
19496
19495
|
init_sha256();
|
|
19497
19496
|
init_esm();
|
|
@@ -28228,6 +28227,7 @@ __export(exports_vault_support, {
|
|
|
28228
28227
|
requireVaultRaw: () => requireVaultRaw,
|
|
28229
28228
|
requireTty: () => requireTty,
|
|
28230
28229
|
requirePromptStreams: () => requirePromptStreams,
|
|
28230
|
+
refusePromptsWithoutTerminal: () => refusePromptsWithoutTerminal,
|
|
28231
28231
|
refuseEnvPassphrase: () => refuseEnvPassphrase,
|
|
28232
28232
|
openWithTypedPassphrase: () => openWithTypedPassphrase,
|
|
28233
28233
|
nonDefaultVaultFooter: () => nonDefaultVaultFooter,
|
|
@@ -28282,6 +28282,16 @@ function requirePromptStreams(ctx, what) {
|
|
|
28282
28282
|
writeVaultFailure(ctx, new VaultError("VAULT_UNLOCK_FAILED", `${what} needs a terminal for the passphrase prompt: standard input and standard error must both be a terminal. Standard output may be redirected; that is where the listing goes.`));
|
|
28283
28283
|
return false;
|
|
28284
28284
|
}
|
|
28285
|
+
function refusePromptsWithoutTerminal(ctx, what) {
|
|
28286
|
+
if (ctx.deps.isTTY.stdin && ctx.deps.isTTY.stderr)
|
|
28287
|
+
return ctx;
|
|
28288
|
+
const refuse2 = async () => {
|
|
28289
|
+
throw new VaultError("VAULT_UNLOCK_FAILED", `${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.`, {
|
|
28290
|
+
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."
|
|
28291
|
+
});
|
|
28292
|
+
};
|
|
28293
|
+
return { ...ctx, deps: { ...ctx.deps, promptSecret: refuse2, promptLine: refuse2 } };
|
|
28294
|
+
}
|
|
28285
28295
|
function vaultPathFor(ctx, parsed) {
|
|
28286
28296
|
const flag = parsed.values["--keystore"];
|
|
28287
28297
|
if (flag !== undefined)
|
|
@@ -45705,7 +45715,7 @@ __export(exports_util, {
|
|
|
45705
45715
|
joinValues: () => joinValues,
|
|
45706
45716
|
issue: () => issue,
|
|
45707
45717
|
isPlainObject: () => isPlainObject,
|
|
45708
|
-
isObject: () =>
|
|
45718
|
+
isObject: () => isObject2,
|
|
45709
45719
|
hexToUint8Array: () => hexToUint8Array,
|
|
45710
45720
|
getSizableOrigin: () => getSizableOrigin,
|
|
45711
45721
|
getParsedType: () => getParsedType3,
|
|
@@ -45872,11 +45882,11 @@ function esc(str) {
|
|
|
45872
45882
|
function slugify(input) {
|
|
45873
45883
|
return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
45874
45884
|
}
|
|
45875
|
-
function
|
|
45885
|
+
function isObject2(data) {
|
|
45876
45886
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
45877
45887
|
}
|
|
45878
45888
|
function isPlainObject(o) {
|
|
45879
|
-
if (
|
|
45889
|
+
if (isObject2(o) === false)
|
|
45880
45890
|
return false;
|
|
45881
45891
|
const ctor = o.constructor;
|
|
45882
45892
|
if (ctor === undefined)
|
|
@@ -45884,7 +45894,7 @@ function isPlainObject(o) {
|
|
|
45884
45894
|
if (typeof ctor !== "function")
|
|
45885
45895
|
return true;
|
|
45886
45896
|
const prot = ctor.prototype;
|
|
45887
|
-
if (
|
|
45897
|
+
if (isObject2(prot) === false)
|
|
45888
45898
|
return false;
|
|
45889
45899
|
if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
|
|
45890
45900
|
return false;
|
|
@@ -47765,13 +47775,13 @@ var init_schemas = __esm(() => {
|
|
|
47765
47775
|
}
|
|
47766
47776
|
return propValues;
|
|
47767
47777
|
});
|
|
47768
|
-
const
|
|
47778
|
+
const isObject3 = isObject2;
|
|
47769
47779
|
const catchall = def.catchall;
|
|
47770
47780
|
let value;
|
|
47771
47781
|
inst._zod.parse = (payload, ctx) => {
|
|
47772
47782
|
value ?? (value = _normalized.value);
|
|
47773
47783
|
const input = payload.value;
|
|
47774
|
-
if (!
|
|
47784
|
+
if (!isObject3(input)) {
|
|
47775
47785
|
payload.issues.push({
|
|
47776
47786
|
expected: "object",
|
|
47777
47787
|
code: "invalid_type",
|
|
@@ -47869,7 +47879,7 @@ var init_schemas = __esm(() => {
|
|
|
47869
47879
|
return (payload, ctx) => fn(shape, payload, ctx);
|
|
47870
47880
|
};
|
|
47871
47881
|
let fastpass;
|
|
47872
|
-
const
|
|
47882
|
+
const isObject3 = isObject2;
|
|
47873
47883
|
const jit = !globalConfig.jitless;
|
|
47874
47884
|
const allowsEval2 = allowsEval;
|
|
47875
47885
|
const fastEnabled = jit && allowsEval2.value;
|
|
@@ -47878,7 +47888,7 @@ var init_schemas = __esm(() => {
|
|
|
47878
47888
|
inst._zod.parse = (payload, ctx) => {
|
|
47879
47889
|
value ?? (value = _normalized.value);
|
|
47880
47890
|
const input = payload.value;
|
|
47881
|
-
if (!
|
|
47891
|
+
if (!isObject3(input)) {
|
|
47882
47892
|
payload.issues.push({
|
|
47883
47893
|
expected: "object",
|
|
47884
47894
|
code: "invalid_type",
|
|
@@ -47982,7 +47992,7 @@ var init_schemas = __esm(() => {
|
|
|
47982
47992
|
});
|
|
47983
47993
|
inst._zod.parse = (payload, ctx) => {
|
|
47984
47994
|
const input = payload.value;
|
|
47985
|
-
if (!
|
|
47995
|
+
if (!isObject2(input)) {
|
|
47986
47996
|
payload.issues.push({
|
|
47987
47997
|
code: "invalid_type",
|
|
47988
47998
|
expected: "object",
|
|
@@ -62391,7 +62401,7 @@ var init_image = __esm(() => {
|
|
|
62391
62401
|
});
|
|
62392
62402
|
|
|
62393
62403
|
// ../mcp/src/orchestrate.ts
|
|
62394
|
-
import { randomUUID as
|
|
62404
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
62395
62405
|
function requireApiKey(cfg) {
|
|
62396
62406
|
if (!cfg.apiKey) {
|
|
62397
62407
|
throw new Error("CANDLE_AGENT_API_KEY is required for this tool. Set it in the environment or MCP client config.");
|
|
@@ -62504,7 +62514,7 @@ async function readPaperInventory(cfg, doFetch, extra) {
|
|
|
62504
62514
|
}
|
|
62505
62515
|
async function executeTrade(args, cfg, doFetch) {
|
|
62506
62516
|
const apiKey = requireApiKey(cfg);
|
|
62507
|
-
const clientTradeId = args.clientTradeId ??
|
|
62517
|
+
const clientTradeId = args.clientTradeId ?? randomUUID8();
|
|
62508
62518
|
if (args.amount !== undefined && args.percent !== undefined) {
|
|
62509
62519
|
return errText("pass exactly one of amount or percent, not both", { clientTradeId });
|
|
62510
62520
|
}
|
|
@@ -62642,7 +62652,7 @@ async function executeTrade(args, cfg, doFetch) {
|
|
|
62642
62652
|
}
|
|
62643
62653
|
async function executeLaunchAndSeed(args, cfg, doFetch, imageDir, imageFs) {
|
|
62644
62654
|
const apiKey = requireApiKey(cfg);
|
|
62645
|
-
const clientLaunchId = args.clientLaunchId ??
|
|
62655
|
+
const clientLaunchId = args.clientLaunchId ?? randomUUID8();
|
|
62646
62656
|
const { devBuy, dryRun, buyAmount: _rawBuyAmount, ...imageFields } = args;
|
|
62647
62657
|
let buyAmount;
|
|
62648
62658
|
if (devBuy !== undefined) {
|
|
@@ -63190,7 +63200,7 @@ function checkOrderMarket2(action, intent, market) {
|
|
|
63190
63200
|
function verifyPerpsBuild2(build, opts) {
|
|
63191
63201
|
try {
|
|
63192
63202
|
const network2 = opts.network ?? "mainnet";
|
|
63193
|
-
if (!
|
|
63203
|
+
if (!EVM_ADDRESS_RE3.test(opts.builder))
|
|
63194
63204
|
return { ok: false, reason: "no valid Candle builder address to check against" };
|
|
63195
63205
|
if (build.network !== undefined && build.network !== network2) {
|
|
63196
63206
|
return { ok: false, reason: `the build is for ${build.network}, this client trades ${network2}` };
|
|
@@ -63278,7 +63288,7 @@ function hyperliquidExchangeBody2(action, nonce, signature) {
|
|
|
63278
63288
|
function hyperliquidRelayBody2(typedData) {
|
|
63279
63289
|
return { method: "eth_signTypedData_v4", params: { typed_data: typedData } };
|
|
63280
63290
|
}
|
|
63281
|
-
var HYPERLIQUID_ALLOWED_ACTION_TYPES2, HYPERLIQUID_EXCHANGE_URLS2, HYPERLIQUID_INFO_URLS2, CANDLE_HYPERLIQUID_BUILDER_ADDRESS2 = null, HYPERLIQUID_MAX_BUILDER_FEE_TENTHS_BPS2 = 100, HYPERLIQUID_MAX_BUILDER_FEE_RATE2 = "0.1%", HYPERLIQUID_SIGNATURE_CHAIN_ID2 = "0xa4b1", ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000", UINT64_MAX2, INT64_MIN2,
|
|
63291
|
+
var HYPERLIQUID_ALLOWED_ACTION_TYPES2, HYPERLIQUID_EXCHANGE_URLS2, HYPERLIQUID_INFO_URLS2, CANDLE_HYPERLIQUID_BUILDER_ADDRESS2 = null, HYPERLIQUID_MAX_BUILDER_FEE_TENTHS_BPS2 = 100, HYPERLIQUID_MAX_BUILDER_FEE_RATE2 = "0.1%", HYPERLIQUID_SIGNATURE_CHAIN_ID2 = "0xa4b1", ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000", UINT64_MAX2, INT64_MIN2, EVM_ADDRESS_RE3, HYPERLIQUID_DEFAULT_SLIPPAGE_BPS2 = 100, HYPERLIQUID_MID_DRIFT_BPS2 = 100, HYPERLIQUID_TPSL_SLIPPAGE_BPS2 = 500;
|
|
63282
63292
|
var init_hyperliquid = __esm(() => {
|
|
63283
63293
|
init_sha3();
|
|
63284
63294
|
HYPERLIQUID_ALLOWED_ACTION_TYPES2 = [
|
|
@@ -63298,7 +63308,7 @@ var init_hyperliquid = __esm(() => {
|
|
|
63298
63308
|
};
|
|
63299
63309
|
UINT64_MAX2 = (1n << 64n) - 1n;
|
|
63300
63310
|
INT64_MIN2 = -(1n << 63n);
|
|
63301
|
-
|
|
63311
|
+
EVM_ADDRESS_RE3 = /^0x[0-9a-fA-F]{40}$/;
|
|
63302
63312
|
});
|
|
63303
63313
|
|
|
63304
63314
|
// ../mcp/src/perps.ts
|
|
@@ -63785,7 +63795,7 @@ var init_plans = __esm(() => {
|
|
|
63785
63795
|
});
|
|
63786
63796
|
|
|
63787
63797
|
// ../mcp/src/tools.ts
|
|
63788
|
-
import { randomUUID as
|
|
63798
|
+
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
63789
63799
|
function resolveToolAllowlist(env) {
|
|
63790
63800
|
const raw = env.CANDLE_MCP_TOOLS?.trim();
|
|
63791
63801
|
if (!raw)
|
|
@@ -64117,7 +64127,7 @@ async function getOperation(args, cfg, doFetch) {
|
|
|
64117
64127
|
};
|
|
64118
64128
|
}
|
|
64119
64129
|
async function swap2(args, cfg, doFetch) {
|
|
64120
|
-
const clientSwapId = typeof args.clientSwapId === "string" && args.clientSwapId.length > 0 ? args.clientSwapId :
|
|
64130
|
+
const clientSwapId = typeof args.clientSwapId === "string" && args.clientSwapId.length > 0 ? args.clientSwapId : randomUUID9();
|
|
64121
64131
|
const { url, init } = buildRequest("candle_swap", { ...args, clientSwapId }, cfg);
|
|
64122
64132
|
let ok;
|
|
64123
64133
|
let body;
|
|
@@ -65028,7 +65038,7 @@ init_render();
|
|
|
65028
65038
|
init_secret_store();
|
|
65029
65039
|
|
|
65030
65040
|
// src/version.ts
|
|
65031
|
-
var CLI_VERSION = "0.11.
|
|
65041
|
+
var CLI_VERSION = "0.11.32";
|
|
65032
65042
|
|
|
65033
65043
|
// src/commands/auth.ts
|
|
65034
65044
|
init_keys_embedded_wallet();
|
|
@@ -65459,6 +65469,10 @@ var ENVIRONMENT = [
|
|
|
65459
65469
|
description: "candle perps: Candle's Hyperliquid builder address, pinned for the check every build passes before it is signed; without it the release's, else the one the server reports"
|
|
65460
65470
|
},
|
|
65461
65471
|
{ name: "CANDLE_HYPERLIQUID_NETWORK", description: "candle perps: set to testnet to trade Hyperliquid testnet" },
|
|
65472
|
+
{
|
|
65473
|
+
name: "CANDLE_PERPS_WITHDRAW_PINS",
|
|
65474
|
+
description: "candle perps withdraw: JSON {token, destinationDex, depositories, amountForm} pinning what every withdrawal is checked against before it is signed; this release ships none, so without it withdrawals are refused"
|
|
65475
|
+
},
|
|
65462
65476
|
{ name: "CANDLE_NO_UPDATE_NOTIFIER", description: "Set to 1 to silence the update notice" },
|
|
65463
65477
|
{
|
|
65464
65478
|
name: "CANDLE_KEYSTORE_PASSPHRASE",
|
|
@@ -65647,8 +65661,8 @@ var HELP = {
|
|
|
65647
65661
|
},
|
|
65648
65662
|
perps: {
|
|
65649
65663
|
group: "Trade",
|
|
65650
|
-
summary: "Hyperliquid perpetuals from your EVM TEE wallet: setup, deposit, open, close, cancel, orders, positions, leverage",
|
|
65651
|
-
description: "Trade perpetuals on Hyperliquid's main perp exchange from the EVM TEE wallet bound to this key (scope perps:write, opt-in). Candle builds each action within the key's limits; this machine recomputes its hash and checks the action type and Candle's builder before anything is signed, then Candle's relay signs and this machine submits to Hyperliquid. Every plan is eligible for perps, once the deployment has perps enabled. Every plan except Max pays Candle a builder fee on each order (0.1% today); Max pays none. candle plans shows the rate in force. Deposits move SOL, USDC, ETH or USDG onto the Hyperliquid account through Relay, and
|
|
65664
|
+
summary: "Hyperliquid perpetuals from your EVM TEE wallet: setup, deposit, withdraw, open, close, cancel, orders, positions, leverage",
|
|
65665
|
+
description: "Trade perpetuals on Hyperliquid's main perp exchange from the EVM TEE wallet bound to this key (scope perps:write, opt-in). Candle builds each action within the key's limits; this machine recomputes its hash and checks the action type and Candle's builder before anything is signed, then Candle's relay signs and this machine submits to Hyperliquid. Every plan is eligible for perps, once the deployment has perps enabled. Every plan except Max pays Candle a builder fee on each order (0.1% today); Max pays none. candle plans shows the rate in force. Deposits move SOL, USDC, ETH or USDG onto the Hyperliquid account through Relay; withdrawals move USDC back through Relay to the key's own Solana or Hood TEE wallet (scope perps:withdraw, opt-in, after one vault unlock and a typed confirm). The account's P&L (candle pnl) carries a Hyperliquid section.",
|
|
65652
65666
|
usage: ["candle perps <subcommand> [flags]"],
|
|
65653
65667
|
rows: [
|
|
65654
65668
|
{
|
|
@@ -65669,7 +65683,14 @@ var HELP = {
|
|
|
65669
65683
|
invocation: "orders [--wallet <tee>]",
|
|
65670
65684
|
description: "Open orders on Hyperliquid, and every action Candle built"
|
|
65671
65685
|
},
|
|
65672
|
-
{
|
|
65686
|
+
{
|
|
65687
|
+
invocation: "withdraw <amount> --to solana|hood [--wallet <evm tee>] | withdraw status <id>",
|
|
65688
|
+
description: "Move USDC from the Hyperliquid account to this key's own Solana or Hood TEE wallet through Relay (perps:withdraw); one vault unlock and a typed confirm, no --yes"
|
|
65689
|
+
},
|
|
65690
|
+
{
|
|
65691
|
+
invocation: "positions [--wallet <tee>]",
|
|
65692
|
+
description: "Positions, account value and withdrawable USDC, read live, and any withdrawal in flight"
|
|
65693
|
+
},
|
|
65673
65694
|
{ invocation: "leverage <coin> <x> [--isolated]", description: "Set a market's leverage and margin mode" }
|
|
65674
65695
|
],
|
|
65675
65696
|
flags: [
|
|
@@ -65687,7 +65708,8 @@ var HELP = {
|
|
|
65687
65708
|
invocation: "--to <evm tee>",
|
|
65688
65709
|
description: "deposit from Solana: the EVM TEE wallet whose account receives it"
|
|
65689
65710
|
},
|
|
65690
|
-
{ invocation: "--id <
|
|
65711
|
+
{ invocation: "--id <id>", description: "deposit and withdraw: the idempotency id (default: a new one)" },
|
|
65712
|
+
{ invocation: "--keystore <path>", description: "withdraw: the vault whose unlock confirms the withdrawal" },
|
|
65691
65713
|
{ invocation: "--rpc-url <url>", description: "deposit from Solana: the RPC the deposit check reads" },
|
|
65692
65714
|
{ invocation: "--no-submit", description: "Build and check only; sign and submit nothing" },
|
|
65693
65715
|
{ invocation: "--sign-only", description: "Build, check and sign through the relay; submit nothing" },
|
|
@@ -65699,9 +65721,10 @@ var HELP = {
|
|
|
65699
65721
|
"candle perps open BTC long 0.01",
|
|
65700
65722
|
"candle perps open ETH short 0.5 --price 3200 --tp 2900 --sl 3400",
|
|
65701
65723
|
"candle perps close BTC",
|
|
65702
|
-
"candle perps leverage BTC 5 --isolated"
|
|
65724
|
+
"candle perps leverage BTC 5 --isolated",
|
|
65725
|
+
"candle perps withdraw 25 --to solana"
|
|
65703
65726
|
],
|
|
65704
|
-
env: [...ENV_API, "CANDLE_HYPERLIQUID_BUILDER", "CANDLE_HYPERLIQUID_NETWORK"]
|
|
65727
|
+
env: [...ENV_API, "CANDLE_HYPERLIQUID_BUILDER", "CANDLE_HYPERLIQUID_NETWORK", "CANDLE_PERPS_WITHDRAW_PINS"]
|
|
65705
65728
|
},
|
|
65706
65729
|
transfer: {
|
|
65707
65730
|
group: "Trade",
|
|
@@ -74279,6 +74302,13 @@ async function perpsLeverage(args, ctx) {
|
|
|
74279
74302
|
return tradingFailure(ctx, error);
|
|
74280
74303
|
}
|
|
74281
74304
|
}
|
|
74305
|
+
function formatUsdcMicros(value) {
|
|
74306
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
|
|
74307
|
+
return "?";
|
|
74308
|
+
const micros = BigInt(value);
|
|
74309
|
+
const frac = (micros % 1000000n).toString().padStart(6, "0").replace(/0+$/, "");
|
|
74310
|
+
return frac ? `${micros / 1000000n}.${frac}` : `${micros / 1000000n}`;
|
|
74311
|
+
}
|
|
74282
74312
|
async function perpsPositions(args, ctx) {
|
|
74283
74313
|
const parsed = parseArgs(args, { valueFlags: ["--wallet"] });
|
|
74284
74314
|
if ("error" in parsed)
|
|
@@ -74302,6 +74332,13 @@ async function perpsPositions(args, ctx) {
|
|
|
74302
74332
|
for (const p of positions) {
|
|
74303
74333
|
const leverage = p.leverage ?? {};
|
|
74304
74334
|
out.write(` ${safeText(p.coin)} ${safeText(p.szi)} at ${safeText(p.entryPx)}, value ${safeText(p.positionValue)}, uPnL ${safeText(p.unrealizedPnl)}, ${safeText(leverage.value)}x ${safeText(leverage.type)}, liq ${safeText(p.liquidationPx ?? "none")}
|
|
74335
|
+
`);
|
|
74336
|
+
}
|
|
74337
|
+
const open5 = Array.isArray(result.openWithdrawals) ? result.openWithdrawals : null;
|
|
74338
|
+
out.write(`Withdrawable now: ${safeText(result.withdrawable)} USDC${open5 === null ? "" : open5.length === 0 ? "; no withdrawal in flight" : ""}.
|
|
74339
|
+
`);
|
|
74340
|
+
for (const w of open5 ?? []) {
|
|
74341
|
+
out.write(` Withdrawal ${safeText(w.id)}: ${safeText(w.status)}, ${formatUsdcMicros(w.amountUsdcMicros)} USDC to ${safeText(w.to)} (candle perps withdraw status ${safeText(w.id)})
|
|
74305
74342
|
`);
|
|
74306
74343
|
}
|
|
74307
74344
|
return 0;
|
|
@@ -74487,7 +74524,7 @@ async function perpsDeposit(args, ctx) {
|
|
|
74487
74524
|
`);
|
|
74488
74525
|
output.write(`Relay delivers at least ${minimum} USDC (estimate ${estimate2})${build.firstDeposit ? "; Hyperliquid keeps 1 USDC to activate this new account" : ""}. Candle fee: none.
|
|
74489
74526
|
`);
|
|
74490
|
-
output.write("
|
|
74527
|
+
output.write("To bring it back: candle perps withdraw <amount> --to solana|hood (perps:withdraw). `candle tee disable` and `tee sweep` do not reach it, so withdraw before disabling.\n");
|
|
74491
74528
|
if (sequenced)
|
|
74492
74529
|
output.write(`Legs, signed one at a time: ${sequenced.plannedLegCount === 2 ? "approval, bridgeDeposit" : "bridgeDeposit"}
|
|
74493
74530
|
`);
|
|
@@ -74541,6 +74578,382 @@ async function perpsDeposit(args, ctx) {
|
|
|
74541
74578
|
}
|
|
74542
74579
|
}
|
|
74543
74580
|
|
|
74581
|
+
// src/commands/perps-withdraw.ts
|
|
74582
|
+
init_args();
|
|
74583
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
74584
|
+
|
|
74585
|
+
// src/hyperliquid-withdraw.ts
|
|
74586
|
+
var CANDLE_PERPS_WITHDRAW_PINS = null;
|
|
74587
|
+
var RELAY_API_URL = "https://api.relay.link";
|
|
74588
|
+
var ZERO = "0x0000000000000000000000000000000000000000";
|
|
74589
|
+
var EVM_ADDRESS_RE2 = /^0x[0-9a-fA-F]{40}$/;
|
|
74590
|
+
var BYTES32_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
74591
|
+
var RELAY_NONCE_MAPPING_TYPES = {
|
|
74592
|
+
NonceMapping: [
|
|
74593
|
+
{ name: "chainId", type: "string" },
|
|
74594
|
+
{ name: "wallet", type: "address" },
|
|
74595
|
+
{ name: "depositor", type: "address" },
|
|
74596
|
+
{ name: "id", type: "bytes32" },
|
|
74597
|
+
{ name: "nonce", type: "uint256" }
|
|
74598
|
+
]
|
|
74599
|
+
};
|
|
74600
|
+
var HYPERLIQUID_SEND_ASSET_TYPES = {
|
|
74601
|
+
"HyperliquidTransaction:SendAsset": [
|
|
74602
|
+
{ name: "hyperliquidChain", type: "string" },
|
|
74603
|
+
{ name: "destination", type: "string" },
|
|
74604
|
+
{ name: "sourceDex", type: "string" },
|
|
74605
|
+
{ name: "destinationDex", type: "string" },
|
|
74606
|
+
{ name: "token", type: "string" },
|
|
74607
|
+
{ name: "amount", type: "string" },
|
|
74608
|
+
{ name: "fromSubAccount", type: "string" },
|
|
74609
|
+
{ name: "nonce", type: "uint64" }
|
|
74610
|
+
]
|
|
74611
|
+
};
|
|
74612
|
+
var SEND_ASSET_KEYS = HYPERLIQUID_SEND_ASSET_TYPES["HyperliquidTransaction:SendAsset"].map((field) => field.name);
|
|
74613
|
+
var REQUEST_AMOUNT_RE = /^(0|[1-9][0-9]{0,11})(\.[0-9]{1,6})?$/;
|
|
74614
|
+
var CANONICAL_RE = /^(0|[1-9][0-9]*)(\.[0-9]{0,5}[1-9])?$/;
|
|
74615
|
+
var FIXED6_RE = /^(0|[1-9][0-9]*)\.[0-9]{6}$/;
|
|
74616
|
+
function parseWithdrawAmount(value) {
|
|
74617
|
+
if (!REQUEST_AMOUNT_RE.test(value))
|
|
74618
|
+
return null;
|
|
74619
|
+
const [whole = "0", frac = ""] = value.split(".");
|
|
74620
|
+
const micros = BigInt(whole) * 1000000n + BigInt(frac.padEnd(6, "0"));
|
|
74621
|
+
return micros > 0n ? micros : null;
|
|
74622
|
+
}
|
|
74623
|
+
function formatWithdrawAmount(micros) {
|
|
74624
|
+
const frac = (micros % 1000000n).toString().padStart(6, "0").replace(/0+$/, "");
|
|
74625
|
+
return frac ? `${micros / 1000000n}.${frac}` : `${micros / 1000000n}`;
|
|
74626
|
+
}
|
|
74627
|
+
function parseSendAssetAmount(value, form) {
|
|
74628
|
+
if (typeof value !== "string" || !(form === "canonical" ? CANONICAL_RE : FIXED6_RE).test(value))
|
|
74629
|
+
return null;
|
|
74630
|
+
const [whole = "0", frac = ""] = value.split(".");
|
|
74631
|
+
if (frac.length > 6)
|
|
74632
|
+
return null;
|
|
74633
|
+
return BigInt(whole) * 1000000n + BigInt(frac.padEnd(6, "0"));
|
|
74634
|
+
}
|
|
74635
|
+
function isObject(value) {
|
|
74636
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
74637
|
+
}
|
|
74638
|
+
function exactKeys(value, keys) {
|
|
74639
|
+
const own = Object.keys(value);
|
|
74640
|
+
return own.length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
74641
|
+
}
|
|
74642
|
+
function sameEvm(a, b) {
|
|
74643
|
+
return typeof a === "string" && EVM_ADDRESS_RE2.test(a) && a.toLowerCase() === b.toLowerCase();
|
|
74644
|
+
}
|
|
74645
|
+
function typesMatch(types2, expected) {
|
|
74646
|
+
if (!isObject(types2) || !exactKeys(types2, Object.keys(expected)))
|
|
74647
|
+
return false;
|
|
74648
|
+
return Object.entries(expected).every(([key, want]) => {
|
|
74649
|
+
const fields = types2[key];
|
|
74650
|
+
return Array.isArray(fields) && fields.length === want.length && fields.every((field, i) => isObject(field) && exactKeys(field, ["name", "type"]) && field.name === want[i]?.name && field.type === want[i]?.type);
|
|
74651
|
+
});
|
|
74652
|
+
}
|
|
74653
|
+
function domainMatches(domain, expected) {
|
|
74654
|
+
return isObject(domain) && exactKeys(domain, ["name", "version", "chainId", "verifyingContract"]) && domain.name === expected.name && domain.version === expected.version && domain.chainId === expected.chainId && typeof domain.verifyingContract === "string" && domain.verifyingContract.toLowerCase() === ZERO;
|
|
74655
|
+
}
|
|
74656
|
+
function verifyWithdrawMapping(build, expect) {
|
|
74657
|
+
const refuse3 = (reason) => ({ ok: false, reason });
|
|
74658
|
+
if (expect.pins === null)
|
|
74659
|
+
return refuse3("the withdrawal pins are pending verification in this release");
|
|
74660
|
+
if (build.id !== expect.id)
|
|
74661
|
+
return refuse3("the build names another withdrawal id");
|
|
74662
|
+
if (!sameEvm(build.from?.address, expect.account))
|
|
74663
|
+
return refuse3("the build withdraws from another account");
|
|
74664
|
+
if (build.to?.chain !== expect.to)
|
|
74665
|
+
return refuse3(`the build lands on ${String(build.to?.chain)}, not ${expect.to}`);
|
|
74666
|
+
const destination = build.to.address;
|
|
74667
|
+
const own = expect.ownDestinations.some((address) => address.startsWith("0x") ? sameEvm(destination, address) : destination === address);
|
|
74668
|
+
if (!own)
|
|
74669
|
+
return refuse3("the build lands on a wallet that is not this key's own");
|
|
74670
|
+
if (parseWithdrawAmount(build.amountUsdc) !== expect.amountMicros)
|
|
74671
|
+
return refuse3("the build is for another amount");
|
|
74672
|
+
const typed = build.typedData;
|
|
74673
|
+
if (!isObject(typed) || typed.primary_type !== "NonceMapping")
|
|
74674
|
+
return refuse3("the typed data is not a NonceMapping");
|
|
74675
|
+
if (!domainMatches(typed.domain, { name: "RelayNonceMapping", version: "2", chainId: 1 }))
|
|
74676
|
+
return refuse3("the NonceMapping domain is not Relay's");
|
|
74677
|
+
if (!typesMatch(typed.types, RELAY_NONCE_MAPPING_TYPES))
|
|
74678
|
+
return refuse3("the NonceMapping type list differs");
|
|
74679
|
+
const message = typed.message;
|
|
74680
|
+
if (!isObject(message) || !exactKeys(message, ["chainId", "wallet", "depositor", "id", "nonce"]))
|
|
74681
|
+
return refuse3("the NonceMapping message has other fields");
|
|
74682
|
+
if (message.chainId !== "hyperliquid")
|
|
74683
|
+
return refuse3("the NonceMapping names another chain");
|
|
74684
|
+
if (!sameEvm(message.wallet, expect.account))
|
|
74685
|
+
return refuse3("the NonceMapping wallet is not this key's account");
|
|
74686
|
+
if (!sameEvm(message.depositor, expect.account))
|
|
74687
|
+
return refuse3("the NonceMapping depositor is not this key's account");
|
|
74688
|
+
if (typeof message.id !== "string" || !BYTES32_RE.test(message.id) || message.id !== build.relayRequestId)
|
|
74689
|
+
return refuse3("the NonceMapping names another Relay request");
|
|
74690
|
+
if (message.nonce !== build.nonce || !Number.isSafeInteger(build.nonce) || build.nonce <= 0)
|
|
74691
|
+
return refuse3("the NonceMapping nonce is not the build's");
|
|
74692
|
+
return { ok: true };
|
|
74693
|
+
}
|
|
74694
|
+
function verifyWithdrawSendAsset(authorization, expect) {
|
|
74695
|
+
const refuse3 = (reason) => ({ ok: false, reason });
|
|
74696
|
+
const pins = expect.pins;
|
|
74697
|
+
if (pins === null)
|
|
74698
|
+
return refuse3("the withdrawal pins are pending verification in this release");
|
|
74699
|
+
const typed = authorization.typedData;
|
|
74700
|
+
if (!isObject(typed) || typed.primary_type !== "HyperliquidTransaction:SendAsset")
|
|
74701
|
+
return refuse3("the typed data is not a SendAsset");
|
|
74702
|
+
if (!domainMatches(typed.domain, { name: "HyperliquidSignTransaction", version: "1", chainId: 42161 }))
|
|
74703
|
+
return refuse3("the SendAsset domain is not Hyperliquid's user-signed domain");
|
|
74704
|
+
if (!typesMatch(typed.types, HYPERLIQUID_SEND_ASSET_TYPES))
|
|
74705
|
+
return refuse3("the SendAsset type list differs");
|
|
74706
|
+
const m = typed.message;
|
|
74707
|
+
if (!isObject(m) || !exactKeys(m, SEND_ASSET_KEYS))
|
|
74708
|
+
return refuse3("the SendAsset message has other fields");
|
|
74709
|
+
if (m.hyperliquidChain !== "Mainnet")
|
|
74710
|
+
return refuse3("the SendAsset is not on Mainnet");
|
|
74711
|
+
if (m.nonce !== expect.mappingNonce || authorization.nonce !== expect.mappingNonce)
|
|
74712
|
+
return refuse3("the SendAsset nonce is not the NonceMapping's");
|
|
74713
|
+
if (parseSendAssetAmount(m.amount, pins.amountForm) !== expect.amountMicros)
|
|
74714
|
+
return refuse3("the SendAsset amount is not the amount asked for");
|
|
74715
|
+
if (m.fromSubAccount !== "")
|
|
74716
|
+
return refuse3("the SendAsset moves from a sub-account");
|
|
74717
|
+
if (m.sourceDex !== "")
|
|
74718
|
+
return refuse3("the SendAsset moves from another dex");
|
|
74719
|
+
if (m.token !== pins.token)
|
|
74720
|
+
return refuse3("the SendAsset moves another token");
|
|
74721
|
+
if (m.destinationDex !== pins.destinationDex)
|
|
74722
|
+
return refuse3("the SendAsset names another destination dex");
|
|
74723
|
+
if (typeof m.destination !== "string" || !pins.depositories.includes(m.destination.toLowerCase()))
|
|
74724
|
+
return refuse3("the SendAsset destination is not a Relay depository on the allow-list");
|
|
74725
|
+
const action = authorization.action;
|
|
74726
|
+
if (!isObject(action) || !exactKeys(action, ["type", "signatureChainId", ...SEND_ASSET_KEYS]))
|
|
74727
|
+
return refuse3("the action has other fields");
|
|
74728
|
+
if (action.type !== "sendAsset" || action.signatureChainId !== "0xa4b1")
|
|
74729
|
+
return refuse3("the action is not a sendAsset");
|
|
74730
|
+
for (const key of SEND_ASSET_KEYS) {
|
|
74731
|
+
if (action[key] !== m[key])
|
|
74732
|
+
return refuse3(`the action's ${key} differs from what is signed`);
|
|
74733
|
+
}
|
|
74734
|
+
return { ok: true };
|
|
74735
|
+
}
|
|
74736
|
+
async function relayRecipientProblem(fetchImpl, requestId, recipient, relayUrl = RELAY_API_URL) {
|
|
74737
|
+
let body;
|
|
74738
|
+
try {
|
|
74739
|
+
const res = await fetchImpl(`${relayUrl}/requests/v3?id=${encodeURIComponent(requestId)}`, { method: "GET" });
|
|
74740
|
+
if (!res.ok)
|
|
74741
|
+
return `Relay's record of the request could not be read (HTTP ${res.status})`;
|
|
74742
|
+
body = await res.json();
|
|
74743
|
+
} catch (error) {
|
|
74744
|
+
return `Relay's record of the request could not be read (${error instanceof Error ? error.message : "unknown"})`;
|
|
74745
|
+
}
|
|
74746
|
+
const requests = isObject(body) && Array.isArray(body.requests) ? body.requests : [];
|
|
74747
|
+
const match = requests.find((r) => isObject(r) && r.id === requestId);
|
|
74748
|
+
if (!isObject(match))
|
|
74749
|
+
return "Relay does not list this request";
|
|
74750
|
+
const named = match.recipient;
|
|
74751
|
+
const same = recipient.startsWith("0x") ? sameEvm(named, recipient) : named === recipient;
|
|
74752
|
+
return same ? null : `Relay's request pays ${String(named)}, not this key's wallet ${recipient}`;
|
|
74753
|
+
}
|
|
74754
|
+
|
|
74755
|
+
// src/commands/perps-withdraw.ts
|
|
74756
|
+
init_render();
|
|
74757
|
+
init_trading();
|
|
74758
|
+
init_errors2();
|
|
74759
|
+
init_store();
|
|
74760
|
+
init_swap();
|
|
74761
|
+
init_vault_support();
|
|
74762
|
+
var WITHDRAW_SCOPE = "perps:withdraw";
|
|
74763
|
+
var ID_RE = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
74764
|
+
var USAGE5 = "Usage: candle perps withdraw <amount> --to solana|hood [--wallet <evm tee>] [--id <id>] [--keystore <path>] | candle perps withdraw status <id>";
|
|
74765
|
+
var WITHDRAW_POLL_MS = 10 * 60 * 1000;
|
|
74766
|
+
var WITHDRAW_POLL_INTERVAL_MS = 5000;
|
|
74767
|
+
var TERMINAL = new Set(["settled", "refunded", "failed"]);
|
|
74768
|
+
function usage5(ctx, line) {
|
|
74769
|
+
writeUsageFailure(ctx.deps, line, ctx.json);
|
|
74770
|
+
return 2;
|
|
74771
|
+
}
|
|
74772
|
+
function withdrawPins(env) {
|
|
74773
|
+
const raw = env.CANDLE_PERPS_WITHDRAW_PINS;
|
|
74774
|
+
if (raw === undefined || raw.trim() === "")
|
|
74775
|
+
return CANDLE_PERPS_WITHDRAW_PINS;
|
|
74776
|
+
let parsed;
|
|
74777
|
+
try {
|
|
74778
|
+
parsed = JSON.parse(raw);
|
|
74779
|
+
} catch {
|
|
74780
|
+
throw new TradingError("INVALID_PINS", "CANDLE_PERPS_WITHDRAW_PINS is not JSON.");
|
|
74781
|
+
}
|
|
74782
|
+
const p = parsed;
|
|
74783
|
+
if (typeof p !== "object" || p === null || typeof p.token !== "string" || typeof p.destinationDex !== "string" || !Array.isArray(p.depositories) || p.depositories.length === 0 || !p.depositories.every((d) => typeof d === "string" && /^0x[0-9a-fA-F]{40}$/.test(d)) || p.amountForm !== "canonical" && p.amountForm !== "fixed6") {
|
|
74784
|
+
throw new TradingError("INVALID_PINS", "CANDLE_PERPS_WITHDRAW_PINS must be {token, destinationDex, depositories: [0x...], amountForm: canonical|fixed6}.");
|
|
74785
|
+
}
|
|
74786
|
+
return {
|
|
74787
|
+
token: p.token,
|
|
74788
|
+
destinationDex: p.destinationDex,
|
|
74789
|
+
depositories: p.depositories.map((d) => d.toLowerCase()),
|
|
74790
|
+
amountForm: p.amountForm
|
|
74791
|
+
};
|
|
74792
|
+
}
|
|
74793
|
+
async function withdrawStatus(ctx, id) {
|
|
74794
|
+
const key = await tradingKey(ctx);
|
|
74795
|
+
const result = await perpsRequest(ctx, key, `/api/v1/agent/perps/withdraw/${encodeURIComponent(id)}`);
|
|
74796
|
+
if (ctx.json)
|
|
74797
|
+
return printTradingResult(ctx, result);
|
|
74798
|
+
const to = result.to ?? {};
|
|
74799
|
+
const relay = result.relay;
|
|
74800
|
+
ctx.deps.stdout.write(`Withdrawal ${safeText(id)}: ${safeText(result.status)}, ${safeText(result.amountUsdc)} USDC to ${safeText(to.address)} (${safeText(to.chain)}), fees ${safeText(result.feesUsdc)} USDC${result.failureReason ? `, ${safeText(result.failureReason)}` : ""}. Relay: ${relay ? safeText(relay.status) : "not read"}.
|
|
74801
|
+
`);
|
|
74802
|
+
return 0;
|
|
74803
|
+
}
|
|
74804
|
+
async function perpsWithdraw(args, ctx) {
|
|
74805
|
+
if (args[0] === "status") {
|
|
74806
|
+
if (args.length !== 2 || !ID_RE.test(args[1]))
|
|
74807
|
+
return usage5(ctx, USAGE5);
|
|
74808
|
+
try {
|
|
74809
|
+
return await withdrawStatus(ctx, args[1]);
|
|
74810
|
+
} catch (error) {
|
|
74811
|
+
return tradingFailure(ctx, error);
|
|
74812
|
+
}
|
|
74813
|
+
}
|
|
74814
|
+
if (args.includes("--yes") || args.includes("-y")) {
|
|
74815
|
+
return usage5(ctx, "candle perps withdraw has no --yes: every withdrawal is confirmed at the terminal.");
|
|
74816
|
+
}
|
|
74817
|
+
const parsed = parseArgs(args, {
|
|
74818
|
+
valueFlags: ["--to", "--wallet", "--id", "--keystore"],
|
|
74819
|
+
pathFlags: ["--keystore"]
|
|
74820
|
+
});
|
|
74821
|
+
if ("error" in parsed)
|
|
74822
|
+
return usage5(ctx, parsed.error);
|
|
74823
|
+
const [amount] = parsed.positionals;
|
|
74824
|
+
const to = parsed.values["--to"];
|
|
74825
|
+
if (parsed.positionals.length !== 1 || !amount || to !== "solana" && to !== "hood")
|
|
74826
|
+
return usage5(ctx, USAGE5);
|
|
74827
|
+
const amountMicros = parseWithdrawAmount(amount);
|
|
74828
|
+
if (amountMicros === null)
|
|
74829
|
+
return usage5(ctx, "The amount must be a positive USDC decimal with at most 6 decimals.");
|
|
74830
|
+
const id = parsed.values["--id"] ?? `withdraw-${randomUUID5()}`;
|
|
74831
|
+
if (!ID_RE.test(id))
|
|
74832
|
+
return usage5(ctx, "--id must be 1 to 128 letters, digits, '.', '_', ':' or '-'.");
|
|
74833
|
+
if (!refuseEnvPassphrase(ctx))
|
|
74834
|
+
return 1;
|
|
74835
|
+
if (!requireTty(ctx, "perps withdraw"))
|
|
74836
|
+
return 1;
|
|
74837
|
+
const resolvedVault = vaultPathFor(ctx, parsed);
|
|
74838
|
+
if ("error" in resolvedVault)
|
|
74839
|
+
return usage5(ctx, resolvedVault.error);
|
|
74840
|
+
try {
|
|
74841
|
+
const pins = withdrawPins(ctx.deps.env);
|
|
74842
|
+
if (pins === null) {
|
|
74843
|
+
throw new TradingError("PERPS_WITHDRAW_UNVERIFIED", "Hyperliquid withdrawals wait on a recorded verification of Relay's quote shape in this release. Nothing was built.");
|
|
74844
|
+
}
|
|
74845
|
+
const key = await tradingKey(ctx);
|
|
74846
|
+
const { wallet } = await perpsWallet(ctx, key, parsed.values["--wallet"], WITHDRAW_SCOPE);
|
|
74847
|
+
if (!wallet)
|
|
74848
|
+
throw new TradingError("TEE_WALLET_REQUIRED", "No EVM TEE wallet resolved.");
|
|
74849
|
+
const { rows } = await listTradingWallets(ctx, key);
|
|
74850
|
+
const ownDestinations = to === "hood" ? [wallet.address] : rows.filter((row) => row.chain === "solana").map((row) => row.address);
|
|
74851
|
+
if (ownDestinations.length === 0)
|
|
74852
|
+
throw new TradingError("TEE_WALLET_REQUIRED", "This key has no Solana TEE wallet, so --to solana has nowhere to land. Withdraw --to hood instead.");
|
|
74853
|
+
ctx.deps.stderr.write(`Withdrawal: ${id}
|
|
74854
|
+
`);
|
|
74855
|
+
const build = await perpsRequest(ctx, key, "/api/v1/agent/perps/withdraw", {
|
|
74856
|
+
clientWithdrawalId: id,
|
|
74857
|
+
amountUsdc: amount,
|
|
74858
|
+
to,
|
|
74859
|
+
wallet: wallet.id
|
|
74860
|
+
});
|
|
74861
|
+
if (build.status !== "quoted")
|
|
74862
|
+
return printTradingResult(ctx, build);
|
|
74863
|
+
const refused = (problem) => new TradingError("PERPS_BUILD_REFUSED", `Refused to sign this withdrawal: ${problem}. Nothing was signed.`);
|
|
74864
|
+
const mapping = verifyWithdrawMapping(build, {
|
|
74865
|
+
id,
|
|
74866
|
+
account: wallet.address,
|
|
74867
|
+
to,
|
|
74868
|
+
amountMicros,
|
|
74869
|
+
ownDestinations,
|
|
74870
|
+
pins
|
|
74871
|
+
});
|
|
74872
|
+
if (!mapping.ok)
|
|
74873
|
+
throw refused(mapping.reason);
|
|
74874
|
+
const recipient = await relayRecipientProblem(ctx.deps.fetch, build.relayRequestId, build.to.address);
|
|
74875
|
+
if (recipient)
|
|
74876
|
+
throw refused(recipient);
|
|
74877
|
+
const output = ctx.json ? ctx.deps.stderr : ctx.deps.stdout;
|
|
74878
|
+
output.write(`Withdraw ${formatWithdrawAmount(amountMicros)} USDC from Hyperliquid account ${safeText(wallet.address)} through Relay.
|
|
74879
|
+
`);
|
|
74880
|
+
output.write(`Relay fees ${safeText(build.feesUsdc)} USDC; you receive about ${safeText(build.netUsdc)} USDC, at least ${safeText(build.minimumOutUsdc)}.
|
|
74881
|
+
`);
|
|
74882
|
+
output.write(`To this key's ${to === "hood" ? "Hood" : "Solana"} TEE wallet:
|
|
74883
|
+
`);
|
|
74884
|
+
output.write(`to ${groupAddress(build.to.address)}
|
|
74885
|
+
`);
|
|
74886
|
+
const positions = await perpsRequest(ctx, key, `/api/v1/agent/perps/positions?walletId=${encodeURIComponent(wallet.id)}`).catch(() => null);
|
|
74887
|
+
if (Array.isArray(positions?.positions) && positions.positions.length > 0)
|
|
74888
|
+
output.write(`This account has open positions: withdrawing margin raises their liquidation risk.
|
|
74889
|
+
`);
|
|
74890
|
+
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
74891
|
+
const opened = await unlockInteractively(ctx, resolvedVault.path, raw, {
|
|
74892
|
+
promptText: "Vault passphrase (input hidden): ",
|
|
74893
|
+
reason: `withdraw ${formatWithdrawAmount(amountMicros)} USDC to ${build.to.address}`
|
|
74894
|
+
});
|
|
74895
|
+
closeVault(opened.vault);
|
|
74896
|
+
const typed = await ctx.deps.promptLine(`Type confirm to withdraw ${formatWithdrawAmount(amountMicros)} USDC to ${build.to.address}: `);
|
|
74897
|
+
if (typed.trim().toLowerCase() !== "confirm")
|
|
74898
|
+
throw new TradingError("CANCELLED", "The withdrawal was not confirmed; nothing was signed.");
|
|
74899
|
+
if (typeof build.expiresAt !== "number" || build.expiresAt <= ctx.deps.now())
|
|
74900
|
+
throw new TradingError("QUOTE_EXPIRED", "The quote expired before signing. Start again with a new --id.");
|
|
74901
|
+
const mappingSignature = await relaySignature(ctx, key, wallet, build.typedData);
|
|
74902
|
+
const authorization = await perpsRequest(ctx, key, "/api/v1/agent/perps/withdraw/authorize", {
|
|
74903
|
+
clientWithdrawalId: id,
|
|
74904
|
+
signature: mappingSignature
|
|
74905
|
+
});
|
|
74906
|
+
const send = verifyWithdrawSendAsset(authorization, { mappingNonce: build.nonce, amountMicros, pins });
|
|
74907
|
+
if (!send.ok)
|
|
74908
|
+
throw new TradingError("PERPS_BUILD_REFUSED", `Refused to sign the transfer: ${send.reason}. The nonce mapping was posted; nothing can move without this signature.`);
|
|
74909
|
+
const signature = await relaySignature(ctx, key, wallet, authorization.typedData);
|
|
74910
|
+
const exchangeBody = hyperliquidExchangeBody(authorization.action, authorization.nonce, signature);
|
|
74911
|
+
let exchange;
|
|
74912
|
+
try {
|
|
74913
|
+
const res = await ctx.deps.fetch(HYPERLIQUID_EXCHANGE_URLS.mainnet, {
|
|
74914
|
+
method: "POST",
|
|
74915
|
+
headers: { "content-type": "application/json" },
|
|
74916
|
+
body: JSON.stringify(exchangeBody)
|
|
74917
|
+
});
|
|
74918
|
+
const text = await res.text();
|
|
74919
|
+
if (!res.ok)
|
|
74920
|
+
throw new Error(`HTTP ${res.status}`);
|
|
74921
|
+
exchange = JSON.parse(text);
|
|
74922
|
+
} catch (error) {
|
|
74923
|
+
throw new TradingError("PERPS_SUBMIT_FAILED", `Signed, but the submit to Hyperliquid failed (${error instanceof Error ? error.message : "unknown"}). The signed transfer is in details.exchangeBody and stays valid for two days; check candle perps withdraw status ${id} before resubmitting.`, { exitCode: 3, details: { clientWithdrawalId: id, exchangeBody } });
|
|
74924
|
+
}
|
|
74925
|
+
await perpsRequest(ctx, key, "/api/v1/agent/perps/withdraw/submitted", {
|
|
74926
|
+
clientWithdrawalId: id,
|
|
74927
|
+
exchangeResponse: exchange
|
|
74928
|
+
}).catch(() => null);
|
|
74929
|
+
const refusal = venueRefusal(exchange);
|
|
74930
|
+
if (refusal)
|
|
74931
|
+
throw new TradingError("PERPS_VENUE_REFUSED", refusal, { details: { clientWithdrawalId: id, exchange } });
|
|
74932
|
+
ctx.deps.stderr.write(`Submitted to Hyperliquid; following Relay's fill.
|
|
74933
|
+
`);
|
|
74934
|
+
const deadline = ctx.deps.now() + WITHDRAW_POLL_MS;
|
|
74935
|
+
let status = await perpsRequest(ctx, key, `/api/v1/agent/perps/withdraw/${encodeURIComponent(id)}`);
|
|
74936
|
+
while (!TERMINAL.has(String(status.status)) && ctx.deps.now() < deadline) {
|
|
74937
|
+
await ctx.deps.sleep(WITHDRAW_POLL_INTERVAL_MS);
|
|
74938
|
+
status = await perpsRequest(ctx, key, `/api/v1/agent/perps/withdraw/${encodeURIComponent(id)}`);
|
|
74939
|
+
}
|
|
74940
|
+
if (!TERMINAL.has(String(status.status)))
|
|
74941
|
+
ctx.deps.stderr.write(`Still ${safeText(status.status)} after 10 minutes. Check it with candle perps withdraw status ${id}.
|
|
74942
|
+
`);
|
|
74943
|
+
return printTradingResult(ctx, { ...status, signature, exchange });
|
|
74944
|
+
} catch (error) {
|
|
74945
|
+
if (isVaultError(error))
|
|
74946
|
+
return writeVaultFailure(ctx, error);
|
|
74947
|
+
return tradingFailure(ctx, error);
|
|
74948
|
+
}
|
|
74949
|
+
}
|
|
74950
|
+
async function relaySignature(ctx, key, wallet, typedData) {
|
|
74951
|
+
const relay = await perpsRequest(ctx, key, `/api/v1/agent/wallets/${encodeURIComponent(wallet.id)}/sign`, typedDataAuthorization(wallet, typedData));
|
|
74952
|
+
if (typeof relay.signature !== "string" || !/^0x[0-9a-fA-F]{130}$/.test(relay.signature))
|
|
74953
|
+
throw new TradingError("INVALID_RESPONSE", "The relay did not return a signature.");
|
|
74954
|
+
return relay.signature;
|
|
74955
|
+
}
|
|
74956
|
+
|
|
74544
74957
|
// src/commands/plans.ts
|
|
74545
74958
|
init_args();
|
|
74546
74959
|
init_client();
|
|
@@ -74615,12 +75028,12 @@ function planPromotionLine(table) {
|
|
|
74615
75028
|
|
|
74616
75029
|
// src/commands/plans.ts
|
|
74617
75030
|
init_render();
|
|
74618
|
-
var
|
|
75031
|
+
var USAGE6 = "Usage: candle plans";
|
|
74619
75032
|
async function plans(args, ctx) {
|
|
74620
75033
|
const parsed = parseArgs(args, {});
|
|
74621
75034
|
if ("error" in parsed || parsed.positionals.length > 0) {
|
|
74622
75035
|
writeUsageFailure(ctx.deps, "error" in parsed ? `${parsed.error}
|
|
74623
|
-
${
|
|
75036
|
+
${USAGE6}` : USAGE6, ctx.json);
|
|
74624
75037
|
return 2;
|
|
74625
75038
|
}
|
|
74626
75039
|
const result = await apiRequest("/api/v1/agent/plans", {
|
|
@@ -74859,20 +75272,20 @@ async function writeSecretNames(deps, profile, names) {
|
|
|
74859
75272
|
else
|
|
74860
75273
|
await deps.updateProfile(profile, { secretNames: sorted });
|
|
74861
75274
|
}
|
|
74862
|
-
function
|
|
75275
|
+
function usage6(ctx, line) {
|
|
74863
75276
|
writeUsageFailure(ctx.deps, line, ctx.json);
|
|
74864
75277
|
return 2;
|
|
74865
75278
|
}
|
|
74866
75279
|
async function secretsSet(args, ctx) {
|
|
74867
75280
|
const parsed = parseArgs(args, {});
|
|
74868
75281
|
if ("error" in parsed)
|
|
74869
|
-
return
|
|
75282
|
+
return usage6(ctx, parsed.error);
|
|
74870
75283
|
const [raw, extra] = parsed.positionals;
|
|
74871
75284
|
if (!raw || extra !== undefined)
|
|
74872
|
-
return
|
|
75285
|
+
return usage6(ctx, "Usage: candle secrets set <name>");
|
|
74873
75286
|
const name2 = canonicalSecretName(raw);
|
|
74874
75287
|
if (name2 === undefined) {
|
|
74875
|
-
return
|
|
75288
|
+
return usage6(ctx, `A secret name is letters, digits and underscores, starting with a letter: ${raw}`);
|
|
74876
75289
|
}
|
|
74877
75290
|
if (!ctx.deps.isTTY.stdin || !ctx.deps.isTTY.stdout) {
|
|
74878
75291
|
writeLocalFailure(ctx.deps, {
|
|
@@ -74884,7 +75297,7 @@ async function secretsSet(args, ctx) {
|
|
|
74884
75297
|
}
|
|
74885
75298
|
const value = await ctx.deps.promptSecret(`Value for ${name2} (input hidden): `);
|
|
74886
75299
|
if (value.length === 0)
|
|
74887
|
-
return
|
|
75300
|
+
return usage6(ctx, "An empty value was typed; nothing was stored.");
|
|
74888
75301
|
try {
|
|
74889
75302
|
await ctx.deps.secretsStore.set(secretRef(ctx.profile, name2), value);
|
|
74890
75303
|
} catch (error) {
|
|
@@ -74904,9 +75317,9 @@ async function secretsSet(args, ctx) {
|
|
|
74904
75317
|
async function secretsList(args, ctx) {
|
|
74905
75318
|
const parsed = parseArgs(args, {});
|
|
74906
75319
|
if ("error" in parsed)
|
|
74907
|
-
return
|
|
75320
|
+
return usage6(ctx, parsed.error);
|
|
74908
75321
|
if (parsed.positionals.length > 0)
|
|
74909
|
-
return
|
|
75322
|
+
return usage6(ctx, `Unexpected argument: ${parsed.positionals[0]}`);
|
|
74910
75323
|
const names = await storedSecretNames(ctx.deps, ctx.profile);
|
|
74911
75324
|
if (ctx.json) {
|
|
74912
75325
|
ctx.deps.stdout.write(`${JSON.stringify({ ok: true, names, backend: ctx.deps.backend })}
|
|
@@ -74925,13 +75338,13 @@ async function secretsList(args, ctx) {
|
|
|
74925
75338
|
async function secretsRemove(args, ctx) {
|
|
74926
75339
|
const parsed = parseArgs(args, {});
|
|
74927
75340
|
if ("error" in parsed)
|
|
74928
|
-
return
|
|
75341
|
+
return usage6(ctx, parsed.error);
|
|
74929
75342
|
const [raw, extra] = parsed.positionals;
|
|
74930
75343
|
if (!raw || extra !== undefined)
|
|
74931
|
-
return
|
|
75344
|
+
return usage6(ctx, "Usage: candle secrets remove <name>");
|
|
74932
75345
|
const name2 = canonicalSecretName(raw);
|
|
74933
75346
|
if (name2 === undefined) {
|
|
74934
|
-
return
|
|
75347
|
+
return usage6(ctx, `A secret name is letters, digits and underscores, starting with a letter: ${raw}`);
|
|
74935
75348
|
}
|
|
74936
75349
|
await ctx.deps.secretsStore.delete(secretRef(ctx.profile, name2));
|
|
74937
75350
|
const names = await storedSecretNames(ctx.deps, ctx.profile);
|
|
@@ -77085,7 +77498,7 @@ init_args();
|
|
|
77085
77498
|
init_render();
|
|
77086
77499
|
init_trading();
|
|
77087
77500
|
init_swap();
|
|
77088
|
-
import { randomUUID as
|
|
77501
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
77089
77502
|
var withdrawal = exports_external.object({ withdrawal: exports_external.string(), amountRaw: exports_external.string(), endTime: exports_external.string(), claimable: exports_external.boolean() }).passthrough();
|
|
77090
77503
|
var positionsSchema = exports_external.object({
|
|
77091
77504
|
success: exports_external.literal(true),
|
|
@@ -77134,16 +77547,16 @@ async function runStake(action, args, ctx) {
|
|
|
77134
77547
|
valueFlags: ["--wallet", "--client-stake-id"],
|
|
77135
77548
|
booleanFlags: ["--yes", ...action === "claim" ? ["--all"] : []]
|
|
77136
77549
|
});
|
|
77137
|
-
const
|
|
77550
|
+
const usage7 = "Usage: candle stake [add <amount> | remove <amount> | finish [<withdrawal>] | claim <rewardId> | claim --all] [--wallet <main|id|address|label>] [--client-stake-id <id>] [--yes] [--json]";
|
|
77138
77551
|
if ("error" in parsed) {
|
|
77139
77552
|
writeUsageFailure(ctx.deps, parsed.error, ctx.json);
|
|
77140
77553
|
return 2;
|
|
77141
77554
|
}
|
|
77142
77555
|
const { values, positionals, booleans } = parsed;
|
|
77143
|
-
const id = values["--client-stake-id"] ?? `stake-${
|
|
77556
|
+
const id = values["--client-stake-id"] ?? `stake-${randomUUID6()}`;
|
|
77144
77557
|
const all = booleans.has("--all");
|
|
77145
77558
|
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)) {
|
|
77146
|
-
writeUsageFailure(ctx.deps,
|
|
77559
|
+
writeUsageFailure(ctx.deps, usage7, ctx.json);
|
|
77147
77560
|
return 2;
|
|
77148
77561
|
}
|
|
77149
77562
|
try {
|
|
@@ -77925,7 +78338,7 @@ init_render();
|
|
|
77925
78338
|
init_solana_verify();
|
|
77926
78339
|
init_trading();
|
|
77927
78340
|
init_swap();
|
|
77928
|
-
var
|
|
78341
|
+
var USAGE7 = "Usage: candle transfer --to <address|wallet name|vault> --asset <SOL|USDC|CNDL|ETH|USDG>|--mint <mint>|--token <0x...> --amount <decimal|max> [--wallet <name>] [--rpc-url <url>] [--yes] [--json]";
|
|
77929
78342
|
var TRANSFER_ASSETS = [...Object.keys(BASES), ...Object.keys(HOOD_BASES)];
|
|
77930
78343
|
var BASE58_ADDRESS = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
77931
78344
|
async function readLinkedWallets(ctx, key) {
|
|
@@ -78011,7 +78424,7 @@ async function transfer(args, ctx) {
|
|
|
78011
78424
|
const flags = parsed.values;
|
|
78012
78425
|
const asset = flags["--asset"]?.toUpperCase();
|
|
78013
78426
|
if (parsed.positionals.length !== 0 || !flags["--to"] || !flags["--amount"] || [flags["--asset"], flags["--mint"], flags["--token"]].filter(Boolean).length !== 1 || asset !== undefined && !TRANSFER_ASSETS.includes(asset) || flags["--mint"] !== undefined && !BASE58_ADDRESS.test(flags["--mint"]) || flags["--token"] !== undefined && !checkEvmAddress(flags["--token"]).ok) {
|
|
78014
|
-
writeUsageFailure(ctx.deps,
|
|
78427
|
+
writeUsageFailure(ctx.deps, USAGE7, ctx.json);
|
|
78015
78428
|
return 2;
|
|
78016
78429
|
}
|
|
78017
78430
|
const to = flags["--to"];
|
|
@@ -81246,9 +81659,11 @@ async function planTransfer(input) {
|
|
|
81246
81659
|
const destination = ataFor(profile, toKey);
|
|
81247
81660
|
const instructions = [];
|
|
81248
81661
|
const accountCreationLines = [];
|
|
81662
|
+
let accountRent;
|
|
81249
81663
|
const destinationState = await tokenAccountFrozen(rpc, encodePubkey(destination));
|
|
81250
81664
|
if (!destinationState.exists) {
|
|
81251
|
-
const rent = await rpc.getMinimumBalanceForRentExemption(
|
|
81665
|
+
const rent = await rpc.getMinimumBalanceForRentExemption(profile.associatedTokenAccountSize);
|
|
81666
|
+
accountRent = rent;
|
|
81252
81667
|
instructions.push(createAssociatedTokenAccountIdempotent({ payer: fromKey, owner: toKey, mint, tokenProgram }));
|
|
81253
81668
|
accountCreationLines.push(`create associated token account ${encodePubkey(destination)} (idempotent)`, `account owner ${input.to}`, `account rent ${rent} lamports, paid by ${input.from} if created`);
|
|
81254
81669
|
}
|
|
@@ -81284,6 +81699,8 @@ async function planTransfer(input) {
|
|
|
81284
81699
|
from: input.from,
|
|
81285
81700
|
to: input.to,
|
|
81286
81701
|
instructions,
|
|
81702
|
+
sourceTokenAccount: encodePubkey(source),
|
|
81703
|
+
...accountRent !== undefined ? { accountRent } : {},
|
|
81287
81704
|
token: {
|
|
81288
81705
|
profile,
|
|
81289
81706
|
extraAccountsMissing: !hook.ok,
|
|
@@ -81314,6 +81731,45 @@ async function quoteTransferFee(rpc, from, instructions) {
|
|
|
81314
81731
|
});
|
|
81315
81732
|
return rpc.getFeeForMessage(toBase642(message));
|
|
81316
81733
|
}
|
|
81734
|
+
var FALLBACK_FEE_LAMPORTS = 5000n;
|
|
81735
|
+
var TOKEN_ACCOUNT_AMOUNT_OFFSET = 64;
|
|
81736
|
+
function tokenAccountAmount(data) {
|
|
81737
|
+
if (data.length < TOKEN_ACCOUNT_AMOUNT_OFFSET + 8)
|
|
81738
|
+
return 0n;
|
|
81739
|
+
return new DataView(data.buffer, data.byteOffset + TOKEN_ACCOUNT_AMOUNT_OFFSET, 8).getBigUint64(0, true);
|
|
81740
|
+
}
|
|
81741
|
+
async function assertTransferAffordable(solana, plan, feeQuote) {
|
|
81742
|
+
const fee = feeQuote ?? FALLBACK_FEE_LAMPORTS;
|
|
81743
|
+
const rent = plan.accountRent ?? 0n;
|
|
81744
|
+
const lamportsNeeded = (plan.asset === "SOL" ? plan.amountRaw : 0n) + fee + rent;
|
|
81745
|
+
const lamports = await solana.read(() => solana.rpc.getBalance(plan.from));
|
|
81746
|
+
if (plan.sourceTokenAccount !== undefined) {
|
|
81747
|
+
const account = await solana.read(() => solana.rpc.getAccountInfo(plan.sourceTokenAccount));
|
|
81748
|
+
const held = account === null ? 0n : tokenAccountAmount(account.data);
|
|
81749
|
+
if (held < plan.amountRaw) {
|
|
81750
|
+
throw new VaultError("TRANSFER_INSUFFICIENT_FUNDS", `${plan.from} holds ${held} raw of ${plan.asset} and this transfer moves ${plan.amountRaw} raw (${plan.amount}). Nothing was signed or sent.`, {
|
|
81751
|
+
suggestion: "Fund the key with this token first, or send a smaller --amount.",
|
|
81752
|
+
details: {
|
|
81753
|
+
from: plan.from,
|
|
81754
|
+
asset: plan.asset,
|
|
81755
|
+
heldRaw: held.toString(),
|
|
81756
|
+
neededRaw: plan.amountRaw.toString()
|
|
81757
|
+
}
|
|
81758
|
+
});
|
|
81759
|
+
}
|
|
81760
|
+
}
|
|
81761
|
+
if (lamports < lamportsNeeded) {
|
|
81762
|
+
const parts = [
|
|
81763
|
+
...plan.asset === "SOL" ? [`${plan.amountRaw} to send`] : [],
|
|
81764
|
+
`${fee} fee${feeQuote === null ? " (estimated)" : ""}`,
|
|
81765
|
+
...rent > 0n ? [`${rent} token account rent`] : []
|
|
81766
|
+
];
|
|
81767
|
+
throw new VaultError("TRANSFER_INSUFFICIENT_FUNDS", `${plan.from} holds ${lamports} lamports and this transfer needs ${lamportsNeeded} (${parts.join(" + ")}). Nothing was signed or sent.`, {
|
|
81768
|
+
suggestion: "Fund the key with SOL first, or send a smaller --amount. candle vault list --balances shows each key's SOL.",
|
|
81769
|
+
details: { from: plan.from, heldLamports: lamports.toString(), neededLamports: lamportsNeeded.toString() }
|
|
81770
|
+
});
|
|
81771
|
+
}
|
|
81772
|
+
}
|
|
81317
81773
|
function displayTransferPlan(ctx, plan, feeQuote) {
|
|
81318
81774
|
ctx.deps.stdout.write(`Decoded transfer (local signing only):
|
|
81319
81775
|
`);
|
|
@@ -81348,8 +81804,9 @@ async function signAndBroadcastTransfer(input) {
|
|
|
81348
81804
|
} catch (error) {
|
|
81349
81805
|
if (isRateLimited(error))
|
|
81350
81806
|
notePostSignatureRateLimit(input.ctx, sigB58);
|
|
81351
|
-
return { signature: sigB58, finalized: false };
|
|
81807
|
+
return { signature: sigB58, finalized: false, seen: true };
|
|
81352
81808
|
}
|
|
81809
|
+
let seen = false;
|
|
81353
81810
|
for (let i = 0;i < 30; i++) {
|
|
81354
81811
|
await input.ctx.deps.sleep(500);
|
|
81355
81812
|
let status;
|
|
@@ -81358,17 +81815,24 @@ async function signAndBroadcastTransfer(input) {
|
|
|
81358
81815
|
} catch (error) {
|
|
81359
81816
|
if (isRateLimited(error))
|
|
81360
81817
|
notePostSignatureRateLimit(input.ctx, sigB58);
|
|
81361
|
-
return { signature: sigB58, finalized: false };
|
|
81818
|
+
return { signature: sigB58, finalized: false, seen: true };
|
|
81362
81819
|
}
|
|
81820
|
+
if (status)
|
|
81821
|
+
seen = true;
|
|
81363
81822
|
if (status?.confirmationStatus === "finalized") {
|
|
81364
81823
|
if (status.err) {
|
|
81365
81824
|
const named = namedSendFailure(input.plan);
|
|
81366
81825
|
throw new VaultError("VAULT_WRITE_FAILED", `Transfer failed on chain${named ? ` (${named})` : ""}. Any pending funding receipt will be reconciled on a rerun.`);
|
|
81367
81826
|
}
|
|
81368
|
-
return { signature: sigB58, finalized: true };
|
|
81827
|
+
return { signature: sigB58, finalized: true, seen: true };
|
|
81369
81828
|
}
|
|
81370
81829
|
}
|
|
81371
|
-
return { signature: sigB58, finalized: false };
|
|
81830
|
+
return { signature: sigB58, finalized: false, seen };
|
|
81831
|
+
}
|
|
81832
|
+
function unfinalizedSendLine(result, suffix = "") {
|
|
81833
|
+
return result.seen ? `Submitted ${result.signature}; finality is not yet confirmed.${suffix}
|
|
81834
|
+
` : `Sent ${result.signature}, but no status read found it on the network: it was most likely dropped and nothing moved. Check the signature in an explorer before sending again.${suffix}
|
|
81835
|
+
`;
|
|
81372
81836
|
}
|
|
81373
81837
|
|
|
81374
81838
|
// src/commands/vault-fund.ts
|
|
@@ -81407,6 +81871,7 @@ async function fundExternal(ctx, vault, external2, input) {
|
|
|
81407
81871
|
}));
|
|
81408
81872
|
const feeQuote = await solana.read(() => quoteTransferFee(solana.rpc, plan.from, plan.instructions));
|
|
81409
81873
|
displayTransferPlan(ctx, plan, feeQuote);
|
|
81874
|
+
await assertTransferAffordable(solana, plan, feeQuote);
|
|
81410
81875
|
await confirmSend(ctx, { amount: plan.amount, asset: plan.asset, address: external2.address });
|
|
81411
81876
|
const secret = await decryptKey(vault, fromEntry.id);
|
|
81412
81877
|
try {
|
|
@@ -81420,12 +81885,12 @@ async function fundExternal(ctx, vault, external2, input) {
|
|
|
81420
81885
|
from: plan.from,
|
|
81421
81886
|
amount: plan.amount,
|
|
81422
81887
|
asset: plan.asset,
|
|
81423
|
-
finalized: result.finalized
|
|
81888
|
+
finalized: result.finalized,
|
|
81889
|
+
...result.finalized ? {} : { seen: result.seen }
|
|
81424
81890
|
});
|
|
81425
81891
|
} else {
|
|
81426
81892
|
ctx.deps.stdout.write(result.finalized ? `Funded ${external2.label} (${external2.address}) with ${plan.amount} ${plan.asset}: ${result.signature}
|
|
81427
|
-
` :
|
|
81428
|
-
`);
|
|
81893
|
+
` : unfinalizedSendLine(result));
|
|
81429
81894
|
}
|
|
81430
81895
|
return result.finalized ? 0 : 3;
|
|
81431
81896
|
} finally {
|
|
@@ -81522,6 +81987,7 @@ async function vaultFund(args, ctx) {
|
|
|
81522
81987
|
}));
|
|
81523
81988
|
const feeQuote = await solana.read(() => quoteTransferFee(solana.rpc, plan.from, plan.instructions));
|
|
81524
81989
|
displayTransferPlan(ctx, plan, feeQuote);
|
|
81990
|
+
await assertTransferAffordable(solana, plan, feeQuote);
|
|
81525
81991
|
await confirmSend(ctx, { amount: plan.amount, asset: plan.asset, address: teeAddress });
|
|
81526
81992
|
const secret = await decryptKey(vault, fromEntry.id);
|
|
81527
81993
|
try {
|
|
@@ -81556,12 +82022,12 @@ async function vaultFund(args, ctx) {
|
|
|
81556
82022
|
from: plan.from,
|
|
81557
82023
|
amount: plan.amount,
|
|
81558
82024
|
asset: plan.asset,
|
|
81559
|
-
finalized: result.finalized
|
|
82025
|
+
finalized: result.finalized,
|
|
82026
|
+
...result.finalized ? {} : { seen: result.seen }
|
|
81560
82027
|
});
|
|
81561
82028
|
} else {
|
|
81562
82029
|
ctx.deps.stdout.write(result.finalized ? `Funded ${teeAddress} with ${plan.amount} ${plan.asset}: ${result.signature}
|
|
81563
|
-
` :
|
|
81564
|
-
`);
|
|
82030
|
+
` : unfinalizedSendLine(result, " Receipt recorded."));
|
|
81565
82031
|
}
|
|
81566
82032
|
return result.finalized ? 0 : 3;
|
|
81567
82033
|
} finally {
|
|
@@ -85371,8 +85837,7 @@ async function vaultStatus(args, ctx) {
|
|
|
85371
85837
|
return usage(ctx, resolvedVault.error);
|
|
85372
85838
|
const path = resolvedVault.path;
|
|
85373
85839
|
const unlock = parsed.booleans.has("--unlock");
|
|
85374
|
-
|
|
85375
|
-
return 1;
|
|
85840
|
+
const unlockCtx = refusePromptsWithoutTerminal(ctx, "vault status --unlock");
|
|
85376
85841
|
return runVaultCommand(ctx, async ({ hold }) => {
|
|
85377
85842
|
const raw = await readVaultRaw(path);
|
|
85378
85843
|
if (raw === null)
|
|
@@ -85389,7 +85854,9 @@ async function vaultStatus(args, ctx) {
|
|
|
85389
85854
|
const backup = sidecar ? describeLastBackup(sidecar, file.envelopes) : undefined;
|
|
85390
85855
|
let unlocked;
|
|
85391
85856
|
if (unlock) {
|
|
85392
|
-
const vault = hold((await unlockInteractively(
|
|
85857
|
+
const vault = hold((await unlockInteractively(unlockCtx, path, raw, {
|
|
85858
|
+
acceptOlderCopy: parsed.booleans.has("--accept-older-copy")
|
|
85859
|
+
})).vault);
|
|
85393
85860
|
unlocked = {
|
|
85394
85861
|
entries: vault.index.entries.map(describeEntry),
|
|
85395
85862
|
duplicateLabels: duplicateLabels(vault.index).map((duplicate) => ({
|
|
@@ -85815,6 +86282,7 @@ async function vaultTransfer(args, ctx) {
|
|
|
85815
86282
|
}));
|
|
85816
86283
|
const feeQuote = await solana.read(() => quoteTransferFee(solana.rpc, plan.from, plan.instructions));
|
|
85817
86284
|
displayTransferPlan(ctx, plan, feeQuote);
|
|
86285
|
+
await assertTransferAffordable(solana, plan, feeQuote);
|
|
85818
86286
|
if (promoted) {
|
|
85819
86287
|
const notice = serverStateNotice(await readTeeServerState(ctx, fromEntry, apiKey));
|
|
85820
86288
|
if (notice !== undefined)
|
|
@@ -85843,12 +86311,12 @@ async function vaultTransfer(args, ctx) {
|
|
|
85843
86311
|
asset: plan.asset,
|
|
85844
86312
|
amountRaw: plan.amountRaw.toString(),
|
|
85845
86313
|
finalized: result.finalized,
|
|
86314
|
+
...result.finalized ? {} : { seen: result.seen },
|
|
85846
86315
|
...activityReport !== undefined ? { activityReport } : {}
|
|
85847
86316
|
});
|
|
85848
86317
|
} else {
|
|
85849
86318
|
ctx.deps.stdout.write(result.finalized ? `Transferred ${plan.amount} ${plan.asset} to ${to}: ${result.signature}
|
|
85850
|
-
` :
|
|
85851
|
-
`);
|
|
86319
|
+
` : unfinalizedSendLine(result));
|
|
85852
86320
|
}
|
|
85853
86321
|
return result.finalized ? 0 : 3;
|
|
85854
86322
|
} finally {
|
|
@@ -85862,7 +86330,7 @@ init_args();
|
|
|
85862
86330
|
init_release();
|
|
85863
86331
|
import { dirname as dirname10, join as join11 } from "node:path";
|
|
85864
86332
|
init_render();
|
|
85865
|
-
var
|
|
86333
|
+
var USAGE8 = "Usage: candle verify <file> --bundle <path> [--identity <uri>] [--issuer <url>]";
|
|
85866
86334
|
async function resolveIdentity(deps, bundlePath, flag) {
|
|
85867
86335
|
if (flag)
|
|
85868
86336
|
return { kind: "ok", uri: flag, provenance: "identity from --identity" };
|
|
@@ -85894,19 +86362,19 @@ async function verify(args, ctx) {
|
|
|
85894
86362
|
});
|
|
85895
86363
|
if ("error" in parsed) {
|
|
85896
86364
|
writeUsageFailure(deps, `${parsed.error}
|
|
85897
|
-
${
|
|
86365
|
+
${USAGE8}`, json);
|
|
85898
86366
|
return 2;
|
|
85899
86367
|
}
|
|
85900
86368
|
const file = parsed.positionals[0];
|
|
85901
86369
|
if (parsed.positionals.length !== 1 || file === undefined) {
|
|
85902
86370
|
writeUsageFailure(deps, `verify takes exactly one file.
|
|
85903
|
-
${
|
|
86371
|
+
${USAGE8}`, json);
|
|
85904
86372
|
return 2;
|
|
85905
86373
|
}
|
|
85906
86374
|
const bundlePath = parsed.values["--bundle"];
|
|
85907
86375
|
if (!bundlePath) {
|
|
85908
86376
|
writeUsageFailure(deps, `--bundle is required.
|
|
85909
|
-
${
|
|
86377
|
+
${USAGE8}`, json);
|
|
85910
86378
|
return 2;
|
|
85911
86379
|
}
|
|
85912
86380
|
const resolved = await resolveIdentity(deps, bundlePath, parsed.values["--identity"]);
|
|
@@ -85920,7 +86388,7 @@ ${USAGE7}`, json);
|
|
|
85920
86388
|
}
|
|
85921
86389
|
if (resolved.kind === "absent") {
|
|
85922
86390
|
writeUsageFailure(deps, `--identity is required: there is no latest.json beside ${bundlePath} to take the release version from.
|
|
85923
|
-
${
|
|
86391
|
+
${USAGE8}`, json);
|
|
85924
86392
|
return 2;
|
|
85925
86393
|
}
|
|
85926
86394
|
const identity = resolved.uri;
|
|
@@ -86029,7 +86497,7 @@ function writeFailure2(ctx, result) {
|
|
|
86029
86497
|
}
|
|
86030
86498
|
async function setAllowLaunch(args, ctx, enabled) {
|
|
86031
86499
|
const { deps, apiUrl, json } = ctx;
|
|
86032
|
-
const
|
|
86500
|
+
const usage7 = enabled ? USAGE_ALLOW : USAGE_DISALLOW;
|
|
86033
86501
|
const command = enabled ? "wallets allow-launch" : "wallets disallow-launch";
|
|
86034
86502
|
const parsed = parseArgs(args, enabled ? {} : { booleanFlags: ["--yes"] });
|
|
86035
86503
|
if ("error" in parsed) {
|
|
@@ -86037,7 +86505,7 @@ async function setAllowLaunch(args, ctx, enabled) {
|
|
|
86037
86505
|
return 2;
|
|
86038
86506
|
}
|
|
86039
86507
|
if (parsed.positionals.length === 0) {
|
|
86040
|
-
writeUsageFailure(deps, `Name at least one wallet. ${
|
|
86508
|
+
writeUsageFailure(deps, `Name at least one wallet. ${usage7}`, json);
|
|
86041
86509
|
return 2;
|
|
86042
86510
|
}
|
|
86043
86511
|
const skipConfirm = !enabled && parsed.booleans.has("--yes");
|
|
@@ -86140,8 +86608,8 @@ init_args();
|
|
|
86140
86608
|
init_render();
|
|
86141
86609
|
init_trading();
|
|
86142
86610
|
init_swap();
|
|
86143
|
-
import { randomUUID as
|
|
86144
|
-
var
|
|
86611
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
86612
|
+
var USAGE9 = "Usage: candle wallet close-empty [--wallet embedded] [--keep <mint>]... [--client-trade-id <id>] [--yes] [--json]";
|
|
86145
86613
|
var BASE58_ADDRESS2 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
86146
86614
|
var LISTED_ROWS = 50;
|
|
86147
86615
|
function splitKeep(args) {
|
|
@@ -86216,7 +86684,7 @@ async function embeddedSolana(ctx, key, wallet) {
|
|
|
86216
86684
|
async function walletsCloseEmpty(args, ctx) {
|
|
86217
86685
|
const lifted = splitKeep(args);
|
|
86218
86686
|
if ("error" in lifted) {
|
|
86219
|
-
writeUsageFailure(ctx.deps, `${lifted.error}. ${
|
|
86687
|
+
writeUsageFailure(ctx.deps, `${lifted.error}. ${USAGE9}`, ctx.json);
|
|
86220
86688
|
return 2;
|
|
86221
86689
|
}
|
|
86222
86690
|
const parsed = parseArgs(lifted.rest, { valueFlags: ["--wallet", "--client-trade-id"], booleanFlags: ["--yes"] });
|
|
@@ -86225,9 +86693,9 @@ async function walletsCloseEmpty(args, ctx) {
|
|
|
86225
86693
|
return 2;
|
|
86226
86694
|
}
|
|
86227
86695
|
const named = parsed.values["--client-trade-id"];
|
|
86228
|
-
const id = named ?? `close-${
|
|
86696
|
+
const id = named ?? `close-${randomUUID7()}`;
|
|
86229
86697
|
if (parsed.positionals.length !== 0 || !validClientId(id) || !lifted.keep.every((m) => BASE58_ADDRESS2.test(m))) {
|
|
86230
|
-
writeUsageFailure(ctx.deps,
|
|
86698
|
+
writeUsageFailure(ctx.deps, USAGE9, ctx.json);
|
|
86231
86699
|
return 2;
|
|
86232
86700
|
}
|
|
86233
86701
|
const keep = lifted.keep;
|
|
@@ -86356,7 +86824,7 @@ function trustTable(rows) {
|
|
|
86356
86824
|
}
|
|
86357
86825
|
async function setTrust(args, ctx, trusted) {
|
|
86358
86826
|
const { deps, apiUrl, json } = ctx;
|
|
86359
|
-
const
|
|
86827
|
+
const usage7 = trusted ? USAGE_TRUST : USAGE_UNTRUST;
|
|
86360
86828
|
const command = trusted ? "wallets trust" : "wallets untrust";
|
|
86361
86829
|
const parsed = parseArgs(args, trusted ? {} : { booleanFlags: ["--yes"] });
|
|
86362
86830
|
if ("error" in parsed) {
|
|
@@ -86364,7 +86832,7 @@ async function setTrust(args, ctx, trusted) {
|
|
|
86364
86832
|
return 2;
|
|
86365
86833
|
}
|
|
86366
86834
|
if (parsed.positionals.length === 0) {
|
|
86367
|
-
writeUsageFailure(deps, `Name at least one wallet. ${
|
|
86835
|
+
writeUsageFailure(deps, `Name at least one wallet. ${usage7}`, json);
|
|
86368
86836
|
return 2;
|
|
86369
86837
|
}
|
|
86370
86838
|
const skipConfirm = !trusted && parsed.booleans.has("--yes");
|
|
@@ -86651,7 +87119,8 @@ var COMMANDS = {
|
|
|
86651
87119
|
cancel: perpsCancel,
|
|
86652
87120
|
orders: perpsOrders,
|
|
86653
87121
|
positions: perpsPositions,
|
|
86654
|
-
leverage: perpsLeverage
|
|
87122
|
+
leverage: perpsLeverage,
|
|
87123
|
+
withdraw: perpsWithdraw
|
|
86655
87124
|
}
|
|
86656
87125
|
},
|
|
86657
87126
|
auth: { subcommands: { login: authLogin, status: authStatus, logout: authLogout } },
|
package/package.json
CHANGED