@candledottv/cli 0.13.0 → 0.13.1
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 +1 -1
- package/dist/index.js +1547 -282
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -524,7 +524,7 @@ var init_terminal_theme = __esm(() => {
|
|
|
524
524
|
});
|
|
525
525
|
|
|
526
526
|
// src/version.ts
|
|
527
|
-
var CLI_VERSION = "0.13.
|
|
527
|
+
var CLI_VERSION = "0.13.1";
|
|
528
528
|
|
|
529
529
|
// src/presentation.ts
|
|
530
530
|
function terminalStyle(deps, stream, machine = false) {
|
|
@@ -38089,7 +38089,7 @@ var init_convert = __esm(() => {
|
|
|
38089
38089
|
});
|
|
38090
38090
|
|
|
38091
38091
|
// ../mcp/src/version.ts
|
|
38092
|
-
var SERVER_VERSION = "0.10.
|
|
38092
|
+
var SERVER_VERSION = "0.10.23";
|
|
38093
38093
|
|
|
38094
38094
|
// ../mcp/src/update-notice.ts
|
|
38095
38095
|
function newer(a, b) {
|
|
@@ -38382,6 +38382,909 @@ var init_image = __esm(() => {
|
|
|
38382
38382
|
nodeFs = { realpath, lstat, open: open4 };
|
|
38383
38383
|
});
|
|
38384
38384
|
|
|
38385
|
+
// ../mcp/src/launch-definition.ts
|
|
38386
|
+
var LAUNCH_DEFINITION2;
|
|
38387
|
+
var init_launch_definition = __esm(() => {
|
|
38388
|
+
LAUNCH_DEFINITION2 = {
|
|
38389
|
+
version: 1,
|
|
38390
|
+
wallet: {
|
|
38391
|
+
embeddedNames: [
|
|
38392
|
+
"embedded",
|
|
38393
|
+
"main"
|
|
38394
|
+
],
|
|
38395
|
+
help: "The paying wallet: a TEE wallet name, or embedded (main) for the account's embedded wallet."
|
|
38396
|
+
},
|
|
38397
|
+
quoteDecimals: {
|
|
38398
|
+
sol: 9,
|
|
38399
|
+
usdc: 6,
|
|
38400
|
+
cndl: 6,
|
|
38401
|
+
eth: 18,
|
|
38402
|
+
usdg: 6
|
|
38403
|
+
},
|
|
38404
|
+
quoteChains: {
|
|
38405
|
+
sol: "solana",
|
|
38406
|
+
usdc: "solana",
|
|
38407
|
+
cndl: "solana",
|
|
38408
|
+
eth: "hood",
|
|
38409
|
+
usdg: "hood"
|
|
38410
|
+
},
|
|
38411
|
+
defaultQuote: {
|
|
38412
|
+
solana: "sol",
|
|
38413
|
+
hood: "eth"
|
|
38414
|
+
},
|
|
38415
|
+
buyLimits: {
|
|
38416
|
+
defaults: {
|
|
38417
|
+
sol: {
|
|
38418
|
+
env: "DEV_BUY_MAX_LAMPORTS",
|
|
38419
|
+
raw: "500000000",
|
|
38420
|
+
unit: "lamports"
|
|
38421
|
+
},
|
|
38422
|
+
usdc: {
|
|
38423
|
+
env: "DEV_BUY_MAX_USDC_RAW",
|
|
38424
|
+
raw: "500000000",
|
|
38425
|
+
unit: "USDC raw units"
|
|
38426
|
+
},
|
|
38427
|
+
cndl: {
|
|
38428
|
+
env: "DEV_BUY_MAX_CNDL_RAW",
|
|
38429
|
+
raw: "2000000000000",
|
|
38430
|
+
unit: "CNDL raw units"
|
|
38431
|
+
},
|
|
38432
|
+
eth: {
|
|
38433
|
+
env: "DEV_BUY_MAX_WEI",
|
|
38434
|
+
raw: "20000000000000000",
|
|
38435
|
+
unit: "wei"
|
|
38436
|
+
}
|
|
38437
|
+
},
|
|
38438
|
+
thresholdBps: 1000,
|
|
38439
|
+
presetThresholds: {
|
|
38440
|
+
"exp-a": {
|
|
38441
|
+
sol: "5000000000",
|
|
38442
|
+
cndl: "330000000000"
|
|
38443
|
+
},
|
|
38444
|
+
"exp-b": {
|
|
38445
|
+
sol: "5000000000",
|
|
38446
|
+
cndl: "330000000000"
|
|
38447
|
+
},
|
|
38448
|
+
"exp-c": {
|
|
38449
|
+
sol: "5000000000",
|
|
38450
|
+
cndl: "330000000000"
|
|
38451
|
+
}
|
|
38452
|
+
}
|
|
38453
|
+
},
|
|
38454
|
+
surfacesIntro: [
|
|
38455
|
+
"CLI: scripts and terminals. Agents always pass --json.",
|
|
38456
|
+
"MCP: chat agents (Claude, the connector). The same launch fields and results as the CLI's --json.",
|
|
38457
|
+
"SDK: code that embeds Candle."
|
|
38458
|
+
],
|
|
38459
|
+
fields: [
|
|
38460
|
+
{
|
|
38461
|
+
key: "wallet",
|
|
38462
|
+
type: "string",
|
|
38463
|
+
help: "The paying wallet: a TEE wallet name, or embedded (main) for the account's embedded wallet.",
|
|
38464
|
+
required: true,
|
|
38465
|
+
surfaces: {
|
|
38466
|
+
cli: {
|
|
38467
|
+
flag: "--wallet",
|
|
38468
|
+
value: "<tee>|embedded"
|
|
38469
|
+
},
|
|
38470
|
+
mcp: {
|
|
38471
|
+
key: "wallet"
|
|
38472
|
+
},
|
|
38473
|
+
sdk: {
|
|
38474
|
+
key: "wallet"
|
|
38475
|
+
}
|
|
38476
|
+
}
|
|
38477
|
+
},
|
|
38478
|
+
{
|
|
38479
|
+
key: "clientLaunchId",
|
|
38480
|
+
type: "string",
|
|
38481
|
+
help: "An idempotency key of at most 128 characters; reuse it to recover the same launch.",
|
|
38482
|
+
range: {
|
|
38483
|
+
min: "1",
|
|
38484
|
+
max: "128",
|
|
38485
|
+
unit: "characters"
|
|
38486
|
+
},
|
|
38487
|
+
surfaces: {
|
|
38488
|
+
cli: {
|
|
38489
|
+
flag: "--client-trade-id",
|
|
38490
|
+
value: "<id>"
|
|
38491
|
+
},
|
|
38492
|
+
mcp: {
|
|
38493
|
+
key: "clientLaunchId"
|
|
38494
|
+
},
|
|
38495
|
+
sdk: {
|
|
38496
|
+
key: "clientLaunchId"
|
|
38497
|
+
}
|
|
38498
|
+
}
|
|
38499
|
+
},
|
|
38500
|
+
{
|
|
38501
|
+
key: "name",
|
|
38502
|
+
type: "string",
|
|
38503
|
+
help: "The token's name, at most 32 UTF-8 bytes.",
|
|
38504
|
+
required: true,
|
|
38505
|
+
range: {
|
|
38506
|
+
min: "1",
|
|
38507
|
+
max: "32",
|
|
38508
|
+
unit: "UTF-8 bytes"
|
|
38509
|
+
},
|
|
38510
|
+
surfaces: {
|
|
38511
|
+
cli: {
|
|
38512
|
+
flag: "--name",
|
|
38513
|
+
value: "<name>"
|
|
38514
|
+
},
|
|
38515
|
+
mcp: {
|
|
38516
|
+
key: "name"
|
|
38517
|
+
},
|
|
38518
|
+
sdk: {
|
|
38519
|
+
key: "name"
|
|
38520
|
+
}
|
|
38521
|
+
}
|
|
38522
|
+
},
|
|
38523
|
+
{
|
|
38524
|
+
key: "symbol",
|
|
38525
|
+
type: "string",
|
|
38526
|
+
help: "The token's ticker, at most 10 UTF-8 bytes.",
|
|
38527
|
+
required: true,
|
|
38528
|
+
range: {
|
|
38529
|
+
min: "1",
|
|
38530
|
+
max: "10",
|
|
38531
|
+
unit: "UTF-8 bytes"
|
|
38532
|
+
},
|
|
38533
|
+
surfaces: {
|
|
38534
|
+
cli: {
|
|
38535
|
+
flag: "--symbol",
|
|
38536
|
+
value: "<symbol>"
|
|
38537
|
+
},
|
|
38538
|
+
mcp: {
|
|
38539
|
+
key: "symbol"
|
|
38540
|
+
},
|
|
38541
|
+
sdk: {
|
|
38542
|
+
key: "symbol"
|
|
38543
|
+
}
|
|
38544
|
+
}
|
|
38545
|
+
},
|
|
38546
|
+
{
|
|
38547
|
+
key: "imageUrl",
|
|
38548
|
+
type: "url",
|
|
38549
|
+
help: "The roughly square token image at an https URL; --image <path> uploads a local file instead.",
|
|
38550
|
+
required: true,
|
|
38551
|
+
surfaces: {
|
|
38552
|
+
cli: {
|
|
38553
|
+
flag: "--image-url",
|
|
38554
|
+
value: "<https-url>"
|
|
38555
|
+
},
|
|
38556
|
+
mcp: {
|
|
38557
|
+
key: "imageUrl"
|
|
38558
|
+
},
|
|
38559
|
+
sdk: {
|
|
38560
|
+
key: "imageUrl"
|
|
38561
|
+
}
|
|
38562
|
+
}
|
|
38563
|
+
},
|
|
38564
|
+
{
|
|
38565
|
+
key: "bannerUrl",
|
|
38566
|
+
type: "url",
|
|
38567
|
+
help: "A wide banner at an https URL or a local file; uploads are public and permanent.",
|
|
38568
|
+
surfaces: {
|
|
38569
|
+
cli: {
|
|
38570
|
+
flag: "--banner",
|
|
38571
|
+
value: "<file|URL>"
|
|
38572
|
+
},
|
|
38573
|
+
mcp: {
|
|
38574
|
+
key: "bannerUrl"
|
|
38575
|
+
},
|
|
38576
|
+
sdk: {
|
|
38577
|
+
key: "bannerUrl"
|
|
38578
|
+
}
|
|
38579
|
+
}
|
|
38580
|
+
},
|
|
38581
|
+
{
|
|
38582
|
+
key: "description",
|
|
38583
|
+
type: "string",
|
|
38584
|
+
help: "The token's description, safe for on-chain JSON.",
|
|
38585
|
+
surfaces: {
|
|
38586
|
+
cli: {
|
|
38587
|
+
flag: "--description",
|
|
38588
|
+
value: "<text>"
|
|
38589
|
+
},
|
|
38590
|
+
mcp: {
|
|
38591
|
+
key: "description"
|
|
38592
|
+
},
|
|
38593
|
+
sdk: {
|
|
38594
|
+
key: "description"
|
|
38595
|
+
}
|
|
38596
|
+
}
|
|
38597
|
+
},
|
|
38598
|
+
{
|
|
38599
|
+
key: "socials.website",
|
|
38600
|
+
type: "url",
|
|
38601
|
+
help: "The token's website link as an absolute http(s) URL.",
|
|
38602
|
+
surfaces: {
|
|
38603
|
+
cli: {
|
|
38604
|
+
flag: "--website",
|
|
38605
|
+
value: "<url>"
|
|
38606
|
+
},
|
|
38607
|
+
mcp: {
|
|
38608
|
+
key: "socials.website"
|
|
38609
|
+
},
|
|
38610
|
+
sdk: {
|
|
38611
|
+
key: "socials.website"
|
|
38612
|
+
}
|
|
38613
|
+
}
|
|
38614
|
+
},
|
|
38615
|
+
{
|
|
38616
|
+
key: "socials.twitter",
|
|
38617
|
+
type: "url",
|
|
38618
|
+
help: "The token's X link as an absolute http(s) URL.",
|
|
38619
|
+
surfaces: {
|
|
38620
|
+
cli: {
|
|
38621
|
+
flag: "--x",
|
|
38622
|
+
value: "<url>"
|
|
38623
|
+
},
|
|
38624
|
+
mcp: {
|
|
38625
|
+
key: "socials.twitter"
|
|
38626
|
+
},
|
|
38627
|
+
sdk: {
|
|
38628
|
+
key: "socials.twitter"
|
|
38629
|
+
}
|
|
38630
|
+
}
|
|
38631
|
+
},
|
|
38632
|
+
{
|
|
38633
|
+
key: "socials.telegram",
|
|
38634
|
+
type: "url",
|
|
38635
|
+
help: "The token's telegram link as an absolute http(s) URL.",
|
|
38636
|
+
surfaces: {
|
|
38637
|
+
cli: {
|
|
38638
|
+
flag: "--telegram",
|
|
38639
|
+
value: "<url>"
|
|
38640
|
+
},
|
|
38641
|
+
mcp: {
|
|
38642
|
+
key: "socials.telegram"
|
|
38643
|
+
},
|
|
38644
|
+
sdk: {
|
|
38645
|
+
key: "socials.telegram"
|
|
38646
|
+
}
|
|
38647
|
+
}
|
|
38648
|
+
},
|
|
38649
|
+
{
|
|
38650
|
+
key: "socials.discord",
|
|
38651
|
+
type: "url",
|
|
38652
|
+
help: "The token's discord link as an absolute http(s) URL.",
|
|
38653
|
+
surfaces: {
|
|
38654
|
+
cli: {
|
|
38655
|
+
flag: "--discord",
|
|
38656
|
+
value: "<url>"
|
|
38657
|
+
},
|
|
38658
|
+
mcp: {
|
|
38659
|
+
key: "socials.discord"
|
|
38660
|
+
},
|
|
38661
|
+
sdk: {
|
|
38662
|
+
key: "socials.discord"
|
|
38663
|
+
}
|
|
38664
|
+
}
|
|
38665
|
+
},
|
|
38666
|
+
{
|
|
38667
|
+
key: "chain",
|
|
38668
|
+
type: "enum",
|
|
38669
|
+
values: [
|
|
38670
|
+
"solana",
|
|
38671
|
+
"hood"
|
|
38672
|
+
],
|
|
38673
|
+
default: "solana",
|
|
38674
|
+
help: "The launch chain, inferred from the wallet, quote asset and dex version on the CLI.",
|
|
38675
|
+
surfaces: {
|
|
38676
|
+
cli: {
|
|
38677
|
+
derived: "wallet, --quote-asset and --dex-version"
|
|
38678
|
+
},
|
|
38679
|
+
mcp: {
|
|
38680
|
+
key: "chain"
|
|
38681
|
+
},
|
|
38682
|
+
sdk: {
|
|
38683
|
+
key: "chain"
|
|
38684
|
+
}
|
|
38685
|
+
}
|
|
38686
|
+
},
|
|
38687
|
+
{
|
|
38688
|
+
key: "quoteAsset",
|
|
38689
|
+
type: "enum",
|
|
38690
|
+
help: "The quote asset: sol, usdc or cndl on Solana; eth or usdg on Hood (defaults sol and eth).",
|
|
38691
|
+
values: [
|
|
38692
|
+
"sol",
|
|
38693
|
+
"usdc",
|
|
38694
|
+
"cndl",
|
|
38695
|
+
"eth",
|
|
38696
|
+
"usdg"
|
|
38697
|
+
],
|
|
38698
|
+
surfaces: {
|
|
38699
|
+
cli: {
|
|
38700
|
+
flag: "--quote-asset",
|
|
38701
|
+
value: "sol|usdc|cndl|eth|usdg"
|
|
38702
|
+
},
|
|
38703
|
+
mcp: {
|
|
38704
|
+
key: "quoteAsset"
|
|
38705
|
+
},
|
|
38706
|
+
sdk: {
|
|
38707
|
+
key: "quoteAsset"
|
|
38708
|
+
}
|
|
38709
|
+
}
|
|
38710
|
+
},
|
|
38711
|
+
{
|
|
38712
|
+
key: "mode",
|
|
38713
|
+
type: "enum",
|
|
38714
|
+
help: "The curve mode: open or exclusive; test-open or test-exclusive need ENABLE_TEST_CURVES; exp-a, exp-b, exp-c and exp-custom are Solana sol/cndl only.",
|
|
38715
|
+
values: [
|
|
38716
|
+
"open",
|
|
38717
|
+
"exclusive",
|
|
38718
|
+
"test-open",
|
|
38719
|
+
"test-exclusive",
|
|
38720
|
+
"exp-a",
|
|
38721
|
+
"exp-b",
|
|
38722
|
+
"exp-c",
|
|
38723
|
+
"exp-custom"
|
|
38724
|
+
],
|
|
38725
|
+
default: "open",
|
|
38726
|
+
surfaces: {
|
|
38727
|
+
cli: {
|
|
38728
|
+
flag: "--mode",
|
|
38729
|
+
value: "<mode>"
|
|
38730
|
+
},
|
|
38731
|
+
mcp: {
|
|
38732
|
+
key: "mode"
|
|
38733
|
+
},
|
|
38734
|
+
sdk: {
|
|
38735
|
+
key: "mode"
|
|
38736
|
+
}
|
|
38737
|
+
}
|
|
38738
|
+
},
|
|
38739
|
+
{
|
|
38740
|
+
key: "curve.virtualQuote",
|
|
38741
|
+
type: "decimal",
|
|
38742
|
+
help: "exp-custom's virtual quote, in whole quote units: 1 to 100 SOL or 66000 to 6600000 CNDL; requires --curve-g and EXP_CUSTOM_CURVES.",
|
|
38743
|
+
modes: [
|
|
38744
|
+
"exp-custom"
|
|
38745
|
+
],
|
|
38746
|
+
chains: [
|
|
38747
|
+
"solana"
|
|
38748
|
+
],
|
|
38749
|
+
requires: [
|
|
38750
|
+
"curve.threshold"
|
|
38751
|
+
],
|
|
38752
|
+
range: {
|
|
38753
|
+
unit: "whole quote units",
|
|
38754
|
+
byQuote: {
|
|
38755
|
+
sol: {
|
|
38756
|
+
min: "1",
|
|
38757
|
+
max: "100"
|
|
38758
|
+
},
|
|
38759
|
+
cndl: {
|
|
38760
|
+
min: "66000",
|
|
38761
|
+
max: "6600000"
|
|
38762
|
+
}
|
|
38763
|
+
}
|
|
38764
|
+
},
|
|
38765
|
+
surfaces: {
|
|
38766
|
+
cli: {
|
|
38767
|
+
flag: "--curve-v",
|
|
38768
|
+
value: "<V>"
|
|
38769
|
+
},
|
|
38770
|
+
mcp: {
|
|
38771
|
+
key: "curve.virtualQuote"
|
|
38772
|
+
},
|
|
38773
|
+
sdk: {
|
|
38774
|
+
key: "curve.virtualQuote"
|
|
38775
|
+
}
|
|
38776
|
+
}
|
|
38777
|
+
},
|
|
38778
|
+
{
|
|
38779
|
+
key: "curve.threshold",
|
|
38780
|
+
type: "decimal",
|
|
38781
|
+
help: "exp-custom's graduation threshold, in whole quote units: 1 to 250 SOL or 66000 to 16500000 CNDL; requires --curve-v and EXP_CUSTOM_CURVES.",
|
|
38782
|
+
modes: [
|
|
38783
|
+
"exp-custom"
|
|
38784
|
+
],
|
|
38785
|
+
chains: [
|
|
38786
|
+
"solana"
|
|
38787
|
+
],
|
|
38788
|
+
requires: [
|
|
38789
|
+
"curve.virtualQuote"
|
|
38790
|
+
],
|
|
38791
|
+
range: {
|
|
38792
|
+
unit: "whole quote units",
|
|
38793
|
+
byQuote: {
|
|
38794
|
+
sol: {
|
|
38795
|
+
min: "1",
|
|
38796
|
+
max: "250"
|
|
38797
|
+
},
|
|
38798
|
+
cndl: {
|
|
38799
|
+
min: "66000",
|
|
38800
|
+
max: "16500000"
|
|
38801
|
+
}
|
|
38802
|
+
}
|
|
38803
|
+
},
|
|
38804
|
+
surfaces: {
|
|
38805
|
+
cli: {
|
|
38806
|
+
flag: "--curve-g",
|
|
38807
|
+
value: "<G>"
|
|
38808
|
+
},
|
|
38809
|
+
mcp: {
|
|
38810
|
+
key: "curve.threshold"
|
|
38811
|
+
},
|
|
38812
|
+
sdk: {
|
|
38813
|
+
key: "curve.threshold"
|
|
38814
|
+
}
|
|
38815
|
+
}
|
|
38816
|
+
},
|
|
38817
|
+
{
|
|
38818
|
+
key: "creatorFeeBps",
|
|
38819
|
+
type: "enum",
|
|
38820
|
+
help: "An exp- mode's extra creator fee: 0, 100 or 200 bps on the curve and pool, paid to the creator in full.",
|
|
38821
|
+
values: [
|
|
38822
|
+
0,
|
|
38823
|
+
100,
|
|
38824
|
+
200
|
|
38825
|
+
],
|
|
38826
|
+
default: 0,
|
|
38827
|
+
surfaces: {
|
|
38828
|
+
cli: {
|
|
38829
|
+
flag: "--creator-fee-bps",
|
|
38830
|
+
value: "0|100|200"
|
|
38831
|
+
},
|
|
38832
|
+
mcp: {
|
|
38833
|
+
key: "creatorFeeBps"
|
|
38834
|
+
},
|
|
38835
|
+
sdk: {
|
|
38836
|
+
key: "creatorFeeBps"
|
|
38837
|
+
}
|
|
38838
|
+
}
|
|
38839
|
+
},
|
|
38840
|
+
{
|
|
38841
|
+
key: "stakerAllocationBps",
|
|
38842
|
+
type: "integer",
|
|
38843
|
+
help: "The staker allocation, 50 to 1000 bps; defaults to 50, or 500 on exp- curves.",
|
|
38844
|
+
range: {
|
|
38845
|
+
min: "50",
|
|
38846
|
+
max: "1000",
|
|
38847
|
+
unit: "bps"
|
|
38848
|
+
},
|
|
38849
|
+
default: 50,
|
|
38850
|
+
surfaces: {
|
|
38851
|
+
cli: {
|
|
38852
|
+
flag: "--staker-allocation-bps",
|
|
38853
|
+
value: "<bps>"
|
|
38854
|
+
},
|
|
38855
|
+
mcp: {
|
|
38856
|
+
key: "stakerAllocationBps"
|
|
38857
|
+
},
|
|
38858
|
+
sdk: {
|
|
38859
|
+
key: "stakerAllocationBps"
|
|
38860
|
+
}
|
|
38861
|
+
}
|
|
38862
|
+
},
|
|
38863
|
+
{
|
|
38864
|
+
key: "poolFee.tier",
|
|
38865
|
+
type: "enum",
|
|
38866
|
+
help: "Solana's graduated pool fee: 25, 50 or 100 bps (default 50), or the ending rate with --pool-fee-from.",
|
|
38867
|
+
values: [
|
|
38868
|
+
25,
|
|
38869
|
+
50,
|
|
38870
|
+
100
|
|
38871
|
+
],
|
|
38872
|
+
default: 50,
|
|
38873
|
+
chains: [
|
|
38874
|
+
"solana"
|
|
38875
|
+
],
|
|
38876
|
+
surfaces: {
|
|
38877
|
+
cli: {
|
|
38878
|
+
flag: "--pool-fee",
|
|
38879
|
+
value: "25|50|100"
|
|
38880
|
+
},
|
|
38881
|
+
mcp: {
|
|
38882
|
+
key: "poolFee.tier"
|
|
38883
|
+
},
|
|
38884
|
+
sdk: {
|
|
38885
|
+
key: "poolFee.tier"
|
|
38886
|
+
}
|
|
38887
|
+
}
|
|
38888
|
+
},
|
|
38889
|
+
{
|
|
38890
|
+
key: "poolFee.startBps",
|
|
38891
|
+
type: "integer",
|
|
38892
|
+
help: "The falling pool fee's starting rate, at most 2500 bps; requires --pool-fee and --pool-fee-minutes.",
|
|
38893
|
+
range: {
|
|
38894
|
+
min: "25",
|
|
38895
|
+
max: "2500",
|
|
38896
|
+
unit: "bps"
|
|
38897
|
+
},
|
|
38898
|
+
chains: [
|
|
38899
|
+
"solana"
|
|
38900
|
+
],
|
|
38901
|
+
requires: [
|
|
38902
|
+
"poolFee.tier",
|
|
38903
|
+
"poolFee.minutes"
|
|
38904
|
+
],
|
|
38905
|
+
surfaces: {
|
|
38906
|
+
cli: {
|
|
38907
|
+
flag: "--pool-fee-from",
|
|
38908
|
+
value: "<bps>"
|
|
38909
|
+
},
|
|
38910
|
+
mcp: {
|
|
38911
|
+
key: "poolFee.startBps"
|
|
38912
|
+
},
|
|
38913
|
+
sdk: {
|
|
38914
|
+
key: "poolFee.startBps"
|
|
38915
|
+
}
|
|
38916
|
+
}
|
|
38917
|
+
},
|
|
38918
|
+
{
|
|
38919
|
+
key: "poolFee.minutes",
|
|
38920
|
+
type: "decimal",
|
|
38921
|
+
help: "The pool fee's fall time, at most 30 minutes; requires --pool-fee and --pool-fee-from.",
|
|
38922
|
+
wireType: "number",
|
|
38923
|
+
range: {
|
|
38924
|
+
min: "0.006666666666666667",
|
|
38925
|
+
max: "30",
|
|
38926
|
+
unit: "minutes"
|
|
38927
|
+
},
|
|
38928
|
+
chains: [
|
|
38929
|
+
"solana"
|
|
38930
|
+
],
|
|
38931
|
+
requires: [
|
|
38932
|
+
"poolFee.tier",
|
|
38933
|
+
"poolFee.startBps"
|
|
38934
|
+
],
|
|
38935
|
+
surfaces: {
|
|
38936
|
+
cli: {
|
|
38937
|
+
flag: "--pool-fee-minutes",
|
|
38938
|
+
value: "<min>"
|
|
38939
|
+
},
|
|
38940
|
+
mcp: {
|
|
38941
|
+
key: "poolFee.minutes"
|
|
38942
|
+
},
|
|
38943
|
+
sdk: {
|
|
38944
|
+
key: "poolFee.minutes"
|
|
38945
|
+
}
|
|
38946
|
+
}
|
|
38947
|
+
},
|
|
38948
|
+
{
|
|
38949
|
+
key: "poolFee.endBps",
|
|
38950
|
+
type: "enum",
|
|
38951
|
+
values: [
|
|
38952
|
+
25,
|
|
38953
|
+
50,
|
|
38954
|
+
100
|
|
38955
|
+
],
|
|
38956
|
+
chains: [
|
|
38957
|
+
"solana"
|
|
38958
|
+
],
|
|
38959
|
+
help: "The falling fee's ending rate, taken from --pool-fee.",
|
|
38960
|
+
surfaces: {
|
|
38961
|
+
cli: {
|
|
38962
|
+
derived: "--pool-fee with --pool-fee-from"
|
|
38963
|
+
},
|
|
38964
|
+
mcp: {
|
|
38965
|
+
key: "poolFee.endBps"
|
|
38966
|
+
},
|
|
38967
|
+
sdk: {
|
|
38968
|
+
key: "poolFee.endBps"
|
|
38969
|
+
}
|
|
38970
|
+
}
|
|
38971
|
+
},
|
|
38972
|
+
{
|
|
38973
|
+
key: "poolOpen",
|
|
38974
|
+
type: "enum",
|
|
38975
|
+
help: "Solana preset pools: curve_price opens at the curve's last price and burns the rest; omitted uses curve_price when allocations fit graduation.maxStakerCarveBps, otherwise legacy; explicit curve_price above that limit is refused.",
|
|
38976
|
+
values: [
|
|
38977
|
+
"legacy",
|
|
38978
|
+
"curve_price"
|
|
38979
|
+
],
|
|
38980
|
+
chains: [
|
|
38981
|
+
"solana"
|
|
38982
|
+
],
|
|
38983
|
+
surfaces: {
|
|
38984
|
+
cli: {
|
|
38985
|
+
flag: "--pool-open",
|
|
38986
|
+
value: "legacy|curve_price"
|
|
38987
|
+
},
|
|
38988
|
+
mcp: {
|
|
38989
|
+
key: "poolOpen"
|
|
38990
|
+
},
|
|
38991
|
+
sdk: {
|
|
38992
|
+
key: "poolOpen"
|
|
38993
|
+
}
|
|
38994
|
+
}
|
|
38995
|
+
},
|
|
38996
|
+
{
|
|
38997
|
+
key: "expPoolShape",
|
|
38998
|
+
type: "enum",
|
|
38999
|
+
help: "Solana exp- pools: floor by default, or full_range to open across the whole price range.",
|
|
39000
|
+
values: [
|
|
39001
|
+
"floor",
|
|
39002
|
+
"full_range"
|
|
39003
|
+
],
|
|
39004
|
+
default: "floor",
|
|
39005
|
+
chains: [
|
|
39006
|
+
"solana"
|
|
39007
|
+
],
|
|
39008
|
+
surfaces: {
|
|
39009
|
+
cli: {
|
|
39010
|
+
flag: "--exp-pool-shape",
|
|
39011
|
+
value: "floor|full_range"
|
|
39012
|
+
},
|
|
39013
|
+
mcp: {
|
|
39014
|
+
key: "expPoolShape"
|
|
39015
|
+
},
|
|
39016
|
+
sdk: {
|
|
39017
|
+
key: "expPoolShape"
|
|
39018
|
+
}
|
|
39019
|
+
}
|
|
39020
|
+
},
|
|
39021
|
+
{
|
|
39022
|
+
key: "dexVersion",
|
|
39023
|
+
type: "enum",
|
|
39024
|
+
help: "Hood only, required there: the Uniswap version the curve graduates to.",
|
|
39025
|
+
values: [
|
|
39026
|
+
"v3",
|
|
39027
|
+
"v4"
|
|
39028
|
+
],
|
|
39029
|
+
chains: [
|
|
39030
|
+
"hood"
|
|
39031
|
+
],
|
|
39032
|
+
surfaces: {
|
|
39033
|
+
cli: {
|
|
39034
|
+
flag: "--dex-version",
|
|
39035
|
+
value: "v3|v4"
|
|
39036
|
+
},
|
|
39037
|
+
mcp: {
|
|
39038
|
+
key: "dexVersion"
|
|
39039
|
+
},
|
|
39040
|
+
sdk: {
|
|
39041
|
+
key: "dexVersion"
|
|
39042
|
+
}
|
|
39043
|
+
}
|
|
39044
|
+
},
|
|
39045
|
+
{
|
|
39046
|
+
key: "visibility",
|
|
39047
|
+
type: "enum",
|
|
39048
|
+
help: "The token's visibility: production, test, local or hidden; test keys require test or hidden.",
|
|
39049
|
+
values: [
|
|
39050
|
+
"production",
|
|
39051
|
+
"test",
|
|
39052
|
+
"local",
|
|
39053
|
+
"hidden"
|
|
39054
|
+
],
|
|
39055
|
+
default: "production",
|
|
39056
|
+
surfaces: {
|
|
39057
|
+
cli: {
|
|
39058
|
+
flag: "--visibility",
|
|
39059
|
+
value: "production|test|local|hidden"
|
|
39060
|
+
},
|
|
39061
|
+
mcp: {
|
|
39062
|
+
key: "visibility"
|
|
39063
|
+
},
|
|
39064
|
+
sdk: {
|
|
39065
|
+
key: "visibility"
|
|
39066
|
+
}
|
|
39067
|
+
}
|
|
39068
|
+
},
|
|
39069
|
+
{
|
|
39070
|
+
key: "buy",
|
|
39071
|
+
type: "decimal",
|
|
39072
|
+
help: "An optional first buy in whole quote units; zero or empty means no buy, and the server applies dev-buy ceilings and TEE_LAUNCH_BUYS_ENABLED for TEE buys.",
|
|
39073
|
+
default: "0",
|
|
39074
|
+
range: {
|
|
39075
|
+
min: "0",
|
|
39076
|
+
unit: "whole quote units"
|
|
39077
|
+
},
|
|
39078
|
+
more: "Default raw caps (apps/api/src/lib/headless-validation.ts): DEV_BUY_MAX_LAMPORTS=500000000, DEV_BUY_MAX_USDC_RAW=500000000, DEV_BUY_MAX_CNDL_RAW=2000000000000, DEV_BUY_MAX_WEI=20000000000000000. On exp- curves the lower of the quote cap and 10% of G applies.",
|
|
39079
|
+
surfaces: {
|
|
39080
|
+
cli: {
|
|
39081
|
+
flag: "--buy",
|
|
39082
|
+
value: "<amount>"
|
|
39083
|
+
},
|
|
39084
|
+
mcp: {
|
|
39085
|
+
key: "buy"
|
|
39086
|
+
},
|
|
39087
|
+
sdk: {
|
|
39088
|
+
key: "buy"
|
|
39089
|
+
}
|
|
39090
|
+
}
|
|
39091
|
+
},
|
|
39092
|
+
{
|
|
39093
|
+
key: "dryRun",
|
|
39094
|
+
type: "boolean",
|
|
39095
|
+
help: "Print the launch preview without building, signing or recording a launch; local image uploads still become public and permanent.",
|
|
39096
|
+
default: false,
|
|
39097
|
+
surfaces: {
|
|
39098
|
+
cli: {
|
|
39099
|
+
flag: "--dry-run"
|
|
39100
|
+
},
|
|
39101
|
+
mcp: {
|
|
39102
|
+
key: "dryRun"
|
|
39103
|
+
},
|
|
39104
|
+
sdk: {
|
|
39105
|
+
key: "dryRun"
|
|
39106
|
+
}
|
|
39107
|
+
}
|
|
39108
|
+
}
|
|
39109
|
+
]
|
|
39110
|
+
};
|
|
39111
|
+
});
|
|
39112
|
+
|
|
39113
|
+
// ../mcp/src/launch-schema.ts
|
|
39114
|
+
function mcpKey(field) {
|
|
39115
|
+
const surface = field.surfaces.mcp;
|
|
39116
|
+
return "key" in surface ? surface.key : undefined;
|
|
39117
|
+
}
|
|
39118
|
+
function topKey(key) {
|
|
39119
|
+
return key.split(".")[0] ?? key;
|
|
39120
|
+
}
|
|
39121
|
+
function mcpLaunchFields() {
|
|
39122
|
+
return LAUNCH_DEFINITION2.fields.filter((field) => {
|
|
39123
|
+
const key = mcpKey(field);
|
|
39124
|
+
return key !== undefined && !M2_TOP_KEYS.has(topKey(key));
|
|
39125
|
+
});
|
|
39126
|
+
}
|
|
39127
|
+
function fieldByKey(key) {
|
|
39128
|
+
const field = LAUNCH_DEFINITION2.fields.find((item) => item.key === key);
|
|
39129
|
+
if (!field)
|
|
39130
|
+
throw new Error(`Missing launch field ${key}`);
|
|
39131
|
+
return field;
|
|
39132
|
+
}
|
|
39133
|
+
function launchFieldHelp(key) {
|
|
39134
|
+
return fieldByKey(key).help;
|
|
39135
|
+
}
|
|
39136
|
+
function stringValues(key) {
|
|
39137
|
+
const field = fieldByKey(key);
|
|
39138
|
+
const values = "values" in field ? field.values : undefined;
|
|
39139
|
+
if (!values || values.some((value) => typeof value !== "string")) {
|
|
39140
|
+
throw new Error(`Expected string values for ${key}`);
|
|
39141
|
+
}
|
|
39142
|
+
return [...values];
|
|
39143
|
+
}
|
|
39144
|
+
function numberValues(key) {
|
|
39145
|
+
const field = fieldByKey(key);
|
|
39146
|
+
const values = "values" in field ? field.values : undefined;
|
|
39147
|
+
if (!values || values.length < 2 || values.some((value) => typeof value !== "number")) {
|
|
39148
|
+
throw new Error(`Expected number values for ${key}`);
|
|
39149
|
+
}
|
|
39150
|
+
return [...values];
|
|
39151
|
+
}
|
|
39152
|
+
function rangeMax(key) {
|
|
39153
|
+
const field = fieldByKey(key);
|
|
39154
|
+
if (!("range" in field) || field.range === undefined || !("max" in field.range)) {
|
|
39155
|
+
throw new Error(`Missing max for ${key}`);
|
|
39156
|
+
}
|
|
39157
|
+
return Number(field.range.max);
|
|
39158
|
+
}
|
|
39159
|
+
function numberEnum(key) {
|
|
39160
|
+
const [first, second, ...rest] = numberValues(key);
|
|
39161
|
+
return exports_external.union([exports_external.literal(first), exports_external.literal(second), ...rest.map((value) => exports_external.literal(value))]);
|
|
39162
|
+
}
|
|
39163
|
+
function described(schema, help, required) {
|
|
39164
|
+
return required ? schema.describe(help) : schema.optional().describe(help);
|
|
39165
|
+
}
|
|
39166
|
+
function isRequired(field) {
|
|
39167
|
+
return "required" in field && field.required === true || field.key === "clientLaunchId";
|
|
39168
|
+
}
|
|
39169
|
+
function launchSchemaField(shape, key) {
|
|
39170
|
+
const field = shape[key];
|
|
39171
|
+
if (!field)
|
|
39172
|
+
throw new Error(`Missing launch schema field ${key}`);
|
|
39173
|
+
return field;
|
|
39174
|
+
}
|
|
39175
|
+
function socialsRefusal(socials) {
|
|
39176
|
+
if (socials === undefined)
|
|
39177
|
+
return;
|
|
39178
|
+
if (!socials || typeof socials !== "object" || Array.isArray(socials)) {
|
|
39179
|
+
return { code: "VALIDATION_FAILED", message: SOCIALS_ONLY, field: "socials" };
|
|
39180
|
+
}
|
|
39181
|
+
for (const [key, value] of Object.entries(socials)) {
|
|
39182
|
+
if (!SOCIAL_KEYS.includes(key))
|
|
39183
|
+
return { code: "VALIDATION_FAILED", message: SOCIALS_ONLY, field: "socials" };
|
|
39184
|
+
if (value === undefined)
|
|
39185
|
+
continue;
|
|
39186
|
+
try {
|
|
39187
|
+
if (typeof value !== "string")
|
|
39188
|
+
throw new Error("not a string");
|
|
39189
|
+
const url = new URL(value);
|
|
39190
|
+
if (!["http:", "https:"].includes(url.protocol))
|
|
39191
|
+
throw new Error("not http(s)");
|
|
39192
|
+
} catch {
|
|
39193
|
+
return { code: "VALIDATION_FAILED", message: `${key} must be an http(s) URL.`, field: `socials.${key}` };
|
|
39194
|
+
}
|
|
39195
|
+
}
|
|
39196
|
+
return;
|
|
39197
|
+
}
|
|
39198
|
+
function socialsSchema() {
|
|
39199
|
+
const shape = {};
|
|
39200
|
+
const helps = [];
|
|
39201
|
+
for (const field of mcpLaunchFields()) {
|
|
39202
|
+
const key = mcpKey(field);
|
|
39203
|
+
if (!key?.startsWith("socials."))
|
|
39204
|
+
continue;
|
|
39205
|
+
shape[key.slice("socials.".length)] = exports_external.string().optional().describe(field.help);
|
|
39206
|
+
helps.push(field.help);
|
|
39207
|
+
}
|
|
39208
|
+
return exports_external.object(shape).passthrough().optional().describe(helps.join(" "));
|
|
39209
|
+
}
|
|
39210
|
+
function curveSchema() {
|
|
39211
|
+
const virtual = launchFieldHelp("curve.virtualQuote");
|
|
39212
|
+
const threshold = launchFieldHelp("curve.threshold");
|
|
39213
|
+
return exports_external.object({
|
|
39214
|
+
virtualQuote: exports_external.string().describe(virtual),
|
|
39215
|
+
threshold: exports_external.string().describe(threshold)
|
|
39216
|
+
}).strict().optional().describe(`${virtual} ${threshold}`);
|
|
39217
|
+
}
|
|
39218
|
+
function poolFeeSchema() {
|
|
39219
|
+
const tierHelp = launchFieldHelp("poolFee.tier");
|
|
39220
|
+
const startHelp = launchFieldHelp("poolFee.startBps");
|
|
39221
|
+
const endHelp = launchFieldHelp("poolFee.endBps");
|
|
39222
|
+
const minutesHelp = launchFieldHelp("poolFee.minutes");
|
|
39223
|
+
return exports_external.union([
|
|
39224
|
+
exports_external.object({ tier: numberEnum("poolFee.tier").describe(tierHelp) }).strict(),
|
|
39225
|
+
exports_external.object({
|
|
39226
|
+
startBps: exports_external.number().int().max(rangeMax("poolFee.startBps")).describe(startHelp),
|
|
39227
|
+
endBps: numberEnum("poolFee.endBps").describe(endHelp),
|
|
39228
|
+
minutes: exports_external.number().positive().max(rangeMax("poolFee.minutes")).describe(minutesHelp)
|
|
39229
|
+
}).strict()
|
|
39230
|
+
]).optional().describe([tierHelp, startHelp, endHelp, minutesHelp].join(" "));
|
|
39231
|
+
}
|
|
39232
|
+
function scalarSchema(field) {
|
|
39233
|
+
const values = "values" in field ? field.values : undefined;
|
|
39234
|
+
let schema;
|
|
39235
|
+
if (values?.every((value) => typeof value === "number"))
|
|
39236
|
+
schema = numberEnum(field.key);
|
|
39237
|
+
else if (values?.every((value) => typeof value === "string"))
|
|
39238
|
+
schema = exports_external.enum(stringValues(field.key));
|
|
39239
|
+
else if (field.type === "boolean")
|
|
39240
|
+
schema = exports_external.boolean();
|
|
39241
|
+
else if (field.type === "integer" || "wireType" in field && field.wireType === "number")
|
|
39242
|
+
schema = exports_external.number();
|
|
39243
|
+
else
|
|
39244
|
+
schema = exports_external.string();
|
|
39245
|
+
return described(schema, field.help, isRequired(field));
|
|
39246
|
+
}
|
|
39247
|
+
function buildLaunchTokenShape() {
|
|
39248
|
+
const shape = {};
|
|
39249
|
+
for (const field of mcpLaunchFields()) {
|
|
39250
|
+
const key = mcpKey(field);
|
|
39251
|
+
if (!key)
|
|
39252
|
+
continue;
|
|
39253
|
+
const top = topKey(key);
|
|
39254
|
+
if (GROUPED_TOP_KEYS.has(top) || top in shape)
|
|
39255
|
+
continue;
|
|
39256
|
+
shape[top] = scalarSchema(field);
|
|
39257
|
+
}
|
|
39258
|
+
shape.socials = socialsSchema();
|
|
39259
|
+
shape.curve = curveSchema();
|
|
39260
|
+
shape.poolFee = poolFeeSchema();
|
|
39261
|
+
shape.buyAmount = exports_external.union([exports_external.string(), exports_external.number()]).optional().describe(BUY_AMOUNT_DESCRIPTION);
|
|
39262
|
+
return shape;
|
|
39263
|
+
}
|
|
39264
|
+
var M2_TOP_KEYS, GROUPED_TOP_KEYS, LAUNCH_SHARED_SENTENCES, SOCIAL_KEYS, SOCIALS_ONLY, BUY_AMOUNT_DESCRIPTION = "Initial buy, in quote base units (string or number)", DEV_BUY_DESCRIPTION, launchTokenShape, launchAndSeedShape;
|
|
39265
|
+
var init_launch_schema = __esm(() => {
|
|
39266
|
+
init_zod();
|
|
39267
|
+
init_launch_definition();
|
|
39268
|
+
M2_TOP_KEYS = new Set(["wallet", "buy"]);
|
|
39269
|
+
GROUPED_TOP_KEYS = new Set(["socials", "curve", "poolFee"]);
|
|
39270
|
+
LAUNCH_SHARED_SENTENCES = mcpLaunchFields().map((field) => field.help).join(" ");
|
|
39271
|
+
SOCIAL_KEYS = mcpLaunchFields().flatMap((field) => {
|
|
39272
|
+
const key = mcpKey(field);
|
|
39273
|
+
return key?.startsWith("socials.") ? [key.slice("socials.".length)] : [];
|
|
39274
|
+
});
|
|
39275
|
+
SOCIALS_ONLY = `socials accepts only ${SOCIAL_KEYS.join(", ")}.`;
|
|
39276
|
+
DEV_BUY_DESCRIPTION = 'Seed buy in DECIMAL units of the quote asset this launch selects (e.g. "0.25" SOL, or ' + "ETH on hood), paid from the account's embedded wallet; the tool's description says how " + "each chain sends it. Follows quoteAsset, which defaults to sol on solana and eth on hood. " + "Capped by the platform dev-buy ceiling; for a larger seed, launch then follow with candle_trade.";
|
|
39277
|
+
launchTokenShape = buildLaunchTokenShape();
|
|
39278
|
+
launchAndSeedShape = (() => {
|
|
39279
|
+
const { buyAmount: _rawBuyAmount, ...seedable } = launchTokenShape;
|
|
39280
|
+
return {
|
|
39281
|
+
...seedable,
|
|
39282
|
+
clientLaunchId: exports_external.string().optional().describe(`${launchFieldHelp("clientLaunchId")} Auto-generated when omitted and echoed in the result.`),
|
|
39283
|
+
devBuy: exports_external.string().optional().describe(DEV_BUY_DESCRIPTION)
|
|
39284
|
+
};
|
|
39285
|
+
})();
|
|
39286
|
+
});
|
|
39287
|
+
|
|
38385
39288
|
// ../mcp/src/market-disclosure.ts
|
|
38386
39289
|
var EXTERNAL_MARKET_DISCLOSURE;
|
|
38387
39290
|
var init_market_disclosure = __esm(() => {
|
|
@@ -38641,6 +39544,9 @@ async function executeTrade(args, cfg, doFetch) {
|
|
|
38641
39544
|
async function executeLaunchAndSeed(args, cfg, doFetch, imageDir, imageFs, opts = {}) {
|
|
38642
39545
|
const apiKey = requireApiKey(cfg);
|
|
38643
39546
|
const clientLaunchId = args.clientLaunchId ?? randomUUID3();
|
|
39547
|
+
const socialsProblem = socialsRefusal(args.socials);
|
|
39548
|
+
if (socialsProblem)
|
|
39549
|
+
return { text: imageRefusalBody(socialsProblem, { clientLaunchId }), isError: true };
|
|
38644
39550
|
const { devBuy, dryRun, buyAmount: _rawBuyAmount, ...imageFields } = args;
|
|
38645
39551
|
let buyAmount;
|
|
38646
39552
|
if (devBuy !== undefined) {
|
|
@@ -38909,6 +39815,7 @@ var SOLANA_SEED_CEILINGS, MAIN_PAYER, SWEEP_BASE_ASSETS;
|
|
|
38909
39815
|
var init_orchestrate = __esm(() => {
|
|
38910
39816
|
init_convert();
|
|
38911
39817
|
init_image();
|
|
39818
|
+
init_launch_schema();
|
|
38912
39819
|
init_update_notice();
|
|
38913
39820
|
SOLANA_SEED_CEILINGS = {
|
|
38914
39821
|
sol: { raw: 500000000n, unit: "lamports", expRaw: 500000000n },
|
|
@@ -42705,8 +43612,7 @@ __export(exports_tools, {
|
|
|
42705
43612
|
STAKING_DISCLOSURE: () => STAKING_DISCLOSURE,
|
|
42706
43613
|
PROFILE_PNL_HYPERLIQUID: () => PROFILE_PNL_HYPERLIQUID,
|
|
42707
43614
|
PROFILE_EDIT_TOOL_NAMES: () => PROFILE_EDIT_TOOL_NAMES,
|
|
42708
|
-
OPERATION_NOT_FOUND_RULE: () => OPERATION_NOT_FOUND_RULE
|
|
42709
|
-
CUSTOM_CURVE_RULE: () => CUSTOM_CURVE_RULE
|
|
43615
|
+
OPERATION_NOT_FOUND_RULE: () => OPERATION_NOT_FOUND_RULE
|
|
42710
43616
|
});
|
|
42711
43617
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
42712
43618
|
function resolveToolAllowlist(env) {
|
|
@@ -43092,9 +43998,13 @@ function registerToolSubset(server, options) {
|
|
|
43092
43998
|
const imageDir = configuredImageDir(env);
|
|
43093
43999
|
register("candle_launch_token", {
|
|
43094
44000
|
title: "Launch a token on Candle",
|
|
43095
|
-
description: "Launch a new token via the Candle headless launch API, from the account's embedded wallet. Works on every plan, Free included. Set dryRun: true to validate without spending anything. " +
|
|
44001
|
+
description: "Launch a new token via the Candle headless launch API, from the account's embedded wallet. Works on every plan, Free included. Set dryRun: true to validate without spending anything. " + LAUNCH_SHARED_SENTENCES + (imageDir ? IMAGE_PATH_DISCLOSURE : ""),
|
|
43096
44002
|
inputSchema: imageDir ? localLaunchTokenShape : launchTokenShape
|
|
43097
44003
|
}, async (args) => {
|
|
44004
|
+
const socialsProblem = socialsRefusal("socials" in args ? args.socials : undefined);
|
|
44005
|
+
if (socialsProblem) {
|
|
44006
|
+
return { content: [{ type: "text", text: imageRefusalBody(socialsProblem) }], isError: true };
|
|
44007
|
+
}
|
|
43098
44008
|
const image = await resolveLaunchImage(args, imageDir, getConfig(), doFetch);
|
|
43099
44009
|
if ("refused" in image) {
|
|
43100
44010
|
return { content: [{ type: "text", text: imageRefusalBody(image.refused) }], isError: true };
|
|
@@ -43352,7 +44262,7 @@ MARKET_NOT_FOUND means Candle has no market for that token and this could not ru
|
|
|
43352
44262
|
});
|
|
43353
44263
|
register("candle_launch_and_seed", {
|
|
43354
44264
|
title: "Launch a token and seed it",
|
|
43355
|
-
description: "Launch a new token from the account's embedded wallet, with an optional dev-buy seed, then " + "return the fresh market state and token links in one result. On solana a seed makes this " + (options.headlessSeed === true ? "the seed is a separate first-buy transaction in the same Jito bundle as the launch, so " + "both land or neither does. That works on every plan, Free included, and needs no " + "swap:write. On hood the seed is " : "the atomic launch: the launch and the seed are two transactions Candle signs and sends as " + "one Jito bundle, so both land or neither does. That needs Pro or Max (TIER_REQUIRED " + "otherwise) and swap:write on the key, and the result carries bundleId. On hood the seed is ") + "a best-effort follow-up. A launch with no seed works on every plan, Free included. MOVES " + "REAL FUNDS unless dryRun; dry-run first, which spends nothing. Seeds above the platform " + "dev-buy ceiling are rejected (DEV_BUY_TOO_HIGH): at most 0.5 SOL / 500 USDC / 2,000,000 " + "CNDL, or on an exp- curve 10% of the threshold when that is lower; the server holds the " + "creator's own seed to the effective ceiling, so launch with the largest devBuy it allows " + "and top up with candle_trade. " +
|
|
44265
|
+
description: "Launch a new token from the account's embedded wallet, with an optional dev-buy seed, then " + "return the fresh market state and token links in one result. On solana a seed makes this " + (options.headlessSeed === true ? "the seed is a separate first-buy transaction in the same Jito bundle as the launch, so " + "both land or neither does. That works on every plan, Free included, and needs no " + "swap:write. On hood the seed is " : "the atomic launch: the launch and the seed are two transactions Candle signs and sends as " + "one Jito bundle, so both land or neither does. That needs Pro or Max (TIER_REQUIRED " + "otherwise) and swap:write on the key, and the result carries bundleId. On hood the seed is ") + "a best-effort follow-up. A launch with no seed works on every plan, Free included. MOVES " + "REAL FUNDS unless dryRun; dry-run first, which spends nothing. Seeds above the platform " + "dev-buy ceiling are rejected (DEV_BUY_TOO_HIGH): at most 0.5 SOL / 500 USDC / 2,000,000 " + "CNDL, or on an exp- curve 10% of the threshold when that is lower; the server holds the " + "creator's own seed to the effective ceiling, so launch with the largest devBuy it allows " + "and top up with candle_trade. " + LAUNCH_SHARED_SENTENCES + (imageDir ? IMAGE_PATH_DISCLOSURE : ""),
|
|
43356
44266
|
inputSchema: imageDir ? localLaunchAndSeedShape : launchAndSeedShape
|
|
43357
44267
|
}, async (args) => {
|
|
43358
44268
|
const result = await executeLaunchAndSeed(args, getConfig(), doFetch, imageDir, undefined, {
|
|
@@ -43512,12 +44422,13 @@ async function registerEligibleProfileTool(server, env = process.env, doFetch =
|
|
|
43512
44422
|
}
|
|
43513
44423
|
return eligible;
|
|
43514
44424
|
}
|
|
43515
|
-
var VAMP_CANDIDATES_DESCRIPTION,
|
|
44425
|
+
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, PROFILE_EDIT_TOOL_NAMES, isProfileEditTool = (name) => PROFILE_EDIT_TOOL_NAMES.includes(name), OPERATION_NOT_FOUND_RULE, getMarketShape, tokenForensicsShape, FEED_LIMIT_MESSAGE = "limit must be a whole number from 1 to 200", feedLimit, getFeedShape, reportActivityShape, getAgentProfileShape, listAgentsShape, createPostShape, deletePostShape, listAgentPostsShape, getOperationShape, vampCandidatesShape, getFeesShape, claimFeesShape, resolveTokenShape, profileWalletsShape, profilePnlShape, profileTradesShape, setProfileWalletsShape, swapShape, tradeShape, tokenImageUrl, tokenBannerUrl, localImageShape, localLaunchTokenShape, localLaunchAndSeedShape, IMAGE_PATH_DISCLOSURE, transferShape, sweepShape, PROFILE_PNL_HYPERLIQUID;
|
|
43516
44426
|
var init_tools = __esm(() => {
|
|
43517
44427
|
init_zod();
|
|
43518
44428
|
init_client2();
|
|
43519
44429
|
init_convert();
|
|
43520
44430
|
init_image();
|
|
44431
|
+
init_launch_schema();
|
|
43521
44432
|
init_market_disclosure();
|
|
43522
44433
|
init_orchestrate();
|
|
43523
44434
|
init_perps();
|
|
@@ -43526,7 +44437,6 @@ var init_tools = __esm(() => {
|
|
|
43526
44437
|
init_plans();
|
|
43527
44438
|
init_update_notice();
|
|
43528
44439
|
VAMP_CANDIDATES_DESCRIPTION = "Lists Solana tokens that are close to graduating or graduated in the last 6 hours whose ticker other tokens " + "are copying, most-copied in the last 24 hours first. Each entry carries a launch draft (name, ticker, image URL) " + "for `candle_launch_token` or `candle_launch_and_seed`; draft notes identify fields you must supply or adjust. " + "Upload your own image through the agent image upload when the image is unavailable. Use `dryRun: true` first. " + "Counts exclude the candidate itself and cover both Solana and Hood launches at the venues returned in " + "`coverage`, not Candle's own. `coverage.since` is the returned Solana coverage date; Hood history may differ.";
|
|
43529
|
-
CUSTOM_CURVE_RULE = 'mode "exp-custom" with curve { virtualQuote, threshold } launches a Solana curve with your own V (the virtual ' + "quote it starts with) and G (the quote it raises before graduating), in whole SOL or CNDL as decimal strings, " + "accepted when V is 1 to 100 SOL and G 1 to 250 SOL (66,000 to 6,600,000 and 66,000 to 16,500,000 CNDL), when " + "every price up to 20 x G stays under the ceiling (V + 20G)^2 < V x X at this stakerAllocationBps, when neither " + "is finer than the quote's decimals, and when the dev buy is at most 0.5 SOL (2,000,000 CNDL), or 10% of G when " + "that is lower (every exp- mode; an operator may set the 0.5 SOL lower); a curve past a limit is " + "CURVE_OUT_OF_RANGE, and at the ceiling it carries maxThresholdRaw, the largest G that fits. " + "Dry-run it first (dryRun: true): the dry run spends nothing and returns curveTerms, the market cap at start and " + "at graduation, the supply sold by graduation, the share your own buyAmount takes, the fees and the migration " + "terms, and devBuyMax, the dev-buy ceiling that applies. Only where the API has EXP_CUSTOM_CURVES on; " + "elsewhere exp-custom is refused as an unknown mode.";
|
|
43530
44440
|
localProfileTools = new WeakMap;
|
|
43531
44441
|
stakingWriteShape = {
|
|
43532
44442
|
payer: exports_external.enum(["main", "linked"]).optional().describe("main is the embedded wallet; linked names a linked or TEE wallet"),
|
|
@@ -43597,38 +44507,6 @@ var init_tools = __esm(() => {
|
|
|
43597
44507
|
"candle_delete_post"
|
|
43598
44508
|
];
|
|
43599
44509
|
OPERATION_NOT_FOUND_RULE = "No record exists under this id in the ledger `kind` asked. That does not prove the write did " + "not run: a write that is still starting has no record yet. If a write was sent with this id, " + "send the SAME request again with the SAME id. It returns the stored outcome, or reports the one " + "still running. It never executes a second time. A refusal of that resend (a conflict or a " + "limit) is an answer about the resend, not about the write: read candle_get_operation again " + "before reporting an outcome. Never send it under a new id: a new id is a second trade or " + "launch. Check `kind`: a swap id is not read here, so replay the swap with its `clientSwapId`.";
|
|
43600
|
-
launchTokenShape = {
|
|
43601
|
-
clientLaunchId: exports_external.string().describe("Caller-chosen idempotency key, unique per account"),
|
|
43602
|
-
name: exports_external.string().describe("Token name"),
|
|
43603
|
-
symbol: exports_external.string().describe("Token symbol"),
|
|
43604
|
-
imageUrl: exports_external.string().describe("https URL to the token image. Must be roughly SQUARE (aspect ratio at most 1.5:1): it " + "renders as a small circle/square avatar everywhere. Share cards, OG images and banners " + "are rejected with IMAGE_WRONG_SHAPE -- pass those as bannerUrl instead"),
|
|
43605
|
-
bannerUrl: exports_external.string().optional().describe("Optional https URL to WIDE artwork for the token page's banner strip (wider than 1.5:1, " + "e.g. 1200x630). This is where a share card or OG image belongs. A square image here is " + "rejected with BANNER_WRONG_SHAPE: send the wide original, not a cropped copy. Omit it and the strip " + "falls back to imageUrl"),
|
|
43606
|
-
chain: exports_external.string().optional().describe('"solana" or "hood"; defaults to solana'),
|
|
43607
|
-
quoteAsset: exports_external.string().optional().describe("Quote asset symbol; defaults per chain"),
|
|
43608
|
-
mode: exports_external.string().optional().describe('"open" or "exclusive"; defaults to open. "test-open" / "test-exclusive" launch the ' + "low-threshold test curves (~1/80 economics) where the API has ENABLE_TEST_CURVES on. " + '"exp-a", "exp-b" and "exp-c" are the experimental fast Solana curves (sol or cndl). ' + '"exp-custom" launches a Solana curve with your own V and G, given in curve, where the API has it ' + "switched on"),
|
|
43609
|
-
curve: exports_external.object({
|
|
43610
|
-
virtualQuote: exports_external.string().describe('V: the virtual quote the curve starts with, in whole quote units, e.g. "12" SOL'),
|
|
43611
|
-
threshold: exports_external.string().describe('G: the quote the curve raises before it graduates, in whole quote units, e.g. "40" SOL')
|
|
43612
|
-
}).strict().optional().describe('mode "exp-custom" only, and required there. Decimal strings in whole units of the quote asset (sol or ' + "cndl), never raw units. Dry-run first: the API checks the limits and returns the curve's terms"),
|
|
43613
|
-
creatorFeeBps: exports_external.union([exports_external.literal(0), exports_external.literal(100), exports_external.literal(200)]).optional().describe("An exp- mode only: an extra creator fee of 100 or 200 basis points on top of the curve's 1% and the " + "pool's 0.5%, paid to the creator in full. 0 is the same as leaving it out"),
|
|
43614
|
-
stakerAllocationBps: exports_external.number().optional().describe("Staker allocation in basis points"),
|
|
43615
|
-
poolFee: exports_external.union([
|
|
43616
|
-
exports_external.object({ tier: exports_external.union([exports_external.literal(25), exports_external.literal(50), exports_external.literal(100)]) }).strict(),
|
|
43617
|
-
exports_external.object({
|
|
43618
|
-
startBps: exports_external.number().int().max(2500),
|
|
43619
|
-
endBps: exports_external.union([exports_external.literal(25), exports_external.literal(50), exports_external.literal(100)]),
|
|
43620
|
-
minutes: exports_external.number().positive().max(30)
|
|
43621
|
-
}).strict()
|
|
43622
|
-
]).optional().describe("Solana only: the graduated pool's fee. { tier: 25 | 50 | 100 } is a flat fee in bps (default 50), or " + "{ startBps, endBps, minutes } is an anti-snipe fall from startBps (at most 2500, at least endBps) to " + "endBps (25, 50 or 100) over minutes (at most 30). Cannot be combined with creatorFeeBps unless the tier is 50. " + "Omit for the standard 50 bps pool"),
|
|
43623
|
-
poolOpen: exports_external.enum(["legacy", "curve_price"]).optional().describe(`Solana preset tiers only. "curve_price" opens the pool at the curve's last price (the whole reserve is ` + "seeded and the remaining base burned); stakerAllocationBps plus the team allocation must fit the tier's limit, " + "which the dry run returns as graduation.maxStakerCarveBps. Omitted defaults to curve_price when those " + "allocations fit, otherwise falls back to legacy without refusal. Set legacy explicitly to opt out. " + "Explicit curve_price is refused when the allocations exceed the limit"),
|
|
43624
|
-
expPoolShape: exports_external.enum(["floor", "full_range"]).optional().describe('Solana exp- curves only. "full_range" opens the pool across the whole price range. Omit for floor'),
|
|
43625
|
-
dexVersion: exports_external.string().nullable().optional().describe('"v3" or "v4"; required for hood launches'),
|
|
43626
|
-
buyAmount: exports_external.union([exports_external.string(), exports_external.number()]).optional().describe("Initial buy, in quote base units (string or number)"),
|
|
43627
|
-
description: exports_external.string().optional(),
|
|
43628
|
-
socials: exports_external.record(exports_external.string()).optional().describe("Social links keyed by platform"),
|
|
43629
|
-
visibility: exports_external.string().optional().describe('"production", "test", "local", or "hidden"'),
|
|
43630
|
-
dryRun: exports_external.boolean().optional().describe("Validate without executing the launch")
|
|
43631
|
-
};
|
|
43632
44510
|
getMarketShape = {
|
|
43633
44511
|
chain: exports_external.string().describe('"solana" or "hood"'),
|
|
43634
44512
|
mint: exports_external.string().describe("Token mint (solana) or contract address (hood)")
|
|
@@ -43734,20 +44612,16 @@ var init_tools = __esm(() => {
|
|
|
43734
44612
|
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."),
|
|
43735
44613
|
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."))
|
|
43736
44614
|
};
|
|
43737
|
-
|
|
43738
|
-
|
|
43739
|
-
clientLaunchId: exports_external.string().optional().describe("Idempotency key. Auto-generated when omitted and echoed in the result."),
|
|
43740
|
-
devBuy: exports_external.string().optional().describe('Seed buy in DECIMAL units of the quote asset this launch selects (e.g. "0.25" SOL, or ' + "ETH on hood), paid from the account's embedded wallet; the tool's description says how " + "each chain sends it. Follows quoteAsset, which defaults to sol on solana and eth on hood. " + "Capped by the platform dev-buy ceiling; for a larger seed, launch then follow with candle_trade.")
|
|
43741
|
-
};
|
|
43742
|
-
launchAndSeedShape = { ...seedableLaunchShape, ...seedShape };
|
|
44615
|
+
tokenImageUrl = launchSchemaField(launchTokenShape, "imageUrl");
|
|
44616
|
+
tokenBannerUrl = launchSchemaField(launchTokenShape, "bannerUrl");
|
|
43743
44617
|
localImageShape = {
|
|
43744
|
-
imageUrl:
|
|
44618
|
+
imageUrl: tokenImageUrl.optional().describe(`${tokenImageUrl.description}. Pass this or imagePath, never both`),
|
|
43745
44619
|
imagePath: exports_external.string().optional().describe("An absolute image path inside CANDLE_IMAGE_DIR. The file is uploaded and becomes public and permanent, " + "including on a dry run. PNG, JPEG, GIF or WebP, at most 10MB, read from the file's first bytes. " + "Uploading spends no funds and does not count against the launch cap; only the upload limit applies. " + "Pass this or imageUrl, never both. The result carries the imageUrl used: pass that on a retry, or " + "after a dry run, instead of uploading the file again. This is the square token image: Candle crops " + "it to a square, so a wide banner belongs in bannerPath, not here"),
|
|
43746
|
-
bannerUrl:
|
|
44620
|
+
bannerUrl: tokenBannerUrl.describe(`${tokenBannerUrl.description}. Pass this or bannerPath, never both`),
|
|
43747
44621
|
bannerPath: exports_external.string().optional().describe("An absolute path, inside CANDLE_IMAGE_DIR, to WIDE banner artwork for the token page strip (wider than " + "1.5:1, e.g. 2171x724 or 1200x630). The file is uploaded uncropped and becomes public and permanent, " + "including on a dry run; PNG, JPEG, GIF or WebP, at most 10MB. A square image is refused with " + "BANNER_WRONG_SHAPE: send the wide original, not a cropped copy. Pass this or bannerUrl, never both. " + "The result carries the bannerUrl used: pass that on a retry instead of uploading the file again")
|
|
43748
44622
|
};
|
|
43749
44623
|
localLaunchTokenShape = { ...launchTokenShape, ...localImageShape };
|
|
43750
|
-
localLaunchAndSeedShape = { ...
|
|
44624
|
+
localLaunchAndSeedShape = { ...launchAndSeedShape, ...localImageShape };
|
|
43751
44625
|
IMAGE_PATH_DISCLOSURE = " With imagePath or bannerPath, the file is uploaded first and becomes public and permanent, on a dry run too; " + "nothing about a dry run keeps that file private. imagePath is the square token image (Candle crops it); " + "a wide banner goes in bannerPath, which is not cropped.";
|
|
43752
44626
|
transferShape = {
|
|
43753
44627
|
chain: exports_external.enum(["solana", "hood"]).describe("Which chain the transfer executes on"),
|
|
@@ -72441,7 +73315,7 @@ var LAUNCH_USAGE = `candle launch ${launchFlagFields.map((field) => {
|
|
|
72441
73315
|
if (field.key === "imageUrl")
|
|
72442
73316
|
return `--image <path>|${invocation}`;
|
|
72443
73317
|
return "required" in field && field.required ? invocation : `[${invocation}]`;
|
|
72444
|
-
}).join(" ")} [--yes] [--rpc-url <url>] [--json]`;
|
|
73318
|
+
}).join(" ")} [--check] [--yes] [--rpc-url <url>] [--json]`;
|
|
72445
73319
|
function launchFieldProblem(flags) {
|
|
72446
73320
|
for (const field of launchFlagFields) {
|
|
72447
73321
|
const value = flags[field.cli.flag];
|
|
@@ -72966,7 +73840,7 @@ var HELP = {
|
|
|
72966
73840
|
launch: {
|
|
72967
73841
|
group: "Trade",
|
|
72968
73842
|
summary: "Create a token on Solana or Hood, with an optional first buy",
|
|
72969
|
-
description: "Creates a token from the wallet you name, with an optional first buy. Embedded launches use /launch/headless on every plan, with the key's embedded-wallet allowance and launch:write; no swap:write is needed. A Solana first buy is a creator bundle, signed by Candle. A Hood first buy is best effort after creation. TEE launches use /launch/self and require Pro or Max, allowLaunch, a bound key and verified authority. TEE buys need TEE_LAUNCH_BUYS_ENABLED on the server; Solana buys also need AGENT_TRADING_ENABLED and Jito. Existing server gates and ceilings apply. The embedded path previews the launch before confirmation; --dry-run stops at the preview. No buy creates only the token. Local image and banner uploads happen before confirmation and are public and permanent even if the launch is declined. Reuse --client-trade-id for recovery.",
|
|
73843
|
+
description: "Creates a token from the wallet you name, with an optional first buy. Embedded launches use /launch/headless on every plan, with the key's embedded-wallet allowance and launch:write; no swap:write is needed. A Solana first buy is a creator bundle, signed by Candle. A Hood first buy is best effort after creation. TEE launches use /launch/self and require Pro or Max, allowLaunch, a bound key and verified authority. TEE buys need TEE_LAUNCH_BUYS_ENABLED on the server; Solana buys also need AGENT_TRADING_ENABLED and Jito. Existing server gates and ceilings apply. The embedded path previews the launch before confirmation; --dry-run stops at the preview. No buy creates only the token. Local image and banner uploads happen before confirmation and are public and permanent even if the launch is declined. Reuse --client-trade-id for recovery. --check answers first, before any of it: which launch path works on this key and server (embedded allowance, tier, allowLaunch, TEE_LAUNCH_BUYS_ENABLED) in one call, with nothing uploaded, built or sent; name a wallet with --wallet to narrow it, and it exits 1 when no path works.",
|
|
72970
73844
|
usage: [LAUNCH_USAGE],
|
|
72971
73845
|
rows: [],
|
|
72972
73846
|
flags: [
|
|
@@ -72975,6 +73849,10 @@ var HELP = {
|
|
|
72975
73849
|
invocation: "--image <path>",
|
|
72976
73850
|
description: "Upload a local PNG, JPEG, GIF or WebP (at most 10MB) instead of --image-url; public and permanent before confirmation."
|
|
72977
73851
|
},
|
|
73852
|
+
{
|
|
73853
|
+
invocation: "--check",
|
|
73854
|
+
description: "Answer which launch path works (embedded allowance, tier, allowLaunch, TEE_LAUNCH_BUYS_ENABLED) in one call, before any image upload; nothing is sent. Exits 1 when no path works."
|
|
73855
|
+
},
|
|
72978
73856
|
{ invocation: "--yes", description: "Confirm the launch without an interactive prompt." },
|
|
72979
73857
|
{
|
|
72980
73858
|
invocation: "--rpc-url <url>",
|
|
@@ -72982,6 +73860,8 @@ var HELP = {
|
|
|
72982
73860
|
}
|
|
72983
73861
|
],
|
|
72984
73862
|
examples: [
|
|
73863
|
+
"candle launch --check",
|
|
73864
|
+
"candle launch --check --wallet AgentOne",
|
|
72985
73865
|
"candle launch --name Demo --symbol DEMO --image ./logo.png --wallet AgentOne",
|
|
72986
73866
|
"candle launch --name Demo --symbol DEMO --image-url https://example.com/d.png --wallet AgentOne",
|
|
72987
73867
|
"candle launch --name Demo --symbol DEMO --image-url https://example.com/d.png --wallet HoodAgent --dex-version v4",
|
|
@@ -73765,6 +74645,8 @@ function renderTopLevel(style) {
|
|
|
73765
74645
|
const out = [
|
|
73766
74646
|
"candle: Candle from the terminal: trade, launch, and hold your keys on your own machine",
|
|
73767
74647
|
"",
|
|
74648
|
+
...LAUNCH_DEFINITION.surfacesIntro,
|
|
74649
|
+
"",
|
|
73768
74650
|
"Usage: candle <command> [<subcommand>] [flags]",
|
|
73769
74651
|
" candle help <command> a command's subcommands, flags and examples"
|
|
73770
74652
|
];
|
|
@@ -73815,6 +74697,8 @@ function renderPresentedTopLevel(style) {
|
|
|
73815
74697
|
const out = [
|
|
73816
74698
|
...brandHeader(style),
|
|
73817
74699
|
...!style.styled ? [...wrapText("Candle CLI: Your wallets and agent tools, one command away.", width), ""] : [],
|
|
74700
|
+
...LAUNCH_DEFINITION.surfacesIntro.flatMap((line) => wrapText(line, width)),
|
|
74701
|
+
"",
|
|
73818
74702
|
heading2("Start here"),
|
|
73819
74703
|
...renderRows([
|
|
73820
74704
|
{ invocation: "candle setup", description: "Connect your account" },
|
|
@@ -85576,24 +86460,25 @@ async function externalSweep(args, ctx) {
|
|
|
85576
86460
|
|
|
85577
86461
|
// src/commands/fees.ts
|
|
85578
86462
|
init_args();
|
|
86463
|
+
init_presentation();
|
|
85579
86464
|
init_render();
|
|
85580
86465
|
init_trading();
|
|
85581
86466
|
init_swap();
|
|
85582
86467
|
var LIST_USAGE = "Usage: candle fees [--cursor <cursor>] [--limit <1-50>] [--json]";
|
|
85583
86468
|
var CLAIM_USAGE = "Usage: candle fees claim <mint> [--dry-run] [--json]";
|
|
85584
|
-
function amountText(row) {
|
|
86469
|
+
function amountText(row, missing) {
|
|
85585
86470
|
const raw = typeof row.amountRaw === "string" && /^\d+$/.test(row.amountRaw) ? row.amountRaw : null;
|
|
85586
86471
|
if (raw === null)
|
|
85587
|
-
return
|
|
86472
|
+
return missing;
|
|
85588
86473
|
const decimals = Number.isInteger(row.decimals) ? row.decimals : 0;
|
|
85589
86474
|
const label = row.symbol ? row.symbol : row.mint ?? "";
|
|
85590
86475
|
const value = decimalAmount(raw, decimals);
|
|
85591
86476
|
return `${value.includes(".") ? value.replace(/\.?0+$/, "") : value} ${safeText(label)}`.trim();
|
|
85592
86477
|
}
|
|
85593
|
-
function amountsText(rows) {
|
|
86478
|
+
function amountsText(rows, missing) {
|
|
85594
86479
|
if (!Array.isArray(rows) || rows.length === 0)
|
|
85595
86480
|
return "none";
|
|
85596
|
-
return rows.map((row) => amountText(row)).join(", ");
|
|
86481
|
+
return rows.map((row) => amountText(row, missing)).join(", ");
|
|
85597
86482
|
}
|
|
85598
86483
|
var REASON_WORDS = {
|
|
85599
86484
|
below_dust: "under the dust floor",
|
|
@@ -85603,8 +86488,8 @@ var REASON_WORDS = {
|
|
|
85603
86488
|
not_recipient: "the fees go to someone else, or the token has no fee vault",
|
|
85604
86489
|
insufficient_gas: "the recipient wallet cannot cover the network fee"
|
|
85605
86490
|
};
|
|
85606
|
-
function reasonText(reason) {
|
|
85607
|
-
return typeof reason === "string" ? REASON_WORDS[reason] ?? safeText(reason) :
|
|
86491
|
+
function reasonText(reason, missing) {
|
|
86492
|
+
return typeof reason === "string" ? REASON_WORDS[reason] ?? safeText(reason) : missing;
|
|
85608
86493
|
}
|
|
85609
86494
|
async function feesList(args, ctx) {
|
|
85610
86495
|
const parsed = parseArgs(args, { valueFlags: ["--cursor", "--limit"] });
|
|
@@ -85629,19 +86514,30 @@ async function feesList(args, ctx) {
|
|
|
85629
86514
|
if (ctx.json)
|
|
85630
86515
|
return printTradingResult(ctx, body);
|
|
85631
86516
|
const page = Array.isArray(body.page) ? body.page : [];
|
|
85632
|
-
const
|
|
86517
|
+
const style = terminalStyle(ctx.deps, "stdout");
|
|
86518
|
+
const missing = symbols(style).missing;
|
|
85633
86519
|
if (page.length === 0)
|
|
85634
|
-
|
|
85635
|
-
|
|
85636
|
-
|
|
85637
|
-
|
|
85638
|
-
|
|
85639
|
-
|
|
86520
|
+
notice(ctx.deps, "stdout", "note", "No launched tokens on this account.");
|
|
86521
|
+
else {
|
|
86522
|
+
const rows = page.map((row) => {
|
|
86523
|
+
const status = row.unreadable ? "could not be read" : row.claimable ? `claimable: ${amountsText(row.claimableAmounts, missing)}` : `not claimable: ${reasonText(row.reason, missing)}`;
|
|
86524
|
+
return [
|
|
86525
|
+
safeText(String(row.symbol ?? row.name ?? row.mint)),
|
|
86526
|
+
safeText(String(row.chain)),
|
|
86527
|
+
status,
|
|
86528
|
+
safeText(String(row.mint))
|
|
86529
|
+
];
|
|
86530
|
+
});
|
|
86531
|
+
ctx.deps.stdout.write(`${renderTable(["TOKEN", "CHAIN", "STATUS", "MINT"], rows, {
|
|
86532
|
+
style,
|
|
86533
|
+
width: style.width,
|
|
86534
|
+
indent: 2,
|
|
86535
|
+
columns: [{}, { priority: 1 }, {}, { id: true }]
|
|
86536
|
+
})}
|
|
85640
86537
|
`);
|
|
85641
86538
|
}
|
|
85642
86539
|
if (body.isDone === false && typeof body.continueCursor === "string")
|
|
85643
|
-
|
|
85644
|
-
`);
|
|
86540
|
+
notice(ctx.deps, "stdout", "note", `More: candle fees --cursor ${safeText(body.continueCursor)}`);
|
|
85645
86541
|
return 0;
|
|
85646
86542
|
} catch (error) {
|
|
85647
86543
|
return tradingFailure(ctx, error);
|
|
@@ -85664,26 +86560,40 @@ async function feesClaim(args, ctx) {
|
|
|
85664
86560
|
const body = await request(ctx, key, `/api/v1/agent/fees/${encodeURIComponent(mint)}/claim`, { dryRun });
|
|
85665
86561
|
if (ctx.json)
|
|
85666
86562
|
return printTradingResult(ctx, body);
|
|
85667
|
-
const
|
|
86563
|
+
const style = terminalStyle(ctx.deps, "stdout");
|
|
86564
|
+
const missing = symbols(style).missing;
|
|
85668
86565
|
const claimed = Array.isArray(body.claimed) ? body.claimed : [];
|
|
85669
|
-
if (dryRun) {
|
|
85670
|
-
|
|
85671
|
-
`
|
|
86566
|
+
if (dryRun && body.claimable === false) {
|
|
86567
|
+
ctx.deps.stdout.write(`${failure(`Would be refused: ${reasonText(body.reason, missing)}.`, style)}
|
|
86568
|
+
`);
|
|
86569
|
+
ctx.deps.stdout.write(`${fields([["Still", amountsText(body.remaining, missing)]], style).join(`
|
|
86570
|
+
`)}
|
|
85672
86571
|
`);
|
|
85673
86572
|
} else {
|
|
85674
|
-
|
|
86573
|
+
const rows = [
|
|
86574
|
+
[dryRun ? "Would claim" : "Amount", amountsText(claimed, missing)],
|
|
86575
|
+
["To", safeText(String(body.recipient))],
|
|
86576
|
+
["Still", amountsText(body.remaining, missing)]
|
|
86577
|
+
];
|
|
86578
|
+
ctx.deps.stdout.write(`${heading(dryRun ? "Dry run" : "Claimed", style)}
|
|
86579
|
+
|
|
86580
|
+
${fields(rows, style).join(`
|
|
86581
|
+
`)}
|
|
85675
86582
|
`);
|
|
85676
|
-
|
|
85677
|
-
|
|
85678
|
-
|
|
86583
|
+
if (!dryRun) {
|
|
86584
|
+
for (const row of claimed) {
|
|
86585
|
+
if (row.txHash)
|
|
86586
|
+
ctx.deps.stdout.write(`${bold(safeText(row.txHash), style)}
|
|
85679
86587
|
`);
|
|
86588
|
+
}
|
|
86589
|
+
}
|
|
85680
86590
|
}
|
|
85681
|
-
|
|
85682
|
-
|
|
85683
|
-
if (Array.isArray(body.warnings))
|
|
86591
|
+
if (dryRun)
|
|
86592
|
+
notice(ctx.deps, "stdout", "note", "Nothing was sent.");
|
|
86593
|
+
if (Array.isArray(body.warnings)) {
|
|
85684
86594
|
for (const warning of body.warnings)
|
|
85685
|
-
ctx.deps
|
|
85686
|
-
|
|
86595
|
+
notice(ctx.deps, "stderr", "warning", safeText(String(warning)));
|
|
86596
|
+
}
|
|
85687
86597
|
return 0;
|
|
85688
86598
|
} catch (error) {
|
|
85689
86599
|
return tradingFailure(ctx, error);
|
|
@@ -86342,6 +87252,163 @@ function readLaunchBundle(args) {
|
|
|
86342
87252
|
return { lamports: launch.lamports + buy.lamports, quoteRaw, feeRaw };
|
|
86343
87253
|
}
|
|
86344
87254
|
|
|
87255
|
+
// src/launch-check.ts
|
|
87256
|
+
init_zod();
|
|
87257
|
+
init_trading();
|
|
87258
|
+
var checkSchema = exports_external.object({
|
|
87259
|
+
key: exports_external.object({
|
|
87260
|
+
keyPrefix: exports_external.string(),
|
|
87261
|
+
scopes: exports_external.array(exports_external.string()),
|
|
87262
|
+
tier: exports_external.string(),
|
|
87263
|
+
selfLaunch: exports_external.boolean(),
|
|
87264
|
+
embeddedWalletPermission: exports_external.enum(["allowed", "denied"])
|
|
87265
|
+
}),
|
|
87266
|
+
switches: exports_external.object({
|
|
87267
|
+
headlessLaunch: exports_external.boolean(),
|
|
87268
|
+
agentTrading: exports_external.boolean(),
|
|
87269
|
+
teeLaunchBuys: exports_external.boolean()
|
|
87270
|
+
}),
|
|
87271
|
+
embedded: exports_external.object({
|
|
87272
|
+
solana: exports_external.object({ address: exports_external.string(), delegated: exports_external.boolean().optional() }).nullable().optional(),
|
|
87273
|
+
evm: exports_external.object({ address: exports_external.string(), delegated: exports_external.boolean().optional() }).nullable().optional()
|
|
87274
|
+
}).optional(),
|
|
87275
|
+
wallets: exports_external.array(exports_external.object({
|
|
87276
|
+
id: exports_external.string(),
|
|
87277
|
+
address: exports_external.string(),
|
|
87278
|
+
chain: exports_external.string(),
|
|
87279
|
+
label: exports_external.string().nullable(),
|
|
87280
|
+
allowLaunch: exports_external.boolean(),
|
|
87281
|
+
active: exports_external.boolean()
|
|
87282
|
+
}))
|
|
87283
|
+
}).passthrough();
|
|
87284
|
+
var EMBEDDED_NAMES = new Set(LAUNCH_DEFINITION.wallet.embeddedNames);
|
|
87285
|
+
function walletName(row) {
|
|
87286
|
+
return row.label ?? row.id;
|
|
87287
|
+
}
|
|
87288
|
+
function walletLines(answer2, row, requestedChain) {
|
|
87289
|
+
const name2 = walletName(row);
|
|
87290
|
+
const chain2 = row.chain === "evm" ? "Hood" : "Solana";
|
|
87291
|
+
const lines = [];
|
|
87292
|
+
if (requestedChain && rowChain(row) !== requestedChain)
|
|
87293
|
+
return [
|
|
87294
|
+
`${name2} (${chain2}) cannot launch on ${chainName(requestedChain)}: choose a ${chainName(requestedChain)} wallet.`
|
|
87295
|
+
];
|
|
87296
|
+
const gated = gatedOff(answer2);
|
|
87297
|
+
if (gated) {
|
|
87298
|
+
lines.push(`${name2} (${chain2}): ${gated}`);
|
|
87299
|
+
return lines;
|
|
87300
|
+
}
|
|
87301
|
+
if (!answer2.key.selfLaunch)
|
|
87302
|
+
lines.push(`${name2} (${chain2}) cannot launch: TEE launches need Pro or Max, and this key is ${answer2.key.tier}.`);
|
|
87303
|
+
else if (!row.allowLaunch)
|
|
87304
|
+
lines.push(`${name2} (${chain2}) cannot launch: the account owner has allowLaunch off for it. Run candle wallets allow-launch ${row.id} on a profile signed in with candle auth login.`);
|
|
87305
|
+
else if (!row.active)
|
|
87306
|
+
lines.push(`${name2} (${chain2}) cannot launch: it is not a verified-active TEE wallet.`);
|
|
87307
|
+
else {
|
|
87308
|
+
lines.push(`This key can launch from ${name2} with no first buy.`);
|
|
87309
|
+
if (!answer2.switches.teeLaunchBuys)
|
|
87310
|
+
lines.push(`A first buy from ${name2} is off on this server; launch with no first buy, then buy with candle swap.`);
|
|
87311
|
+
else if (chain2 === "Solana" && !answer2.switches.agentTrading)
|
|
87312
|
+
lines.push(`A first buy from ${name2} is off on this server (AGENT_TRADING_ENABLED off for Solana first buys).`);
|
|
87313
|
+
else
|
|
87314
|
+
lines.push(`A first buy from ${name2} rides the launch (TEE_LAUNCH_BUYS_ENABLED is on).`);
|
|
87315
|
+
}
|
|
87316
|
+
return lines;
|
|
87317
|
+
}
|
|
87318
|
+
function gatedOff(answer2) {
|
|
87319
|
+
if (!answer2.key.scopes.includes("launch:write"))
|
|
87320
|
+
return "this key lacks launch:write, so no launch path works under it.";
|
|
87321
|
+
if (!answer2.switches.headlessLaunch)
|
|
87322
|
+
return "launches are off on this server (HEADLESS_LAUNCH_ENABLED off).";
|
|
87323
|
+
return;
|
|
87324
|
+
}
|
|
87325
|
+
function embeddedLines(answer2, requestedChain) {
|
|
87326
|
+
const lines = [];
|
|
87327
|
+
const gated = gatedOff(answer2);
|
|
87328
|
+
if (answer2.key.embeddedWalletPermission === "denied") {
|
|
87329
|
+
lines.push("The embedded wallet is denied on this key.");
|
|
87330
|
+
return lines;
|
|
87331
|
+
}
|
|
87332
|
+
const paths = embeddedPaths(answer2, requestedChain);
|
|
87333
|
+
if (!paths.some((path) => path.wallet)) {
|
|
87334
|
+
lines.push(`This account has no ${requestedChain ? `${chainName(requestedChain)} ` : ""}embedded wallet. Create one in the app, or launch from a TEE wallet with --wallet <tee>.`);
|
|
87335
|
+
return lines;
|
|
87336
|
+
}
|
|
87337
|
+
if (gated) {
|
|
87338
|
+
lines.push(`Embedded wallet: ${gated}`);
|
|
87339
|
+
return lines;
|
|
87340
|
+
}
|
|
87341
|
+
for (const { chain: chain2, wallet: wallet3 } of paths) {
|
|
87342
|
+
if (!wallet3)
|
|
87343
|
+
continue;
|
|
87344
|
+
if (wallet3.delegated === false) {
|
|
87345
|
+
lines.push(`The account's ${chainName(chain2)} embedded wallet is not delegated for agent access.`);
|
|
87346
|
+
continue;
|
|
87347
|
+
}
|
|
87348
|
+
lines.push(`This key can launch from the ${chainName(chain2)} embedded wallet with no first buy.`);
|
|
87349
|
+
if (chain2 === "solana" && !answer2.switches.agentTrading)
|
|
87350
|
+
lines.push("A Solana first buy from the embedded wallet is off on this server (AGENT_TRADING_ENABLED off).");
|
|
87351
|
+
else
|
|
87352
|
+
lines.push(chain2 === "solana" ? "A Solana first buy can ride the launch as one bundle signed by Candle." : "A Hood first buy can ride the launch best effort after creation.");
|
|
87353
|
+
}
|
|
87354
|
+
return lines;
|
|
87355
|
+
}
|
|
87356
|
+
function walletWorks(answer2, row, requestedChain) {
|
|
87357
|
+
return (requestedChain === undefined || rowChain(row) === requestedChain) && gatedOff(answer2) === undefined && answer2.key.selfLaunch && row.allowLaunch && row.active;
|
|
87358
|
+
}
|
|
87359
|
+
function embeddedPaths(answer2, requestedChain) {
|
|
87360
|
+
return (requestedChain ? [requestedChain] : ["solana", "hood"]).map((chain2) => ({
|
|
87361
|
+
chain: chain2,
|
|
87362
|
+
wallet: chain2 === "solana" ? answer2.embedded?.solana : answer2.embedded?.evm
|
|
87363
|
+
}));
|
|
87364
|
+
}
|
|
87365
|
+
function embeddedWorks(answer2, requestedChain) {
|
|
87366
|
+
return gatedOff(answer2) === undefined && answer2.key.embeddedWalletPermission === "allowed" && embeddedPaths(answer2, requestedChain).some(({ wallet: wallet3 }) => wallet3 != null && wallet3.delegated !== false);
|
|
87367
|
+
}
|
|
87368
|
+
function describeRow(row) {
|
|
87369
|
+
return `${row.label ? `${row.label} ` : ""}(${row.id}, ${row.address})`;
|
|
87370
|
+
}
|
|
87371
|
+
async function launchCheck(ctx, key, walletFlag, requestedChain) {
|
|
87372
|
+
const answer2 = checkSchema.parse(await request(ctx, key, "/api/v1/launch/preflight"));
|
|
87373
|
+
const embeddedOnly = walletFlag !== undefined && EMBEDDED_NAMES.has(walletFlag);
|
|
87374
|
+
const named = walletFlag === undefined || embeddedOnly ? [] : answer2.wallets.filter((row) => row.id === walletFlag || row.address === walletFlag || row.label === walletFlag);
|
|
87375
|
+
if (walletFlag !== undefined && !embeddedOnly && named.length !== 1)
|
|
87376
|
+
throw new TradingError("TEE_WALLET_REQUIRED", named.length > 1 ? `"${walletFlag}" matches ${named.length} TEE wallets on this key: ${named.map(describeRow).join("; ")}. Name one by id or address.` : answer2.wallets.length === 0 ? "This key has no TEE wallets bound to it. Enrol one, or select the profile whose key holds it." : `No TEE wallet on this key is called "${walletFlag}". Bound to this key: ${answer2.wallets.map(describeRow).join("; ")}.`);
|
|
87377
|
+
const shownWallets = walletFlag === undefined ? answer2.wallets : named;
|
|
87378
|
+
const lines = [`Launch paths for key ${answer2.key.keyPrefix} (tier ${answer2.key.tier}):`];
|
|
87379
|
+
if (requestedChain)
|
|
87380
|
+
lines.push(`Requested launch chain: ${chainName(requestedChain)}.`);
|
|
87381
|
+
if (walletFlag === undefined || embeddedOnly)
|
|
87382
|
+
lines.push(...embeddedLines(answer2, requestedChain));
|
|
87383
|
+
for (const row of shownWallets)
|
|
87384
|
+
lines.push(...walletLines(answer2, row, requestedChain));
|
|
87385
|
+
const works = (walletFlag === undefined || embeddedOnly ? embeddedWorks(answer2, requestedChain) : false) || shownWallets.some((row) => walletWorks(answer2, row, requestedChain));
|
|
87386
|
+
lines.push(works ? "Nothing was uploaded, built or sent; this was the whole check." : "No launch path works for this key. Nothing was uploaded, built or sent.");
|
|
87387
|
+
const result = {
|
|
87388
|
+
success: true,
|
|
87389
|
+
check: true,
|
|
87390
|
+
works,
|
|
87391
|
+
...requestedChain ? { requestedChain } : {},
|
|
87392
|
+
key: answer2.key,
|
|
87393
|
+
switches: answer2.switches,
|
|
87394
|
+
...answer2.embedded ? { embedded: answer2.embedded } : {},
|
|
87395
|
+
wallets: shownWallets.map((row) => ({
|
|
87396
|
+
...row,
|
|
87397
|
+
canLaunch: walletWorks(answer2, row, requestedChain),
|
|
87398
|
+
canBuy: walletWorks(answer2, row, requestedChain) && answer2.switches.teeLaunchBuys && (row.chain !== "solana" || answer2.switches.agentTrading)
|
|
87399
|
+
})),
|
|
87400
|
+
...embeddedOnly || walletFlag === undefined ? { embeddedCanLaunch: embeddedWorks(answer2, requestedChain) } : {}
|
|
87401
|
+
};
|
|
87402
|
+
if (ctx.json)
|
|
87403
|
+
ctx.deps.stdout.write(`${JSON.stringify(result)}
|
|
87404
|
+
`);
|
|
87405
|
+
else
|
|
87406
|
+
ctx.deps.stdout.write(`${lines.join(`
|
|
87407
|
+
`)}
|
|
87408
|
+
`);
|
|
87409
|
+
return works ? 0 : 1;
|
|
87410
|
+
}
|
|
87411
|
+
|
|
86345
87412
|
// src/commands/launch.ts
|
|
86346
87413
|
init_render();
|
|
86347
87414
|
init_solana_endpoint();
|
|
@@ -86400,6 +87467,7 @@ var LAUNCH_ARG_SPEC = {
|
|
|
86400
87467
|
],
|
|
86401
87468
|
booleanFlags: [
|
|
86402
87469
|
...launchFlagFields.filter((field) => field.type === "boolean").map((field) => field.cli.flag),
|
|
87470
|
+
"--check",
|
|
86403
87471
|
"--yes"
|
|
86404
87472
|
],
|
|
86405
87473
|
pathFlags: ["--image", "--banner"]
|
|
@@ -86497,6 +87565,11 @@ async function launch(args, ctx) {
|
|
|
86497
87565
|
const id = flags["--client-trade-id"] ?? `launch-${randomUUID5()}`;
|
|
86498
87566
|
const embedded = EMBEDDED_WALLET_NAMES.has(flags["--wallet"] ?? "");
|
|
86499
87567
|
const dryRun = parsed.booleans.has("--dry-run");
|
|
87568
|
+
const check = parsed.booleans.has("--check");
|
|
87569
|
+
if (check && dryRun) {
|
|
87570
|
+
writeUsageFailure(ctx.deps, `--check and --dry-run are two different stops; pass one. ${USAGE6}`, ctx.json);
|
|
87571
|
+
return 2;
|
|
87572
|
+
}
|
|
86500
87573
|
const fieldProblem = launchFieldProblem(flags);
|
|
86501
87574
|
if (fieldProblem) {
|
|
86502
87575
|
writeUsageFailure(ctx.deps, fieldProblem, ctx.json);
|
|
@@ -86519,7 +87592,8 @@ async function launch(args, ctx) {
|
|
|
86519
87592
|
writeUsageFailure(ctx.deps, "--banner takes a local file or an https:// URL.", ctx.json);
|
|
86520
87593
|
return 2;
|
|
86521
87594
|
}
|
|
86522
|
-
|
|
87595
|
+
const missingRequired = !flags["--name"] || !flags["--symbol"] || flags["--image"] === undefined && flags["--image-url"] === undefined || !flags["--wallet"];
|
|
87596
|
+
if (parsed.positionals.length || !check && missingRequired || !validClientId(id)) {
|
|
86523
87597
|
writeUsageFailure(ctx.deps, USAGE6, ctx.json);
|
|
86524
87598
|
return 2;
|
|
86525
87599
|
}
|
|
@@ -86533,21 +87607,25 @@ async function launch(args, ctx) {
|
|
|
86533
87607
|
if (flags["--buy"]?.trim() && !/^0+(\.0+)?$/.test(flags["--buy"]))
|
|
86534
87608
|
launchBuyRaw(flags["--buy"], 18);
|
|
86535
87609
|
const hinted = flagChain(flags);
|
|
86536
|
-
if (hinted === "hood" && flags["--dex-version"] === undefined) {
|
|
87610
|
+
if (!check && hinted === "hood" && flags["--dex-version"] === undefined) {
|
|
86537
87611
|
writeUsageFailure(ctx.deps, `A Hood launch needs --dex-version v3|v4. ${USAGE6}`, ctx.json);
|
|
86538
87612
|
return 2;
|
|
86539
87613
|
}
|
|
86540
87614
|
let image;
|
|
86541
87615
|
let banner;
|
|
86542
|
-
|
|
86543
|
-
|
|
86544
|
-
|
|
86545
|
-
|
|
86546
|
-
banner
|
|
86547
|
-
|
|
86548
|
-
|
|
87616
|
+
if (!check) {
|
|
87617
|
+
try {
|
|
87618
|
+
if (flags["--image"] !== undefined)
|
|
87619
|
+
image = await readLocalImage(flags["--image"]);
|
|
87620
|
+
if (flags["--banner"] && !flags["--banner"].startsWith("https://"))
|
|
87621
|
+
banner = await readLocalImage(flags["--banner"]);
|
|
87622
|
+
} catch (error) {
|
|
87623
|
+
return tradingFailure(ctx, error);
|
|
87624
|
+
}
|
|
86549
87625
|
}
|
|
86550
87626
|
const key = await tradingKey(ctx);
|
|
87627
|
+
if (check)
|
|
87628
|
+
return await launchCheck(ctx, key, flags["--wallet"], hinted);
|
|
86551
87629
|
const prior = dryRun ? null : await lookupOperation(ctx, key, id, "launch");
|
|
86552
87630
|
let retry;
|
|
86553
87631
|
if (prior) {
|
|
@@ -86584,7 +87662,7 @@ async function launch(args, ctx) {
|
|
|
86584
87662
|
yes: parsed.booleans.has("--yes"),
|
|
86585
87663
|
dryRun
|
|
86586
87664
|
});
|
|
86587
|
-
const wallet3 = await tradingWallet(ctx, key, flags["--wallet"], "launch:write", hinted ?? "any");
|
|
87665
|
+
const wallet3 = await tradingWallet(ctx, key, flags["--wallet"] ?? "", "launch:write", hinted ?? "any");
|
|
86588
87666
|
const chain2 = wallet3.chain === "evm" ? "hood" : "solana";
|
|
86589
87667
|
if (chain2 === "hood" && flags["--dex-version"] === undefined)
|
|
86590
87668
|
throw new TradingUsage(`${wallet3.address} is a Hood TEE wallet, and a Hood launch needs --dex-version v3|v4.`);
|
|
@@ -86672,7 +87750,7 @@ async function launch(args, ctx) {
|
|
|
86672
87750
|
const result = await request(ctx, key, "/api/v1/launch/self/confirm", { clientLaunchId: id, signature });
|
|
86673
87751
|
return printTradingResult(ctx, { ...result, clientTradeId: id, kind: "launch", quote, ...used });
|
|
86674
87752
|
} catch (error) {
|
|
86675
|
-
return tradingFailure(ctx, error, dryRun ? undefined : id);
|
|
87753
|
+
return tradingFailure(ctx, error, dryRun || check ? undefined : id);
|
|
86676
87754
|
}
|
|
86677
87755
|
}
|
|
86678
87756
|
async function uploadLaunchImages(ctx, key, args) {
|
|
@@ -90450,8 +91528,54 @@ function isParsedClearinghouse2(value) {
|
|
|
90450
91528
|
}
|
|
90451
91529
|
|
|
90452
91530
|
// src/commands/perps-risk.ts
|
|
91531
|
+
init_presentation();
|
|
90453
91532
|
init_render();
|
|
90454
91533
|
init_trading();
|
|
91534
|
+
|
|
91535
|
+
// src/usd.ts
|
|
91536
|
+
init_render();
|
|
91537
|
+
var COMPACT_UNITS = [
|
|
91538
|
+
[1000000000000, "T"],
|
|
91539
|
+
[1e9, "B"],
|
|
91540
|
+
[1e6, "M"]
|
|
91541
|
+
];
|
|
91542
|
+
function formatUsd(value, options = {}) {
|
|
91543
|
+
if (!Number.isFinite(value))
|
|
91544
|
+
return "?";
|
|
91545
|
+
const sign4 = value < 0 ? "-" : options.signed && value > 0 ? "+" : "";
|
|
91546
|
+
const abs = Math.abs(value);
|
|
91547
|
+
if (abs > 0 && abs < 0.005)
|
|
91548
|
+
return `${sign4}<$0.01`;
|
|
91549
|
+
if (options.compact) {
|
|
91550
|
+
for (const [unit, suffix] of COMPACT_UNITS) {
|
|
91551
|
+
if (Math.round(abs / (unit / 100)) >= 100)
|
|
91552
|
+
return `${sign4}$${(abs / unit).toFixed(2)}${suffix}`;
|
|
91553
|
+
}
|
|
91554
|
+
}
|
|
91555
|
+
return `${sign4}$${abs.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
91556
|
+
}
|
|
91557
|
+
function formatPrice(value) {
|
|
91558
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
91559
|
+
return "?";
|
|
91560
|
+
if (value >= 1)
|
|
91561
|
+
return formatUsd(value);
|
|
91562
|
+
return `$${Number(value.toPrecision(4)).toLocaleString("en-US", { maximumFractionDigits: 12 })}`;
|
|
91563
|
+
}
|
|
91564
|
+
function formatAmount(raw, decimals) {
|
|
91565
|
+
const digits = BigInt(raw).toString().padStart(decimals + 1, "0");
|
|
91566
|
+
if (decimals === 0)
|
|
91567
|
+
return digits;
|
|
91568
|
+
const whole = digits.slice(0, -decimals);
|
|
91569
|
+
const fraction = digits.slice(-decimals).replace(/0+$/, "");
|
|
91570
|
+
return fraction ? `${whole}.${fraction}` : whole;
|
|
91571
|
+
}
|
|
91572
|
+
function formatQuantity(value) {
|
|
91573
|
+
if (!Number.isFinite(value))
|
|
91574
|
+
return "?";
|
|
91575
|
+
return value.toLocaleString("en-US", { maximumFractionDigits: 6 });
|
|
91576
|
+
}
|
|
91577
|
+
|
|
91578
|
+
// src/commands/perps-risk.ts
|
|
90455
91579
|
init_keys();
|
|
90456
91580
|
init_swap();
|
|
90457
91581
|
init_tee_rebind();
|
|
@@ -90496,6 +91620,16 @@ var usd = (value) => {
|
|
|
90496
91620
|
return Number.isFinite(n) ? n.toFixed(2) : safeText(value);
|
|
90497
91621
|
};
|
|
90498
91622
|
var pct = (fraction) => fraction === null ? "-" : `${(fraction * 100).toFixed(1)}%`;
|
|
91623
|
+
function signedPct(fraction, missing) {
|
|
91624
|
+
if (fraction === null || !Number.isFinite(fraction))
|
|
91625
|
+
return missing;
|
|
91626
|
+
const body = `${Math.abs(fraction * 100).toFixed(1)}%`;
|
|
91627
|
+
if (fraction > 0)
|
|
91628
|
+
return `+${body}`;
|
|
91629
|
+
if (fraction < 0)
|
|
91630
|
+
return `-${body}`;
|
|
91631
|
+
return body;
|
|
91632
|
+
}
|
|
90499
91633
|
function describeDriver(d) {
|
|
90500
91634
|
if (d.kind === "position") {
|
|
90501
91635
|
if (d.reason === "unmarked")
|
|
@@ -90523,6 +91657,69 @@ function levelCell(row) {
|
|
|
90523
91657
|
function accountText(line) {
|
|
90524
91658
|
return `Account value ${usd(line.accountValueUsd)} USD (cross ${usd(line.crossAccountValueUsd)}), margin used ${usd(line.marginUsedUsd)}, free margin ${usd(line.freeMarginUsd)}, withdrawable ${usd(line.withdrawableUsd)}, margin ratio ${line.marginRatio === null ? "none (no cross equity)" : line.marginRatio.toFixed(4)}, unrealized ${usd(line.unrealizedPnlUsd)}, ${line.positionCount} ${line.positionCount === 1 ? "position" : "positions"}, ${safeText(line.mode)} mode`;
|
|
90525
91659
|
}
|
|
91660
|
+
function usdField(value, missing, signed = false) {
|
|
91661
|
+
if (value === null || value === undefined || value === "")
|
|
91662
|
+
return missing;
|
|
91663
|
+
const n = Number(value);
|
|
91664
|
+
return Number.isFinite(n) ? formatUsd(n, { signed }) : safeText(value);
|
|
91665
|
+
}
|
|
91666
|
+
function riskPositionsTable(rows, style) {
|
|
91667
|
+
if (rows.length === 0)
|
|
91668
|
+
return "No open positions.";
|
|
91669
|
+
const missing = symbols(style).missing;
|
|
91670
|
+
const price = (value) => usdField(value, missing);
|
|
91671
|
+
return renderTable([
|
|
91672
|
+
"COIN",
|
|
91673
|
+
"SIDE",
|
|
91674
|
+
"SIZE",
|
|
91675
|
+
"ENTRY",
|
|
91676
|
+
"MARK",
|
|
91677
|
+
"UPNL",
|
|
91678
|
+
"ROE",
|
|
91679
|
+
"LEV",
|
|
91680
|
+
"LIQ",
|
|
91681
|
+
"DIST",
|
|
91682
|
+
"FUNDING PAID",
|
|
91683
|
+
"MARGIN",
|
|
91684
|
+
"VALUE",
|
|
91685
|
+
"LEVEL"
|
|
91686
|
+
], rows.map((r) => [
|
|
91687
|
+
safeText(r.coin),
|
|
91688
|
+
safeText(r.side),
|
|
91689
|
+
safeText(r.size),
|
|
91690
|
+
price(r.entryPx),
|
|
91691
|
+
r.markPx === null ? missing : price(r.markPx),
|
|
91692
|
+
usdField(r.unrealizedPnlUsd, missing, true),
|
|
91693
|
+
signedPct(Number(r.returnOnEquity), missing),
|
|
91694
|
+
safeText(`${r.leverage.value}x ${r.leverage.type}`),
|
|
91695
|
+
r.liquidationPx === null ? missing : price(r.liquidationPx),
|
|
91696
|
+
r.liquidationDistance === null ? missing : pct(r.liquidationDistance),
|
|
91697
|
+
usdField(r.fundingSinceOpenUsd, missing, true),
|
|
91698
|
+
usdField(r.marginUsedUsd, missing),
|
|
91699
|
+
usdField(r.positionValueUsd, missing),
|
|
91700
|
+
levelCell(r)
|
|
91701
|
+
]), {
|
|
91702
|
+
style,
|
|
91703
|
+
width: style.width,
|
|
91704
|
+
indent: 2,
|
|
91705
|
+
columns: [
|
|
91706
|
+
{ id: true },
|
|
91707
|
+
{},
|
|
91708
|
+
{},
|
|
91709
|
+
{ priority: 5 },
|
|
91710
|
+
{},
|
|
91711
|
+
{ signed: true },
|
|
91712
|
+
{ priority: 4, signed: true },
|
|
91713
|
+
{ priority: 6 },
|
|
91714
|
+
{},
|
|
91715
|
+
{},
|
|
91716
|
+
{ priority: 1, signed: true },
|
|
91717
|
+
{ priority: 3 },
|
|
91718
|
+
{ priority: 2 },
|
|
91719
|
+
{}
|
|
91720
|
+
]
|
|
91721
|
+
});
|
|
91722
|
+
}
|
|
90526
91723
|
function positionsTable(rows) {
|
|
90527
91724
|
if (rows.length === 0)
|
|
90528
91725
|
return "No open positions.";
|
|
@@ -90562,6 +91759,62 @@ function thresholdsText(t) {
|
|
|
90562
91759
|
return `Thresholds (${t.source}): warn under ${(t.warnDistanceBps / 100).toFixed(1)}% or ratio above ${(t.warnMarginRatioBps / 1e4).toFixed(2)}; critical under ${(t.criticalDistanceBps / 100).toFixed(1)}% or above ${(t.criticalMarginRatioBps / 1e4).toFixed(2)}; clear band ${(t.clearBandBps / 100).toFixed(1)} points`;
|
|
90563
91760
|
}
|
|
90564
91761
|
var NO_MONITOR = "Until Candle's server-side monitor exists, nothing warns you unless you poll candle perps risk or run candle perps watch.";
|
|
91762
|
+
function writeRiskText(ctx, report) {
|
|
91763
|
+
const style = terminalStyle(ctx.deps, "stdout");
|
|
91764
|
+
const sym = symbols(style);
|
|
91765
|
+
const level = report.level.toUpperCase();
|
|
91766
|
+
const painted = report.level === "critical" ? failure(level, style) : report.level === "warn" ? accent(`${sym.warning} ${level}`, style, "warning") : success(level, style);
|
|
91767
|
+
const lead = report.drivers[0] ?? report.closest;
|
|
91768
|
+
const also = report.drivers.slice(1).map((driver) => describeDriver(driver));
|
|
91769
|
+
const account = report.account;
|
|
91770
|
+
const thresholds2 = report.thresholds;
|
|
91771
|
+
const lines = [
|
|
91772
|
+
painted,
|
|
91773
|
+
"",
|
|
91774
|
+
heading("Risk", style),
|
|
91775
|
+
"",
|
|
91776
|
+
...fields([
|
|
91777
|
+
["Driver", lead ? describeDriver(lead) : sym.missing],
|
|
91778
|
+
...also.length > 0 ? [["Also", also.join(`
|
|
91779
|
+
`)]] : []
|
|
91780
|
+
], style),
|
|
91781
|
+
"",
|
|
91782
|
+
heading("Account", style),
|
|
91783
|
+
"",
|
|
91784
|
+
...account ? fields([
|
|
91785
|
+
["Value", usdField(account.accountValueUsd, sym.missing)],
|
|
91786
|
+
["Cross", usdField(account.crossAccountValueUsd, sym.missing)],
|
|
91787
|
+
["Margin used", usdField(account.marginUsedUsd, sym.missing)],
|
|
91788
|
+
["Free margin", usdField(account.freeMarginUsd, sym.missing)],
|
|
91789
|
+
["Withdrawable", usdField(account.withdrawableUsd, sym.missing)],
|
|
91790
|
+
["Margin ratio", account.marginRatio === null ? "none (no cross equity)" : account.marginRatio.toFixed(4)],
|
|
91791
|
+
["Unrealized", usdField(account.unrealizedPnlUsd, sym.missing, true)],
|
|
91792
|
+
["Positions", String(account.positionCount)],
|
|
91793
|
+
["Mode", safeText(account.mode)]
|
|
91794
|
+
], style) : [`${" ".repeat(2)}No account figures.`],
|
|
91795
|
+
"",
|
|
91796
|
+
riskPositionsTable(report.positions, style),
|
|
91797
|
+
"",
|
|
91798
|
+
heading("Thresholds", style),
|
|
91799
|
+
"",
|
|
91800
|
+
...fields([
|
|
91801
|
+
["Source", thresholds2.source],
|
|
91802
|
+
[
|
|
91803
|
+
"Warn",
|
|
91804
|
+
`under ${(thresholds2.warnDistanceBps / 100).toFixed(1)}% or ratio above ${(thresholds2.warnMarginRatioBps / 1e4).toFixed(2)}`
|
|
91805
|
+
],
|
|
91806
|
+
[
|
|
91807
|
+
"Critical",
|
|
91808
|
+
`under ${(thresholds2.criticalDistanceBps / 100).toFixed(1)}% or above ${(thresholds2.criticalMarginRatioBps / 1e4).toFixed(2)}`
|
|
91809
|
+
],
|
|
91810
|
+
["Clear band", `${(thresholds2.clearBandBps / 100).toFixed(1)} points`]
|
|
91811
|
+
], style)
|
|
91812
|
+
];
|
|
91813
|
+
ctx.deps.stdout.write(`${lines.join(`
|
|
91814
|
+
`)}
|
|
91815
|
+
`);
|
|
91816
|
+
notice(ctx.deps, "stdout", "note", NO_MONITOR);
|
|
91817
|
+
}
|
|
90565
91818
|
async function perpsRisk(args, ctx) {
|
|
90566
91819
|
const parsed = parseArgs(args, { valueFlags: ["--wallet"] });
|
|
90567
91820
|
if ("error" in parsed)
|
|
@@ -90588,22 +91841,8 @@ async function perpsRisk(args, ctx) {
|
|
|
90588
91841
|
if (ctx.json)
|
|
90589
91842
|
ctx.deps.stdout.write(`${JSON.stringify(report)}
|
|
90590
91843
|
`);
|
|
90591
|
-
else
|
|
90592
|
-
|
|
90593
|
-
out.write(`${riskLine(report)}
|
|
90594
|
-
`);
|
|
90595
|
-
for (const d of report.drivers.slice(1))
|
|
90596
|
-
out.write(` also: ${describeDriver(d)}
|
|
90597
|
-
`);
|
|
90598
|
-
if (report.account)
|
|
90599
|
-
out.write(`${accountText(report.account)}
|
|
90600
|
-
`);
|
|
90601
|
-
out.write(`${positionsTable(report.positions)}
|
|
90602
|
-
`);
|
|
90603
|
-
out.write(`${thresholdsText(report.thresholds)}
|
|
90604
|
-
${NO_MONITOR}
|
|
90605
|
-
`);
|
|
90606
|
-
}
|
|
91844
|
+
else
|
|
91845
|
+
writeRiskText(ctx, report);
|
|
90607
91846
|
return perpsRiskExitCode(report.level);
|
|
90608
91847
|
} catch (error) {
|
|
90609
91848
|
return tradingFailure(ctx, error);
|
|
@@ -92600,9 +93839,75 @@ async function perpsReduceOnly(args, ctx) {
|
|
|
92600
93839
|
// src/commands/perps-markets.ts
|
|
92601
93840
|
init_args();
|
|
92602
93841
|
init_deps();
|
|
93842
|
+
init_presentation();
|
|
92603
93843
|
init_render();
|
|
92604
93844
|
init_trading();
|
|
92605
93845
|
init_swap();
|
|
93846
|
+
function marketUsd(value, missing, compact) {
|
|
93847
|
+
if (value === null)
|
|
93848
|
+
return missing;
|
|
93849
|
+
const n = Number(value);
|
|
93850
|
+
return Number.isFinite(n) ? formatUsd(n, { compact }) : missing;
|
|
93851
|
+
}
|
|
93852
|
+
function changeCell(value, missing) {
|
|
93853
|
+
if (value === null)
|
|
93854
|
+
return missing;
|
|
93855
|
+
const n = Number(value);
|
|
93856
|
+
if (!Number.isFinite(n))
|
|
93857
|
+
return safeText(value);
|
|
93858
|
+
const body = `${Math.abs(n).toFixed(1)}%`;
|
|
93859
|
+
if (n > 0)
|
|
93860
|
+
return `+${body}`;
|
|
93861
|
+
if (n < 0)
|
|
93862
|
+
return `-${body}`;
|
|
93863
|
+
return body;
|
|
93864
|
+
}
|
|
93865
|
+
function fundingCell(rate, missing) {
|
|
93866
|
+
if (rate === null)
|
|
93867
|
+
return missing;
|
|
93868
|
+
const n = Number(rate) * 100;
|
|
93869
|
+
return Number.isFinite(n) ? `${n.toFixed(4)}%` : missing;
|
|
93870
|
+
}
|
|
93871
|
+
function flagCell(row) {
|
|
93872
|
+
return [
|
|
93873
|
+
row.onlyIsolated ? "iso" : "",
|
|
93874
|
+
row.atOpenInterestCap ? "cap" : "",
|
|
93875
|
+
row.candle.tradeable === false ? `x:${row.candle.reason}` : row.candle.tradeable === null ? "?:networkMismatch" : ""
|
|
93876
|
+
].filter(Boolean).join(",");
|
|
93877
|
+
}
|
|
93878
|
+
function writeMarkets(ctx, result) {
|
|
93879
|
+
const style = terminalStyle(ctx.deps, "stdout");
|
|
93880
|
+
const missing = symbols(style).missing;
|
|
93881
|
+
const rows = result.markets.map((row) => [
|
|
93882
|
+
safeText(row.coin),
|
|
93883
|
+
marketUsd(row.markPx, missing, false),
|
|
93884
|
+
changeCell(row.change24hPct, missing),
|
|
93885
|
+
fundingCell(row.fundingRate, missing),
|
|
93886
|
+
row.fundingAprPct === null ? missing : `${row.fundingAprPct}%`,
|
|
93887
|
+
marketUsd(row.openInterestUsd, missing, true),
|
|
93888
|
+
marketUsd(row.dayNotionalVolumeUsd, missing, true),
|
|
93889
|
+
`${row.maxLeverage}x`,
|
|
93890
|
+
flagCell(row)
|
|
93891
|
+
]);
|
|
93892
|
+
ctx.deps.stdout.write(`${renderTable(["COIN", "MARK", "24H", "FUNDING/H", "APR", "OI", "24H VOL", "LEV", "FLAGS"], rows, {
|
|
93893
|
+
style,
|
|
93894
|
+
width: style.width,
|
|
93895
|
+
indent: 2,
|
|
93896
|
+
columns: [
|
|
93897
|
+
{ id: true },
|
|
93898
|
+
{},
|
|
93899
|
+
{ signed: true, priority: 4 },
|
|
93900
|
+
{ priority: 3 },
|
|
93901
|
+
{ priority: 5 },
|
|
93902
|
+
{},
|
|
93903
|
+
{ priority: 2 },
|
|
93904
|
+
{},
|
|
93905
|
+
{ priority: 1 }
|
|
93906
|
+
]
|
|
93907
|
+
})}
|
|
93908
|
+
`);
|
|
93909
|
+
notice(ctx.deps, "stdout", "note", `Hyperliquid ${result.network}. OI floor ${formatUsd(result.gate.minOpenInterestUsd, { compact: true })} (${result.gate.source}). Tradeable now is Candle's build gate at read time, not a promise. OI caps may still refuse an order.`);
|
|
93910
|
+
}
|
|
92606
93911
|
var flags = {
|
|
92607
93912
|
markets: ["--sort", "--limit"],
|
|
92608
93913
|
market: [],
|
|
@@ -92646,32 +93951,19 @@ async function read(ctx, argv, command) {
|
|
|
92646
93951
|
`);
|
|
92647
93952
|
return 0;
|
|
92648
93953
|
}
|
|
93954
|
+
if ("markets" in result) {
|
|
93955
|
+
for (const warning of result.warnings)
|
|
93956
|
+
notice(ctx.deps, "stderr", "warning", safeText(warning));
|
|
93957
|
+
writeMarkets(ctx, result);
|
|
93958
|
+
return 0;
|
|
93959
|
+
}
|
|
92649
93960
|
for (const warning of result.warnings)
|
|
92650
93961
|
ctx.deps.stderr.write(`${safeText(warning)}
|
|
92651
93962
|
`);
|
|
92652
93963
|
const out = (line) => ctx.deps.stdout.write(`${line}
|
|
92653
93964
|
`);
|
|
92654
93965
|
out(`Hyperliquid ${result.network}; OI floor $${result.gate.minOpenInterestUsd} (${result.gate.source}). Tradeability is Candle's build gate at read time, not a promise. OI caps may still refuse an order.`);
|
|
92655
|
-
|
|
92656
|
-
if ("markets" in result) {
|
|
92657
|
-
out("COIN MARK USD 24H % FUNDING %/h APR % OI USD 24H VOLUME USD LEVERAGE FLAGS");
|
|
92658
|
-
for (const row of result.markets)
|
|
92659
|
-
out([
|
|
92660
|
-
safeText(row.coin),
|
|
92661
|
-
money(row.markPx),
|
|
92662
|
-
row.change24hPct ?? "-",
|
|
92663
|
-
row.fundingRate === null ? "-" : (Number(row.fundingRate) * 100).toFixed(4),
|
|
92664
|
-
row.fundingAprPct ?? "-",
|
|
92665
|
-
row.openInterestUsd ?? "-",
|
|
92666
|
-
money(row.dayNotionalVolumeUsd),
|
|
92667
|
-
`${row.maxLeverage}x`,
|
|
92668
|
-
[
|
|
92669
|
-
row.onlyIsolated ? "iso" : "",
|
|
92670
|
-
row.atOpenInterestCap ? "cap" : "",
|
|
92671
|
-
row.candle.tradeable === false ? `x:${row.candle.reason}` : row.candle.tradeable === null ? "?:networkMismatch" : ""
|
|
92672
|
-
].filter(Boolean).join(",")
|
|
92673
|
-
].join(" "));
|
|
92674
|
-
} else if ("market" in result) {
|
|
93966
|
+
if ("market" in result) {
|
|
92675
93967
|
out(JSON.stringify({ ...result.market, coin: safeText(result.market.coin) }, null, 2));
|
|
92676
93968
|
} else if ("bids" in result) {
|
|
92677
93969
|
out(`${safeText(result.coin)}: BID PX / SIZE / ORDERS | ASK PX / SIZE / ORDERS`);
|
|
@@ -93580,49 +94872,6 @@ init_presentation();
|
|
|
93580
94872
|
init_profiles();
|
|
93581
94873
|
init_render();
|
|
93582
94874
|
|
|
93583
|
-
// src/usd.ts
|
|
93584
|
-
init_render();
|
|
93585
|
-
var COMPACT_UNITS = [
|
|
93586
|
-
[1000000000000, "T"],
|
|
93587
|
-
[1e9, "B"],
|
|
93588
|
-
[1e6, "M"]
|
|
93589
|
-
];
|
|
93590
|
-
function formatUsd(value, options = {}) {
|
|
93591
|
-
if (!Number.isFinite(value))
|
|
93592
|
-
return "?";
|
|
93593
|
-
const sign5 = value < 0 ? "-" : options.signed && value > 0 ? "+" : "";
|
|
93594
|
-
const abs = Math.abs(value);
|
|
93595
|
-
if (abs > 0 && abs < 0.005)
|
|
93596
|
-
return `${sign5}<$0.01`;
|
|
93597
|
-
if (options.compact) {
|
|
93598
|
-
for (const [unit, suffix] of COMPACT_UNITS) {
|
|
93599
|
-
if (Math.round(abs / (unit / 100)) >= 100)
|
|
93600
|
-
return `${sign5}$${(abs / unit).toFixed(2)}${suffix}`;
|
|
93601
|
-
}
|
|
93602
|
-
}
|
|
93603
|
-
return `${sign5}$${abs.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
93604
|
-
}
|
|
93605
|
-
function formatPrice(value) {
|
|
93606
|
-
if (!Number.isFinite(value) || value <= 0)
|
|
93607
|
-
return "?";
|
|
93608
|
-
if (value >= 1)
|
|
93609
|
-
return formatUsd(value);
|
|
93610
|
-
return `$${Number(value.toPrecision(4)).toLocaleString("en-US", { maximumFractionDigits: 12 })}`;
|
|
93611
|
-
}
|
|
93612
|
-
function formatAmount(raw, decimals) {
|
|
93613
|
-
const digits = BigInt(raw).toString().padStart(decimals + 1, "0");
|
|
93614
|
-
if (decimals === 0)
|
|
93615
|
-
return digits;
|
|
93616
|
-
const whole = digits.slice(0, -decimals);
|
|
93617
|
-
const fraction = digits.slice(-decimals).replace(/0+$/, "");
|
|
93618
|
-
return fraction ? `${whole}.${fraction}` : whole;
|
|
93619
|
-
}
|
|
93620
|
-
function formatQuantity(value) {
|
|
93621
|
-
if (!Number.isFinite(value))
|
|
93622
|
-
return "?";
|
|
93623
|
-
return value.toLocaleString("en-US", { maximumFractionDigits: 6 });
|
|
93624
|
-
}
|
|
93625
|
-
|
|
93626
94875
|
// src/commands/financial-output.ts
|
|
93627
94876
|
init_presentation();
|
|
93628
94877
|
init_render();
|
|
@@ -98124,6 +99373,7 @@ async function tokenVampCandidates(args, ctx) {
|
|
|
98124
99373
|
// src/commands/transfer.ts
|
|
98125
99374
|
init_args();
|
|
98126
99375
|
init_evm_lite();
|
|
99376
|
+
init_presentation();
|
|
98127
99377
|
init_profiles();
|
|
98128
99378
|
init_render();
|
|
98129
99379
|
init_solana_verify();
|
|
@@ -98192,6 +99442,16 @@ function describeDestination(destination) {
|
|
|
98192
99442
|
return `address ${destination.address} (not one of this account's linked wallets; Candle decides whether it is allowed)`;
|
|
98193
99443
|
}
|
|
98194
99444
|
}
|
|
99445
|
+
function writePreview(ctx, title, rows, note) {
|
|
99446
|
+
const style = terminalStyle(ctx.deps, "stderr");
|
|
99447
|
+
ctx.deps.stderr.write(`${heading(title, style)}
|
|
99448
|
+
|
|
99449
|
+
${fields(rows, style).join(`
|
|
99450
|
+
`)}
|
|
99451
|
+
`);
|
|
99452
|
+
if (note)
|
|
99453
|
+
notice(ctx.deps, "stderr", "note", note);
|
|
99454
|
+
}
|
|
98195
99455
|
async function confirmTransfer(ctx, lines, yes) {
|
|
98196
99456
|
const output = ctx.json ? ctx.deps.stderr : ctx.deps.stdout;
|
|
98197
99457
|
for (const line of lines)
|
|
@@ -98273,8 +99533,11 @@ async function transfer(args, ctx) {
|
|
|
98273
99533
|
if (builtAmount === null || !isMax && builtAmount !== amountRaw)
|
|
98274
99534
|
throw new TradingError("INVALID_RESPONSE", "The transfer build does not name the confirmed amount; nothing was signed.");
|
|
98275
99535
|
const amountShown = asset ? `${decimalAmount(builtAmount, BASES[asset]?.decimals ?? 0)} ${asset}` : `${builtAmount} raw units of ${label}`;
|
|
98276
|
-
ctx
|
|
98277
|
-
|
|
99536
|
+
writePreview(ctx, "Built", [
|
|
99537
|
+
["Transfer", transferId],
|
|
99538
|
+
["Amount", amountShown],
|
|
99539
|
+
...typeof built.destinationKind === "string" ? [["To", built.destinationKind === "vault" ? "the vault" : "a linked wallet"]] : []
|
|
99540
|
+
]);
|
|
98278
99541
|
const reader = await solana();
|
|
98279
99542
|
const problem = await tradingRead(ctx, reader, () => solanaSpendProblem(unsigned[0], wallet3.address, reader.rpc, {
|
|
98280
99543
|
kind: "transfer",
|
|
@@ -98284,8 +99547,12 @@ async function transfer(args, ctx) {
|
|
|
98284
99547
|
}));
|
|
98285
99548
|
if (problem)
|
|
98286
99549
|
throw new TradingError("INVALID_RESPONSE", `The transaction Candle built does not move ${safeText(amountShown)} to ${destination.address} and nothing else (${safeText(problem)}); nothing was signed.`);
|
|
98287
|
-
ctx
|
|
98288
|
-
|
|
99550
|
+
writePreview(ctx, "Checked locally", [
|
|
99551
|
+
["Moves", safeText(amountShown)],
|
|
99552
|
+
["From", wallet3.address],
|
|
99553
|
+
["To", destination.address],
|
|
99554
|
+
["Else", "nothing else"]
|
|
99555
|
+
], "Simulated. A program can still behave differently once signed.");
|
|
98289
99556
|
const signed2 = await relaySign(ctx, key, wallet3, unsigned[0]);
|
|
98290
99557
|
const result = await request(ctx, key, "/api/v1/agent/transfer/submit", {
|
|
98291
99558
|
transferId,
|
|
@@ -98364,8 +99631,15 @@ async function hoodTransfer(ctx, args) {
|
|
|
98364
99631
|
throw new TradingError("INVALID_RESPONSE", `The leg Candle built does not move ${decimalAmount(amountRaw, decimals)} ${label} to ${destination.address}; nothing was signed.`);
|
|
98365
99632
|
const leg = first.nextLeg;
|
|
98366
99633
|
const maxFee = BigInt(leg.maxFeePerGas);
|
|
98367
|
-
ctx
|
|
98368
|
-
|
|
99634
|
+
writePreview(ctx, "Built", [
|
|
99635
|
+
["Transfer", safeText(transferId)],
|
|
99636
|
+
["Amount", `${decimalAmount(amountRaw, decimals)} ${safeText(label)}`],
|
|
99637
|
+
...typeof built.destinationKind === "string" ? [["To", built.destinationKind === "vault" ? "the vault" : "a linked wallet"]] : [],
|
|
99638
|
+
[
|
|
99639
|
+
"Gas",
|
|
99640
|
+
`up to ${formatUnits(BigInt(leg.gas) * maxFee, 18)} ETH (gas ${leg.gas} at ${formatUnits(maxFee, 9)} gwei)`
|
|
99641
|
+
]
|
|
99642
|
+
]);
|
|
98369
99643
|
const run2 = await runSequencedLegs(ctx, key, {
|
|
98370
99644
|
wallet: wallet3,
|
|
98371
99645
|
first,
|
|
@@ -100651,8 +101925,8 @@ async function addTouchIdFactor(ctx, parsed, resolvedVault, hold) {
|
|
|
100651
101925
|
});
|
|
100652
101926
|
}
|
|
100653
101927
|
if (helper.biometry !== "available") {
|
|
100654
|
-
const
|
|
100655
|
-
throw new VaultError("VAULT_FACTOR_UNAVAILABLE",
|
|
101928
|
+
const described2 = describeBiometry(helper);
|
|
101929
|
+
throw new VaultError("VAULT_FACTOR_UNAVAILABLE", described2.message, { suggestion: described2.suggestion });
|
|
100656
101930
|
}
|
|
100657
101931
|
const session = {
|
|
100658
101932
|
path: helper.path,
|
|
@@ -105448,6 +106722,8 @@ async function resolveLegacyPath(env, home) {
|
|
|
105448
106722
|
|
|
105449
106723
|
// src/commands/vault-status.ts
|
|
105450
106724
|
init_args();
|
|
106725
|
+
init_presentation();
|
|
106726
|
+
init_render();
|
|
105451
106727
|
init_fido2();
|
|
105452
106728
|
init_format();
|
|
105453
106729
|
init_passphrase();
|
|
@@ -105456,7 +106732,9 @@ init_sidecar();
|
|
|
105456
106732
|
init_store();
|
|
105457
106733
|
init_vault_support();
|
|
105458
106734
|
var LIST_POINTER = "One line per key, with an optional SOL balance: candle vault list";
|
|
105459
|
-
var NO_VERIFIED_BACKUP_NOTE =
|
|
106735
|
+
var NO_VERIFIED_BACKUP_NOTE = `No backup of this vault has ever been verified from this machine. If this file is lost, only the 24-word recovery phrase can rebuild it, and it rebuilds derived keys only. Take one now:
|
|
106736
|
+
candle vault backup --to <path>
|
|
106737
|
+
on a Mac with iCloud Drive: candle vault backup --to icloud`;
|
|
105460
106738
|
async function vaultStatus(args, ctx) {
|
|
105461
106739
|
const parsed = parseArgs(args, {
|
|
105462
106740
|
valueFlags: ["--keystore"],
|
|
@@ -105535,132 +106813,119 @@ async function vaultStatus(args, ctx) {
|
|
|
105535
106813
|
});
|
|
105536
106814
|
return 0;
|
|
105537
106815
|
}
|
|
105538
|
-
deps
|
|
105539
|
-
|
|
105540
|
-
deps.stdout.write(
|
|
105541
|
-
`);
|
|
105542
|
-
|
|
105543
|
-
|
|
105544
|
-
|
|
105545
|
-
|
|
105546
|
-
|
|
105547
|
-
|
|
105548
|
-
|
|
106816
|
+
const style = terminalStyle(deps, "stdout");
|
|
106817
|
+
const miss = symbols(style).missing;
|
|
106818
|
+
const write = (line = "") => deps.stdout.write(`${line}
|
|
106819
|
+
`);
|
|
106820
|
+
write(path);
|
|
106821
|
+
write("");
|
|
106822
|
+
write(heading("Vault", style));
|
|
106823
|
+
write("");
|
|
106824
|
+
for (const line of fields([
|
|
106825
|
+
["Version", String(file.version)],
|
|
106826
|
+
["Generation", String(file.generation)],
|
|
106827
|
+
["Updated", formatTime(file.updatedAt)]
|
|
106828
|
+
], style))
|
|
106829
|
+
write(line);
|
|
106830
|
+
write("");
|
|
106831
|
+
write(heading("Factors", style));
|
|
106832
|
+
write("");
|
|
106833
|
+
write(`${" ".repeat(2)}${envelopes.length} factors, ${recoverable} recoverable (domains counted once)`);
|
|
105549
106834
|
for (const envelope of envelopes) {
|
|
105550
|
-
|
|
105551
|
-
`);
|
|
105552
|
-
|
|
105553
|
-
|
|
105554
|
-
|
|
105555
|
-
|
|
105556
|
-
|
|
106835
|
+
write("");
|
|
106836
|
+
write(`${" ".repeat(2)}${envelope.id}`);
|
|
106837
|
+
const rows = [
|
|
106838
|
+
["Factor", envelope.factor],
|
|
106839
|
+
["Domain", envelope.domain],
|
|
106840
|
+
["Availability", envelope.availability],
|
|
106841
|
+
["Label", envelope.label || miss]
|
|
106842
|
+
];
|
|
106843
|
+
if (envelope.transport)
|
|
106844
|
+
rows.splice(1, 0, ["Transport", envelope.transport]);
|
|
106845
|
+
if (envelope.strengthLabel)
|
|
106846
|
+
rows.push(["Strength", envelope.strengthLabel]);
|
|
105557
106847
|
if (envelope.sharedDomainNote)
|
|
105558
|
-
|
|
105559
|
-
|
|
106848
|
+
rows.push(["Note", envelope.sharedDomainNote]);
|
|
106849
|
+
for (const line of fields(rows, style, 4))
|
|
106850
|
+
write(line);
|
|
105560
106851
|
}
|
|
105561
106852
|
if (recoverable === 1) {
|
|
105562
|
-
deps
|
|
105563
|
-
This vault has exactly one recoverable factor. Lose it and no copy of this file can be opened; the recovery phrase is then the only route, and it restores derived keys only.
|
|
105564
|
-
`);
|
|
106853
|
+
notice(deps, "stdout", "warning", "This vault has exactly one recoverable factor. Lose it and no copy of this file can be opened; the recovery phrase is then the only route, and it restores derived keys only.");
|
|
105565
106854
|
}
|
|
106855
|
+
write("");
|
|
106856
|
+
write(heading("This machine", style));
|
|
105566
106857
|
if (sidecar) {
|
|
105567
|
-
|
|
105568
|
-
|
|
105569
|
-
|
|
105570
|
-
|
|
105571
|
-
|
|
106858
|
+
write("");
|
|
106859
|
+
for (const line of fields([
|
|
106860
|
+
["Record", "vault.state.json, cleartext, best effort"],
|
|
106861
|
+
["Last generation seen", String(sidecar.lastGeneration)]
|
|
106862
|
+
], style))
|
|
106863
|
+
write(line);
|
|
105572
106864
|
if (sidecar.lastVerifiedBackupAt && backup) {
|
|
105573
106865
|
for (const line of lastBackupLines(sidecar, backup))
|
|
105574
|
-
|
|
105575
|
-
`);
|
|
106866
|
+
write(line);
|
|
105576
106867
|
}
|
|
105577
106868
|
if (sidecar.lastBackupSharedDomainAccepted) {
|
|
105578
|
-
|
|
105579
|
-
`);
|
|
106869
|
+
write(" shared domain accepted for the last backup destination (an unsealed copy, every envelope included)");
|
|
105580
106870
|
}
|
|
105581
106871
|
if (sidecar.removedEnvelopeIds.length > 0) {
|
|
105582
|
-
|
|
105583
|
-
|
|
105584
|
-
deps.stdout.write(` any copy of this file made while one existed still opens with that factor.
|
|
105585
|
-
`);
|
|
106872
|
+
write(` removed envelopes ${sidecar.removedEnvelopeIds.join(", ")}`);
|
|
106873
|
+
write(" any copy of this file made while one existed still opens with that factor.");
|
|
105586
106874
|
}
|
|
105587
106875
|
} else {
|
|
105588
|
-
deps
|
|
105589
|
-
No vault.state.json beside this vault, so an older copy of it cannot be recognized on this machine.
|
|
105590
|
-
`);
|
|
105591
|
-
}
|
|
105592
|
-
if (sidecar?.lastVerifiedBackupAt === undefined) {
|
|
105593
|
-
deps.stdout.write(`
|
|
105594
|
-
${NO_VERIFIED_BACKUP_NOTE}
|
|
105595
|
-
`);
|
|
106876
|
+
notice(deps, "stdout", "note", "No vault.state.json beside this vault, so an older copy of it cannot be recognized on this machine.");
|
|
105596
106877
|
}
|
|
106878
|
+
if (sidecar?.lastVerifiedBackupAt === undefined)
|
|
106879
|
+
notice(deps, "stdout", "warning", NO_VERIFIED_BACKUP_NOTE);
|
|
105597
106880
|
if (legacyPresent) {
|
|
105598
|
-
deps
|
|
105599
|
-
A
|
|
105600
|
-
|
|
105601
|
-
deps.stdout.write(` A key it holds that was imported is already a linked wallet, managed with: candle wallets
|
|
105602
|
-
`);
|
|
105603
|
-
deps.stdout.write(` A key it holds that was never imported opens with an earlier release's \`wallets export\`; move those funds on chain to a vault key.
|
|
105604
|
-
`);
|
|
106881
|
+
notice(deps, "stdout", "warning", `A legacy wallets.enc exists at ${legacy}; this CLI does not read it.
|
|
106882
|
+
A key it holds that was imported is already a linked wallet, managed with: candle wallets
|
|
106883
|
+
A key it holds that was never imported opens with an earlier release's \`wallets export\`; move those funds on chain to a vault key.`);
|
|
105605
106884
|
}
|
|
105606
106885
|
if (!unlocked) {
|
|
105607
|
-
deps
|
|
105608
|
-
Run with --unlock to list the keys, the derivation counters and the exposure flags, which are inside the encrypted index.
|
|
105609
|
-
`);
|
|
106886
|
+
notice(deps, "stdout", "note", "Run with --unlock to list the keys, the derivation counters and the exposure flags, which are inside the encrypted index.");
|
|
105610
106887
|
return 0;
|
|
105611
106888
|
}
|
|
105612
|
-
|
|
105613
|
-
Keys (${unlocked.entries.length})
|
|
105614
|
-
`);
|
|
106889
|
+
write("");
|
|
106890
|
+
write(heading(`Keys (${unlocked.entries.length})`, style));
|
|
105615
106891
|
for (const entry of unlocked.entries) {
|
|
105616
|
-
|
|
105617
|
-
`);
|
|
105618
|
-
|
|
105619
|
-
|
|
105620
|
-
|
|
105621
|
-
|
|
106892
|
+
write("");
|
|
106893
|
+
write(`${" ".repeat(2)}${entry.address}`);
|
|
106894
|
+
const rows = [
|
|
106895
|
+
["Label", entry.label || miss],
|
|
106896
|
+
["Role", `${entry.role} (${entry.origin})`],
|
|
106897
|
+
["Exposure", entry.exposure]
|
|
106898
|
+
];
|
|
105622
106899
|
if (entry.derivation)
|
|
105623
|
-
|
|
105624
|
-
`);
|
|
105625
|
-
deps.stdout.write(` exposure ${entry.exposure}
|
|
105626
|
-
`);
|
|
106900
|
+
rows.splice(2, 0, ["Derivation", entry.derivation]);
|
|
105627
106901
|
if (entry.teeLifecycle)
|
|
105628
|
-
|
|
105629
|
-
`);
|
|
106902
|
+
rows.push(["Tee", `${entry.teeLifecycle}${entry.teeRemoteState ? ` (server: ${entry.teeRemoteState})` : ""}`]);
|
|
105630
106903
|
if (entry.destinationExposureAccepted) {
|
|
105631
|
-
|
|
105632
|
-
`);
|
|
106904
|
+
rows.push(["Accepted", "an operator asserted this pinned destination's history despite exposureUnknown"]);
|
|
105633
106905
|
}
|
|
106906
|
+
for (const line of fields(rows, style, 4))
|
|
106907
|
+
write(line);
|
|
105634
106908
|
}
|
|
105635
|
-
|
|
105636
|
-
|
|
105637
|
-
`);
|
|
106909
|
+
write("");
|
|
106910
|
+
write(LIST_POINTER);
|
|
105638
106911
|
if (unlocked.duplicateLabels.length > 0) {
|
|
105639
|
-
|
|
105640
|
-
Duplicate labels (${unlocked.duplicateLabels.length})
|
|
105641
|
-
`);
|
|
106912
|
+
write("");
|
|
106913
|
+
write(heading(`Duplicate labels (${unlocked.duplicateLabels.length})`, style));
|
|
105642
106914
|
for (const line of duplicateLabelLines(unlocked.duplicateLabels))
|
|
105643
|
-
|
|
105644
|
-
`);
|
|
106915
|
+
write(line);
|
|
105645
106916
|
}
|
|
105646
|
-
|
|
105647
|
-
Derivation counters (next index per branch)
|
|
105648
|
-
|
|
106917
|
+
write("");
|
|
106918
|
+
write(heading("Derivation counters (next index per branch)", style));
|
|
106919
|
+
write("");
|
|
105649
106920
|
for (const [branch, value] of Object.entries(unlocked.nextIndex)) {
|
|
105650
106921
|
const exposed = unlocked.exposedIndexes[branch] ?? [];
|
|
105651
|
-
|
|
105652
|
-
`);
|
|
106922
|
+
write(` ${branch.padEnd(12)} ${value}${exposed.length > 0 ? ` known exposed: ${exposed.join(", ")}` : ""}`);
|
|
105653
106923
|
}
|
|
105654
|
-
|
|
105655
|
-
`);
|
|
106924
|
+
write(` recovery phrase exported: ${unlocked.rootExported ? "yes" : "no"}`);
|
|
105656
106925
|
if (unlocked.restored) {
|
|
105657
|
-
deps
|
|
105658
|
-
This vault was built by \`vault restore --phrase\`. Its keys are recovery artifacts, not fresh cold keys, and it does not allocate new addresses; \`vault new-key\` and \`vault promote --from\` refuse here.
|
|
105659
|
-
`);
|
|
106926
|
+
notice(deps, "stdout", "note", "This vault was built by `vault restore --phrase`.\nIts keys are recovery artifacts, not fresh cold keys, and it does not allocate new addresses; `vault new-key` and `vault promote --from` refuse here.");
|
|
105660
106927
|
}
|
|
105661
|
-
deps
|
|
105662
|
-
The recovery phrase restores derived keys only. It does not restore any key imported from the Phase 1 TEE wallet store; the vault file plus a factor does that.
|
|
105663
|
-
`);
|
|
106928
|
+
notice(deps, "stdout", "note", "The recovery phrase restores derived keys only. It does not restore any key imported from the Phase 1 TEE wallet store; the vault file plus a factor does that.");
|
|
105664
106929
|
return 0;
|
|
105665
106930
|
});
|
|
105666
106931
|
}
|
|
@@ -105693,7 +106958,7 @@ function lastBackupLines(sidecar, backup) {
|
|
|
105693
106958
|
return factors.length > 0 ? `, sealed: opens with the passphrase or ${factors.join(", ")}` : ", sealed: opens with the passphrase only";
|
|
105694
106959
|
};
|
|
105695
106960
|
const lines = [
|
|
105696
|
-
` last verified backup ${sidecar.lastVerifiedBackupAt} (${sidecar.lastBackupDomain ?? "not recorded"}${opensWith()})`
|
|
106961
|
+
` last verified backup ${formatTime(sidecar.lastVerifiedBackupAt)} (${sidecar.lastBackupDomain ?? "not recorded"}${opensWith()})`
|
|
105697
106962
|
];
|
|
105698
106963
|
if (backup.freshBackupAdvised.length > 0) {
|
|
105699
106964
|
lines.push(` fresh backup advised the last ${sidecar.lastBackupSealed ? "sealed " : ""}copy does not carry security key ${backup.freshBackupAdvised.join(", ")}; a new \`candle vault backup\` to the same destination would open with it too.`);
|
|
@@ -106211,9 +107476,9 @@ function solOf(lamports) {
|
|
|
106211
107476
|
const digits = lamports.padStart(10, "0");
|
|
106212
107477
|
return `${digits.slice(0, -9)}.${digits.slice(-9)}`.replace(/\.?0+$/, "");
|
|
106213
107478
|
}
|
|
106214
|
-
function previewLines3(preview,
|
|
107479
|
+
function previewLines3(preview, walletName2) {
|
|
106215
107480
|
const lines = [
|
|
106216
|
-
`Close ${preview.accountCount} empty token account${preview.accountCount === 1 ? "" : "s"} on the ${
|
|
107481
|
+
`Close ${preview.accountCount} empty token account${preview.accountCount === 1 ? "" : "s"} on the ${walletName2}`,
|
|
106217
107482
|
`Rent returned to that same wallet: ${safeText(preview.totalSol)} SOL in ${preview.transactions} transaction${preview.transactions === 1 ? "" : "s"}, network fee about ${solOf(preview.estimatedFeeLamports)} SOL, net ${safeText(preview.netSol)} SOL`
|
|
106218
107483
|
];
|
|
106219
107484
|
for (const row of preview.accounts.slice(0, LISTED_ROWS))
|
|
@@ -106242,12 +107507,12 @@ async function confirmClose(ctx, lines, yes) {
|
|
|
106242
107507
|
throw new TradingError("CONFIRMATION_REQUIRED", "Run interactively to confirm, or use --yes to close without a prompt.");
|
|
106243
107508
|
return (await ctx.deps.promptLine("Proceed? [y/N] ")).trim().toLowerCase() === "y";
|
|
106244
107509
|
}
|
|
106245
|
-
async function previewAndConfirm(ctx, preview,
|
|
107510
|
+
async function previewAndConfirm(ctx, preview, walletName2, yes) {
|
|
106246
107511
|
if (preview.accounts.length === 0) {
|
|
106247
107512
|
const output = ctx.json ? ctx.deps.stderr : ctx.deps.stdout;
|
|
106248
|
-
output.write(`No empty token accounts to close on the ${
|
|
107513
|
+
output.write(`No empty token accounts to close on the ${walletName2}.
|
|
106249
107514
|
`);
|
|
106250
|
-
for (const line of previewLines3(preview,
|
|
107515
|
+
for (const line of previewLines3(preview, walletName2).slice(2))
|
|
106251
107516
|
output.write(`${line}
|
|
106252
107517
|
`);
|
|
106253
107518
|
return printTradingResult(ctx, {
|
|
@@ -106257,7 +107522,7 @@ async function previewAndConfirm(ctx, preview, walletName, yes) {
|
|
|
106257
107522
|
preview
|
|
106258
107523
|
});
|
|
106259
107524
|
}
|
|
106260
|
-
if (!await confirmClose(ctx, previewLines3(preview,
|
|
107525
|
+
if (!await confirmClose(ctx, previewLines3(preview, walletName2), yes))
|
|
106261
107526
|
return printTradingResult(ctx, {
|
|
106262
107527
|
success: true,
|
|
106263
107528
|
status: "cancelled",
|
|
@@ -106457,17 +107722,17 @@ async function walletsCloseEmpty(args, ctx) {
|
|
|
106457
107722
|
return 2;
|
|
106458
107723
|
}
|
|
106459
107724
|
const run2 = { id, named: named !== undefined, keep: lifted.keep, yes: parsed.booleans.has("--yes") };
|
|
106460
|
-
const
|
|
107725
|
+
const walletName2 = parsed.values["--wallet"];
|
|
106461
107726
|
try {
|
|
106462
107727
|
const key = await tradingKey(ctx);
|
|
106463
|
-
if (
|
|
107728
|
+
if (walletName2 === undefined || walletName2 === "embedded")
|
|
106464
107729
|
return await closeEmbedded(ctx, key, run2);
|
|
106465
|
-
if (
|
|
107730
|
+
if (walletName2 === await embeddedAddress2(ctx, key))
|
|
106466
107731
|
return await closeEmbedded(ctx, key, run2);
|
|
106467
|
-
const payer = await tradingPayer(ctx, key,
|
|
107732
|
+
const payer = await tradingPayer(ctx, key, walletName2, "swap:write", "solana");
|
|
106468
107733
|
if (payer.kind === "embedded")
|
|
106469
107734
|
return await closeEmbedded(ctx, key, run2);
|
|
106470
|
-
return await closeTee(ctx, key, payer.wallet,
|
|
107735
|
+
return await closeTee(ctx, key, payer.wallet, walletName2, run2);
|
|
106471
107736
|
} catch (error) {
|
|
106472
107737
|
return tradingFailure(ctx, error);
|
|
106473
107738
|
}
|