@layr-labs/ecloud-cli 1.0.0-devep1 → 1.0.0-devep2
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/VERSION +2 -2
- package/dist/commands/billing/__tests__/status.test.js +4 -23
- package/dist/commands/billing/__tests__/status.test.js.map +1 -1
- package/dist/commands/billing/__tests__/top-up.test.js +273 -89
- package/dist/commands/billing/__tests__/top-up.test.js.map +1 -1
- package/dist/commands/billing/list-cards.js +409 -0
- package/dist/commands/billing/list-cards.js.map +1 -0
- package/dist/commands/billing/status.js +4 -23
- package/dist/commands/billing/status.js.map +1 -1
- package/dist/commands/billing/top-up.js +151 -79
- package/dist/commands/billing/top-up.js.map +1 -1
- package/dist/commands/compute/app/deploy.js +1 -1
- package/dist/commands/compute/app/info.js +1 -1
- package/dist/commands/compute/app/list.js +1 -1
- package/dist/commands/compute/app/logs.js +1 -1
- package/dist/commands/compute/app/profile/set.js +1 -1
- package/dist/commands/compute/app/releases.js +1 -1
- package/dist/commands/compute/app/start.js +1 -1
- package/dist/commands/compute/app/stop.js +1 -1
- package/dist/commands/compute/app/terminate.js +1 -1
- package/dist/commands/compute/app/upgrade.js +1 -1
- package/dist/commands/compute/build/info.js +1 -1
- package/dist/commands/compute/build/list.js +1 -1
- package/dist/commands/compute/build/logs.js +1 -1
- package/dist/commands/compute/build/status.js +1 -1
- package/dist/commands/compute/build/submit.js +1 -1
- package/dist/commands/compute/build/verify.js +1 -1
- package/dist/commands/compute/undelegate.js +1 -1
- package/dist/hooks/init/__tests__/version-check.test.js +1 -1
- package/dist/hooks/init/version-check.js +1 -1
- package/package.json +2 -2
|
@@ -316,7 +316,8 @@ async function createBillingClient(flags) {
|
|
|
316
316
|
// src/commands/billing/top-up.ts
|
|
317
317
|
import { formatUnits } from "viem";
|
|
318
318
|
import chalk2 from "chalk";
|
|
319
|
-
import { input as input2 } from "@inquirer/prompts";
|
|
319
|
+
import { input as input2, select as select2 } from "@inquirer/prompts";
|
|
320
|
+
import open from "open";
|
|
320
321
|
|
|
321
322
|
// src/telemetry.ts
|
|
322
323
|
import {
|
|
@@ -380,12 +381,22 @@ async function withTelemetry(command, action) {
|
|
|
380
381
|
var POLL_INTERVAL_MS = 5e3;
|
|
381
382
|
var POLL_TIMEOUT_MS = 3 * 60 * 1e3;
|
|
382
383
|
var BillingTopUp = class _BillingTopUp extends Command {
|
|
383
|
-
static description = "Purchase EigenCompute credits with USDC";
|
|
384
|
+
static description = "Purchase EigenCompute credits with USDC or credit card";
|
|
385
|
+
static examples = [
|
|
386
|
+
"<%= config.bin %> billing top-up",
|
|
387
|
+
"<%= config.bin %> billing top-up --method usdc --amount 50",
|
|
388
|
+
"<%= config.bin %> billing top-up --method card --amount 25"
|
|
389
|
+
];
|
|
384
390
|
static flags = {
|
|
385
391
|
...commonFlags,
|
|
392
|
+
method: Flags2.string({
|
|
393
|
+
required: false,
|
|
394
|
+
description: "Payment method: usdc (on-chain) or card (credit card)",
|
|
395
|
+
options: ["usdc", "card"]
|
|
396
|
+
}),
|
|
386
397
|
amount: Flags2.string({
|
|
387
398
|
required: false,
|
|
388
|
-
description: "Amount
|
|
399
|
+
description: "Amount to spend (USDC for on-chain, whole dollars for card)"
|
|
389
400
|
}),
|
|
390
401
|
account: Flags2.string({
|
|
391
402
|
required: false,
|
|
@@ -421,96 +432,157 @@ ${chalk2.bold("Purchase EigenCompute credits")}`);
|
|
|
421
432
|
const remaining = status.remainingCredits ?? 0;
|
|
422
433
|
const applied = status.creditsApplied ?? 0;
|
|
423
434
|
baselineTotal = remaining + applied;
|
|
424
|
-
|
|
425
|
-
this.log(` ${chalk2.bold("Credits:")} ${chalk2.cyan(`$${status.remainingCredits.toFixed(2)}`)}`);
|
|
426
|
-
}
|
|
435
|
+
this.log(` ${chalk2.bold("Credits:")} ${chalk2.cyan(`$${remaining.toFixed(2)}`)}`);
|
|
427
436
|
} catch {
|
|
428
437
|
this.debug("Could not fetch current credit balance");
|
|
429
438
|
}
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
439
|
+
const method = flags.method ?? await select2({
|
|
440
|
+
message: "How would you like to pay?",
|
|
441
|
+
choices: [
|
|
442
|
+
{ value: "card", name: "Credit card" },
|
|
443
|
+
{ value: "usdc", name: "USDC (on-chain)" }
|
|
444
|
+
]
|
|
445
|
+
});
|
|
446
|
+
if (method === "usdc") {
|
|
447
|
+
await this.handleUsdc(billing, flags, walletAddress, targetAccount, baselineTotal);
|
|
448
|
+
} else {
|
|
449
|
+
await this.handleCard(billing, flags, baselineTotal);
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
async handleUsdc(billing, flags, walletAddress, targetAccount, baselineTotal) {
|
|
454
|
+
const onChainState = await billing.getTopUpInfo();
|
|
455
|
+
const { usdcBalance, minimumPurchase } = onChainState;
|
|
456
|
+
const balanceFormatted = formatUnits(usdcBalance, 6);
|
|
457
|
+
this.log(` ${chalk2.bold("USDC:")} ${balanceFormatted} USDC`);
|
|
458
|
+
if (usdcBalance === BigInt(0)) {
|
|
459
|
+
this.log(`
|
|
436
460
|
${chalk2.yellow(" No USDC in wallet.")}`);
|
|
437
|
-
|
|
438
|
-
|
|
461
|
+
this.log(` Send USDC on Sepolia to: ${chalk2.cyan(walletAddress)}`);
|
|
462
|
+
this.log(` Then re-run: ${chalk2.cyan("ecloud billing top-up")}
|
|
439
463
|
`);
|
|
440
|
-
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const minimumFormatted = formatUnits(minimumPurchase, 6);
|
|
467
|
+
const amountStr = flags.amount ?? await input2({
|
|
468
|
+
message: `How much USDC to spend on credits? (minimum: ${minimumFormatted})`,
|
|
469
|
+
validate: (val) => {
|
|
470
|
+
const n = parseFloat(val);
|
|
471
|
+
if (isNaN(n) || n <= 0) return "Enter a positive number";
|
|
472
|
+
const raw = BigInt(Math.round(n * 1e6));
|
|
473
|
+
if (raw < minimumPurchase)
|
|
474
|
+
return `Minimum purchase is ${minimumFormatted} USDC`;
|
|
475
|
+
if (raw > usdcBalance)
|
|
476
|
+
return `Insufficient balance. You have ${balanceFormatted} USDC`;
|
|
477
|
+
return true;
|
|
441
478
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
479
|
+
});
|
|
480
|
+
const amountFloat = parseFloat(amountStr);
|
|
481
|
+
const amountRaw = BigInt(Math.round(amountFloat * 1e6));
|
|
482
|
+
if (amountRaw < minimumPurchase) {
|
|
483
|
+
this.error(`Minimum purchase is ${minimumFormatted} USDC`);
|
|
484
|
+
}
|
|
485
|
+
if (amountRaw > usdcBalance) {
|
|
486
|
+
this.error(
|
|
487
|
+
`Insufficient USDC balance. You have ${balanceFormatted} USDC but requested ${amountFloat.toFixed(2)}`
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
this.log(`
|
|
491
|
+
Purchasing ${chalk2.bold(`$${amountFloat.toFixed(2)}`)} in credits...`);
|
|
492
|
+
const { txHash } = await billing.topUp({
|
|
493
|
+
amount: amountRaw,
|
|
494
|
+
account: targetAccount
|
|
495
|
+
});
|
|
496
|
+
this.log(` ${chalk2.green("\u2713")} Transaction confirmed: ${txHash}`);
|
|
497
|
+
await this.pollForCredits(billing, flags, baselineTotal, amountFloat);
|
|
498
|
+
}
|
|
499
|
+
async handleCard(billing, flags, baselineTotal) {
|
|
500
|
+
const MINIMUM_DOLLARS = 5;
|
|
501
|
+
const amountStr = flags.amount ?? await input2({
|
|
502
|
+
message: `How many dollars of credits to purchase? (minimum: $${MINIMUM_DOLLARS})`,
|
|
503
|
+
validate: (val) => {
|
|
504
|
+
const n = parseInt(val, 10);
|
|
505
|
+
if (isNaN(n) || n <= 0) return "Enter a positive whole number";
|
|
506
|
+
if (n.toString() !== val.trim()) return "Enter a whole dollar amount (no cents)";
|
|
507
|
+
if (n < MINIMUM_DOLLARS) return `Minimum purchase is $${MINIMUM_DOLLARS}`;
|
|
508
|
+
return true;
|
|
460
509
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
510
|
+
});
|
|
511
|
+
const dollars = parseInt(amountStr, 10);
|
|
512
|
+
if (isNaN(dollars) || dollars < MINIMUM_DOLLARS) {
|
|
513
|
+
this.error(`Minimum purchase is $${MINIMUM_DOLLARS}`);
|
|
514
|
+
}
|
|
515
|
+
const amountCents = dollars * 100;
|
|
516
|
+
const { paymentMethods } = await billing.getPaymentMethods();
|
|
517
|
+
let paymentMethodId;
|
|
518
|
+
if (paymentMethods.length > 0) {
|
|
519
|
+
const choices = paymentMethods.map((card) => ({
|
|
520
|
+
value: card.id,
|
|
521
|
+
name: `${card.brand.charAt(0).toUpperCase() + card.brand.slice(1)} ending in ${card.last4}`
|
|
522
|
+
}));
|
|
523
|
+
choices.push({ value: "new", name: "Add a new card" });
|
|
524
|
+
const selection = await select2({
|
|
525
|
+
message: "Which card would you like to use?",
|
|
526
|
+
choices
|
|
527
|
+
});
|
|
528
|
+
if (selection !== "new") {
|
|
529
|
+
paymentMethodId = selection;
|
|
465
530
|
}
|
|
531
|
+
}
|
|
532
|
+
this.log(`
|
|
533
|
+
Purchasing ${chalk2.bold(`$${dollars}`)} in credits...`);
|
|
534
|
+
const result = await billing.purchaseCredits(amountCents, paymentMethodId);
|
|
535
|
+
if (result.checkoutUrl) {
|
|
466
536
|
this.log(`
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
537
|
+
${chalk2.cyan(result.checkoutUrl)}`);
|
|
538
|
+
this.log(chalk2.gray(" Opening checkout in browser..."));
|
|
539
|
+
await open(result.checkoutUrl);
|
|
540
|
+
} else if (result.checkoutSessionId) {
|
|
541
|
+
this.error(
|
|
542
|
+
"Checkout session created but no URL was returned. Please contact support."
|
|
543
|
+
);
|
|
544
|
+
} else {
|
|
545
|
+
this.log(` ${chalk2.green("\u2713")} Payment submitted`);
|
|
546
|
+
}
|
|
547
|
+
await this.pollForCredits(billing, flags, baselineTotal, dollars);
|
|
548
|
+
}
|
|
549
|
+
async pollForCredits(billing, flags, baselineTotal, amountPurchased) {
|
|
550
|
+
this.log(chalk2.gray("\n Waiting for credits to appear..."));
|
|
551
|
+
const startTime = Date.now();
|
|
552
|
+
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
553
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
554
|
+
try {
|
|
555
|
+
const status = await billing.getStatus({
|
|
556
|
+
productId: flags.product
|
|
557
|
+
});
|
|
558
|
+
const remaining = status.remainingCredits ?? 0;
|
|
559
|
+
const applied = status.creditsApplied ?? 0;
|
|
560
|
+
const currentTotal = remaining + applied;
|
|
561
|
+
this.debug(
|
|
562
|
+
`Poll: remaining=${remaining}, applied=${applied}, total=${currentTotal}, baseline=${baselineTotal}`
|
|
563
|
+
);
|
|
564
|
+
if (baselineTotal === void 0 || currentTotal > baselineTotal) {
|
|
565
|
+
const creditsAdded = baselineTotal !== void 0 ? currentTotal - baselineTotal : void 0;
|
|
566
|
+
this.log(
|
|
567
|
+
`
|
|
568
|
+
${chalk2.green("\u2713")} Credits received: ${chalk2.cyan(`$${(creditsAdded ?? amountPurchased).toFixed(2)}`)}`
|
|
569
|
+
);
|
|
570
|
+
if (remaining > 0) {
|
|
571
|
+
this.log(` Remaining balance: ${chalk2.cyan(`$${remaining.toFixed(2)}`)}`);
|
|
502
572
|
}
|
|
503
|
-
|
|
504
|
-
|
|
573
|
+
this.log();
|
|
574
|
+
return;
|
|
505
575
|
}
|
|
576
|
+
} catch {
|
|
577
|
+
this.debug("Error polling for credit balance");
|
|
506
578
|
}
|
|
507
|
-
|
|
508
|
-
|
|
579
|
+
}
|
|
580
|
+
this.log(
|
|
581
|
+
`
|
|
509
582
|
${chalk2.yellow("\u26A0")} Credits haven't appeared yet. This can take a few minutes.`
|
|
510
|
-
|
|
511
|
-
|
|
583
|
+
);
|
|
584
|
+
this.log(` ${chalk2.gray("Check your balance:")} ecloud billing status
|
|
512
585
|
`);
|
|
513
|
-
});
|
|
514
586
|
}
|
|
515
587
|
};
|
|
516
588
|
export {
|