perp-cli 0.3.3
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/LICENSE +21 -0
- package/README.md +293 -0
- package/dist/__tests__/alert-logic.test.d.ts +1 -0
- package/dist/__tests__/alert-logic.test.js +107 -0
- package/dist/__tests__/arb-auto-3dex.test.d.ts +1 -0
- package/dist/__tests__/arb-auto-3dex.test.js +397 -0
- package/dist/__tests__/arb-history-stats.test.d.ts +1 -0
- package/dist/__tests__/arb-history-stats.test.js +176 -0
- package/dist/__tests__/arb-logic.test.d.ts +1 -0
- package/dist/__tests__/arb-logic.test.js +84 -0
- package/dist/__tests__/arb-manage.test.d.ts +1 -0
- package/dist/__tests__/arb-manage.test.js +253 -0
- package/dist/__tests__/arb-new-features.test.d.ts +1 -0
- package/dist/__tests__/arb-new-features.test.js +457 -0
- package/dist/__tests__/arb-sizing.test.d.ts +1 -0
- package/dist/__tests__/arb-sizing.test.js +48 -0
- package/dist/__tests__/arb-state.test.d.ts +1 -0
- package/dist/__tests__/arb-state.test.js +284 -0
- package/dist/__tests__/arb-userflow.test.d.ts +1 -0
- package/dist/__tests__/arb-userflow.test.js +945 -0
- package/dist/__tests__/arb-utils.test.d.ts +1 -0
- package/dist/__tests__/arb-utils.test.js +264 -0
- package/dist/__tests__/bot-conditions.test.d.ts +1 -0
- package/dist/__tests__/bot-conditions.test.js +341 -0
- package/dist/__tests__/client-id-tracker.test.d.ts +1 -0
- package/dist/__tests__/client-id-tracker.test.js +137 -0
- package/dist/__tests__/commands/new-atomic-commands.test.d.ts +1 -0
- package/dist/__tests__/commands/new-atomic-commands.test.js +502 -0
- package/dist/__tests__/commands/order-intent.test.d.ts +1 -0
- package/dist/__tests__/commands/order-intent.test.js +600 -0
- package/dist/__tests__/commands/trade-commands.test.d.ts +1 -0
- package/dist/__tests__/commands/trade-commands.test.js +821 -0
- package/dist/__tests__/config.test.d.ts +1 -0
- package/dist/__tests__/config.test.js +86 -0
- package/dist/__tests__/cross-chain-margin.test.d.ts +1 -0
- package/dist/__tests__/cross-chain-margin.test.js +287 -0
- package/dist/__tests__/dex-asset-map.test.d.ts +1 -0
- package/dist/__tests__/dex-asset-map.test.js +191 -0
- package/dist/__tests__/errors.test.d.ts +1 -0
- package/dist/__tests__/errors.test.js +110 -0
- package/dist/__tests__/event-stream.test.d.ts +1 -0
- package/dist/__tests__/event-stream.test.js +276 -0
- package/dist/__tests__/exchanges/interface.test.d.ts +1 -0
- package/dist/__tests__/exchanges/interface.test.js +132 -0
- package/dist/__tests__/exchanges/mock-adapter.d.ts +69 -0
- package/dist/__tests__/exchanges/mock-adapter.js +137 -0
- package/dist/__tests__/execution-log.test.d.ts +1 -0
- package/dist/__tests__/execution-log.test.js +106 -0
- package/dist/__tests__/funding-calc.test.d.ts +1 -0
- package/dist/__tests__/funding-calc.test.js +71 -0
- package/dist/__tests__/funding-history.test.d.ts +1 -0
- package/dist/__tests__/funding-history.test.js +343 -0
- package/dist/__tests__/funding-rates.test.d.ts +1 -0
- package/dist/__tests__/funding-rates.test.js +342 -0
- package/dist/__tests__/funding.test.d.ts +1 -0
- package/dist/__tests__/funding.test.js +173 -0
- package/dist/__tests__/gap-logic.test.d.ts +1 -0
- package/dist/__tests__/gap-logic.test.js +43 -0
- package/dist/__tests__/hip3-dex.test.d.ts +1 -0
- package/dist/__tests__/hip3-dex.test.js +234 -0
- package/dist/__tests__/integration/agent-features.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/agent-features.integration.test.js +553 -0
- package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +13 -0
- package/dist/__tests__/integration/atomic-commands.integration.test.js +246 -0
- package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge-simulation.integration.test.js +453 -0
- package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge-strict.integration.test.js +812 -0
- package/dist/__tests__/integration/bridge.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge.integration.test.js +309 -0
- package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/cli-e2e.integration.test.js +202 -0
- package/dist/__tests__/integration/dex-arb.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/dex-arb.integration.test.js +116 -0
- package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +13 -0
- package/dist/__tests__/integration/envelope-consistency.integration.test.js +205 -0
- package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/hip3-dex.integration.test.js +147 -0
- package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/hyperliquid.integration.test.js +79 -0
- package/dist/__tests__/integration/lighter.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/lighter.integration.test.js +53 -0
- package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +9 -0
- package/dist/__tests__/integration/new-commands-e2e.integration.test.js +236 -0
- package/dist/__tests__/integration/order-verification.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/order-verification.integration.test.js +321 -0
- package/dist/__tests__/integration/pacifica.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/pacifica.integration.test.js +75 -0
- package/dist/__tests__/integration/response-shapes.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/response-shapes.integration.test.js +278 -0
- package/dist/__tests__/liquidity.test.d.ts +1 -0
- package/dist/__tests__/liquidity.test.js +225 -0
- package/dist/__tests__/plan-executor.test.d.ts +1 -0
- package/dist/__tests__/plan-executor.test.js +314 -0
- package/dist/__tests__/position-history.test.d.ts +1 -0
- package/dist/__tests__/position-history.test.js +367 -0
- package/dist/__tests__/retry.test.d.ts +1 -0
- package/dist/__tests__/retry.test.js +310 -0
- package/dist/__tests__/risk-assessment.test.d.ts +1 -0
- package/dist/__tests__/risk-assessment.test.js +145 -0
- package/dist/__tests__/security-adversarial.test.d.ts +1 -0
- package/dist/__tests__/security-adversarial.test.js +574 -0
- package/dist/__tests__/strategies.test.d.ts +1 -0
- package/dist/__tests__/strategies.test.js +539 -0
- package/dist/__tests__/trade-execution.test.d.ts +1 -0
- package/dist/__tests__/trade-execution.test.js +129 -0
- package/dist/__tests__/trade-validator.test.d.ts +1 -0
- package/dist/__tests__/trade-validator.test.js +655 -0
- package/dist/__tests__/utils.test.d.ts +1 -0
- package/dist/__tests__/utils.test.js +76 -0
- package/dist/api/public/hyperliquid.d.ts +18 -0
- package/dist/api/public/hyperliquid.js +82 -0
- package/dist/api/public/index.d.ts +8 -0
- package/dist/api/public/index.js +8 -0
- package/dist/api/public/lighter.d.ts +24 -0
- package/dist/api/public/lighter.js +100 -0
- package/dist/api/public/pacifica.d.ts +17 -0
- package/dist/api/public/pacifica.js +54 -0
- package/dist/api/public/urls.d.ts +12 -0
- package/dist/api/public/urls.js +33 -0
- package/dist/arb/history-stats.d.ts +44 -0
- package/dist/arb/history-stats.js +135 -0
- package/dist/arb/index.d.ts +4 -0
- package/dist/arb/index.js +4 -0
- package/dist/arb/sizing.d.ts +23 -0
- package/dist/arb/sizing.js +96 -0
- package/dist/arb/state.d.ts +51 -0
- package/dist/arb/state.js +112 -0
- package/dist/arb/utils.d.ts +81 -0
- package/dist/arb/utils.js +267 -0
- package/dist/arb-history-stats.d.ts +5 -0
- package/dist/arb-history-stats.js +5 -0
- package/dist/arb-sizing.d.ts +5 -0
- package/dist/arb-sizing.js +5 -0
- package/dist/arb-state.d.ts +5 -0
- package/dist/arb-state.js +5 -0
- package/dist/arb-utils.d.ts +5 -0
- package/dist/arb-utils.js +5 -0
- package/dist/bot/conditions.d.ts +32 -0
- package/dist/bot/conditions.js +141 -0
- package/dist/bot/config.d.ts +76 -0
- package/dist/bot/config.js +160 -0
- package/dist/bot/engine.d.ts +8 -0
- package/dist/bot/engine.js +519 -0
- package/dist/bot/presets.d.ts +11 -0
- package/dist/bot/presets.js +296 -0
- package/dist/bridge-engine.d.ts +133 -0
- package/dist/bridge-engine.js +1487 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.js +99 -0
- package/dist/cli-spec.d.ts +50 -0
- package/dist/cli-spec.js +75 -0
- package/dist/client-id-tracker.d.ts +25 -0
- package/dist/client-id-tracker.js +76 -0
- package/dist/commands/account.d.ts +3 -0
- package/dist/commands/account.js +425 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.js +386 -0
- package/dist/commands/alert.d.ts +2 -0
- package/dist/commands/alert.js +421 -0
- package/dist/commands/analytics.d.ts +3 -0
- package/dist/commands/analytics.js +311 -0
- package/dist/commands/arb/index.d.ts +3 -0
- package/dist/commands/arb/index.js +921 -0
- package/dist/commands/arb-auto.d.ts +54 -0
- package/dist/commands/arb-auto.js +1328 -0
- package/dist/commands/arb-manage.d.ts +5 -0
- package/dist/commands/arb-manage.js +5 -0
- package/dist/commands/arb.d.ts +2 -0
- package/dist/commands/arb.js +347 -0
- package/dist/commands/backtest.d.ts +2 -0
- package/dist/commands/backtest.js +327 -0
- package/dist/commands/bot.d.ts +3 -0
- package/dist/commands/bot.js +412 -0
- package/dist/commands/bridge.d.ts +2 -0
- package/dist/commands/bridge.js +396 -0
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.js +176 -0
- package/dist/commands/deposit.d.ts +4 -0
- package/dist/commands/deposit.js +573 -0
- package/dist/commands/dex.d.ts +3 -0
- package/dist/commands/dex.js +114 -0
- package/dist/commands/env.d.ts +2 -0
- package/dist/commands/env.js +136 -0
- package/dist/commands/funding.d.ts +2 -0
- package/dist/commands/funding.js +347 -0
- package/dist/commands/gap.d.ts +2 -0
- package/dist/commands/gap.js +305 -0
- package/dist/commands/health.d.ts +2 -0
- package/dist/commands/health.js +67 -0
- package/dist/commands/history.d.ts +2 -0
- package/dist/commands/history.js +235 -0
- package/dist/commands/init.d.ts +15 -0
- package/dist/commands/init.js +266 -0
- package/dist/commands/jobs.d.ts +2 -0
- package/dist/commands/jobs.js +133 -0
- package/dist/commands/manage.d.ts +4 -0
- package/dist/commands/manage.js +309 -0
- package/dist/commands/market.d.ts +3 -0
- package/dist/commands/market.js +225 -0
- package/dist/commands/plan.d.ts +3 -0
- package/dist/commands/plan.js +95 -0
- package/dist/commands/portfolio.d.ts +3 -0
- package/dist/commands/portfolio.js +169 -0
- package/dist/commands/rebalance.d.ts +3 -0
- package/dist/commands/rebalance.js +293 -0
- package/dist/commands/risk.d.ts +3 -0
- package/dist/commands/risk.js +169 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.js +202 -0
- package/dist/commands/settings.d.ts +2 -0
- package/dist/commands/settings.js +102 -0
- package/dist/commands/stream.d.ts +5 -0
- package/dist/commands/stream.js +123 -0
- package/dist/commands/trade.d.ts +3 -0
- package/dist/commands/trade.js +1273 -0
- package/dist/commands/wallet.d.ts +14 -0
- package/dist/commands/wallet.js +602 -0
- package/dist/commands/withdraw.d.ts +3 -0
- package/dist/commands/withdraw.js +187 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +68 -0
- package/dist/cross-chain-margin.d.ts +46 -0
- package/dist/cross-chain-margin.js +107 -0
- package/dist/dashboard/server.d.ts +80 -0
- package/dist/dashboard/server.js +340 -0
- package/dist/dashboard/ui.d.ts +4 -0
- package/dist/dashboard/ui.js +538 -0
- package/dist/dashboard/ws-feeds.d.ts +29 -0
- package/dist/dashboard/ws-feeds.js +660 -0
- package/dist/dex-asset-map.d.ts +80 -0
- package/dist/dex-asset-map.js +201 -0
- package/dist/errors.d.ts +109 -0
- package/dist/errors.js +84 -0
- package/dist/event-stream.d.ts +25 -0
- package/dist/event-stream.js +168 -0
- package/dist/exchanges/hyperliquid.d.ts +212 -0
- package/dist/exchanges/hyperliquid.js +931 -0
- package/dist/exchanges/interface.d.ts +95 -0
- package/dist/exchanges/interface.js +5 -0
- package/dist/exchanges/lighter.d.ts +159 -0
- package/dist/exchanges/lighter.js +793 -0
- package/dist/exchanges/pacifica.d.ts +51 -0
- package/dist/exchanges/pacifica.js +248 -0
- package/dist/execution-log.d.ts +36 -0
- package/dist/execution-log.js +102 -0
- package/dist/funding/history.d.ts +63 -0
- package/dist/funding/history.js +266 -0
- package/dist/funding/index.d.ts +3 -0
- package/dist/funding/index.js +3 -0
- package/dist/funding/normalize.d.ts +39 -0
- package/dist/funding/normalize.js +66 -0
- package/dist/funding/rates.d.ts +45 -0
- package/dist/funding/rates.js +172 -0
- package/dist/funding-history.d.ts +5 -0
- package/dist/funding-history.js +5 -0
- package/dist/funding-rates.d.ts +5 -0
- package/dist/funding-rates.js +5 -0
- package/dist/funding.d.ts +5 -0
- package/dist/funding.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +458 -0
- package/dist/jobs.d.ts +37 -0
- package/dist/jobs.js +152 -0
- package/dist/liquidity.d.ts +34 -0
- package/dist/liquidity.js +100 -0
- package/dist/mcp-server.d.ts +9 -0
- package/dist/mcp-server.js +1206 -0
- package/dist/pacifica/client.d.ts +111 -0
- package/dist/pacifica/client.js +310 -0
- package/dist/pacifica/constants.d.ts +27 -0
- package/dist/pacifica/constants.js +47 -0
- package/dist/pacifica/deposit.d.ts +14 -0
- package/dist/pacifica/deposit.js +78 -0
- package/dist/pacifica/index.d.ts +6 -0
- package/dist/pacifica/index.js +11 -0
- package/dist/pacifica/signing.d.ts +49 -0
- package/dist/pacifica/signing.js +97 -0
- package/dist/pacifica/types/account.d.ts +42 -0
- package/dist/pacifica/types/account.js +1 -0
- package/dist/pacifica/types/index.d.ts +6 -0
- package/dist/pacifica/types/index.js +6 -0
- package/dist/pacifica/types/lake.d.ts +18 -0
- package/dist/pacifica/types/lake.js +1 -0
- package/dist/pacifica/types/market.d.ts +64 -0
- package/dist/pacifica/types/market.js +1 -0
- package/dist/pacifica/types/order.d.ts +92 -0
- package/dist/pacifica/types/order.js +1 -0
- package/dist/pacifica/types/position.d.ts +25 -0
- package/dist/pacifica/types/position.js +1 -0
- package/dist/pacifica/types/ws.d.ts +34 -0
- package/dist/pacifica/types/ws.js +41 -0
- package/dist/pacifica/ws-client.d.ts +42 -0
- package/dist/pacifica/ws-client.js +180 -0
- package/dist/plan-executor.d.ts +48 -0
- package/dist/plan-executor.js +280 -0
- package/dist/position-history.d.ts +68 -0
- package/dist/position-history.js +222 -0
- package/dist/rebalance.d.ts +64 -0
- package/dist/rebalance.js +142 -0
- package/dist/retry.d.ts +74 -0
- package/dist/retry.js +129 -0
- package/dist/risk.d.ts +48 -0
- package/dist/risk.js +156 -0
- package/dist/settings.d.ts +19 -0
- package/dist/settings.js +45 -0
- package/dist/shared-api.d.ts +5 -0
- package/dist/shared-api.js +5 -0
- package/dist/strategies/dca.d.ts +25 -0
- package/dist/strategies/dca.js +114 -0
- package/dist/strategies/funding-arb.d.ts +15 -0
- package/dist/strategies/funding-arb.js +281 -0
- package/dist/strategies/grid.d.ts +34 -0
- package/dist/strategies/grid.js +185 -0
- package/dist/strategies/trailing-stop.d.ts +17 -0
- package/dist/strategies/trailing-stop.js +121 -0
- package/dist/strategies/twap.d.ts +20 -0
- package/dist/strategies/twap.js +78 -0
- package/dist/trade-validator.d.ts +39 -0
- package/dist/trade-validator.js +154 -0
- package/dist/utils.d.ts +38 -0
- package/dist/utils.js +110 -0
- package/package.json +63 -0
- package/skills/perp-cli/SKILL.md +149 -0
- package/skills/perp-cli/references/commands.md +143 -0
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { printJson, formatUsd, jsonOk } from "../utils.js";
|
|
3
|
+
import { PacificaAdapter } from "../exchanges/pacifica.js";
|
|
4
|
+
import { logExecution } from "../execution-log.js";
|
|
5
|
+
const DEFAULT_RELAYER = "http://localhost:3100";
|
|
6
|
+
function getRelayerUrl() {
|
|
7
|
+
return process.env.PERP_RELAYER_URL || DEFAULT_RELAYER;
|
|
8
|
+
}
|
|
9
|
+
async function relayerAvailable() {
|
|
10
|
+
try {
|
|
11
|
+
const res = await fetch(`${getRelayerUrl()}/health`, { signal: AbortSignal.timeout(2000) });
|
|
12
|
+
return res.ok;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function registerDepositCommands(program, getAdapter, isJson, getNetwork) {
|
|
19
|
+
const deposit = program.command("deposit").description("Deposit funds into exchange accounts");
|
|
20
|
+
// ── Pacifica (Solana) ──
|
|
21
|
+
deposit
|
|
22
|
+
.command("pacifica <amount>")
|
|
23
|
+
.description("Deposit USDC into Pacifica")
|
|
24
|
+
.option("--no-relay", "Skip relayer, pay gas yourself")
|
|
25
|
+
.action(async (amount, opts) => {
|
|
26
|
+
const amountNum = parseFloat(amount);
|
|
27
|
+
if (isNaN(amountNum) || amountNum <= 0)
|
|
28
|
+
throw new Error("Invalid amount");
|
|
29
|
+
if (amountNum < 10)
|
|
30
|
+
throw new Error("Minimum deposit is 10 USDC");
|
|
31
|
+
// Try relayer first (gas-sponsored)
|
|
32
|
+
if (opts.relay && await relayerAvailable()) {
|
|
33
|
+
if (!isJson())
|
|
34
|
+
console.log(chalk.cyan(`\n Depositing $${formatUsd(amountNum)} via relayer (gasless)...\n`));
|
|
35
|
+
const adapter = await getAdapter();
|
|
36
|
+
if (!(adapter instanceof PacificaAdapter))
|
|
37
|
+
throw new Error("Requires --exchange pacifica");
|
|
38
|
+
try {
|
|
39
|
+
// 1. Get sponsored TX from relayer
|
|
40
|
+
const buildRes = await fetch(`${getRelayerUrl()}/deposit/pacifica/build`, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: { "Content-Type": "application/json" },
|
|
43
|
+
body: JSON.stringify({
|
|
44
|
+
userPubkey: adapter.keypair.publicKey.toBase58(),
|
|
45
|
+
amount: amountNum,
|
|
46
|
+
testnet: getNetwork() === "testnet",
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
if (!buildRes.ok) {
|
|
50
|
+
const err = await buildRes.json();
|
|
51
|
+
throw new Error(err.error || "Relayer build failed");
|
|
52
|
+
}
|
|
53
|
+
const { transaction, fee, netAmount } = (await buildRes.json());
|
|
54
|
+
if (!isJson()) {
|
|
55
|
+
console.log(` Fee: $${formatUsd(fee)} (gas sponsored)`);
|
|
56
|
+
console.log(` Net: $${formatUsd(netAmount)} to Pacifica\n`);
|
|
57
|
+
}
|
|
58
|
+
// 2. User signs the TX
|
|
59
|
+
const { Transaction } = await import("@solana/web3.js");
|
|
60
|
+
const tx = Transaction.from(Buffer.from(transaction, "base64"));
|
|
61
|
+
tx.partialSign(adapter.keypair);
|
|
62
|
+
const signed = tx.serialize().toString("base64");
|
|
63
|
+
// 3. Submit via relayer
|
|
64
|
+
const submitRes = await fetch(`${getRelayerUrl()}/deposit/pacifica/submit`, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers: { "Content-Type": "application/json" },
|
|
67
|
+
body: JSON.stringify({
|
|
68
|
+
signedTransaction: signed,
|
|
69
|
+
testnet: getNetwork() === "testnet",
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
if (!submitRes.ok) {
|
|
73
|
+
const err = await submitRes.json();
|
|
74
|
+
throw new Error(err.error || "Submit failed");
|
|
75
|
+
}
|
|
76
|
+
const result = (await submitRes.json());
|
|
77
|
+
logExecution({
|
|
78
|
+
type: "bridge", exchange: "pacifica", symbol: "USDC", side: "deposit",
|
|
79
|
+
size: String(amountNum), status: "success", dryRun: false,
|
|
80
|
+
meta: { action: "deposit", method: "relay", signature: result.signature },
|
|
81
|
+
});
|
|
82
|
+
if (isJson())
|
|
83
|
+
return printJson(jsonOk({ ...result, fee, netAmount, method: "relay" }));
|
|
84
|
+
console.log(chalk.green(` Deposit confirmed!`));
|
|
85
|
+
console.log(` Signature: ${result.signature}`);
|
|
86
|
+
console.log(chalk.gray(`\n Gas: FREE (relayer sponsored)\n`));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
logExecution({
|
|
91
|
+
type: "bridge", exchange: "pacifica", symbol: "USDC", side: "deposit",
|
|
92
|
+
size: String(amountNum), status: "failed", dryRun: false,
|
|
93
|
+
error: err instanceof Error ? err.message : String(err),
|
|
94
|
+
meta: { action: "deposit", method: "relay" },
|
|
95
|
+
});
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Fallback: direct deposit (user pays gas)
|
|
100
|
+
if (!isJson()) {
|
|
101
|
+
console.log(chalk.cyan(`\n Depositing $${formatUsd(amountNum)} USDC into Pacifica...\n`));
|
|
102
|
+
console.log(chalk.gray(" Requires: SOL for gas (~0.005 SOL) + USDC on Solana\n"));
|
|
103
|
+
}
|
|
104
|
+
const adapter = await getAdapter();
|
|
105
|
+
if (!(adapter instanceof PacificaAdapter))
|
|
106
|
+
throw new Error("Requires --exchange pacifica");
|
|
107
|
+
try {
|
|
108
|
+
const { Connection, Transaction } = await import("@solana/web3.js");
|
|
109
|
+
const { buildDepositInstruction } = await import("../pacifica/deposit.js");
|
|
110
|
+
const network = getNetwork();
|
|
111
|
+
const rpcUrl = network === "testnet" ? "https://api.devnet.solana.com" : "https://api.mainnet-beta.solana.com";
|
|
112
|
+
const connection = new Connection(rpcUrl, "confirmed");
|
|
113
|
+
const solBalance = await connection.getBalance(adapter.keypair.publicKey);
|
|
114
|
+
if (solBalance < 5_000_000) {
|
|
115
|
+
console.error(chalk.red(" Insufficient SOL for gas. Need ~0.005 SOL."));
|
|
116
|
+
console.error(chalk.gray(" Tip: Use relayer for gasless deposits (start relayer server)\n"));
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
// Check USDC balance before sending TX
|
|
120
|
+
const { PublicKey } = await import("@solana/web3.js");
|
|
121
|
+
const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
122
|
+
const tokenAccounts = await connection.getParsedTokenAccountsByOwner(adapter.keypair.publicKey, { mint: USDC_MINT });
|
|
123
|
+
const usdcBalance = tokenAccounts.value.length > 0
|
|
124
|
+
? tokenAccounts.value[0].account.data.parsed.info.tokenAmount.uiAmount ?? 0
|
|
125
|
+
: 0;
|
|
126
|
+
if (usdcBalance < amountNum) {
|
|
127
|
+
console.error(chalk.red(` Insufficient USDC on Solana. Have: $${formatUsd(usdcBalance)}, need: $${formatUsd(amountNum)}`));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
const ix = await buildDepositInstruction(adapter.keypair.publicKey, amountNum, network);
|
|
131
|
+
const tx = new Transaction().add(ix);
|
|
132
|
+
tx.feePayer = adapter.keypair.publicKey;
|
|
133
|
+
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
|
|
134
|
+
tx.sign(adapter.keypair);
|
|
135
|
+
const sig = await connection.sendRawTransaction(tx.serialize());
|
|
136
|
+
await connection.confirmTransaction(sig, "confirmed");
|
|
137
|
+
logExecution({
|
|
138
|
+
type: "bridge", exchange: "pacifica", symbol: "USDC", side: "deposit",
|
|
139
|
+
size: String(amountNum), status: "success", dryRun: false,
|
|
140
|
+
meta: { action: "deposit", method: "direct", signature: sig },
|
|
141
|
+
});
|
|
142
|
+
if (isJson())
|
|
143
|
+
return printJson(jsonOk({ signature: sig, amount: amountNum, method: "direct" }));
|
|
144
|
+
console.log(chalk.green(` Deposit confirmed!`));
|
|
145
|
+
console.log(` Amount: $${formatUsd(amountNum)} USDC`);
|
|
146
|
+
console.log(` Signature: ${sig}\n`);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
logExecution({
|
|
150
|
+
type: "bridge", exchange: "pacifica", symbol: "USDC", side: "deposit",
|
|
151
|
+
size: String(amountNum), status: "failed", dryRun: false,
|
|
152
|
+
error: err instanceof Error ? err.message : String(err),
|
|
153
|
+
meta: { action: "deposit", method: "direct" },
|
|
154
|
+
});
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
// ── Hyperliquid (Arbitrum — USDC transfer to Bridge2) ──
|
|
159
|
+
deposit
|
|
160
|
+
.command("hyperliquid <amount>")
|
|
161
|
+
.description("Deposit USDC into Hyperliquid (Arbitrum)")
|
|
162
|
+
.option("--no-relay", "Skip relayer, pay gas yourself")
|
|
163
|
+
.action(async (amount, opts) => {
|
|
164
|
+
const amountNum = parseFloat(amount);
|
|
165
|
+
if (isNaN(amountNum) || amountNum <= 0)
|
|
166
|
+
throw new Error("Invalid amount");
|
|
167
|
+
if (amountNum < 5)
|
|
168
|
+
throw new Error("Minimum deposit is $5 for Hyperliquid");
|
|
169
|
+
// Try relayer
|
|
170
|
+
if (opts.relay && await relayerAvailable()) {
|
|
171
|
+
if (!isJson())
|
|
172
|
+
console.log(chalk.cyan(`\n Depositing $${formatUsd(amountNum)} via relayer (gasless)...\n`));
|
|
173
|
+
const { ethers } = await import("ethers");
|
|
174
|
+
const { loadPrivateKey } = await import("../config.js");
|
|
175
|
+
const pk = await loadPrivateKey("hyperliquid");
|
|
176
|
+
const wallet = new ethers.Wallet(pk);
|
|
177
|
+
try {
|
|
178
|
+
const res = await fetch(`${getRelayerUrl()}/deposit/hyperliquid`, {
|
|
179
|
+
method: "POST",
|
|
180
|
+
headers: { "Content-Type": "application/json" },
|
|
181
|
+
body: JSON.stringify({ userAddress: wallet.address, amount: amountNum }),
|
|
182
|
+
});
|
|
183
|
+
const result = (await res.json());
|
|
184
|
+
if (!res.ok)
|
|
185
|
+
throw new Error(String(result.error || "Relayer failed"));
|
|
186
|
+
logExecution({
|
|
187
|
+
type: "bridge", exchange: "hyperliquid", symbol: "USDC", side: "deposit",
|
|
188
|
+
size: String(amountNum), status: "success", dryRun: false,
|
|
189
|
+
meta: { action: "deposit", method: "relay", txHash: String(result.txHash) },
|
|
190
|
+
});
|
|
191
|
+
if (isJson())
|
|
192
|
+
return printJson(jsonOk({ ...result, method: "relay" }));
|
|
193
|
+
console.log(chalk.green(` Deposit confirmed!`));
|
|
194
|
+
console.log(` TX Hash: ${result.txHash}`);
|
|
195
|
+
console.log(` Fee: $${formatUsd(Number(result.fee))}`);
|
|
196
|
+
console.log(` Net: $${formatUsd(Number(result.netAmount))}`);
|
|
197
|
+
console.log(chalk.gray(`\n Gas: FREE (relayer sponsored)\n`));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
logExecution({
|
|
202
|
+
type: "bridge", exchange: "hyperliquid", symbol: "USDC", side: "deposit",
|
|
203
|
+
size: String(amountNum), status: "failed", dryRun: false,
|
|
204
|
+
error: err instanceof Error ? err.message : String(err),
|
|
205
|
+
meta: { action: "deposit", method: "relay" },
|
|
206
|
+
});
|
|
207
|
+
throw err;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Direct: USDC.transfer() to HL Bridge2
|
|
211
|
+
if (!isJson()) {
|
|
212
|
+
console.log(chalk.cyan(`\n Depositing $${formatUsd(amountNum)} USDC into Hyperliquid...\n`));
|
|
213
|
+
console.log(chalk.gray(" Requires: ETH on Arbitrum for gas (~0.0001 ETH) + USDC on Arbitrum\n"));
|
|
214
|
+
}
|
|
215
|
+
const { ethers } = await import("ethers");
|
|
216
|
+
const { loadPrivateKey } = await import("../config.js");
|
|
217
|
+
const pk = await loadPrivateKey("hyperliquid");
|
|
218
|
+
const provider = new ethers.JsonRpcProvider("https://arb1.arbitrum.io/rpc");
|
|
219
|
+
const wallet = new ethers.Wallet(pk, provider);
|
|
220
|
+
const HL_BRIDGE = "0x2Df1c51E09aECF9cacB7bc98cB1742757f163dF7";
|
|
221
|
+
const USDC_ARB = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831";
|
|
222
|
+
// Check balances
|
|
223
|
+
const ethBal = await provider.getBalance(wallet.address);
|
|
224
|
+
if (ethBal < ethers.parseEther("0.0001")) {
|
|
225
|
+
console.error(chalk.red(" Insufficient ETH on Arbitrum for gas."));
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
const usdc = new ethers.Contract(USDC_ARB, [
|
|
229
|
+
"function transfer(address to, uint256 amount) returns (bool)",
|
|
230
|
+
"function balanceOf(address) view returns (uint256)",
|
|
231
|
+
], wallet);
|
|
232
|
+
const amountRaw = ethers.parseUnits(amount, 6);
|
|
233
|
+
const usdcBal = await usdc.balanceOf(wallet.address);
|
|
234
|
+
if (usdcBal < amountRaw) {
|
|
235
|
+
console.error(chalk.red(` Insufficient USDC. Have: $${formatUsd(Number(ethers.formatUnits(usdcBal, 6)))}`));
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
// Direct USDC transfer to HL Bridge2 (simplest method)
|
|
240
|
+
if (!isJson())
|
|
241
|
+
console.log(chalk.gray(" Sending USDC to Hyperliquid Bridge2..."));
|
|
242
|
+
const tx = await usdc.transfer(HL_BRIDGE, amountRaw);
|
|
243
|
+
const receipt = await tx.wait();
|
|
244
|
+
logExecution({
|
|
245
|
+
type: "bridge", exchange: "hyperliquid", symbol: "USDC", side: "deposit",
|
|
246
|
+
size: String(amountNum), status: "success", dryRun: false,
|
|
247
|
+
meta: { action: "deposit", method: "direct", txHash: receipt.hash },
|
|
248
|
+
});
|
|
249
|
+
if (isJson())
|
|
250
|
+
return printJson(jsonOk({ txHash: receipt.hash, amount: amountNum, method: "direct" }));
|
|
251
|
+
console.log(chalk.green(` Deposit confirmed!`));
|
|
252
|
+
console.log(` Amount: $${formatUsd(amountNum)} USDC`);
|
|
253
|
+
console.log(` TX Hash: ${receipt.hash}`);
|
|
254
|
+
console.log(chalk.gray(`\n Funds appear in ~1-3 minutes.\n`));
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
logExecution({
|
|
258
|
+
type: "bridge", exchange: "hyperliquid", symbol: "USDC", side: "deposit",
|
|
259
|
+
size: String(amountNum), status: "failed", dryRun: false,
|
|
260
|
+
error: err instanceof Error ? err.message : String(err),
|
|
261
|
+
meta: { action: "deposit", method: "direct" },
|
|
262
|
+
});
|
|
263
|
+
throw err;
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
// ── Lighter ──
|
|
267
|
+
const lighterDeposit = deposit.command("lighter").description("Deposit USDC into Lighter");
|
|
268
|
+
// Lighter via Ethereum L1 (min 1 USDC)
|
|
269
|
+
lighterDeposit
|
|
270
|
+
.command("ethereum <amount>")
|
|
271
|
+
.description("Deposit USDC via Ethereum L1 (min 1 USDC, gas: $3-10+)")
|
|
272
|
+
.option("--asset-id <id>", "Asset index (default: 2 = USDC)", "2")
|
|
273
|
+
.option("--route <type>", "Route: 0=perps, 1=spot (default: 0)", "0")
|
|
274
|
+
.action(async (amount, opts) => {
|
|
275
|
+
const amountNum = parseFloat(amount);
|
|
276
|
+
if (isNaN(amountNum) || amountNum <= 0)
|
|
277
|
+
throw new Error("Invalid amount");
|
|
278
|
+
if (amountNum < 1)
|
|
279
|
+
throw new Error("Minimum deposit via Ethereum is 1 USDC");
|
|
280
|
+
if (!isJson()) {
|
|
281
|
+
console.log(chalk.cyan(`\n Depositing $${formatUsd(amountNum)} USDC into Lighter via Ethereum L1...\n`));
|
|
282
|
+
console.log(chalk.yellow(" Warning: Ethereum L1 gas fees may be high ($3-10+)\n"));
|
|
283
|
+
}
|
|
284
|
+
const { ethers } = await import("ethers");
|
|
285
|
+
const { loadPrivateKey } = await import("../config.js");
|
|
286
|
+
const pk = await loadPrivateKey("lighter");
|
|
287
|
+
const provider = new ethers.JsonRpcProvider("https://eth.llamarpc.com");
|
|
288
|
+
const wallet = new ethers.Wallet(pk, provider);
|
|
289
|
+
const LIGHTER_CONTRACT = "0x3B4D794a66304f130a4db8f2551b0070dfcf5ca7";
|
|
290
|
+
const USDC_ETH = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
|
|
291
|
+
const usdc = new ethers.Contract(USDC_ETH, [
|
|
292
|
+
"function approve(address spender, uint256 amount) returns (bool)",
|
|
293
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
294
|
+
"function balanceOf(address) view returns (uint256)",
|
|
295
|
+
], wallet);
|
|
296
|
+
const lighter = new ethers.Contract(LIGHTER_CONTRACT, [
|
|
297
|
+
"function deposit(address _to, uint8 _assetIndex, uint8 _routeType, uint256 _amount) payable",
|
|
298
|
+
], wallet);
|
|
299
|
+
const amountRaw = ethers.parseUnits(amount, 6);
|
|
300
|
+
// Check ETH balance for gas
|
|
301
|
+
const ethBal = await provider.getBalance(wallet.address);
|
|
302
|
+
if (ethBal < ethers.parseEther("0.003")) {
|
|
303
|
+
console.error(chalk.red(" Insufficient ETH on Ethereum for gas. Need ~0.003 ETH ($3+)."));
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
// Check USDC balance
|
|
307
|
+
const usdcBal = await usdc.balanceOf(wallet.address);
|
|
308
|
+
if (usdcBal < amountRaw) {
|
|
309
|
+
console.error(chalk.red(` Insufficient USDC on Ethereum. Have: $${formatUsd(Number(ethers.formatUnits(usdcBal, 6)))}`));
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
// Approve if needed
|
|
313
|
+
const allowance = await usdc.allowance(wallet.address, LIGHTER_CONTRACT);
|
|
314
|
+
if (allowance < amountRaw) {
|
|
315
|
+
if (!isJson())
|
|
316
|
+
console.log(chalk.gray(" Approving USDC for Lighter..."));
|
|
317
|
+
const approveTx = await usdc.approve(LIGHTER_CONTRACT, amountRaw);
|
|
318
|
+
await approveTx.wait();
|
|
319
|
+
if (!isJson())
|
|
320
|
+
console.log(chalk.gray(" Approved."));
|
|
321
|
+
}
|
|
322
|
+
// Call deposit(address _to, uint8 _assetIndex, uint8 _routeType, uint256 _amount)
|
|
323
|
+
if (!isJson())
|
|
324
|
+
console.log(chalk.gray(" Calling deposit()..."));
|
|
325
|
+
try {
|
|
326
|
+
const tx = await lighter.deposit(wallet.address, parseInt(opts.assetId), parseInt(opts.route), amountRaw);
|
|
327
|
+
const receipt = await tx.wait();
|
|
328
|
+
logExecution({
|
|
329
|
+
type: "bridge", exchange: "lighter", symbol: "USDC", side: "deposit",
|
|
330
|
+
size: String(amountNum), status: "success", dryRun: false,
|
|
331
|
+
meta: { action: "deposit", method: "ethereum", txHash: receipt.hash },
|
|
332
|
+
});
|
|
333
|
+
if (isJson())
|
|
334
|
+
return printJson(jsonOk({ txHash: receipt.hash, amount: amountNum, chain: "ethereum", method: "direct" }));
|
|
335
|
+
console.log(chalk.green(`\n Deposit confirmed!`));
|
|
336
|
+
console.log(` Amount: $${formatUsd(amountNum)} USDC`);
|
|
337
|
+
console.log(` TX Hash: ${receipt.hash}`);
|
|
338
|
+
console.log(chalk.gray(`\n Funds appear in your Lighter perps account shortly.\n`));
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
logExecution({
|
|
342
|
+
type: "bridge", exchange: "lighter", symbol: "USDC", side: "deposit",
|
|
343
|
+
size: String(amountNum), status: "failed", dryRun: false,
|
|
344
|
+
error: err instanceof Error ? err.message : String(err),
|
|
345
|
+
meta: { action: "deposit", method: "ethereum" },
|
|
346
|
+
});
|
|
347
|
+
throw err;
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
// Lighter via CCTP (Arbitrum, Base, Avalanche — min 5 USDC)
|
|
351
|
+
const CCTP_CHAINS = {
|
|
352
|
+
arbitrum: { chainId: 42161, rpc: "https://arb1.arbitrum.io/rpc", usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", name: "Arbitrum" },
|
|
353
|
+
base: { chainId: 8453, rpc: "https://mainnet.base.org", usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", name: "Base" },
|
|
354
|
+
avalanche: { chainId: 43114, rpc: "https://api.avax.network/ext/bc/C/rpc", usdc: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", name: "Avalanche" },
|
|
355
|
+
};
|
|
356
|
+
lighterDeposit
|
|
357
|
+
.command("cctp <chain> <amount>")
|
|
358
|
+
.description("Deposit USDC via CCTP (arbitrum, base, avalanche — min 5 USDC)")
|
|
359
|
+
.action(async (chain, amount) => {
|
|
360
|
+
const amountNum = parseFloat(amount);
|
|
361
|
+
if (isNaN(amountNum) || amountNum <= 0)
|
|
362
|
+
throw new Error("Invalid amount");
|
|
363
|
+
if (amountNum < 5)
|
|
364
|
+
throw new Error("Minimum CCTP deposit is 5 USDC");
|
|
365
|
+
const chainKey = chain.toLowerCase();
|
|
366
|
+
const chainInfo = CCTP_CHAINS[chainKey];
|
|
367
|
+
if (!chainInfo)
|
|
368
|
+
throw new Error(`Unsupported chain: ${chain}. Use: arbitrum, base, avalanche`);
|
|
369
|
+
if (!isJson())
|
|
370
|
+
console.log(chalk.cyan(`\n Depositing $${formatUsd(amountNum)} USDC into Lighter via CCTP (${chainInfo.name})...\n`));
|
|
371
|
+
const { ethers } = await import("ethers");
|
|
372
|
+
const { loadPrivateKey } = await import("../config.js");
|
|
373
|
+
const pk = await loadPrivateKey("lighter");
|
|
374
|
+
const provider = new ethers.JsonRpcProvider(chainInfo.rpc);
|
|
375
|
+
const wallet = new ethers.Wallet(pk, provider);
|
|
376
|
+
// 1. Create intent address via Lighter API
|
|
377
|
+
if (!isJson())
|
|
378
|
+
console.log(chalk.gray(" Creating intent address..."));
|
|
379
|
+
const intentRes = await fetch("https://mainnet.zklighter.elliot.ai/api/v1/createIntentAddress", {
|
|
380
|
+
method: "POST",
|
|
381
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
382
|
+
body: new URLSearchParams({
|
|
383
|
+
chain_id: String(chainInfo.chainId),
|
|
384
|
+
from_addr: wallet.address,
|
|
385
|
+
amount: "0",
|
|
386
|
+
is_external_deposit: "true",
|
|
387
|
+
}),
|
|
388
|
+
});
|
|
389
|
+
if (!intentRes.ok) {
|
|
390
|
+
const err = await intentRes.text();
|
|
391
|
+
throw new Error(`Failed to create intent address: ${err}`);
|
|
392
|
+
}
|
|
393
|
+
const intentData = (await intentRes.json());
|
|
394
|
+
const intentAddress = String(intentData.intent_address || intentData.address || "");
|
|
395
|
+
if (!intentAddress)
|
|
396
|
+
throw new Error("No intent address returned");
|
|
397
|
+
if (!isJson())
|
|
398
|
+
console.log(` Intent address: ${intentAddress}`);
|
|
399
|
+
// 2. Check gas + USDC balance
|
|
400
|
+
const gasBal = await provider.getBalance(wallet.address);
|
|
401
|
+
if (gasBal < ethers.parseEther("0.00005")) {
|
|
402
|
+
console.error(chalk.red(` Insufficient gas token on ${chainInfo.name}. Need native token for gas (~$0.01).`));
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
const usdc = new ethers.Contract(chainInfo.usdc, [
|
|
406
|
+
"function transfer(address to, uint256 amount) returns (bool)",
|
|
407
|
+
"function balanceOf(address) view returns (uint256)",
|
|
408
|
+
], wallet);
|
|
409
|
+
const amountRaw = ethers.parseUnits(amount, 6);
|
|
410
|
+
const usdcBal = await usdc.balanceOf(wallet.address);
|
|
411
|
+
if (usdcBal < amountRaw) {
|
|
412
|
+
console.error(chalk.red(` Insufficient USDC on ${chainInfo.name}. Have: $${formatUsd(Number(ethers.formatUnits(usdcBal, 6)))}`));
|
|
413
|
+
process.exit(1);
|
|
414
|
+
}
|
|
415
|
+
// 3. Transfer USDC to intent address
|
|
416
|
+
if (!isJson())
|
|
417
|
+
console.log(chalk.gray(` Sending USDC to intent address on ${chainInfo.name}...`));
|
|
418
|
+
try {
|
|
419
|
+
const tx = await usdc.transfer(intentAddress, amountRaw);
|
|
420
|
+
const receipt = await tx.wait();
|
|
421
|
+
logExecution({
|
|
422
|
+
type: "bridge", exchange: "lighter", symbol: "USDC", side: "deposit",
|
|
423
|
+
size: String(amountNum), status: "success", dryRun: false,
|
|
424
|
+
meta: { action: "deposit", method: "cctp", chain: chainKey, txHash: receipt.hash },
|
|
425
|
+
});
|
|
426
|
+
if (isJson())
|
|
427
|
+
return printJson(jsonOk({ txHash: receipt.hash, amount: amountNum, chain: chainKey, intentAddress, method: "cctp" }));
|
|
428
|
+
console.log(chalk.green(`\n USDC sent to intent address!`));
|
|
429
|
+
console.log(` Amount: $${formatUsd(amountNum)} USDC`);
|
|
430
|
+
console.log(` TX Hash: ${receipt.hash}`);
|
|
431
|
+
console.log(` Chain: ${chainInfo.name}`);
|
|
432
|
+
console.log(chalk.gray(`\n CCTP bridging takes ~1-3 minutes. Funds will appear in Lighter automatically.\n`));
|
|
433
|
+
}
|
|
434
|
+
catch (err) {
|
|
435
|
+
logExecution({
|
|
436
|
+
type: "bridge", exchange: "lighter", symbol: "USDC", side: "deposit",
|
|
437
|
+
size: String(amountNum), status: "failed", dryRun: false,
|
|
438
|
+
error: err instanceof Error ? err.message : String(err),
|
|
439
|
+
meta: { action: "deposit", method: "cctp", chain: chainKey },
|
|
440
|
+
});
|
|
441
|
+
throw err;
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
// Lighter deposit info
|
|
445
|
+
lighterDeposit
|
|
446
|
+
.command("info")
|
|
447
|
+
.description("Show Lighter deposit routes & minimums")
|
|
448
|
+
.action(async () => {
|
|
449
|
+
if (isJson()) {
|
|
450
|
+
return printJson(jsonOk({
|
|
451
|
+
routes: {
|
|
452
|
+
ethereum: { min: "1 USDC", gas: "$3-10+", method: "deposit() on L1 contract" },
|
|
453
|
+
arbitrum: { min: "5 USDC", gas: "~$0.01", method: "CCTP via intent address" },
|
|
454
|
+
base: { min: "5 USDC", gas: "~$0.01", method: "CCTP via intent address" },
|
|
455
|
+
avalanche: { min: "5 USDC", gas: "~$0.01", method: "CCTP via intent address" },
|
|
456
|
+
},
|
|
457
|
+
contract: "0x3B4D794a66304f130a4db8f2551b0070dfcf5ca7",
|
|
458
|
+
}));
|
|
459
|
+
}
|
|
460
|
+
console.log(chalk.cyan.bold("\n Lighter Deposit Routes\n"));
|
|
461
|
+
console.log(chalk.white.bold(" Ethereum L1") + chalk.gray(" (direct)"));
|
|
462
|
+
console.log(` Min: 1 USDC | Gas: $3-10+`);
|
|
463
|
+
console.log(` Command: ${chalk.green("perp deposit lighter ethereum <amount>")}`);
|
|
464
|
+
console.log(chalk.white.bold("\n Arbitrum") + chalk.gray(" (CCTP)"));
|
|
465
|
+
console.log(` Min: 5 USDC | Gas: ~$0.01`);
|
|
466
|
+
console.log(` Command: ${chalk.green("perp deposit lighter cctp arbitrum <amount>")}`);
|
|
467
|
+
console.log(chalk.white.bold("\n Base") + chalk.gray(" (CCTP)"));
|
|
468
|
+
console.log(` Min: 5 USDC | Gas: ~$0.01`);
|
|
469
|
+
console.log(` Command: ${chalk.green("perp deposit lighter cctp base <amount>")}`);
|
|
470
|
+
console.log(chalk.white.bold("\n Avalanche") + chalk.gray(" (CCTP)"));
|
|
471
|
+
console.log(` Min: 5 USDC | Gas: ~$0.01`);
|
|
472
|
+
console.log(` Command: ${chalk.green("perp deposit lighter cctp avalanche <amount>")}`);
|
|
473
|
+
console.log();
|
|
474
|
+
});
|
|
475
|
+
// ── CCTP Bridge ──
|
|
476
|
+
deposit
|
|
477
|
+
.command("bridge")
|
|
478
|
+
.description("Bridge USDC between chains via CCTP V2")
|
|
479
|
+
.requiredOption("--from <chain>", "Source chain (arbitrum, ethereum)")
|
|
480
|
+
.requiredOption("--to <chain>", "Destination chain (arbitrum, ethereum, solana)")
|
|
481
|
+
.requiredOption("--amount <amount>", "USDC amount")
|
|
482
|
+
.requiredOption("--recipient <address>", "Recipient address on destination chain")
|
|
483
|
+
.action(async (opts) => {
|
|
484
|
+
const amountNum = parseFloat(opts.amount);
|
|
485
|
+
if (isNaN(amountNum) || amountNum <= 0)
|
|
486
|
+
throw new Error("Invalid amount");
|
|
487
|
+
if (await relayerAvailable()) {
|
|
488
|
+
if (!isJson())
|
|
489
|
+
console.log(chalk.cyan(`\n Bridging $${formatUsd(amountNum)} USDC via CCTP V2 (${opts.from} → ${opts.to})...\n`));
|
|
490
|
+
const res = await fetch(`${getRelayerUrl()}/bridge/cctp`, {
|
|
491
|
+
method: "POST",
|
|
492
|
+
headers: { "Content-Type": "application/json" },
|
|
493
|
+
body: JSON.stringify({
|
|
494
|
+
fromChain: opts.from,
|
|
495
|
+
toChain: opts.to,
|
|
496
|
+
amount: amountNum,
|
|
497
|
+
recipient: opts.recipient,
|
|
498
|
+
}),
|
|
499
|
+
});
|
|
500
|
+
const result = (await res.json());
|
|
501
|
+
if (!res.ok)
|
|
502
|
+
throw new Error(String(result.error || "CCTP bridge failed"));
|
|
503
|
+
if (isJson())
|
|
504
|
+
return printJson(jsonOk(result));
|
|
505
|
+
console.log(chalk.green(` Burn TX submitted!`));
|
|
506
|
+
console.log(` TX Hash: ${result.txHash}`);
|
|
507
|
+
console.log(` Message Hash: ${result.messageHash}`);
|
|
508
|
+
console.log(chalk.gray(`\n Waiting for Circle attestation (~1-3 min)...`));
|
|
509
|
+
console.log(chalk.gray(` Check: perp deposit bridge-status --hash ${result.messageHash}\n`));
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
console.error(chalk.red("\n CCTP bridge requires relayer server."));
|
|
513
|
+
console.error(chalk.gray(" Start: cd packages/relayer && pnpm start\n"));
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
deposit
|
|
517
|
+
.command("bridge-status")
|
|
518
|
+
.description("Check CCTP bridge status")
|
|
519
|
+
.requiredOption("--hash <messageHash>", "Message hash from bridge TX")
|
|
520
|
+
.action(async (opts) => {
|
|
521
|
+
const res = await fetch(`${getRelayerUrl()}/bridge/cctp/status/${opts.hash}`);
|
|
522
|
+
const result = (await res.json());
|
|
523
|
+
if (isJson())
|
|
524
|
+
return printJson(jsonOk(result));
|
|
525
|
+
console.log(chalk.cyan.bold("\n CCTP Bridge Status\n"));
|
|
526
|
+
console.log(` Status: ${result.status === "complete" ? chalk.green("complete") : chalk.yellow(String(result.status))}`);
|
|
527
|
+
if (result.attestation)
|
|
528
|
+
console.log(` Attestation: ${chalk.gray("received")}`);
|
|
529
|
+
console.log();
|
|
530
|
+
});
|
|
531
|
+
// ── Info ──
|
|
532
|
+
deposit
|
|
533
|
+
.command("info")
|
|
534
|
+
.description("Show deposit instructions & gas requirements")
|
|
535
|
+
.action(async () => {
|
|
536
|
+
const hasRelay = await relayerAvailable();
|
|
537
|
+
if (isJson()) {
|
|
538
|
+
return printJson(jsonOk({
|
|
539
|
+
relayer: hasRelay ? getRelayerUrl() : null,
|
|
540
|
+
exchanges: {
|
|
541
|
+
pacifica: { chain: "Solana", token: "USDC", min: 10, gas: "SOL ~0.005", method: "on-chain program" },
|
|
542
|
+
hyperliquid: { chain: "Arbitrum", token: "USDC", min: 5, gas: "ETH ~0.0001", method: "USDC transfer to Bridge2" },
|
|
543
|
+
lighter: { chain: "Ethereum L1 / Arbitrum / Base / Avalanche", token: "USDC", min: "1 (L1) or 5 (CCTP)", gas: "$3-10 (L1), ~$0.01 (CCTP)", method: "deposit() or CCTP intent" },
|
|
544
|
+
},
|
|
545
|
+
}));
|
|
546
|
+
}
|
|
547
|
+
console.log(chalk.cyan.bold("\n Deposit Instructions\n"));
|
|
548
|
+
if (hasRelay) {
|
|
549
|
+
console.log(chalk.green(" ✓ Relayer available — gas-free deposits!\n"));
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
console.log(chalk.yellow(" ⚠ Relayer offline — deposits will cost gas.\n"));
|
|
553
|
+
console.log(chalk.gray(" Start relayer: cd packages/relayer && pnpm start\n"));
|
|
554
|
+
}
|
|
555
|
+
console.log(chalk.white.bold(" Pacifica") + chalk.gray(" (Solana)"));
|
|
556
|
+
console.log(` Token: USDC | Min: $10 | Gas: ${hasRelay ? chalk.green("FREE") : "SOL ~0.005"}`);
|
|
557
|
+
console.log(` Command: ${chalk.green("perp deposit pacifica <amount>")}`);
|
|
558
|
+
console.log(chalk.white.bold("\n Hyperliquid") + chalk.gray(" (Arbitrum)"));
|
|
559
|
+
console.log(` Token: USDC | Min: $5 | Gas: ${hasRelay ? chalk.green("FREE") : "ETH ~0.0001"}`);
|
|
560
|
+
console.log(` Method: USDC transfer to Bridge2`);
|
|
561
|
+
console.log(` Command: ${chalk.green("perp deposit hyperliquid <amount>")}`);
|
|
562
|
+
console.log(chalk.white.bold("\n Lighter") + chalk.gray(" (Ethereum L1 / CCTP)"));
|
|
563
|
+
console.log(` Token: USDC | Min: 1 (L1), 5 (CCTP)`);
|
|
564
|
+
console.log(` L1: ${chalk.green("perp deposit lighter ethereum <amount>")} — gas $3-10+`);
|
|
565
|
+
console.log(` CCTP: ${chalk.green("perp deposit lighter cctp <chain> <amount>")} — gas ~$0.01`);
|
|
566
|
+
console.log(` Chains: arbitrum, base, avalanche`);
|
|
567
|
+
console.log(` Info: ${chalk.green("perp deposit lighter info")}`);
|
|
568
|
+
console.log(chalk.white.bold("\n CCTP Bridge") + chalk.gray(" (Cross-chain USDC)"));
|
|
569
|
+
console.log(` Routes: Arbitrum ↔ Ethereum ↔ Solana`);
|
|
570
|
+
console.log(` Command: ${chalk.green("perp deposit bridge --from arbitrum --to solana --amount 100 --recipient <addr>")}`);
|
|
571
|
+
console.log(chalk.gray("\n Use --no-relay to skip relayer and pay gas yourself.\n"));
|
|
572
|
+
});
|
|
573
|
+
}
|