perp-cli 0.12.17 → 0.14.0
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 +247 -27
- package/dist/_stderr-filter.js +0 -1
- package/dist/agent-wallet/lighter-keystore.d.ts +11 -0
- package/dist/agent-wallet/lighter-keystore.js +92 -10
- package/dist/agent-wallet/passphrase.d.ts +1 -1
- package/dist/agent-wallet/passphrase.js +16 -3
- package/dist/agent-wallet/store.d.ts +37 -4
- package/dist/agent-wallet/store.js +132 -0
- package/dist/agent-wallet/venue-keystore.d.ts +14 -0
- package/dist/agent-wallet/venue-keystore.js +121 -0
- package/dist/api/public/hyperliquid.js +4 -4
- package/dist/api/public/lighter.js +5 -5
- package/dist/api/public/pacifica.d.ts +0 -1
- package/dist/api/public/pacifica.js +4 -13
- package/dist/api/public/urls.d.ts +9 -1
- package/dist/api/public/urls.js +35 -13
- package/dist/arb/file-lock.d.ts +11 -0
- package/dist/arb/file-lock.js +74 -0
- package/dist/arb/index.d.ts +2 -2
- package/dist/arb/index.js +2 -2
- package/dist/arb/pending-entry.d.ts +32 -0
- package/dist/arb/pending-entry.js +77 -0
- package/dist/arb/sizing.d.ts +6 -13
- package/dist/arb/sizing.js +12 -51
- package/dist/arb/state.d.ts +45 -12
- package/dist/arb/state.js +214 -42
- package/dist/arb/utils.d.ts +20 -6
- package/dist/arb/utils.js +43 -39
- package/dist/bot/conditions.d.ts +4 -4
- package/dist/bot/conditions.js +11 -6
- package/dist/bot/config.d.ts +17 -1
- package/dist/bot/config.js +48 -12
- package/dist/bot/engine.d.ts +61 -2
- package/dist/bot/engine.js +610 -144
- package/dist/bot/fill-size.d.ts +7 -0
- package/dist/bot/fill-size.js +18 -0
- package/dist/bot/index.d.ts +1 -1
- package/dist/bot/index.js +1 -1
- package/dist/bot/perp-order.d.ts +99 -0
- package/dist/bot/perp-order.js +299 -0
- package/dist/bot/presets.d.ts +7 -0
- package/dist/bot/presets.js +17 -0
- package/dist/bot/quarantine.d.ts +75 -0
- package/dist/bot/quarantine.js +190 -0
- package/dist/bot/quote-rules.d.ts +23 -0
- package/dist/bot/quote-rules.js +69 -0
- package/dist/bot/strategies/avellaneda-mm.d.ts +32 -2
- package/dist/bot/strategies/avellaneda-mm.js +72 -70
- package/dist/bot/strategies/basis-arb.js +2 -0
- package/dist/bot/strategies/dca-strategy.d.ts +2 -0
- package/dist/bot/strategies/dca-strategy.js +5 -0
- package/dist/bot/strategies/funding-arb-strategy.d.ts +43 -3
- package/dist/bot/strategies/funding-arb-strategy.js +571 -380
- package/dist/bot/strategies/funding-arb-utils.d.ts +48 -23
- package/dist/bot/strategies/funding-arb-utils.js +159 -195
- package/dist/bot/strategies/funding-arb-v2.d.ts +32 -0
- package/dist/bot/strategies/funding-arb-v2.js +258 -47
- package/dist/bot/strategies/funding-auto.js +215 -194
- package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +14 -0
- package/dist/bot/strategies/spot-perp-arb-strategy.js +217 -50
- package/dist/bot/strategies/spot-perp-executor.d.ts +73 -19
- package/dist/bot/strategies/spot-perp-executor.js +319 -120
- package/dist/bot/strategies/spot-perp-state.d.ts +43 -0
- package/dist/bot/strategies/spot-perp-state.js +70 -0
- package/dist/bot/strategy-types.d.ts +6 -0
- package/dist/bot/tui/BotDashboard.d.ts +7 -2
- package/dist/bot/tui/BotDashboard.js +6 -3
- package/dist/bridge-engine.d.ts +120 -19
- package/dist/bridge-engine.js +710 -422
- package/dist/bridge-signers.d.ts +54 -0
- package/dist/bridge-signers.js +161 -0
- package/dist/cache.d.ts +4 -1
- package/dist/cache.js +11 -0
- package/dist/cli-flags.d.ts +50 -0
- package/dist/cli-flags.js +104 -0
- package/dist/cli-program.d.ts +35 -0
- package/dist/cli-program.js +286 -0
- package/dist/cli-spec.js +1 -1
- package/dist/client-id-tracker.js +11 -2
- package/dist/commands/account.d.ts +7 -1
- package/dist/commands/account.js +70 -92
- package/dist/commands/agent-import.d.ts +114 -0
- package/dist/commands/agent-import.js +794 -0
- package/dist/commands/agent.d.ts +232 -2
- package/dist/commands/agent.js +1858 -955
- package/dist/commands/alerts.d.ts +1 -0
- package/dist/commands/alerts.js +31 -28
- package/dist/commands/arb/index.d.ts +60 -1
- package/dist/commands/arb/index.js +381 -384
- package/dist/commands/arb-auto.d.ts +132 -4
- package/dist/commands/arb-auto.js +1474 -967
- package/dist/commands/arb.d.ts +3 -1
- package/dist/commands/arb.js +62 -30
- package/dist/commands/bot.js +127 -47
- package/dist/commands/bridge.d.ts +2 -1
- package/dist/commands/bridge.js +339 -192
- package/dist/commands/builder.d.ts +55 -0
- package/dist/commands/builder.js +387 -0
- package/dist/commands/funds-onchain.d.ts +32 -0
- package/dist/commands/funds-onchain.js +286 -0
- package/dist/commands/funds.d.ts +25 -0
- package/dist/commands/funds.js +939 -245
- package/dist/commands/history.js +72 -45
- package/dist/commands/init.d.ts +107 -15
- package/dist/commands/init.js +1116 -249
- package/dist/commands/jobs.d.ts +2 -1
- package/dist/commands/jobs.js +77 -15
- package/dist/commands/manage.js +136 -91
- package/dist/commands/market.d.ts +4 -1
- package/dist/commands/market.js +35 -32
- package/dist/commands/outcome.d.ts +10 -0
- package/dist/commands/outcome.js +396 -0
- package/dist/commands/plan.js +52 -12
- package/dist/commands/portfolio.d.ts +1 -0
- package/dist/commands/portfolio.js +60 -72
- package/dist/commands/rebalance.d.ts +2 -1
- package/dist/commands/rebalance.js +33 -43
- package/dist/commands/risk.js +46 -19
- package/dist/commands/settings.d.ts +1 -0
- package/dist/commands/settings.js +246 -51
- package/dist/commands/trade.d.ts +50 -0
- package/dist/commands/trade.js +1943 -695
- package/dist/commands/wallet.d.ts +30 -1
- package/dist/commands/wallet.js +316 -337
- package/dist/config.d.ts +60 -9
- package/dist/config.js +252 -82
- package/dist/constants.d.ts +29 -4
- package/dist/constants.js +62 -20
- package/dist/dashboard/server.d.ts +2 -0
- package/dist/dashboard/server.js +9 -6
- package/dist/dashboard/ui.js +6 -4
- package/dist/dashboard/ws-feeds.js +20 -8
- package/dist/dex-asset-map.js +5 -3
- package/dist/errors.d.ts +122 -0
- package/dist/errors.js +194 -0
- package/dist/event-stream.js +75 -19
- package/dist/evm-master-tx.d.ts +59 -0
- package/dist/evm-master-tx.js +154 -0
- package/dist/exchanges/adapter-network.d.ts +10 -0
- package/dist/exchanges/adapter-network.js +10 -0
- package/dist/exchanges/arcus/adapter.d.ts +196 -0
- package/dist/exchanges/arcus/adapter.js +1092 -0
- package/dist/exchanges/arcus/agent.d.ts +55 -0
- package/dist/exchanges/arcus/agent.js +188 -0
- package/dist/exchanges/arcus/client.d.ts +63 -0
- package/dist/exchanges/arcus/client.js +285 -0
- package/dist/exchanges/arcus/public.d.ts +65 -0
- package/dist/exchanges/arcus/public.js +138 -0
- package/dist/exchanges/arcus/signing.d.ts +176 -0
- package/dist/exchanges/arcus/signing.js +288 -0
- package/dist/exchanges/arcus/spot-chain.d.ts +60 -0
- package/dist/exchanges/arcus/spot-chain.js +241 -0
- package/dist/exchanges/arcus/spot-router.d.ts +177 -0
- package/dist/exchanges/arcus/spot-router.js +183 -0
- package/dist/exchanges/arcus/spot.d.ts +109 -0
- package/dist/exchanges/arcus/spot.js +645 -0
- package/dist/exchanges/aster-funds.d.ts +127 -0
- package/dist/exchanges/aster-funds.js +252 -0
- package/dist/exchanges/aster-nonce.d.ts +2 -0
- package/dist/exchanges/aster-nonce.js +107 -0
- package/dist/exchanges/aster-spot.d.ts +117 -0
- package/dist/exchanges/aster-spot.js +686 -0
- package/dist/exchanges/aster-typed-data.d.ts +133 -7
- package/dist/exchanges/aster-typed-data.js +121 -7
- package/dist/exchanges/aster.d.ts +219 -6
- package/dist/exchanges/aster.js +1023 -153
- package/dist/exchanges/builder-fee.d.ts +254 -0
- package/dist/exchanges/builder-fee.js +421 -0
- package/dist/exchanges/canonical-symbols.d.ts +32 -0
- package/dist/exchanges/canonical-symbols.js +96 -0
- package/dist/exchanges/capabilities.d.ts +7 -1
- package/dist/exchanges/capabilities.js +7 -0
- package/dist/exchanges/hl-signing.d.ts +165 -0
- package/dist/exchanges/hl-signing.js +158 -0
- package/dist/exchanges/hyperliquid-outcome.d.ts +211 -0
- package/dist/exchanges/hyperliquid-outcome.js +646 -0
- package/dist/exchanges/hyperliquid-spot.d.ts +44 -5
- package/dist/exchanges/hyperliquid-spot.js +308 -117
- package/dist/exchanges/hyperliquid.d.ts +462 -57
- package/dist/exchanges/hyperliquid.js +1575 -449
- package/dist/exchanges/index.d.ts +6 -0
- package/dist/exchanges/index.js +4 -0
- package/dist/exchanges/interface.d.ts +63 -10
- package/dist/exchanges/interface.js +18 -3
- package/dist/exchanges/ioc-outcome.d.ts +44 -0
- package/dist/exchanges/ioc-outcome.js +57 -0
- package/dist/exchanges/legacy-trading-v2.d.ts +39 -0
- package/dist/exchanges/legacy-trading-v2.js +940 -0
- package/dist/exchanges/lighter-public.d.ts +43 -0
- package/dist/exchanges/lighter-public.js +276 -0
- package/dist/exchanges/lighter-rh-deposit.d.ts +62 -0
- package/dist/exchanges/lighter-rh-deposit.js +185 -0
- package/dist/exchanges/lighter-signer.d.ts +123 -0
- package/dist/exchanges/lighter-signer.js +215 -0
- package/dist/exchanges/lighter-spot.d.ts +91 -13
- package/dist/exchanges/lighter-spot.js +595 -233
- package/dist/exchanges/lighter.d.ts +611 -39
- package/dist/exchanges/lighter.js +1890 -649
- package/dist/exchanges/ondo/adapter.d.ts +166 -0
- package/dist/exchanges/ondo/adapter.js +835 -0
- package/dist/exchanges/ondo/agent.d.ts +31 -0
- package/dist/exchanges/ondo/agent.js +101 -0
- package/dist/exchanges/ondo/client.d.ts +60 -0
- package/dist/exchanges/ondo/client.js +297 -0
- package/dist/exchanges/ondo/funds.d.ts +122 -0
- package/dist/exchanges/ondo/funds.js +290 -0
- package/dist/exchanges/ondo/public.d.ts +95 -0
- package/dist/exchanges/ondo/public.js +214 -0
- package/dist/exchanges/ondo/spot.d.ts +127 -0
- package/dist/exchanges/ondo/spot.js +392 -0
- package/dist/exchanges/outcome-interface.d.ts +140 -0
- package/dist/exchanges/outcome-interface.js +16 -0
- package/dist/exchanges/pacifica-spot.d.ts +83 -0
- package/dist/exchanges/pacifica-spot.js +324 -0
- package/dist/exchanges/pacifica-typed-data.d.ts +30 -13
- package/dist/exchanges/pacifica-typed-data.js +57 -9
- package/dist/exchanges/pacifica.d.ts +288 -4
- package/dist/exchanges/pacifica.js +1013 -76
- package/dist/exchanges/precision.d.ts +47 -0
- package/dist/exchanges/precision.js +209 -0
- package/dist/exchanges/qfex/adapter.d.ts +341 -0
- package/dist/exchanges/qfex/adapter.js +1383 -0
- package/dist/exchanges/qfex/agent.d.ts +58 -0
- package/dist/exchanges/qfex/agent.js +188 -0
- package/dist/exchanges/qfex/client.d.ts +54 -0
- package/dist/exchanges/qfex/client.js +138 -0
- package/dist/exchanges/qfex/errors.d.ts +70 -0
- package/dist/exchanges/qfex/errors.js +236 -0
- package/dist/exchanges/qfex/public.d.ts +122 -0
- package/dist/exchanges/qfex/public.js +268 -0
- package/dist/exchanges/qfex/signing.d.ts +160 -0
- package/dist/exchanges/qfex/signing.js +283 -0
- package/dist/exchanges/qfex/ws-transport.d.ts +294 -0
- package/dist/exchanges/qfex/ws-transport.js +798 -0
- package/dist/exchanges/rate-limiter.d.ts +12 -0
- package/dist/exchanges/rate-limiter.js +64 -0
- package/dist/exchanges/registry.d.ts +14 -2
- package/dist/exchanges/registry.js +20 -32
- package/dist/exchanges/slippage.d.ts +37 -0
- package/dist/exchanges/slippage.js +70 -0
- package/dist/exchanges/spot-factory.d.ts +166 -0
- package/dist/exchanges/spot-factory.js +431 -0
- package/dist/exchanges/spot-interface.d.ts +98 -4
- package/dist/exchanges/spot-interface.js +27 -1
- package/dist/exchanges/spot-result.d.ts +79 -0
- package/dist/exchanges/spot-result.js +273 -0
- package/dist/exchanges/trading-v2.d.ts +117 -0
- package/dist/exchanges/trading-v2.js +301 -0
- package/dist/exchanges/venue-types.d.ts +231 -0
- package/dist/exchanges/venue-types.js +87 -0
- package/dist/exchanges/venue.d.ts +211 -0
- package/dist/exchanges/venue.js +132 -0
- package/dist/exchanges/venues/arcus.d.ts +8 -0
- package/dist/exchanges/venues/arcus.js +109 -0
- package/dist/exchanges/venues/aster.d.ts +49 -0
- package/dist/exchanges/venues/aster.js +176 -0
- package/dist/exchanges/venues/create-helpers.d.ts +18 -0
- package/dist/exchanges/venues/create-helpers.js +199 -0
- package/dist/exchanges/venues/hyperliquid.d.ts +23 -0
- package/dist/exchanges/venues/hyperliquid.js +126 -0
- package/dist/exchanges/venues/index.d.ts +16 -0
- package/dist/exchanges/venues/index.js +26 -0
- package/dist/exchanges/venues/lighter-family.d.ts +92 -0
- package/dist/exchanges/venues/lighter-family.js +166 -0
- package/dist/exchanges/venues/lighter-rh.d.ts +7 -0
- package/dist/exchanges/venues/lighter-rh.js +58 -0
- package/dist/exchanges/venues/lighter.d.ts +6 -0
- package/dist/exchanges/venues/lighter.js +55 -0
- package/dist/exchanges/venues/ondo.d.ts +34 -0
- package/dist/exchanges/venues/ondo.js +140 -0
- package/dist/exchanges/venues/pacifica.d.ts +8 -0
- package/dist/exchanges/venues/pacifica.js +95 -0
- package/dist/exchanges/venues/qfex.d.ts +28 -0
- package/dist/exchanges/venues/qfex.js +132 -0
- package/dist/execution-log.js +10 -2
- package/dist/funding/legacy-public.d.ts +31 -0
- package/dist/funding/legacy-public.js +369 -0
- package/dist/funding/normalize.d.ts +28 -16
- package/dist/funding/normalize.js +61 -29
- package/dist/funding/rates.d.ts +46 -11
- package/dist/funding/rates.js +228 -277
- package/dist/guardrail/perp-guardrail.js +0 -0
- package/dist/index.js +156 -684
- package/dist/jobs.d.ts +84 -6
- package/dist/jobs.js +239 -41
- package/dist/landing.d.ts +7 -2
- package/dist/landing.js +19 -3
- package/dist/mcp-server.d.ts +14 -4
- package/dist/mcp-server.js +820 -402
- package/dist/mcp-trade-guard.d.ts +16 -0
- package/dist/mcp-trade-guard.js +59 -0
- package/dist/pacifica/client.d.ts +41 -10
- package/dist/pacifica/client.js +167 -63
- package/dist/pacifica/constants.d.ts +0 -1
- package/dist/pacifica/constants.js +0 -5
- package/dist/pacifica/deposit.d.ts +30 -3
- package/dist/pacifica/deposit.js +119 -2
- package/dist/pacifica/faucet.d.ts +29 -0
- package/dist/pacifica/faucet.js +100 -0
- package/dist/pacifica/sign-tx.d.ts +15 -0
- package/dist/pacifica/sign-tx.js +41 -0
- package/dist/pacifica/types/account.d.ts +28 -4
- package/dist/pacifica/types/lake.d.ts +20 -5
- package/dist/pacifica/types/lake.js +4 -0
- package/dist/pacifica/types/market.d.ts +5 -0
- package/dist/pacifica/types/order.d.ts +4 -1
- package/dist/pacifica/types/position.d.ts +9 -1
- package/dist/plan-executor.d.ts +10 -5
- package/dist/plan-executor.js +165 -54
- package/dist/position-history.js +13 -0
- package/dist/rebalance.d.ts +26 -2
- package/dist/rebalance.js +86 -7
- package/dist/relay-swap.d.ts +232 -0
- package/dist/relay-swap.js +630 -0
- package/dist/risk.js +5 -1
- package/dist/secret-env.d.ts +15 -0
- package/dist/secret-env.js +36 -0
- package/dist/settings.d.ts +169 -6
- package/dist/settings.js +219 -38
- package/dist/signer/ows-evm.d.ts +13 -0
- package/dist/signer/ows-evm.js +22 -0
- package/dist/smart-order.d.ts +9 -0
- package/dist/smart-order.js +53 -8
- package/dist/strategies/dca.d.ts +6 -2
- package/dist/strategies/dca.js +66 -26
- package/dist/strategies/funding-arb.d.ts +11 -1
- package/dist/strategies/funding-arb.js +202 -89
- package/dist/strategies/grid.d.ts +31 -0
- package/dist/strategies/grid.js +89 -11
- package/dist/strategies/pending-stop.d.ts +8 -0
- package/dist/strategies/pending-stop.js +12 -0
- package/dist/strategies/split-order.d.ts +45 -17
- package/dist/strategies/split-order.js +157 -91
- package/dist/strategies/trailing-stop.d.ts +2 -1
- package/dist/strategies/trailing-stop.js +52 -6
- package/dist/strategies/twap.d.ts +6 -1
- package/dist/strategies/twap.js +39 -11
- package/dist/trade-validator.d.ts +22 -0
- package/dist/trade-validator.js +119 -15
- package/dist/utils/numeric.d.ts +46 -0
- package/dist/utils/numeric.js +79 -0
- package/dist/utils.d.ts +19 -0
- package/dist/utils.js +96 -19
- package/dist/withdraw-gate.d.ts +49 -0
- package/dist/withdraw-gate.js +131 -0
- package/package.json +15 -6
- package/skills/perp-cli/SKILL.md +142 -55
- package/skills/perp-cli/references/agent-operations.md +110 -56
- package/skills/perp-cli/references/commands.md +266 -71
- package/skills/perp-cli/references/strategies.md +34 -31
- package/skills/perp-cli/scripts/arb-monitor.sh +10 -5
- package/skills/perp-cli/scripts/funding-analysis.sh +14 -9
- package/skills/perp-cli/scripts/preflight.sh +21 -14
- package/skills/perp-cli/scripts/spot-perp-scan.sh +12 -7
- package/skills/perp-cli/scripts/validate-arb.sh +31 -15
- package/vendor/lighter-signer/SHA256SUMS +2 -0
- package/vendor/lighter-signer/VERSION +8 -0
- package/vendor/lighter-signer/lighter-signer.wasm +0 -0
- package/vendor/lighter-signer/wasm_exec.js +575 -0
- package/dist/bot/strategies/funding-arb-v2-strategy.d.ts +0 -42
- package/dist/bot/strategies/funding-arb-v2-strategy.js +0 -864
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `perp wallet builder status|approve|revoke <venue>` — the one place a user inspects,
|
|
3
|
+
* approves and revokes builder fees (src/exchanges/builder-fee.ts).
|
|
4
|
+
*
|
|
5
|
+
* Builder fees are OFF unless settings `builder.<venue>` is set, and the approve command
|
|
6
|
+
* only ever approves exactly that configured builder and fee (never a value from argv),
|
|
7
|
+
* after an explicit y/N (or --yes). Approvals are signed by the MASTER wallet (venue rule:
|
|
8
|
+
* an agent / API wallet cannot sign them); without a master on this computer the command
|
|
9
|
+
* stops with NO_SIGNER_AVAILABLE and tells the user to approve on the venue website.
|
|
10
|
+
*/
|
|
11
|
+
import { Command } from "commander";
|
|
12
|
+
import "../exchanges/venues/index.js";
|
|
13
|
+
import type { ExchangeAdapter } from "../exchanges/interface.js";
|
|
14
|
+
import type { Network, VenueId } from "../exchanges/venue-types.js";
|
|
15
|
+
import { type BuilderApproval, type BuilderFeeCapability, type BuilderFeeKind } from "../exchanges/builder-fee.js";
|
|
16
|
+
export interface BuilderApprovalPlan {
|
|
17
|
+
venue: VenueId;
|
|
18
|
+
network: Network;
|
|
19
|
+
/** Builder address (lowercase) or code — from settings builder.<venue>. */
|
|
20
|
+
identity: string;
|
|
21
|
+
/** null = feeKind "share" (QFEX): the user pays the normal venue fee, nothing extra. */
|
|
22
|
+
feeBps: number | null;
|
|
23
|
+
feeKind: BuilderFeeKind;
|
|
24
|
+
/** null = feeKind "share". */
|
|
25
|
+
capBps: number | null;
|
|
26
|
+
/** "none" = the venue has no approval step (Ondo): nothing to sign. */
|
|
27
|
+
approval: "master" | "none";
|
|
28
|
+
/** The venue action that would be signed, and the fee value in the venue's own unit. */
|
|
29
|
+
action: string | null;
|
|
30
|
+
venueFeeValue: string | null;
|
|
31
|
+
/** Master wallet that would sign (--master or settings owsActiveWallet); null = none on this computer. */
|
|
32
|
+
master: string | null;
|
|
33
|
+
}
|
|
34
|
+
export declare function builderApprovalPlan(venue: VenueId, network: Network, master: string | null): BuilderApprovalPlan;
|
|
35
|
+
/** Sign + send the master approval described by `plan` (settings target, never argv). */
|
|
36
|
+
export declare function approveBuilderOnVenue(plan: BuilderApprovalPlan, passphrase: string): Promise<unknown>;
|
|
37
|
+
/** Revoke the configured builder's approval (master-signed). */
|
|
38
|
+
export declare function revokeBuilderOnVenue(plan: BuilderApprovalPlan, passphrase: string): Promise<unknown>;
|
|
39
|
+
export interface BuilderVenueStatus {
|
|
40
|
+
venue: VenueId;
|
|
41
|
+
supported: boolean;
|
|
42
|
+
/** Why the venue has no builder fees (null when supported). */
|
|
43
|
+
reason: string | null;
|
|
44
|
+
capability: BuilderFeeCapability | null;
|
|
45
|
+
configured: {
|
|
46
|
+
identity: string;
|
|
47
|
+
feeBps: number | null;
|
|
48
|
+
} | null;
|
|
49
|
+
/** Venue approval read for the loaded account; null = not configured / unsupported / not applicable on this network. */
|
|
50
|
+
approval: BuilderApproval | null;
|
|
51
|
+
/** Whether the next perp order would carry the fee (configured + approved + network supported). */
|
|
52
|
+
attaches: boolean;
|
|
53
|
+
}
|
|
54
|
+
export declare function builderStatus(venue: VenueId, network: Network, getAdapter: ((v: string) => Promise<ExchangeAdapter>) | null): Promise<BuilderVenueStatus>;
|
|
55
|
+
export declare function registerWalletBuilderCommands(wallet: Command, isJson: () => boolean, getAdapterForExchange: ((v: string) => Promise<ExchangeAdapter>) | null): void;
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `perp wallet builder status|approve|revoke <venue>` — the one place a user inspects,
|
|
3
|
+
* approves and revokes builder fees (src/exchanges/builder-fee.ts).
|
|
4
|
+
*
|
|
5
|
+
* Builder fees are OFF unless settings `builder.<venue>` is set, and the approve command
|
|
6
|
+
* only ever approves exactly that configured builder and fee (never a value from argv),
|
|
7
|
+
* after an explicit y/N (or --yes). Approvals are signed by the MASTER wallet (venue rule:
|
|
8
|
+
* an agent / API wallet cannot sign them); without a master on this computer the command
|
|
9
|
+
* stops with NO_SIGNER_AVAILABLE and tells the user to approve on the venue website.
|
|
10
|
+
*/
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import { isDryRun, networkOf, supportsDryRun } from "../cli-flags.js";
|
|
13
|
+
import { PerpError } from "../errors.js";
|
|
14
|
+
import { loadSettings } from "../settings.js";
|
|
15
|
+
import { printJson, jsonOk, withJsonErrors } from "../utils.js";
|
|
16
|
+
import { resolvePassphrase } from "../agent-wallet/passphrase.js";
|
|
17
|
+
import { getVenue, networkConfig } from "../exchanges/venue.js";
|
|
18
|
+
import "../exchanges/venues/index.js";
|
|
19
|
+
import { BUILDER_FEE_CAPABILITIES, BUILDER_FEE_UNSUPPORTED, builderFeeUnsupported, builderFeeVenues, builderTargetOf, bpsToRate, feeText, hlMaxFeeRatePercent, requireBuilderCapability, requireFeeBps, resetBuilderApprovalCache, } from "../exchanges/builder-fee.js";
|
|
20
|
+
const APPROVE_ACTION = {
|
|
21
|
+
hyperliquid: "approveBuilderFee (HyperliquidTransaction:ApproveBuilderFee, user-signed)",
|
|
22
|
+
pacifica: "approve_builder_code (POST /account/builder_codes/approve)",
|
|
23
|
+
aster: "ApproveBuilder (POST /fapi/v3/approveBuilder, EIP-712 domain A)",
|
|
24
|
+
};
|
|
25
|
+
function venueFeeValue(venue, feeBps) {
|
|
26
|
+
// HL: percent string ("0.01%" = 1 bp); Pacifica / Aster: decimal fraction ("0.0001" = 1 bp).
|
|
27
|
+
return venue === "hyperliquid" ? hlMaxFeeRatePercent(feeBps) : bpsToRate(feeBps);
|
|
28
|
+
}
|
|
29
|
+
/** The configured target, or INVALID_PARAMS telling the user how to configure it. */
|
|
30
|
+
function configuredTarget(venue) {
|
|
31
|
+
const target = builderTargetOf(loadSettings().builder, venue);
|
|
32
|
+
if (target === null) {
|
|
33
|
+
const cap = requireBuilderCapability(venue);
|
|
34
|
+
throw new PerpError("INVALID_PARAMS", `No builder fee is configured for ${venue} (settings builder.${venue} is not set — builder fees are off)`, {
|
|
35
|
+
remediation: `Configure it first (explicit opt-in): perp settings set builder.${venue} ${cap.feeKind === "share" ? "<uuid>" : `<${cap.identity}>,<feeBps>`}`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return target;
|
|
39
|
+
}
|
|
40
|
+
function assertNetworkSupported(venue, cap, network) {
|
|
41
|
+
if (network === "testnet" && !cap.testnet) {
|
|
42
|
+
throw new PerpError("CAPABILITY_UNSUPPORTED", `${venue} does not support builderFee on testnet — the testnet API documents no builder approval flow`, {
|
|
43
|
+
venue, feature: "builderFee", reason: "no testnet builder approval flow", remediation: `Run on mainnet (-n mainnet)`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function builderApprovalPlan(venue, network, master) {
|
|
48
|
+
const cap = requireBuilderCapability(venue);
|
|
49
|
+
assertNetworkSupported(venue, cap, network);
|
|
50
|
+
const t = configuredTarget(venue);
|
|
51
|
+
const masterName = master ?? (loadSettings().owsActiveWallet || null);
|
|
52
|
+
return {
|
|
53
|
+
venue, network, identity: t.identity, feeBps: t.feeBps, feeKind: cap.feeKind, capBps: cap.perpCapBps,
|
|
54
|
+
approval: cap.approval,
|
|
55
|
+
action: cap.approval === "none" ? null : APPROVE_ACTION[venue] ?? null,
|
|
56
|
+
venueFeeValue: cap.approval === "none" ? null : venueFeeValue(venue, requireFeeBps(t)),
|
|
57
|
+
master: cap.approval === "none" ? null : masterName,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function requirePlanMaster(plan, verb) {
|
|
61
|
+
if (plan.master === null) {
|
|
62
|
+
throw new PerpError("NO_SIGNER_AVAILABLE", `Builder ${verb === "approve" ? "approval" : "revocation"} on ${plan.venue} is signed by the master wallet, and none is configured on this computer (no --master, settings.owsActiveWallet empty)`, {
|
|
63
|
+
remediation: `${verb === "approve" ? "Approve" : "Revoke"} it on the ${getVenue(plan.venue).displayName} website (builder ${plan.identity}${verb === "approve" ? `, max fee ${plan.feeBps} bps` : ""}), or add the master wallet (perp wallet import --name <name>) and pass --master <name>.`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return plan.master;
|
|
67
|
+
}
|
|
68
|
+
// ── Venue signing ──
|
|
69
|
+
async function hlPostUserSigned(network, master, passphrase, maxFeeRate, builder) {
|
|
70
|
+
const { OwsEvmSigner } = await import("../signer/ows-evm.js");
|
|
71
|
+
const { HL_USER_SIGNED } = await import("../exchanges/venues/hyperliquid.js");
|
|
72
|
+
const { ethers } = await import("ethers");
|
|
73
|
+
const signer = OwsEvmSigner.create(master, passphrase);
|
|
74
|
+
const { hyperliquidChain, signatureChainIdHex, signatureChainId } = HL_USER_SIGNED[network];
|
|
75
|
+
const nonce = Date.now();
|
|
76
|
+
const builderLc = builder.toLowerCase();
|
|
77
|
+
const sig = ethers.Signature.from(await signer.signTypedData({ name: "HyperliquidSignTransaction", version: "1", chainId: signatureChainId, verifyingContract: "0x0000000000000000000000000000000000000000" }, {
|
|
78
|
+
"HyperliquidTransaction:ApproveBuilderFee": [
|
|
79
|
+
{ name: "hyperliquidChain", type: "string" },
|
|
80
|
+
{ name: "maxFeeRate", type: "string" },
|
|
81
|
+
{ name: "builder", type: "address" },
|
|
82
|
+
{ name: "nonce", type: "uint64" },
|
|
83
|
+
],
|
|
84
|
+
}, { hyperliquidChain, maxFeeRate, builder: builderLc, nonce }));
|
|
85
|
+
const action = { type: "approveBuilderFee", hyperliquidChain, signatureChainId: signatureChainIdHex, maxFeeRate, builder: builderLc, nonce };
|
|
86
|
+
const res = await fetch(`${networkConfig(getVenue("hyperliquid"), network).restUrl}/exchange`, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: { "Content-Type": "application/json" },
|
|
89
|
+
body: JSON.stringify({ action, nonce, signature: { r: sig.r, s: sig.s, v: sig.v }, vaultAddress: null }),
|
|
90
|
+
});
|
|
91
|
+
const text = await res.text();
|
|
92
|
+
let body;
|
|
93
|
+
try {
|
|
94
|
+
body = JSON.parse(text);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
body = null;
|
|
98
|
+
}
|
|
99
|
+
if (!res.ok || body === null || body.status !== "ok") {
|
|
100
|
+
throw new PerpError("EXCHANGE_ERROR", `Hyperliquid approveBuilderFee rejected (${res.status}): ${text.slice(0, 200)}`, {
|
|
101
|
+
exchange: "hyperliquid",
|
|
102
|
+
remediation: "The builder must hold ≥ 100 USDC perps account value, and the fee must be ≤ 0.1% — check the venue message above",
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return body;
|
|
106
|
+
}
|
|
107
|
+
function pacificaResult(raw, what) {
|
|
108
|
+
const r = raw;
|
|
109
|
+
if (r === null || typeof r !== "object" || r.success === false || (r.error !== undefined && r.error !== null)) {
|
|
110
|
+
throw new PerpError("EXCHANGE_ERROR", `Pacifica ${what} rejected: ${JSON.stringify(raw).slice(0, 200)}`, { exchange: "pacifica" });
|
|
111
|
+
}
|
|
112
|
+
return raw;
|
|
113
|
+
}
|
|
114
|
+
async function pacificaMaster(network, master, passphrase) {
|
|
115
|
+
const { OwsSolanaSigner } = await import("../signer/ows-solana.js");
|
|
116
|
+
const { PacificaClient } = await import("../pacifica/index.js");
|
|
117
|
+
const signer = OwsSolanaSigner.create(master, passphrase);
|
|
118
|
+
// A fresh client: no agent_wallet field — the account (master) signs for itself.
|
|
119
|
+
const client = new PacificaClient({ network });
|
|
120
|
+
return { client, account: signer.getPublicKeyBase58(), sign: (m) => signer.signMessage(m) };
|
|
121
|
+
}
|
|
122
|
+
async function asterMasterRequest(method, path, master, passphrase, typed, params) {
|
|
123
|
+
const { OwsEvmSigner } = await import("../signer/ows-evm.js");
|
|
124
|
+
const { ASTER_DOMAIN_A } = await import("../exchanges/aster-typed-data.js");
|
|
125
|
+
const signer = OwsEvmSigner.create(master, passphrase);
|
|
126
|
+
const user = signer.getAddress();
|
|
127
|
+
const nonceMicros = Date.now() * 1000 + Math.floor(Math.random() * 1000);
|
|
128
|
+
const td = typed(user, nonceMicros);
|
|
129
|
+
const signature = await signer.signTypedData(td.domain, td.types, td.message);
|
|
130
|
+
const qs = new URLSearchParams(Object.fromEntries([
|
|
131
|
+
...params,
|
|
132
|
+
["asterChain", "Mainnet"],
|
|
133
|
+
["user", user],
|
|
134
|
+
["nonce", String(nonceMicros)],
|
|
135
|
+
["signature", signature],
|
|
136
|
+
["signatureChainId", String(ASTER_DOMAIN_A.chainId)],
|
|
137
|
+
]));
|
|
138
|
+
const res = await fetch(`${networkConfig(getVenue("aster"), "mainnet").restUrl}${path}?${qs.toString()}`, {
|
|
139
|
+
method,
|
|
140
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
141
|
+
body: "",
|
|
142
|
+
});
|
|
143
|
+
const text = await res.text();
|
|
144
|
+
let body = null;
|
|
145
|
+
try {
|
|
146
|
+
body = JSON.parse(text);
|
|
147
|
+
}
|
|
148
|
+
catch { /* non-JSON body: judged by status below */ }
|
|
149
|
+
const code = body !== null && typeof body === "object" ? body.code : undefined;
|
|
150
|
+
const ok = res.ok && (code === undefined || code === "000000" || code === 200 || (typeof code === "number" && code >= 0));
|
|
151
|
+
if (!ok) {
|
|
152
|
+
throw new PerpError("EXCHANGE_ERROR", `Aster ${method} ${path} rejected (${res.status}): ${text.slice(0, 200)}`, { exchange: "aster" });
|
|
153
|
+
}
|
|
154
|
+
return body;
|
|
155
|
+
}
|
|
156
|
+
/** Sign + send the master approval described by `plan` (settings target, never argv). */
|
|
157
|
+
export async function approveBuilderOnVenue(plan, passphrase) {
|
|
158
|
+
const master = requirePlanMaster(plan, "approve");
|
|
159
|
+
const fee = venueFeeValue(plan.venue, requireFeeBps(plan));
|
|
160
|
+
let out;
|
|
161
|
+
switch (plan.venue) {
|
|
162
|
+
case "hyperliquid":
|
|
163
|
+
out = await hlPostUserSigned(plan.network, master, passphrase, fee, plan.identity);
|
|
164
|
+
break;
|
|
165
|
+
case "pacifica": {
|
|
166
|
+
const m = await pacificaMaster(plan.network, master, passphrase);
|
|
167
|
+
out = pacificaResult(await m.client.approveBuilderCode({ builder_code: plan.identity, max_fee_rate: fee }, m.account, m.sign), "approve_builder_code");
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case "aster": {
|
|
171
|
+
const { buildApproveBuilderTypedData } = await import("../exchanges/aster-typed-data.js");
|
|
172
|
+
out = await asterMasterRequest("POST", "/fapi/v3/approveBuilder", master, passphrase, (user, nonceMicros) => buildApproveBuilderTypedData({ builder: plan.identity, maxFeeRate: fee, user, nonceMicros, asterChain: "Mainnet" }), [["builder", plan.identity], ["maxFeeRate", fee]]);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
default:
|
|
176
|
+
throw builderFeeUnsupported(plan.venue);
|
|
177
|
+
}
|
|
178
|
+
resetBuilderApprovalCache();
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
/** Revoke the configured builder's approval (master-signed). */
|
|
182
|
+
export async function revokeBuilderOnVenue(plan, passphrase) {
|
|
183
|
+
const master = requirePlanMaster(plan, "revoke");
|
|
184
|
+
let out;
|
|
185
|
+
switch (plan.venue) {
|
|
186
|
+
case "pacifica": {
|
|
187
|
+
const m = await pacificaMaster(plan.network, master, passphrase);
|
|
188
|
+
out = pacificaResult(await m.client.revokeBuilderCode({ builder_code: plan.identity }, m.account, m.sign), "revoke_builder_code");
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
case "aster": {
|
|
192
|
+
const { buildDelBuilderTypedData } = await import("../exchanges/aster-typed-data.js");
|
|
193
|
+
out = await asterMasterRequest("DELETE", "/fapi/v3/builder", master, passphrase, (user, nonceMicros) => buildDelBuilderTypedData({ builder: plan.identity, user, nonceMicros, asterChain: "Mainnet" }), [["builder", plan.identity]]);
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
case "hyperliquid":
|
|
197
|
+
throw new PerpError("CAPABILITY_UNSUPPORTED", "hyperliquid does not support builderFeeRevoke — the venue docs define no revoke action for ApproveBuilderFee", {
|
|
198
|
+
venue: "hyperliquid", feature: "builderFeeRevoke", reason: "no documented revoke action",
|
|
199
|
+
remediation: "An approval alone charges nothing: a fee is paid only on orders that carry it. Stop perp-cli attaching it: perp settings unset builder.hyperliquid",
|
|
200
|
+
});
|
|
201
|
+
default:
|
|
202
|
+
throw builderFeeUnsupported(plan.venue);
|
|
203
|
+
}
|
|
204
|
+
resetBuilderApprovalCache();
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
export async function builderStatus(venue, network, getAdapter) {
|
|
208
|
+
const cap = BUILDER_FEE_CAPABILITIES[venue];
|
|
209
|
+
if (cap === null) {
|
|
210
|
+
return { venue, supported: false, reason: BUILDER_FEE_UNSUPPORTED[venue] ?? null, capability: null, configured: null, approval: null, attaches: false };
|
|
211
|
+
}
|
|
212
|
+
const t = builderTargetOf(loadSettings().builder, venue);
|
|
213
|
+
const base = { venue, supported: true, reason: null, capability: cap, configured: t === null ? null : { identity: t.identity, feeBps: t.feeBps } };
|
|
214
|
+
if (t === null)
|
|
215
|
+
return { ...base, approval: null, attaches: false };
|
|
216
|
+
if (network === "testnet" && !cap.testnet)
|
|
217
|
+
return { ...base, reason: `${venue} testnet has no builder-fee flow`, approval: null, attaches: false };
|
|
218
|
+
if (cap.approval === "none")
|
|
219
|
+
return { ...base, approval: { status: "approved", maxFeeBps: null }, attaches: true };
|
|
220
|
+
let approval;
|
|
221
|
+
if (getAdapter === null) {
|
|
222
|
+
approval = { status: "unknown", reason: "no account adapter available" };
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
try {
|
|
226
|
+
const adapter = await getAdapter(venue);
|
|
227
|
+
if (typeof adapter.builderApprovalOf !== "function")
|
|
228
|
+
throw new Error(`${venue} adapter cannot read builder approvals`);
|
|
229
|
+
approval = await adapter.builderApprovalOf(t);
|
|
230
|
+
}
|
|
231
|
+
catch (e) {
|
|
232
|
+
// Shown to the user as "unknown" with the reason — a live order would skip the fee the same way.
|
|
233
|
+
approval = { status: "unknown", reason: e instanceof Error ? e.message : String(e) };
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { ...base, approval, attaches: approval.status === "approved" };
|
|
237
|
+
}
|
|
238
|
+
// ── Commands ──
|
|
239
|
+
async function confirmConsent(plan, yes, json, verb) {
|
|
240
|
+
if (yes)
|
|
241
|
+
return;
|
|
242
|
+
if (json || process.stdin.isTTY !== true) {
|
|
243
|
+
throw new PerpError("INVALID_PARAMS", `Builder ${verb} needs explicit confirmation`, {
|
|
244
|
+
remediation: `Review with --dry-run, then re-run with --yes: perp${plan.network === "testnet" ? " -n testnet" : ""} wallet builder ${verb} ${plan.venue} --yes`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const { createInterface } = await import("node:readline/promises");
|
|
248
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
249
|
+
try {
|
|
250
|
+
const q = verb === "approve"
|
|
251
|
+
? ` Approve builder ${plan.identity} to charge ${plan.feeKind === "max" ? "up to " : ""}${plan.feeBps} bps on YOUR ${plan.venue} perp orders (cap ${plan.capBps} bps)? [y/N]: `
|
|
252
|
+
: ` Revoke builder ${plan.identity} on ${plan.venue}? [y/N]: `;
|
|
253
|
+
const a = (await rl.question(q)).trim().toLowerCase();
|
|
254
|
+
if (a !== "y" && a !== "yes")
|
|
255
|
+
throw new PerpError("INVALID_PARAMS", `Builder ${verb} cancelled — nothing was signed`);
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
rl.close();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async function masterPassphrase(flag) {
|
|
262
|
+
const pp = await resolvePassphrase({ flag });
|
|
263
|
+
if (pp !== null)
|
|
264
|
+
return pp;
|
|
265
|
+
const { promptHidden } = await import("./agent.js");
|
|
266
|
+
return promptHidden(" Master wallet passphrase (hidden): ");
|
|
267
|
+
}
|
|
268
|
+
function printPlan(plan, verb, dryRun) {
|
|
269
|
+
console.log(chalk.cyan.bold(`\n ${dryRun ? "[dry-run] " : ""}Builder ${verb} — ${getVenue(plan.venue).displayName} (${plan.network})\n`));
|
|
270
|
+
console.log(` Builder: ${plan.identity}`);
|
|
271
|
+
if (verb === "approve") {
|
|
272
|
+
console.log(` Fee: ${plan.feeKind === "max" ? "up to " : ""}${plan.feeBps} bps per perp order (venue cap ${plan.capBps} bps)${plan.venueFeeValue !== null ? ` — sent as ${plan.venueFeeValue}` : ""}`);
|
|
273
|
+
}
|
|
274
|
+
console.log(` Signs: ${plan.action ?? "nothing (no approval step)"}`);
|
|
275
|
+
console.log(` Master: ${plan.master ?? chalk.yellow("none on this computer")}`);
|
|
276
|
+
console.log(chalk.gray(" Paid by you, on every perp order perp-cli sends while builder." + plan.venue + " is set (disable per run: --no-builder)."));
|
|
277
|
+
}
|
|
278
|
+
export function registerWalletBuilderCommands(wallet, isJson, getAdapterForExchange) {
|
|
279
|
+
const builder = wallet.command("builder").description("Builder fees (off by default): status / approve / revoke the builder configured in settings builder.<venue>");
|
|
280
|
+
builder
|
|
281
|
+
.command("status [venue]")
|
|
282
|
+
.description("Builder-fee support, configuration and venue approval (all venues when omitted)")
|
|
283
|
+
.action(async (venueArg, _opts, cmd) => {
|
|
284
|
+
await withJsonErrors(isJson(), async () => {
|
|
285
|
+
const network = networkOf(cmd);
|
|
286
|
+
const venues = venueArg !== undefined ? [getVenue(venueArg).id] : [...builderFeeVenues(), ...["lighter", "lighter-rh", "arcus"]];
|
|
287
|
+
const rows = [];
|
|
288
|
+
for (const v of venues) {
|
|
289
|
+
const configured = builderTargetOf(loadSettings().builder, v) !== null;
|
|
290
|
+
// Only read approvals for a configured builder (an unconfigured venue needs no account access).
|
|
291
|
+
rows.push(await builderStatus(v, network, configured ? getAdapterForExchange : null));
|
|
292
|
+
}
|
|
293
|
+
if (isJson())
|
|
294
|
+
return printJson(jsonOk({ network, venues: rows }));
|
|
295
|
+
console.log(chalk.cyan.bold(`\n Builder fees (${network}) — off unless settings builder.<venue> is set\n`));
|
|
296
|
+
for (const r of rows) {
|
|
297
|
+
if (!r.supported) {
|
|
298
|
+
console.log(` ${r.venue.padEnd(12)} ${chalk.gray(`unsupported — ${r.reason}`)}`);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const cfg = r.configured === null ? chalk.gray("not configured (off)") : `${r.configured.identity} ${feeText(r.configured)}`;
|
|
302
|
+
const appr = r.approval === null ? (r.reason !== null ? chalk.gray(r.reason) : "")
|
|
303
|
+
: r.approval.status === "approved" ? chalk.green(r.capability?.approval === "none" ? "no approval needed" : `approved (max ${r.approval.maxFeeBps} bps)`)
|
|
304
|
+
: r.approval.status === "notApproved" ? chalk.yellow(`NOT approved (max ${r.approval.maxFeeBps ?? "none"}) — perp wallet builder approve ${r.venue}`)
|
|
305
|
+
: r.approval.status === "aboveConsent" ? chalk.yellow(`approved max ${r.approval.maxFeeBps} bps is ABOVE the configured ${r.configured?.feeBps} bps — not attached; re-approve: perp wallet builder approve ${r.venue}`)
|
|
306
|
+
: chalk.yellow(`unknown — ${r.approval.reason}`);
|
|
307
|
+
console.log(` ${r.venue.padEnd(12)} ${cfg} ${appr}${r.attaches ? chalk.green(" → attached") : ""}`);
|
|
308
|
+
}
|
|
309
|
+
console.log();
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
supportsDryRun(builder
|
|
313
|
+
.command("approve <venue>")
|
|
314
|
+
.description("Approve the builder configured in settings builder.<venue> (master-signed; y/N or --yes; --dry-run prints the plan)")
|
|
315
|
+
.option("--master <name>", "Master OWS wallet name (default: settings owsActiveWallet)")
|
|
316
|
+
.option("--passphrase <pp>", "Master OWS passphrase (fallback: OWS_PASSPHRASE env / stdin / hidden prompt)")
|
|
317
|
+
.option("--yes", "Confirm without the y/N prompt")
|
|
318
|
+
.action(async (venueArg, opts, cmd) => {
|
|
319
|
+
await withJsonErrors(isJson(), async () => {
|
|
320
|
+
const network = networkOf(cmd);
|
|
321
|
+
const venue = getVenue(venueArg).id;
|
|
322
|
+
const plan = builderApprovalPlan(venue, network, opts.master ?? null);
|
|
323
|
+
if (plan.approval === "none") {
|
|
324
|
+
const info = { ...plan, approvalRequired: false, sent: false };
|
|
325
|
+
if (isJson())
|
|
326
|
+
return printJson(jsonOk(info));
|
|
327
|
+
console.log(chalk.green(`\n ${getVenue(venue).displayName} has no builder approval step — ${plan.identity} (${feeText(plan)}) is ${plan.feeKind === "share" ? "attached to the trading connection as configured: your fills are attributed to it and you pay the normal fee" : "attached to perp orders as configured"}.`));
|
|
328
|
+
console.log(chalk.gray(` Stop it: perp settings unset builder.${venue} (or --no-builder per run)\n`));
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (isDryRun(cmd)) {
|
|
332
|
+
if (isJson())
|
|
333
|
+
return printJson(jsonOk({ dryRun: true, sent: false, ...plan }));
|
|
334
|
+
printPlan(plan, "approve", true);
|
|
335
|
+
console.log(chalk.yellow("\n Nothing signed or sent.\n"));
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
requirePlanMaster(plan, "approve");
|
|
339
|
+
if (!isJson())
|
|
340
|
+
printPlan(plan, "approve", false);
|
|
341
|
+
await confirmConsent(plan, opts.yes === true, isJson(), "approve");
|
|
342
|
+
const merged = cmd.optsWithGlobals();
|
|
343
|
+
const result = await approveBuilderOnVenue(plan, await masterPassphrase(merged.passphrase ?? opts.passphrase));
|
|
344
|
+
if (isJson())
|
|
345
|
+
return printJson(jsonOk({ ...plan, sent: true, venueResponse: result }));
|
|
346
|
+
console.log(chalk.green(`\n Approved builder ${plan.identity} on ${plan.venue} (${plan.feeBps} bps). Check: perp wallet builder status ${plan.venue}\n`));
|
|
347
|
+
});
|
|
348
|
+
}));
|
|
349
|
+
supportsDryRun(builder
|
|
350
|
+
.command("revoke <venue>")
|
|
351
|
+
.description("Revoke the builder configured in settings builder.<venue> (master-signed; y/N or --yes)")
|
|
352
|
+
.option("--master <name>", "Master OWS wallet name (default: settings owsActiveWallet)")
|
|
353
|
+
.option("--passphrase <pp>", "Master OWS passphrase (fallback: OWS_PASSPHRASE env / stdin / hidden prompt)")
|
|
354
|
+
.option("--yes", "Confirm without the y/N prompt")
|
|
355
|
+
.action(async (venueArg, opts, cmd) => {
|
|
356
|
+
await withJsonErrors(isJson(), async () => {
|
|
357
|
+
const network = networkOf(cmd);
|
|
358
|
+
const venue = getVenue(venueArg).id;
|
|
359
|
+
const plan = builderApprovalPlan(venue, network, opts.master ?? null);
|
|
360
|
+
if (plan.approval === "none") {
|
|
361
|
+
throw new PerpError("CAPABILITY_UNSUPPORTED", `${venue} has no builder approval to revoke`, {
|
|
362
|
+
venue, feature: "builderFeeRevoke", reason: "no approval step",
|
|
363
|
+
remediation: `Stop attaching the builder code: perp settings unset builder.${venue}`,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
if (venue === "hyperliquid")
|
|
367
|
+
return void (await revokeBuilderOnVenue(plan, ""));
|
|
368
|
+
const revokePlan = { ...plan, action: venue === "pacifica" ? "revoke_builder_code (POST /account/builder_codes/revoke)" : "DelBuilder (DELETE /fapi/v3/builder, EIP-712 domain A)" };
|
|
369
|
+
if (isDryRun(cmd)) {
|
|
370
|
+
if (isJson())
|
|
371
|
+
return printJson(jsonOk({ dryRun: true, sent: false, ...revokePlan }));
|
|
372
|
+
printPlan(revokePlan, "revoke", true);
|
|
373
|
+
console.log(chalk.yellow("\n Nothing signed or sent.\n"));
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
requirePlanMaster(revokePlan, "revoke");
|
|
377
|
+
if (!isJson())
|
|
378
|
+
printPlan(revokePlan, "revoke", false);
|
|
379
|
+
await confirmConsent(revokePlan, opts.yes === true, isJson(), "revoke");
|
|
380
|
+
const merged = cmd.optsWithGlobals();
|
|
381
|
+
const result = await revokeBuilderOnVenue(revokePlan, await masterPassphrase(merged.passphrase ?? opts.passphrase));
|
|
382
|
+
if (isJson())
|
|
383
|
+
return printJson(jsonOk({ ...revokePlan, sent: true, venueResponse: result }));
|
|
384
|
+
console.log(chalk.green(`\n Revoked builder ${plan.identity} on ${plan.venue}. perp-cli no longer attaches it (approval missing); remove the setting with: perp settings unset builder.${venue}\n`));
|
|
385
|
+
});
|
|
386
|
+
}));
|
|
387
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `funds deposit aster|ondo`, `funds withdraw aster`, `funds address-book add ondo` — fund movements
|
|
3
|
+
* whose on-chain leg is signed by the OWS master wallet (src/evm-master-tx.ts).
|
|
4
|
+
*
|
|
5
|
+
* Every command: `--dry-run` prints the plan and returns before any key, adapter or network is
|
|
6
|
+
* touched; the live path runs all read-only checks before the first signature; an outcome that
|
|
7
|
+
* cannot be read after a send is ORDER_PENDING (logged "unverified"), never "failed".
|
|
8
|
+
*/
|
|
9
|
+
import { Command } from "commander";
|
|
10
|
+
import type { ExchangeAdapter } from "../exchanges/index.js";
|
|
11
|
+
import { type Network } from "../exchanges/venue.js";
|
|
12
|
+
export interface OnchainFundsContext {
|
|
13
|
+
program: Command;
|
|
14
|
+
funds: Command;
|
|
15
|
+
deposit: Command;
|
|
16
|
+
withdraw: Command;
|
|
17
|
+
isJson: () => boolean;
|
|
18
|
+
isDryRun: () => boolean;
|
|
19
|
+
getNetwork: () => Network;
|
|
20
|
+
getAdapterForExchange: (exchange: string) => Promise<ExchangeAdapter>;
|
|
21
|
+
requireMaster: (venue: "aster" | "ondo", action: string) => Promise<void>;
|
|
22
|
+
printFundsPlan: (plan: {
|
|
23
|
+
action: "deposit" | "withdraw" | "transfer";
|
|
24
|
+
venue: string;
|
|
25
|
+
network: string;
|
|
26
|
+
asset: string;
|
|
27
|
+
amount: string;
|
|
28
|
+
[k: string]: unknown;
|
|
29
|
+
}) => void;
|
|
30
|
+
failStatus: (err: unknown) => "failed" | "unverified";
|
|
31
|
+
}
|
|
32
|
+
export declare function registerOnchainFundsCommands(ctx: OnchainFundsContext): void;
|