@candledottv/cli 0.13.17 → 0.13.18
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 +7 -4
- package/dist/index.js +280 -80
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -116,6 +116,7 @@ chosen words from your saved copy. Clear your terminal's scrollback too: the CLI
|
|
|
116
116
|
| `candle keys wallets <prefix>`, `keys wallets set <prefix> --wallets <id,id>`, `keys wallets scope <prefix> --scope all\|selected` | Each key is an agent profile with its own wallet set: list it, replace it, or limit the profile to its assigned wallets. |
|
|
117
117
|
| `candle wallets` (alias `candle wallet`) | Shows the account's embedded (launch) wallets and any linked wallets, using the API key, with a `Signer` column saying whether this machine holds each linked wallet's signing key and a `Trusted` column saying whether it is yours (linked while signed in, or marked with `trust`). It links and revokes; it never generates or prints a key, which the vault now derives. |
|
|
118
118
|
| `candle wallets import --chain solana\|evm`, `wallets revoke <wallet-id>` | Link a wallet you own (key from `--key-file` or a hidden prompt, sealed to Privy before it leaves the machine), or unlink one. |
|
|
119
|
+
| `candle wallets allowlist [--all]` | Print the account's withdrawal allowlist: each approved address's id, chain, address and label, the same rows `candle transfer --to <allowlist label>` resolves by. `--all` adds removed rows. Approving an address is the owner's, in the agent console's Wallets tab; an API key cannot approve. An acting key can revoke an address; a read-only key cannot. |
|
|
119
120
|
| `candle wallets authorize` | Gives agents access to the account's embedded wallets. The grant is yours to make in a browser (each wallet's owner is you, so neither the CLI nor the API can attach Candle's signer): this opens the Agents page's grant-only step, creates no key, and with an API key waits until the wallets show as delegated. On a new account, run it before `candle auth login`. |
|
|
120
121
|
| `candle wallets trust\|untrust <label\|address\|id\|prefix*>...` | Mark linked wallets as yours so an agent's Withdraw key can move funds into them, or clear the mark. Owner only, over the device token, with a typed confirm; a trailing `*`, as in `'tr-*'`, selects by prefix. |
|
|
121
122
|
| `candle wallets allow-launch\|disallow-launch <label\|address\|id\|prefix*>...` | Deprecated. Where the server has `TEE_LAUNCH_BY_SCOPE_ENABLED` on, a key's `launch:write` decides TEE launches: `allow-launch` saves the value and exits 0, and `disallow-launch` saves it, prints the server's `ALLOW_LAUNCH_NOT_ENFORCED` message and exits 1, because no launch is stopped. A server with the switch off still lets TEE wallets pay for a launch (`candle launch`) only after `allow-launch`. Owner only, over the device token; the screen shows each wallet's label, address and bound key before a typed confirm. `disallow-launch --yes` skips the confirm. |
|
|
@@ -142,7 +143,7 @@ chosen words from your saved copy. Clear your terminal's scrollback too: the CLI
|
|
|
142
143
|
| `candle launch status <mint-or-client-launch-id> [--wait]` | One launch state, `build`, `signed`, `confirmed`, `trading`, `bonded` or `migrated`, for a mint or a `clientLaunchId`. Reads `GET /api/v1/launch/status/{id}` (`launch:read`) and reports the market's own `lifecycle` and `migration` fields. `--wait` follows it until the token is trading, the launch has failed, or ten minutes pass. Nothing is sent. |
|
|
143
144
|
| `candle image upload <path>` | Uploads a PNG, JPEG, GIF or WebP file, at most 10MB, and prints the `https://` URL Candle hosts it at, ready to be a launch's `imageUrl`. The type is read from the file's first bytes. The file becomes public and permanent. Needs `launch:write`; spends nothing and does not count against the launch cap. `--json` adds the content type and the size. |
|
|
144
145
|
| `candle pnl [--profile <name>]` | P&L: realized net, fees, unrealized and total, then open positions, the same figures the console shows. Without `--profile`, the account's books (every profile, the web app and the CLI, one ledger; needs the Read scope), with a By agent table (each key, then Manual, then the account total). With `--profile <name>`, that profile's key's share of the same P&L: realized on its sales and the open positions that belong to it, with a row per wallet that sums to the total. Each wallet is its own average-cost pool, and a recorded move between the account's wallets carries its cost. Both show a Solana block, a Hood block, then the combined figure, the closed positions as money made, minus money lost, plus partial sells, and a `CHAIN` column (and `WALLET`, where the API names it) on positions. Unpriced positions are never valued at zero; dust (worth under one cent) is marked and still counted. A bridge between Solana and Hood is not a trade, and bridge and Relay costs are not netted. DAMM v2 LP positions are included: realized (withdrawals against the cost basis at the add, plus claimed fees), unrealized (open positions at their share of the pool plus unclaimed fees, against that basis) and vs holding (what the deposited tokens would be worth held); `Total` stays the console's token figure and `Total with LP` covers tokens and LP. |
|
|
145
|
-
| `candle portfolio [<filter>] [--chain solana\|hood] [--rpc-url <url>] [--evm-rpc-url <url>] [--no-perps]` | Vault, TEE and embedded wallets on Solana and Hood in one table with chain, amount, price and value, subtotals per group and per chain, and a total. Solana vault and external balances are read over your own Solana RPC; Candle is sent only the mints, for prices. EVM vault keys are read over your own Hood RPC (`--evm-rpc-url`, else `CANDLE_EVM_RPC_URL`, else the built-in Hood RPC) for ETH and USDG only, priced by asset, never by address; an RPC on another chain is refused. An unpriced Hood token shows why (`no-market-row`, `stale-mark`, ...). `--chain` shows one chain. Each TEE wallet's DAMM v2 LP positions follow the tokens, valued by Candle at the same marks, and count in the total; one whose pool could not be read is shown as not read and the total says it is partial. A Hyperliquid section follows (each EVM TEE wallet's account, read from Hyperliquid directly, never in the total); `--no-perps` skips it. |
|
|
146
|
+
| `candle portfolio [<filter>] [--chain solana\|hood] [--rpc-url <url>] [--evm-rpc-url <url>] [--no-perps] [--no-vault]` | Vault, TEE and embedded wallets on Solana and Hood in one table with chain, amount, price and value, subtotals per group and per chain, and a total. Solana vault and external balances are read over your own Solana RPC; Candle is sent only the mints, for prices. EVM vault keys are read over your own Hood RPC (`--evm-rpc-url`, else `CANDLE_EVM_RPC_URL`, else the built-in Hood RPC) for ETH and USDG only, priced by asset, never by address; an RPC on another chain is refused. An unpriced Hood token shows why (`no-market-row`, `stale-mark`, ...). `--chain` shows one chain. Each TEE wallet's DAMM v2 LP positions follow the tokens, valued by Candle at the same marks, and count in the total; one whose pool could not be read is shown as not read and the total says it is partial. A Hyperliquid section follows (each EVM TEE wallet's account, read from Hyperliquid directly, never in the total); `--no-perps` skips it. It needs no terminal: without one, `--factor touch-id` or `passkey` still unlocks the vault, and otherwise the vault row says not read and the rest is shown, but the total is partial (exit 3); `--no-vault` leaves the vault out on purpose and may exit 0. |
|
|
146
147
|
| `candle scan <contract address> [--chain solana\|hood]` | The token card for one contract, as a Quant group gets it: market, all-time high since launch, bundles, snipers, insiders, fresh wallets, wallet age, tracked wallets in the last hour, the same-name leader, forensics coverage and the Candle page, with the source of each figure. Facts only; a field with no source prints a dash. The chain is read from the address unless `--chain` names it. No key needed and none sent. Served only where `TOKEN_CARD_ENABLED` is on, and `TOKEN_CARD_DISABLED` elsewhere. |
|
|
147
148
|
| `candle token clusters <mint> [--chain solana\|hood]` | Optional third-party Solana cluster estimates, with fetched time and a linked-wallet caveat. Unavailable analysis prints a reason and exits 0; `--json` emits the availability union. No key. |
|
|
148
149
|
| `candle token edit <mint> [--chain solana\|hood] [--description <text>] [--website <url>] [--x <url>] [--telegram <url>] [--discord <url>] [--clear <field>]...` | As the token's creator, set Candle's copy of its description and links; `--clear` removes one. Name, symbol and image cannot be changed, and nothing is written on chain. Needs a `launch:write` key from the launching account. Served only where `TOKEN_METADATA_EDIT_ENABLED` is on, and `METADATA_EDIT_DISABLED` elsewhere. |
|
|
@@ -360,9 +361,11 @@ address is shortened (`FfU8…8pPD`; `--full` prints it whole) and an `https://`
|
|
|
360
361
|
`auth login` and `doctor`, which carry `profile` and `account`; `auth status` and `doctor` also
|
|
361
362
|
carry `cachedAccount`, the account the profile recorded, whenever a profile is resolved; scripts
|
|
362
363
|
get identity from `auth status --json`. The account is cached
|
|
363
|
-
at login from the API. Where the line is printed
|
|
364
|
-
|
|
365
|
-
|
|
364
|
+
at login from the API. Where the line is printed and `CANDLE_API_KEY` or `CANDLE_DEVICE_TOKEN` is
|
|
365
|
+
overriding the stored credential, it reads `Account <address> (CANDLE_API_KEY override)` when the
|
|
366
|
+
overriding key could be asked which account it belongs to, and
|
|
367
|
+
`Account unknown (CANDLE_API_KEY override)` when it could not (offline, revoked, or a
|
|
368
|
+
device-token-only override), rather than naming an account that credential was
|
|
366
369
|
never checked against; `auth status` and `setup` look the account up live and print what they
|
|
367
370
|
get, and `auth status` and `doctor` name the account the profile recorded beside it when the two
|
|
368
371
|
differ (not under an env credential override, where the live answer is not the profile's key's).
|
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.18";
|
|
536
536
|
|
|
537
537
|
// src/presentation.ts
|
|
538
538
|
function terminalStyle(deps, stream, machine = false) {
|
|
@@ -11163,6 +11163,25 @@ var init_solana_alt = __esm(() => {
|
|
|
11163
11163
|
];
|
|
11164
11164
|
});
|
|
11165
11165
|
|
|
11166
|
+
// src/account.ts
|
|
11167
|
+
async function fetchAccount(deps, apiUrl, apiKey) {
|
|
11168
|
+
const identity = await apiRequest("/api/v1/agent/wallets/embedded", {
|
|
11169
|
+
auth: "key",
|
|
11170
|
+
credentials: { apiKey },
|
|
11171
|
+
apiUrl,
|
|
11172
|
+
fetch: deps.fetch,
|
|
11173
|
+
env: deps.env
|
|
11174
|
+
});
|
|
11175
|
+
const body = identity.ok ? identity.body : undefined;
|
|
11176
|
+
const account = body?.account;
|
|
11177
|
+
if (account)
|
|
11178
|
+
return { account, ...body?.username ? { username: body.username } : {} };
|
|
11179
|
+
return { failure: identity.ok ? "no account in the response" : identity.message };
|
|
11180
|
+
}
|
|
11181
|
+
var init_account = __esm(() => {
|
|
11182
|
+
init_client();
|
|
11183
|
+
});
|
|
11184
|
+
|
|
11166
11185
|
// src/profiles.ts
|
|
11167
11186
|
function assertOrdinaryProfile(config, profile) {
|
|
11168
11187
|
if (profile !== undefined && config.profiles?.[profile]?.apiKeyItem !== undefined) {
|
|
@@ -11297,7 +11316,7 @@ function credentialEnvOverrides(env) {
|
|
|
11297
11316
|
}
|
|
11298
11317
|
function identityLine(profile, account, apiUrl, overrides, username, options = {}) {
|
|
11299
11318
|
const address = account === undefined ? undefined : shortId(account, options.full);
|
|
11300
|
-
const shown = overrides?.length ? `unknown (${overrides.join(", ")} override)` : username && address ? `${username} (${address})` : address ?? "unknown";
|
|
11319
|
+
const shown = overrides?.length ? address ? `${address} (${overrides.join(", ")} override)` : `unknown (${overrides.join(", ")} override)` : username && address ? `${username} (${address})` : address ?? "unknown";
|
|
11301
11320
|
const name = profile ?? "none";
|
|
11302
11321
|
const host = apiUrl.replace(/^https:\/\//i, "").replace(/\/+$/, "");
|
|
11303
11322
|
const sep = options.style ? symbols(options.style).note : "·";
|
|
@@ -11308,7 +11327,20 @@ async function printIdentity(ctx) {
|
|
|
11308
11327
|
return;
|
|
11309
11328
|
const config = await ctx.deps.readConfig();
|
|
11310
11329
|
const fields2 = effectiveProfileFields(config, ctx.profile);
|
|
11311
|
-
const
|
|
11330
|
+
const overrides = credentialEnvOverrides(ctx.deps.env);
|
|
11331
|
+
let account = fields2.account;
|
|
11332
|
+
let username = fields2.username;
|
|
11333
|
+
if (overrides.length > 0) {
|
|
11334
|
+
account = undefined;
|
|
11335
|
+
username = undefined;
|
|
11336
|
+
const key = ctx.deps.env.CANDLE_API_KEY?.trim();
|
|
11337
|
+
if (key) {
|
|
11338
|
+
const lookup = await fetchAccount(ctx.deps, ctx.apiUrl, key);
|
|
11339
|
+
if (lookup.account)
|
|
11340
|
+
account = lookup.account;
|
|
11341
|
+
}
|
|
11342
|
+
}
|
|
11343
|
+
const line = identityLine(ctx.profile, account, ctx.apiUrl, overrides.length > 0 ? overrides : undefined, username, identityStyle(ctx.deps, "stdout"));
|
|
11312
11344
|
ctx.deps.stdout.write(`${line}
|
|
11313
11345
|
|
|
11314
11346
|
`);
|
|
@@ -11352,6 +11384,7 @@ function profileTable(config, now) {
|
|
|
11352
11384
|
}
|
|
11353
11385
|
var ProfileFleetOnlyError, PRE_PROFILE_FIELDS, API_KEY_PREFIXES, API_KEY_RANDOM_LENGTH = 43, INVALID_RPC_HOST = "invalid (fix with profile set)";
|
|
11354
11386
|
var init_profiles = __esm(() => {
|
|
11387
|
+
init_account();
|
|
11355
11388
|
init_presentation();
|
|
11356
11389
|
init_render();
|
|
11357
11390
|
ProfileFleetOnlyError = class ProfileFleetOnlyError extends Error {
|
|
@@ -28989,13 +29022,13 @@ __export(exports_vault_support, {
|
|
|
28989
29022
|
runVaultCommand: () => runVaultCommand,
|
|
28990
29023
|
requireVaultRaw: () => requireVaultRaw,
|
|
28991
29024
|
requireTty: () => requireTty,
|
|
28992
|
-
requirePromptStreams: () => requirePromptStreams,
|
|
28993
29025
|
refusePromptsWithoutTerminal: () => refusePromptsWithoutTerminal,
|
|
28994
29026
|
refuseEnvPassphrase: () => refuseEnvPassphrase,
|
|
28995
29027
|
openWithTypedPassphrase: () => openWithTypedPassphrase,
|
|
28996
29028
|
nonDefaultVaultFooter: () => nonDefaultVaultFooter,
|
|
28997
29029
|
missingVault: () => missingVault,
|
|
28998
29030
|
menuSafeText: () => menuSafeText,
|
|
29031
|
+
isNoTerminalRefusal: () => isNoTerminalRefusal,
|
|
28999
29032
|
groupAddress: () => groupAddress,
|
|
29000
29033
|
findExternalEntry: () => findExternalEntry,
|
|
29001
29034
|
factorMenu: () => factorMenu,
|
|
@@ -29039,22 +29072,23 @@ function requireTty(ctx, what) {
|
|
|
29039
29072
|
}));
|
|
29040
29073
|
return false;
|
|
29041
29074
|
}
|
|
29042
|
-
function requirePromptStreams(ctx, what) {
|
|
29043
|
-
if (ctx.deps.isTTY.stdin && ctx.deps.isTTY.stderr)
|
|
29044
|
-
return true;
|
|
29045
|
-
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.`));
|
|
29046
|
-
return false;
|
|
29047
|
-
}
|
|
29048
29075
|
function refusePromptsWithoutTerminal(ctx, what) {
|
|
29049
29076
|
if (ctx.deps.isTTY.stdin && ctx.deps.isTTY.stderr)
|
|
29050
29077
|
return ctx;
|
|
29051
29078
|
const refuse2 = async () => {
|
|
29052
|
-
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.`, {
|
|
29079
|
+
throw tagNoTerminal(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.`, {
|
|
29053
29080
|
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."
|
|
29054
|
-
});
|
|
29081
|
+
}));
|
|
29055
29082
|
};
|
|
29056
29083
|
return { ...ctx, deps: { ...ctx.deps, promptSecret: refuse2, promptLine: refuse2 } };
|
|
29057
29084
|
}
|
|
29085
|
+
function tagNoTerminal(error) {
|
|
29086
|
+
noTerminalRefusals.add(error);
|
|
29087
|
+
return error;
|
|
29088
|
+
}
|
|
29089
|
+
function isNoTerminalRefusal(error) {
|
|
29090
|
+
return error instanceof VaultError && noTerminalRefusals.has(error);
|
|
29091
|
+
}
|
|
29058
29092
|
function vaultPathFor(ctx, parsed) {
|
|
29059
29093
|
const flag = parsed.values["--keystore"];
|
|
29060
29094
|
if (flag !== undefined)
|
|
@@ -29674,7 +29708,7 @@ function takeRepeatedFlag(args, flag) {
|
|
|
29674
29708
|
}
|
|
29675
29709
|
return { values, rest };
|
|
29676
29710
|
}
|
|
29677
|
-
var PASSPHRASE_ONLY_PREFIX = "Passphrase only: ";
|
|
29711
|
+
var noTerminalRefusals, PASSPHRASE_ONLY_PREFIX = "Passphrase only: ";
|
|
29678
29712
|
var init_vault_support = __esm(() => {
|
|
29679
29713
|
init_args();
|
|
29680
29714
|
init_presentation();
|
|
@@ -29690,6 +29724,7 @@ var init_vault_support = __esm(() => {
|
|
|
29690
29724
|
init_platform();
|
|
29691
29725
|
init_sidecar();
|
|
29692
29726
|
init_store();
|
|
29727
|
+
noTerminalRefusals = new WeakSet;
|
|
29693
29728
|
});
|
|
29694
29729
|
|
|
29695
29730
|
// src/vault/ed25519.ts
|
|
@@ -38566,7 +38601,7 @@ var init_token_clusters = __esm(() => {
|
|
|
38566
38601
|
});
|
|
38567
38602
|
|
|
38568
38603
|
// ../mcp/src/version.ts
|
|
38569
|
-
var SERVER_VERSION = "0.10.
|
|
38604
|
+
var SERVER_VERSION = "0.10.30";
|
|
38570
38605
|
|
|
38571
38606
|
// ../mcp/src/update-notice.ts
|
|
38572
38607
|
function newer(a, b) {
|
|
@@ -40424,6 +40459,12 @@ async function executeTrade(args, cfg, doFetch) {
|
|
|
40424
40459
|
let amountRaw;
|
|
40425
40460
|
let resolved = {};
|
|
40426
40461
|
let paper = isPaperFlag(args.paper);
|
|
40462
|
+
const deferExecution = isPaperFlag(args.deferExecution);
|
|
40463
|
+
if (deferExecution && paper) {
|
|
40464
|
+
return errText("deferExecution cannot be combined with paper; a paper trade records a quote and never executes", {
|
|
40465
|
+
clientTradeId
|
|
40466
|
+
});
|
|
40467
|
+
}
|
|
40427
40468
|
try {
|
|
40428
40469
|
if (args.side === "buy") {
|
|
40429
40470
|
const amount = args.amount;
|
|
@@ -40532,6 +40573,9 @@ async function executeTrade(args, cfg, doFetch) {
|
|
|
40532
40573
|
} catch (err) {
|
|
40533
40574
|
return errText(err instanceof Error ? err.message : String(err), { clientTradeId });
|
|
40534
40575
|
}
|
|
40576
|
+
if (deferExecution && paper) {
|
|
40577
|
+
return errText("deferExecution needs a live holding, and this sell resolved to the paper book (the wallet holds none of the mint); a paper trade never executes", { clientTradeId });
|
|
40578
|
+
}
|
|
40535
40579
|
const posted = await postJson(`${base(cfg)}/api/v1/trade/agent/build`, apiKey, {
|
|
40536
40580
|
clientTradeId,
|
|
40537
40581
|
mint: args.mint,
|
|
@@ -40541,7 +40585,8 @@ async function executeTrade(args, cfg, doFetch) {
|
|
|
40541
40585
|
payer: { type: "main" },
|
|
40542
40586
|
...args.quoteAsset !== undefined ? { quoteAsset: args.quoteAsset } : {},
|
|
40543
40587
|
...args.maxSlippageBps !== undefined ? { maxSlippageBps: args.maxSlippageBps } : {},
|
|
40544
|
-
...paper ? { paper: true } : {}
|
|
40588
|
+
...paper ? { paper: true } : {},
|
|
40589
|
+
...deferExecution ? { deferExecution: true } : {}
|
|
40545
40590
|
}, doFetch);
|
|
40546
40591
|
if ("thrown" in posted)
|
|
40547
40592
|
return transportError("clientTradeId", clientTradeId, posted.thrown);
|
|
@@ -45166,6 +45211,13 @@ function buildRequest(name, args, cfg) {
|
|
|
45166
45211
|
const path = kind === "launch" ? `/api/v1/launch/headless/jobs/${id}` : kind === "swap" ? `/api/v1/agent/swap/by-client-id/${id}` : `/api/v1/trade/agent/jobs/${id}`;
|
|
45167
45212
|
return { url: `${base2}${path}`, init: { method: "GET", headers: jsonHeaders(apiKey) } };
|
|
45168
45213
|
}
|
|
45214
|
+
case "candle_trade_execute": {
|
|
45215
|
+
const { clientTradeId } = args;
|
|
45216
|
+
return {
|
|
45217
|
+
url: `${base2}/api/v1/trade/agent/execute`,
|
|
45218
|
+
init: { method: "POST", headers: jsonHeaders(requireApiKey2(cfg)), body: JSON.stringify({ clientTradeId }) }
|
|
45219
|
+
};
|
|
45220
|
+
}
|
|
45169
45221
|
case "candle_get_staking": {
|
|
45170
45222
|
return { url: `${base2}/api/v1/agent/staking`, init: { method: "GET", headers: jsonHeaders(requireApiKey2(cfg)) } };
|
|
45171
45223
|
}
|
|
@@ -45732,7 +45784,7 @@ function registerToolSubset(server, options) {
|
|
|
45732
45784
|
}, async (args) => callAndRelay("candle_get_profile_pnl", args, getConfig(), doFetch));
|
|
45733
45785
|
register("candle_get_profile_trades", {
|
|
45734
45786
|
title: "Read an agent profile's trade history",
|
|
45735
|
-
description: "Orders, actual fills, fees, timestamps and transaction hashes for this profile. Page by time with " + "`since`, `until` and `cursor` (continue with the response's `nextCursor`). Includes FAILED " + "trades, with an errorCode saying why each did not go through, so this answers 'what happened to " + "my order' as well as 'what did I trade'. Reads only; moves nothing.",
|
|
45787
|
+
description: "Orders, actual fills, fees, timestamps and transaction hashes for this profile. Page by time with " + "`since`, `until` and `cursor` (continue with the response's `nextCursor`). Includes FAILED " + "trades, with an errorCode saying why each did not go through, so this answers 'what happened to " + "my order' as well as 'what did I trade'. The profile's swaps are listed among the trades by " + "time, each with kind 'swap', rail ('swap' for candle_swap, 'build' for the CLI's build and " + "submit), fromAsset, toAsset and amountRaw; a candle_swap made without a clientSwapId is not " + "recorded, so it is not listed. Reads only; moves nothing.",
|
|
45736
45788
|
inputSchema: profileTradesShape
|
|
45737
45789
|
}, async (args) => callAndRelay("candle_get_profile_trades", args, getConfig(), doFetch));
|
|
45738
45790
|
register("candle_get_paper_inventory", {
|
|
@@ -45830,6 +45882,13 @@ function registerToolSubset(server, options) {
|
|
|
45830
45882
|
const result = await executeTrade(args, getConfig(), doFetch);
|
|
45831
45883
|
return { content: [{ type: "text", text: result.text }], ...result.isError ? { isError: true } : {} };
|
|
45832
45884
|
});
|
|
45885
|
+
register("candle_trade_execute", {
|
|
45886
|
+
title: "Execute a deferred trade",
|
|
45887
|
+
description: "Execute a trade you built with candle_trade and deferExecution: true. MOVES REAL FUNDS: Candle signs " + "the plan it kept back from the build with the account's own delegation and broadcasts it. Takes the " + "clientTradeId and nothing else, so nothing you send can change what gets signed; to change the trade, " + `build a new one.
|
|
45888
|
+
|
|
45889
|
+
` + "Exactly one execution per build. Calling again never broadcasts a second time: it returns the stored " + "result once the first has confirmed, and a 409 IDEMPOTENCY_CONFLICT while the outcome is still " + "unknown (read candle_get_operation with the clientTradeId, kind trade, before doing anything else). " + "More than 30 minutes after the build it is 409 QUOTE_EXPIRED: build again for a fresh quote. A " + "trade built without deferExecution has already executed and is refused here.",
|
|
45890
|
+
inputSchema: tradeExecuteShape
|
|
45891
|
+
}, async (args) => callAndRelay("candle_trade_execute", args, getConfig(), doFetch));
|
|
45833
45892
|
register("candle_atomic_launch_dry_run", {
|
|
45834
45893
|
title: "Dry-run an atomic Solana launch",
|
|
45835
45894
|
description: "Check a Solana launch bundled with first buys and price each buy, without building anything. Runs the " + "same checks as the atomic build, in the same order (payers, Believer check, fee and spend limits, the " + "key's transaction cap), then returns the launch preview (curveTerms) and one fill per first buy: the " + "tokens it would receive and the floor the build would sign it at. Reads only; moves nothing, reserves " + "nothing, and the clientLaunchId stays unused. It does not check balances, the daily cap, or the " + "transaction's size, so the real launch can still refuse. Needs launch:write, swap:write and Pro or Max.",
|
|
@@ -46038,7 +46097,7 @@ async function registerEligibleProfileTool(server, env = process.env, doFetch =
|
|
|
46038
46097
|
}
|
|
46039
46098
|
return eligible;
|
|
46040
46099
|
}
|
|
46041
|
-
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, tokenImageUrl, tokenBannerUrl, localImageShape, localLaunchTokenShape, localLaunchAndSeedShape, IMAGE_PATH_DISCLOSURE, transferShape, sweepShape, PROFILE_PNL_HYPERLIQUID;
|
|
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;
|
|
46042
46101
|
var init_tools = __esm(() => {
|
|
46043
46102
|
init_zod();
|
|
46044
46103
|
init_client2();
|
|
@@ -46103,6 +46162,7 @@ var init_tools = __esm(() => {
|
|
|
46103
46162
|
"candle_get_agent_positions",
|
|
46104
46163
|
"candle_get_plans",
|
|
46105
46164
|
"candle_trade",
|
|
46165
|
+
"candle_trade_execute",
|
|
46106
46166
|
"candle_launch_and_seed",
|
|
46107
46167
|
"candle_atomic_launch_dry_run",
|
|
46108
46168
|
"candle_swap",
|
|
@@ -46324,7 +46384,11 @@ var init_tools = __esm(() => {
|
|
|
46324
46384
|
quoteAsset: exports_external.string().optional().describe('What the wallet spends on a buy or receives on a sell: "sol", "usdc" or "cndl" on Solana, ' + '"eth" or "usdg" on Hood. Echoing the `quoteAsset` a quote (POST /api/v1/trade/agent/quote) returned is safe. On Solana it applies only ' + "to an arbitrary mint Candle never launched (a buy or sell works on any plan) and is ignored for a Candle token, " + "whose quote comes from the token itself. On Hood it is the settlement asset of a DEX " + "trade; a USDG buy adds an approval transaction an ETH buy does not. It is not the route: " + "the cheapest path to the asset is chosen separately. Defaults to sol / ETH settlement."),
|
|
46325
46385
|
maxSlippageBps: exports_external.number().int().min(0).max(5000).optional().describe("Slippage bound in bps, 0-5000 (50%). Server defaults to 100 (1%)"),
|
|
46326
46386
|
clientTradeId: exports_external.string().optional().describe("Idempotency key. Auto-generated when omitted and echoed in the result. Retrying with the " + "SAME id is safe (idempotent replay); a new id is a SECOND trade."),
|
|
46327
|
-
paper: exports_external.preprocess((value) => value === "true" || value === 1 || value === "1" ? true : value === "false" || value === 0 || value === "0" ? false : value, exports_external.boolean().optional().describe("Rehearse instead of trading. The request passes every admission rule a live trade passes " + "-- the same planner, spend gate, key cap and loss limits -- and records the quote, but " + "nothing is ever broadcast and no funds move. Use it to check that a strategy is admitted " + "before risking anything on it. A paper fill is optimistic by construction: it books the " + "quoted price, so the gap between a paper arm and a live one IS the execution cost. " + "A sell of a mint this key already paper-bought also closes that paper book when the " + "live wallet is empty, even if this flag is omitted."))
|
|
46387
|
+
paper: exports_external.preprocess((value) => value === "true" || value === 1 || value === "1" ? true : value === "false" || value === 0 || value === "0" ? false : value, exports_external.boolean().optional().describe("Rehearse instead of trading. The request passes every admission rule a live trade passes " + "-- the same planner, spend gate, key cap and loss limits -- and records the quote, but " + "nothing is ever broadcast and no funds move. Use it to check that a strategy is admitted " + "before risking anything on it. A paper fill is optimistic by construction: it books the " + "quoted price, so the gap between a paper arm and a live one IS the execution cost. " + "A sell of a mint this key already paper-bought also closes that paper book when the " + "live wallet is empty, even if this flag is omitted.")),
|
|
46388
|
+
deferExecution: exports_external.preprocess((value) => value === "true" || value === 1 || value === "1" ? true : value === "false" || value === 0 || value === "0" ? false : value, exports_external.boolean().optional().describe("Quote first, spend later. The trade is built and the result carries the quote with " + 'status "built"; NOTHING is signed or spent until candle_trade_execute is called with the same ' + "clientTradeId. The build holds for 30 minutes; executing after that is QUOTE_EXPIRED and you " + "build again for a fresh quote. Rebuilding with the same clientTradeId re-quotes it, until it has " + "been sent to candle_trade_execute. There is no cancel: let an unwanted build expire. Cannot be " + "combined with `paper`."))
|
|
46389
|
+
};
|
|
46390
|
+
tradeExecuteShape = {
|
|
46391
|
+
clientTradeId: exports_external.string().min(1).describe("The clientTradeId of a candle_trade built with deferExecution: true. Nothing else is accepted.")
|
|
46328
46392
|
};
|
|
46329
46393
|
tokenImageUrl = launchSchemaField(launchTokenShape, "imageUrl");
|
|
46330
46394
|
tokenBannerUrl = launchSchemaField(launchTokenShape, "bannerUrl");
|
|
@@ -74149,29 +74213,11 @@ ${USAGE3}` : USAGE3, ctx.json);
|
|
|
74149
74213
|
}
|
|
74150
74214
|
|
|
74151
74215
|
// src/commands/auth.ts
|
|
74152
|
-
|
|
74153
|
-
import { join as join9 } from "node:path";
|
|
74154
|
-
|
|
74155
|
-
// src/account.ts
|
|
74156
|
-
init_client();
|
|
74157
|
-
async function fetchAccount(deps, apiUrl, apiKey) {
|
|
74158
|
-
const identity = await apiRequest("/api/v1/agent/wallets/embedded", {
|
|
74159
|
-
auth: "key",
|
|
74160
|
-
credentials: { apiKey },
|
|
74161
|
-
apiUrl,
|
|
74162
|
-
fetch: deps.fetch,
|
|
74163
|
-
env: deps.env
|
|
74164
|
-
});
|
|
74165
|
-
const body = identity.ok ? identity.body : undefined;
|
|
74166
|
-
const account = body?.account;
|
|
74167
|
-
if (account)
|
|
74168
|
-
return { account, ...body?.username ? { username: body.username } : {} };
|
|
74169
|
-
return { failure: identity.ok ? "no account in the response" : identity.message };
|
|
74170
|
-
}
|
|
74171
|
-
|
|
74172
|
-
// src/commands/auth.ts
|
|
74216
|
+
init_account();
|
|
74173
74217
|
init_agent_key_access();
|
|
74174
74218
|
init_args();
|
|
74219
|
+
import { homedir as homedir3 } from "node:os";
|
|
74220
|
+
import { join as join9 } from "node:path";
|
|
74175
74221
|
|
|
74176
74222
|
// src/checks.ts
|
|
74177
74223
|
init_client();
|
|
@@ -76567,9 +76613,9 @@ var HELP = {
|
|
|
76567
76613
|
group: "Account",
|
|
76568
76614
|
intro: "Holdings, prices and value of every wallet",
|
|
76569
76615
|
summary: "Every wallet's holdings, prices and value on Solana and Hood (vault read over your own RPCs)",
|
|
76570
|
-
description: "Vault, TEE and embedded wallets on Solana and Hood, each token with its chain, amount, price and value, then subtotals per group and per chain and one total. TEE and embedded balances, on both chains, come from Candle. Solana vault and external wallets are read over your own Solana RPC, and Candle is sent only the mints they hold, for prices. EVM vault keys are read over your own EVM RPC for ETH and USDG only (the CLI cannot list other tokens), which must be Hood's (chain id 4663); any other chain is refused. Their prices come from Candle by asset (ETH, USDG), never by address. Unpriced tokens are shown as unpriced with the reason (no-market-row, stale-mark, unusable-price, source-unavailable) and left out of the total. A wallet that could not be read in full is marked not read, the footer names its chain, and the exit is 3. Where Candle serves LP, each Solana TEE wallet's DAMM v2 positions follow the tokens (share of the pool plus unclaimed fees, valued by Candle at the same marks) and count in the total; a position whose pool could not be read is shown as not read and the total says it is partial. A bridge moves value between the chain subtotals; it is not a trade. With a filter, only the wallets whose label or address contains it are read and shown, the subtotals and total are for those wallets, and the first line says how many matched. When Candle listed only part of the account's wallets, the result stays partial even if every wallet shown was read, and no match says so. After the totals, a Hyperliquid section (live from Hyperliquid, not in the ledger, and never in the total) lists each EVM TEE wallet's Hyperliquid account value, unrealized P&L, positions and risk at the default thresholds, read from Hyperliquid's public API on this machine; a failed read there is not read and the exit is 3. --chain solana and --no-perps read nothing from Hyperliquid.",
|
|
76616
|
+
description: "Vault, TEE and embedded wallets on Solana and Hood, each token with its chain, amount, price and value, then subtotals per group and per chain and one total. TEE and embedded balances, on both chains, come from Candle. Solana vault and external wallets are read over your own Solana RPC, and Candle is sent only the mints they hold, for prices. EVM vault keys are read over your own EVM RPC for ETH and USDG only (the CLI cannot list other tokens), which must be Hood's (chain id 4663); any other chain is refused. Their prices come from Candle by asset (ETH, USDG), never by address. Unpriced tokens are shown as unpriced with the reason (no-market-row, stale-mark, unusable-price, source-unavailable) and left out of the total. A wallet that could not be read in full is marked not read, the footer names its chain, and the exit is 3. Where Candle serves LP, each Solana TEE wallet's DAMM v2 positions follow the tokens (share of the pool plus unclaimed fees, valued by Candle at the same marks) and count in the total; a position whose pool could not be read is shown as not read and the total says it is partial. A bridge moves value between the chain subtotals; it is not a trade. With a filter, only the wallets whose label or address contains it are read and shown, the subtotals and total are for those wallets, and the first line says how many matched. When Candle listed only part of the account's wallets, the result stays partial even if every wallet shown was read, and no match says so. After the totals, a Hyperliquid section (live from Hyperliquid, not in the ledger, and never in the total) lists each EVM TEE wallet's Hyperliquid account value, unrealized P&L, positions and risk at the default thresholds, read from Hyperliquid's public API on this machine; a failed read there is not read and the exit is 3. --chain solana and --no-perps read nothing from Hyperliquid. The vault is read only for its addresses, so the command needs no terminal: without one, a factor that asks nothing (--factor touch-id or passkey) still unlocks it, and otherwise the vault row says not read and the other groups are shown, but the result is partial: the total says so and the exit is 3. --no-vault leaves the vault out on purpose, and then the result can be complete (exit 0).",
|
|
76571
76617
|
usage: [
|
|
76572
|
-
"candle portfolio [<filter>] [--chain solana|hood] [--rpc-url <url>] [--evm-rpc-url <url>] [--no-perps] [--json]"
|
|
76618
|
+
"candle portfolio [<filter>] [--chain solana|hood] [--rpc-url <url>] [--evm-rpc-url <url>] [--no-perps] [--no-vault] [--json]"
|
|
76573
76619
|
],
|
|
76574
76620
|
rows: [
|
|
76575
76621
|
{
|
|
@@ -76591,6 +76637,10 @@ var HELP = {
|
|
|
76591
76637
|
invocation: "--no-perps",
|
|
76592
76638
|
description: "Skip the Hyperliquid section: send no EVM TEE address to Hyperliquid's public API"
|
|
76593
76639
|
},
|
|
76640
|
+
{
|
|
76641
|
+
invocation: "--no-vault",
|
|
76642
|
+
description: "Leave the vault out: no unlock, and no vault address is sent to your RPC"
|
|
76643
|
+
},
|
|
76594
76644
|
KEYSTORE_FLAG
|
|
76595
76645
|
],
|
|
76596
76646
|
examples: [
|
|
@@ -77043,6 +77093,10 @@ var HELP = {
|
|
|
77043
77093
|
description: "Give agents access to the account's embedded wallets: opens the Agents page's grant step in your browser (no key is created) and, with an API key, waits until it lands"
|
|
77044
77094
|
},
|
|
77045
77095
|
{ invocation: "revoke <wallet-id>", description: "Revoke a linked wallet" },
|
|
77096
|
+
{
|
|
77097
|
+
invocation: "allowlist [--all]",
|
|
77098
|
+
description: "The account's withdrawal allowlist (the destinations candle transfer --to <label> resolves by). Any key reads it; approving an address is the owner's, in the agent console's Wallets tab (an API key cannot approve); an acting key can revoke one, a read-only key cannot. --all adds removed rows"
|
|
77099
|
+
},
|
|
77046
77100
|
{
|
|
77047
77101
|
invocation: "trust <label|address|id|prefix*>...",
|
|
77048
77102
|
description: "Mark linked wallets yours, so agents can move funds into them (owner only; typed confirm)"
|
|
@@ -77076,6 +77130,7 @@ var HELP = {
|
|
|
77076
77130
|
"candle wallet authorize",
|
|
77077
77131
|
"candle wallet import --chain solana --key-file ./signer.json",
|
|
77078
77132
|
"candle wallet revoke wal_123",
|
|
77133
|
+
"candle wallet allowlist",
|
|
77079
77134
|
"candle wallet trust 'tr-*' 'dest-*'",
|
|
77080
77135
|
"candle wallet allow-launch launcher-1",
|
|
77081
77136
|
"candle wallet close-empty --keep EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
@@ -77956,6 +78011,7 @@ var TOOL_CLASSES = {
|
|
|
77956
78011
|
candle_perps_risk: READ,
|
|
77957
78012
|
candle_perps_thresholds: READ,
|
|
77958
78013
|
candle_trade: card("trade", "clientTradeId"),
|
|
78014
|
+
candle_trade_execute: card("trade"),
|
|
77959
78015
|
candle_swap: card("swap", "clientSwapId"),
|
|
77960
78016
|
candle_launch_token: card("launch", "clientLaunchId"),
|
|
77961
78017
|
candle_launch_and_seed: card("launch", "clientLaunchId"),
|
|
@@ -78045,7 +78101,8 @@ async function readOnce(executor, target, writeKey) {
|
|
|
78045
78101
|
if (!readsById(target))
|
|
78046
78102
|
return { readable: false };
|
|
78047
78103
|
const kind = target.kind === "launch" ? "launch" : "trade";
|
|
78048
|
-
const
|
|
78104
|
+
const clientId = target.tool === "candle_trade_execute" ? target.params.clientTradeId : target.opId;
|
|
78105
|
+
const outcome = await executor.execute("candle_get_operation", { clientId, kind }, writeKey);
|
|
78049
78106
|
if (outcome.state === "succeeded" || outcome.state === "pending") {
|
|
78050
78107
|
const job = outcome.result?.job ?? outcome.result;
|
|
78051
78108
|
const status = typeof job?.status === "string" ? job.status : "";
|
|
@@ -89536,8 +89593,7 @@ async function externalList(args, ctx) {
|
|
|
89536
89593
|
return usage(ctx, `Unexpected argument: ${parsed.positionals[0]}`);
|
|
89537
89594
|
if (!refuseEnvPassphrase(ctx))
|
|
89538
89595
|
return 1;
|
|
89539
|
-
|
|
89540
|
-
return 1;
|
|
89596
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "external list");
|
|
89541
89597
|
const { deps } = ctx;
|
|
89542
89598
|
const resolvedVault = vaultPathFor(ctx, parsed);
|
|
89543
89599
|
if ("error" in resolvedVault)
|
|
@@ -89545,7 +89601,7 @@ async function externalList(args, ctx) {
|
|
|
89545
89601
|
const path = resolvedVault.path;
|
|
89546
89602
|
return runVaultCommand(ctx, async ({ hold }) => {
|
|
89547
89603
|
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
89548
|
-
const vault = hold((await unlockInteractively(
|
|
89604
|
+
const vault = hold((await unlockInteractively(promptCtx, path, raw, { acceptOlderCopy: parsed.booleans.has("--accept-older-copy") })).vault);
|
|
89549
89605
|
const externals = vault.index.entries.filter((entry) => entry.role === "external");
|
|
89550
89606
|
const rows = externals.map((entry) => ({
|
|
89551
89607
|
address: entry.address,
|
|
@@ -92206,6 +92262,8 @@ init_solana_lite();
|
|
|
92206
92262
|
var NATIVE_MINT2 = "So11111111111111111111111111111111111111112";
|
|
92207
92263
|
var LAMPORTS_PER_SIGNATURE = 5000n;
|
|
92208
92264
|
var TOKEN_ACCOUNT_RENT_LAMPORTS = 2039280n;
|
|
92265
|
+
var BUYER_TALLY_RENT_LAMPORTS = 1461600n;
|
|
92266
|
+
var REGISTER_BUYER = [144, 92, 137, 46, 209, 220, 27, 164];
|
|
92209
92267
|
var DEFAULT_UNITS_PER_INSTRUCTION = 200000n;
|
|
92210
92268
|
var MAX_COMPUTE_UNITS = 1400000n;
|
|
92211
92269
|
|
|
@@ -92289,7 +92347,7 @@ function readLeg(tx, index, wallet3) {
|
|
|
92289
92347
|
if (program === MEMO_PROGRAM_ID)
|
|
92290
92348
|
continue;
|
|
92291
92349
|
if (program !== undefined)
|
|
92292
|
-
others.push({ program, data: ix.data });
|
|
92350
|
+
others.push({ program, data: ix.data, accounts: accounts.map(key) });
|
|
92293
92351
|
}
|
|
92294
92352
|
if (unitPrice > 0n) {
|
|
92295
92353
|
const units = unitLimit ?? (computed * DEFAULT_UNITS_PER_INSTRUCTION > MAX_COMPUTE_UNITS ? MAX_COMPUTE_UNITS : computed * DEFAULT_UNITS_PER_INSTRUCTION);
|
|
@@ -92297,12 +92355,36 @@ function readLeg(tx, index, wallet3) {
|
|
|
92297
92355
|
}
|
|
92298
92356
|
return { lamports, tokenOut, systemTransfers, others };
|
|
92299
92357
|
}
|
|
92358
|
+
function isRegisterBuyer(data) {
|
|
92359
|
+
return data.length >= 8 && REGISTER_BUYER.every((byte, i) => data[i] === byte);
|
|
92360
|
+
}
|
|
92361
|
+
function readRegisterBuyer(register, swap3, wallet3) {
|
|
92362
|
+
if (register.program !== swap3.program)
|
|
92363
|
+
throw new BundleReadError("leg 1 registers the buyer with another program");
|
|
92364
|
+
if (register.data.length !== 12 || u323(register.data, 8) !== 0n)
|
|
92365
|
+
throw new BundleReadError("leg 1 registers the buyer with a proof");
|
|
92366
|
+
const [buyer, rentPayer, rules, tally] = register.accounts;
|
|
92367
|
+
if (buyer !== wallet3)
|
|
92368
|
+
throw new BundleReadError("leg 1 registers a buyer other than this wallet");
|
|
92369
|
+
if (rules === undefined || tally === undefined || !swap3.accounts.includes(rules) || !swap3.accounts.includes(tally))
|
|
92370
|
+
throw new BundleReadError("leg 1 registers the buyer for another pool than the one it buys");
|
|
92371
|
+
return rentPayer === wallet3 ? BUYER_TALLY_RENT_LAMPORTS : 0n;
|
|
92372
|
+
}
|
|
92300
92373
|
function readLaunchBundle(args) {
|
|
92301
92374
|
const launch = readLeg(decodeLeg(args.legs[0], 0), 0, args.wallet);
|
|
92302
92375
|
const buy = readLeg(decodeLeg(args.legs[1], 1), 1, args.wallet);
|
|
92303
|
-
|
|
92376
|
+
let registerLamports = 0n;
|
|
92377
|
+
const first = buy.others[0];
|
|
92378
|
+
if (first && isRegisterBuyer(first.data)) {
|
|
92379
|
+
if (!args.rules)
|
|
92380
|
+
throw new BundleReadError("leg 1 registers a buyer on a launch without rules");
|
|
92381
|
+
const swap4 = buy.others[1];
|
|
92382
|
+
if (buy.others.length !== 2 || !swap4 || isRegisterBuyer(swap4.data))
|
|
92383
|
+
throw new BundleReadError("leg 1 is not one curve swap");
|
|
92384
|
+
registerLamports = readRegisterBuyer(first, swap4, args.wallet);
|
|
92385
|
+
} else if (buy.others.length !== 1)
|
|
92304
92386
|
throw new BundleReadError("leg 1 is not one curve swap");
|
|
92305
|
-
const swap3 = buy.others[
|
|
92387
|
+
const swap3 = buy.others[buy.others.length - 1];
|
|
92306
92388
|
if (u64(swap3.data, 8) !== args.buyRaw)
|
|
92307
92389
|
throw new BundleReadError("leg 1 does not swap the confirmed --buy");
|
|
92308
92390
|
if (args.minOutRaw !== undefined && u64(swap3.data, 16) !== args.minOutRaw)
|
|
@@ -92322,7 +92404,7 @@ function readLaunchBundle(args) {
|
|
|
92322
92404
|
feeRaw = buy.tokenOut;
|
|
92323
92405
|
quoteRaw = args.buyRaw + buy.tokenOut + launch.tokenOut;
|
|
92324
92406
|
}
|
|
92325
|
-
return { lamports: launch.lamports + buy.lamports, quoteRaw, feeRaw };
|
|
92407
|
+
return { lamports: launch.lamports + buy.lamports + registerLamports, quoteRaw, feeRaw };
|
|
92326
92408
|
}
|
|
92327
92409
|
|
|
92328
92410
|
// src/launch-check.ts
|
|
@@ -93131,7 +93213,8 @@ async function atomicLaunch(ctx, key, args) {
|
|
|
93131
93213
|
wallet: wallet3.address,
|
|
93132
93214
|
quote: quoteAsset,
|
|
93133
93215
|
buyRaw: BigInt(buy.raw),
|
|
93134
|
-
...minOut !== undefined ? { minOutRaw: BigInt(minOut) } : {}
|
|
93216
|
+
...minOut !== undefined ? { minOutRaw: BigInt(minOut) } : {},
|
|
93217
|
+
rules: args.rules !== undefined
|
|
93135
93218
|
});
|
|
93136
93219
|
} catch (error) {
|
|
93137
93220
|
if (!(error instanceof BundleReadError))
|
|
@@ -94074,6 +94157,7 @@ var MCP_TOOL_NAMES = [
|
|
|
94074
94157
|
"candle_get_plans",
|
|
94075
94158
|
"candle_report_activity",
|
|
94076
94159
|
"candle_trade",
|
|
94160
|
+
"candle_trade_execute",
|
|
94077
94161
|
"candle_swap",
|
|
94078
94162
|
"candle_transfer",
|
|
94079
94163
|
"candle_sweep",
|
|
@@ -94206,7 +94290,8 @@ async function mcp(args, ctx) {
|
|
|
94206
94290
|
}
|
|
94207
94291
|
const identityConfig = await deps.readConfig();
|
|
94208
94292
|
const identityFields = effectiveProfileFields(identityConfig, ctx.profile);
|
|
94209
|
-
|
|
94293
|
+
const identityOverrides = credentialEnvOverrides(deps.env);
|
|
94294
|
+
deps.stderr.write(`${identityLine(ctx.profile, identityOverrides.length > 0 ? undefined : identityFields.account, apiUrl, identityOverrides.length > 0 ? identityOverrides : undefined, identityFields.username, identityStyle(deps, "stderr"))}
|
|
94210
94295
|
|
|
94211
94296
|
`);
|
|
94212
94297
|
if (parsed.booleans.has("--print-config")) {
|
|
@@ -102036,15 +102121,14 @@ async function runPlugin(name2, rawArgs, ctx) {
|
|
|
102036
102121
|
if (split2.wallets.length > 0) {
|
|
102037
102122
|
if (!refuseEnvPassphrase(ctx))
|
|
102038
102123
|
return 1;
|
|
102039
|
-
|
|
102040
|
-
return 1;
|
|
102124
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "candle <plugin> --wallet");
|
|
102041
102125
|
const resolvedVault = vaultPathFor(ctx, { values: {}, booleans: new Set, positionals: [] });
|
|
102042
102126
|
if ("error" in resolvedVault)
|
|
102043
102127
|
return usage(ctx, resolvedVault.error);
|
|
102044
102128
|
const vaultPath = resolvedVault.path;
|
|
102045
102129
|
const resolved = await runVaultCommand(ctx, async ({ hold }) => {
|
|
102046
102130
|
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
102047
|
-
const vault = hold((await unlockInteractively(
|
|
102131
|
+
const vault = hold((await unlockInteractively(promptCtx, vaultPath, raw)).vault);
|
|
102048
102132
|
for (const requested of split2.wallets) {
|
|
102049
102133
|
const entry = findExternalEntry(vault.index, requested);
|
|
102050
102134
|
if (entry === undefined) {
|
|
@@ -102734,14 +102818,20 @@ var NO_API_KEY6 = {
|
|
|
102734
102818
|
message: "No API key for this profile.",
|
|
102735
102819
|
suggestion: "Set CANDLE_API_KEY, or run `candle auth login` to store one."
|
|
102736
102820
|
};
|
|
102821
|
+
var NO_TERMINAL_REASON = "not read: no terminal for its unlock";
|
|
102822
|
+
var NO_TERMINAL_NOTE = "The vault was not read: its unlock needs a prompt, and standard input and standard error are not both a terminal. Run it at a terminal, pass --factor touch-id or --factor passkey to unlock without a prompt, or pass --no-vault to leave it out.";
|
|
102737
102823
|
async function portfolio(args, ctx) {
|
|
102738
102824
|
const parsed = parseArgs(args, {
|
|
102739
102825
|
valueFlags: ["--rpc-url", "--evm-rpc-url", "--keystore", "--chain"],
|
|
102740
|
-
booleanFlags: ["--no-perps"],
|
|
102826
|
+
booleanFlags: ["--no-perps", "--no-vault"],
|
|
102741
102827
|
pathFlags: ["--keystore"]
|
|
102742
102828
|
});
|
|
102743
102829
|
if ("error" in parsed)
|
|
102744
102830
|
return usage(ctx, parsed.error);
|
|
102831
|
+
const noVault = parsed.booleans.has("--no-vault");
|
|
102832
|
+
if (noVault && parsed.values["--keystore"] !== undefined) {
|
|
102833
|
+
return usage(ctx, "--keystore names the vault to read and has no effect with --no-vault.");
|
|
102834
|
+
}
|
|
102745
102835
|
if (parsed.positionals.length > 1)
|
|
102746
102836
|
return usage(ctx, `Unexpected argument: ${parsed.positionals[1]}`);
|
|
102747
102837
|
const filter = parsed.positionals[0];
|
|
@@ -102788,16 +102878,30 @@ async function portfolio(args, ctx) {
|
|
|
102788
102878
|
let vaultEntries;
|
|
102789
102879
|
let evmEntries;
|
|
102790
102880
|
let vaultReason;
|
|
102881
|
+
let vaultSkipped = false;
|
|
102791
102882
|
let known = 0;
|
|
102792
|
-
const raw = await readVaultRaw(resolvedVault.path);
|
|
102793
|
-
|
|
102794
|
-
|
|
102795
|
-
} else {
|
|
102883
|
+
const raw = noVault ? null : await readVaultRaw(resolvedVault.path);
|
|
102884
|
+
let vault;
|
|
102885
|
+
if (raw !== null) {
|
|
102796
102886
|
if (!refuseEnvPassphrase(ctx))
|
|
102797
102887
|
return 1;
|
|
102798
|
-
|
|
102799
|
-
|
|
102800
|
-
|
|
102888
|
+
try {
|
|
102889
|
+
const unlockCtx = refusePromptsWithoutTerminal(ctx, "portfolio");
|
|
102890
|
+
vault = hold((await unlockInteractively(unlockCtx, resolvedVault.path, raw)).vault);
|
|
102891
|
+
} catch (error) {
|
|
102892
|
+
if (!isNoTerminalRefusal(error))
|
|
102893
|
+
throw error;
|
|
102894
|
+
}
|
|
102895
|
+
}
|
|
102896
|
+
if (noVault) {
|
|
102897
|
+
vaultReason = "not read (--no-vault)";
|
|
102898
|
+
} else if (raw === null) {
|
|
102899
|
+
vaultReason = `no vault at ${resolvedVault.path}`;
|
|
102900
|
+
} else if (vault === undefined) {
|
|
102901
|
+
vaultReason = NO_TERMINAL_REASON;
|
|
102902
|
+
vaultSkipped = true;
|
|
102903
|
+
notice(deps, "stderr", "note", NO_TERMINAL_NOTE);
|
|
102904
|
+
} else {
|
|
102801
102905
|
vaultEntries = showSolana ? vault.index.entries.filter((entry) => entry.chain === "solana" && (entry.role === "vault" || entry.role === "external")).map((entry) => ({ address: entry.address, label: entry.label, role: entry.role })) : [];
|
|
102802
102906
|
evmEntries = showHood ? vault.index.entries.filter((entry) => entry.chain === "evm" && entry.role === "vault").map((entry) => ({ address: entry.address, label: entry.label })) : [];
|
|
102803
102907
|
known += vaultEntries.length + evmEntries.length;
|
|
@@ -103012,7 +103116,7 @@ async function portfolio(args, ctx) {
|
|
|
103012
103116
|
const listStatus = walletListStatus(answered);
|
|
103013
103117
|
const shownUnread = unavailable6.length > 0 || lpUnread > 0;
|
|
103014
103118
|
const listNote = listStatus === "cut-off" ? "cut-off" : listStatus === "unknown" && !shownUnread ? "unknown" : null;
|
|
103015
|
-
const complete = listNote === null && !shownUnread && !perpsPartial;
|
|
103119
|
+
const complete = listNote === null && !shownUnread && !perpsPartial && !vaultSkipped;
|
|
103016
103120
|
const totalUsd = groups.reduce((sum, g) => sum + g.valueUsd, 0);
|
|
103017
103121
|
const unpriced = groups.reduce((sum, g) => sum + g.unpriced, 0);
|
|
103018
103122
|
const lp = lpSection ? {
|
|
@@ -103089,6 +103193,7 @@ async function portfolio(args, ctx) {
|
|
|
103089
103193
|
byChain,
|
|
103090
103194
|
unavailableByChain,
|
|
103091
103195
|
listNote,
|
|
103196
|
+
vaultSkipped,
|
|
103092
103197
|
lp
|
|
103093
103198
|
});
|
|
103094
103199
|
if (perps)
|
|
@@ -103464,7 +103569,7 @@ ${tableBlock(ctx, "LP positions", ["GROUP", "WALLET", "POSITION", "POOL", "HOLDI
|
|
|
103464
103569
|
notes.push([
|
|
103465
103570
|
"total",
|
|
103466
103571
|
formatUsd(totals.totalUsd),
|
|
103467
|
-
totals.unpriced > 0 ? `${totals.unpriced} unpriced ${totals.unpriced === 1 ? "holding" : "holdings"} not counted` : "every holding priced"
|
|
103572
|
+
(totals.vaultSkipped ? "partial: the vault was not read (no terminal for its unlock). " : "") + (totals.unpriced > 0 ? `${totals.unpriced} unpriced ${totals.unpriced === 1 ? "holding" : "holdings"} not counted` : totals.vaultSkipped ? "other holdings priced" : "every holding priced")
|
|
103468
103573
|
]);
|
|
103469
103574
|
const style = terminalStyle(deps, "stdout");
|
|
103470
103575
|
const width = Math.max(...notes.map(([label]) => displayWidth(label)));
|
|
@@ -103486,6 +103591,8 @@ ${tableBlock(ctx, "LP positions", ["GROUP", "WALLET", "POSITION", "POOL", "HOLDI
|
|
|
103486
103591
|
if (unavailable6 > 0) {
|
|
103487
103592
|
warnings.push(`${unavailable6} ${unavailable6 === 1 ? "wallet" : "wallets"} could not be read in full (${unreadByChain.map(([chain2, n]) => `${n} on ${CHAIN_NAMES[chain2]}`).join(", ")}). What was not read is marked "not read" and is not in the total.`);
|
|
103488
103593
|
}
|
|
103594
|
+
if (totals.vaultSkipped)
|
|
103595
|
+
warnings.push("The vault was not read: no terminal for its unlock. Its holdings are not in the total, so the total is partial (exit 3). Run it in a terminal, or pass --no-vault to leave the vault out on purpose.");
|
|
103489
103596
|
writeNotes2(ctx, warnings);
|
|
103490
103597
|
writeListNote(ctx, totals.listNote);
|
|
103491
103598
|
const later = [];
|
|
@@ -103539,6 +103646,7 @@ ${financialHeading("Hyperliquid (live from Hyperliquid, not in the ledger; not i
|
|
|
103539
103646
|
}
|
|
103540
103647
|
|
|
103541
103648
|
// src/commands/profile.ts
|
|
103649
|
+
init_account();
|
|
103542
103650
|
init_args();
|
|
103543
103651
|
init_client();
|
|
103544
103652
|
init_human_output();
|
|
@@ -104669,8 +104777,7 @@ async function sign5(args, ctx) {
|
|
|
104669
104777
|
return usage(ctx, solana.error);
|
|
104670
104778
|
if (!refuseEnvPassphrase(ctx))
|
|
104671
104779
|
return 1;
|
|
104672
|
-
|
|
104673
|
-
return 1;
|
|
104780
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "candle sign");
|
|
104674
104781
|
const { deps } = ctx;
|
|
104675
104782
|
const resolvedVault = vaultPathFor(ctx, parsed);
|
|
104676
104783
|
if ("error" in resolvedVault)
|
|
@@ -104700,7 +104807,7 @@ async function sign5(args, ctx) {
|
|
|
104700
104807
|
throw error;
|
|
104701
104808
|
}
|
|
104702
104809
|
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
104703
|
-
const opened = await unlockInteractively(
|
|
104810
|
+
const opened = await unlockInteractively(promptCtx, path, raw, {
|
|
104704
104811
|
acceptOlderCopy: parsed.booleans.has("--accept-older-copy")
|
|
104705
104812
|
});
|
|
104706
104813
|
const vault = hold(opened.vault);
|
|
@@ -104859,8 +104966,7 @@ async function signMessage2(args, ctx) {
|
|
|
104859
104966
|
const requested = lifted.values[0];
|
|
104860
104967
|
if (!refuseEnvPassphrase(ctx))
|
|
104861
104968
|
return 1;
|
|
104862
|
-
|
|
104863
|
-
return 1;
|
|
104969
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "candle sign message");
|
|
104864
104970
|
const { deps } = ctx;
|
|
104865
104971
|
const resolvedVault = vaultPathFor(ctx, parsed);
|
|
104866
104972
|
if ("error" in resolvedVault)
|
|
@@ -104874,7 +104980,7 @@ async function signMessage2(args, ctx) {
|
|
|
104874
104980
|
throw new VaultError("VAULT_UNREADABLE", `The message could not be read: ${error instanceof Error ? error.message : error}.`);
|
|
104875
104981
|
}
|
|
104876
104982
|
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
104877
|
-
const opened = await unlockInteractively(
|
|
104983
|
+
const opened = await unlockInteractively(promptCtx, path, raw, {
|
|
104878
104984
|
acceptOlderCopy: parsed.booleans.has("--accept-older-copy")
|
|
104879
104985
|
});
|
|
104880
104986
|
const vault = hold(opened.vault);
|
|
@@ -105642,8 +105748,7 @@ async function vaultRename(args, ctx) {
|
|
|
105642
105748
|
const id = parsed.values["--id"];
|
|
105643
105749
|
if (!refuseEnvPassphrase(ctx))
|
|
105644
105750
|
return 1;
|
|
105645
|
-
|
|
105646
|
-
return 1;
|
|
105751
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "vault rename");
|
|
105647
105752
|
const { deps } = ctx;
|
|
105648
105753
|
const resolvedVault = vaultPathFor(ctx, parsed);
|
|
105649
105754
|
if ("error" in resolvedVault)
|
|
@@ -105664,7 +105769,7 @@ async function vaultRename(args, ctx) {
|
|
|
105664
105769
|
}
|
|
105665
105770
|
return runVaultCommand(ctx, async ({ hold }) => {
|
|
105666
105771
|
const raw = await requireVaultRaw(ctx, resolvedVault);
|
|
105667
|
-
const opened = await unlockInteractively(
|
|
105772
|
+
const opened = await unlockInteractively(promptCtx, path, raw, {
|
|
105668
105773
|
acceptOlderCopy: parsed.booleans.has("--accept-older-copy")
|
|
105669
105774
|
});
|
|
105670
105775
|
const vault = hold(opened.vault);
|
|
@@ -105935,10 +106040,9 @@ async function teeRename(args, ctx) {
|
|
|
105935
106040
|
if ("error" in vaultPath)
|
|
105936
106041
|
return usage(ctx, vaultPath.error);
|
|
105937
106042
|
const raw = rows.some((row2) => row2.tee) ? await readVaultRaw(vaultPath.path) : null;
|
|
105938
|
-
|
|
105939
|
-
return 1;
|
|
106043
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "tee rename");
|
|
105940
106044
|
return runVaultCommand(ctx, async ({ hold }) => {
|
|
105941
|
-
const vault = raw === null ? null : hold((await unlockInteractively(
|
|
106045
|
+
const vault = raw === null ? null : hold((await unlockInteractively(promptCtx, vaultPath.path, raw, {
|
|
105942
106046
|
acceptOlderCopy: parsed.booleans.has("--accept-older-copy")
|
|
105943
106047
|
})).vault);
|
|
105944
106048
|
const local = [];
|
|
@@ -111130,13 +111234,12 @@ async function vaultList(args, ctx) {
|
|
|
111130
111234
|
if (balances && "error" in evmRpc)
|
|
111131
111235
|
return usage(ctx, evmRpc.error);
|
|
111132
111236
|
const evmRpcUrl = "url" in evmRpc ? evmRpc.url : DEFAULT_HOOD_RPC_URL;
|
|
111133
|
-
|
|
111134
|
-
return 1;
|
|
111237
|
+
const promptCtx = refusePromptsWithoutTerminal(ctx, "vault list");
|
|
111135
111238
|
return runVaultCommand(ctx, async ({ hold }) => {
|
|
111136
111239
|
const raw = await readVaultRaw(path);
|
|
111137
111240
|
if (raw === null)
|
|
111138
111241
|
throw missingVault(ctx, resolvedVault);
|
|
111139
|
-
const vault = hold((await unlockInteractively(
|
|
111242
|
+
const vault = hold((await unlockInteractively(promptCtx, path, raw, { acceptOlderCopy: parsed.booleans.has("--accept-older-copy") })).vault);
|
|
111140
111243
|
const all = vault.index.entries;
|
|
111141
111244
|
const filtered = filter === undefined ? all : all.filter((entry) => matches2(entry, filter));
|
|
111142
111245
|
const showRetired = parsed.booleans.has("--all");
|
|
@@ -115175,6 +115278,101 @@ function walletsDisallowLaunch(args, ctx) {
|
|
|
115175
115278
|
return setAllowLaunch(args, ctx, false);
|
|
115176
115279
|
}
|
|
115177
115280
|
|
|
115281
|
+
// src/commands/wallets-allowlist.ts
|
|
115282
|
+
init_args();
|
|
115283
|
+
init_client();
|
|
115284
|
+
init_deps();
|
|
115285
|
+
init_human_output();
|
|
115286
|
+
init_presentation();
|
|
115287
|
+
init_profiles();
|
|
115288
|
+
init_render();
|
|
115289
|
+
init_key_output();
|
|
115290
|
+
var EMPTY_NOTE = "No approved withdrawal addresses. An agent can move funds only to the account's own wallets and its vault.";
|
|
115291
|
+
var MANAGED_HINT = "Approving an address needs the owner signed in to the agent console's Wallets tab; an API key cannot approve. An acting key can revoke one; a read-only key cannot.";
|
|
115292
|
+
async function walletsAllowlist(args, ctx) {
|
|
115293
|
+
const { deps, apiUrl, json } = ctx;
|
|
115294
|
+
const parsed = parseArgs(args, { booleanFlags: ["--all"] });
|
|
115295
|
+
if ("error" in parsed) {
|
|
115296
|
+
writeScreenUsageFailure(deps, parsed.error, json);
|
|
115297
|
+
return 2;
|
|
115298
|
+
}
|
|
115299
|
+
if (parsed.positionals.length > 0) {
|
|
115300
|
+
writeScreenUsageFailure(deps, `Unexpected argument: ${parsed.positionals[0]}`, json);
|
|
115301
|
+
return 2;
|
|
115302
|
+
}
|
|
115303
|
+
await printIdentity(ctx);
|
|
115304
|
+
const apiKey = await resolveApiKey(deps, ctx.profile);
|
|
115305
|
+
if (!apiKey) {
|
|
115306
|
+
writeScreenLocalFailure(deps, { code: "NO_API_KEY", message: "No API key available.", suggestion: "Run: candle keys create" }, json);
|
|
115307
|
+
return 1;
|
|
115308
|
+
}
|
|
115309
|
+
const query3 = parsed.booleans.has("--all") ? "?includeRevoked=true" : "";
|
|
115310
|
+
const result = await apiRequest(`/api/v1/agent/withdrawal-addresses${query3}`, {
|
|
115311
|
+
method: "GET",
|
|
115312
|
+
auth: "key",
|
|
115313
|
+
credentials: { apiKey },
|
|
115314
|
+
apiUrl,
|
|
115315
|
+
fetch: deps.fetch,
|
|
115316
|
+
env: deps.env
|
|
115317
|
+
});
|
|
115318
|
+
if (!result.ok) {
|
|
115319
|
+
writeScreenFailure(deps, result, { apiUrl, authType: "key" }, json);
|
|
115320
|
+
return 1;
|
|
115321
|
+
}
|
|
115322
|
+
const body = result.body;
|
|
115323
|
+
if (!Array.isArray(body.addresses)) {
|
|
115324
|
+
writeScreenLocalFailure(deps, { code: "INVALID_RESPONSE", message: "The withdrawal allowlist read did not return a list." }, json);
|
|
115325
|
+
return 1;
|
|
115326
|
+
}
|
|
115327
|
+
const rows = body.addresses;
|
|
115328
|
+
if (json) {
|
|
115329
|
+
deps.stdout.write(`${JSON.stringify({ success: true, addresses: rows, count: rows.length })}
|
|
115330
|
+
`);
|
|
115331
|
+
return 0;
|
|
115332
|
+
}
|
|
115333
|
+
const style = terminalStyle(deps, "stdout");
|
|
115334
|
+
deps.stdout.write(`${heading(`Withdrawal allowlist (${rows.length})`, style)}
|
|
115335
|
+
|
|
115336
|
+
`);
|
|
115337
|
+
if (rows.length === 0) {
|
|
115338
|
+
notice(deps, "stdout", "note", EMPTY_NOTE);
|
|
115339
|
+
notice(deps, "stdout", "note", MANAGED_HINT);
|
|
115340
|
+
return 0;
|
|
115341
|
+
}
|
|
115342
|
+
if (style.width < 100) {
|
|
115343
|
+
for (const row2 of rows) {
|
|
115344
|
+
writeFields(deps, "stdout", [
|
|
115345
|
+
["Id", row2.id],
|
|
115346
|
+
["Address", row2.address],
|
|
115347
|
+
["Label", row2.label],
|
|
115348
|
+
["Chain", row2.chain],
|
|
115349
|
+
["Revoked", row2.revokedAt ? "yes" : "no"]
|
|
115350
|
+
]);
|
|
115351
|
+
deps.stdout.write(`
|
|
115352
|
+
`);
|
|
115353
|
+
}
|
|
115354
|
+
} else {
|
|
115355
|
+
deps.stdout.write(`${renderTable(["Id", "Chain", "Address", "Label", "Revoked"], rows.map((row2) => [
|
|
115356
|
+
row2.id,
|
|
115357
|
+
row2.chain,
|
|
115358
|
+
row2.address,
|
|
115359
|
+
row2.label ?? symbols(style).missing,
|
|
115360
|
+
row2.revokedAt ? "yes" : "no"
|
|
115361
|
+
]), {
|
|
115362
|
+
style,
|
|
115363
|
+
width: style.width,
|
|
115364
|
+
indent: 2,
|
|
115365
|
+
rule: false,
|
|
115366
|
+
columns: [{ id: true }, { priority: 3 }, { id: true }, { priority: 2 }, { priority: 1 }]
|
|
115367
|
+
})}
|
|
115368
|
+
`);
|
|
115369
|
+
}
|
|
115370
|
+
deps.stdout.write(`
|
|
115371
|
+
`);
|
|
115372
|
+
notice(deps, "stdout", "note", MANAGED_HINT);
|
|
115373
|
+
return 0;
|
|
115374
|
+
}
|
|
115375
|
+
|
|
115178
115376
|
// src/commands/wallets-authorize.ts
|
|
115179
115377
|
init_args();
|
|
115180
115378
|
init_client();
|
|
@@ -115877,6 +116075,7 @@ init_ownership();
|
|
|
115877
116075
|
init_file_lock();
|
|
115878
116076
|
|
|
115879
116077
|
// src/guard.ts
|
|
116078
|
+
init_account();
|
|
115880
116079
|
init_profiles();
|
|
115881
116080
|
async function verifyProfileAccount(ctx, config) {
|
|
115882
116081
|
const { deps, profile } = ctx;
|
|
@@ -116144,7 +116343,8 @@ var COMMANDS = {
|
|
|
116144
116343
|
"allow-launch": walletsAllowLaunch,
|
|
116145
116344
|
"disallow-launch": walletsDisallowLaunch,
|
|
116146
116345
|
"close-empty": walletsCloseEmpty,
|
|
116147
|
-
authorize: walletsAuthorize
|
|
116346
|
+
authorize: walletsAuthorize,
|
|
116347
|
+
allowlist: walletsAllowlist
|
|
116148
116348
|
},
|
|
116149
116349
|
bare: wallets
|
|
116150
116350
|
},
|
package/package.json
CHANGED