perp-cli 0.14.3 → 0.16.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.ja.md +243 -0
- package/README.ko.md +243 -0
- package/README.md +138 -405
- package/README.zh.md +243 -0
- package/dist/agent-wallet/store.d.ts +3 -2
- package/dist/agent-wallet/store.js +2 -2
- package/dist/api/public/hyperliquid.js +4 -0
- package/dist/api/public/lighter.d.ts +5 -0
- package/dist/api/public/lighter.js +5 -0
- package/dist/api/public/pacifica.js +85 -15
- package/dist/api/public/urls.d.ts +2 -0
- package/dist/api/public/urls.js +3 -0
- package/dist/arb/sizing.js +4 -2
- package/dist/arb/state.d.ts +6 -0
- package/dist/arb/utils.d.ts +1 -2
- package/dist/arb/utils.js +1 -2
- package/dist/balance-by-address.d.ts +39 -0
- package/dist/balance-by-address.js +44 -0
- package/dist/bot/apex/pulse.js +3 -0
- package/dist/bot/apex/radar.js +2 -1
- package/dist/bot/engine.d.ts +10 -1
- package/dist/bot/engine.js +14 -6
- package/dist/bot/indicators.d.ts +7 -0
- package/dist/bot/indicators.js +16 -0
- package/dist/bot/perp-order.d.ts +4 -4
- package/dist/bot/perp-order.js +5 -5
- package/dist/bot/quarantine.d.ts +1 -1
- package/dist/bot/quarantine.js +1 -1
- package/dist/bot/quote-rules.js +1 -1
- package/dist/bot/strategies/apex-strategy.js +3 -2
- package/dist/bot/strategies/engine-mm.js +6 -5
- package/dist/bot/strategies/funding-arb-strategy.js +12 -5
- package/dist/bot/strategies/funding-arb-utils.d.ts +15 -3
- package/dist/bot/strategies/funding-arb-utils.js +53 -20
- package/dist/bot/strategies/funding-auto.js +2 -2
- package/dist/bot/strategies/liquidation-mm.d.ts +1 -0
- package/dist/bot/strategies/liquidation-mm.js +8 -2
- package/dist/bot/strategies/momentum-breakout.d.ts +1 -0
- package/dist/bot/strategies/momentum-breakout.js +11 -3
- package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +1 -1
- package/dist/bot/strategies/spot-perp-arb-strategy.js +6 -4
- package/dist/bot/strategies/spot-perp-executor.d.ts +1 -1
- package/dist/bot/strategies/spot-perp-executor.js +3 -3
- package/dist/bot/strategy-types.d.ts +2 -1
- package/dist/bot/tui/BotDashboard.d.ts +1 -1
- package/dist/bot/tui/BotDashboard.js +1 -1
- package/dist/cache.js +12 -6
- package/dist/cli-program.d.ts +15 -0
- package/dist/cli-program.js +126 -6
- package/dist/commands/account.js +168 -24
- package/dist/commands/agent-import.d.ts +7 -0
- package/dist/commands/agent-import.js +30 -1
- package/dist/commands/agent.d.ts +6 -3
- package/dist/commands/agent.js +160 -12
- package/dist/commands/arb/index.js +23 -81
- package/dist/commands/arb-auto.d.ts +24 -0
- package/dist/commands/arb-auto.js +175 -97
- package/dist/commands/arb.d.ts +20 -0
- package/dist/commands/arb.js +107 -163
- package/dist/commands/backtest.js +23 -21
- package/dist/commands/bot.js +1 -1
- package/dist/commands/builder.js +1 -1
- package/dist/commands/funds.js +113 -3
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +27 -7
- package/dist/commands/manage.js +23 -21
- package/dist/commands/market.d.ts +48 -0
- package/dist/commands/market.js +395 -100
- package/dist/commands/options.d.ts +14 -0
- package/dist/commands/options.js +151 -0
- package/dist/commands/pacifica-earn.d.ts +5 -0
- package/dist/commands/pacifica-earn.js +455 -0
- package/dist/commands/pacifica-orders.d.ts +14 -0
- package/dist/commands/pacifica-orders.js +142 -0
- package/dist/commands/portfolio.d.ts +16 -0
- package/dist/commands/portfolio.js +83 -55
- package/dist/commands/rebalance.d.ts +1 -0
- package/dist/commands/rebalance.js +15 -0
- package/dist/commands/risk.d.ts +11 -2
- package/dist/commands/risk.js +39 -44
- package/dist/commands/trade.d.ts +1 -0
- package/dist/commands/trade.js +67 -62
- package/dist/commands/wallet.js +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.js +15 -4
- package/dist/constants.js +6 -0
- package/dist/cross-chain-margin.d.ts +3 -1
- package/dist/cross-chain-margin.js +4 -2
- package/dist/dashboard/server.js +3 -3
- package/dist/dashboard/ui.js +9 -6
- package/dist/errors.d.ts +29 -1
- package/dist/errors.js +35 -3
- package/dist/exchanges/address.d.ts +8 -0
- package/dist/exchanges/address.js +47 -0
- package/dist/exchanges/arcus/adapter.d.ts +16 -12
- package/dist/exchanges/arcus/adapter.js +45 -18
- package/dist/exchanges/arcus/agent.d.ts +1 -1
- package/dist/exchanges/arcus/agent.js +1 -1
- package/dist/exchanges/arcus/client.js +1 -1
- package/dist/exchanges/arcus/public.js +3 -2
- package/dist/exchanges/arcus/signing.d.ts +2 -3
- package/dist/exchanges/arcus/signing.js +1 -1
- package/dist/exchanges/arcus/spot-chain.d.ts +1 -1
- package/dist/exchanges/arcus/spot-chain.js +1 -1
- package/dist/exchanges/arcus/spot-router.d.ts +1 -1
- package/dist/exchanges/arcus/spot-router.js +2 -2
- package/dist/exchanges/arcus/spot.d.ts +2 -3
- package/dist/exchanges/arcus/spot.js +2 -3
- package/dist/exchanges/aster-spot.d.ts +3 -3
- package/dist/exchanges/aster-spot.js +6 -9
- package/dist/exchanges/aster-typed-data.d.ts +1 -1
- package/dist/exchanges/aster-typed-data.js +1 -1
- package/dist/exchanges/aster.d.ts +30 -10
- package/dist/exchanges/aster.js +153 -53
- package/dist/exchanges/builder-fee.d.ts +1 -1
- package/dist/exchanges/builder-fee.js +13 -2
- package/dist/exchanges/canonical-symbols.d.ts +8 -2
- package/dist/exchanges/canonical-symbols.js +46 -2
- package/dist/exchanges/capabilities.d.ts +2 -0
- package/dist/exchanges/capabilities.js +2 -1
- package/dist/exchanges/derive/client.d.ts +37 -0
- package/dist/exchanges/derive/client.js +135 -0
- package/dist/exchanges/derive/options.d.ts +73 -0
- package/dist/exchanges/derive/options.js +379 -0
- package/dist/exchanges/extended/adapter.d.ts +96 -0
- package/dist/exchanges/extended/adapter.js +241 -0
- package/dist/exchanges/extended/client.d.ts +38 -0
- package/dist/exchanges/extended/client.js +88 -0
- package/dist/exchanges/extended/public.d.ts +133 -0
- package/dist/exchanges/extended/public.js +436 -0
- package/dist/exchanges/grvt/adapter.d.ts +100 -0
- package/dist/exchanges/grvt/adapter.js +283 -0
- package/dist/exchanges/grvt/client.d.ts +25 -0
- package/dist/exchanges/grvt/client.js +86 -0
- package/dist/exchanges/grvt/public.d.ts +151 -0
- package/dist/exchanges/grvt/public.js +438 -0
- package/dist/exchanges/hyperliquid-outcome.d.ts +2 -0
- package/dist/exchanges/hyperliquid-outcome.js +29 -1
- package/dist/exchanges/hyperliquid-spot.d.ts +9 -1
- package/dist/exchanges/hyperliquid-spot.js +24 -16
- package/dist/exchanges/hyperliquid.d.ts +199 -22
- package/dist/exchanges/hyperliquid.js +550 -202
- package/dist/exchanges/index.d.ts +1 -1
- package/dist/exchanges/index.js +1 -1
- package/dist/exchanges/interface.d.ts +157 -13
- package/dist/exchanges/interface.js +47 -0
- package/dist/exchanges/ioc-outcome.d.ts +1 -1
- package/dist/exchanges/ioc-outcome.js +1 -1
- package/dist/exchanges/legacy-trading-v2.d.ts +2 -2
- package/dist/exchanges/legacy-trading-v2.js +7 -2
- package/dist/exchanges/lighter-public.d.ts +67 -12
- package/dist/exchanges/lighter-public.js +250 -82
- package/dist/exchanges/lighter-signer.d.ts +1 -1
- package/dist/exchanges/lighter-signer.js +1 -1
- package/dist/exchanges/lighter-spot.d.ts +10 -5
- package/dist/exchanges/lighter-spot.js +105 -97
- package/dist/exchanges/lighter.d.ts +80 -21
- package/dist/exchanges/lighter.js +312 -105
- package/dist/exchanges/ondo/adapter.d.ts +38 -13
- package/dist/exchanges/ondo/adapter.js +86 -27
- package/dist/exchanges/ondo/agent.d.ts +1 -1
- package/dist/exchanges/ondo/agent.js +1 -1
- package/dist/exchanges/ondo/client.d.ts +1 -1
- package/dist/exchanges/ondo/client.js +2 -2
- package/dist/exchanges/ondo/public.d.ts +35 -3
- package/dist/exchanges/ondo/public.js +75 -32
- package/dist/exchanges/ondo/spot.d.ts +2 -2
- package/dist/exchanges/ondo/spot.js +3 -3
- package/dist/exchanges/options-interface.d.ts +158 -0
- package/dist/exchanges/options-interface.js +17 -0
- package/dist/exchanges/options-venues.d.ts +12 -0
- package/dist/exchanges/options-venues.js +35 -0
- package/dist/exchanges/outcome-interface.d.ts +4 -0
- package/dist/exchanges/pacifica-atomic.d.ts +211 -0
- package/dist/exchanges/pacifica-atomic.js +440 -0
- package/dist/exchanges/pacifica-decimal.d.ts +10 -0
- package/dist/exchanges/pacifica-decimal.js +51 -0
- package/dist/exchanges/pacifica-presigned.d.ts +53 -0
- package/dist/exchanges/pacifica-presigned.js +141 -0
- package/dist/exchanges/pacifica-print.d.ts +200 -0
- package/dist/exchanges/pacifica-print.js +417 -0
- package/dist/exchanges/pacifica-spot.d.ts +8 -2
- package/dist/exchanges/pacifica-spot.js +26 -18
- package/dist/exchanges/pacifica-typed-data.d.ts +1 -1
- package/dist/exchanges/pacifica-typed-data.js +1 -1
- package/dist/exchanges/pacifica-vaults.d.ts +130 -0
- package/dist/exchanges/pacifica-vaults.js +277 -0
- package/dist/exchanges/pacifica.d.ts +108 -10
- package/dist/exchanges/pacifica.js +428 -171
- package/dist/exchanges/precision.d.ts +3 -1
- package/dist/exchanges/precision.js +9 -2
- package/dist/exchanges/public-balances.d.ts +25 -0
- package/dist/exchanges/public-balances.js +158 -0
- package/dist/exchanges/public-positions.d.ts +43 -0
- package/dist/exchanges/public-positions.js +154 -0
- package/dist/exchanges/qfex/adapter.d.ts +69 -13
- package/dist/exchanges/qfex/adapter.js +102 -17
- package/dist/exchanges/qfex/agent.d.ts +1 -1
- package/dist/exchanges/qfex/agent.js +1 -1
- package/dist/exchanges/qfex/client.d.ts +1 -1
- package/dist/exchanges/qfex/client.js +1 -1
- package/dist/exchanges/qfex/errors.d.ts +1 -1
- package/dist/exchanges/qfex/errors.js +2 -2
- package/dist/exchanges/qfex/public.d.ts +65 -12
- package/dist/exchanges/qfex/public.js +131 -25
- package/dist/exchanges/qfex/signing.d.ts +2 -2
- package/dist/exchanges/qfex/signing.js +4 -5
- package/dist/exchanges/qfex/ws-transport.d.ts +3 -3
- package/dist/exchanges/qfex/ws-transport.js +3 -3
- package/dist/exchanges/rate-limiter.d.ts +1 -1
- package/dist/exchanges/rate-limiter.js +5 -2
- package/dist/exchanges/registry.d.ts +1 -1
- package/dist/exchanges/registry.js +1 -1
- package/dist/exchanges/risex/adapter.d.ts +160 -0
- package/dist/exchanges/risex/adapter.js +842 -0
- package/dist/exchanges/risex/agent.d.ts +154 -0
- package/dist/exchanges/risex/agent.js +384 -0
- package/dist/exchanges/risex/client.d.ts +47 -0
- package/dist/exchanges/risex/client.js +174 -0
- package/dist/exchanges/risex/nonce.d.ts +43 -0
- package/dist/exchanges/risex/nonce.js +212 -0
- package/dist/exchanges/risex/public.d.ts +64 -0
- package/dist/exchanges/risex/public.js +150 -0
- package/dist/exchanges/risex/session.d.ts +71 -0
- package/dist/exchanges/risex/session.js +171 -0
- package/dist/exchanges/risex/typed-data.d.ts +173 -0
- package/dist/exchanges/risex/typed-data.js +240 -0
- package/dist/exchanges/risex/units.d.ts +29 -0
- package/dist/exchanges/risex/units.js +104 -0
- package/dist/exchanges/slippage.d.ts +1 -1
- package/dist/exchanges/slippage.js +1 -1
- package/dist/exchanges/spot-factory.d.ts +3 -3
- package/dist/exchanges/spot-factory.js +8 -8
- package/dist/exchanges/spot-interface.d.ts +5 -5
- package/dist/exchanges/spot-interface.js +2 -2
- package/dist/exchanges/spot-result.d.ts +3 -3
- package/dist/exchanges/spot-result.js +3 -3
- package/dist/exchanges/trading-v2.d.ts +3 -3
- package/dist/exchanges/trading-v2.js +9 -6
- package/dist/exchanges/txflow/adapter.d.ts +61 -0
- package/dist/exchanges/txflow/adapter.js +150 -0
- package/dist/exchanges/txflow/client.d.ts +47 -0
- package/dist/exchanges/txflow/client.js +155 -0
- package/dist/exchanges/txflow/public.d.ts +87 -0
- package/dist/exchanges/txflow/public.js +355 -0
- package/dist/exchanges/unknown-symbol.d.ts +34 -0
- package/dist/exchanges/unknown-symbol.js +56 -0
- package/dist/exchanges/variational/adapter.d.ts +67 -0
- package/dist/exchanges/variational/adapter.js +137 -0
- package/dist/exchanges/variational/client.d.ts +21 -0
- package/dist/exchanges/variational/client.js +66 -0
- package/dist/exchanges/variational/public.d.ts +126 -0
- package/dist/exchanges/variational/public.js +430 -0
- package/dist/exchanges/venue-types.d.ts +52 -10
- package/dist/exchanges/venue-types.js +82 -11
- package/dist/exchanges/venue.d.ts +150 -11
- package/dist/exchanges/venue.js +126 -4
- package/dist/exchanges/venues/arcus.d.ts +1 -2
- package/dist/exchanges/venues/arcus.js +5 -4
- package/dist/exchanges/venues/aster.d.ts +2 -2
- package/dist/exchanges/venues/aster.js +6 -4
- package/dist/exchanges/venues/create-helpers.d.ts +1 -1
- package/dist/exchanges/venues/create-helpers.js +3 -3
- package/dist/exchanges/venues/extended.d.ts +21 -0
- package/dist/exchanges/venues/extended.js +96 -0
- package/dist/exchanges/venues/grvt.d.ts +30 -0
- package/dist/exchanges/venues/grvt.js +98 -0
- package/dist/exchanges/venues/hyperliquid.d.ts +2 -2
- package/dist/exchanges/venues/hyperliquid.js +19 -8
- package/dist/exchanges/venues/index.d.ts +7 -2
- package/dist/exchanges/venues/index.js +8 -2
- package/dist/exchanges/venues/lighter-family.d.ts +7 -9
- package/dist/exchanges/venues/lighter-family.js +9 -11
- package/dist/exchanges/venues/lighter-rh.d.ts +2 -2
- package/dist/exchanges/venues/lighter-rh.js +20 -8
- package/dist/exchanges/venues/lighter.d.ts +1 -1
- package/dist/exchanges/venues/lighter.js +18 -6
- package/dist/exchanges/venues/ondo.d.ts +1 -1
- package/dist/exchanges/venues/ondo.js +5 -3
- package/dist/exchanges/venues/pacifica.d.ts +2 -2
- package/dist/exchanges/venues/pacifica.js +15 -4
- package/dist/exchanges/venues/qfex.d.ts +1 -1
- package/dist/exchanges/venues/qfex.js +4 -2
- package/dist/exchanges/venues/risex-release.d.ts +14 -0
- package/dist/exchanges/venues/risex-release.js +14 -0
- package/dist/exchanges/venues/risex.d.ts +31 -0
- package/dist/exchanges/venues/risex.js +131 -0
- package/dist/exchanges/venues/txflow.d.ts +18 -0
- package/dist/exchanges/venues/txflow.js +77 -0
- package/dist/exchanges/venues/variational.d.ts +21 -0
- package/dist/exchanges/venues/variational.js +85 -0
- package/dist/execution-log.d.ts +1 -1
- package/dist/funding/legacy-public.d.ts +6 -4
- package/dist/funding/legacy-public.js +42 -41
- package/dist/funding/normalize.d.ts +1 -1
- package/dist/funding/normalize.js +1 -1
- package/dist/funding/rates.d.ts +95 -6
- package/dist/funding/rates.js +99 -19
- package/dist/index.js +7 -6
- package/dist/jobs.d.ts +1 -0
- package/dist/jobs.js +25 -1
- package/dist/landing.d.ts +2 -0
- package/dist/landing.js +1 -1
- package/dist/liquidity.d.ts +3 -1
- package/dist/liquidity.js +8 -4
- package/dist/mcp-pacifica-earn.d.ts +9 -0
- package/dist/mcp-pacifica-earn.js +64 -0
- package/dist/mcp-server.js +211 -68
- package/dist/pacifica/client.d.ts +69 -5
- package/dist/pacifica/client.js +181 -8
- package/dist/pacifica/rate-limit.d.ts +144 -0
- package/dist/pacifica/rate-limit.js +267 -0
- package/dist/pacifica/types/order.d.ts +2 -1
- package/dist/plan-executor.js +7 -2
- package/dist/positions-by-address.d.ts +34 -0
- package/dist/positions-by-address.js +46 -0
- package/dist/rebalance.js +1 -1
- package/dist/risk.d.ts +2 -1
- package/dist/risk.js +2 -4
- package/dist/settings.d.ts +32 -3
- package/dist/signer/ows-evm.d.ts +1 -1
- package/dist/signer/ows-evm.js +1 -1
- package/dist/smart-order.d.ts +1 -1
- package/dist/smart-order.js +1 -1
- package/dist/strategies/funding-arb.d.ts +1 -1
- package/dist/strategies/funding-arb.js +36 -63
- package/dist/trade-validator.js +2 -2
- package/dist/utils.d.ts +22 -3
- package/dist/utils.js +11 -3
- package/dist/venue-health.d.ts +40 -0
- package/dist/venue-health.js +75 -0
- package/dist/withdraw-gate.d.ts +1 -1
- package/dist/withdraw-gate.js +1 -1
- package/dist/ws/arcus-market.d.ts +9 -0
- package/dist/ws/arcus-market.js +9 -0
- package/dist/ws/aster-market.d.ts +33 -0
- package/dist/ws/aster-market.js +30 -0
- package/dist/ws/index.d.ts +980 -0
- package/dist/ws/index.js +4460 -0
- package/dist/ws/lighter-market.d.ts +14 -0
- package/dist/ws/lighter-market.js +13 -0
- package/dist/ws/node.d.ts +996 -0
- package/dist/ws/node.js +4554 -0
- package/dist/ws/pacifica-instrument.d.ts +8 -0
- package/dist/ws/pacifica-instrument.js +8 -0
- package/dist/ws/unclassified.d.ts +16 -0
- package/dist/ws/unclassified.js +43 -0
- package/dist/ws-cli/credentials.d.ts +62 -0
- package/dist/ws-cli/credentials.js +126 -0
- package/package.json +71 -10
- package/skills/perp-cli/SKILL.md +153 -36
- package/skills/perp-cli/package.json +15 -2
- package/skills/perp-cli/references/agent-operations.md +22 -12
- package/skills/perp-cli/references/commands.md +143 -17
- package/skills/perp-cli/references/strategies.md +38 -3
- package/skills/perp-cli/references/ws-library.md +523 -0
- package/skills/perp-cli/scripts/arb-monitor.sh +13 -10
- package/skills/perp-cli/scripts/funding-analysis.sh +57 -29
- package/skills/perp-cli/scripts/preflight.sh +27 -19
- package/skills/perp-cli/scripts/spot-perp-scan.sh +58 -38
- package/skills/perp-cli/scripts/validate-arb.sh +56 -39
- package/dist/cli-spec.d.ts +0 -46
- package/dist/cli-spec.js +0 -81
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pacifica Print — yield-earning strike orders (docs.pacifica.fi/print.md and pages under it).
|
|
3
|
+
*
|
|
4
|
+
* Product (docs + live API, 2026-10-19):
|
|
5
|
+
* - A Print order locks a USDC deposit (min 10) as margin and names a target ("strike") price:
|
|
6
|
+
* long = target BELOW mark, short = target ABOVE mark, 0.5% … 10% / 20% / 40% from mark for a
|
|
7
|
+
* 24-hour / 7-day / 30-day cycle. Leverage 1× … the product's max (live /game/config).
|
|
8
|
+
* - Each cycle the Print pool pays a premium ("yield") at the START of the cycle; it is added to
|
|
9
|
+
* the order's locked margin. WHILE RESTING (before a fill) the order is evaluated only at the
|
|
10
|
+
* END of each cycle: mark at/through the target → it fills AT the target; otherwise it rolls to
|
|
11
|
+
* a new target at the same % distance from the new mark; mark beyond the liquidation level at
|
|
12
|
+
* that checkpoint → liquidated, the margin (deposit + premiums) is lost.
|
|
13
|
+
* - AFTER a fill it is an ordinary perp position in the main account (size = deposit × leverage
|
|
14
|
+
* / target): continuous mark-price liquidation, it nets with an existing position in the same
|
|
15
|
+
* market and may be cross-margined — read it with `perp -e pacifica account positions`.
|
|
16
|
+
* - Max loss of the resting order = deposit + premiums.
|
|
17
|
+
* - `total_segments` 1 … 1000 caps the number of cycles; omitted = rolls until ended.
|
|
18
|
+
* - Ending ("cancel", signing type `end_game`, POST /game/end) takes effect only at the end of
|
|
19
|
+
* the current cycle; funds return to the trading balance then. No in-place edit.
|
|
20
|
+
*
|
|
21
|
+
* Endpoints (unauthenticated GET; writes are signed):
|
|
22
|
+
* GET /game/config products (game ids, cycle, caps, max leverage)
|
|
23
|
+
* GET /game/accounts?account= the account's current Print orders
|
|
24
|
+
* GET /game/history_summary?account= every Print order of the account (paginated)
|
|
25
|
+
* GET /game/history?game_account= event log of one Print order (paginated)
|
|
26
|
+
* POST /game/deposit type deposit_to_game create (documented)
|
|
27
|
+
* POST /game/end type end_game end at the cycle's close — UNOFFICIAL: not in the API docs,
|
|
28
|
+
* shape captured from app.pacifica.fi (fixture app-unofficial-requests.json)
|
|
29
|
+
* `preview_game_account` (POST /game/preview, the app's premium quote) is a SIGNED request; read
|
|
30
|
+
* paths never sign, so the premium of a new order is not quoted here (estimatedPremium: null).
|
|
31
|
+
*/
|
|
32
|
+
import { PerpError } from "../errors.js";
|
|
33
|
+
import { USDC_DECIMALS } from "../pacifica/constants.js";
|
|
34
|
+
import { exactUnits, mulDecimal, subDecimal, formatComputedPrice } from "./precision.js";
|
|
35
|
+
import { cmpDecimal, divDecimal, fractionDigits, isPlainDecimal, trimDecimal } from "./pacifica-decimal.js";
|
|
36
|
+
import { buildPresigned } from "./pacifica-presigned.js";
|
|
37
|
+
// ── Documented limits ───────────────────────────────────────────────────
|
|
38
|
+
/** docs print/placing-and-managing-orders: "Deposit — your locked margin, minimum $10". */
|
|
39
|
+
export const PRINT_MIN_DEPOSIT_USDC = "10";
|
|
40
|
+
/** docs: target "at least 0.5% away from the current mark price". */
|
|
41
|
+
export const PRINT_MIN_TARGET_DISTANCE_PCT = "0.5";
|
|
42
|
+
/** docs: "at most 10% on a 24-hour cycle, 20% on 7 days, or 40% on 30 days" (keyed by cycle days). */
|
|
43
|
+
export const PRINT_MAX_TARGET_DISTANCE_PCT = { 1: "10", 7: "20", 30: "40" };
|
|
44
|
+
/** API create-print-order: "total_segments … 1 to 1000". */
|
|
45
|
+
export const PRINT_MAX_CYCLES = 1000;
|
|
46
|
+
const DAY_MS = 86_400_000;
|
|
47
|
+
/**
|
|
48
|
+
* The docs formula (target ∓ (deposit + yield) × target / (leverage × deposit)) is where the margin
|
|
49
|
+
* is fully lost. A liquidation was observed earlier than that in one live case (mainnet 2026-10-16:
|
|
50
|
+
* a ZEC_24H short, strike 1150.95, 10×, deposit 922.93, premium 23.80, liquidated at the checkpoint
|
|
51
|
+
* with mark 1216.836 while the formula gives ≈ 1269.0). The venue's trigger is unpublished; its own
|
|
52
|
+
* figure comes only from the signed preview (preview_game_account), not called here.
|
|
53
|
+
*/
|
|
54
|
+
export const PRINT_LIQUIDATION_NOTE = "docsLiquidationPrice is the docs formula (margin fully lost) — an outer bound, not the trigger: a liquidation was observed earlier in one live case (2026-10-16); the venue's trigger is unpublished. After a fill the position follows ordinary perp liquidation (continuous mark price, main account).";
|
|
55
|
+
// ── Parsing (strict: a missing required field is EXCHANGE_ERROR, an optional one is null) ──
|
|
56
|
+
function bad(what, row) {
|
|
57
|
+
return new PerpError("EXCHANGE_ERROR", `Pacifica ${what}: ${JSON.stringify(row).slice(0, 200)}`, { exchange: "pacifica" });
|
|
58
|
+
}
|
|
59
|
+
function reqStr(row, key, ctx) {
|
|
60
|
+
const v = row[key];
|
|
61
|
+
if (typeof v === "string" && v !== "")
|
|
62
|
+
return v;
|
|
63
|
+
throw bad(`${ctx} without a readable ${key}`, row);
|
|
64
|
+
}
|
|
65
|
+
function optStr(row, key) {
|
|
66
|
+
const v = row[key];
|
|
67
|
+
return typeof v === "string" && v !== "" ? v : null;
|
|
68
|
+
}
|
|
69
|
+
function reqDec(row, key, ctx) {
|
|
70
|
+
const v = reqStr(row, key, ctx);
|
|
71
|
+
if (!isPlainDecimal(v))
|
|
72
|
+
throw bad(`${ctx} with a non-decimal ${key}`, row);
|
|
73
|
+
return trimDecimal(v);
|
|
74
|
+
}
|
|
75
|
+
function optDec(row, key) {
|
|
76
|
+
const v = row[key];
|
|
77
|
+
return isPlainDecimal(v) ? trimDecimal(v) : null;
|
|
78
|
+
}
|
|
79
|
+
/** Positive ms timestamp, else null (the venue sends null / 0 for "not yet"). */
|
|
80
|
+
function optMs(row, key) {
|
|
81
|
+
const v = row[key];
|
|
82
|
+
return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : null;
|
|
83
|
+
}
|
|
84
|
+
function optInt(row, key) {
|
|
85
|
+
const v = row[key];
|
|
86
|
+
return typeof v === "number" && Number.isInteger(v) ? v : null;
|
|
87
|
+
}
|
|
88
|
+
function asRow(x, ctx) {
|
|
89
|
+
if (x === null || typeof x !== "object" || Array.isArray(x))
|
|
90
|
+
throw bad(`${ctx} is not an object`, x);
|
|
91
|
+
return x;
|
|
92
|
+
}
|
|
93
|
+
/** `data` of a `{success: true, data}` envelope; a refusal / other shape is EXCHANGE_ERROR. */
|
|
94
|
+
export function envelopeData(env, path) {
|
|
95
|
+
if (env.success !== true)
|
|
96
|
+
throw new PerpError("EXCHANGE_ERROR", `Pacifica GET ${path} failed: ${String(env.error ?? JSON.stringify(env).slice(0, 160))}`, { exchange: "pacifica" });
|
|
97
|
+
return env.data;
|
|
98
|
+
}
|
|
99
|
+
function pageMeta(env) {
|
|
100
|
+
return {
|
|
101
|
+
hasMore: typeof env.has_more === "boolean" ? env.has_more : null,
|
|
102
|
+
nextCursor: typeof env.next_cursor === "string" && env.next_cursor !== "" ? env.next_cursor : null,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** GET /game/config `data` → products (`{configs: [...]}`; the app also accepts a bare array). */
|
|
106
|
+
export function parsePrintGames(data) {
|
|
107
|
+
const rows = Array.isArray(data) ? data : asRow(data, "/game/config data").configs;
|
|
108
|
+
if (!Array.isArray(rows))
|
|
109
|
+
throw bad("/game/config without a configs list", data);
|
|
110
|
+
return rows.map((x) => {
|
|
111
|
+
const r = asRow(x, "/game/config row");
|
|
112
|
+
const kind = optStr(r, "segment_kind");
|
|
113
|
+
const days = kind === "fixed_days" ? optInt(r, "segment_value") : null;
|
|
114
|
+
return {
|
|
115
|
+
schema: 1,
|
|
116
|
+
game: reqStr(r, "game", "/game/config row"),
|
|
117
|
+
asset: reqStr(r, "target_asset", "/game/config row"),
|
|
118
|
+
cycleDays: days !== null && days > 0 ? days : null,
|
|
119
|
+
maxLeverage: optDec(r, "max_leverage"),
|
|
120
|
+
productDepositCap: optDec(r, "deposit_cap"),
|
|
121
|
+
poolDepositCap: optDec(r, "lake_deposit_cap"),
|
|
122
|
+
capScope: optStr(r, "cap_scope"),
|
|
123
|
+
poolAddress: optStr(r, "lake_address"),
|
|
124
|
+
minDeposit: PRINT_MIN_DEPOSIT_USDC,
|
|
125
|
+
targetDistancePct: { min: PRINT_MIN_TARGET_DISTANCE_PCT, max: days !== null ? PRINT_MAX_TARGET_DISTANCE_PCT[days] ?? null : null },
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function directionOf(v, row) {
|
|
130
|
+
if (v === 0)
|
|
131
|
+
return "long";
|
|
132
|
+
if (v === 1)
|
|
133
|
+
return "short";
|
|
134
|
+
throw bad(`Print order with unknown direction ${JSON.stringify(v)}`, row);
|
|
135
|
+
}
|
|
136
|
+
/** docs margin-and-liquidation: target ∓ (deposit + yield) × target / (leverage × deposit). */
|
|
137
|
+
export function printLiquidationPrice(direction, strike, deposit, leverage, premium, tickSize = null) {
|
|
138
|
+
const k = divDecimal(sum(deposit, premium), mulDecimal(leverage, deposit));
|
|
139
|
+
if (k === null)
|
|
140
|
+
return null;
|
|
141
|
+
const off = mulDecimal(strike, k);
|
|
142
|
+
const px = direction === "long" ? subDecimal(strike, off) : sum(strike, off);
|
|
143
|
+
if (cmpDecimal(px, "0") <= 0)
|
|
144
|
+
return null;
|
|
145
|
+
return formatComputedPrice(Number(px), tickSize);
|
|
146
|
+
}
|
|
147
|
+
function sum(a, b) {
|
|
148
|
+
return subDecimal(a, `-${b}`.replace(/^--/, ""));
|
|
149
|
+
}
|
|
150
|
+
/** One /game/accounts or /game/history_summary row. */
|
|
151
|
+
export function parsePrintPosition(x) {
|
|
152
|
+
const r = asRow(x, "Print order row");
|
|
153
|
+
const ctx = "Print order row";
|
|
154
|
+
const direction = directionOf(r.direction, r);
|
|
155
|
+
const deposit = reqDec(r, "initial_deposit", ctx);
|
|
156
|
+
const strike = reqDec(r, "strike_price", ctx);
|
|
157
|
+
const leverage = reqDec(r, "leverage", ctx);
|
|
158
|
+
const premium = optDec(r, "premium_paid");
|
|
159
|
+
const endedAt = optMs(r, "game_ended_at_ms");
|
|
160
|
+
const endReason = optStr(r, "end_reason");
|
|
161
|
+
const cancelRequested = r.user_initiated_game_end === true;
|
|
162
|
+
const notional = mulDecimal(deposit, leverage);
|
|
163
|
+
let status;
|
|
164
|
+
if (endedAt !== null) {
|
|
165
|
+
status = endReason === "filled" ? "filled"
|
|
166
|
+
: endReason === "liquidated" ? "liquidated"
|
|
167
|
+
: endReason === "user_requested" ? "cancelled"
|
|
168
|
+
: endReason === "schedule_completed" ? "completed"
|
|
169
|
+
: "ended"; // ended, reason not reported — never guessed
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
status = cancelRequested ? "cancelling" : "printing";
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
schema: 1,
|
|
176
|
+
address: reqStr(r, "address", ctx),
|
|
177
|
+
game: reqStr(r, "game", ctx),
|
|
178
|
+
asset: optStr(r, "target_asset"),
|
|
179
|
+
direction,
|
|
180
|
+
deposit,
|
|
181
|
+
strikePrice: strike,
|
|
182
|
+
leverage,
|
|
183
|
+
entryMarkPrice: optDec(r, "entry_mark_price"),
|
|
184
|
+
premiumPaid: premium,
|
|
185
|
+
notional,
|
|
186
|
+
size: divDecimal(notional, strike, 8),
|
|
187
|
+
docsLiquidationPrice: premium === null ? null : printLiquidationPrice(direction, strike, deposit, leverage, premium),
|
|
188
|
+
maxLoss: premium === null ? null : sum(deposit, premium),
|
|
189
|
+
cycles: { total: optInt(r, "total_segments"), started: optInt(r, "segments_started") },
|
|
190
|
+
createdAt: optMs(r, "game_created_at_ms"),
|
|
191
|
+
startedAt: optMs(r, "game_started_at_ms"),
|
|
192
|
+
currentCycleEndAt: optMs(r, "current_segment_end_at_ms"),
|
|
193
|
+
scheduledEndAt: optMs(r, "scheduled_end_at_ms"),
|
|
194
|
+
endedAt,
|
|
195
|
+
endReason,
|
|
196
|
+
cancelRequested,
|
|
197
|
+
status,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/** GET /game/accounts `data` (`{game_accounts: [...]}`). */
|
|
201
|
+
export function parsePrintAccounts(data) {
|
|
202
|
+
const rows = Array.isArray(data) ? data : asRow(data, "/game/accounts data").game_accounts;
|
|
203
|
+
if (!Array.isArray(rows))
|
|
204
|
+
throw bad("/game/accounts without a game_accounts list", data);
|
|
205
|
+
return rows.map(parsePrintPosition);
|
|
206
|
+
}
|
|
207
|
+
/** One /game/history event (created / premium / end_requested / filled / liquidated / …). */
|
|
208
|
+
export function parsePrintEvent(x) {
|
|
209
|
+
const r = asRow(x, "/game/history row");
|
|
210
|
+
const id = r.history_id;
|
|
211
|
+
const time = r.created_at;
|
|
212
|
+
if (typeof id !== "number" || typeof time !== "number")
|
|
213
|
+
throw bad("/game/history row without history_id / created_at", r);
|
|
214
|
+
return {
|
|
215
|
+
schema: 1,
|
|
216
|
+
id,
|
|
217
|
+
printAddress: reqStr(r, "address", "/game/history row"),
|
|
218
|
+
depositor: optStr(r, "depositor_address"),
|
|
219
|
+
event: reqStr(r, "event_type", "/game/history row"),
|
|
220
|
+
amount: optDec(r, "amount"),
|
|
221
|
+
strikePrice: optDec(r, "strike_price"),
|
|
222
|
+
markPrice: optDec(r, "mark_price"),
|
|
223
|
+
size: optDec(r, "size"),
|
|
224
|
+
cycleStartedAt: optMs(r, "segment_started_at_ms"),
|
|
225
|
+
cycleEndedAt: optMs(r, "segment_ended_at_ms"),
|
|
226
|
+
cycleNumber: optInt(r, "segment_number"),
|
|
227
|
+
time,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
export async function fetchPrintGames(client) {
|
|
231
|
+
return parsePrintGames(envelopeData(await client.getEnvelope("/game/config"), "/game/config"));
|
|
232
|
+
}
|
|
233
|
+
export async function fetchPrintPositions(client, account) {
|
|
234
|
+
return parsePrintAccounts(envelopeData(await client.getEnvelope("/game/accounts", { account }), "/game/accounts"));
|
|
235
|
+
}
|
|
236
|
+
function pageParams(p) {
|
|
237
|
+
return { ...(p.limit !== null ? { limit: String(p.limit) } : {}), ...(p.cursor !== null ? { cursor: p.cursor } : {}) };
|
|
238
|
+
}
|
|
239
|
+
export async function fetchPrintHistory(client, account, page) {
|
|
240
|
+
const env = await client.getEnvelope("/game/history_summary", { account, ...pageParams(page) });
|
|
241
|
+
const data = envelopeData(env, "/game/history_summary");
|
|
242
|
+
if (!Array.isArray(data))
|
|
243
|
+
throw bad("/game/history_summary without a list", data);
|
|
244
|
+
return { items: data.map(parsePrintPosition), ...pageMeta(env) };
|
|
245
|
+
}
|
|
246
|
+
export async function fetchPrintEvents(client, printAddress, page) {
|
|
247
|
+
const env = await client.getEnvelope("/game/history", { game_account: printAddress, ...pageParams(page) });
|
|
248
|
+
const data = envelopeData(env, "/game/history");
|
|
249
|
+
if (!Array.isArray(data))
|
|
250
|
+
throw bad("/game/history without a list", data);
|
|
251
|
+
return { items: data.map(parsePrintEvent), ...pageMeta(env) };
|
|
252
|
+
}
|
|
253
|
+
/** Mark price + tick size of the product's perp market (GET /prices, GET /info). */
|
|
254
|
+
export async function fetchPrintMarket(client, asset) {
|
|
255
|
+
const [prices, info] = await Promise.all([client.getPrices(), client.getInfo()]);
|
|
256
|
+
const p = prices.find((x) => x.symbol === asset);
|
|
257
|
+
const m = info.find((x) => x.symbol === asset);
|
|
258
|
+
if (p === undefined || !isPlainDecimal(p.mark) || !(Number(p.mark) > 0)) {
|
|
259
|
+
throw new PerpError("PRICE_STALE", `Pacifica has no mark price for ${asset}`, { exchange: "pacifica", remediation: "Retry; check perp -e pacifica market list" });
|
|
260
|
+
}
|
|
261
|
+
if (m === undefined || !isPlainDecimal(m.tick_size)) {
|
|
262
|
+
throw new PerpError("EXCHANGE_ERROR", `Pacifica /info has no tick_size for ${asset}`, { exchange: "pacifica" });
|
|
263
|
+
}
|
|
264
|
+
return { mark: p.mark, tickSize: m.tick_size };
|
|
265
|
+
}
|
|
266
|
+
/** The product `game` (case-insensitive) from the live list; unknown → SYMBOL_NOT_FOUND naming what exists. */
|
|
267
|
+
export function findPrintGame(games, game) {
|
|
268
|
+
const g = games.find((x) => x.game.toUpperCase() === game.toUpperCase());
|
|
269
|
+
if (g === undefined) {
|
|
270
|
+
throw new PerpError("SYMBOL_NOT_FOUND", `No Print product "${game}"`, {
|
|
271
|
+
exchange: "pacifica", remediation: `Available: ${games.map((x) => x.game).join(", ")} — see: perp -e pacifica print games`,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return g;
|
|
275
|
+
}
|
|
276
|
+
function invalid(msg, remediation) {
|
|
277
|
+
return new PerpError("INVALID_PARAMS", msg, { exchange: "pacifica", remediation });
|
|
278
|
+
}
|
|
279
|
+
/** Plain non-negative decimal without leading zeros ("0.5", "10", "10.25"; not "010", "1e3", ".5"). */
|
|
280
|
+
export const PLAIN_POSITIVE = /^(0|[1-9]\d*)(\.\d+)?$/;
|
|
281
|
+
/** Strict USDC amount: plain positive decimal, no leading zeros, at most 6 fractional digits (USDC precision). */
|
|
282
|
+
export function requireUsdcAmount(raw, what) {
|
|
283
|
+
const s = String(raw).trim();
|
|
284
|
+
if (!PLAIN_POSITIVE.test(s) || !(Number(s) > 0))
|
|
285
|
+
throw invalid(`${what} must be a positive decimal number without leading zeros (got "${raw}")`, "Pass a plain decimal amount, e.g. 100 or 100.5");
|
|
286
|
+
if (fractionDigits(s) > USDC_DECIMALS)
|
|
287
|
+
throw invalid(`${what} ${s} has more than ${USDC_DECIMALS} decimals (USDC precision)`, `Round to ${USDC_DECIMALS} decimals`);
|
|
288
|
+
return trimDecimal(s);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Validate a Print order against the product and the live mark (docs limits) and compute the
|
|
292
|
+
* preview. Throws INVALID_PARAMS / INVALID_TICK / CAPABILITY_UNSUPPORTED; signs and sends nothing.
|
|
293
|
+
*/
|
|
294
|
+
export function quotePrintOrder(game, input, market) {
|
|
295
|
+
const where = `Print ${game.game}`;
|
|
296
|
+
if (game.cycleDays === null || game.targetDistancePct.max === null) {
|
|
297
|
+
throw new PerpError("CAPABILITY_UNSUPPORTED", `${where}: cycle length not in the documented set (24 hours / 7 days / 30 days) — no documented target range`, {
|
|
298
|
+
exchange: "pacifica", remediation: "Use a product listed with a 1d / 7d / 30d cycle in: perp -e pacifica print games",
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
if (game.maxLeverage === null) {
|
|
302
|
+
throw new PerpError("EXCHANGE_ERROR", `${where}: /game/config reports no max_leverage`, { exchange: "pacifica" });
|
|
303
|
+
}
|
|
304
|
+
const deposit = requireUsdcAmount(input.deposit, "deposit");
|
|
305
|
+
if (cmpDecimal(deposit, PRINT_MIN_DEPOSIT_USDC) < 0) {
|
|
306
|
+
throw invalid(`${where}: deposit ${deposit} USDC is below the ${PRINT_MIN_DEPOSIT_USDC} USDC minimum`, `Deposit at least ${PRINT_MIN_DEPOSIT_USDC} USDC`);
|
|
307
|
+
}
|
|
308
|
+
// No local deposit_cap check: it caps the product's total open deposits (all users), so only the
|
|
309
|
+
// venue knows the remaining room — its refusal decides.
|
|
310
|
+
if (!/^[1-9]\d*$/.test(input.leverage) || cmpDecimal(input.leverage, game.maxLeverage) > 0) {
|
|
311
|
+
throw invalid(`${where}: leverage must be a whole number from 1 to ${game.maxLeverage} (got "${input.leverage}")`, `Pass --leverage 1 … ${game.maxLeverage}`);
|
|
312
|
+
}
|
|
313
|
+
const leverage = input.leverage;
|
|
314
|
+
const strike = String(input.strikePrice).trim();
|
|
315
|
+
if (!PLAIN_POSITIVE.test(strike) || !(Number(strike) > 0))
|
|
316
|
+
throw invalid(`${where}: strike must be a positive price (got "${input.strikePrice}")`, "Pass --strike <price>");
|
|
317
|
+
if (exactUnits(strike, market.tickSize) === null) {
|
|
318
|
+
throw new PerpError("INVALID_TICK", `${where}: strike ${strike} is not a multiple of the ${game.asset} tick ${market.tickSize}`, {
|
|
319
|
+
exchange: "pacifica", remediation: `Use a strike that is a multiple of ${market.tickSize}`,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
const mark = market.mark;
|
|
323
|
+
const side = cmpDecimal(strike, mark);
|
|
324
|
+
if (input.direction === "long" && side >= 0)
|
|
325
|
+
throw invalid(`${where}: a Print long's target must be BELOW the mark ${mark} (got ${strike})`, "Lower the strike, or use short");
|
|
326
|
+
if (input.direction === "short" && side <= 0)
|
|
327
|
+
throw invalid(`${where}: a Print short's target must be ABOVE the mark ${mark} (got ${strike})`, "Raise the strike, or use long");
|
|
328
|
+
const gap = input.direction === "long" ? subDecimal(mark, strike) : subDecimal(strike, mark);
|
|
329
|
+
const gapX100 = mulDecimal(gap, "100");
|
|
330
|
+
const { min, max } = { min: game.targetDistancePct.min, max: game.targetDistancePct.max };
|
|
331
|
+
if (cmpDecimal(gapX100, mulDecimal(min, mark)) < 0 || cmpDecimal(gapX100, mulDecimal(max, mark)) > 0) {
|
|
332
|
+
throw invalid(`${where}: target ${strike} is ${divDecimal(gapX100, mark, 4)}% from the mark ${mark}; allowed ${min}% … ${max}% for a ${game.cycleDays}-day cycle`, `Pick a strike ${min}% … ${max}% ${input.direction === "long" ? "below" : "above"} the mark`);
|
|
333
|
+
}
|
|
334
|
+
if (input.cycles !== null && !(Number.isInteger(input.cycles) && input.cycles >= 1 && input.cycles <= PRINT_MAX_CYCLES)) {
|
|
335
|
+
throw invalid(`${where}: cycles must be a whole number from 1 to ${PRINT_MAX_CYCLES} (got ${input.cycles})`, `Pass --cycles 1 … ${PRINT_MAX_CYCLES}, or omit it to roll until ended`);
|
|
336
|
+
}
|
|
337
|
+
const notional = mulDecimal(deposit, leverage);
|
|
338
|
+
const size = divDecimal(notional, strike, 8);
|
|
339
|
+
return {
|
|
340
|
+
schema: 1,
|
|
341
|
+
game: game.game,
|
|
342
|
+
asset: game.asset,
|
|
343
|
+
cycleDays: game.cycleDays,
|
|
344
|
+
direction: input.direction,
|
|
345
|
+
deposit,
|
|
346
|
+
leverage,
|
|
347
|
+
strikePrice: strike,
|
|
348
|
+
markPrice: mark,
|
|
349
|
+
targetDistancePct: divDecimal(gapX100, mark, 4),
|
|
350
|
+
allowedTargetDistancePct: { min, max },
|
|
351
|
+
notional,
|
|
352
|
+
size,
|
|
353
|
+
docsLiquidationPrice: printLiquidationPrice(input.direction, strike, deposit, leverage, "0", market.tickSize),
|
|
354
|
+
maxLoss: deposit,
|
|
355
|
+
cycles: input.cycles,
|
|
356
|
+
maxDurationDays: input.cycles === null ? null : input.cycles * game.cycleDays,
|
|
357
|
+
cancelSettlesWithinDays: game.cycleDays,
|
|
358
|
+
liquidationNote: PRINT_LIQUIDATION_NOTE,
|
|
359
|
+
estimatedPremium: null,
|
|
360
|
+
notes: [
|
|
361
|
+
`While resting, fill and liquidation are checked only at each ${game.cycleDays}-day checkpoint; a fill opens ${size} ${game.asset} at exactly ${strike}.`,
|
|
362
|
+
`After a fill it is an ordinary position in your main account: continuous mark-price liquidation, it nets with any ${game.asset} position you hold and may be cross-margined — see perp -e pacifica account positions.`,
|
|
363
|
+
`Max loss: the ${deposit} USDC deposit plus every premium credited to it — never more.`,
|
|
364
|
+
`Cancelling resolves at the end of the running cycle (up to ${game.cycleDays} day${game.cycleDays === 1 ? "" : "s"}); funds stay locked until then.`,
|
|
365
|
+
"The premium is set by the venue at the start of each cycle; its quote (preview_game_account) is a signed request and is not called here.",
|
|
366
|
+
"Product and pool deposit caps are totals shared by all users: the venue refuses when they are full (not checkable here).",
|
|
367
|
+
],
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
// ── Write requests (pure) ───────────────────────────────────────────────
|
|
371
|
+
/** POST /game/deposit body + canonical message (deposit_to_game). Field set per the API docs. */
|
|
372
|
+
export function buildPrintDepositRequest(q, identity, opts) {
|
|
373
|
+
return buildPresigned({
|
|
374
|
+
type: "deposit_to_game",
|
|
375
|
+
path: "/game/deposit",
|
|
376
|
+
identity,
|
|
377
|
+
timestamp: opts.timestamp,
|
|
378
|
+
expiryWindow: opts.expiryWindow,
|
|
379
|
+
payload: {
|
|
380
|
+
game: q.game,
|
|
381
|
+
deposit_amount: q.deposit,
|
|
382
|
+
direction: q.direction === "long" ? 0 : 1,
|
|
383
|
+
strike_price: q.strikePrice,
|
|
384
|
+
leverage: q.leverage,
|
|
385
|
+
...(q.cycles !== null ? { total_segments: q.cycles } : {}),
|
|
386
|
+
idempotency_key: opts.idempotencyKey,
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
/** POST /game/end body + canonical message (end_game, payload `{game_account}` as app.pacifica.fi sends it). */
|
|
391
|
+
export function buildPrintEndRequest(printAddress, identity, opts) {
|
|
392
|
+
return buildPresigned({
|
|
393
|
+
type: "end_game",
|
|
394
|
+
path: "/game/end",
|
|
395
|
+
identity,
|
|
396
|
+
timestamp: opts.timestamp,
|
|
397
|
+
expiryWindow: opts.expiryWindow,
|
|
398
|
+
payload: { game_account: printAddress },
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
/** An end request is accepted only for a running order of the account (not ended, not already ending). */
|
|
402
|
+
export function requireEndable(positions, printAddress) {
|
|
403
|
+
const p = positions.find((x) => x.address === printAddress);
|
|
404
|
+
if (p === undefined) {
|
|
405
|
+
throw new PerpError("POSITION_NOT_FOUND", `No current Print order ${printAddress} on this account`, {
|
|
406
|
+
exchange: "pacifica", remediation: "List them: perp -e pacifica print positions",
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
if (p.status !== "printing") {
|
|
410
|
+
throw invalid(`Print order ${printAddress} is ${p.status}; only a running (printing) order can be ended`, "Nothing to do — see: perp -e pacifica print positions");
|
|
411
|
+
}
|
|
412
|
+
return p;
|
|
413
|
+
}
|
|
414
|
+
/** When a cycle that ends at `cycleEndAt` frees the funds, in days from `now` (null = unknown). */
|
|
415
|
+
export function daysUntil(cycleEndAt, now) {
|
|
416
|
+
return cycleEndAt === null ? null : (Math.max(0, cycleEndAt - now) / DAY_MS).toFixed(2);
|
|
417
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pacifica Spot Adapter
|
|
2
|
+
* Pacifica Spot Adapter.
|
|
3
3
|
*
|
|
4
4
|
* Spot is a different `symbol` ("SOL-USDC") on the same Pacifica order
|
|
5
5
|
* endpoints as perps, signed by the same Solana signer (incl. agent_wallet).
|
|
@@ -18,6 +18,12 @@ import type { SpotAdapter, SpotMarketInfo, SpotBalance, SpotOrderResult, SpotOpe
|
|
|
18
18
|
import type { ResolvedSlippage } from "./slippage.js";
|
|
19
19
|
import { type PacificaAdapter } from "./pacifica.js";
|
|
20
20
|
import type { IocOutcome } from "./ioc-outcome.js";
|
|
21
|
+
/**
|
|
22
|
+
* Spot balances from Pacifica GET /account (USDC = the shared perp collateral, then
|
|
23
|
+
* `spot_balances`) — the one mapper behind the keyed spot adapter and the keyless
|
|
24
|
+
* `account balance --address`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function pacificaSpotBalanceRows(info: Record<string, unknown>): SpotBalance[];
|
|
21
27
|
export declare class PacificaSpotAdapter implements SpotAdapter, SpotOpenOrdersCapable, SpotCancelAllCapable {
|
|
22
28
|
readonly name = "pacifica";
|
|
23
29
|
private _perp;
|
|
@@ -56,7 +62,7 @@ export declare class PacificaSpotAdapter implements SpotAdapter, SpotOpenOrdersC
|
|
|
56
62
|
private _withOutcome;
|
|
57
63
|
/**
|
|
58
64
|
* Venue-final outcome of a SpotOrderResult returned by spotMarketOrder / IOC
|
|
59
|
-
* spotLimitOrder (IocOutcomeCapable
|
|
65
|
+
* spotLimitOrder (IocOutcomeCapable): set only when the polled
|
|
60
66
|
* history row was terminal and reported filled_amount. null for anything else.
|
|
61
67
|
*/
|
|
62
68
|
iocOutcomeOf(raw: unknown): IocOutcome | null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pacifica Spot Adapter
|
|
2
|
+
* Pacifica Spot Adapter.
|
|
3
3
|
*
|
|
4
4
|
* Spot is a different `symbol` ("SOL-USDC") on the same Pacifica order
|
|
5
5
|
* endpoints as perps, signed by the same Solana signer (incl. agent_wallet).
|
|
@@ -36,6 +36,28 @@ function decimalsOf(step) {
|
|
|
36
36
|
const frac = step.split(".")[1];
|
|
37
37
|
return frac ? frac.replace(/0+$/, "").length : 0;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Spot balances from Pacifica GET /account (USDC = the shared perp collateral, then
|
|
41
|
+
* `spot_balances`) — the one mapper behind the keyed spot adapter and the keyless
|
|
42
|
+
* `account balance --address`.
|
|
43
|
+
*/
|
|
44
|
+
export function pacificaSpotBalanceRows(info) {
|
|
45
|
+
const rows = info.spot_balances;
|
|
46
|
+
if (!Array.isArray(rows)) {
|
|
47
|
+
throw new PerpError("INVALID_PARAMS", "Pacifica /account response has no spot_balances array", { exchange: "pacifica" });
|
|
48
|
+
}
|
|
49
|
+
const out = [];
|
|
50
|
+
// USDC = the shared perp collateral (same money as perp equity).
|
|
51
|
+
const total = String(info.balance);
|
|
52
|
+
const available = String(info.available_to_spend);
|
|
53
|
+
out.push({ token: QUOTE, total, available, held: subDecimal(total, available), shared: true });
|
|
54
|
+
for (const r of rows) {
|
|
55
|
+
const t = String(r.amount);
|
|
56
|
+
const a = String(r.available_to_withdraw);
|
|
57
|
+
out.push({ token: String(r.symbol), total: t, available: a, held: subDecimal(t, a), shared: false });
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
39
61
|
export class PacificaSpotAdapter {
|
|
40
62
|
name = "pacifica";
|
|
41
63
|
_perp;
|
|
@@ -105,21 +127,7 @@ export class PacificaSpotAdapter {
|
|
|
105
127
|
}
|
|
106
128
|
async getSpotBalances() {
|
|
107
129
|
const info = (await this._perp.sdk.getAccount(this._perp.publicKey));
|
|
108
|
-
|
|
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;
|
|
130
|
+
return pacificaSpotBalanceRows(info);
|
|
123
131
|
}
|
|
124
132
|
/** Mark price for min-notional / slippage checks. */
|
|
125
133
|
async _mark(wire) {
|
|
@@ -211,7 +219,7 @@ export class PacificaSpotAdapter {
|
|
|
211
219
|
}
|
|
212
220
|
/**
|
|
213
221
|
* Venue-final outcome of a SpotOrderResult returned by spotMarketOrder / IOC
|
|
214
|
-
* spotLimitOrder (IocOutcomeCapable
|
|
222
|
+
* spotLimitOrder (IocOutcomeCapable): set only when the polled
|
|
215
223
|
* history row was terminal and reported filled_amount. null for anything else.
|
|
216
224
|
*/
|
|
217
225
|
iocOutcomeOf(raw) {
|
|
@@ -254,7 +262,7 @@ export class PacificaSpotAdapter {
|
|
|
254
262
|
try { // pre-send (see spotMarketOrder)
|
|
255
263
|
await this.init();
|
|
256
264
|
m = this._resolve(symbol);
|
|
257
|
-
tif = pacificaTif(opts?.tif); //
|
|
265
|
+
tif = pacificaTif(opts?.tif); // common mapping; FOK → CAPABILITY_UNSUPPORTED
|
|
258
266
|
px = requireOnTick(price, m.tick_size);
|
|
259
267
|
requirePositive(size, "size");
|
|
260
268
|
amount = floorSize(size, m.lot_size);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Pacifica agent-wallet signing builders (Phase 2c).
|
|
3
3
|
*
|
|
4
4
|
* Pacifica auth model — off-chain signed messages over Solana Ed25519.
|
|
5
|
-
* Reference: https://pacifica.
|
|
5
|
+
* Reference: https://docs.pacifica.fi/api-documentation/api/signing.md
|
|
6
6
|
*
|
|
7
7
|
* Canonical JSON rules (from Pacifica signing spec + production reference
|
|
8
8
|
* `src/pacifica/signing.ts:prepareMessage()`):
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Pacifica agent-wallet signing builders (Phase 2c).
|
|
3
3
|
*
|
|
4
4
|
* Pacifica auth model — off-chain signed messages over Solana Ed25519.
|
|
5
|
-
* Reference: https://pacifica.
|
|
5
|
+
* Reference: https://docs.pacifica.fi/api-documentation/api/signing.md
|
|
6
6
|
*
|
|
7
7
|
* Canonical JSON rules (from Pacifica signing spec + production reference
|
|
8
8
|
* `src/pacifica/signing.ts:prepareMessage()`):
|