@pear-protocol/symmio-client 0.2.27 → 0.2.29
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/dist/react/index.js +1314 -1136
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +1314 -1136
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/provider.js +443 -0
- package/dist/react/provider.js.map +1 -1
- package/dist/react/provider.mjs +444 -1
- package/dist/react/provider.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react/provider.js
CHANGED
|
@@ -2,10 +2,449 @@
|
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
4
|
var symmCore = require('@pear-protocol/symm-core');
|
|
5
|
+
var zustand = require('zustand');
|
|
5
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
7
|
|
|
7
8
|
// src/react/provider.tsx
|
|
8
9
|
var SymmContext = react.createContext(null);
|
|
10
|
+
|
|
11
|
+
// src/utils/binance-symbol-map.ts
|
|
12
|
+
var SYMBOL_OVERRIDES = {
|
|
13
|
+
// Add overrides here as needed for SYMM markets that don't map 1:1 to Binance
|
|
14
|
+
// e.g., 'SOME_SYMM_SYMBOL': 'BINANCE_SYMBOL',
|
|
15
|
+
};
|
|
16
|
+
var UNSUPPORTED_SYMBOLS = /* @__PURE__ */ new Set([
|
|
17
|
+
// Add symbols here that have no Binance equivalent
|
|
18
|
+
]);
|
|
19
|
+
function resolveBinanceSymbol(symmSymbol) {
|
|
20
|
+
if (!symmSymbol || !symmSymbol.trim()) {
|
|
21
|
+
return {
|
|
22
|
+
symmSymbol,
|
|
23
|
+
normalizedSymbol: "",
|
|
24
|
+
binanceSymbol: null,
|
|
25
|
+
supported: false,
|
|
26
|
+
reason: "missing_symbol"
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const normalized = symmSymbol.toUpperCase().trim();
|
|
30
|
+
if (!/^[A-Z0-9]+$/.test(normalized)) {
|
|
31
|
+
return {
|
|
32
|
+
symmSymbol,
|
|
33
|
+
normalizedSymbol: normalized,
|
|
34
|
+
binanceSymbol: null,
|
|
35
|
+
supported: false,
|
|
36
|
+
reason: "invalid_symbol"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (UNSUPPORTED_SYMBOLS.has(normalized)) {
|
|
40
|
+
return {
|
|
41
|
+
symmSymbol,
|
|
42
|
+
normalizedSymbol: normalized,
|
|
43
|
+
binanceSymbol: null,
|
|
44
|
+
supported: false,
|
|
45
|
+
reason: "unsupported_symbol"
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const binanceSymbol = SYMBOL_OVERRIDES[normalized] ?? (normalized.endsWith("USDT") ? normalized : `${normalized}USDT`);
|
|
49
|
+
return {
|
|
50
|
+
symmSymbol,
|
|
51
|
+
normalizedSymbol: normalized,
|
|
52
|
+
binanceSymbol,
|
|
53
|
+
supported: true,
|
|
54
|
+
reason: null
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/utils/binance-ws.ts
|
|
59
|
+
var BINANCE_WS_URL = "wss://fstream.binance.com/market/ws";
|
|
60
|
+
var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
|
|
61
|
+
var STABLE_QUOTES = ["USDT0", "USDT", "USDC", "USDE", "USDH", "USD"];
|
|
62
|
+
function normalizeBaseSymbol(symbol) {
|
|
63
|
+
const normalized = symbol.toUpperCase().trim();
|
|
64
|
+
for (const quote of STABLE_QUOTES) {
|
|
65
|
+
if (normalized.endsWith(quote) && normalized.length > quote.length) {
|
|
66
|
+
return normalized.slice(0, -quote.length);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return normalized;
|
|
70
|
+
}
|
|
71
|
+
function isBinanceMarkPriceTicker(value) {
|
|
72
|
+
return Boolean(
|
|
73
|
+
value && typeof value === "object" && typeof value.s === "string" && typeof value.p === "string" && typeof value.i === "string" && typeof value.E === "number"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
function extractTickers(payload) {
|
|
77
|
+
if (Array.isArray(payload)) {
|
|
78
|
+
return payload.filter(isBinanceMarkPriceTicker);
|
|
79
|
+
}
|
|
80
|
+
if (payload && typeof payload === "object") {
|
|
81
|
+
const maybeData = payload.data;
|
|
82
|
+
if (Array.isArray(maybeData)) {
|
|
83
|
+
return maybeData.filter(isBinanceMarkPriceTicker);
|
|
84
|
+
}
|
|
85
|
+
return isBinanceMarkPriceTicker(payload) ? [payload] : [];
|
|
86
|
+
}
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
function extractWsPayload(payload) {
|
|
90
|
+
if (payload && typeof payload === "object" && "data" in payload) {
|
|
91
|
+
return payload.data ?? payload;
|
|
92
|
+
}
|
|
93
|
+
return payload;
|
|
94
|
+
}
|
|
95
|
+
function isKlinePayload(payload) {
|
|
96
|
+
return Boolean(
|
|
97
|
+
payload && typeof payload === "object" && payload.e === "kline" && typeof payload.s === "string" && payload.k && typeof payload.k.i === "string"
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
function isMarkPricePayload(payload) {
|
|
101
|
+
return Boolean(
|
|
102
|
+
payload && typeof payload === "object" && payload.e === "markPriceUpdate" && typeof payload.s === "string"
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
var BinanceWsManager = class {
|
|
106
|
+
ws = null;
|
|
107
|
+
streams = /* @__PURE__ */ new Map();
|
|
108
|
+
reconnectAttempt = 0;
|
|
109
|
+
reconnectTimer = null;
|
|
110
|
+
intentionalClose = false;
|
|
111
|
+
pendingSubscribes = [];
|
|
112
|
+
idCounter = 0;
|
|
113
|
+
/**
|
|
114
|
+
* Subscribe to a kline stream. Returns an unsubscribe function.
|
|
115
|
+
*/
|
|
116
|
+
subscribeKline(symbol, interval, cb) {
|
|
117
|
+
const streamName = `${symbol.toLowerCase()}@kline_${interval}`;
|
|
118
|
+
const id = this.generateId();
|
|
119
|
+
const wrappedCb = (raw) => {
|
|
120
|
+
const k = raw.k;
|
|
121
|
+
if (!k) return;
|
|
122
|
+
cb({
|
|
123
|
+
symbol: raw.s,
|
|
124
|
+
interval: k.i,
|
|
125
|
+
openTime: k.t,
|
|
126
|
+
closeTime: k.T,
|
|
127
|
+
open: parseFloat(k.o),
|
|
128
|
+
high: parseFloat(k.h),
|
|
129
|
+
low: parseFloat(k.l),
|
|
130
|
+
close: parseFloat(k.c),
|
|
131
|
+
volume: parseFloat(k.v),
|
|
132
|
+
isFinal: k.x
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
this.addStreamCallback(streamName, id, wrappedCb);
|
|
136
|
+
return () => this.removeStreamCallback(streamName, id);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Subscribe to a mark price stream (1s updates). Returns an unsubscribe function.
|
|
140
|
+
*/
|
|
141
|
+
subscribeMarkPrice(symbol, cb) {
|
|
142
|
+
const streamName = `${symbol.toLowerCase()}@markPrice@1s`;
|
|
143
|
+
const id = this.generateId();
|
|
144
|
+
const wrappedCb = (raw) => {
|
|
145
|
+
cb({
|
|
146
|
+
symbol: normalizeBaseSymbol(raw.s),
|
|
147
|
+
markPrice: parseFloat(raw.p),
|
|
148
|
+
indexPrice: parseFloat(raw.i),
|
|
149
|
+
time: raw.E
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
this.addStreamCallback(streamName, id, wrappedCb);
|
|
153
|
+
return () => this.removeStreamCallback(streamName, id);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Subscribe to the all-market mark price stream (1s updates).
|
|
157
|
+
* Returns an unsubscribe function.
|
|
158
|
+
*/
|
|
159
|
+
subscribeAllMarkPrices(cb) {
|
|
160
|
+
const streamName = "!markPrice@arr@1s";
|
|
161
|
+
const id = this.generateId();
|
|
162
|
+
const wrappedCb = (raw) => {
|
|
163
|
+
cb(
|
|
164
|
+
extractTickers(raw).map((entry) => ({
|
|
165
|
+
symbol: normalizeBaseSymbol(entry.s),
|
|
166
|
+
markPrice: parseFloat(entry.p),
|
|
167
|
+
indexPrice: parseFloat(entry.i),
|
|
168
|
+
time: entry.E
|
|
169
|
+
}))
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
this.addStreamCallback(streamName, id, wrappedCb);
|
|
173
|
+
return () => this.removeStreamCallback(streamName, id);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Destroy the manager and close the connection.
|
|
177
|
+
*/
|
|
178
|
+
destroy() {
|
|
179
|
+
this.intentionalClose = true;
|
|
180
|
+
if (this.reconnectTimer) {
|
|
181
|
+
clearTimeout(this.reconnectTimer);
|
|
182
|
+
this.reconnectTimer = null;
|
|
183
|
+
}
|
|
184
|
+
if (this.ws) {
|
|
185
|
+
this.ws.close();
|
|
186
|
+
this.ws = null;
|
|
187
|
+
}
|
|
188
|
+
this.streams.clear();
|
|
189
|
+
}
|
|
190
|
+
// --- Private ---
|
|
191
|
+
generateId() {
|
|
192
|
+
return `sub_${++this.idCounter}_${Date.now()}`;
|
|
193
|
+
}
|
|
194
|
+
addStreamCallback(streamName, id, cb) {
|
|
195
|
+
let sub = this.streams.get(streamName);
|
|
196
|
+
const isNew = !sub;
|
|
197
|
+
if (!sub) {
|
|
198
|
+
sub = { callbacks: /* @__PURE__ */ new Map() };
|
|
199
|
+
this.streams.set(streamName, sub);
|
|
200
|
+
}
|
|
201
|
+
sub.callbacks.set(id, cb);
|
|
202
|
+
if (isNew) {
|
|
203
|
+
this.ensureConnected();
|
|
204
|
+
this.sendSubscribe([streamName]);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
removeStreamCallback(streamName, id) {
|
|
208
|
+
const sub = this.streams.get(streamName);
|
|
209
|
+
if (!sub) return;
|
|
210
|
+
sub.callbacks.delete(id);
|
|
211
|
+
if (sub.callbacks.size === 0) {
|
|
212
|
+
this.streams.delete(streamName);
|
|
213
|
+
this.sendUnsubscribe([streamName]);
|
|
214
|
+
if (this.streams.size === 0 && this.ws) {
|
|
215
|
+
this.intentionalClose = true;
|
|
216
|
+
this.ws.close();
|
|
217
|
+
this.ws = null;
|
|
218
|
+
this.intentionalClose = false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
ensureConnected() {
|
|
223
|
+
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
this.connect();
|
|
227
|
+
}
|
|
228
|
+
connect() {
|
|
229
|
+
if (typeof WebSocket === "undefined") return;
|
|
230
|
+
this.intentionalClose = false;
|
|
231
|
+
this.ws = new WebSocket(BINANCE_WS_URL);
|
|
232
|
+
this.ws.onopen = () => {
|
|
233
|
+
this.reconnectAttempt = 0;
|
|
234
|
+
const activeStreams = Array.from(this.streams.keys());
|
|
235
|
+
if (activeStreams.length > 0) {
|
|
236
|
+
this.sendSubscribe(activeStreams);
|
|
237
|
+
}
|
|
238
|
+
if (this.pendingSubscribes.length > 0) {
|
|
239
|
+
this.sendSubscribe(this.pendingSubscribes);
|
|
240
|
+
this.pendingSubscribes = [];
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
this.ws.onmessage = (event) => {
|
|
244
|
+
try {
|
|
245
|
+
const data = JSON.parse(event.data);
|
|
246
|
+
this.handleMessage(data);
|
|
247
|
+
} catch {
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
this.ws.onclose = () => {
|
|
251
|
+
if (this.intentionalClose) return;
|
|
252
|
+
this.scheduleReconnect();
|
|
253
|
+
};
|
|
254
|
+
this.ws.onerror = () => {
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
handleMessage(data) {
|
|
258
|
+
const payload = extractWsPayload(data);
|
|
259
|
+
if (Array.isArray(payload)) {
|
|
260
|
+
this.dispatchToStream("!markPrice@arr@1s", payload);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (isKlinePayload(payload)) {
|
|
264
|
+
const k = payload.k;
|
|
265
|
+
const streamName = `${payload.s.toLowerCase()}@kline_${k.i}`;
|
|
266
|
+
this.dispatchToStream(streamName, payload);
|
|
267
|
+
} else if (isMarkPricePayload(payload)) {
|
|
268
|
+
const streamName = `${payload.s.toLowerCase()}@markPrice@1s`;
|
|
269
|
+
this.dispatchToStream(streamName, payload);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
dispatchToStream(streamName, data) {
|
|
273
|
+
const sub = this.streams.get(streamName);
|
|
274
|
+
if (!sub) return;
|
|
275
|
+
sub.callbacks.forEach((cb) => {
|
|
276
|
+
try {
|
|
277
|
+
cb(data);
|
|
278
|
+
} catch {
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
sendSubscribe(streams) {
|
|
283
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
284
|
+
this.pendingSubscribes.push(...streams);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
this.ws.send(JSON.stringify({
|
|
288
|
+
method: "SUBSCRIBE",
|
|
289
|
+
params: streams,
|
|
290
|
+
id: Date.now()
|
|
291
|
+
}));
|
|
292
|
+
}
|
|
293
|
+
sendUnsubscribe(streams) {
|
|
294
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
295
|
+
this.pendingSubscribes = this.pendingSubscribes.filter(
|
|
296
|
+
(s) => !streams.includes(s)
|
|
297
|
+
);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
this.ws.send(JSON.stringify({
|
|
301
|
+
method: "UNSUBSCRIBE",
|
|
302
|
+
params: streams,
|
|
303
|
+
id: Date.now()
|
|
304
|
+
}));
|
|
305
|
+
}
|
|
306
|
+
scheduleReconnect() {
|
|
307
|
+
if (this.reconnectTimer) return;
|
|
308
|
+
if (this.streams.size === 0) return;
|
|
309
|
+
const delay = RECONNECT_DELAYS[Math.min(this.reconnectAttempt, RECONNECT_DELAYS.length - 1)];
|
|
310
|
+
this.reconnectAttempt++;
|
|
311
|
+
this.reconnectTimer = setTimeout(() => {
|
|
312
|
+
this.reconnectTimer = null;
|
|
313
|
+
this.connect();
|
|
314
|
+
}, delay);
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
var _instance = null;
|
|
318
|
+
function getBinanceWsManager() {
|
|
319
|
+
if (!_instance) {
|
|
320
|
+
_instance = new BinanceWsManager();
|
|
321
|
+
}
|
|
322
|
+
return _instance;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/react/stores/use-binance-mark-price-store.ts
|
|
326
|
+
var refCounts = /* @__PURE__ */ new Map();
|
|
327
|
+
var streamSymbols = /* @__PURE__ */ new Map();
|
|
328
|
+
var allMarkPricesRefCount = 0;
|
|
329
|
+
var allMarkPricesUnsubscribe = null;
|
|
330
|
+
var STABLE_QUOTES2 = ["USDT0", "USDT", "USDC", "USDE", "USDH", "USD"];
|
|
331
|
+
function normalizeBinanceSymbol(symbol) {
|
|
332
|
+
const normalized = symbol.toUpperCase().trim();
|
|
333
|
+
for (const quote of STABLE_QUOTES2) {
|
|
334
|
+
if (normalized.endsWith(quote) && normalized.length > quote.length) {
|
|
335
|
+
return normalized.slice(0, -quote.length);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return normalized;
|
|
339
|
+
}
|
|
340
|
+
function getNextRefCount(binanceSymbol) {
|
|
341
|
+
return (refCounts.get(binanceSymbol) ?? 0) + 1;
|
|
342
|
+
}
|
|
343
|
+
function getPrevRefCount(binanceSymbol) {
|
|
344
|
+
return Math.max(0, (refCounts.get(binanceSymbol) ?? 0) - 1);
|
|
345
|
+
}
|
|
346
|
+
var useBinanceMarkPriceStore = zustand.create((set) => ({
|
|
347
|
+
markPrices: {},
|
|
348
|
+
subscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
|
|
349
|
+
const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
|
|
350
|
+
const nextRefCount = getNextRefCount(binanceSymbol);
|
|
351
|
+
refCounts.set(binanceSymbol, nextRefCount);
|
|
352
|
+
const symbols = streamSymbols.get(binanceSymbol) ?? /* @__PURE__ */ new Set();
|
|
353
|
+
symbols.add(symmSymbol);
|
|
354
|
+
streamSymbols.set(binanceSymbol, symbols);
|
|
355
|
+
if (allMarkPricesRefCount === 0) {
|
|
356
|
+
const wsManager = getBinanceWsManager();
|
|
357
|
+
allMarkPricesUnsubscribe = wsManager.subscribeAllMarkPrices((entries) => {
|
|
358
|
+
set((state) => {
|
|
359
|
+
let nextMarkPrices = null;
|
|
360
|
+
entries.forEach((entry) => {
|
|
361
|
+
const canonicalSymbol = normalizeBinanceSymbol(entry.symbol);
|
|
362
|
+
const mappedSymbols = streamSymbols.get(canonicalSymbol);
|
|
363
|
+
if (!mappedSymbols || mappedSymbols.size === 0) return;
|
|
364
|
+
nextMarkPrices ??= { ...state.markPrices };
|
|
365
|
+
mappedSymbols.forEach((mappedSymbol) => {
|
|
366
|
+
nextMarkPrices[mappedSymbol] = entry.markPrice;
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
return nextMarkPrices ? { markPrices: nextMarkPrices } : state;
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
allMarkPricesRefCount += 1;
|
|
374
|
+
},
|
|
375
|
+
unsubscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
|
|
376
|
+
const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
|
|
377
|
+
const symbols = streamSymbols.get(binanceSymbol);
|
|
378
|
+
if (symbols) {
|
|
379
|
+
symbols.delete(symmSymbol);
|
|
380
|
+
if (symbols.size === 0) {
|
|
381
|
+
streamSymbols.delete(binanceSymbol);
|
|
382
|
+
} else {
|
|
383
|
+
streamSymbols.set(binanceSymbol, symbols);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
const nextRefCount = getPrevRefCount(binanceSymbol);
|
|
387
|
+
if (nextRefCount === 0) {
|
|
388
|
+
refCounts.delete(binanceSymbol);
|
|
389
|
+
} else {
|
|
390
|
+
refCounts.set(binanceSymbol, nextRefCount);
|
|
391
|
+
}
|
|
392
|
+
allMarkPricesRefCount = Math.max(0, allMarkPricesRefCount - 1);
|
|
393
|
+
if (allMarkPricesRefCount === 0) {
|
|
394
|
+
allMarkPricesUnsubscribe?.();
|
|
395
|
+
allMarkPricesUnsubscribe = null;
|
|
396
|
+
}
|
|
397
|
+
set((state) => {
|
|
398
|
+
if (state.markPrices[symmSymbol] == null) return state;
|
|
399
|
+
const nextMarkPrices = { ...state.markPrices };
|
|
400
|
+
delete nextMarkPrices[symmSymbol];
|
|
401
|
+
return { markPrices: nextMarkPrices };
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
}));
|
|
405
|
+
|
|
406
|
+
// src/react/hooks/use-binance-ws.ts
|
|
407
|
+
function useBinanceWs(params) {
|
|
408
|
+
const { symmCoreClient, chainId } = params;
|
|
409
|
+
const subscribeSymbol = useBinanceMarkPriceStore((state) => state.subscribeSymbol);
|
|
410
|
+
const unsubscribeSymbol = useBinanceMarkPriceStore((state) => state.unsubscribeSymbol);
|
|
411
|
+
react.useEffect(() => {
|
|
412
|
+
if (!symmCoreClient) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
let cancelled = false;
|
|
416
|
+
let subscribedPairs = [];
|
|
417
|
+
const run = async () => {
|
|
418
|
+
try {
|
|
419
|
+
const result = await symmCoreClient.markets.listSymmHedger({ chainId });
|
|
420
|
+
if (cancelled) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const uniqueSymbols = Array.from(
|
|
424
|
+
new Set(
|
|
425
|
+
result.markets.map((market) => market.symbol).filter((symbol) => !!symbol)
|
|
426
|
+
)
|
|
427
|
+
);
|
|
428
|
+
subscribedPairs = uniqueSymbols.map((symbol) => {
|
|
429
|
+
const binanceSymbol = resolveBinanceSymbol(symbol).binanceSymbol;
|
|
430
|
+
if (!binanceSymbol) return null;
|
|
431
|
+
return [symbol, binanceSymbol];
|
|
432
|
+
}).filter((entry) => !!entry);
|
|
433
|
+
subscribedPairs.forEach(([symbol, binanceSymbol]) => {
|
|
434
|
+
subscribeSymbol(symbol, binanceSymbol);
|
|
435
|
+
});
|
|
436
|
+
} catch {
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
void run();
|
|
440
|
+
return () => {
|
|
441
|
+
cancelled = true;
|
|
442
|
+
subscribedPairs.forEach(([symbol, binanceSymbol]) => {
|
|
443
|
+
unsubscribeSymbol(symbol, binanceSymbol);
|
|
444
|
+
});
|
|
445
|
+
};
|
|
446
|
+
}, [symmCoreClient, chainId, subscribeSymbol, unsubscribeSymbol]);
|
|
447
|
+
}
|
|
9
448
|
function SymmProvider({
|
|
10
449
|
chainId = 42161,
|
|
11
450
|
address,
|
|
@@ -32,6 +471,10 @@ function SymmProvider({
|
|
|
32
471
|
}),
|
|
33
472
|
[symmCoreClient, chainId, address, symmioConfig]
|
|
34
473
|
);
|
|
474
|
+
useBinanceWs({
|
|
475
|
+
symmCoreClient,
|
|
476
|
+
chainId
|
|
477
|
+
});
|
|
35
478
|
return /* @__PURE__ */ jsxRuntime.jsx(SymmContext.Provider, { value, children });
|
|
36
479
|
}
|
|
37
480
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/context.ts","../../src/react/provider.tsx"],"names":["createContext","useMemo","createSymmSDK","jsx"],"mappings":";;;;;;;AAYO,IAAM,WAAA,GAAcA,oBAAuC,IAAI,CAAA;ACM/D,SAAS,YAAA,CAAa;AAAA,EAC3B,OAAA,GAAU,KAAA;AAAA,EACV,OAAA;AAAA,EACA,cAAA,GAAiB;AAAA,IACf,MAAA,EAAQ,6CAAA;AAAA,IACR,KAAA,EAAO;AAAA,GACT;AAAA,EACA,YAAA;AAAA,EACA;AACF,CAAA,EAAsB;AACpB,EAAA,MAAM,cAAA,GAAiBC,cAAQ,MAAe;AAC5C,IAAA,OAAOC,sBAAA,CAAc;AAAA,MACnB,QAAQ,cAAA,CAAe,MAAA;AAAA,MACvB,OAAO,cAAA,CAAe,KAAA;AAAA,MACtB,cAAA,EAAgB;AAAA,KACjB,CAAA;AAAA,EACH,GAAG,CAAC,OAAA,EAAS,eAAe,MAAA,EAAQ,cAAA,CAAe,KAAK,CAAC,CAAA;AAEzD,EAAA,MAAM,KAAA,GAAQD,aAAA;AAAA,IACZ,OAAO;AAAA,MACL,cAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACA,CAAC,cAAA,EAAgB,OAAA,EAAS,OAAA,EAAS,YAAY;AAAA,GACjD;AAEA,EAAA,uBAAOE,cAAA,CAAC,WAAA,CAAY,QAAA,EAAZ,EAAqB,OAAe,QAAA,EAAS,CAAA;AACvD","file":"provider.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { Address, PublicClient, WalletClient } from \"viem\";\nimport type { SymmSDK } from \"@pear-protocol/symm-core\";\nimport type { SymmioSDKConfig } from \"../types/common\";\n\nexport type SymmContextValue = {\n symmCoreClient: SymmSDK | null;\n chainId: number;\n address?: `0x${string}`;\n symmioConfig?: Partial<SymmioSDKConfig>;\n};\n\nexport const SymmContext = createContext<SymmContextValue | null>(null);\n\nexport function useSymmContext(): SymmContextValue {\n const ctx = useContext(SymmContext);\n if (!ctx) {\n throw new Error(\"useSymmContext must be used within <SymmProvider>\");\n }\n return ctx;\n}\n","import { useMemo } from \"react\";\nimport type { Address } from \"viem\";\nimport { createSymmSDK, type SymmSDK } from \"@pear-protocol/symm-core\";\n\nimport type { SymmioSDKConfig } from \"../types/common\";\nimport { SymmContext, type SymmContextValue } from \"./context\";\n\nexport type SymmProviderProps = {\n chainId?: number;\n address?: Address;\n symmCoreConfig: {\n apiUrl: string;\n wsUrl?: string;\n };\n symmioConfig?: Partial<SymmioSDKConfig>;\n children: React.ReactNode;\n};\n\nexport function SymmProvider({\n chainId = 42161,\n address,\n symmCoreConfig = {\n apiUrl: \"https://nginx-server-staging.up.railway.app\",\n wsUrl: \"wss://nginx-server-staging.up.railway.app\",\n },\n symmioConfig,\n children,\n}: SymmProviderProps) {\n const symmCoreClient = useMemo((): SymmSDK => {\n return createSymmSDK({\n apiUrl: symmCoreConfig.apiUrl,\n wsUrl: symmCoreConfig.wsUrl,\n defaultChainId: chainId,\n });\n }, [chainId, symmCoreConfig.apiUrl, symmCoreConfig.wsUrl]);\n\n const value = useMemo<SymmContextValue>(\n () => ({\n symmCoreClient,\n chainId,\n address,\n symmioConfig,\n }),\n [symmCoreClient, chainId, address, symmioConfig]\n );\n\n return <SymmContext.Provider value={value}>{children}</SymmContext.Provider>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/context.ts","../../src/utils/binance-symbol-map.ts","../../src/utils/binance-ws.ts","../../src/react/stores/use-binance-mark-price-store.ts","../../src/react/hooks/use-binance-ws.ts","../../src/react/provider.tsx"],"names":["createContext","STABLE_QUOTES","create","useEffect","useMemo","createSymmSDK","jsx"],"mappings":"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","file":"provider.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { Address, PublicClient, WalletClient } from \"viem\";\nimport type { SymmSDK } from \"@pear-protocol/symm-core\";\nimport type { SymmioSDKConfig } from \"../types/common\";\n\nexport type SymmContextValue = {\n symmCoreClient: SymmSDK | null;\n chainId: number;\n address?: `0x${string}`;\n symmioConfig?: Partial<SymmioSDKConfig>;\n};\n\nexport const SymmContext = createContext<SymmContextValue | null>(null);\n\nexport function useSymmContext(): SymmContextValue {\n const ctx = useContext(SymmContext);\n if (!ctx) {\n throw new Error(\"useSymmContext must be used within <SymmProvider>\");\n }\n return ctx;\n}\n","/**\n * Maps SYMM market symbols to Binance USD-M Futures symbols.\n *\n * Most SYMM markets use the same symbol format as Binance (e.g., \"BTCUSDT\").\n * The override table handles exceptions.\n */\n\nconst SYMBOL_OVERRIDES: Record<string, string> = {\n // Add overrides here as needed for SYMM markets that don't map 1:1 to Binance\n // e.g., 'SOME_SYMM_SYMBOL': 'BINANCE_SYMBOL',\n};\n\n// Symbols known to not exist on Binance USD-M Futures\nconst UNSUPPORTED_SYMBOLS = new Set<string>([\n // Add symbols here that have no Binance equivalent\n]);\n\nexport interface BinanceSymbolResolution {\n symmSymbol: string;\n normalizedSymbol: string;\n binanceSymbol: string | null;\n supported: boolean;\n reason: 'missing_symbol' | 'unsupported_symbol' | 'invalid_symbol' | null;\n}\n\nexport function resolveBinanceSymbol(symmSymbol: string): BinanceSymbolResolution {\n if (!symmSymbol || !symmSymbol.trim()) {\n return {\n symmSymbol,\n normalizedSymbol: '',\n binanceSymbol: null,\n supported: false,\n reason: 'missing_symbol',\n };\n }\n\n const normalized = symmSymbol.toUpperCase().trim();\n\n if (!/^[A-Z0-9]+$/.test(normalized)) {\n return {\n symmSymbol,\n normalizedSymbol: normalized,\n binanceSymbol: null,\n supported: false,\n reason: 'invalid_symbol',\n };\n }\n\n if (UNSUPPORTED_SYMBOLS.has(normalized)) {\n return {\n symmSymbol,\n normalizedSymbol: normalized,\n binanceSymbol: null,\n supported: false,\n reason: 'unsupported_symbol',\n };\n }\n\n const binanceSymbol = SYMBOL_OVERRIDES[normalized]\n ?? (normalized.endsWith('USDT') ? normalized : `${normalized}USDT`);\n\n return {\n symmSymbol,\n normalizedSymbol: normalized,\n binanceSymbol,\n supported: true,\n reason: null,\n };\n}\n\nexport function getUnsupportedBinanceSymbols(symbols: string[]): string[] {\n return symbols.filter((symbol) => !resolveBinanceSymbol(symbol).supported);\n}\n\n/**\n * Maps a SYMM market symbol or asset name to its Binance USD-M Futures symbol.\n * Returns null if the symbol is not supported on Binance.\n */\nexport function toBinanceSymbol(symmSymbol: string): string | null {\n return resolveBinanceSymbol(symmSymbol).binanceSymbol;\n}\n\n/**\n * Checks if a SYMM symbol can be mapped to a Binance USD-M Futures symbol.\n */\nexport function isBinanceSupported(symmSymbol: string): boolean {\n return resolveBinanceSymbol(symmSymbol).supported;\n}\n","/**\n * Binance USD-M Futures WebSocket manager.\n *\n * Manages a single connection to wss://fstream.binance.com/market/ws and uses\n * Binance's dynamic subscribe/unsubscribe protocol to add or remove streams\n * without reconnecting.\n *\n * Supports:\n * - Kline (candlestick) streams: <symbol>@kline_<interval>\n * - Mark price streams: <symbol>@markPrice@1s\n *\n * Usage is through a module-level singleton (no auth required for public streams).\n */\n\nconst BINANCE_WS_URL = 'wss://fstream.binance.com/market/ws';\nconst RECONNECT_DELAYS = [1000, 2000, 4000, 8000, 16000, 30000];\n\nexport interface BinanceWsKline {\n symbol: string;\n interval: string;\n openTime: number;\n closeTime: number;\n open: number;\n high: number;\n low: number;\n close: number;\n volume: number;\n isFinal: boolean;\n}\n\nexport type BinanceWsKlineCallback = (kline: BinanceWsKline) => void;\n\nexport interface BinanceWsMarkPrice {\n symbol: string;\n markPrice: number;\n indexPrice: number;\n time: number;\n}\n\nexport type BinanceWsMarkPriceCallback = (data: BinanceWsMarkPrice) => void;\nexport type BinanceWsAllMarkPricesCallback = (\n data: BinanceWsMarkPrice[],\n) => void;\n\ntype StreamCallback = (data: any) => void;\n\ninterface BinanceMarkPriceTicker {\n s: string;\n p: string;\n i: string;\n E: number;\n}\n\ninterface StreamSubscription {\n callbacks: Map<string, StreamCallback>;\n}\n\nconst STABLE_QUOTES = ['USDT0', 'USDT', 'USDC', 'USDE', 'USDH', 'USD'];\n\nfunction normalizeBaseSymbol(symbol: string): string {\n const normalized = symbol.toUpperCase().trim();\n\n for (const quote of STABLE_QUOTES) {\n if (normalized.endsWith(quote) && normalized.length > quote.length) {\n return normalized.slice(0, -quote.length);\n }\n }\n\n return normalized;\n}\n\nfunction isBinanceMarkPriceTicker(\n value: unknown,\n): value is BinanceMarkPriceTicker {\n return Boolean(\n value &&\n typeof value === 'object' &&\n typeof (value as { s?: unknown }).s === 'string' &&\n typeof (value as { p?: unknown }).p === 'string' &&\n typeof (value as { i?: unknown }).i === 'string' &&\n typeof (value as { E?: unknown }).E === 'number',\n );\n}\n\nfunction extractTickers(payload: unknown): BinanceMarkPriceTicker[] {\n if (Array.isArray(payload)) {\n return payload.filter(isBinanceMarkPriceTicker);\n }\n\n if (payload && typeof payload === 'object') {\n const maybeData = (payload as { data?: unknown }).data;\n if (Array.isArray(maybeData)) {\n return maybeData.filter(isBinanceMarkPriceTicker);\n }\n\n return isBinanceMarkPriceTicker(payload) ? [payload] : [];\n }\n\n return [];\n}\n\nfunction extractWsPayload(payload: unknown): unknown {\n if (\n payload &&\n typeof payload === 'object' &&\n 'data' in payload\n ) {\n return (payload as { data?: unknown }).data ?? payload;\n }\n\n return payload;\n}\n\nfunction isKlinePayload(\n payload: unknown,\n): payload is {\n e: 'kline';\n s: string;\n k: {\n i: string;\n t: number;\n T: number;\n o: string;\n h: string;\n l: string;\n c: string;\n v: string;\n x: boolean;\n };\n} {\n return Boolean(\n payload &&\n typeof payload === 'object' &&\n (payload as { e?: unknown }).e === 'kline' &&\n typeof (payload as { s?: unknown }).s === 'string' &&\n (payload as { k?: unknown }).k &&\n typeof (payload as { k: { i?: unknown } }).k.i === 'string',\n );\n}\n\nfunction isMarkPricePayload(\n payload: unknown,\n): payload is {\n e: 'markPriceUpdate';\n s: string;\n} {\n return Boolean(\n payload &&\n typeof payload === 'object' &&\n (payload as { e?: unknown }).e === 'markPriceUpdate' &&\n typeof (payload as { s?: unknown }).s === 'string',\n );\n}\n\nexport class BinanceWsManager {\n private ws: WebSocket | null = null;\n private streams: Map<string, StreamSubscription> = new Map();\n private reconnectAttempt = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private intentionalClose = false;\n private pendingSubscribes: string[] = [];\n private idCounter = 0;\n\n /**\n * Subscribe to a kline stream. Returns an unsubscribe function.\n */\n subscribeKline(\n symbol: string,\n interval: string,\n cb: BinanceWsKlineCallback,\n ): () => void {\n const streamName = `${symbol.toLowerCase()}@kline_${interval}`;\n const id = this.generateId();\n\n const wrappedCb: StreamCallback = (raw: any) => {\n const k = raw.k;\n if (!k) return;\n cb({\n symbol: raw.s,\n interval: k.i,\n openTime: k.t,\n closeTime: k.T,\n open: parseFloat(k.o),\n high: parseFloat(k.h),\n low: parseFloat(k.l),\n close: parseFloat(k.c),\n volume: parseFloat(k.v),\n isFinal: k.x,\n });\n };\n\n this.addStreamCallback(streamName, id, wrappedCb);\n return () => this.removeStreamCallback(streamName, id);\n }\n\n /**\n * Subscribe to a mark price stream (1s updates). Returns an unsubscribe function.\n */\n subscribeMarkPrice(\n symbol: string,\n cb: BinanceWsMarkPriceCallback,\n ): () => void {\n const streamName = `${symbol.toLowerCase()}@markPrice@1s`;\n const id = this.generateId();\n\n const wrappedCb: StreamCallback = (raw: any) => {\n cb({\n symbol: normalizeBaseSymbol(raw.s),\n markPrice: parseFloat(raw.p),\n indexPrice: parseFloat(raw.i),\n time: raw.E,\n });\n };\n\n this.addStreamCallback(streamName, id, wrappedCb);\n return () => this.removeStreamCallback(streamName, id);\n }\n\n /**\n * Subscribe to the all-market mark price stream (1s updates).\n * Returns an unsubscribe function.\n */\n subscribeAllMarkPrices(cb: BinanceWsAllMarkPricesCallback): () => void {\n const streamName = '!markPrice@arr@1s';\n const id = this.generateId();\n\n const wrappedCb: StreamCallback = (raw: any) => {\n cb(\n extractTickers(raw)\n .map((entry) => ({\n symbol: normalizeBaseSymbol(entry.s),\n markPrice: parseFloat(entry.p),\n indexPrice: parseFloat(entry.i),\n time: entry.E,\n })),\n );\n };\n\n this.addStreamCallback(streamName, id, wrappedCb);\n return () => this.removeStreamCallback(streamName, id);\n }\n\n /**\n * Destroy the manager and close the connection.\n */\n destroy(): void {\n this.intentionalClose = true;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n this.streams.clear();\n }\n\n // --- Private ---\n\n private generateId(): string {\n return `sub_${++this.idCounter}_${Date.now()}`;\n }\n\n private addStreamCallback(\n streamName: string,\n id: string,\n cb: StreamCallback,\n ): void {\n let sub = this.streams.get(streamName);\n const isNew = !sub;\n\n if (!sub) {\n sub = { callbacks: new Map() };\n this.streams.set(streamName, sub);\n }\n sub.callbacks.set(id, cb);\n\n if (isNew) {\n this.ensureConnected();\n this.sendSubscribe([streamName]);\n }\n }\n\n private removeStreamCallback(streamName: string, id: string): void {\n const sub = this.streams.get(streamName);\n if (!sub) return;\n\n sub.callbacks.delete(id);\n\n if (sub.callbacks.size === 0) {\n this.streams.delete(streamName);\n this.sendUnsubscribe([streamName]);\n\n // Close connection if no subscriptions left\n if (this.streams.size === 0 && this.ws) {\n this.intentionalClose = true;\n this.ws.close();\n this.ws = null;\n this.intentionalClose = false;\n }\n }\n }\n\n private ensureConnected(): void {\n if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {\n return;\n }\n this.connect();\n }\n\n private connect(): void {\n if (typeof WebSocket === 'undefined') return;\n\n this.intentionalClose = false;\n this.ws = new WebSocket(BINANCE_WS_URL);\n\n this.ws.onopen = () => {\n this.reconnectAttempt = 0;\n\n // Subscribe to all active streams on (re)connect\n const activeStreams = Array.from(this.streams.keys());\n if (activeStreams.length > 0) {\n this.sendSubscribe(activeStreams);\n }\n\n // Process any pending subscribes\n if (this.pendingSubscribes.length > 0) {\n this.sendSubscribe(this.pendingSubscribes);\n this.pendingSubscribes = [];\n }\n };\n\n this.ws.onmessage = (event: MessageEvent) => {\n try {\n const data = JSON.parse(event.data as string);\n this.handleMessage(data);\n } catch {\n // Ignore parse errors\n }\n };\n\n this.ws.onclose = () => {\n if (this.intentionalClose) return;\n this.scheduleReconnect();\n };\n\n this.ws.onerror = () => {\n // onclose will fire after onerror, reconnect handled there\n };\n }\n\n private handleMessage(data: any): void {\n // Binance sends kline events with { e: 'kline', s: 'BTCUSDT', k: {...} }\n // and mark price events with { e: 'markPriceUpdate', s: 'BTCUSDT', p: '...', ... }\n // or all-market mark price arrays for !markPrice@arr@1s.\n // Determine the stream name from the event type.\n\n const payload = extractWsPayload(data);\n\n if (Array.isArray(payload)) {\n this.dispatchToStream('!markPrice@arr@1s', payload);\n return;\n }\n\n if (isKlinePayload(payload)) {\n const k = payload.k;\n const streamName = `${payload.s.toLowerCase()}@kline_${k.i}`;\n this.dispatchToStream(streamName, payload);\n } else if (isMarkPricePayload(payload)) {\n const streamName = `${payload.s.toLowerCase()}@markPrice@1s`;\n this.dispatchToStream(streamName, payload);\n }\n // Ignore subscription confirmations and other system messages\n }\n\n private dispatchToStream(streamName: string, data: any): void {\n const sub = this.streams.get(streamName);\n if (!sub) return;\n\n sub.callbacks.forEach((cb) => {\n try {\n cb(data);\n } catch {\n // Ignore callback errors\n }\n });\n }\n\n private sendSubscribe(streams: string[]): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n this.pendingSubscribes.push(...streams);\n return;\n }\n\n this.ws.send(JSON.stringify({\n method: 'SUBSCRIBE',\n params: streams,\n id: Date.now(),\n }));\n }\n\n private sendUnsubscribe(streams: string[]): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n // Remove from pending if not yet sent\n this.pendingSubscribes = this.pendingSubscribes.filter(\n (s) => !streams.includes(s),\n );\n return;\n }\n\n this.ws.send(JSON.stringify({\n method: 'UNSUBSCRIBE',\n params: streams,\n id: Date.now(),\n }));\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return;\n if (this.streams.size === 0) return; // No need to reconnect if no subscriptions\n\n const delay = RECONNECT_DELAYS[\n Math.min(this.reconnectAttempt, RECONNECT_DELAYS.length - 1)\n ];\n this.reconnectAttempt++;\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connect();\n }, delay);\n }\n}\n\n// Module-level singleton — Binance public WS requires no authentication\nlet _instance: BinanceWsManager | null = null;\n\nexport function getBinanceWsManager(): BinanceWsManager {\n if (!_instance) {\n _instance = new BinanceWsManager();\n }\n return _instance;\n}\n","import { create } from 'zustand';\nimport { getBinanceWsManager } from '../../utils/binance-ws';\n\ntype MarkPrices = Record<string, number>;\n\ntype BinanceMarkPriceState = {\n markPrices: MarkPrices;\n subscribeSymbol: (symmSymbol: string, binanceSymbol: string) => void;\n unsubscribeSymbol: (symmSymbol: string, binanceSymbol: string) => void;\n};\n\nconst refCounts = new Map<string, number>();\nconst streamSymbols = new Map<string, Set<string>>();\nlet allMarkPricesRefCount = 0;\nlet allMarkPricesUnsubscribe: (() => void) | null = null;\n\nconst STABLE_QUOTES = ['USDT0', 'USDT', 'USDC', 'USDE', 'USDH', 'USD'];\n\nfunction normalizeBinanceSymbol(symbol: string): string {\n const normalized = symbol.toUpperCase().trim();\n\n for (const quote of STABLE_QUOTES) {\n if (normalized.endsWith(quote) && normalized.length > quote.length) {\n return normalized.slice(0, -quote.length);\n }\n }\n\n return normalized;\n}\n\nfunction getNextRefCount(binanceSymbol: string): number {\n return (refCounts.get(binanceSymbol) ?? 0) + 1;\n}\n\nfunction getPrevRefCount(binanceSymbol: string): number {\n return Math.max(0, (refCounts.get(binanceSymbol) ?? 0) - 1);\n}\n\nexport const useBinanceMarkPriceStore = create<BinanceMarkPriceState>((set) => ({\n markPrices: {},\n\n subscribeSymbol: (symmSymbol, rawBinanceSymbol) => {\n const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);\n const nextRefCount = getNextRefCount(binanceSymbol);\n refCounts.set(binanceSymbol, nextRefCount);\n\n const symbols = streamSymbols.get(binanceSymbol) ?? new Set<string>();\n symbols.add(symmSymbol);\n streamSymbols.set(binanceSymbol, symbols);\n\n if (allMarkPricesRefCount === 0) {\n const wsManager = getBinanceWsManager();\n allMarkPricesUnsubscribe = wsManager.subscribeAllMarkPrices((entries) => {\n set((state) => {\n let nextMarkPrices: MarkPrices | null = null;\n\n entries.forEach((entry) => {\n const canonicalSymbol = normalizeBinanceSymbol(entry.symbol);\n const mappedSymbols = streamSymbols.get(canonicalSymbol);\n if (!mappedSymbols || mappedSymbols.size === 0) return;\n\n nextMarkPrices ??= { ...state.markPrices };\n mappedSymbols.forEach((mappedSymbol) => {\n nextMarkPrices![mappedSymbol] = entry.markPrice;\n });\n });\n\n return nextMarkPrices ? { markPrices: nextMarkPrices } : state;\n });\n });\n }\n\n allMarkPricesRefCount += 1;\n },\n\n unsubscribeSymbol: (symmSymbol, rawBinanceSymbol) => {\n const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);\n\n const symbols = streamSymbols.get(binanceSymbol);\n if (symbols) {\n symbols.delete(symmSymbol);\n if (symbols.size === 0) {\n streamSymbols.delete(binanceSymbol);\n } else {\n streamSymbols.set(binanceSymbol, symbols);\n }\n }\n\n const nextRefCount = getPrevRefCount(binanceSymbol);\n if (nextRefCount === 0) {\n refCounts.delete(binanceSymbol);\n } else {\n refCounts.set(binanceSymbol, nextRefCount);\n }\n\n allMarkPricesRefCount = Math.max(0, allMarkPricesRefCount - 1);\n if (allMarkPricesRefCount === 0) {\n allMarkPricesUnsubscribe?.();\n allMarkPricesUnsubscribe = null;\n }\n\n set((state) => {\n if (state.markPrices[symmSymbol] == null) return state;\n const nextMarkPrices = { ...state.markPrices };\n delete nextMarkPrices[symmSymbol];\n return { markPrices: nextMarkPrices };\n });\n },\n}));\n","import { useEffect } from \"react\";\nimport type { SymmSDK } from \"@pear-protocol/symm-core\";\nimport { resolveBinanceSymbol } from \"../../utils/binance-symbol-map\";\nimport { useBinanceMarkPriceStore } from \"../stores/use-binance-mark-price-store\";\n\n/**\n * Use case: Establish Binance mark-price WS subscriptions at provider level.\n * Subscribes to all SYMM hedger symbols for the active chain and feeds the shared store.\n */\nexport function useBinanceWs(params: {\n symmCoreClient: SymmSDK | null;\n chainId: number;\n}) {\n const { symmCoreClient, chainId } = params;\n const subscribeSymbol = useBinanceMarkPriceStore((state) => state.subscribeSymbol);\n const unsubscribeSymbol = useBinanceMarkPriceStore((state) => state.unsubscribeSymbol);\n\n useEffect(() => {\n if (!symmCoreClient) {\n return;\n }\n\n let cancelled = false;\n let subscribedPairs: Array<readonly [string, string]> = [];\n\n const run = async () => {\n try {\n const result = await symmCoreClient.markets.listSymmHedger({ chainId });\n if (cancelled) {\n return;\n }\n\n const uniqueSymbols = Array.from(\n new Set(\n result.markets\n .map((market) => market.symbol)\n .filter((symbol): symbol is string => !!symbol)\n )\n );\n\n subscribedPairs = uniqueSymbols\n .map((symbol) => {\n const binanceSymbol = resolveBinanceSymbol(symbol).binanceSymbol;\n if (!binanceSymbol) return null;\n return [symbol, binanceSymbol] as const;\n })\n .filter((entry): entry is readonly [string, string] => !!entry);\n\n subscribedPairs.forEach(([symbol, binanceSymbol]) => {\n subscribeSymbol(symbol, binanceSymbol);\n });\n } catch {\n // best-effort subscription bootstrap\n }\n };\n\n void run();\n\n return () => {\n cancelled = true;\n subscribedPairs.forEach(([symbol, binanceSymbol]) => {\n unsubscribeSymbol(symbol, binanceSymbol);\n });\n };\n }, [symmCoreClient, chainId, subscribeSymbol, unsubscribeSymbol]);\n}\n","import { useMemo } from \"react\";\nimport type { Address } from \"viem\";\nimport { createSymmSDK, type SymmSDK } from \"@pear-protocol/symm-core\";\n\nimport type { SymmioSDKConfig } from \"../types/common\";\nimport { SymmContext, type SymmContextValue } from \"./context\";\nimport { useBinanceWs } from \"./hooks/use-binance-ws\";\n\nexport type SymmProviderProps = {\n chainId?: number;\n address?: Address;\n symmCoreConfig: {\n apiUrl: string;\n wsUrl?: string;\n };\n symmioConfig?: Partial<SymmioSDKConfig>;\n children: React.ReactNode;\n};\n\nexport function SymmProvider({\n chainId = 42161,\n address,\n symmCoreConfig = {\n apiUrl: \"https://nginx-server-staging.up.railway.app\",\n wsUrl: \"wss://nginx-server-staging.up.railway.app\",\n },\n symmioConfig,\n children,\n}: SymmProviderProps) {\n const symmCoreClient = useMemo((): SymmSDK => {\n return createSymmSDK({\n apiUrl: symmCoreConfig.apiUrl,\n wsUrl: symmCoreConfig.wsUrl,\n defaultChainId: chainId,\n });\n }, [chainId, symmCoreConfig.apiUrl, symmCoreConfig.wsUrl]);\n\n const value = useMemo<SymmContextValue>(\n () => ({\n symmCoreClient,\n chainId,\n address,\n symmioConfig,\n }),\n [symmCoreClient, chainId, address, symmioConfig]\n );\n\n useBinanceWs({\n symmCoreClient,\n chainId,\n });\n\n return <SymmContext.Provider value={value}>{children}</SymmContext.Provider>;\n}\n"]}
|