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,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pacifica Spot Adapter (design §12).
|
|
3
|
+
*
|
|
4
|
+
* Spot is a different `symbol` ("SOL-USDC") on the same Pacifica order
|
|
5
|
+
* endpoints as perps, signed by the same Solana signer (incl. agent_wallet).
|
|
6
|
+
* Account model is unified: spot BUYs spend the shared USDC perp margin and
|
|
7
|
+
* spot holdings are cross-margin collateral — there is no transfer.
|
|
8
|
+
*
|
|
9
|
+
* Venue constraints: reduce_only must be false, no TP/SL, no builder code.
|
|
10
|
+
*
|
|
11
|
+
* Unverifiable without a funded live account (documented, not guessed):
|
|
12
|
+
* - whether GET /orders lists spot orders (assumed: it covers all markets;
|
|
13
|
+
* we filter by spot symbol);
|
|
14
|
+
* - the exact shape of /orders/history_by_id rows (fill price field), so
|
|
15
|
+
* avgPrice is reported only when the row exposes `average_filled_price`.
|
|
16
|
+
*/
|
|
17
|
+
import type { SpotAdapter, SpotMarketInfo, SpotBalance, SpotOrderResult, SpotOpenOrdersCapable, SpotCancelAllCapable } from "./spot-interface.js";
|
|
18
|
+
import type { ResolvedSlippage } from "./slippage.js";
|
|
19
|
+
import { type PacificaAdapter } from "./pacifica.js";
|
|
20
|
+
import type { IocOutcome } from "./ioc-outcome.js";
|
|
21
|
+
export declare class PacificaSpotAdapter implements SpotAdapter, SpotOpenOrdersCapable, SpotCancelAllCapable {
|
|
22
|
+
readonly name = "pacifica";
|
|
23
|
+
private _perp;
|
|
24
|
+
/** base ("SOL") → /info spot row */
|
|
25
|
+
private _spot;
|
|
26
|
+
private _initialized;
|
|
27
|
+
/** Venue-final IOC outcomes keyed by the SpotOrderResult returned (see iocOutcomeOf). */
|
|
28
|
+
private readonly _iocOutcomes;
|
|
29
|
+
constructor(perp: PacificaAdapter);
|
|
30
|
+
init(): Promise<void>;
|
|
31
|
+
private _loadSpotInfo;
|
|
32
|
+
/** bare base or "BASE/USDC" → /info spot row (wire symbol "BASE-USDC"). */
|
|
33
|
+
private _resolve;
|
|
34
|
+
getSpotMarkets(): Promise<SpotMarketInfo[]>;
|
|
35
|
+
getSpotOrderbook(symbol: string): Promise<{
|
|
36
|
+
bids: [string, string][];
|
|
37
|
+
asks: [string, string][];
|
|
38
|
+
}>;
|
|
39
|
+
getSpotBalances(): Promise<SpotBalance[]>;
|
|
40
|
+
/** Mark price for min-notional / slippage checks. */
|
|
41
|
+
private _mark;
|
|
42
|
+
private _checkNotional;
|
|
43
|
+
/** Throws unless the response is an accepted order; returns order_id when present. */
|
|
44
|
+
private _accepted;
|
|
45
|
+
/**
|
|
46
|
+
* Poll history_by_id until a terminal state (or, for resting orders, the first
|
|
47
|
+
* row) — otherwise finality "pending", never resubmit. The order is already
|
|
48
|
+
* accepted, so a poll error is reported as pending (with the order id), not thrown.
|
|
49
|
+
* `amount` = the placed size (a "filled" row without filled_amount filled all of it).
|
|
50
|
+
* `finalFill` = the reported filled_amount of a terminal row (filled / cancelled /
|
|
51
|
+
* rejected) when `wantTerminal` (IOC / market) — the venue-final fill for iocOutcomeOf;
|
|
52
|
+
* null otherwise (no report, resting order, pending), never the placed amount.
|
|
53
|
+
*/
|
|
54
|
+
private _settle;
|
|
55
|
+
/** Assemble the result and remember its venue-final fill (when proven) for iocOutcomeOf. */
|
|
56
|
+
private _withOutcome;
|
|
57
|
+
/**
|
|
58
|
+
* Venue-final outcome of a SpotOrderResult returned by spotMarketOrder / IOC
|
|
59
|
+
* spotLimitOrder (IocOutcomeCapable, design §13.10 #4): set only when the polled
|
|
60
|
+
* history row was terminal and reported filled_amount. null for anything else.
|
|
61
|
+
*/
|
|
62
|
+
iocOutcomeOf(raw: unknown): IocOutcome | null;
|
|
63
|
+
spotMarketOrder(symbol: string, side: "buy" | "sell", size: string, opts: {
|
|
64
|
+
slippage: ResolvedSlippage;
|
|
65
|
+
}): Promise<SpotOrderResult>;
|
|
66
|
+
spotLimitOrder(symbol: string, side: "buy" | "sell", price: string, size: string, opts?: {
|
|
67
|
+
tif?: string;
|
|
68
|
+
}): Promise<SpotOrderResult>;
|
|
69
|
+
spotCancelOrder(symbol: string, orderId: string): Promise<SpotOrderResult>;
|
|
70
|
+
getSpotOpenOrders(symbol: string | null): Promise<Array<{
|
|
71
|
+
orderId: string;
|
|
72
|
+
symbol: string;
|
|
73
|
+
side: "buy" | "sell";
|
|
74
|
+
price: string;
|
|
75
|
+
size: string;
|
|
76
|
+
filled: string;
|
|
77
|
+
}>>;
|
|
78
|
+
/**
|
|
79
|
+
* Cancels spot orders one by one. The venue `cancel_all` with all_symbols
|
|
80
|
+
* would also cancel perp orders, so it is never used here.
|
|
81
|
+
*/
|
|
82
|
+
spotCancelAllOrders(symbol: string | null): Promise<SpotOrderResult[]>;
|
|
83
|
+
}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pacifica Spot Adapter (design §12).
|
|
3
|
+
*
|
|
4
|
+
* Spot is a different `symbol` ("SOL-USDC") on the same Pacifica order
|
|
5
|
+
* endpoints as perps, signed by the same Solana signer (incl. agent_wallet).
|
|
6
|
+
* Account model is unified: spot BUYs spend the shared USDC perp margin and
|
|
7
|
+
* spot holdings are cross-margin collateral — there is no transfer.
|
|
8
|
+
*
|
|
9
|
+
* Venue constraints: reduce_only must be false, no TP/SL, no builder code.
|
|
10
|
+
*
|
|
11
|
+
* Unverifiable without a funded live account (documented, not guessed):
|
|
12
|
+
* - whether GET /orders lists spot orders (assumed: it covers all markets;
|
|
13
|
+
* we filter by spot symbol);
|
|
14
|
+
* - the exact shape of /orders/history_by_id rows (fill price field), so
|
|
15
|
+
* avgPrice is reported only when the row exposes `average_filled_price`.
|
|
16
|
+
*/
|
|
17
|
+
import { pacificaCreateRejection, pacificaLatestHistoryRow, pacificaTif } from "./pacifica.js";
|
|
18
|
+
import { PerpError, orderNotSent } from "../errors.js";
|
|
19
|
+
import { floorSize, requireOnTick, requirePositive, subDecimal } from "./precision.js";
|
|
20
|
+
const QUOTE = "USDC";
|
|
21
|
+
let buyWarned = false;
|
|
22
|
+
function warnSharedMarginOnce() {
|
|
23
|
+
if (buyWarned)
|
|
24
|
+
return;
|
|
25
|
+
buyWarned = true;
|
|
26
|
+
console.error("[pacifica-spot] Warning: spot buys on Pacifica spend the shared USDC perp margin (unified account) and reduce perp available margin.");
|
|
27
|
+
}
|
|
28
|
+
/** Required venue field as a string (Rule #2: a missing field is an error, not "0"). */
|
|
29
|
+
function reqField(row, field) {
|
|
30
|
+
const v = row[field];
|
|
31
|
+
if (typeof v === "string" || (typeof v === "number" && Number.isFinite(v)))
|
|
32
|
+
return String(v);
|
|
33
|
+
throw new PerpError("EXCHANGE_ERROR", `Pacifica order row ${String(row.order_id)} has no "${field}"`, { exchange: "pacifica" });
|
|
34
|
+
}
|
|
35
|
+
function decimalsOf(step) {
|
|
36
|
+
const frac = step.split(".")[1];
|
|
37
|
+
return frac ? frac.replace(/0+$/, "").length : 0;
|
|
38
|
+
}
|
|
39
|
+
export class PacificaSpotAdapter {
|
|
40
|
+
name = "pacifica";
|
|
41
|
+
_perp;
|
|
42
|
+
/** base ("SOL") → /info spot row */
|
|
43
|
+
_spot = new Map();
|
|
44
|
+
_initialized = false;
|
|
45
|
+
/** Venue-final IOC outcomes keyed by the SpotOrderResult returned (see iocOutcomeOf). */
|
|
46
|
+
_iocOutcomes = new WeakMap();
|
|
47
|
+
constructor(perp) {
|
|
48
|
+
this._perp = perp;
|
|
49
|
+
}
|
|
50
|
+
async init() {
|
|
51
|
+
if (this._initialized)
|
|
52
|
+
return;
|
|
53
|
+
await this._loadSpotInfo();
|
|
54
|
+
this._initialized = true;
|
|
55
|
+
}
|
|
56
|
+
async _loadSpotInfo() {
|
|
57
|
+
const rows = (await this._perp.sdk.getInfo()).filter((m) => m.instrument_type === "spot");
|
|
58
|
+
this._spot.clear();
|
|
59
|
+
for (const m of rows)
|
|
60
|
+
this._spot.set(m.base_asset.toUpperCase(), m);
|
|
61
|
+
return rows;
|
|
62
|
+
}
|
|
63
|
+
/** bare base or "BASE/USDC" → /info spot row (wire symbol "BASE-USDC"). */
|
|
64
|
+
_resolve(symbol) {
|
|
65
|
+
const [base, quote] = symbol.toUpperCase().split("/");
|
|
66
|
+
if (quote !== undefined && quote !== QUOTE) {
|
|
67
|
+
throw new PerpError("SYMBOL_NOT_FOUND", `Pacifica spot only quotes ${QUOTE} (got ${symbol})`, { exchange: "pacifica" });
|
|
68
|
+
}
|
|
69
|
+
const m = this._spot.get(base);
|
|
70
|
+
if (!m) {
|
|
71
|
+
throw new PerpError("SYMBOL_NOT_FOUND", `No Pacifica spot market for ${symbol}`, {
|
|
72
|
+
exchange: "pacifica",
|
|
73
|
+
remediation: `Available: ${[...this._spot.keys()].map((b) => `${b}/${QUOTE}`).join(", ") || "none"}`,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return m;
|
|
77
|
+
}
|
|
78
|
+
async getSpotMarkets() {
|
|
79
|
+
const [rows, prices] = await Promise.all([this._loadSpotInfo(), this._perp.sdk.getPrices()]);
|
|
80
|
+
this._initialized = true;
|
|
81
|
+
const priceMap = new Map(prices.map((p) => [p.symbol, p]));
|
|
82
|
+
return rows.map((m) => {
|
|
83
|
+
const p = priceMap.get(m.symbol);
|
|
84
|
+
if (!p)
|
|
85
|
+
throw new PerpError("PRICE_STALE", `No price for Pacifica spot market ${m.symbol}`, { exchange: "pacifica" });
|
|
86
|
+
return {
|
|
87
|
+
symbol: `${m.base_asset}/${QUOTE}`,
|
|
88
|
+
baseToken: m.base_asset,
|
|
89
|
+
quoteToken: QUOTE,
|
|
90
|
+
markPrice: p.mark,
|
|
91
|
+
volume24h: p.volume_24h,
|
|
92
|
+
sizeDecimals: decimalsOf(m.lot_size),
|
|
93
|
+
priceDecimals: decimalsOf(m.tick_size),
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async getSpotOrderbook(symbol) {
|
|
98
|
+
await this.init();
|
|
99
|
+
const m = this._resolve(symbol);
|
|
100
|
+
const book = await this._perp.sdk.getBook(m.symbol);
|
|
101
|
+
return {
|
|
102
|
+
bids: book.l[0].map((e) => [e.p, e.a]),
|
|
103
|
+
asks: book.l[1].map((e) => [e.p, e.a]),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
async getSpotBalances() {
|
|
107
|
+
const info = (await this._perp.sdk.getAccount(this._perp.publicKey));
|
|
108
|
+
const rows = info.spot_balances;
|
|
109
|
+
if (!Array.isArray(rows)) {
|
|
110
|
+
throw new PerpError("INVALID_PARAMS", "Pacifica /account response has no spot_balances array", { exchange: "pacifica" });
|
|
111
|
+
}
|
|
112
|
+
const out = [];
|
|
113
|
+
// USDC = the shared perp collateral (same money as perp equity).
|
|
114
|
+
const total = String(info.balance);
|
|
115
|
+
const available = String(info.available_to_spend);
|
|
116
|
+
out.push({ token: QUOTE, total, available, held: subDecimal(total, available), shared: true });
|
|
117
|
+
for (const r of rows) {
|
|
118
|
+
const t = String(r.amount);
|
|
119
|
+
const a = String(r.available_to_withdraw);
|
|
120
|
+
out.push({ token: String(r.symbol), total: t, available: a, held: subDecimal(t, a), shared: false });
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
/** Mark price for min-notional / slippage checks. */
|
|
125
|
+
async _mark(wire) {
|
|
126
|
+
const p = (await this._perp.sdk.getPrices()).find((x) => x.symbol === wire);
|
|
127
|
+
if (!p)
|
|
128
|
+
throw new PerpError("PRICE_STALE", `No price for ${wire}`, { exchange: "pacifica" });
|
|
129
|
+
return p.mark;
|
|
130
|
+
}
|
|
131
|
+
_checkNotional(m, price, size) {
|
|
132
|
+
if (Number(price) * Number(size) < Number(m.min_order_size)) {
|
|
133
|
+
throw new PerpError("SIZE_TOO_SMALL", `Notional ${(Number(price) * Number(size)).toFixed(4)} USD is below Pacifica spot minimum ${m.min_order_size} USD`, {
|
|
134
|
+
exchange: "pacifica",
|
|
135
|
+
remediation: `Increase size so price x size >= ${m.min_order_size} USD`,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Throws unless the response is an accepted order; returns order_id when present. */
|
|
140
|
+
_accepted(res, what) {
|
|
141
|
+
const r = res;
|
|
142
|
+
if (r.success === false || r.error) {
|
|
143
|
+
throw new PerpError("EXCHANGE_ERROR", `${what}: ${String(r.error ?? "order rejected")}`, { exchange: "pacifica" });
|
|
144
|
+
}
|
|
145
|
+
const data = (r.data ?? r);
|
|
146
|
+
return { orderId: data.order_id != null ? String(data.order_id) : null };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Poll history_by_id until a terminal state (or, for resting orders, the first
|
|
150
|
+
* row) — otherwise finality "pending", never resubmit. The order is already
|
|
151
|
+
* accepted, so a poll error is reported as pending (with the order id), not thrown.
|
|
152
|
+
* `amount` = the placed size (a "filled" row without filled_amount filled all of it).
|
|
153
|
+
* `finalFill` = the reported filled_amount of a terminal row (filled / cancelled /
|
|
154
|
+
* rejected) when `wantTerminal` (IOC / market) — the venue-final fill for iocOutcomeOf;
|
|
155
|
+
* null otherwise (no report, resting order, pending), never the placed amount.
|
|
156
|
+
*/
|
|
157
|
+
async _settle(orderId, amount, wantTerminal) {
|
|
158
|
+
const pending = (why) => ({
|
|
159
|
+
result: {
|
|
160
|
+
status: "pending",
|
|
161
|
+
filledSize: null,
|
|
162
|
+
avgPrice: null,
|
|
163
|
+
finality: "pending",
|
|
164
|
+
remediation: `Order ${orderId} was accepted but ${why}; do NOT resubmit. Check: perp -e pacifica trade spot-orders / spot-balance`,
|
|
165
|
+
},
|
|
166
|
+
finalFill: null,
|
|
167
|
+
});
|
|
168
|
+
const done = (result, reported) => ({ result, finalFill: wantTerminal ? reported : null });
|
|
169
|
+
const attempts = wantTerminal ? 10 : 3;
|
|
170
|
+
try {
|
|
171
|
+
for (let i = 0; i < attempts; i++) {
|
|
172
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
173
|
+
const hist = await this._perp.sdk.getOrderHistoryById(Number(orderId));
|
|
174
|
+
const latest = pacificaLatestHistoryRow(hist.data ?? hist, String(orderId));
|
|
175
|
+
if (latest === null)
|
|
176
|
+
continue;
|
|
177
|
+
const st = String(latest.order_status);
|
|
178
|
+
const filled = latest.filled_amount != null ? String(latest.filled_amount) : null;
|
|
179
|
+
const avg = latest.average_filled_price != null ? String(latest.average_filled_price) : null;
|
|
180
|
+
if (st === "filled") {
|
|
181
|
+
// "filled" = the whole placed amount; filled_amount (when present) is authoritative.
|
|
182
|
+
return done({ status: "filled", filledSize: filled !== null ? filled : amount, avgPrice: avg, finality: "final", remediation: null }, filled);
|
|
183
|
+
}
|
|
184
|
+
if (st === "rejected")
|
|
185
|
+
return done({ status: "rejected", filledSize: filled, avgPrice: avg, finality: "final", remediation: null }, filled);
|
|
186
|
+
if (st === "cancelled" || st === "canceled") {
|
|
187
|
+
if (filled === null)
|
|
188
|
+
return pending(`was cancelled without a reported filled_amount (a partial fill cannot be ruled out)`);
|
|
189
|
+
return done({ status: Number(filled) > 0 ? "partial" : "cancelled", filledSize: filled, avgPrice: avg, finality: "final", remediation: null }, filled);
|
|
190
|
+
}
|
|
191
|
+
if (st === "open" && !wantTerminal) {
|
|
192
|
+
if (filled === null)
|
|
193
|
+
return done({ status: "open", filledSize: null, avgPrice: avg, finality: "final", remediation: null }, null);
|
|
194
|
+
return done({ status: Number(filled) > 0 ? "partial" : "open", filledSize: filled, avgPrice: avg, finality: "final", remediation: null }, null);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
return pending(`its status could not be read (${e instanceof Error ? e.message : String(e)})`);
|
|
200
|
+
}
|
|
201
|
+
// No terminal (or, for resting orders, no) history row within the poll window.
|
|
202
|
+
return pending("its final state was not confirmed in time");
|
|
203
|
+
}
|
|
204
|
+
/** Assemble the result and remember its venue-final fill (when proven) for iocOutcomeOf. */
|
|
205
|
+
_withOutcome(base, settled) {
|
|
206
|
+
const out = { ...base, ...settled.result };
|
|
207
|
+
if (settled.finalFill !== null && settled.finalFill.trim() !== "" && Number.isFinite(Number(settled.finalFill))) {
|
|
208
|
+
this._iocOutcomes.set(out, { filledSize: settled.finalFill.trim(), final: true });
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Venue-final outcome of a SpotOrderResult returned by spotMarketOrder / IOC
|
|
214
|
+
* spotLimitOrder (IocOutcomeCapable, design §13.10 #4): set only when the polled
|
|
215
|
+
* history row was terminal and reported filled_amount. null for anything else.
|
|
216
|
+
*/
|
|
217
|
+
iocOutcomeOf(raw) {
|
|
218
|
+
if (raw === null || typeof raw !== "object")
|
|
219
|
+
return null;
|
|
220
|
+
return this._iocOutcomes.get(raw) ?? null;
|
|
221
|
+
}
|
|
222
|
+
async spotMarketOrder(symbol, side, size, opts) {
|
|
223
|
+
// Pre-send (market resolution, mark read, notional check, signing setup): any failure here
|
|
224
|
+
// is a definite "not sent" — never a retryable error that hides whether an order went out.
|
|
225
|
+
let m;
|
|
226
|
+
let amount;
|
|
227
|
+
const bps = opts.slippage.bps;
|
|
228
|
+
try {
|
|
229
|
+
await this.init();
|
|
230
|
+
m = this._resolve(symbol);
|
|
231
|
+
requirePositive(size, "size");
|
|
232
|
+
amount = floorSize(size, m.lot_size);
|
|
233
|
+
this._checkNotional(m, await this._mark(m.symbol), amount);
|
|
234
|
+
this._perp.prepareSigning();
|
|
235
|
+
}
|
|
236
|
+
catch (e) {
|
|
237
|
+
throw orderNotSent("pacifica", `spot market ${side} ${size} ${symbol}`, e);
|
|
238
|
+
}
|
|
239
|
+
if (side === "buy")
|
|
240
|
+
warnSharedMarginOnce();
|
|
241
|
+
const res = await this._perp.sdk.createMarketOrder({ symbol: m.symbol, amount, side: side === "buy" ? "bid" : "ask", reduce_only: false, slippage_percent: String(bps / 100) }, this._perp.publicKey, this._perp.signer, null).catch((e) => { throw pacificaCreateRejection(e, `spot market ${side} ${amount} ${m.symbol}`); });
|
|
242
|
+
const { orderId } = this._accepted(res, `Spot market ${side} ${m.symbol}`);
|
|
243
|
+
const base = { venue: "pacifica", symbol: `${m.base_asset}/${QUOTE}`, orderId, receivedToken: null, venueRaw: res };
|
|
244
|
+
if (!orderId) {
|
|
245
|
+
return { ...base, status: "pending", filledSize: null, avgPrice: null, finality: "pending", remediation: "Order accepted without order_id; do NOT resubmit. Check open orders and balances." };
|
|
246
|
+
}
|
|
247
|
+
return this._withOutcome(base, await this._settle(orderId, amount, true));
|
|
248
|
+
}
|
|
249
|
+
async spotLimitOrder(symbol, side, price, size, opts) {
|
|
250
|
+
let m;
|
|
251
|
+
let tif;
|
|
252
|
+
let px;
|
|
253
|
+
let amount;
|
|
254
|
+
try { // pre-send (see spotMarketOrder)
|
|
255
|
+
await this.init();
|
|
256
|
+
m = this._resolve(symbol);
|
|
257
|
+
tif = pacificaTif(opts?.tif); // §13.3 common mapping; FOK → CAPABILITY_UNSUPPORTED
|
|
258
|
+
px = requireOnTick(price, m.tick_size);
|
|
259
|
+
requirePositive(size, "size");
|
|
260
|
+
amount = floorSize(size, m.lot_size);
|
|
261
|
+
this._checkNotional(m, px, amount);
|
|
262
|
+
this._perp.prepareSigning();
|
|
263
|
+
}
|
|
264
|
+
catch (e) {
|
|
265
|
+
throw orderNotSent("pacifica", `spot limit ${side} ${size} ${symbol} @ ${price}`, e);
|
|
266
|
+
}
|
|
267
|
+
if (side === "buy")
|
|
268
|
+
warnSharedMarginOnce();
|
|
269
|
+
const res = await this._perp.sdk.createLimitOrder({ symbol: m.symbol, price: px, amount, side: side === "buy" ? "bid" : "ask", tif, reduce_only: false }, this._perp.publicKey, this._perp.signer, null).catch((e) => { throw pacificaCreateRejection(e, `spot limit ${tif} ${side} ${amount} ${m.symbol} @ ${px}`); });
|
|
270
|
+
const { orderId } = this._accepted(res, `Spot limit ${side} ${m.symbol}`);
|
|
271
|
+
const base = { venue: "pacifica", symbol: `${m.base_asset}/${QUOTE}`, orderId, receivedToken: null, venueRaw: res };
|
|
272
|
+
if (!orderId) {
|
|
273
|
+
return { ...base, status: "pending", filledSize: null, avgPrice: null, finality: "pending", remediation: "Order accepted without order_id; do NOT resubmit. Check open orders." };
|
|
274
|
+
}
|
|
275
|
+
// IOC resolves immediately (terminal); resting TIFs just report open/partial.
|
|
276
|
+
return this._withOutcome(base, await this._settle(orderId, amount, tif === "IOC"));
|
|
277
|
+
}
|
|
278
|
+
async spotCancelOrder(symbol, orderId) {
|
|
279
|
+
await this.init();
|
|
280
|
+
const m = this._resolve(symbol);
|
|
281
|
+
this._perp.prepareSigning();
|
|
282
|
+
const res = await this._perp.sdk.cancelOrder({ symbol: m.symbol, order_id: Number(orderId) }, this._perp.publicKey, this._perp.signer);
|
|
283
|
+
this._accepted(res, `Spot cancel ${m.symbol} ${orderId}`);
|
|
284
|
+
return {
|
|
285
|
+
venue: "pacifica", symbol: `${m.base_asset}/${QUOTE}`, orderId, status: "cancelled",
|
|
286
|
+
filledSize: null, avgPrice: null, finality: "final", receivedToken: null, remediation: null, venueRaw: res,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
async getSpotOpenOrders(symbol) {
|
|
290
|
+
await this.init();
|
|
291
|
+
const wire = symbol === null ? null : this._resolve(symbol).symbol;
|
|
292
|
+
const spotWire = new Set([...this._spot.values()].map((m) => m.symbol));
|
|
293
|
+
const orders = await this._perp.sdk.getOrders(this._perp.publicKey);
|
|
294
|
+
return orders
|
|
295
|
+
.map((o) => o)
|
|
296
|
+
.filter((o) => spotWire.has(String(o.symbol)) && (wire === null || o.symbol === wire))
|
|
297
|
+
.map((o) => {
|
|
298
|
+
const w = String(o.symbol);
|
|
299
|
+
return {
|
|
300
|
+
orderId: String(o.order_id),
|
|
301
|
+
symbol: `${w.split("-")[0]}/${QUOTE}`,
|
|
302
|
+
side: o.side === "bid" ? "buy" : o.side === "ask" ? "sell" : (() => {
|
|
303
|
+
throw new PerpError("EXCHANGE_ERROR", `Pacifica spot order ${String(o.order_id)} has an unrecognized side ${JSON.stringify(o.side ?? null)} (expected "bid" / "ask")`, {
|
|
304
|
+
exchange: "pacifica", remediation: "Retry: perp spot orders -e pacifica",
|
|
305
|
+
});
|
|
306
|
+
})(),
|
|
307
|
+
price: String(o.price),
|
|
308
|
+
size: reqField(o, "initial_amount"),
|
|
309
|
+
filled: reqField(o, "filled_amount"),
|
|
310
|
+
};
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Cancels spot orders one by one. The venue `cancel_all` with all_symbols
|
|
315
|
+
* would also cancel perp orders, so it is never used here.
|
|
316
|
+
*/
|
|
317
|
+
async spotCancelAllOrders(symbol) {
|
|
318
|
+
const open = await this.getSpotOpenOrders(symbol);
|
|
319
|
+
const results = [];
|
|
320
|
+
for (const o of open)
|
|
321
|
+
results.push(await this.spotCancelOrder(o.symbol, o.orderId));
|
|
322
|
+
return results;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
@@ -12,10 +12,14 @@
|
|
|
12
12
|
* where header is `{ type, timestamp, expiry_window }`. The `data` key
|
|
13
13
|
* is sorted lexicographically alongside the header fields.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
* `
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* `bind_agent_wallet` mirrors HypurrQuant_FE `PacificaPerpAdapter.ts` (see
|
|
16
|
+
* project memory `hypurrquant_fe_reference.md`).
|
|
17
|
+
*
|
|
18
|
+
* Revocation follows the OFFICIAL Pacifica SDK (`rest/api_agent_keys_detailed.py`):
|
|
19
|
+
* `revoke_agent_wallet` → POST /agent/revoke, `revoke_all_agent_wallets` →
|
|
20
|
+
* POST /agent/revoke_all. An earlier `unbind_agent_wallet` → /agent/bind shape
|
|
21
|
+
* was carried over from the HypurrQuant reference and is NOT part of Pacifica's
|
|
22
|
+
* API; it was silently rejected, leaving revoked-looking agents authorized.
|
|
19
23
|
*
|
|
20
24
|
* Pure functions — no side effects, no fs/fetch/env reads.
|
|
21
25
|
*/
|
|
@@ -31,13 +35,10 @@ export interface BindAgentMessageParams {
|
|
|
31
35
|
/** Optional expiry window (ms); defaults to DEFAULT_EXPIRY_WINDOW. */
|
|
32
36
|
expiryWindow?: number;
|
|
33
37
|
}
|
|
34
|
-
export interface
|
|
38
|
+
export interface RevokeAgentMessageParams {
|
|
35
39
|
/** Master Solana base58 public key. */
|
|
36
40
|
account: string;
|
|
37
|
-
/**
|
|
38
|
-
* Agent Solana base58 public key to unbind. Pass an empty string to
|
|
39
|
-
* unbind all agent wallets per Pacifica convention.
|
|
40
|
-
*/
|
|
41
|
+
/** Agent Solana base58 public key to revoke. Required — must not be empty. */
|
|
41
42
|
agentWallet: string;
|
|
42
43
|
timestamp?: number;
|
|
43
44
|
expiryWindow?: number;
|
|
@@ -67,9 +68,25 @@ export interface BuiltMessage {
|
|
|
67
68
|
*/
|
|
68
69
|
export declare function buildBindAgentMessage(params: BindAgentMessageParams): BuiltMessage;
|
|
69
70
|
/**
|
|
70
|
-
* Build the canonical JSON for `
|
|
71
|
+
* Build the canonical JSON for `revoke_agent_wallet` (POST /agent/revoke).
|
|
71
72
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
73
|
+
* Revokes exactly one agent wallet. To revoke every agent, use
|
|
74
|
+
* `buildRevokeAllAgentsMessage` — Pacifica exposes that as its own operation
|
|
75
|
+
* type and endpoint, not as an empty-string sentinel on this one.
|
|
76
|
+
*/
|
|
77
|
+
export declare function buildRevokeAgentMessage(params: RevokeAgentMessageParams): BuiltMessage;
|
|
78
|
+
/**
|
|
79
|
+
* Build the canonical JSON for `revoke_all_agent_wallets`
|
|
80
|
+
* (POST /agent/revoke_all). Payload is empty per the official SDK.
|
|
74
81
|
*/
|
|
75
|
-
export declare function
|
|
82
|
+
export declare function buildRevokeAllAgentsMessage(params: Omit<RevokeAgentMessageParams, "agentWallet">): BuiltMessage;
|
|
83
|
+
/**
|
|
84
|
+
* Build the canonical JSON for `list_agent_wallets` (POST /agent/list).
|
|
85
|
+
* Payload is empty per the official SDK (`rest/api_agent_keys_detailed.py`
|
|
86
|
+
* `list_agent_wallets`), so the signed message nests `"data":{}`:
|
|
87
|
+
* {"data":{},"expiry_window":5000,"timestamp":...,"type":"list_agent_wallets"}
|
|
88
|
+
*/
|
|
89
|
+
export declare function buildListAgentWalletsMessage(params?: {
|
|
90
|
+
timestamp?: number;
|
|
91
|
+
expiryWindow?: number;
|
|
92
|
+
}): BuiltMessage;
|
|
@@ -12,10 +12,14 @@
|
|
|
12
12
|
* where header is `{ type, timestamp, expiry_window }`. The `data` key
|
|
13
13
|
* is sorted lexicographically alongside the header fields.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
* `
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* `bind_agent_wallet` mirrors HypurrQuant_FE `PacificaPerpAdapter.ts` (see
|
|
16
|
+
* project memory `hypurrquant_fe_reference.md`).
|
|
17
|
+
*
|
|
18
|
+
* Revocation follows the OFFICIAL Pacifica SDK (`rest/api_agent_keys_detailed.py`):
|
|
19
|
+
* `revoke_agent_wallet` → POST /agent/revoke, `revoke_all_agent_wallets` →
|
|
20
|
+
* POST /agent/revoke_all. An earlier `unbind_agent_wallet` → /agent/bind shape
|
|
21
|
+
* was carried over from the HypurrQuant reference and is NOT part of Pacifica's
|
|
22
|
+
* API; it was silently rejected, leaving revoked-looking agents authorized.
|
|
19
23
|
*
|
|
20
24
|
* Pure functions — no side effects, no fs/fetch/env reads.
|
|
21
25
|
*/
|
|
@@ -51,15 +55,21 @@ export function buildBindAgentMessage(params) {
|
|
|
51
55
|
};
|
|
52
56
|
}
|
|
53
57
|
/**
|
|
54
|
-
* Build the canonical JSON for `
|
|
58
|
+
* Build the canonical JSON for `revoke_agent_wallet` (POST /agent/revoke).
|
|
55
59
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
60
|
+
* Revokes exactly one agent wallet. To revoke every agent, use
|
|
61
|
+
* `buildRevokeAllAgentsMessage` — Pacifica exposes that as its own operation
|
|
62
|
+
* type and endpoint, not as an empty-string sentinel on this one.
|
|
58
63
|
*/
|
|
59
|
-
export function
|
|
64
|
+
export function buildRevokeAgentMessage(params) {
|
|
65
|
+
if (!params.agentWallet) {
|
|
66
|
+
// Rule #2: an empty agent address is not a "revoke all" sentinel on this
|
|
67
|
+
// operation. Signing one would produce a request the venue cannot act on.
|
|
68
|
+
throw new Error("buildRevokeAgentMessage requires a non-empty agentWallet; use buildRevokeAllAgentsMessage to revoke every agent.");
|
|
69
|
+
}
|
|
60
70
|
const timestamp = params.timestamp ?? Date.now();
|
|
61
71
|
const expiryWindow = params.expiryWindow ?? DEFAULT_EXPIRY_WINDOW;
|
|
62
|
-
const type = "
|
|
72
|
+
const type = "revoke_agent_wallet";
|
|
63
73
|
const payload = {
|
|
64
74
|
agent_wallet: params.agentWallet,
|
|
65
75
|
};
|
|
@@ -72,3 +82,41 @@ export function buildUnbindAgentMessage(params) {
|
|
|
72
82
|
payload,
|
|
73
83
|
};
|
|
74
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Build the canonical JSON for `revoke_all_agent_wallets`
|
|
87
|
+
* (POST /agent/revoke_all). Payload is empty per the official SDK.
|
|
88
|
+
*/
|
|
89
|
+
export function buildRevokeAllAgentsMessage(params) {
|
|
90
|
+
const timestamp = params.timestamp ?? Date.now();
|
|
91
|
+
const expiryWindow = params.expiryWindow ?? DEFAULT_EXPIRY_WINDOW;
|
|
92
|
+
const type = "revoke_all_agent_wallets";
|
|
93
|
+
const payload = {};
|
|
94
|
+
const header = { type, timestamp, expiry_window: expiryWindow };
|
|
95
|
+
const wrapped = { ...header, data: payload };
|
|
96
|
+
const sorted = sortJsonKeys(wrapped);
|
|
97
|
+
return {
|
|
98
|
+
canonicalJson: JSON.stringify(sorted),
|
|
99
|
+
header,
|
|
100
|
+
payload,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Build the canonical JSON for `list_agent_wallets` (POST /agent/list).
|
|
105
|
+
* Payload is empty per the official SDK (`rest/api_agent_keys_detailed.py`
|
|
106
|
+
* `list_agent_wallets`), so the signed message nests `"data":{}`:
|
|
107
|
+
* {"data":{},"expiry_window":5000,"timestamp":...,"type":"list_agent_wallets"}
|
|
108
|
+
*/
|
|
109
|
+
export function buildListAgentWalletsMessage(params = {}) {
|
|
110
|
+
const timestamp = params.timestamp ?? Date.now();
|
|
111
|
+
const expiryWindow = params.expiryWindow ?? DEFAULT_EXPIRY_WINDOW;
|
|
112
|
+
const type = "list_agent_wallets";
|
|
113
|
+
const payload = {};
|
|
114
|
+
const header = { type, timestamp, expiry_window: expiryWindow };
|
|
115
|
+
const wrapped = { ...header, data: payload };
|
|
116
|
+
const sorted = sortJsonKeys(wrapped);
|
|
117
|
+
return {
|
|
118
|
+
canonicalJson: JSON.stringify(sorted),
|
|
119
|
+
header,
|
|
120
|
+
payload,
|
|
121
|
+
};
|
|
122
|
+
}
|