@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/index.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var symmCore = require('@pear-protocol/symm-core');
|
|
6
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
7
6
|
var zustand = require('zustand');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
8
|
var reactQuery = require('@tanstack/react-query');
|
|
9
9
|
var viem = require('viem');
|
|
10
10
|
|
|
@@ -16,348 +16,796 @@ function useSymmContext() {
|
|
|
16
16
|
}
|
|
17
17
|
return ctx;
|
|
18
18
|
}
|
|
19
|
-
function SymmProvider({
|
|
20
|
-
chainId = 42161,
|
|
21
|
-
address,
|
|
22
|
-
symmCoreConfig = {
|
|
23
|
-
apiUrl: "https://nginx-server-staging.up.railway.app",
|
|
24
|
-
wsUrl: "wss://nginx-server-staging.up.railway.app"
|
|
25
|
-
},
|
|
26
|
-
symmioConfig,
|
|
27
|
-
children
|
|
28
|
-
}) {
|
|
29
|
-
const symmCoreClient = react.useMemo(() => {
|
|
30
|
-
return symmCore.createSymmSDK({
|
|
31
|
-
apiUrl: symmCoreConfig.apiUrl,
|
|
32
|
-
wsUrl: symmCoreConfig.wsUrl,
|
|
33
|
-
defaultChainId: chainId
|
|
34
|
-
});
|
|
35
|
-
}, [chainId, symmCoreConfig.apiUrl, symmCoreConfig.wsUrl]);
|
|
36
|
-
const value = react.useMemo(
|
|
37
|
-
() => ({
|
|
38
|
-
symmCoreClient,
|
|
39
|
-
chainId,
|
|
40
|
-
address,
|
|
41
|
-
symmioConfig
|
|
42
|
-
}),
|
|
43
|
-
[symmCoreClient, chainId, address, symmioConfig]
|
|
44
|
-
);
|
|
45
|
-
return /* @__PURE__ */ jsxRuntime.jsx(SymmContext.Provider, { value, children });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// src/react/hooks/use-symm-core-client.ts
|
|
49
|
-
function useSymmCoreClient() {
|
|
50
|
-
return useSymmContext().symmCoreClient;
|
|
51
|
-
}
|
|
52
19
|
|
|
53
|
-
// src/
|
|
54
|
-
var
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"https://muon-oracle2.rasa.capital/v1/",
|
|
58
|
-
"https://muon-oracle3.rasa.capital/v1/",
|
|
59
|
-
"https://muon-oracle4.rasa.capital/v1/"
|
|
60
|
-
];
|
|
61
|
-
var MUON_APP_NAME = "symmio";
|
|
62
|
-
var MUON_REQUEST_TIMEOUT = 15e3;
|
|
63
|
-
var HEDGER_BASE_URLS = {
|
|
64
|
-
[42161 /* ARBITRUM */]: "https://www.perps-streaming.com/v1/42161a/0x6273242a7E88b3De90822b31648C212215caaFE4",
|
|
65
|
-
[8453 /* BASE */]: "https://www.perps-streaming.com/v1/8453a/0x6273242a7E88b3De90822b31648C212215caaFE4"
|
|
20
|
+
// src/utils/binance-symbol-map.ts
|
|
21
|
+
var SYMBOL_OVERRIDES = {
|
|
22
|
+
// Add overrides here as needed for SYMM markets that don't map 1:1 to Binance
|
|
23
|
+
// e.g., 'SOME_SYMM_SYMBOL': 'BINANCE_SYMBOL',
|
|
66
24
|
};
|
|
67
|
-
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (!
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const expirationTime = new Date(
|
|
80
|
-
Date.now() + 30 * 24 * 60 * 60 * 1e3
|
|
81
|
-
).toISOString();
|
|
82
|
-
const message = [
|
|
83
|
-
`${params.domain} wants you to sign in with your Ethereum account:`,
|
|
84
|
-
params.address,
|
|
85
|
-
"",
|
|
86
|
-
params.statement,
|
|
87
|
-
"",
|
|
88
|
-
`URI: ${params.uri}`,
|
|
89
|
-
`Version: ${version}`,
|
|
90
|
-
`Chain ID: ${params.chainId}`,
|
|
91
|
-
`Nonce: ${params.nonce}`,
|
|
92
|
-
`Issued At: ${issuedAt}`,
|
|
93
|
-
`Expiration Time: ${expirationTime}`
|
|
94
|
-
].join("\n");
|
|
95
|
-
return { message, issuedAt, expirationTime };
|
|
96
|
-
}
|
|
97
|
-
async function getNonce(chainId, subAccount) {
|
|
98
|
-
const hedgerBaseUrl = getHedgerBaseUrl(chainId);
|
|
99
|
-
const url = new URL(`nonce/${subAccount}`, hedgerBaseUrl).href;
|
|
100
|
-
const response = await fetch(url);
|
|
101
|
-
if (!response.ok) {
|
|
102
|
-
throw new Error(`Failed to fetch nonce: ${response.statusText}`);
|
|
25
|
+
var UNSUPPORTED_SYMBOLS = /* @__PURE__ */ new Set([
|
|
26
|
+
// Add symbols here that have no Binance equivalent
|
|
27
|
+
]);
|
|
28
|
+
function resolveBinanceSymbol(symmSymbol) {
|
|
29
|
+
if (!symmSymbol || !symmSymbol.trim()) {
|
|
30
|
+
return {
|
|
31
|
+
symmSymbol,
|
|
32
|
+
normalizedSymbol: "",
|
|
33
|
+
binanceSymbol: null,
|
|
34
|
+
supported: false,
|
|
35
|
+
reason: "missing_symbol"
|
|
36
|
+
};
|
|
103
37
|
}
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
issued_at: params.issuedAt,
|
|
114
|
-
signature: params.signature,
|
|
115
|
-
nonce: params.nonce
|
|
116
|
-
};
|
|
117
|
-
const response = await fetch(url, {
|
|
118
|
-
method: "POST",
|
|
119
|
-
headers: { "Content-Type": "application/json" },
|
|
120
|
-
body: JSON.stringify(body)
|
|
121
|
-
});
|
|
122
|
-
if (!response.ok) {
|
|
123
|
-
const errorData = await response.json().catch(() => null);
|
|
124
|
-
throw new Error(
|
|
125
|
-
errorData?.error_message ?? `Login failed: ${response.statusText}`
|
|
126
|
-
);
|
|
38
|
+
const normalized = symmSymbol.toUpperCase().trim();
|
|
39
|
+
if (!/^[A-Z0-9]+$/.test(normalized)) {
|
|
40
|
+
return {
|
|
41
|
+
symmSymbol,
|
|
42
|
+
normalizedSymbol: normalized,
|
|
43
|
+
binanceSymbol: null,
|
|
44
|
+
supported: false,
|
|
45
|
+
reason: "invalid_symbol"
|
|
46
|
+
};
|
|
127
47
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
48
|
+
if (UNSUPPORTED_SYMBOLS.has(normalized)) {
|
|
49
|
+
return {
|
|
50
|
+
symmSymbol,
|
|
51
|
+
normalizedSymbol: normalized,
|
|
52
|
+
binanceSymbol: null,
|
|
53
|
+
supported: false,
|
|
54
|
+
reason: "unsupported_symbol"
|
|
55
|
+
};
|
|
131
56
|
}
|
|
57
|
+
const binanceSymbol = SYMBOL_OVERRIDES[normalized] ?? (normalized.endsWith("USDT") ? normalized : `${normalized}USDT`);
|
|
132
58
|
return {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
59
|
+
symmSymbol,
|
|
60
|
+
normalizedSymbol: normalized,
|
|
61
|
+
binanceSymbol,
|
|
62
|
+
supported: true,
|
|
63
|
+
reason: null
|
|
136
64
|
};
|
|
137
65
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
var TOKEN_STORAGE_PREFIX = "symm_access_token";
|
|
141
|
-
var TOKEN_STORAGE_VERSION = 1;
|
|
142
|
-
var tokenCache = /* @__PURE__ */ new Map();
|
|
143
|
-
function cacheKey(address, chainId) {
|
|
144
|
-
return `${address}:${chainId}`;
|
|
66
|
+
function getUnsupportedBinanceSymbols(symbols) {
|
|
67
|
+
return symbols.filter((symbol) => !resolveBinanceSymbol(symbol).supported);
|
|
145
68
|
}
|
|
146
|
-
function
|
|
147
|
-
return
|
|
69
|
+
function toBinanceSymbol(symmSymbol) {
|
|
70
|
+
return resolveBinanceSymbol(symmSymbol).binanceSymbol;
|
|
148
71
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (Date.now() >= parsed.expiresAt) {
|
|
160
|
-
window.localStorage.removeItem(storageKey(address, chainId));
|
|
161
|
-
return null;
|
|
72
|
+
|
|
73
|
+
// src/utils/binance-ws.ts
|
|
74
|
+
var BINANCE_WS_URL = "wss://fstream.binance.com/market/ws";
|
|
75
|
+
var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
|
|
76
|
+
var STABLE_QUOTES = ["USDT0", "USDT", "USDC", "USDE", "USDH", "USD"];
|
|
77
|
+
function normalizeBaseSymbol(symbol) {
|
|
78
|
+
const normalized = symbol.toUpperCase().trim();
|
|
79
|
+
for (const quote of STABLE_QUOTES) {
|
|
80
|
+
if (normalized.endsWith(quote) && normalized.length > quote.length) {
|
|
81
|
+
return normalized.slice(0, -quote.length);
|
|
162
82
|
}
|
|
163
|
-
return parsed;
|
|
164
|
-
} catch {
|
|
165
|
-
window.localStorage.removeItem(storageKey(address, chainId));
|
|
166
|
-
return null;
|
|
167
83
|
}
|
|
84
|
+
return normalized;
|
|
168
85
|
}
|
|
169
|
-
function
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
storageKey(address, chainId),
|
|
174
|
-
JSON.stringify(cached)
|
|
175
|
-
);
|
|
176
|
-
} catch {
|
|
177
|
-
}
|
|
86
|
+
function isBinanceMarkPriceTicker(value) {
|
|
87
|
+
return Boolean(
|
|
88
|
+
value && typeof value === "object" && typeof value.s === "string" && typeof value.p === "string" && typeof value.i === "string" && typeof value.E === "number"
|
|
89
|
+
);
|
|
178
90
|
}
|
|
179
|
-
function
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return cached.token;
|
|
91
|
+
function extractTickers(payload) {
|
|
92
|
+
if (Array.isArray(payload)) {
|
|
93
|
+
return payload.filter(isBinanceMarkPriceTicker);
|
|
183
94
|
}
|
|
184
|
-
if (
|
|
185
|
-
|
|
95
|
+
if (payload && typeof payload === "object") {
|
|
96
|
+
const maybeData = payload.data;
|
|
97
|
+
if (Array.isArray(maybeData)) {
|
|
98
|
+
return maybeData.filter(isBinanceMarkPriceTicker);
|
|
99
|
+
}
|
|
100
|
+
return isBinanceMarkPriceTicker(payload) ? [payload] : [];
|
|
186
101
|
}
|
|
187
|
-
|
|
188
|
-
if (!stored) return null;
|
|
189
|
-
tokenCache.set(cacheKey(address, chainId), stored);
|
|
190
|
-
return stored.token;
|
|
102
|
+
return [];
|
|
191
103
|
}
|
|
192
|
-
function
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
window.localStorage.removeItem(storageKey(address, chainId));
|
|
104
|
+
function extractWsPayload(payload) {
|
|
105
|
+
if (payload && typeof payload === "object" && "data" in payload) {
|
|
106
|
+
return payload.data ?? payload;
|
|
196
107
|
}
|
|
108
|
+
return payload;
|
|
197
109
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const { message, issuedAt, expirationTime } = createSiweMessage({
|
|
203
|
-
address: signerAddress,
|
|
204
|
-
statement: "Sign in to Symm Protocol",
|
|
205
|
-
chainId,
|
|
206
|
-
nonce,
|
|
207
|
-
domain: resolvedDomain,
|
|
208
|
-
uri
|
|
209
|
-
});
|
|
210
|
-
const signature = await walletClient.signMessage({
|
|
211
|
-
account: signerAddress,
|
|
212
|
-
message
|
|
213
|
-
});
|
|
214
|
-
const { accessToken } = await login(chainId, {
|
|
215
|
-
accountAddress,
|
|
216
|
-
signature,
|
|
217
|
-
expirationTime,
|
|
218
|
-
issuedAt,
|
|
219
|
-
nonce
|
|
220
|
-
});
|
|
221
|
-
const expiresAt = new Date(expirationTime).getTime();
|
|
222
|
-
const cachedToken = {
|
|
223
|
-
token: accessToken,
|
|
224
|
-
expiresAt: expiresAt - 6e4
|
|
225
|
-
};
|
|
226
|
-
tokenCache.set(cacheKey(accountAddress, chainId), cachedToken);
|
|
227
|
-
writeStoredToken(accountAddress, chainId, cachedToken);
|
|
228
|
-
return accessToken;
|
|
110
|
+
function isKlinePayload(payload) {
|
|
111
|
+
return Boolean(
|
|
112
|
+
payload && typeof payload === "object" && payload.e === "kline" && typeof payload.s === "string" && payload.k && typeof payload.k.i === "string"
|
|
113
|
+
);
|
|
229
114
|
}
|
|
230
|
-
function
|
|
231
|
-
return
|
|
115
|
+
function isMarkPricePayload(payload) {
|
|
116
|
+
return Boolean(
|
|
117
|
+
payload && typeof payload === "object" && payload.e === "markPriceUpdate" && typeof payload.s === "string"
|
|
118
|
+
);
|
|
232
119
|
}
|
|
233
|
-
var
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
120
|
+
var BinanceWsManager = class {
|
|
121
|
+
ws = null;
|
|
122
|
+
streams = /* @__PURE__ */ new Map();
|
|
123
|
+
reconnectAttempt = 0;
|
|
124
|
+
reconnectTimer = null;
|
|
125
|
+
intentionalClose = false;
|
|
126
|
+
pendingSubscribes = [];
|
|
127
|
+
idCounter = 0;
|
|
128
|
+
/**
|
|
129
|
+
* Subscribe to a kline stream. Returns an unsubscribe function.
|
|
130
|
+
*/
|
|
131
|
+
subscribeKline(symbol, interval, cb) {
|
|
132
|
+
const streamName = `${symbol.toLowerCase()}@kline_${interval}`;
|
|
133
|
+
const id = this.generateId();
|
|
134
|
+
const wrappedCb = (raw) => {
|
|
135
|
+
const k = raw.k;
|
|
136
|
+
if (!k) return;
|
|
137
|
+
cb({
|
|
138
|
+
symbol: raw.s,
|
|
139
|
+
interval: k.i,
|
|
140
|
+
openTime: k.t,
|
|
141
|
+
closeTime: k.T,
|
|
142
|
+
open: parseFloat(k.o),
|
|
143
|
+
high: parseFloat(k.h),
|
|
144
|
+
low: parseFloat(k.l),
|
|
145
|
+
close: parseFloat(k.c),
|
|
146
|
+
volume: parseFloat(k.v),
|
|
147
|
+
isFinal: k.x
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
this.addStreamCallback(streamName, id, wrappedCb);
|
|
151
|
+
return () => this.removeStreamCallback(streamName, id);
|
|
259
152
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Subscribe to a mark price stream (1s updates). Returns an unsubscribe function.
|
|
155
|
+
*/
|
|
156
|
+
subscribeMarkPrice(symbol, cb) {
|
|
157
|
+
const streamName = `${symbol.toLowerCase()}@markPrice@1s`;
|
|
158
|
+
const id = this.generateId();
|
|
159
|
+
const wrappedCb = (raw) => {
|
|
160
|
+
cb({
|
|
161
|
+
symbol: normalizeBaseSymbol(raw.s),
|
|
162
|
+
markPrice: parseFloat(raw.p),
|
|
163
|
+
indexPrice: parseFloat(raw.i),
|
|
164
|
+
time: raw.E
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
this.addStreamCallback(streamName, id, wrappedCb);
|
|
168
|
+
return () => this.removeStreamCallback(streamName, id);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Subscribe to the all-market mark price stream (1s updates).
|
|
172
|
+
* Returns an unsubscribe function.
|
|
173
|
+
*/
|
|
174
|
+
subscribeAllMarkPrices(cb) {
|
|
175
|
+
const streamName = "!markPrice@arr@1s";
|
|
176
|
+
const id = this.generateId();
|
|
177
|
+
const wrappedCb = (raw) => {
|
|
178
|
+
cb(
|
|
179
|
+
extractTickers(raw).map((entry) => ({
|
|
180
|
+
symbol: normalizeBaseSymbol(entry.s),
|
|
181
|
+
markPrice: parseFloat(entry.p),
|
|
182
|
+
indexPrice: parseFloat(entry.i),
|
|
183
|
+
time: entry.E
|
|
184
|
+
}))
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
this.addStreamCallback(streamName, id, wrappedCb);
|
|
188
|
+
return () => this.removeStreamCallback(streamName, id);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Destroy the manager and close the connection.
|
|
192
|
+
*/
|
|
193
|
+
destroy() {
|
|
194
|
+
this.intentionalClose = true;
|
|
195
|
+
if (this.reconnectTimer) {
|
|
196
|
+
clearTimeout(this.reconnectTimer);
|
|
197
|
+
this.reconnectTimer = null;
|
|
272
198
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
199
|
+
if (this.ws) {
|
|
200
|
+
this.ws.close();
|
|
201
|
+
this.ws = null;
|
|
202
|
+
}
|
|
203
|
+
this.streams.clear();
|
|
204
|
+
}
|
|
205
|
+
// --- Private ---
|
|
206
|
+
generateId() {
|
|
207
|
+
return `sub_${++this.idCounter}_${Date.now()}`;
|
|
208
|
+
}
|
|
209
|
+
addStreamCallback(streamName, id, cb) {
|
|
210
|
+
let sub = this.streams.get(streamName);
|
|
211
|
+
const isNew = !sub;
|
|
212
|
+
if (!sub) {
|
|
213
|
+
sub = { callbacks: /* @__PURE__ */ new Map() };
|
|
214
|
+
this.streams.set(streamName, sub);
|
|
215
|
+
}
|
|
216
|
+
sub.callbacks.set(id, cb);
|
|
217
|
+
if (isNew) {
|
|
218
|
+
this.ensureConnected();
|
|
219
|
+
this.sendSubscribe([streamName]);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
removeStreamCallback(streamName, id) {
|
|
223
|
+
const sub = this.streams.get(streamName);
|
|
224
|
+
if (!sub) return;
|
|
225
|
+
sub.callbacks.delete(id);
|
|
226
|
+
if (sub.callbacks.size === 0) {
|
|
227
|
+
this.streams.delete(streamName);
|
|
228
|
+
this.sendUnsubscribe([streamName]);
|
|
229
|
+
if (this.streams.size === 0 && this.ws) {
|
|
230
|
+
this.intentionalClose = true;
|
|
231
|
+
this.ws.close();
|
|
232
|
+
this.ws = null;
|
|
233
|
+
this.intentionalClose = false;
|
|
287
234
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
ensureConnected() {
|
|
238
|
+
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
this.connect();
|
|
242
|
+
}
|
|
243
|
+
connect() {
|
|
244
|
+
if (typeof WebSocket === "undefined") return;
|
|
245
|
+
this.intentionalClose = false;
|
|
246
|
+
this.ws = new WebSocket(BINANCE_WS_URL);
|
|
247
|
+
this.ws.onopen = () => {
|
|
248
|
+
this.reconnectAttempt = 0;
|
|
249
|
+
const activeStreams = Array.from(this.streams.keys());
|
|
250
|
+
if (activeStreams.length > 0) {
|
|
251
|
+
this.sendSubscribe(activeStreams);
|
|
252
|
+
}
|
|
253
|
+
if (this.pendingSubscribes.length > 0) {
|
|
254
|
+
this.sendSubscribe(this.pendingSubscribes);
|
|
255
|
+
this.pendingSubscribes = [];
|
|
292
256
|
}
|
|
257
|
+
};
|
|
258
|
+
this.ws.onmessage = (event) => {
|
|
293
259
|
try {
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
address,
|
|
297
|
-
resolvedAccountAddress,
|
|
298
|
-
chainId,
|
|
299
|
-
siweDomain
|
|
300
|
-
);
|
|
301
|
-
setToken(resolvedAccountAddress, chainId, token);
|
|
302
|
-
return token;
|
|
260
|
+
const data = JSON.parse(event.data);
|
|
261
|
+
this.handleMessage(data);
|
|
303
262
|
} catch {
|
|
304
|
-
clearToken(resolvedAccountAddress, chainId);
|
|
305
|
-
return null;
|
|
306
263
|
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const addressChanged = previousAddress !== address;
|
|
320
|
-
const chainChanged = previousChainId !== chainId;
|
|
321
|
-
previousAddressRef.current = address;
|
|
322
|
-
previousChainIdRef.current = chainId;
|
|
323
|
-
if (!address || !walletClient) {
|
|
264
|
+
};
|
|
265
|
+
this.ws.onclose = () => {
|
|
266
|
+
if (this.intentionalClose) return;
|
|
267
|
+
this.scheduleReconnect();
|
|
268
|
+
};
|
|
269
|
+
this.ws.onerror = () => {
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
handleMessage(data) {
|
|
273
|
+
const payload = extractWsPayload(data);
|
|
274
|
+
if (Array.isArray(payload)) {
|
|
275
|
+
this.dispatchToStream("!markPrice@arr@1s", payload);
|
|
324
276
|
return;
|
|
325
277
|
}
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
|
|
278
|
+
if (isKlinePayload(payload)) {
|
|
279
|
+
const k = payload.k;
|
|
280
|
+
const streamName = `${payload.s.toLowerCase()}@kline_${k.i}`;
|
|
281
|
+
this.dispatchToStream(streamName, payload);
|
|
282
|
+
} else if (isMarkPricePayload(payload)) {
|
|
283
|
+
const streamName = `${payload.s.toLowerCase()}@markPrice@1s`;
|
|
284
|
+
this.dispatchToStream(streamName, payload);
|
|
329
285
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
286
|
+
}
|
|
287
|
+
dispatchToStream(streamName, data) {
|
|
288
|
+
const sub = this.streams.get(streamName);
|
|
289
|
+
if (!sub) return;
|
|
290
|
+
sub.callbacks.forEach((cb) => {
|
|
291
|
+
try {
|
|
292
|
+
cb(data);
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
sendSubscribe(streams) {
|
|
298
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
299
|
+
this.pendingSubscribes.push(...streams);
|
|
300
|
+
return;
|
|
335
301
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
302
|
+
this.ws.send(JSON.stringify({
|
|
303
|
+
method: "SUBSCRIBE",
|
|
304
|
+
params: streams,
|
|
305
|
+
id: Date.now()
|
|
306
|
+
}));
|
|
307
|
+
}
|
|
308
|
+
sendUnsubscribe(streams) {
|
|
309
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
310
|
+
this.pendingSubscribes = this.pendingSubscribes.filter(
|
|
311
|
+
(s) => !streams.includes(s)
|
|
312
|
+
);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
this.ws.send(JSON.stringify({
|
|
316
|
+
method: "UNSUBSCRIBE",
|
|
317
|
+
params: streams,
|
|
318
|
+
id: Date.now()
|
|
319
|
+
}));
|
|
320
|
+
}
|
|
321
|
+
scheduleReconnect() {
|
|
322
|
+
if (this.reconnectTimer) return;
|
|
323
|
+
if (this.streams.size === 0) return;
|
|
324
|
+
const delay = RECONNECT_DELAYS[Math.min(this.reconnectAttempt, RECONNECT_DELAYS.length - 1)];
|
|
325
|
+
this.reconnectAttempt++;
|
|
326
|
+
this.reconnectTimer = setTimeout(() => {
|
|
327
|
+
this.reconnectTimer = null;
|
|
328
|
+
this.connect();
|
|
329
|
+
}, delay);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
var _instance = null;
|
|
333
|
+
function getBinanceWsManager() {
|
|
334
|
+
if (!_instance) {
|
|
335
|
+
_instance = new BinanceWsManager();
|
|
336
|
+
}
|
|
337
|
+
return _instance;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/react/stores/use-binance-mark-price-store.ts
|
|
341
|
+
var refCounts = /* @__PURE__ */ new Map();
|
|
342
|
+
var streamSymbols = /* @__PURE__ */ new Map();
|
|
343
|
+
var allMarkPricesRefCount = 0;
|
|
344
|
+
var allMarkPricesUnsubscribe = null;
|
|
345
|
+
var STABLE_QUOTES2 = ["USDT0", "USDT", "USDC", "USDE", "USDH", "USD"];
|
|
346
|
+
function normalizeBinanceSymbol(symbol) {
|
|
347
|
+
const normalized = symbol.toUpperCase().trim();
|
|
348
|
+
for (const quote of STABLE_QUOTES2) {
|
|
349
|
+
if (normalized.endsWith(quote) && normalized.length > quote.length) {
|
|
350
|
+
return normalized.slice(0, -quote.length);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return normalized;
|
|
354
|
+
}
|
|
355
|
+
function getNextRefCount(binanceSymbol) {
|
|
356
|
+
return (refCounts.get(binanceSymbol) ?? 0) + 1;
|
|
357
|
+
}
|
|
358
|
+
function getPrevRefCount(binanceSymbol) {
|
|
359
|
+
return Math.max(0, (refCounts.get(binanceSymbol) ?? 0) - 1);
|
|
360
|
+
}
|
|
361
|
+
var useBinanceMarkPriceStore = zustand.create((set) => ({
|
|
362
|
+
markPrices: {},
|
|
363
|
+
subscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
|
|
364
|
+
const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
|
|
365
|
+
const nextRefCount = getNextRefCount(binanceSymbol);
|
|
366
|
+
refCounts.set(binanceSymbol, nextRefCount);
|
|
367
|
+
const symbols = streamSymbols.get(binanceSymbol) ?? /* @__PURE__ */ new Set();
|
|
368
|
+
symbols.add(symmSymbol);
|
|
369
|
+
streamSymbols.set(binanceSymbol, symbols);
|
|
370
|
+
if (allMarkPricesRefCount === 0) {
|
|
371
|
+
const wsManager = getBinanceWsManager();
|
|
372
|
+
allMarkPricesUnsubscribe = wsManager.subscribeAllMarkPrices((entries) => {
|
|
373
|
+
set((state) => {
|
|
374
|
+
let nextMarkPrices = null;
|
|
375
|
+
entries.forEach((entry) => {
|
|
376
|
+
const canonicalSymbol = normalizeBinanceSymbol(entry.symbol);
|
|
377
|
+
const mappedSymbols = streamSymbols.get(canonicalSymbol);
|
|
378
|
+
if (!mappedSymbols || mappedSymbols.size === 0) return;
|
|
379
|
+
nextMarkPrices ??= { ...state.markPrices };
|
|
380
|
+
mappedSymbols.forEach((mappedSymbol) => {
|
|
381
|
+
nextMarkPrices[mappedSymbol] = entry.markPrice;
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
return nextMarkPrices ? { markPrices: nextMarkPrices } : state;
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
allMarkPricesRefCount += 1;
|
|
389
|
+
},
|
|
390
|
+
unsubscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
|
|
391
|
+
const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
|
|
392
|
+
const symbols = streamSymbols.get(binanceSymbol);
|
|
393
|
+
if (symbols) {
|
|
394
|
+
symbols.delete(symmSymbol);
|
|
395
|
+
if (symbols.size === 0) {
|
|
396
|
+
streamSymbols.delete(binanceSymbol);
|
|
397
|
+
} else {
|
|
398
|
+
streamSymbols.set(binanceSymbol, symbols);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const nextRefCount = getPrevRefCount(binanceSymbol);
|
|
402
|
+
if (nextRefCount === 0) {
|
|
403
|
+
refCounts.delete(binanceSymbol);
|
|
404
|
+
} else {
|
|
405
|
+
refCounts.set(binanceSymbol, nextRefCount);
|
|
406
|
+
}
|
|
407
|
+
allMarkPricesRefCount = Math.max(0, allMarkPricesRefCount - 1);
|
|
408
|
+
if (allMarkPricesRefCount === 0) {
|
|
409
|
+
allMarkPricesUnsubscribe?.();
|
|
410
|
+
allMarkPricesUnsubscribe = null;
|
|
411
|
+
}
|
|
412
|
+
set((state) => {
|
|
413
|
+
if (state.markPrices[symmSymbol] == null) return state;
|
|
414
|
+
const nextMarkPrices = { ...state.markPrices };
|
|
415
|
+
delete nextMarkPrices[symmSymbol];
|
|
416
|
+
return { markPrices: nextMarkPrices };
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}));
|
|
420
|
+
|
|
421
|
+
// src/react/hooks/use-binance-ws.ts
|
|
422
|
+
function useBinanceWs(params) {
|
|
423
|
+
const { symmCoreClient, chainId } = params;
|
|
424
|
+
const subscribeSymbol = useBinanceMarkPriceStore((state) => state.subscribeSymbol);
|
|
425
|
+
const unsubscribeSymbol = useBinanceMarkPriceStore((state) => state.unsubscribeSymbol);
|
|
426
|
+
react.useEffect(() => {
|
|
427
|
+
if (!symmCoreClient) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
let cancelled = false;
|
|
431
|
+
let subscribedPairs = [];
|
|
432
|
+
const run = async () => {
|
|
433
|
+
try {
|
|
434
|
+
const result = await symmCoreClient.markets.listSymmHedger({ chainId });
|
|
435
|
+
if (cancelled) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const uniqueSymbols = Array.from(
|
|
439
|
+
new Set(
|
|
440
|
+
result.markets.map((market) => market.symbol).filter((symbol) => !!symbol)
|
|
441
|
+
)
|
|
442
|
+
);
|
|
443
|
+
subscribedPairs = uniqueSymbols.map((symbol) => {
|
|
444
|
+
const binanceSymbol = resolveBinanceSymbol(symbol).binanceSymbol;
|
|
445
|
+
if (!binanceSymbol) return null;
|
|
446
|
+
return [symbol, binanceSymbol];
|
|
447
|
+
}).filter((entry) => !!entry);
|
|
448
|
+
subscribedPairs.forEach(([symbol, binanceSymbol]) => {
|
|
449
|
+
subscribeSymbol(symbol, binanceSymbol);
|
|
450
|
+
});
|
|
451
|
+
} catch {
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
void run();
|
|
455
|
+
return () => {
|
|
456
|
+
cancelled = true;
|
|
457
|
+
subscribedPairs.forEach(([symbol, binanceSymbol]) => {
|
|
458
|
+
unsubscribeSymbol(symbol, binanceSymbol);
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
}, [symmCoreClient, chainId, subscribeSymbol, unsubscribeSymbol]);
|
|
462
|
+
}
|
|
463
|
+
function SymmProvider({
|
|
464
|
+
chainId = 42161,
|
|
465
|
+
address,
|
|
466
|
+
symmCoreConfig = {
|
|
467
|
+
apiUrl: "https://nginx-server-staging.up.railway.app",
|
|
468
|
+
wsUrl: "wss://nginx-server-staging.up.railway.app"
|
|
469
|
+
},
|
|
470
|
+
symmioConfig,
|
|
471
|
+
children
|
|
472
|
+
}) {
|
|
473
|
+
const symmCoreClient = react.useMemo(() => {
|
|
474
|
+
return symmCore.createSymmSDK({
|
|
475
|
+
apiUrl: symmCoreConfig.apiUrl,
|
|
476
|
+
wsUrl: symmCoreConfig.wsUrl,
|
|
477
|
+
defaultChainId: chainId
|
|
478
|
+
});
|
|
479
|
+
}, [chainId, symmCoreConfig.apiUrl, symmCoreConfig.wsUrl]);
|
|
480
|
+
const value = react.useMemo(
|
|
481
|
+
() => ({
|
|
482
|
+
symmCoreClient,
|
|
483
|
+
chainId,
|
|
484
|
+
address,
|
|
485
|
+
symmioConfig
|
|
486
|
+
}),
|
|
487
|
+
[symmCoreClient, chainId, address, symmioConfig]
|
|
488
|
+
);
|
|
489
|
+
useBinanceWs({
|
|
490
|
+
symmCoreClient,
|
|
491
|
+
chainId
|
|
492
|
+
});
|
|
493
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SymmContext.Provider, { value, children });
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/react/hooks/use-symm-core-client.ts
|
|
497
|
+
function useSymmCoreClient() {
|
|
498
|
+
return useSymmContext().symmCoreClient;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/constants/defaults.ts
|
|
502
|
+
var GAS_MARGIN_PERCENTAGE = 20n;
|
|
503
|
+
var MUON_BASE_URLS = [
|
|
504
|
+
"https://muon-oracle1.rasa.capital/v1/",
|
|
505
|
+
"https://muon-oracle2.rasa.capital/v1/",
|
|
506
|
+
"https://muon-oracle3.rasa.capital/v1/",
|
|
507
|
+
"https://muon-oracle4.rasa.capital/v1/"
|
|
508
|
+
];
|
|
509
|
+
var MUON_APP_NAME = "symmio";
|
|
510
|
+
var MUON_REQUEST_TIMEOUT = 15e3;
|
|
511
|
+
var HEDGER_BASE_URLS = {
|
|
512
|
+
[42161 /* ARBITRUM */]: "https://www.perps-streaming.com/v1/42161a/0x6273242a7E88b3De90822b31648C212215caaFE4",
|
|
513
|
+
[8453 /* BASE */]: "https://www.perps-streaming.com/v1/8453a/0x6273242a7E88b3De90822b31648C212215caaFE4"
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
// src/actions/instant.ts
|
|
517
|
+
function getHedgerBaseUrl(chainId) {
|
|
518
|
+
const baseUrl = HEDGER_BASE_URLS[chainId];
|
|
519
|
+
if (!baseUrl) {
|
|
520
|
+
throw new Error(`No hedger base URL configured for chain ${chainId}.`);
|
|
521
|
+
}
|
|
522
|
+
return baseUrl;
|
|
523
|
+
}
|
|
524
|
+
function createSiweMessage(params) {
|
|
525
|
+
const version = params.version ?? "1";
|
|
526
|
+
const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
527
|
+
const expirationTime = new Date(
|
|
528
|
+
Date.now() + 30 * 24 * 60 * 60 * 1e3
|
|
529
|
+
).toISOString();
|
|
530
|
+
const message = [
|
|
531
|
+
`${params.domain} wants you to sign in with your Ethereum account:`,
|
|
532
|
+
params.address,
|
|
533
|
+
"",
|
|
534
|
+
params.statement,
|
|
535
|
+
"",
|
|
536
|
+
`URI: ${params.uri}`,
|
|
537
|
+
`Version: ${version}`,
|
|
538
|
+
`Chain ID: ${params.chainId}`,
|
|
539
|
+
`Nonce: ${params.nonce}`,
|
|
540
|
+
`Issued At: ${issuedAt}`,
|
|
541
|
+
`Expiration Time: ${expirationTime}`
|
|
542
|
+
].join("\n");
|
|
543
|
+
return { message, issuedAt, expirationTime };
|
|
544
|
+
}
|
|
545
|
+
async function getNonce(chainId, subAccount) {
|
|
546
|
+
const hedgerBaseUrl = getHedgerBaseUrl(chainId);
|
|
547
|
+
const url = new URL(`nonce/${subAccount}`, hedgerBaseUrl).href;
|
|
548
|
+
const response = await fetch(url);
|
|
549
|
+
if (!response.ok) {
|
|
550
|
+
throw new Error(`Failed to fetch nonce: ${response.statusText}`);
|
|
551
|
+
}
|
|
552
|
+
const data = await response.json();
|
|
553
|
+
return data.nonce;
|
|
554
|
+
}
|
|
555
|
+
async function login(chainId, params) {
|
|
556
|
+
const hedgerBaseUrl = getHedgerBaseUrl(chainId);
|
|
557
|
+
const url = new URL("login", hedgerBaseUrl).href;
|
|
558
|
+
const body = {
|
|
559
|
+
account_address: params.accountAddress,
|
|
560
|
+
expiration_time: params.expirationTime,
|
|
561
|
+
issued_at: params.issuedAt,
|
|
562
|
+
signature: params.signature,
|
|
563
|
+
nonce: params.nonce
|
|
564
|
+
};
|
|
565
|
+
const response = await fetch(url, {
|
|
566
|
+
method: "POST",
|
|
567
|
+
headers: { "Content-Type": "application/json" },
|
|
568
|
+
body: JSON.stringify(body)
|
|
569
|
+
});
|
|
570
|
+
if (!response.ok) {
|
|
571
|
+
const errorData = await response.json().catch(() => null);
|
|
572
|
+
throw new Error(
|
|
573
|
+
errorData?.error_message ?? `Login failed: ${response.statusText}`
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
const data = await response.json();
|
|
577
|
+
if (!data.access_token) {
|
|
578
|
+
throw new Error("No access token received");
|
|
579
|
+
}
|
|
580
|
+
return {
|
|
581
|
+
accessToken: data.access_token,
|
|
582
|
+
expirationTime: params.expirationTime,
|
|
583
|
+
issuedAt: params.issuedAt
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// src/react/auth.ts
|
|
588
|
+
var TOKEN_STORAGE_PREFIX = "symm_access_token";
|
|
589
|
+
var TOKEN_STORAGE_VERSION = 1;
|
|
590
|
+
var tokenCache = /* @__PURE__ */ new Map();
|
|
591
|
+
function cacheKey(address, chainId) {
|
|
592
|
+
return `${address}:${chainId}`;
|
|
593
|
+
}
|
|
594
|
+
function storageKey(address, chainId) {
|
|
595
|
+
return `${TOKEN_STORAGE_PREFIX}:${TOKEN_STORAGE_VERSION}:${cacheKey(address, chainId)}`;
|
|
596
|
+
}
|
|
597
|
+
function readStoredToken(address, chainId) {
|
|
598
|
+
if (typeof window === "undefined") return null;
|
|
599
|
+
try {
|
|
600
|
+
const raw = window.localStorage.getItem(storageKey(address, chainId));
|
|
601
|
+
if (!raw) return null;
|
|
602
|
+
const parsed = JSON.parse(raw);
|
|
603
|
+
if (!parsed || typeof parsed.token !== "string" || typeof parsed.expiresAt !== "number") {
|
|
604
|
+
window.localStorage.removeItem(storageKey(address, chainId));
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
if (Date.now() >= parsed.expiresAt) {
|
|
608
|
+
window.localStorage.removeItem(storageKey(address, chainId));
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
return parsed;
|
|
612
|
+
} catch {
|
|
613
|
+
window.localStorage.removeItem(storageKey(address, chainId));
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function writeStoredToken(address, chainId, cached) {
|
|
618
|
+
if (typeof window === "undefined") return;
|
|
619
|
+
try {
|
|
620
|
+
window.localStorage.setItem(
|
|
621
|
+
storageKey(address, chainId),
|
|
622
|
+
JSON.stringify(cached)
|
|
623
|
+
);
|
|
624
|
+
} catch {
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
function getCachedToken(address, chainId) {
|
|
628
|
+
const cached = tokenCache.get(cacheKey(address, chainId));
|
|
629
|
+
if (cached && Date.now() < cached.expiresAt) {
|
|
630
|
+
return cached.token;
|
|
631
|
+
}
|
|
632
|
+
if (cached) {
|
|
633
|
+
tokenCache.delete(cacheKey(address, chainId));
|
|
634
|
+
}
|
|
635
|
+
const stored = readStoredToken(address, chainId);
|
|
636
|
+
if (!stored) return null;
|
|
637
|
+
tokenCache.set(cacheKey(address, chainId), stored);
|
|
638
|
+
return stored.token;
|
|
639
|
+
}
|
|
640
|
+
function clearCachedToken(address, chainId) {
|
|
641
|
+
tokenCache.delete(cacheKey(address, chainId));
|
|
642
|
+
if (typeof window !== "undefined") {
|
|
643
|
+
window.localStorage.removeItem(storageKey(address, chainId));
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
async function fetchAccessToken(walletClient, signerAddress, accountAddress, chainId, domain) {
|
|
647
|
+
const resolvedDomain = domain ?? (typeof window !== "undefined" ? window.location.host : "localhost");
|
|
648
|
+
const uri = typeof window !== "undefined" ? window.location.origin : `https://${resolvedDomain}`;
|
|
649
|
+
const nonce = await getNonce(chainId, accountAddress);
|
|
650
|
+
const { message, issuedAt, expirationTime } = createSiweMessage({
|
|
651
|
+
address: signerAddress,
|
|
652
|
+
statement: "Sign in to Symm Protocol",
|
|
653
|
+
chainId,
|
|
654
|
+
nonce,
|
|
655
|
+
domain: resolvedDomain,
|
|
656
|
+
uri
|
|
657
|
+
});
|
|
658
|
+
const signature = await walletClient.signMessage({
|
|
659
|
+
account: signerAddress,
|
|
660
|
+
message
|
|
661
|
+
});
|
|
662
|
+
const { accessToken } = await login(chainId, {
|
|
663
|
+
accountAddress,
|
|
664
|
+
signature,
|
|
665
|
+
expirationTime,
|
|
666
|
+
issuedAt,
|
|
667
|
+
nonce
|
|
668
|
+
});
|
|
669
|
+
const expiresAt = new Date(expirationTime).getTime();
|
|
670
|
+
const cachedToken = {
|
|
671
|
+
token: accessToken,
|
|
672
|
+
expiresAt: expiresAt - 6e4
|
|
673
|
+
};
|
|
674
|
+
tokenCache.set(cacheKey(accountAddress, chainId), cachedToken);
|
|
675
|
+
writeStoredToken(accountAddress, chainId, cachedToken);
|
|
676
|
+
return accessToken;
|
|
677
|
+
}
|
|
678
|
+
function symmAuthTokenKey(address, chainId) {
|
|
679
|
+
return `${address.toLowerCase()}:${chainId}`;
|
|
680
|
+
}
|
|
681
|
+
var useSymmAuthStore = zustand.create((set, get) => ({
|
|
682
|
+
tokensByKey: {},
|
|
683
|
+
setToken: (address, chainId, token) => {
|
|
684
|
+
const key = symmAuthTokenKey(address, chainId);
|
|
685
|
+
set((state) => ({
|
|
686
|
+
tokensByKey: {
|
|
687
|
+
...state.tokensByKey,
|
|
688
|
+
[key]: token
|
|
689
|
+
}
|
|
690
|
+
}));
|
|
691
|
+
},
|
|
692
|
+
getToken: (address, chainId) => {
|
|
693
|
+
const key = symmAuthTokenKey(address, chainId);
|
|
694
|
+
return get().tokensByKey[key] ?? null;
|
|
695
|
+
},
|
|
696
|
+
clearToken: (address, chainId) => {
|
|
697
|
+
const key = symmAuthTokenKey(address, chainId);
|
|
698
|
+
set((state) => {
|
|
699
|
+
if (!(key in state.tokensByKey)) return state;
|
|
700
|
+
const next = { ...state.tokensByKey };
|
|
701
|
+
delete next[key];
|
|
702
|
+
return { tokensByKey: next };
|
|
703
|
+
});
|
|
704
|
+
},
|
|
705
|
+
clearAll: () => {
|
|
706
|
+
set({ tokensByKey: {} });
|
|
707
|
+
}
|
|
708
|
+
}));
|
|
709
|
+
|
|
710
|
+
// src/react/hooks/use-symm-auth.ts
|
|
711
|
+
function useSymmAuth(params) {
|
|
712
|
+
const ctx = useSymmContext();
|
|
713
|
+
const address = params?.address ?? ctx.address;
|
|
714
|
+
const chainId = params?.chainId ?? ctx.chainId ?? 42161;
|
|
715
|
+
const walletClient = params?.walletClient ?? ctx.walletClient;
|
|
716
|
+
const siweDomain = params?.siweDomain;
|
|
717
|
+
const accessToken = useSymmAuthStore((state) => {
|
|
718
|
+
if (address) {
|
|
719
|
+
return state.tokensByKey[symmAuthTokenKey(address, chainId)] ?? null;
|
|
720
|
+
}
|
|
721
|
+
return ctx.accessToken ?? ctx.authToken ?? null;
|
|
722
|
+
});
|
|
723
|
+
const setToken = useSymmAuthStore((state) => state.setToken);
|
|
724
|
+
const getToken = useSymmAuthStore((state) => state.getToken);
|
|
725
|
+
const clearToken = useSymmAuthStore((state) => state.clearToken);
|
|
726
|
+
const previousAddressRef = react.useRef(address);
|
|
727
|
+
const previousChainIdRef = react.useRef(chainId);
|
|
728
|
+
const refreshAuth = react.useCallback(
|
|
729
|
+
async (accountAddress) => {
|
|
730
|
+
if (!walletClient || !address) return null;
|
|
731
|
+
const resolvedAccountAddress = accountAddress ?? address;
|
|
732
|
+
const inMemory = getToken(resolvedAccountAddress, chainId);
|
|
733
|
+
if (inMemory) {
|
|
734
|
+
return inMemory;
|
|
735
|
+
}
|
|
736
|
+
const cached = getCachedToken(resolvedAccountAddress, chainId);
|
|
737
|
+
if (cached) {
|
|
738
|
+
setToken(resolvedAccountAddress, chainId, cached);
|
|
739
|
+
return cached;
|
|
740
|
+
}
|
|
741
|
+
try {
|
|
742
|
+
const token = await fetchAccessToken(
|
|
743
|
+
walletClient,
|
|
744
|
+
address,
|
|
745
|
+
resolvedAccountAddress,
|
|
746
|
+
chainId,
|
|
747
|
+
siweDomain
|
|
748
|
+
);
|
|
749
|
+
setToken(resolvedAccountAddress, chainId, token);
|
|
750
|
+
return token;
|
|
751
|
+
} catch {
|
|
752
|
+
clearToken(resolvedAccountAddress, chainId);
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
755
|
+
},
|
|
756
|
+
[walletClient, address, chainId, siweDomain, getToken, setToken, clearToken]
|
|
757
|
+
);
|
|
758
|
+
const clearAuth = react.useCallback(() => {
|
|
759
|
+
if (address) {
|
|
760
|
+
clearCachedToken(address, chainId);
|
|
761
|
+
clearToken(address, chainId);
|
|
762
|
+
}
|
|
763
|
+
}, [address, chainId, clearToken]);
|
|
764
|
+
react.useEffect(() => {
|
|
765
|
+
const previousAddress = previousAddressRef.current;
|
|
766
|
+
const previousChainId = previousChainIdRef.current;
|
|
767
|
+
const addressChanged = previousAddress !== address;
|
|
768
|
+
const chainChanged = previousChainId !== chainId;
|
|
769
|
+
previousAddressRef.current = address;
|
|
770
|
+
previousChainIdRef.current = chainId;
|
|
771
|
+
if (!address || !walletClient) {
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
if (previousAddress && (addressChanged || chainChanged)) {
|
|
775
|
+
clearCachedToken(previousAddress, previousChainId);
|
|
776
|
+
clearToken(previousAddress, previousChainId);
|
|
777
|
+
}
|
|
778
|
+
const cached = getCachedToken(address, chainId);
|
|
779
|
+
if (cached) {
|
|
780
|
+
setToken(address, chainId, cached);
|
|
781
|
+
} else {
|
|
782
|
+
clearToken(address, chainId);
|
|
783
|
+
}
|
|
784
|
+
}, [address, walletClient, chainId, setToken, clearToken]);
|
|
785
|
+
return {
|
|
786
|
+
accessToken,
|
|
787
|
+
authToken: accessToken,
|
|
788
|
+
isAuthenticated: !!accessToken,
|
|
789
|
+
refresh: refreshAuth,
|
|
790
|
+
refreshAuth,
|
|
791
|
+
clear: clearAuth
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// src/constants/selectors.ts
|
|
796
|
+
var SEND_QUOTE_WITH_AFFILIATE_SELECTOR = "0x40f1310c";
|
|
797
|
+
var CLOSE_QUOTE_SELECTOR = "0x501e891f";
|
|
798
|
+
var ALL_TRADING_SELECTORS = [
|
|
799
|
+
SEND_QUOTE_WITH_AFFILIATE_SELECTOR,
|
|
800
|
+
CLOSE_QUOTE_SELECTOR
|
|
801
|
+
];
|
|
802
|
+
|
|
803
|
+
// src/constants/addresses.ts
|
|
804
|
+
var MULTI_ACCOUNT_ADDRESS = {
|
|
805
|
+
[42161 /* ARBITRUM */]: "0x6273242a7E88b3De90822b31648C212215caaFE4",
|
|
806
|
+
[8453 /* BASE */]: "0xE43166cE17d3511B09438a359dAa53513225101D"
|
|
807
|
+
};
|
|
808
|
+
var SYMMIO_DIAMOND_ADDRESS = {
|
|
361
809
|
[42161 /* ARBITRUM */]: "0x8F06459f184553e5d04F07F868720BDaCAB39395",
|
|
362
810
|
[8453 /* BASE */]: "0x91Cf2D8Ed503EC52768999aA6D8DBeA6e52dbe43"
|
|
363
811
|
};
|
|
@@ -24527,854 +24975,535 @@ function useSymmAccountSummary(params) {
|
|
|
24527
24975
|
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24528
24976
|
});
|
|
24529
24977
|
}
|
|
24530
|
-
function useSymmAccountData(params) {
|
|
24531
|
-
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24532
|
-
const { address, upnl } = params;
|
|
24533
|
-
const chainId = params.chainId ?? ctxChainId;
|
|
24534
|
-
const internalEnabled = !!symmCoreClient && !!address;
|
|
24535
|
-
return reactQuery.useQuery({
|
|
24536
|
-
...params.query,
|
|
24537
|
-
queryKey: symmKeys.accountData(address, chainId, upnl),
|
|
24538
|
-
queryFn: () => symmCoreClient.accounts.getData({
|
|
24539
|
-
address,
|
|
24540
|
-
chainId,
|
|
24541
|
-
upnl
|
|
24542
|
-
}),
|
|
24543
|
-
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24544
|
-
});
|
|
24545
|
-
}
|
|
24546
|
-
function useSymmBalances(params) {
|
|
24547
|
-
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24548
|
-
const { userAddress, multiAccountAddress } = params;
|
|
24549
|
-
const chainId = params.chainId ?? ctxChainId;
|
|
24550
|
-
const internalEnabled = !!symmCoreClient && !!userAddress;
|
|
24551
|
-
return reactQuery.useQuery({
|
|
24552
|
-
...params.query,
|
|
24553
|
-
queryKey: symmKeys.balances(userAddress, chainId),
|
|
24554
|
-
queryFn: () => symmCoreClient.accounts.getBalanceInfo({
|
|
24555
|
-
userAddress,
|
|
24556
|
-
chainId,
|
|
24557
|
-
multiAccountAddress
|
|
24558
|
-
}),
|
|
24559
|
-
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24560
|
-
});
|
|
24561
|
-
}
|
|
24562
|
-
function useResolveTradeAuthToken() {
|
|
24563
|
-
const context = useSymmContext();
|
|
24564
|
-
const { address, chainId } = context;
|
|
24565
|
-
const { refreshAuth } = useSymmAuth({ address, chainId });
|
|
24566
|
-
const refreshAuthFromContext = context.refreshAuth;
|
|
24567
|
-
return react.useCallback(
|
|
24568
|
-
async (providedAuthToken, accountAddress) => {
|
|
24569
|
-
if (providedAuthToken) {
|
|
24570
|
-
return providedAuthToken;
|
|
24571
|
-
}
|
|
24572
|
-
const resolvedAccountAddress = accountAddress ?? address;
|
|
24573
|
-
if (!resolvedAccountAddress) {
|
|
24574
|
-
return null;
|
|
24575
|
-
}
|
|
24576
|
-
const inMemoryToken = useSymmAuthStore.getState().getToken(resolvedAccountAddress, chainId);
|
|
24577
|
-
if (inMemoryToken) {
|
|
24578
|
-
return inMemoryToken;
|
|
24579
|
-
}
|
|
24580
|
-
if (refreshAuthFromContext) {
|
|
24581
|
-
return refreshAuthFromContext(resolvedAccountAddress);
|
|
24582
|
-
}
|
|
24583
|
-
return refreshAuth(resolvedAccountAddress);
|
|
24584
|
-
},
|
|
24585
|
-
[address, chainId, refreshAuth, refreshAuthFromContext]
|
|
24586
|
-
);
|
|
24587
|
-
}
|
|
24588
|
-
function useSymmOpenBasketMutation(options) {
|
|
24589
|
-
const { symmCoreClient } = useSymmContext();
|
|
24590
|
-
const queryClient = reactQuery.useQueryClient();
|
|
24591
|
-
return reactQuery.useMutation({
|
|
24592
|
-
...withSymmMutationConfig(options?.mutation, {
|
|
24593
|
-
onSuccess: () => {
|
|
24594
|
-
invalidatePositions(queryClient);
|
|
24595
|
-
}
|
|
24596
|
-
}),
|
|
24597
|
-
mutationFn: async (request) => {
|
|
24598
|
-
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
24599
|
-
return symmCoreClient.positions.openBasket(request);
|
|
24600
|
-
}
|
|
24601
|
-
});
|
|
24602
|
-
}
|
|
24603
|
-
function useSymmClosePositionMutation(options) {
|
|
24604
|
-
const { symmCoreClient } = useSymmContext();
|
|
24605
|
-
const queryClient = reactQuery.useQueryClient();
|
|
24606
|
-
const resolveAuthToken = useResolveTradeAuthToken();
|
|
24607
|
-
return reactQuery.useMutation({
|
|
24608
|
-
...withSymmMutationConfig(options?.mutation, {
|
|
24609
|
-
onSuccess: () => {
|
|
24610
|
-
invalidatePositions(queryClient);
|
|
24611
|
-
}
|
|
24612
|
-
}),
|
|
24613
|
-
mutationFn: async (request) => {
|
|
24614
|
-
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
24615
|
-
const typedRequest = request;
|
|
24616
|
-
const authToken = await resolveAuthToken(
|
|
24617
|
-
typedRequest.authToken,
|
|
24618
|
-
typedRequest.accountAddress
|
|
24619
|
-
);
|
|
24620
|
-
if (!authToken) {
|
|
24621
|
-
throw new Error("auth token is required to close a position");
|
|
24622
|
-
}
|
|
24623
|
-
return symmCoreClient.positions.close({
|
|
24624
|
-
...request,
|
|
24625
|
-
authToken
|
|
24626
|
-
});
|
|
24627
|
-
}
|
|
24628
|
-
});
|
|
24629
|
-
}
|
|
24630
|
-
function useSymmCloseAllPositionsMutation(options) {
|
|
24631
|
-
const { symmCoreClient } = useSymmContext();
|
|
24632
|
-
const queryClient = reactQuery.useQueryClient();
|
|
24633
|
-
return reactQuery.useMutation({
|
|
24634
|
-
...withSymmMutationConfig(options?.mutation, {
|
|
24635
|
-
onSuccess: () => {
|
|
24636
|
-
invalidatePositions(queryClient);
|
|
24637
|
-
}
|
|
24638
|
-
}),
|
|
24639
|
-
mutationFn: async (request) => {
|
|
24640
|
-
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
24641
|
-
return symmCoreClient.positions.closeAll(request);
|
|
24642
|
-
}
|
|
24643
|
-
});
|
|
24644
|
-
}
|
|
24645
|
-
function useSymmCancelOpenMutation(options) {
|
|
24646
|
-
const { symmCoreClient } = useSymmContext();
|
|
24647
|
-
const queryClient = reactQuery.useQueryClient();
|
|
24648
|
-
return reactQuery.useMutation({
|
|
24649
|
-
...withSymmMutationConfig(options?.mutation, {
|
|
24650
|
-
onSuccess: () => {
|
|
24651
|
-
invalidatePositions(queryClient);
|
|
24652
|
-
}
|
|
24653
|
-
}),
|
|
24654
|
-
mutationFn: async (request) => {
|
|
24655
|
-
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
24656
|
-
return symmCoreClient.positions.cancelOpen(request);
|
|
24657
|
-
}
|
|
24658
|
-
});
|
|
24659
|
-
}
|
|
24660
|
-
function useSymmUpdatePositionMutation(options) {
|
|
24661
|
-
const { symmCoreClient } = useSymmContext();
|
|
24662
|
-
const queryClient = reactQuery.useQueryClient();
|
|
24663
|
-
const resolveAuthToken = useResolveTradeAuthToken();
|
|
24664
|
-
return reactQuery.useMutation({
|
|
24665
|
-
...withSymmMutationConfig(options?.mutation, {
|
|
24666
|
-
onSuccess: () => {
|
|
24667
|
-
invalidatePositions(queryClient);
|
|
24668
|
-
}
|
|
24669
|
-
}),
|
|
24670
|
-
mutationFn: async ({
|
|
24671
|
-
positionId,
|
|
24672
|
-
request
|
|
24673
|
-
}) => {
|
|
24674
|
-
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
24675
|
-
const typedRequest = request;
|
|
24676
|
-
const authToken = await resolveAuthToken(
|
|
24677
|
-
typedRequest.authToken,
|
|
24678
|
-
typedRequest.accountAddress
|
|
24679
|
-
);
|
|
24680
|
-
if (!authToken) {
|
|
24681
|
-
throw new Error("auth token is required to update a position");
|
|
24682
|
-
}
|
|
24683
|
-
return symmCoreClient.positions.update(positionId, {
|
|
24684
|
-
...request,
|
|
24685
|
-
authToken
|
|
24686
|
-
});
|
|
24687
|
-
}
|
|
24688
|
-
});
|
|
24689
|
-
}
|
|
24690
|
-
function useSymmPositions(params) {
|
|
24691
|
-
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24692
|
-
const { accountAddress, address } = params;
|
|
24693
|
-
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24694
|
-
const chainId = params.chainId ?? ctxChainId;
|
|
24695
|
-
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
24696
|
-
const enabled = internalEnabled && (params.query?.enabled ?? true);
|
|
24697
|
-
return reactQuery.useQuery({
|
|
24698
|
-
...params.query,
|
|
24699
|
-
queryKey: symmKeys.positions({
|
|
24700
|
-
accountAddress,
|
|
24701
|
-
address: resolvedAddress,
|
|
24702
|
-
chainId
|
|
24703
|
-
}),
|
|
24704
|
-
queryFn: () => symmCoreClient.positions.getOpen({
|
|
24705
|
-
accountAddress,
|
|
24706
|
-
address: resolvedAddress,
|
|
24707
|
-
chainId
|
|
24708
|
-
}),
|
|
24709
|
-
enabled
|
|
24710
|
-
});
|
|
24711
|
-
}
|
|
24712
|
-
function useSymmOpenOrders(params) {
|
|
24978
|
+
function useSymmAccountData(params) {
|
|
24713
24979
|
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24714
|
-
const {
|
|
24715
|
-
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24980
|
+
const { address, upnl } = params;
|
|
24716
24981
|
const chainId = params.chainId ?? ctxChainId;
|
|
24717
|
-
const internalEnabled = !!symmCoreClient && !!
|
|
24982
|
+
const internalEnabled = !!symmCoreClient && !!address;
|
|
24718
24983
|
return reactQuery.useQuery({
|
|
24719
24984
|
...params.query,
|
|
24720
|
-
queryKey: symmKeys.
|
|
24721
|
-
|
|
24722
|
-
address
|
|
24723
|
-
chainId
|
|
24724
|
-
|
|
24725
|
-
queryFn: () => symmCoreClient.orders.list({
|
|
24726
|
-
address: accountAddress ?? resolvedAddress,
|
|
24727
|
-
chainId
|
|
24985
|
+
queryKey: symmKeys.accountData(address, chainId, upnl),
|
|
24986
|
+
queryFn: () => symmCoreClient.accounts.getData({
|
|
24987
|
+
address,
|
|
24988
|
+
chainId,
|
|
24989
|
+
upnl
|
|
24728
24990
|
}),
|
|
24729
24991
|
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24730
24992
|
});
|
|
24731
24993
|
}
|
|
24732
|
-
function
|
|
24994
|
+
function useSymmBalances(params) {
|
|
24733
24995
|
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24734
|
-
const {
|
|
24735
|
-
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24996
|
+
const { userAddress, multiAccountAddress } = params;
|
|
24736
24997
|
const chainId = params.chainId ?? ctxChainId;
|
|
24737
|
-
const internalEnabled = !!symmCoreClient && !!
|
|
24998
|
+
const internalEnabled = !!symmCoreClient && !!userAddress;
|
|
24738
24999
|
return reactQuery.useQuery({
|
|
24739
25000
|
...params.query,
|
|
24740
|
-
queryKey: symmKeys.
|
|
24741
|
-
|
|
24742
|
-
|
|
24743
|
-
chainId
|
|
25001
|
+
queryKey: symmKeys.balances(userAddress, chainId),
|
|
25002
|
+
queryFn: () => symmCoreClient.accounts.getBalanceInfo({
|
|
25003
|
+
userAddress,
|
|
25004
|
+
chainId,
|
|
25005
|
+
multiAccountAddress
|
|
24744
25006
|
}),
|
|
24745
|
-
queryFn: () => {
|
|
24746
|
-
const request = {
|
|
24747
|
-
accountAddress,
|
|
24748
|
-
address: resolvedAddress,
|
|
24749
|
-
chainId
|
|
24750
|
-
};
|
|
24751
|
-
return symmCoreClient.positions.getTradeHistory(request);
|
|
24752
|
-
},
|
|
24753
25007
|
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24754
25008
|
});
|
|
24755
25009
|
}
|
|
24756
|
-
function
|
|
25010
|
+
function useResolveTradeAuthToken() {
|
|
25011
|
+
const context = useSymmContext();
|
|
25012
|
+
const { address, chainId } = context;
|
|
25013
|
+
const { refreshAuth } = useSymmAuth({ address, chainId });
|
|
25014
|
+
const refreshAuthFromContext = context.refreshAuth;
|
|
25015
|
+
return react.useCallback(
|
|
25016
|
+
async (providedAuthToken, accountAddress) => {
|
|
25017
|
+
if (providedAuthToken) {
|
|
25018
|
+
return providedAuthToken;
|
|
25019
|
+
}
|
|
25020
|
+
const resolvedAccountAddress = accountAddress ?? address;
|
|
25021
|
+
if (!resolvedAccountAddress) {
|
|
25022
|
+
return null;
|
|
25023
|
+
}
|
|
25024
|
+
const inMemoryToken = useSymmAuthStore.getState().getToken(resolvedAccountAddress, chainId);
|
|
25025
|
+
if (inMemoryToken) {
|
|
25026
|
+
return inMemoryToken;
|
|
25027
|
+
}
|
|
25028
|
+
if (refreshAuthFromContext) {
|
|
25029
|
+
return refreshAuthFromContext(resolvedAccountAddress);
|
|
25030
|
+
}
|
|
25031
|
+
return refreshAuth(resolvedAccountAddress);
|
|
25032
|
+
},
|
|
25033
|
+
[address, chainId, refreshAuth, refreshAuthFromContext]
|
|
25034
|
+
);
|
|
25035
|
+
}
|
|
25036
|
+
function useSymmOpenBasketMutation(options) {
|
|
24757
25037
|
const { symmCoreClient } = useSymmContext();
|
|
24758
25038
|
const queryClient = reactQuery.useQueryClient();
|
|
24759
25039
|
return reactQuery.useMutation({
|
|
24760
25040
|
...withSymmMutationConfig(options?.mutation, {
|
|
24761
25041
|
onSuccess: () => {
|
|
24762
|
-
invalidateOrders(queryClient);
|
|
24763
25042
|
invalidatePositions(queryClient);
|
|
24764
25043
|
}
|
|
24765
25044
|
}),
|
|
24766
25045
|
mutationFn: async (request) => {
|
|
24767
25046
|
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
24768
|
-
return symmCoreClient.
|
|
25047
|
+
return symmCoreClient.positions.openBasket(request);
|
|
24769
25048
|
}
|
|
24770
25049
|
});
|
|
24771
25050
|
}
|
|
24772
|
-
function
|
|
25051
|
+
function useSymmClosePositionMutation(options) {
|
|
24773
25052
|
const { symmCoreClient } = useSymmContext();
|
|
24774
25053
|
const queryClient = reactQuery.useQueryClient();
|
|
25054
|
+
const resolveAuthToken = useResolveTradeAuthToken();
|
|
24775
25055
|
return reactQuery.useMutation({
|
|
24776
25056
|
...withSymmMutationConfig(options?.mutation, {
|
|
24777
25057
|
onSuccess: () => {
|
|
24778
|
-
invalidateOrders(queryClient);
|
|
24779
25058
|
invalidatePositions(queryClient);
|
|
24780
25059
|
}
|
|
24781
25060
|
}),
|
|
24782
25061
|
mutationFn: async (request) => {
|
|
24783
25062
|
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
24784
|
-
|
|
25063
|
+
const typedRequest = request;
|
|
25064
|
+
const authToken = await resolveAuthToken(
|
|
25065
|
+
typedRequest.authToken,
|
|
25066
|
+
typedRequest.accountAddress
|
|
25067
|
+
);
|
|
25068
|
+
if (!authToken) {
|
|
25069
|
+
throw new Error("auth token is required to close a position");
|
|
25070
|
+
}
|
|
25071
|
+
return symmCoreClient.positions.close({
|
|
25072
|
+
...request,
|
|
25073
|
+
authToken
|
|
25074
|
+
});
|
|
24785
25075
|
}
|
|
24786
25076
|
});
|
|
24787
25077
|
}
|
|
24788
|
-
function
|
|
24789
|
-
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24790
|
-
const { accountAddress, address, positionId, type, status } = params;
|
|
24791
|
-
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24792
|
-
const chainId = params.chainId ?? ctxChainId;
|
|
24793
|
-
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress || positionId);
|
|
24794
|
-
const request = {
|
|
24795
|
-
address: accountAddress ?? resolvedAddress,
|
|
24796
|
-
positionId,
|
|
24797
|
-
type,
|
|
24798
|
-
status,
|
|
24799
|
-
chainId
|
|
24800
|
-
};
|
|
24801
|
-
return reactQuery.useQuery({
|
|
24802
|
-
...params.query,
|
|
24803
|
-
queryKey: symmKeys.tpslOrdersList({
|
|
24804
|
-
accountAddress,
|
|
24805
|
-
address: resolvedAddress,
|
|
24806
|
-
positionId,
|
|
24807
|
-
type,
|
|
24808
|
-
status,
|
|
24809
|
-
chainId
|
|
24810
|
-
}),
|
|
24811
|
-
queryFn: () => symmCoreClient.orders.getTpslOrders(request),
|
|
24812
|
-
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24813
|
-
});
|
|
24814
|
-
}
|
|
24815
|
-
function useResolvedTwapParams(params) {
|
|
24816
|
-
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24817
|
-
const { accountAddress, address } = params;
|
|
24818
|
-
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24819
|
-
const chainId = params.chainId ?? ctxChainId;
|
|
24820
|
-
return { symmCoreClient, accountAddress, resolvedAddress, chainId, query: params.query };
|
|
24821
|
-
}
|
|
24822
|
-
function useSymmTwapOrdersQuery(params) {
|
|
24823
|
-
const { symmCoreClient, accountAddress, resolvedAddress, chainId, query } = useResolvedTwapParams(params);
|
|
24824
|
-
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
24825
|
-
return reactQuery.useQuery({
|
|
24826
|
-
...query,
|
|
24827
|
-
queryKey: symmKeys.twapOrders(accountAddress ?? resolvedAddress, chainId),
|
|
24828
|
-
queryFn: () => symmCoreClient.orders.getTwapOrders({
|
|
24829
|
-
address: accountAddress ?? resolvedAddress,
|
|
24830
|
-
chainId
|
|
24831
|
-
}),
|
|
24832
|
-
enabled: internalEnabled && (query?.enabled ?? true)
|
|
24833
|
-
});
|
|
24834
|
-
}
|
|
24835
|
-
function useSymmCancelTwapOrderMutation(options) {
|
|
25078
|
+
function useSymmCloseAllPositionsMutation(options) {
|
|
24836
25079
|
const { symmCoreClient } = useSymmContext();
|
|
24837
25080
|
const queryClient = reactQuery.useQueryClient();
|
|
24838
25081
|
return reactQuery.useMutation({
|
|
24839
25082
|
...withSymmMutationConfig(options?.mutation, {
|
|
24840
25083
|
onSuccess: () => {
|
|
24841
|
-
|
|
25084
|
+
invalidatePositions(queryClient);
|
|
24842
25085
|
}
|
|
24843
25086
|
}),
|
|
24844
|
-
mutationFn: async (
|
|
25087
|
+
mutationFn: async (request) => {
|
|
24845
25088
|
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
24846
|
-
return symmCoreClient.
|
|
25089
|
+
return symmCoreClient.positions.closeAll(request);
|
|
24847
25090
|
}
|
|
24848
25091
|
});
|
|
24849
25092
|
}
|
|
24850
|
-
function
|
|
24851
|
-
const { symmCoreClient } = useSymmContext();
|
|
24852
|
-
const { orderId } = params;
|
|
24853
|
-
const internalEnabled = !!symmCoreClient && !!orderId;
|
|
24854
|
-
return reactQuery.useQuery({
|
|
24855
|
-
...params.query,
|
|
24856
|
-
queryKey: symmKeys.triggerConfig(orderId),
|
|
24857
|
-
queryFn: () => symmCoreClient.orders.getTriggerConfig(orderId),
|
|
24858
|
-
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24859
|
-
});
|
|
24860
|
-
}
|
|
24861
|
-
function useSymmSetTriggerConfigMutation(params, options) {
|
|
25093
|
+
function useSymmCancelOpenMutation(options) {
|
|
24862
25094
|
const { symmCoreClient } = useSymmContext();
|
|
24863
|
-
const { orderId } = params;
|
|
24864
25095
|
const queryClient = reactQuery.useQueryClient();
|
|
24865
25096
|
return reactQuery.useMutation({
|
|
24866
25097
|
...withSymmMutationConfig(options?.mutation, {
|
|
24867
25098
|
onSuccess: () => {
|
|
24868
|
-
queryClient
|
|
24869
|
-
queryKey: symmKeys.triggerConfig(orderId)
|
|
24870
|
-
});
|
|
24871
|
-
invalidateOrders(queryClient);
|
|
25099
|
+
invalidatePositions(queryClient);
|
|
24872
25100
|
}
|
|
24873
25101
|
}),
|
|
24874
25102
|
mutationFn: async (request) => {
|
|
24875
|
-
if (!symmCoreClient
|
|
24876
|
-
|
|
24877
|
-
}
|
|
24878
|
-
return symmCoreClient.orders.setTriggerConfig(orderId, request);
|
|
25103
|
+
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
25104
|
+
return symmCoreClient.positions.cancelOpen(request);
|
|
24879
25105
|
}
|
|
24880
25106
|
});
|
|
24881
25107
|
}
|
|
24882
|
-
function
|
|
25108
|
+
function useSymmUpdatePositionMutation(options) {
|
|
24883
25109
|
const { symmCoreClient } = useSymmContext();
|
|
24884
|
-
const { orderId } = params;
|
|
24885
25110
|
const queryClient = reactQuery.useQueryClient();
|
|
25111
|
+
const resolveAuthToken = useResolveTradeAuthToken();
|
|
24886
25112
|
return reactQuery.useMutation({
|
|
24887
25113
|
...withSymmMutationConfig(options?.mutation, {
|
|
24888
25114
|
onSuccess: () => {
|
|
24889
|
-
queryClient
|
|
24890
|
-
queryKey: symmKeys.triggerConfig(orderId)
|
|
24891
|
-
});
|
|
24892
|
-
invalidateOrders(queryClient);
|
|
25115
|
+
invalidatePositions(queryClient);
|
|
24893
25116
|
}
|
|
24894
25117
|
}),
|
|
24895
|
-
mutationFn: async (
|
|
24896
|
-
|
|
24897
|
-
|
|
25118
|
+
mutationFn: async ({
|
|
25119
|
+
positionId,
|
|
25120
|
+
request
|
|
25121
|
+
}) => {
|
|
25122
|
+
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
25123
|
+
const typedRequest = request;
|
|
25124
|
+
const authToken = await resolveAuthToken(
|
|
25125
|
+
typedRequest.authToken,
|
|
25126
|
+
typedRequest.accountAddress
|
|
25127
|
+
);
|
|
25128
|
+
if (!authToken) {
|
|
25129
|
+
throw new Error("auth token is required to update a position");
|
|
24898
25130
|
}
|
|
24899
|
-
return symmCoreClient.
|
|
25131
|
+
return symmCoreClient.positions.update(positionId, {
|
|
25132
|
+
...request,
|
|
25133
|
+
authToken
|
|
25134
|
+
});
|
|
24900
25135
|
}
|
|
24901
25136
|
});
|
|
24902
25137
|
}
|
|
24903
|
-
function
|
|
24904
|
-
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24905
|
-
const { accountAddress, address, status, limit, offset } = params;
|
|
24906
|
-
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24907
|
-
const chainId = params.chainId ?? ctxChainId;
|
|
24908
|
-
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
24909
|
-
const request = {
|
|
24910
|
-
address: accountAddress ?? resolvedAddress,
|
|
24911
|
-
chainId,
|
|
24912
|
-
status,
|
|
24913
|
-
limit,
|
|
24914
|
-
offset
|
|
24915
|
-
};
|
|
24916
|
-
return reactQuery.useQuery({
|
|
24917
|
-
...params.query,
|
|
24918
|
-
queryKey: symmKeys.triggerOrders({
|
|
24919
|
-
accountAddress,
|
|
24920
|
-
address: resolvedAddress,
|
|
24921
|
-
chainId,
|
|
24922
|
-
status,
|
|
24923
|
-
limit,
|
|
24924
|
-
offset
|
|
24925
|
-
}),
|
|
24926
|
-
queryFn: () => symmCoreClient.orders.listTriggerOrders(request),
|
|
24927
|
-
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24928
|
-
});
|
|
24929
|
-
}
|
|
24930
|
-
function useSymmMarkets(params) {
|
|
24931
|
-
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24932
|
-
const chainId = params?.chainId ?? ctxChainId;
|
|
24933
|
-
const searchText = params?.searchText;
|
|
24934
|
-
const internalEnabled = !!symmCoreClient;
|
|
24935
|
-
return reactQuery.useQuery({
|
|
24936
|
-
...params?.query,
|
|
24937
|
-
queryKey: symmKeys.markets(chainId, searchText),
|
|
24938
|
-
queryFn: () => {
|
|
24939
|
-
if (searchText) {
|
|
24940
|
-
return symmCoreClient.markets.search(searchText, { chainId });
|
|
24941
|
-
}
|
|
24942
|
-
return symmCoreClient.markets.list({ pageSize: "1000", chainId });
|
|
24943
|
-
},
|
|
24944
|
-
enabled: internalEnabled && (params?.query?.enabled ?? true)
|
|
24945
|
-
});
|
|
24946
|
-
}
|
|
24947
|
-
function useSymmHedgerMarketById(params) {
|
|
24948
|
-
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24949
|
-
const { id } = params;
|
|
24950
|
-
const chainId = params.chainId ?? ctxChainId;
|
|
24951
|
-
const internalEnabled = !!symmCoreClient && id != null;
|
|
24952
|
-
return reactQuery.useQuery({
|
|
24953
|
-
...params.query,
|
|
24954
|
-
queryKey: symmKeys.hedgerMarketById(id, chainId),
|
|
24955
|
-
queryFn: () => symmCoreClient.markets.getById(id, chainId),
|
|
24956
|
-
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24957
|
-
});
|
|
24958
|
-
}
|
|
24959
|
-
function useSymmHedgerMarketBySymbol(params) {
|
|
25138
|
+
function useSymmPositions(params) {
|
|
24960
25139
|
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24961
|
-
const {
|
|
25140
|
+
const { accountAddress, address } = params;
|
|
25141
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24962
25142
|
const chainId = params.chainId ?? ctxChainId;
|
|
24963
|
-
const internalEnabled = !!symmCoreClient && !!
|
|
25143
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
25144
|
+
const enabled = internalEnabled && (params.query?.enabled ?? true);
|
|
24964
25145
|
return reactQuery.useQuery({
|
|
24965
25146
|
...params.query,
|
|
24966
|
-
queryKey: symmKeys.
|
|
24967
|
-
|
|
24968
|
-
|
|
25147
|
+
queryKey: symmKeys.positions({
|
|
25148
|
+
accountAddress,
|
|
25149
|
+
address: resolvedAddress,
|
|
25150
|
+
chainId
|
|
25151
|
+
}),
|
|
25152
|
+
queryFn: () => symmCoreClient.positions.getOpen({
|
|
25153
|
+
accountAddress,
|
|
25154
|
+
address: resolvedAddress,
|
|
25155
|
+
chainId
|
|
25156
|
+
}),
|
|
25157
|
+
enabled
|
|
24969
25158
|
});
|
|
24970
25159
|
}
|
|
24971
|
-
function
|
|
25160
|
+
function useSymmOpenOrders(params) {
|
|
24972
25161
|
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24973
|
-
const {
|
|
25162
|
+
const { accountAddress, address } = params;
|
|
25163
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24974
25164
|
const chainId = params.chainId ?? ctxChainId;
|
|
24975
|
-
const internalEnabled = !!symmCoreClient && !!
|
|
25165
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
24976
25166
|
return reactQuery.useQuery({
|
|
24977
25167
|
...params.query,
|
|
24978
|
-
queryKey: symmKeys.
|
|
24979
|
-
|
|
24980
|
-
|
|
24981
|
-
leverage,
|
|
25168
|
+
queryKey: symmKeys.openOrders({
|
|
25169
|
+
accountAddress,
|
|
25170
|
+
address: resolvedAddress,
|
|
24982
25171
|
chainId
|
|
24983
25172
|
}),
|
|
24984
|
-
|
|
24985
|
-
|
|
24986
|
-
|
|
24987
|
-
|
|
24988
|
-
|
|
24989
|
-
|
|
24990
|
-
|
|
24991
|
-
|
|
24992
|
-
const {
|
|
24993
|
-
const
|
|
24994
|
-
|
|
24995
|
-
|
|
24996
|
-
|
|
24997
|
-
|
|
24998
|
-
|
|
24999
|
-
|
|
25000
|
-
|
|
25001
|
-
|
|
25002
|
-
|
|
25003
|
-
|
|
25004
|
-
|
|
25005
|
-
|
|
25006
|
-
|
|
25007
|
-
|
|
25008
|
-
|
|
25009
|
-
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
|
|
25014
|
-
|
|
25015
|
-
|
|
25016
|
-
});
|
|
25017
|
-
const
|
|
25018
|
-
|
|
25019
|
-
|
|
25020
|
-
|
|
25021
|
-
|
|
25022
|
-
|
|
25023
|
-
openTime: Number(k[0]),
|
|
25024
|
-
open: parseFloat(k[1]),
|
|
25025
|
-
high: parseFloat(k[2]),
|
|
25026
|
-
low: parseFloat(k[3]),
|
|
25027
|
-
close: parseFloat(k[4]),
|
|
25028
|
-
volume: parseFloat(k[5]),
|
|
25029
|
-
closeTime: Number(k[6])
|
|
25030
|
-
}));
|
|
25031
|
-
}
|
|
25032
|
-
async function fetch24hrTicker(symbol) {
|
|
25033
|
-
const params = new URLSearchParams({ symbol });
|
|
25034
|
-
const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/ticker/24hr?${params}`);
|
|
25035
|
-
if (!response.ok) return null;
|
|
25036
|
-
const data = await response.json();
|
|
25037
|
-
return {
|
|
25038
|
-
lastPrice: parseFloat(data.lastPrice),
|
|
25039
|
-
prevClosePrice: parseFloat(data.prevClosePrice),
|
|
25040
|
-
priceChangePercent: parseFloat(data.priceChangePercent)
|
|
25041
|
-
};
|
|
25042
|
-
}
|
|
25043
|
-
async function fetch24hrTickers() {
|
|
25044
|
-
const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/ticker/24hr`);
|
|
25045
|
-
if (!response.ok) {
|
|
25046
|
-
throw new Error(`Binance 24hr tickers failed: ${response.status}`);
|
|
25047
|
-
}
|
|
25048
|
-
const data = await response.json();
|
|
25049
|
-
const result = {};
|
|
25050
|
-
for (const item of data) {
|
|
25051
|
-
result[item.symbol] = {
|
|
25052
|
-
lastPrice: parseFloat(item.lastPrice),
|
|
25053
|
-
prevClosePrice: parseFloat(item.prevClosePrice),
|
|
25054
|
-
priceChangePercent: parseFloat(item.priceChangePercent)
|
|
25055
|
-
};
|
|
25056
|
-
}
|
|
25057
|
-
return result;
|
|
25058
|
-
}
|
|
25059
|
-
|
|
25060
|
-
// src/utils/binance-symbol-map.ts
|
|
25061
|
-
var SYMBOL_OVERRIDES = {
|
|
25062
|
-
// Add overrides here as needed for SYMM markets that don't map 1:1 to Binance
|
|
25063
|
-
// e.g., 'SOME_SYMM_SYMBOL': 'BINANCE_SYMBOL',
|
|
25064
|
-
};
|
|
25065
|
-
var UNSUPPORTED_SYMBOLS = /* @__PURE__ */ new Set([
|
|
25066
|
-
// Add symbols here that have no Binance equivalent
|
|
25067
|
-
]);
|
|
25068
|
-
function resolveBinanceSymbol(symmSymbol) {
|
|
25069
|
-
if (!symmSymbol || !symmSymbol.trim()) {
|
|
25070
|
-
return {
|
|
25071
|
-
symmSymbol,
|
|
25072
|
-
normalizedSymbol: "",
|
|
25073
|
-
binanceSymbol: null,
|
|
25074
|
-
supported: false,
|
|
25075
|
-
reason: "missing_symbol"
|
|
25076
|
-
};
|
|
25077
|
-
}
|
|
25078
|
-
const normalized = symmSymbol.toUpperCase().trim();
|
|
25079
|
-
if (!/^[A-Z0-9]+$/.test(normalized)) {
|
|
25080
|
-
return {
|
|
25081
|
-
symmSymbol,
|
|
25082
|
-
normalizedSymbol: normalized,
|
|
25083
|
-
binanceSymbol: null,
|
|
25084
|
-
supported: false,
|
|
25085
|
-
reason: "invalid_symbol"
|
|
25086
|
-
};
|
|
25087
|
-
}
|
|
25088
|
-
if (UNSUPPORTED_SYMBOLS.has(normalized)) {
|
|
25089
|
-
return {
|
|
25090
|
-
symmSymbol,
|
|
25091
|
-
normalizedSymbol: normalized,
|
|
25092
|
-
binanceSymbol: null,
|
|
25093
|
-
supported: false,
|
|
25094
|
-
reason: "unsupported_symbol"
|
|
25095
|
-
};
|
|
25096
|
-
}
|
|
25097
|
-
const binanceSymbol = SYMBOL_OVERRIDES[normalized] ?? (normalized.endsWith("USDT") ? normalized : `${normalized}USDT`);
|
|
25098
|
-
return {
|
|
25099
|
-
symmSymbol,
|
|
25100
|
-
normalizedSymbol: normalized,
|
|
25101
|
-
binanceSymbol,
|
|
25102
|
-
supported: true,
|
|
25103
|
-
reason: null
|
|
25104
|
-
};
|
|
25105
|
-
}
|
|
25106
|
-
function getUnsupportedBinanceSymbols(symbols) {
|
|
25107
|
-
return symbols.filter((symbol) => !resolveBinanceSymbol(symbol).supported);
|
|
25108
|
-
}
|
|
25109
|
-
function toBinanceSymbol(symmSymbol) {
|
|
25110
|
-
return resolveBinanceSymbol(symmSymbol).binanceSymbol;
|
|
25111
|
-
}
|
|
25112
|
-
|
|
25113
|
-
// src/utils/binance-ws.ts
|
|
25114
|
-
var BINANCE_WS_URL = "wss://fstream.binance.com/ws";
|
|
25115
|
-
var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
|
|
25116
|
-
var BinanceWsManager = class {
|
|
25117
|
-
ws = null;
|
|
25118
|
-
streams = /* @__PURE__ */ new Map();
|
|
25119
|
-
reconnectAttempt = 0;
|
|
25120
|
-
reconnectTimer = null;
|
|
25121
|
-
intentionalClose = false;
|
|
25122
|
-
pendingSubscribes = [];
|
|
25123
|
-
idCounter = 0;
|
|
25124
|
-
/**
|
|
25125
|
-
* Subscribe to a kline stream. Returns an unsubscribe function.
|
|
25126
|
-
*/
|
|
25127
|
-
subscribeKline(symbol, interval, cb) {
|
|
25128
|
-
const streamName = `${symbol.toLowerCase()}@kline_${interval}`;
|
|
25129
|
-
const id = this.generateId();
|
|
25130
|
-
const wrappedCb = (raw) => {
|
|
25131
|
-
const k = raw.k;
|
|
25132
|
-
if (!k) return;
|
|
25133
|
-
cb({
|
|
25134
|
-
symbol: raw.s,
|
|
25135
|
-
interval: k.i,
|
|
25136
|
-
openTime: k.t,
|
|
25137
|
-
closeTime: k.T,
|
|
25138
|
-
open: parseFloat(k.o),
|
|
25139
|
-
high: parseFloat(k.h),
|
|
25140
|
-
low: parseFloat(k.l),
|
|
25141
|
-
close: parseFloat(k.c),
|
|
25142
|
-
volume: parseFloat(k.v),
|
|
25143
|
-
isFinal: k.x
|
|
25144
|
-
});
|
|
25145
|
-
};
|
|
25146
|
-
this.addStreamCallback(streamName, id, wrappedCb);
|
|
25147
|
-
return () => this.removeStreamCallback(streamName, id);
|
|
25148
|
-
}
|
|
25149
|
-
/**
|
|
25150
|
-
* Subscribe to a mark price stream (1s updates). Returns an unsubscribe function.
|
|
25151
|
-
*/
|
|
25152
|
-
subscribeMarkPrice(symbol, cb) {
|
|
25153
|
-
const streamName = `${symbol.toLowerCase()}@markPrice@1s`;
|
|
25154
|
-
const id = this.generateId();
|
|
25155
|
-
const wrappedCb = (raw) => {
|
|
25156
|
-
cb({
|
|
25157
|
-
symbol: raw.s,
|
|
25158
|
-
markPrice: parseFloat(raw.p),
|
|
25159
|
-
indexPrice: parseFloat(raw.i),
|
|
25160
|
-
time: raw.E
|
|
25161
|
-
});
|
|
25162
|
-
};
|
|
25163
|
-
this.addStreamCallback(streamName, id, wrappedCb);
|
|
25164
|
-
return () => this.removeStreamCallback(streamName, id);
|
|
25165
|
-
}
|
|
25166
|
-
/**
|
|
25167
|
-
* Destroy the manager and close the connection.
|
|
25168
|
-
*/
|
|
25169
|
-
destroy() {
|
|
25170
|
-
this.intentionalClose = true;
|
|
25171
|
-
if (this.reconnectTimer) {
|
|
25172
|
-
clearTimeout(this.reconnectTimer);
|
|
25173
|
-
this.reconnectTimer = null;
|
|
25174
|
-
}
|
|
25175
|
-
if (this.ws) {
|
|
25176
|
-
this.ws.close();
|
|
25177
|
-
this.ws = null;
|
|
25178
|
-
}
|
|
25179
|
-
this.streams.clear();
|
|
25180
|
-
}
|
|
25181
|
-
// --- Private ---
|
|
25182
|
-
generateId() {
|
|
25183
|
-
return `sub_${++this.idCounter}_${Date.now()}`;
|
|
25184
|
-
}
|
|
25185
|
-
addStreamCallback(streamName, id, cb) {
|
|
25186
|
-
let sub = this.streams.get(streamName);
|
|
25187
|
-
const isNew = !sub;
|
|
25188
|
-
if (!sub) {
|
|
25189
|
-
sub = { callbacks: /* @__PURE__ */ new Map() };
|
|
25190
|
-
this.streams.set(streamName, sub);
|
|
25191
|
-
}
|
|
25192
|
-
sub.callbacks.set(id, cb);
|
|
25193
|
-
if (isNew) {
|
|
25194
|
-
this.ensureConnected();
|
|
25195
|
-
this.sendSubscribe([streamName]);
|
|
25196
|
-
}
|
|
25197
|
-
}
|
|
25198
|
-
removeStreamCallback(streamName, id) {
|
|
25199
|
-
const sub = this.streams.get(streamName);
|
|
25200
|
-
if (!sub) return;
|
|
25201
|
-
sub.callbacks.delete(id);
|
|
25202
|
-
if (sub.callbacks.size === 0) {
|
|
25203
|
-
this.streams.delete(streamName);
|
|
25204
|
-
this.sendUnsubscribe([streamName]);
|
|
25205
|
-
if (this.streams.size === 0 && this.ws) {
|
|
25206
|
-
this.intentionalClose = true;
|
|
25207
|
-
this.ws.close();
|
|
25208
|
-
this.ws = null;
|
|
25209
|
-
this.intentionalClose = false;
|
|
25173
|
+
queryFn: () => symmCoreClient.orders.list({
|
|
25174
|
+
address: accountAddress ?? resolvedAddress,
|
|
25175
|
+
chainId
|
|
25176
|
+
}),
|
|
25177
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25178
|
+
});
|
|
25179
|
+
}
|
|
25180
|
+
function useSymmTradeHistory(params) {
|
|
25181
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25182
|
+
const { accountAddress, address } = params;
|
|
25183
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
25184
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25185
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
25186
|
+
return reactQuery.useQuery({
|
|
25187
|
+
...params.query,
|
|
25188
|
+
queryKey: symmKeys.tradeHistory({
|
|
25189
|
+
accountAddress,
|
|
25190
|
+
address: resolvedAddress,
|
|
25191
|
+
chainId
|
|
25192
|
+
}),
|
|
25193
|
+
queryFn: () => {
|
|
25194
|
+
const request = {
|
|
25195
|
+
accountAddress,
|
|
25196
|
+
address: resolvedAddress,
|
|
25197
|
+
chainId
|
|
25198
|
+
};
|
|
25199
|
+
return symmCoreClient.positions.getTradeHistory(request);
|
|
25200
|
+
},
|
|
25201
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25202
|
+
});
|
|
25203
|
+
}
|
|
25204
|
+
function useSymmSetTpslMutation(options) {
|
|
25205
|
+
const { symmCoreClient } = useSymmContext();
|
|
25206
|
+
const queryClient = reactQuery.useQueryClient();
|
|
25207
|
+
return reactQuery.useMutation({
|
|
25208
|
+
...withSymmMutationConfig(options?.mutation, {
|
|
25209
|
+
onSuccess: () => {
|
|
25210
|
+
invalidateOrders(queryClient);
|
|
25211
|
+
invalidatePositions(queryClient);
|
|
25210
25212
|
}
|
|
25213
|
+
}),
|
|
25214
|
+
mutationFn: async (request) => {
|
|
25215
|
+
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
25216
|
+
return symmCoreClient.orders.setTpsl(request);
|
|
25211
25217
|
}
|
|
25212
|
-
}
|
|
25213
|
-
|
|
25214
|
-
|
|
25215
|
-
|
|
25218
|
+
});
|
|
25219
|
+
}
|
|
25220
|
+
function useSymmCancelTpslMutation(options) {
|
|
25221
|
+
const { symmCoreClient } = useSymmContext();
|
|
25222
|
+
const queryClient = reactQuery.useQueryClient();
|
|
25223
|
+
return reactQuery.useMutation({
|
|
25224
|
+
...withSymmMutationConfig(options?.mutation, {
|
|
25225
|
+
onSuccess: () => {
|
|
25226
|
+
invalidateOrders(queryClient);
|
|
25227
|
+
invalidatePositions(queryClient);
|
|
25228
|
+
}
|
|
25229
|
+
}),
|
|
25230
|
+
mutationFn: async (request) => {
|
|
25231
|
+
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
25232
|
+
return symmCoreClient.orders.cancelTpsl(request);
|
|
25216
25233
|
}
|
|
25217
|
-
|
|
25218
|
-
|
|
25219
|
-
|
|
25220
|
-
|
|
25221
|
-
|
|
25222
|
-
|
|
25223
|
-
|
|
25224
|
-
|
|
25225
|
-
|
|
25226
|
-
|
|
25227
|
-
|
|
25234
|
+
});
|
|
25235
|
+
}
|
|
25236
|
+
function useSymmTpslOrders(params) {
|
|
25237
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25238
|
+
const { accountAddress, address, positionId, type, status } = params;
|
|
25239
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
25240
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25241
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress || positionId);
|
|
25242
|
+
const request = {
|
|
25243
|
+
address: accountAddress ?? resolvedAddress,
|
|
25244
|
+
positionId,
|
|
25245
|
+
type,
|
|
25246
|
+
status,
|
|
25247
|
+
chainId
|
|
25248
|
+
};
|
|
25249
|
+
return reactQuery.useQuery({
|
|
25250
|
+
...params.query,
|
|
25251
|
+
queryKey: symmKeys.tpslOrdersList({
|
|
25252
|
+
accountAddress,
|
|
25253
|
+
address: resolvedAddress,
|
|
25254
|
+
positionId,
|
|
25255
|
+
type,
|
|
25256
|
+
status,
|
|
25257
|
+
chainId
|
|
25258
|
+
}),
|
|
25259
|
+
queryFn: () => symmCoreClient.orders.getTpslOrders(request),
|
|
25260
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25261
|
+
});
|
|
25262
|
+
}
|
|
25263
|
+
function useResolvedTwapParams(params) {
|
|
25264
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25265
|
+
const { accountAddress, address } = params;
|
|
25266
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
25267
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25268
|
+
return { symmCoreClient, accountAddress, resolvedAddress, chainId, query: params.query };
|
|
25269
|
+
}
|
|
25270
|
+
function useSymmTwapOrdersQuery(params) {
|
|
25271
|
+
const { symmCoreClient, accountAddress, resolvedAddress, chainId, query } = useResolvedTwapParams(params);
|
|
25272
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
25273
|
+
return reactQuery.useQuery({
|
|
25274
|
+
...query,
|
|
25275
|
+
queryKey: symmKeys.twapOrders(accountAddress ?? resolvedAddress, chainId),
|
|
25276
|
+
queryFn: () => symmCoreClient.orders.getTwapOrders({
|
|
25277
|
+
address: accountAddress ?? resolvedAddress,
|
|
25278
|
+
chainId
|
|
25279
|
+
}),
|
|
25280
|
+
enabled: internalEnabled && (query?.enabled ?? true)
|
|
25281
|
+
});
|
|
25282
|
+
}
|
|
25283
|
+
function useSymmCancelTwapOrderMutation(options) {
|
|
25284
|
+
const { symmCoreClient } = useSymmContext();
|
|
25285
|
+
const queryClient = reactQuery.useQueryClient();
|
|
25286
|
+
return reactQuery.useMutation({
|
|
25287
|
+
...withSymmMutationConfig(options?.mutation, {
|
|
25288
|
+
onSuccess: () => {
|
|
25289
|
+
invalidateOrders(queryClient);
|
|
25228
25290
|
}
|
|
25229
|
-
|
|
25230
|
-
|
|
25231
|
-
|
|
25291
|
+
}),
|
|
25292
|
+
mutationFn: async (orderId) => {
|
|
25293
|
+
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
25294
|
+
return symmCoreClient.orders.cancelTwapOrder(orderId);
|
|
25295
|
+
}
|
|
25296
|
+
});
|
|
25297
|
+
}
|
|
25298
|
+
function useSymmTriggerConfigQuery(params) {
|
|
25299
|
+
const { symmCoreClient } = useSymmContext();
|
|
25300
|
+
const { orderId } = params;
|
|
25301
|
+
const internalEnabled = !!symmCoreClient && !!orderId;
|
|
25302
|
+
return reactQuery.useQuery({
|
|
25303
|
+
...params.query,
|
|
25304
|
+
queryKey: symmKeys.triggerConfig(orderId),
|
|
25305
|
+
queryFn: () => symmCoreClient.orders.getTriggerConfig(orderId),
|
|
25306
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25307
|
+
});
|
|
25308
|
+
}
|
|
25309
|
+
function useSymmSetTriggerConfigMutation(params, options) {
|
|
25310
|
+
const { symmCoreClient } = useSymmContext();
|
|
25311
|
+
const { orderId } = params;
|
|
25312
|
+
const queryClient = reactQuery.useQueryClient();
|
|
25313
|
+
return reactQuery.useMutation({
|
|
25314
|
+
...withSymmMutationConfig(options?.mutation, {
|
|
25315
|
+
onSuccess: () => {
|
|
25316
|
+
queryClient.invalidateQueries({
|
|
25317
|
+
queryKey: symmKeys.triggerConfig(orderId)
|
|
25318
|
+
});
|
|
25319
|
+
invalidateOrders(queryClient);
|
|
25232
25320
|
}
|
|
25233
|
-
}
|
|
25234
|
-
|
|
25235
|
-
|
|
25236
|
-
|
|
25237
|
-
this.handleMessage(data);
|
|
25238
|
-
} catch {
|
|
25321
|
+
}),
|
|
25322
|
+
mutationFn: async (request) => {
|
|
25323
|
+
if (!symmCoreClient || !orderId) {
|
|
25324
|
+
throw new Error("symm-core client or orderId not available");
|
|
25239
25325
|
}
|
|
25240
|
-
|
|
25241
|
-
this.ws.onclose = () => {
|
|
25242
|
-
if (this.intentionalClose) return;
|
|
25243
|
-
this.scheduleReconnect();
|
|
25244
|
-
};
|
|
25245
|
-
this.ws.onerror = () => {
|
|
25246
|
-
};
|
|
25247
|
-
}
|
|
25248
|
-
handleMessage(data) {
|
|
25249
|
-
if (data.e === "kline") {
|
|
25250
|
-
const k = data.k;
|
|
25251
|
-
const streamName = `${data.s.toLowerCase()}@kline_${k.i}`;
|
|
25252
|
-
this.dispatchToStream(streamName, data);
|
|
25253
|
-
} else if (data.e === "markPriceUpdate") {
|
|
25254
|
-
const streamName = `${data.s.toLowerCase()}@markPrice@1s`;
|
|
25255
|
-
this.dispatchToStream(streamName, data);
|
|
25326
|
+
return symmCoreClient.orders.setTriggerConfig(orderId, request);
|
|
25256
25327
|
}
|
|
25257
|
-
}
|
|
25258
|
-
|
|
25259
|
-
|
|
25260
|
-
|
|
25261
|
-
|
|
25262
|
-
|
|
25263
|
-
|
|
25264
|
-
|
|
25328
|
+
});
|
|
25329
|
+
}
|
|
25330
|
+
function useSymmClearTriggerConfigMutation(params, options) {
|
|
25331
|
+
const { symmCoreClient } = useSymmContext();
|
|
25332
|
+
const { orderId } = params;
|
|
25333
|
+
const queryClient = reactQuery.useQueryClient();
|
|
25334
|
+
return reactQuery.useMutation({
|
|
25335
|
+
...withSymmMutationConfig(options?.mutation, {
|
|
25336
|
+
onSuccess: () => {
|
|
25337
|
+
queryClient.invalidateQueries({
|
|
25338
|
+
queryKey: symmKeys.triggerConfig(orderId)
|
|
25339
|
+
});
|
|
25340
|
+
invalidateOrders(queryClient);
|
|
25265
25341
|
}
|
|
25266
|
-
})
|
|
25267
|
-
|
|
25268
|
-
|
|
25269
|
-
|
|
25270
|
-
|
|
25271
|
-
return;
|
|
25272
|
-
}
|
|
25273
|
-
this.ws.send(JSON.stringify({
|
|
25274
|
-
method: "SUBSCRIBE",
|
|
25275
|
-
params: streams,
|
|
25276
|
-
id: Date.now()
|
|
25277
|
-
}));
|
|
25278
|
-
}
|
|
25279
|
-
sendUnsubscribe(streams) {
|
|
25280
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
25281
|
-
this.pendingSubscribes = this.pendingSubscribes.filter(
|
|
25282
|
-
(s) => !streams.includes(s)
|
|
25283
|
-
);
|
|
25284
|
-
return;
|
|
25342
|
+
}),
|
|
25343
|
+
mutationFn: async () => {
|
|
25344
|
+
if (!symmCoreClient || !orderId) {
|
|
25345
|
+
throw new Error("symm-core client or orderId not available");
|
|
25346
|
+
}
|
|
25347
|
+
return symmCoreClient.orders.clearTriggerConfig(orderId);
|
|
25285
25348
|
}
|
|
25286
|
-
|
|
25287
|
-
|
|
25288
|
-
|
|
25289
|
-
|
|
25290
|
-
|
|
25291
|
-
|
|
25292
|
-
|
|
25293
|
-
|
|
25294
|
-
|
|
25295
|
-
|
|
25296
|
-
|
|
25297
|
-
|
|
25298
|
-
|
|
25299
|
-
|
|
25300
|
-
|
|
25301
|
-
|
|
25302
|
-
|
|
25303
|
-
|
|
25304
|
-
|
|
25305
|
-
|
|
25306
|
-
|
|
25307
|
-
|
|
25308
|
-
|
|
25349
|
+
});
|
|
25350
|
+
}
|
|
25351
|
+
function useSymmTriggerOrders(params) {
|
|
25352
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25353
|
+
const { accountAddress, address, status, limit, offset } = params;
|
|
25354
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
25355
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25356
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
25357
|
+
const request = {
|
|
25358
|
+
address: accountAddress ?? resolvedAddress,
|
|
25359
|
+
chainId,
|
|
25360
|
+
status,
|
|
25361
|
+
limit,
|
|
25362
|
+
offset
|
|
25363
|
+
};
|
|
25364
|
+
return reactQuery.useQuery({
|
|
25365
|
+
...params.query,
|
|
25366
|
+
queryKey: symmKeys.triggerOrders({
|
|
25367
|
+
accountAddress,
|
|
25368
|
+
address: resolvedAddress,
|
|
25369
|
+
chainId,
|
|
25370
|
+
status,
|
|
25371
|
+
limit,
|
|
25372
|
+
offset
|
|
25373
|
+
}),
|
|
25374
|
+
queryFn: () => symmCoreClient.orders.listTriggerOrders(request),
|
|
25375
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25376
|
+
});
|
|
25309
25377
|
}
|
|
25310
|
-
|
|
25311
|
-
|
|
25312
|
-
|
|
25313
|
-
|
|
25314
|
-
|
|
25315
|
-
|
|
25316
|
-
|
|
25378
|
+
function useSymmMarkets(params) {
|
|
25379
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25380
|
+
const chainId = params?.chainId ?? ctxChainId;
|
|
25381
|
+
const searchText = params?.searchText;
|
|
25382
|
+
const internalEnabled = !!symmCoreClient;
|
|
25383
|
+
return reactQuery.useQuery({
|
|
25384
|
+
...params?.query,
|
|
25385
|
+
queryKey: symmKeys.markets(chainId, searchText),
|
|
25386
|
+
queryFn: () => {
|
|
25387
|
+
if (searchText) {
|
|
25388
|
+
return symmCoreClient.markets.search(searchText, { chainId });
|
|
25389
|
+
}
|
|
25390
|
+
return symmCoreClient.markets.list({ pageSize: "1000", chainId });
|
|
25391
|
+
},
|
|
25392
|
+
enabled: internalEnabled && (params?.query?.enabled ?? true)
|
|
25393
|
+
});
|
|
25317
25394
|
}
|
|
25318
|
-
function
|
|
25319
|
-
|
|
25395
|
+
function useSymmHedgerMarketById(params) {
|
|
25396
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25397
|
+
const { id } = params;
|
|
25398
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25399
|
+
const internalEnabled = !!symmCoreClient && id != null;
|
|
25400
|
+
return reactQuery.useQuery({
|
|
25401
|
+
...params.query,
|
|
25402
|
+
queryKey: symmKeys.hedgerMarketById(id, chainId),
|
|
25403
|
+
queryFn: () => symmCoreClient.markets.getById(id, chainId),
|
|
25404
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25405
|
+
});
|
|
25320
25406
|
}
|
|
25321
|
-
function
|
|
25322
|
-
|
|
25407
|
+
function useSymmHedgerMarketBySymbol(params) {
|
|
25408
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25409
|
+
const { symbol } = params;
|
|
25410
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25411
|
+
const internalEnabled = !!symmCoreClient && !!symbol;
|
|
25412
|
+
return reactQuery.useQuery({
|
|
25413
|
+
...params.query,
|
|
25414
|
+
queryKey: symmKeys.hedgerMarketBySymbol(symbol, chainId),
|
|
25415
|
+
queryFn: () => symmCoreClient.markets.getBySymbol(symbol, chainId),
|
|
25416
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25417
|
+
});
|
|
25323
25418
|
}
|
|
25324
|
-
|
|
25325
|
-
|
|
25326
|
-
|
|
25327
|
-
|
|
25328
|
-
|
|
25329
|
-
|
|
25330
|
-
|
|
25331
|
-
|
|
25332
|
-
|
|
25333
|
-
|
|
25334
|
-
|
|
25335
|
-
|
|
25336
|
-
|
|
25337
|
-
|
|
25338
|
-
|
|
25339
|
-
|
|
25340
|
-
|
|
25341
|
-
|
|
25342
|
-
|
|
25343
|
-
|
|
25344
|
-
|
|
25345
|
-
|
|
25346
|
-
|
|
25347
|
-
|
|
25348
|
-
|
|
25349
|
-
|
|
25350
|
-
|
|
25351
|
-
|
|
25352
|
-
|
|
25353
|
-
|
|
25354
|
-
|
|
25355
|
-
|
|
25356
|
-
|
|
25357
|
-
|
|
25358
|
-
|
|
25359
|
-
|
|
25360
|
-
|
|
25361
|
-
|
|
25362
|
-
|
|
25363
|
-
|
|
25364
|
-
|
|
25365
|
-
|
|
25366
|
-
|
|
25367
|
-
|
|
25368
|
-
|
|
25369
|
-
|
|
25370
|
-
|
|
25371
|
-
|
|
25372
|
-
|
|
25373
|
-
delete nextMarkPrices[symmSymbol];
|
|
25374
|
-
return { markPrices: nextMarkPrices };
|
|
25375
|
-
});
|
|
25419
|
+
function useSymmLockedParams(params) {
|
|
25420
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25421
|
+
const { marketName, leverage } = params;
|
|
25422
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25423
|
+
const internalEnabled = !!symmCoreClient && !!marketName && leverage != null;
|
|
25424
|
+
return reactQuery.useQuery({
|
|
25425
|
+
...params.query,
|
|
25426
|
+
queryKey: symmKeys.lockedParams(marketName, leverage, chainId),
|
|
25427
|
+
queryFn: () => symmCoreClient.markets.getLockedParams({
|
|
25428
|
+
marketName,
|
|
25429
|
+
leverage,
|
|
25430
|
+
chainId
|
|
25431
|
+
}),
|
|
25432
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25433
|
+
});
|
|
25434
|
+
}
|
|
25435
|
+
function useSymmHedgerMarkets(params) {
|
|
25436
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25437
|
+
const chainId = params?.chainId ?? ctxChainId;
|
|
25438
|
+
const searchText = params?.searchText?.trim();
|
|
25439
|
+
const consumerEnabled = params?.query?.enabled ?? params?.enabled ?? true;
|
|
25440
|
+
const { enabled: _, query: __, ...restParams } = params ?? {};
|
|
25441
|
+
const request = {
|
|
25442
|
+
...restParams,
|
|
25443
|
+
chainId,
|
|
25444
|
+
searchText: searchText || void 0
|
|
25445
|
+
};
|
|
25446
|
+
const internalEnabled = !!symmCoreClient;
|
|
25447
|
+
return reactQuery.useQuery({
|
|
25448
|
+
...params?.query,
|
|
25449
|
+
queryKey: symmKeys.hedgerMarkets(request),
|
|
25450
|
+
queryFn: () => symmCoreClient.markets.listSymmHedger(request),
|
|
25451
|
+
enabled: internalEnabled && consumerEnabled
|
|
25452
|
+
});
|
|
25453
|
+
}
|
|
25454
|
+
|
|
25455
|
+
// src/utils/binance-api.ts
|
|
25456
|
+
var BINANCE_FAPI_BASE = "https://fapi.binance.com";
|
|
25457
|
+
async function fetchKlines(symbol, interval, startTime, endTime, limit = 1500) {
|
|
25458
|
+
const params = new URLSearchParams({
|
|
25459
|
+
symbol,
|
|
25460
|
+
interval,
|
|
25461
|
+
startTime: String(startTime),
|
|
25462
|
+
endTime: String(endTime),
|
|
25463
|
+
limit: String(Math.min(limit, 1500))
|
|
25464
|
+
});
|
|
25465
|
+
const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/klines?${params}`);
|
|
25466
|
+
if (!response.ok) {
|
|
25467
|
+
throw new Error(`Binance klines failed: ${response.status}`);
|
|
25376
25468
|
}
|
|
25377
|
-
|
|
25469
|
+
const data = await response.json();
|
|
25470
|
+
return data.map((k) => ({
|
|
25471
|
+
openTime: Number(k[0]),
|
|
25472
|
+
open: parseFloat(k[1]),
|
|
25473
|
+
high: parseFloat(k[2]),
|
|
25474
|
+
low: parseFloat(k[3]),
|
|
25475
|
+
close: parseFloat(k[4]),
|
|
25476
|
+
volume: parseFloat(k[5]),
|
|
25477
|
+
closeTime: Number(k[6])
|
|
25478
|
+
}));
|
|
25479
|
+
}
|
|
25480
|
+
async function fetch24hrTicker(symbol) {
|
|
25481
|
+
const params = new URLSearchParams({ symbol });
|
|
25482
|
+
const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/ticker/24hr?${params}`);
|
|
25483
|
+
if (!response.ok) return null;
|
|
25484
|
+
const data = await response.json();
|
|
25485
|
+
return {
|
|
25486
|
+
lastPrice: parseFloat(data.lastPrice),
|
|
25487
|
+
openPrice: parseFloat(data.openPrice),
|
|
25488
|
+
priceChangePercent: parseFloat(data.priceChangePercent)
|
|
25489
|
+
};
|
|
25490
|
+
}
|
|
25491
|
+
async function fetch24hrTickers() {
|
|
25492
|
+
const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/ticker/24hr`);
|
|
25493
|
+
if (!response.ok) {
|
|
25494
|
+
throw new Error(`Binance 24hr tickers failed: ${response.status}`);
|
|
25495
|
+
}
|
|
25496
|
+
const data = await response.json();
|
|
25497
|
+
const result = {};
|
|
25498
|
+
for (const item of data) {
|
|
25499
|
+
result[item.symbol] = {
|
|
25500
|
+
lastPrice: parseFloat(item.lastPrice),
|
|
25501
|
+
openPrice: parseFloat(item.openPrice),
|
|
25502
|
+
priceChangePercent: parseFloat(item.priceChangePercent)
|
|
25503
|
+
};
|
|
25504
|
+
}
|
|
25505
|
+
return result;
|
|
25506
|
+
}
|
|
25378
25507
|
|
|
25379
25508
|
// src/react/hooks/use-symm-token-selection-markets.ts
|
|
25380
25509
|
function useSymmTokenSelectionMarkets(params) {
|
|
@@ -25392,19 +25521,7 @@ function useSymmTokenSelectionMarkets(params) {
|
|
|
25392
25521
|
() => [...marketSymbols].sort().join(","),
|
|
25393
25522
|
[marketSymbols]
|
|
25394
25523
|
);
|
|
25395
|
-
const symbolToBinanceMap = react.useMemo(
|
|
25396
|
-
() => new Map(
|
|
25397
|
-
marketSymbols.map((symbol) => {
|
|
25398
|
-
const binanceSymbol = resolveBinanceSymbol(symbol).binanceSymbol;
|
|
25399
|
-
if (!binanceSymbol) return null;
|
|
25400
|
-
return [symbol, binanceSymbol];
|
|
25401
|
-
}).filter((entry) => !!entry)
|
|
25402
|
-
),
|
|
25403
|
-
[marketSymbols]
|
|
25404
|
-
);
|
|
25405
25524
|
const liveMarkPrices = useBinanceMarkPriceStore((state) => state.markPrices);
|
|
25406
|
-
const subscribeSymbol = useBinanceMarkPriceStore((state) => state.subscribeSymbol);
|
|
25407
|
-
const unsubscribeSymbol = useBinanceMarkPriceStore((state) => state.unsubscribeSymbol);
|
|
25408
25525
|
const priceQuery = reactQuery.useQuery({
|
|
25409
25526
|
queryKey: ["symm", "token-selection-markets-price", symbolsKey],
|
|
25410
25527
|
queryFn: async () => {
|
|
@@ -25417,7 +25534,7 @@ function useSymmTokenSelectionMarkets(params) {
|
|
|
25417
25534
|
return;
|
|
25418
25535
|
}
|
|
25419
25536
|
const ticker = allTickers[binanceSymbol];
|
|
25420
|
-
tickerSnapshots[symbol] = ticker ? {
|
|
25537
|
+
tickerSnapshots[symbol] = ticker ? { openPrice: ticker.openPrice } : null;
|
|
25421
25538
|
});
|
|
25422
25539
|
return { tickerSnapshots };
|
|
25423
25540
|
},
|
|
@@ -25425,26 +25542,12 @@ function useSymmTokenSelectionMarkets(params) {
|
|
|
25425
25542
|
staleTime: 6e4,
|
|
25426
25543
|
gcTime: 5 * 6e4
|
|
25427
25544
|
});
|
|
25428
|
-
react.useEffect(() => {
|
|
25429
|
-
if (!query.isSuccess || symbolToBinanceMap.size === 0) {
|
|
25430
|
-
return;
|
|
25431
|
-
}
|
|
25432
|
-
const symbolEntries = Array.from(symbolToBinanceMap.entries());
|
|
25433
|
-
symbolEntries.forEach(
|
|
25434
|
-
([symbol, binanceSymbol]) => subscribeSymbol(symbol, binanceSymbol)
|
|
25435
|
-
);
|
|
25436
|
-
return () => {
|
|
25437
|
-
symbolEntries.forEach(
|
|
25438
|
-
([symbol, binanceSymbol]) => unsubscribeSymbol(symbol, binanceSymbol)
|
|
25439
|
-
);
|
|
25440
|
-
};
|
|
25441
|
-
}, [query.isSuccess, symbolsKey, symbolToBinanceMap, subscribeSymbol, unsubscribeSymbol]);
|
|
25442
25545
|
const markets = react.useMemo(() => {
|
|
25443
25546
|
const snapshots = priceQuery.data?.tickerSnapshots ?? {};
|
|
25444
25547
|
return baseMarkets.map((market) => {
|
|
25445
25548
|
const symbol = market.symbol ?? "";
|
|
25446
25549
|
const markPrice = symbol ? liveMarkPrices[symbol] ?? null : null;
|
|
25447
|
-
const prevDayPrice = symbol ? snapshots[symbol]?.
|
|
25550
|
+
const prevDayPrice = symbol ? snapshots[symbol]?.openPrice ?? null : null;
|
|
25448
25551
|
const priceChange24h = markPrice != null && prevDayPrice != null ? markPrice - prevDayPrice : null;
|
|
25449
25552
|
const priceChange24hPercent = markPrice != null && prevDayPrice != null && prevDayPrice !== 0 ? (markPrice - prevDayPrice) / prevDayPrice * 100 : null;
|
|
25450
25553
|
return {
|
|
@@ -25467,6 +25570,61 @@ function useSymmTokenSelectionMarkets(params) {
|
|
|
25467
25570
|
() => new Map(markets.map((market) => [market.id, market])),
|
|
25468
25571
|
[markets]
|
|
25469
25572
|
);
|
|
25573
|
+
react.useEffect(() => {
|
|
25574
|
+
console.debug("[useSymmTokenSelectionMarkets] data flow", {
|
|
25575
|
+
params,
|
|
25576
|
+
query: {
|
|
25577
|
+
status: query.status,
|
|
25578
|
+
fetchStatus: query.fetchStatus,
|
|
25579
|
+
isLoading: query.isLoading,
|
|
25580
|
+
isSuccess: query.isSuccess,
|
|
25581
|
+
isError: query.isError,
|
|
25582
|
+
error: query.error
|
|
25583
|
+
},
|
|
25584
|
+
priceQuery: {
|
|
25585
|
+
status: priceQuery.status,
|
|
25586
|
+
fetchStatus: priceQuery.fetchStatus,
|
|
25587
|
+
isLoading: priceQuery.isLoading,
|
|
25588
|
+
isSuccess: priceQuery.isSuccess,
|
|
25589
|
+
isError: priceQuery.isError,
|
|
25590
|
+
error: priceQuery.error
|
|
25591
|
+
},
|
|
25592
|
+
marketSymbols,
|
|
25593
|
+
symbolsKey,
|
|
25594
|
+
liveMarkPrices,
|
|
25595
|
+
tickerSnapshots: priceQuery.data?.tickerSnapshots ?? {},
|
|
25596
|
+
result: {
|
|
25597
|
+
marketCount: markets.length,
|
|
25598
|
+
markets: markets.map((market) => ({
|
|
25599
|
+
id: market.id,
|
|
25600
|
+
symbol: market.symbol,
|
|
25601
|
+
markPrice: market.markPrice,
|
|
25602
|
+
prevDayPrice: market.prevDayPrice,
|
|
25603
|
+
priceChange24h: market.priceChange24h,
|
|
25604
|
+
priceChange24hPercent: market.priceChange24hPercent
|
|
25605
|
+
}))
|
|
25606
|
+
}
|
|
25607
|
+
});
|
|
25608
|
+
}, [
|
|
25609
|
+
liveMarkPrices,
|
|
25610
|
+
marketSymbols,
|
|
25611
|
+
markets,
|
|
25612
|
+
params,
|
|
25613
|
+
priceQuery.data,
|
|
25614
|
+
priceQuery.error,
|
|
25615
|
+
priceQuery.fetchStatus,
|
|
25616
|
+
priceQuery.isError,
|
|
25617
|
+
priceQuery.isLoading,
|
|
25618
|
+
priceQuery.isSuccess,
|
|
25619
|
+
priceQuery.status,
|
|
25620
|
+
query.error,
|
|
25621
|
+
query.fetchStatus,
|
|
25622
|
+
query.isError,
|
|
25623
|
+
query.isLoading,
|
|
25624
|
+
query.isSuccess,
|
|
25625
|
+
query.status,
|
|
25626
|
+
symbolsKey
|
|
25627
|
+
]);
|
|
25470
25628
|
return {
|
|
25471
25629
|
query,
|
|
25472
25630
|
priceQuery,
|
|
@@ -25481,17 +25639,6 @@ function useSymmTokenMarkPrice(symbol) {
|
|
|
25481
25639
|
const normalizedSymbol = symbol?.trim().toUpperCase() || null;
|
|
25482
25640
|
const resolution = resolveBinanceSymbol(normalizedSymbol ?? "");
|
|
25483
25641
|
const liveMarkPrices = useBinanceMarkPriceStore((state) => state.markPrices);
|
|
25484
|
-
const subscribeSymbol = useBinanceMarkPriceStore((state) => state.subscribeSymbol);
|
|
25485
|
-
const unsubscribeSymbol = useBinanceMarkPriceStore((state) => state.unsubscribeSymbol);
|
|
25486
|
-
react.useEffect(() => {
|
|
25487
|
-
if (!normalizedSymbol || !resolution.binanceSymbol || !resolution.supported) {
|
|
25488
|
-
return;
|
|
25489
|
-
}
|
|
25490
|
-
subscribeSymbol(normalizedSymbol, resolution.binanceSymbol);
|
|
25491
|
-
return () => {
|
|
25492
|
-
unsubscribeSymbol(normalizedSymbol, resolution.binanceSymbol);
|
|
25493
|
-
};
|
|
25494
|
-
}, [normalizedSymbol, resolution.binanceSymbol, resolution.supported, subscribeSymbol, unsubscribeSymbol]);
|
|
25495
25642
|
const markPrice = react.useMemo(() => {
|
|
25496
25643
|
if (!normalizedSymbol) return null;
|
|
25497
25644
|
return liveMarkPrices[normalizedSymbol] ?? null;
|
|
@@ -26018,7 +26165,7 @@ async function fetchTickerSnapshot(symbol) {
|
|
|
26018
26165
|
const ticker = await fetch24hrTicker(resolution.binanceSymbol);
|
|
26019
26166
|
if (!ticker) return null;
|
|
26020
26167
|
return {
|
|
26021
|
-
|
|
26168
|
+
openPrice: ticker.openPrice
|
|
26022
26169
|
};
|
|
26023
26170
|
}
|
|
26024
26171
|
function toSymmTokenMetadata(currentPrice, prevDayPrice) {
|
|
@@ -26044,17 +26191,7 @@ function useSymmTokenSelectionMetadata(selection, options = {}) {
|
|
|
26044
26191
|
const isUnsupported = unsupportedSymbols.length > 0;
|
|
26045
26192
|
const unavailableReason = isUnsupported ? `Binance market data is unavailable for ${unsupportedSymbols.join(", ")}.` : null;
|
|
26046
26193
|
const symbolsKey = [...selectedSymbols].sort().join(",");
|
|
26047
|
-
const symbolToBinanceMap = react.useMemo(
|
|
26048
|
-
() => new Map(
|
|
26049
|
-
selectedSymbols.map(
|
|
26050
|
-
(symbol) => [symbol, resolveBinanceSymbol(symbol).binanceSymbol]
|
|
26051
|
-
).filter((entry) => !!entry[1])
|
|
26052
|
-
),
|
|
26053
|
-
[selectedSymbols]
|
|
26054
|
-
);
|
|
26055
26194
|
const liveMarkPrices = useBinanceMarkPriceStore((state) => state.markPrices);
|
|
26056
|
-
const subscribeSymbol = useBinanceMarkPriceStore((state) => state.subscribeSymbol);
|
|
26057
|
-
const unsubscribeSymbol = useBinanceMarkPriceStore((state) => state.unsubscribeSymbol);
|
|
26058
26195
|
const query = reactQuery.useQuery({
|
|
26059
26196
|
queryKey: ["symm", "chart-metadata", symbolsKey],
|
|
26060
26197
|
queryFn: async () => {
|
|
@@ -26080,28 +26217,7 @@ function useSymmTokenSelectionMetadata(selection, options = {}) {
|
|
|
26080
26217
|
enabled: enabled && selectedSymbols.length > 0 && !isUnsupported,
|
|
26081
26218
|
gcTime: 5 * 6e4
|
|
26082
26219
|
});
|
|
26083
|
-
react.
|
|
26084
|
-
if (!enabled || isUnsupported || selectedSymbols.length === 0) {
|
|
26085
|
-
return;
|
|
26086
|
-
}
|
|
26087
|
-
const symbolEntries = Array.from(symbolToBinanceMap.entries());
|
|
26088
|
-
symbolEntries.forEach(
|
|
26089
|
-
([symbol, binanceSymbol]) => subscribeSymbol(symbol, binanceSymbol)
|
|
26090
|
-
);
|
|
26091
|
-
return () => {
|
|
26092
|
-
symbolEntries.forEach(
|
|
26093
|
-
([symbol, binanceSymbol]) => unsubscribeSymbol(symbol, binanceSymbol)
|
|
26094
|
-
);
|
|
26095
|
-
};
|
|
26096
|
-
}, [
|
|
26097
|
-
enabled,
|
|
26098
|
-
isUnsupported,
|
|
26099
|
-
selectedSymbols.length,
|
|
26100
|
-
symbolToBinanceMap,
|
|
26101
|
-
subscribeSymbol,
|
|
26102
|
-
unsubscribeSymbol
|
|
26103
|
-
]);
|
|
26104
|
-
return react.useMemo(() => {
|
|
26220
|
+
const result = react.useMemo(() => {
|
|
26105
26221
|
const tickerSnapshots = query.data?.tickerSnapshots ?? {};
|
|
26106
26222
|
const longMeta = {};
|
|
26107
26223
|
const shortMeta = {};
|
|
@@ -26109,12 +26225,12 @@ function useSymmTokenSelectionMetadata(selection, options = {}) {
|
|
|
26109
26225
|
for (const token of longTokens) {
|
|
26110
26226
|
const currentPrice = liveMarkPrices[token.symbol];
|
|
26111
26227
|
const ticker = tickerSnapshots[token.symbol];
|
|
26112
|
-
longMeta[token.symbol] = currentPrice != null && ticker ? toSymmTokenMetadata(currentPrice, ticker.
|
|
26228
|
+
longMeta[token.symbol] = currentPrice != null && ticker ? toSymmTokenMetadata(currentPrice, ticker.openPrice) : null;
|
|
26113
26229
|
}
|
|
26114
26230
|
for (const token of shortTokens) {
|
|
26115
26231
|
const currentPrice = liveMarkPrices[token.symbol];
|
|
26116
26232
|
const ticker = tickerSnapshots[token.symbol];
|
|
26117
|
-
shortMeta[token.symbol] = currentPrice != null && ticker ? toSymmTokenMetadata(currentPrice, ticker.
|
|
26233
|
+
shortMeta[token.symbol] = currentPrice != null && ticker ? toSymmTokenMetadata(currentPrice, ticker.openPrice) : null;
|
|
26118
26234
|
}
|
|
26119
26235
|
const allLongReady = longTokens.every(
|
|
26120
26236
|
(t) => longMeta[t.symbol]?.currentPrice != null
|
|
@@ -26160,6 +26276,53 @@ function useSymmTokenSelectionMetadata(selection, options = {}) {
|
|
|
26160
26276
|
selectedSymbols.length,
|
|
26161
26277
|
liveMarkPrices
|
|
26162
26278
|
]);
|
|
26279
|
+
react.useEffect(() => {
|
|
26280
|
+
console.debug("[useSymmTokenSelectionMetadata] data flow", {
|
|
26281
|
+
selection,
|
|
26282
|
+
options,
|
|
26283
|
+
query: {
|
|
26284
|
+
status: query.status,
|
|
26285
|
+
fetchStatus: query.fetchStatus,
|
|
26286
|
+
isLoading: query.isLoading,
|
|
26287
|
+
isSuccess: query.isSuccess,
|
|
26288
|
+
isError: query.isError,
|
|
26289
|
+
error: query.error
|
|
26290
|
+
},
|
|
26291
|
+
selectedSymbols,
|
|
26292
|
+
unsupportedSymbols,
|
|
26293
|
+
unavailableReason,
|
|
26294
|
+
liveMarkPrices,
|
|
26295
|
+
tickerSnapshots: query.data?.tickerSnapshots ?? {},
|
|
26296
|
+
result: {
|
|
26297
|
+
isLoading: result.isLoading,
|
|
26298
|
+
isPriceDataReady: result.isPriceDataReady,
|
|
26299
|
+
isUnsupported: result.isUnsupported,
|
|
26300
|
+
longTokensMetadata: result.longTokensMetadata,
|
|
26301
|
+
shortTokensMetadata: result.shortTokensMetadata,
|
|
26302
|
+
weightedRatio: result.weightedRatio,
|
|
26303
|
+
weightedRatio24h: result.weightedRatio24h,
|
|
26304
|
+
priceRatio: result.priceRatio,
|
|
26305
|
+
priceRatio24h: result.priceRatio24h,
|
|
26306
|
+
sumNetFunding: result.sumNetFunding
|
|
26307
|
+
}
|
|
26308
|
+
});
|
|
26309
|
+
}, [
|
|
26310
|
+
liveMarkPrices,
|
|
26311
|
+
options,
|
|
26312
|
+
query.data,
|
|
26313
|
+
query.error,
|
|
26314
|
+
query.fetchStatus,
|
|
26315
|
+
query.isError,
|
|
26316
|
+
query.isLoading,
|
|
26317
|
+
query.isSuccess,
|
|
26318
|
+
query.status,
|
|
26319
|
+
result,
|
|
26320
|
+
selectedSymbols,
|
|
26321
|
+
selection,
|
|
26322
|
+
unavailableReason,
|
|
26323
|
+
unsupportedSymbols
|
|
26324
|
+
]);
|
|
26325
|
+
return result;
|
|
26163
26326
|
}
|
|
26164
26327
|
|
|
26165
26328
|
// src/utils/binance-intervals.ts
|
|
@@ -26188,10 +26351,13 @@ function toBinanceInterval(interval) {
|
|
|
26188
26351
|
}
|
|
26189
26352
|
|
|
26190
26353
|
// src/react/hooks/use-symm-chart-candles.ts
|
|
26354
|
+
function areIntervalsEqual(currentInterval, nextInterval) {
|
|
26355
|
+
return currentInterval === toBinanceInterval(nextInterval);
|
|
26356
|
+
}
|
|
26191
26357
|
async function fetchSymbolKlines(symbol, interval, start, end) {
|
|
26192
26358
|
const binanceSymbol = toBinanceSymbol(symbol);
|
|
26193
26359
|
if (!binanceSymbol) return [];
|
|
26194
|
-
const klines = await
|
|
26360
|
+
const klines = await fetchKlines(
|
|
26195
26361
|
binanceSymbol,
|
|
26196
26362
|
toBinanceInterval(interval),
|
|
26197
26363
|
start,
|
|
@@ -26352,6 +26518,15 @@ function useSymmChartCandles(selection) {
|
|
|
26352
26518
|
wsUnsubsRef.current.push(unsub);
|
|
26353
26519
|
}
|
|
26354
26520
|
}, [emitRealtimeBar, isUnsupported, selectedSymbols]);
|
|
26521
|
+
const setRealtimeInterval = react.useCallback((interval) => {
|
|
26522
|
+
if (areIntervalsEqual(activeIntervalRef.current, interval)) {
|
|
26523
|
+
return;
|
|
26524
|
+
}
|
|
26525
|
+
activeIntervalRef.current = toBinanceInterval(interval);
|
|
26526
|
+
if (listenersRef.current.size > 0) {
|
|
26527
|
+
setupWsSubscriptions();
|
|
26528
|
+
}
|
|
26529
|
+
}, [setupWsSubscriptions]);
|
|
26355
26530
|
react.useEffect(() => {
|
|
26356
26531
|
if (listenersRef.current.size > 0) {
|
|
26357
26532
|
setupWsSubscriptions();
|
|
@@ -26363,6 +26538,7 @@ function useSymmChartCandles(selection) {
|
|
|
26363
26538
|
}, [setupWsSubscriptions]);
|
|
26364
26539
|
const fetchBasketCandles = react.useCallback(
|
|
26365
26540
|
async (start, end, interval) => {
|
|
26541
|
+
setRealtimeInterval(interval);
|
|
26366
26542
|
const longSymbol = longTokens[0]?.symbol;
|
|
26367
26543
|
const shortSymbol = shortTokens[0]?.symbol;
|
|
26368
26544
|
if (isUnsupported) {
|
|
@@ -26388,18 +26564,20 @@ function useSymmChartCandles(selection) {
|
|
|
26388
26564
|
Object.fromEntries(entries)
|
|
26389
26565
|
);
|
|
26390
26566
|
},
|
|
26391
|
-
[isUnsupported, longTokens, shortTokens]
|
|
26567
|
+
[isUnsupported, longTokens, setRealtimeInterval, shortTokens]
|
|
26392
26568
|
);
|
|
26393
26569
|
const fetchPerformanceCandles = react.useCallback(
|
|
26394
26570
|
async (start, end, interval, symbol) => {
|
|
26571
|
+
setRealtimeInterval(interval);
|
|
26395
26572
|
const parts = symbol.split(" ");
|
|
26396
26573
|
const assetSymbol = parts.length >= 2 ? parts.slice(1).join(" ") : symbol;
|
|
26397
26574
|
return fetchSymbolKlines(assetSymbol, interval, start, end);
|
|
26398
26575
|
},
|
|
26399
|
-
[]
|
|
26576
|
+
[setRealtimeInterval]
|
|
26400
26577
|
);
|
|
26401
26578
|
const fetchOverallPerformanceCandles = react.useCallback(
|
|
26402
26579
|
async (start, end, interval) => {
|
|
26580
|
+
setRealtimeInterval(interval);
|
|
26403
26581
|
const longSymbol = longTokens[0]?.symbol;
|
|
26404
26582
|
const shortSymbol = shortTokens[0]?.symbol;
|
|
26405
26583
|
if (isUnsupported) return [];
|
|
@@ -26456,7 +26634,7 @@ function useSymmChartCandles(selection) {
|
|
|
26456
26634
|
}
|
|
26457
26635
|
return result;
|
|
26458
26636
|
},
|
|
26459
|
-
[isUnsupported, longTokens, shortTokens]
|
|
26637
|
+
[isUnsupported, longTokens, setRealtimeInterval, shortTokens]
|
|
26460
26638
|
);
|
|
26461
26639
|
const addRealtimeListener = react.useCallback((cb) => {
|
|
26462
26640
|
const id = Math.random().toString(36).slice(2);
|