perp-cli 0.11.0 → 0.12.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.
Files changed (240) hide show
  1. package/README.md +30 -25
  2. package/dist/_stderr-filter.d.ts +0 -0
  3. package/dist/_stderr-filter.js +30 -0
  4. package/dist/agent-wallet/expiry.d.ts +15 -0
  5. package/dist/agent-wallet/expiry.js +26 -0
  6. package/dist/agent-wallet/lighter-keystore.d.ts +34 -0
  7. package/dist/agent-wallet/lighter-keystore.js +232 -0
  8. package/dist/agent-wallet/passphrase.d.ts +13 -0
  9. package/dist/agent-wallet/passphrase.js +54 -0
  10. package/dist/agent-wallet/signing-strategy.d.ts +67 -0
  11. package/dist/agent-wallet/signing-strategy.js +51 -0
  12. package/dist/agent-wallet/store.d.ts +37 -0
  13. package/dist/agent-wallet/store.js +172 -0
  14. package/dist/api/public/_http.d.ts +15 -0
  15. package/dist/api/public/_http.js +24 -0
  16. package/dist/api/public/hyperliquid.js +36 -24
  17. package/dist/api/public/lighter.d.ts +7 -1
  18. package/dist/api/public/lighter.js +85 -37
  19. package/dist/api/public/pacifica.js +47 -18
  20. package/dist/bridge-engine.js +102 -49
  21. package/dist/cli-spec.js +1 -2
  22. package/dist/commands/account.js +2 -500
  23. package/dist/commands/agent.d.ts +8 -2
  24. package/dist/commands/agent.js +1903 -366
  25. package/dist/commands/alerts.js +3 -1
  26. package/dist/commands/arb/index.js +37 -7
  27. package/dist/commands/arb-auto.js +44 -11
  28. package/dist/commands/arb.js +83 -34
  29. package/dist/commands/bot.d.ts +1 -1
  30. package/dist/commands/bot.js +18 -14
  31. package/dist/commands/bridge.d.ts +1 -1
  32. package/dist/commands/bridge.js +4 -4
  33. package/dist/commands/funds.d.ts +1 -1
  34. package/dist/commands/funds.js +18 -68
  35. package/dist/commands/jobs.d.ts +1 -1
  36. package/dist/commands/jobs.js +2 -2
  37. package/dist/commands/manage.d.ts +1 -1
  38. package/dist/commands/manage.js +12 -84
  39. package/dist/commands/plan.d.ts +1 -1
  40. package/dist/commands/plan.js +2 -2
  41. package/dist/commands/portfolio.d.ts +19 -3
  42. package/dist/commands/portfolio.js +747 -163
  43. package/dist/commands/rebalance.d.ts +1 -1
  44. package/dist/commands/rebalance.js +18 -12
  45. package/dist/commands/risk.js +2 -6
  46. package/dist/commands/settings.js +30 -53
  47. package/dist/commands/trade.js +16 -15
  48. package/dist/commands/wallet.d.ts +2 -1
  49. package/dist/commands/wallet.js +756 -28
  50. package/dist/errors.d.ts +64 -0
  51. package/dist/errors.js +82 -2
  52. package/dist/event-stream.js +12 -1
  53. package/dist/exchanges/aster-typed-data.d.ts +156 -0
  54. package/dist/exchanges/aster-typed-data.js +189 -0
  55. package/dist/exchanges/aster.d.ts +60 -11
  56. package/dist/exchanges/aster.js +278 -136
  57. package/dist/exchanges/hyperliquid.d.ts +14 -0
  58. package/dist/exchanges/hyperliquid.js +84 -34
  59. package/dist/exchanges/lighter-spot.js +14 -3
  60. package/dist/exchanges/lighter.d.ts +66 -0
  61. package/dist/exchanges/lighter.js +199 -27
  62. package/dist/exchanges/pacifica-typed-data.d.ts +75 -0
  63. package/dist/exchanges/pacifica-typed-data.js +74 -0
  64. package/dist/exchanges/pacifica.d.ts +29 -1
  65. package/dist/exchanges/pacifica.js +94 -6
  66. package/dist/funding/rates.js +59 -21
  67. package/dist/guardrail/perp-guardrail.js +9 -1
  68. package/dist/index.d.ts +1 -1
  69. package/dist/index.js +255 -360
  70. package/dist/jobs.js +3 -1
  71. package/dist/mcp-server.js +85 -83
  72. package/dist/position-history.js +47 -4
  73. package/dist/settings.d.ts +67 -3
  74. package/dist/settings.js +1 -2
  75. package/dist/signer/ows-evm.d.ts +14 -0
  76. package/dist/signer/ows-evm.js +28 -9
  77. package/dist/smart-order.d.ts +8 -2
  78. package/dist/smart-order.js +46 -11
  79. package/dist/strategies/funding-arb.js +29 -8
  80. package/dist/trade-validator.js +30 -5
  81. package/dist/utils.d.ts +10 -0
  82. package/dist/utils.js +19 -0
  83. package/package.json +2 -2
  84. package/skills/perp-cli/SKILL.md +23 -14
  85. package/skills/perp-cli/references/agent-operations.md +6 -4
  86. package/skills/perp-cli/references/commands.md +55 -34
  87. package/skills/perp-cli/scripts/preflight.sh +4 -4
  88. package/skills/perp-cli/scripts/spot-perp-scan.sh +2 -2
  89. package/dist/__tests__/alert-logic.test.d.ts +0 -1
  90. package/dist/__tests__/alert-logic.test.js +0 -107
  91. package/dist/__tests__/arb-auto-3dex.test.d.ts +0 -1
  92. package/dist/__tests__/arb-auto-3dex.test.js +0 -397
  93. package/dist/__tests__/arb-history-stats.test.d.ts +0 -1
  94. package/dist/__tests__/arb-history-stats.test.js +0 -176
  95. package/dist/__tests__/arb-logic.test.d.ts +0 -1
  96. package/dist/__tests__/arb-logic.test.js +0 -84
  97. package/dist/__tests__/arb-manage.test.d.ts +0 -1
  98. package/dist/__tests__/arb-manage.test.js +0 -253
  99. package/dist/__tests__/arb-new-features.test.d.ts +0 -1
  100. package/dist/__tests__/arb-new-features.test.js +0 -457
  101. package/dist/__tests__/arb-sizing.test.d.ts +0 -1
  102. package/dist/__tests__/arb-sizing.test.js +0 -48
  103. package/dist/__tests__/arb-state.test.d.ts +0 -1
  104. package/dist/__tests__/arb-state.test.js +0 -284
  105. package/dist/__tests__/arb-userflow.test.d.ts +0 -1
  106. package/dist/__tests__/arb-userflow.test.js +0 -945
  107. package/dist/__tests__/arb-utils.test.d.ts +0 -1
  108. package/dist/__tests__/arb-utils.test.js +0 -264
  109. package/dist/__tests__/bot-conditions.test.d.ts +0 -1
  110. package/dist/__tests__/bot-conditions.test.js +0 -341
  111. package/dist/__tests__/client-id-tracker.test.d.ts +0 -1
  112. package/dist/__tests__/client-id-tracker.test.js +0 -137
  113. package/dist/__tests__/commands/new-atomic-commands.test.d.ts +0 -1
  114. package/dist/__tests__/commands/new-atomic-commands.test.js +0 -502
  115. package/dist/__tests__/commands/order-intent.test.d.ts +0 -1
  116. package/dist/__tests__/commands/order-intent.test.js +0 -600
  117. package/dist/__tests__/commands/trade-commands.test.d.ts +0 -1
  118. package/dist/__tests__/commands/trade-commands.test.js +0 -821
  119. package/dist/__tests__/config.test.d.ts +0 -1
  120. package/dist/__tests__/config.test.js +0 -86
  121. package/dist/__tests__/cross-chain-margin.test.d.ts +0 -1
  122. package/dist/__tests__/cross-chain-margin.test.js +0 -287
  123. package/dist/__tests__/dex-asset-map.test.d.ts +0 -1
  124. package/dist/__tests__/dex-asset-map.test.js +0 -191
  125. package/dist/__tests__/errors.test.d.ts +0 -1
  126. package/dist/__tests__/errors.test.js +0 -110
  127. package/dist/__tests__/event-stream.test.d.ts +0 -1
  128. package/dist/__tests__/event-stream.test.js +0 -276
  129. package/dist/__tests__/exchanges/interface.test.d.ts +0 -1
  130. package/dist/__tests__/exchanges/interface.test.js +0 -132
  131. package/dist/__tests__/exchanges/mock-adapter.d.ts +0 -69
  132. package/dist/__tests__/exchanges/mock-adapter.js +0 -137
  133. package/dist/__tests__/execution-log.test.d.ts +0 -1
  134. package/dist/__tests__/execution-log.test.js +0 -106
  135. package/dist/__tests__/funding-calc.test.d.ts +0 -1
  136. package/dist/__tests__/funding-calc.test.js +0 -71
  137. package/dist/__tests__/funding-history.test.d.ts +0 -1
  138. package/dist/__tests__/funding-history.test.js +0 -343
  139. package/dist/__tests__/funding-rates.test.d.ts +0 -1
  140. package/dist/__tests__/funding-rates.test.js +0 -342
  141. package/dist/__tests__/funding.test.d.ts +0 -1
  142. package/dist/__tests__/funding.test.js +0 -173
  143. package/dist/__tests__/gap-logic.test.d.ts +0 -1
  144. package/dist/__tests__/gap-logic.test.js +0 -43
  145. package/dist/__tests__/hip3-dex.test.d.ts +0 -1
  146. package/dist/__tests__/hip3-dex.test.js +0 -234
  147. package/dist/__tests__/integration/agent-features.integration.test.d.ts +0 -1
  148. package/dist/__tests__/integration/agent-features.integration.test.js +0 -553
  149. package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +0 -13
  150. package/dist/__tests__/integration/atomic-commands.integration.test.js +0 -246
  151. package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +0 -1
  152. package/dist/__tests__/integration/bridge-simulation.integration.test.js +0 -453
  153. package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +0 -1
  154. package/dist/__tests__/integration/bridge-strict.integration.test.js +0 -812
  155. package/dist/__tests__/integration/bridge.integration.test.d.ts +0 -1
  156. package/dist/__tests__/integration/bridge.integration.test.js +0 -309
  157. package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +0 -1
  158. package/dist/__tests__/integration/cli-e2e.integration.test.js +0 -202
  159. package/dist/__tests__/integration/dex-arb.integration.test.d.ts +0 -1
  160. package/dist/__tests__/integration/dex-arb.integration.test.js +0 -116
  161. package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +0 -13
  162. package/dist/__tests__/integration/envelope-consistency.integration.test.js +0 -205
  163. package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +0 -1
  164. package/dist/__tests__/integration/hip3-dex.integration.test.js +0 -147
  165. package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +0 -1
  166. package/dist/__tests__/integration/hyperliquid.integration.test.js +0 -79
  167. package/dist/__tests__/integration/lighter.integration.test.d.ts +0 -1
  168. package/dist/__tests__/integration/lighter.integration.test.js +0 -53
  169. package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +0 -9
  170. package/dist/__tests__/integration/new-commands-e2e.integration.test.js +0 -236
  171. package/dist/__tests__/integration/order-verification.integration.test.d.ts +0 -1
  172. package/dist/__tests__/integration/order-verification.integration.test.js +0 -321
  173. package/dist/__tests__/integration/pacifica.integration.test.d.ts +0 -1
  174. package/dist/__tests__/integration/pacifica.integration.test.js +0 -75
  175. package/dist/__tests__/integration/response-shapes.integration.test.d.ts +0 -1
  176. package/dist/__tests__/integration/response-shapes.integration.test.js +0 -278
  177. package/dist/__tests__/liquidity.test.d.ts +0 -1
  178. package/dist/__tests__/liquidity.test.js +0 -225
  179. package/dist/__tests__/plan-executor.test.d.ts +0 -1
  180. package/dist/__tests__/plan-executor.test.js +0 -314
  181. package/dist/__tests__/position-history.test.d.ts +0 -1
  182. package/dist/__tests__/position-history.test.js +0 -367
  183. package/dist/__tests__/retry.test.d.ts +0 -1
  184. package/dist/__tests__/retry.test.js +0 -310
  185. package/dist/__tests__/risk-assessment.test.d.ts +0 -1
  186. package/dist/__tests__/risk-assessment.test.js +0 -288
  187. package/dist/__tests__/security-adversarial.test.d.ts +0 -1
  188. package/dist/__tests__/security-adversarial.test.js +0 -574
  189. package/dist/__tests__/strategies.test.d.ts +0 -1
  190. package/dist/__tests__/strategies.test.js +0 -539
  191. package/dist/__tests__/trade-execution.test.d.ts +0 -1
  192. package/dist/__tests__/trade-execution.test.js +0 -129
  193. package/dist/__tests__/trade-validator.test.d.ts +0 -1
  194. package/dist/__tests__/trade-validator.test.js +0 -655
  195. package/dist/__tests__/utils.test.d.ts +0 -1
  196. package/dist/__tests__/utils.test.js +0 -76
  197. package/dist/arb/reconcile.d.ts +0 -28
  198. package/dist/arb/reconcile.js +0 -162
  199. package/dist/commands/alert.d.ts +0 -2
  200. package/dist/commands/alert.js +0 -421
  201. package/dist/commands/analytics.d.ts +0 -3
  202. package/dist/commands/analytics.js +0 -311
  203. package/dist/commands/arb-manage.d.ts +0 -5
  204. package/dist/commands/arb-manage.js +0 -5
  205. package/dist/commands/dashboard.d.ts +0 -3
  206. package/dist/commands/dashboard.js +0 -176
  207. package/dist/commands/deposit.d.ts +0 -4
  208. package/dist/commands/deposit.js +0 -573
  209. package/dist/commands/dex.d.ts +0 -3
  210. package/dist/commands/dex.js +0 -120
  211. package/dist/commands/env.d.ts +0 -2
  212. package/dist/commands/env.js +0 -136
  213. package/dist/commands/funding.d.ts +0 -3
  214. package/dist/commands/funding.js +0 -554
  215. package/dist/commands/gap.d.ts +0 -2
  216. package/dist/commands/gap.js +0 -305
  217. package/dist/commands/health.d.ts +0 -2
  218. package/dist/commands/health.js +0 -67
  219. package/dist/commands/multileg.d.ts +0 -16
  220. package/dist/commands/multileg.js +0 -174
  221. package/dist/commands/ows.d.ts +0 -2
  222. package/dist/commands/ows.js +0 -887
  223. package/dist/commands/pnl.d.ts +0 -12
  224. package/dist/commands/pnl.js +0 -243
  225. package/dist/commands/run.d.ts +0 -3
  226. package/dist/commands/run.js +0 -202
  227. package/dist/commands/stream.d.ts +0 -5
  228. package/dist/commands/stream.js +0 -200
  229. package/dist/commands/withdraw.d.ts +0 -3
  230. package/dist/commands/withdraw.js +0 -187
  231. package/dist/exchanges/edgex.d.ts +0 -62
  232. package/dist/exchanges/edgex.js +0 -552
  233. package/dist/exchanges/nado.d.ts +0 -52
  234. package/dist/exchanges/nado.js +0 -223
  235. package/dist/funding/index.d.ts +0 -3
  236. package/dist/funding/index.js +0 -3
  237. package/dist/retry.d.ts +0 -74
  238. package/dist/retry.js +0 -129
  239. package/dist/ws/market-feed.d.ts +0 -74
  240. package/dist/ws/market-feed.js +0 -227
@@ -1,13 +1,27 @@
1
1
  import { PacificaClient } from "../pacifica/index.js";
2
2
  import { LocalSolanaSigner } from "../signer/index.js";
3
+ import { isExpired } from "../agent-wallet/expiry.js";
4
+ import { PerpError } from "../errors.js";
3
5
  export class PacificaAdapter {
4
6
  name = "pacifica";
5
7
  chain = "solana";
6
8
  aliases = ["pac"];
7
9
  client;
10
+ /** Tier 3: PK direct (constructor-supplied keypair). */
8
11
  _solanaSigner;
12
+ /** Tier 2: OWS master signer (set via setSigner). */
13
+ _masterSigner;
14
+ /** Tier 1: agent OWS signer (Phase 2c). */
15
+ _agentSigner;
16
+ /** Tier 1 metadata. */
17
+ _agentMeta;
18
+ /** --no-agent bypass flag. */
19
+ _useNoAgent = false;
20
+ /** True when adapter was constructed without a real PK (read-only). */
9
21
  _hasRealKey;
22
+ /** Public Solana base58 of the active signer (`_resolveSigner().address`). */
10
23
  account;
24
+ /** Bound `signMessage` for the active signer (Phase 2c routes through `_resolveSigner`). */
11
25
  signMessage;
12
26
  _marketsCache = null;
13
27
  _marketsCacheTime = 0;
@@ -17,18 +31,92 @@ export class PacificaAdapter {
17
31
  this._hasRealKey = hasRealKey;
18
32
  this.account = this._solanaSigner.getPublicKeyBase58();
19
33
  this.client = new PacificaClient({ network, builderCode });
20
- this.signMessage = (msg) => this._solanaSigner.signMessage(msg);
34
+ // Hot-path methods always go through `_resolveSigner()` so the bound
35
+ // `signMessage` reflects the highest-priority active tier.
36
+ this.signMessage = (msg) => this._resolveSigner().signer.signMessage(msg);
21
37
  }
22
38
  ensureSigner() {
23
- if (!this._hasRealKey) {
24
- throw new Error("No private key configured. Run: perp setup");
25
- }
39
+ // Resolves across all tiers; throws PerpError if none available.
40
+ this._resolveSigner();
26
41
  }
27
- /** Inject an external Solana signer. */
42
+ /**
43
+ * Inject an external Solana signer (Tier 2 — OWS master).
44
+ * Backward-compatible: existing callers (e.g. `perp -e pacifica --ows ...`
45
+ * in `_initWithOws`) continue to work; the master signer is what
46
+ * `_resolveSigner()` falls back to when no agent is registered.
47
+ */
28
48
  setSigner(signer) {
49
+ this._masterSigner = signer;
29
50
  this._solanaSigner = signer;
30
51
  this.account = signer.getPublicKeyBase58();
31
- this.signMessage = (msg) => this._solanaSigner.signMessage(msg);
52
+ this._hasRealKey = true;
53
+ this.signMessage = (msg) => this._resolveSigner().signer.signMessage(msg);
54
+ }
55
+ /** Tier 1: inject agent OWS Solana signer (Phase 2c). */
56
+ setAgentSigner(meta, signer) {
57
+ this._agentMeta = meta;
58
+ this._agentSigner = signer;
59
+ // The `account` field is the master Solana address (used in REST envelopes
60
+ // as the `account` key) — agent path puts the master in `account` and the
61
+ // agent in `agent_wallet`. Capture the master Solana address from the
62
+ // AgentMeta so REST flows can reference it.
63
+ if (meta.userSolanaAddress) {
64
+ this.account = meta.userSolanaAddress;
65
+ }
66
+ }
67
+ /** Bypass Tier 1 (agent) — used for `--no-agent` flag. */
68
+ setNoAgent(noAgent) {
69
+ this._useNoAgent = noAgent;
70
+ }
71
+ /** True only when ALL THREE tiers are unconfigured. */
72
+ get isReadOnly() {
73
+ return !this._agentSigner && !this._masterSigner && !this._hasRealKey;
74
+ }
75
+ /** Which signer tier is currently active (for debug/diagnostics). */
76
+ get activeSignerTier() {
77
+ try {
78
+ return this._resolveSigner().tier;
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ /** Resolve the active signer across three tiers. Mirrors AsterAdapter._resolveSigner. */
85
+ _resolveSigner() {
86
+ // Tier 1: agent (when registered, not expired, --no-agent NOT set)
87
+ if (!this._useNoAgent && this._agentSigner && this._agentMeta) {
88
+ if (isExpired(this._agentMeta)) {
89
+ // Skip Tier 1 only if Tier 2/3 available; otherwise throw AGENT_EXPIRED
90
+ if (!this._masterSigner && !this._hasRealKey) {
91
+ throw new PerpError("AGENT_EXPIRED", "Agent wallet has expired", {
92
+ remediation: "perp wallet agent approve pacifica --rotate",
93
+ });
94
+ }
95
+ // fall through to Tier 2/3
96
+ }
97
+ else {
98
+ return { tier: "agent", signer: this._agentSigner, address: this._agentSigner.getPublicKeyBase58() };
99
+ }
100
+ }
101
+ // Tier 2: OWS master
102
+ if (this._masterSigner) {
103
+ return { tier: "master", signer: this._masterSigner, address: this._masterSigner.getPublicKeyBase58() };
104
+ }
105
+ // Tier 3: PK direct
106
+ if (this._hasRealKey) {
107
+ // Guard: legacy test fixtures (e.g. bugfix-v042.test.ts) construct adapters
108
+ // via `Object.create(PacificaAdapter.prototype)` and only set `_hasRealKey = true`
109
+ // — `_solanaSigner` is undefined in that case. Fall back to the cached
110
+ // `account` field when available.
111
+ const address = this._solanaSigner
112
+ ? this._solanaSigner.getPublicKeyBase58()
113
+ : this.account;
114
+ return { tier: "pk", signer: this._solanaSigner, address };
115
+ }
116
+ // No signer at any tier
117
+ throw new PerpError("NO_SIGNER_AVAILABLE", "No signing path configured for Pacifica", {
118
+ remediation: "Run one of: (a) perp wallet agent approve pacifica --master <wallet>; (b) perp wallet generate && perp -e pacifica --ows <wallet>; (c) export PACIFICA_PRIVATE_KEY=...",
119
+ });
32
120
  }
33
121
  /** Access the underlying Keypair (only available with LocalSolanaSigner). */
34
122
  get keypair() {
@@ -56,6 +56,9 @@ async function fetchHyperliquidRates() {
56
56
  }
57
57
  }
58
58
  async function fetchLighterRates() {
59
+ // Outer try/catch returning [] is intentional graceful degradation; see
60
+ // fetchAsterRates for the same pattern. Inside, SSOT rule #2 applies —
61
+ // skip rows whose mark price cannot be resolved instead of 0-coercing.
59
62
  try {
60
63
  const [details, funding] = await Promise.all([
61
64
  fetchLighterOrderBookDetails(),
@@ -63,25 +66,47 @@ async function fetchLighterRates() {
63
66
  ]);
64
67
  const priceMap = new Map(details.map(d => [d.marketId, d.lastTradePrice]));
65
68
  const symMap = new Map(details.map(d => [d.marketId, d.symbol]));
66
- return funding.map(fr => {
69
+ const out = [];
70
+ for (const fr of funding) {
67
71
  const symbol = fr.symbol || symMap.get(fr.marketId) || "";
72
+ if (!symbol)
73
+ continue;
68
74
  const rate = fr.rate;
69
- const hourly = toHourlyRate(rate, "lighter");
70
- return {
75
+ // Prefer fr.markPrice; fall back to orderBook last-trade as a documented
76
+ // price-source preference (NOT an error fallback).
77
+ const directMark = fr.markPrice;
78
+ const fallbackMark = priceMap.get(fr.marketId);
79
+ let markPrice;
80
+ if (directMark !== null && Number.isFinite(directMark) && directMark > 0) {
81
+ markPrice = directMark;
82
+ }
83
+ else if (fallbackMark !== undefined && Number.isFinite(fallbackMark) && fallbackMark > 0) {
84
+ markPrice = fallbackMark;
85
+ }
86
+ if (!Number.isFinite(rate) || markPrice === undefined)
87
+ continue;
88
+ out.push({
71
89
  exchange: "lighter",
72
90
  symbol,
73
91
  fundingRate: rate,
74
- hourlyRate: hourly,
92
+ hourlyRate: toHourlyRate(rate, "lighter"),
75
93
  annualizedPct: annualizeRate(rate, "lighter"),
76
- markPrice: fr.markPrice || priceMap.get(fr.marketId) || 0,
77
- };
78
- });
94
+ markPrice,
95
+ });
96
+ }
97
+ return out;
79
98
  }
80
99
  catch {
81
100
  return [];
82
101
  }
83
102
  }
84
103
  async function fetchAsterRates() {
104
+ // Outer try/catch returning [] is intentional graceful degradation: when
105
+ // the entire endpoint is unreachable, the cross-DEX comparison should
106
+ // continue with the remaining venues.
107
+ // Inside the try block, SSOT rule #2 applies — refuse to silently 0-coerce
108
+ // missing markPrice / lastFundingRate, since those values feed downstream
109
+ // sizing and any 0 corrupts the comparison.
85
110
  try {
86
111
  const [premiums, tickers] = await Promise.all([
87
112
  fetch("https://fapi.asterdex.com/fapi/v1/premiumIndex").then(r => r.json()),
@@ -89,25 +114,38 @@ async function fetchAsterRates() {
89
114
  ]);
90
115
  const priceMap = new Map();
91
116
  for (const t of tickers ?? []) {
92
- priceMap.set(String(t.symbol), Number(t.lastPrice ?? 0));
117
+ const last = Number(t.lastPrice);
118
+ if (Number.isFinite(last) && last > 0)
119
+ priceMap.set(String(t.symbol), last);
93
120
  }
94
- return (premiums ?? [])
95
- .filter(p => String(p.symbol ?? "").endsWith("USDT"))
96
- .map(p => {
121
+ const out = [];
122
+ for (const p of premiums ?? []) {
97
123
  const rawSymbol = String(p.symbol ?? "");
124
+ if (!rawSymbol.endsWith("USDT"))
125
+ continue;
126
+ const fundingRate = Number(p.lastFundingRate);
127
+ // Prefer premiumIndex.markPrice; fall back to ticker.lastPrice as a
128
+ // documented price-source preference (NOT an error fallback).
129
+ const markFromPremium = Number(p.markPrice);
130
+ const markPrice = Number.isFinite(markFromPremium) && markFromPremium > 0
131
+ ? markFromPremium
132
+ : priceMap.get(rawSymbol);
133
+ if (!Number.isFinite(fundingRate) || markPrice === undefined || !Number.isFinite(markPrice) || markPrice <= 0) {
134
+ continue;
135
+ }
98
136
  const symbol = rawSymbol.replace(/USDT$/, "");
99
- const rate = Number(p.lastFundingRate ?? 0);
100
- const hourly = toHourlyRate(rate, "aster");
101
- return {
137
+ const nextFundingTimeRaw = Number(p.nextFundingTime);
138
+ out.push({
102
139
  exchange: "aster",
103
140
  symbol,
104
- fundingRate: rate,
105
- hourlyRate: hourly,
106
- annualizedPct: annualizeRate(rate, "aster"),
107
- markPrice: Number(p.markPrice ?? priceMap.get(rawSymbol) ?? 0),
108
- nextFundingTime: Number(p.nextFundingTime ?? 0),
109
- };
110
- });
141
+ fundingRate,
142
+ hourlyRate: toHourlyRate(fundingRate, "aster"),
143
+ annualizedPct: annualizeRate(fundingRate, "aster"),
144
+ markPrice,
145
+ nextFundingTime: Number.isFinite(nextFundingTimeRaw) ? nextFundingTimeRaw : 0,
146
+ });
147
+ }
148
+ return out;
111
149
  }
112
150
  catch {
113
151
  return [];
@@ -46,7 +46,15 @@ function isWithdrawTx(data) {
46
46
  return selector === "0x3ccfd60b" || selector === "0x2e1a7d4d";
47
47
  }
48
48
  function evaluate(ctx) {
49
- const config = ctx.policy_config ?? {};
49
+ // SSOT rule #2: policy_config must be explicitly supplied. A missing
50
+ // policy_config previously fell through to silent defaults — that is a
51
+ // safety-critical fallback (the user might assume their custom limits are
52
+ // active when in fact only baked-in defaults are). Fail closed instead.
53
+ if (!ctx.policy_config) {
54
+ return deny("policy_config missing from PolicyContext; refusing to fall through to baked-in defaults. " +
55
+ "Caller must supply policy_config explicitly (max_tx_usd, max_daily_usd, etc.).");
56
+ }
57
+ const config = ctx.policy_config;
50
58
  const maxTxUsd = config.max_tx_usd ?? DEFAULT_LIMITS.max_tx_usd;
51
59
  const maxDailyUsd = config.max_daily_usd ?? DEFAULT_LIMITS.max_daily_usd;
52
60
  const maxWithdrawUsd = config.max_withdraw_usd ?? maxTxUsd;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ import "./_stderr-filter.js";