@reefclaw/openclaw-plugin 0.1.12 → 0.1.13

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.
@@ -29,6 +29,8 @@ import { logger } from '../../logger.js';
29
29
  import { toCcxtSymbol } from '../symbols.js';
30
30
  import { assertNotLimited, noteError, noteSuccess, exchangeIpWeight, updateAddressBudget, } from './hl-rate-gate.js';
31
31
  import { isValidHlCloid } from './hl-cloid.js';
32
+ import { normalizeHlPosition } from './hl-position.js';
33
+ import { normalizeHlOrder } from './hl-order.js';
32
34
  // ccxt via CJS require — OpenClaw's ESM loader yields the wrong module shape
33
35
  // (same rationale as binance-private.ts / hl-public.ts).
34
36
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -124,7 +126,15 @@ export class HyperliquidPrivateApi {
124
126
  }
125
127
  // ---- Reads (null on failure — NEVER []) ----
126
128
  /** Positions for the MASTER account. `null` = fetch failed (state unknown);
127
- * `[]` = the exchange confirmed flat. */
129
+ * `[]` = the exchange confirmed flat.
130
+ *
131
+ * ★ Rows are normalized through `normalizeHlPosition` — ccxt leaves
132
+ * `markPrice`/`contractSize`/`timestamp`/`datetime` UNDEFINED on this venue
133
+ * (clearinghouseState carries no markPx) while `CcxtPosition` declares them
134
+ * required, which crashed `get_risk_summary` and degraded every other
135
+ * mark-reading consumer. See hl-position.ts for the doc-exact derivation.
136
+ * A row that cannot be priced makes the WHOLE snapshot null (unknown) — never
137
+ * a partial list, never a fabricated zero. */
128
138
  async fetchPositions(symbol) {
129
139
  try {
130
140
  assertNotLimited('clearinghouseState');
@@ -133,7 +143,19 @@ export class HyperliquidPrivateApi {
133
143
  noteSuccess('clearinghouseState');
134
144
  if (!Array.isArray(raw))
135
145
  return null;
136
- return raw.filter((p) => Math.abs(Number(p.contracts ?? 0)) > 0);
146
+ const out = [];
147
+ for (const p of raw) {
148
+ if (Math.abs(Number(p?.contracts ?? 0)) <= 0)
149
+ continue; // flat row — information, not failure
150
+ const normalized = normalizeHlPosition(p);
151
+ if (!normalized) {
152
+ logger.error(TAG, `fetchPositions: unparseable position row for ${String(p?.symbol ?? 'unknown symbol')} ` +
153
+ '— returning null (snapshot UNTRUSTED; a dropped row would read as "flat")');
154
+ return null;
155
+ }
156
+ out.push(normalized);
157
+ }
158
+ return out;
137
159
  }
138
160
  catch (err) {
139
161
  noteError(err, 'fetchPositions');
@@ -143,14 +165,19 @@ export class HyperliquidPrivateApi {
143
165
  }
144
166
  /** Open orders INCLUDING trigger/TPSL legs. CCXT's HL `fetchOpenOrders`
145
167
  * defaults to `frontendOpenOrders`, which is the only endpoint that returns
146
- * trigger orders (plan §3.6) — the analog of Binance's merged algo endpoints. */
168
+ * trigger orders (plan §3.6) — the analog of Binance's merged algo endpoints.
169
+ *
170
+ * ★ Rows are normalized through `normalizeHlOrder` (ccxt leaves cost/fee/
171
+ * average/timeInForce undefined and leaks 'take profit market' past the
172
+ * type union — see hl-order.ts); `info` + `clientOrderId` survive verbatim
173
+ * (protective classification + bracket reconciliation read them). */
147
174
  async fetchOpenOrders(symbol) {
148
175
  try {
149
176
  assertNotLimited('frontendOpenOrders');
150
177
  const s = symbol ? toCcxtSymbol('hyperliquid', symbol) : undefined;
151
178
  const raw = await this.exchange.fetchOpenOrders(s);
152
179
  noteSuccess('frontendOpenOrders');
153
- return Array.isArray(raw) ? raw : null;
180
+ return Array.isArray(raw) ? raw.map((o) => normalizeHlOrder(o)) : null;
154
181
  }
155
182
  catch (err) {
156
183
  noteError(err, 'fetchOpenOrders');
@@ -173,14 +200,18 @@ export class HyperliquidPrivateApi {
173
200
  }
174
201
  /** Per-order status — the liveness resolver's REST tier (Tier 2 of the
175
202
  * 3-tier rule). Weight 2. `null` = lookup FAILED (unknown), which callers
176
- * must treat as "do not act", NOT as "gone". */
203
+ * must treat as "do not act", NOT as "gone".
204
+ *
205
+ * Normalized like fetchOpenOrders. Unknown statuses collapse to 'open'
206
+ * (= not confirmed terminal — the conservative direction for both the
207
+ * liveness resolver and the entry poller); raw status stays in `info`. */
177
208
  async fetchOrder(orderId, symbol) {
178
209
  try {
179
210
  assertNotLimited('orderStatus');
180
211
  const s = symbol ? toCcxtSymbol('hyperliquid', symbol) : undefined;
181
212
  const raw = await this.exchange.fetchOrder(orderId, s);
182
213
  noteSuccess('orderStatus');
183
- return raw ?? null;
214
+ return raw ? normalizeHlOrder(raw) : null;
184
215
  }
185
216
  catch (err) {
186
217
  noteError(err, 'fetchOrder');