@pear-protocol/symmio-client 0.2.26 → 0.2.28
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.d.mts +14 -1
- package/dist/react/index.d.ts +14 -1
- package/dist/react/index.js +1299 -1113
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +1299 -1114
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/provider.js +426 -0
- package/dist/react/provider.js.map +1 -1
- package/dist/react/provider.mjs +427 -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,342 +16,773 @@ 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/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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
104
|
+
var BinanceWsManager = class {
|
|
105
|
+
ws = null;
|
|
106
|
+
streams = /* @__PURE__ */ new Map();
|
|
107
|
+
reconnectAttempt = 0;
|
|
108
|
+
reconnectTimer = null;
|
|
109
|
+
intentionalClose = false;
|
|
110
|
+
pendingSubscribes = [];
|
|
111
|
+
idCounter = 0;
|
|
112
|
+
/**
|
|
113
|
+
* Subscribe to a kline stream. Returns an unsubscribe function.
|
|
114
|
+
*/
|
|
115
|
+
subscribeKline(symbol, interval, cb) {
|
|
116
|
+
const streamName = `${symbol.toLowerCase()}@kline_${interval}`;
|
|
117
|
+
const id = this.generateId();
|
|
118
|
+
const wrappedCb = (raw) => {
|
|
119
|
+
const k = raw.k;
|
|
120
|
+
if (!k) return;
|
|
121
|
+
cb({
|
|
122
|
+
symbol: raw.s,
|
|
123
|
+
interval: k.i,
|
|
124
|
+
openTime: k.t,
|
|
125
|
+
closeTime: k.T,
|
|
126
|
+
open: parseFloat(k.o),
|
|
127
|
+
high: parseFloat(k.h),
|
|
128
|
+
low: parseFloat(k.l),
|
|
129
|
+
close: parseFloat(k.c),
|
|
130
|
+
volume: parseFloat(k.v),
|
|
131
|
+
isFinal: k.x
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
this.addStreamCallback(streamName, id, wrappedCb);
|
|
135
|
+
return () => this.removeStreamCallback(streamName, id);
|
|
196
136
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Subscribe to a mark price stream (1s updates). Returns an unsubscribe function.
|
|
139
|
+
*/
|
|
140
|
+
subscribeMarkPrice(symbol, cb) {
|
|
141
|
+
const streamName = `${symbol.toLowerCase()}@markPrice@1s`;
|
|
142
|
+
const id = this.generateId();
|
|
143
|
+
const wrappedCb = (raw) => {
|
|
144
|
+
cb({
|
|
145
|
+
symbol: normalizeBaseSymbol(raw.s),
|
|
146
|
+
markPrice: parseFloat(raw.p),
|
|
147
|
+
indexPrice: parseFloat(raw.i),
|
|
148
|
+
time: raw.E
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
this.addStreamCallback(streamName, id, wrappedCb);
|
|
152
|
+
return () => this.removeStreamCallback(streamName, id);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Subscribe to the all-market mark price stream (1s updates).
|
|
156
|
+
* Returns an unsubscribe function.
|
|
157
|
+
*/
|
|
158
|
+
subscribeAllMarkPrices(cb) {
|
|
159
|
+
const streamName = "!markPrice@arr@1s";
|
|
160
|
+
const id = this.generateId();
|
|
161
|
+
const wrappedCb = (raw) => {
|
|
162
|
+
cb(
|
|
163
|
+
extractTickers(raw).map((entry) => ({
|
|
164
|
+
symbol: normalizeBaseSymbol(entry.s),
|
|
165
|
+
markPrice: parseFloat(entry.p),
|
|
166
|
+
indexPrice: parseFloat(entry.i),
|
|
167
|
+
time: entry.E
|
|
168
|
+
}))
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
this.addStreamCallback(streamName, id, wrappedCb);
|
|
172
|
+
return () => this.removeStreamCallback(streamName, id);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Destroy the manager and close the connection.
|
|
176
|
+
*/
|
|
177
|
+
destroy() {
|
|
178
|
+
this.intentionalClose = true;
|
|
179
|
+
if (this.reconnectTimer) {
|
|
180
|
+
clearTimeout(this.reconnectTimer);
|
|
181
|
+
this.reconnectTimer = null;
|
|
182
|
+
}
|
|
183
|
+
if (this.ws) {
|
|
184
|
+
this.ws.close();
|
|
185
|
+
this.ws = null;
|
|
186
|
+
}
|
|
187
|
+
this.streams.clear();
|
|
188
|
+
}
|
|
189
|
+
// --- Private ---
|
|
190
|
+
generateId() {
|
|
191
|
+
return `sub_${++this.idCounter}_${Date.now()}`;
|
|
192
|
+
}
|
|
193
|
+
addStreamCallback(streamName, id, cb) {
|
|
194
|
+
let sub = this.streams.get(streamName);
|
|
195
|
+
const isNew = !sub;
|
|
196
|
+
if (!sub) {
|
|
197
|
+
sub = { callbacks: /* @__PURE__ */ new Map() };
|
|
198
|
+
this.streams.set(streamName, sub);
|
|
199
|
+
}
|
|
200
|
+
sub.callbacks.set(id, cb);
|
|
201
|
+
if (isNew) {
|
|
202
|
+
this.ensureConnected();
|
|
203
|
+
this.sendSubscribe([streamName]);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
removeStreamCallback(streamName, id) {
|
|
207
|
+
const sub = this.streams.get(streamName);
|
|
208
|
+
if (!sub) return;
|
|
209
|
+
sub.callbacks.delete(id);
|
|
210
|
+
if (sub.callbacks.size === 0) {
|
|
211
|
+
this.streams.delete(streamName);
|
|
212
|
+
this.sendUnsubscribe([streamName]);
|
|
213
|
+
if (this.streams.size === 0 && this.ws) {
|
|
214
|
+
this.intentionalClose = true;
|
|
215
|
+
this.ws.close();
|
|
216
|
+
this.ws = null;
|
|
217
|
+
this.intentionalClose = false;
|
|
241
218
|
}
|
|
242
|
-
}
|
|
243
|
-
},
|
|
244
|
-
getToken: (address, chainId) => {
|
|
245
|
-
const key = symmAuthTokenKey(address, chainId);
|
|
246
|
-
return get().tokensByKey[key] ?? null;
|
|
247
|
-
},
|
|
248
|
-
clearToken: (address, chainId) => {
|
|
249
|
-
const key = symmAuthTokenKey(address, chainId);
|
|
250
|
-
set((state) => {
|
|
251
|
-
if (!(key in state.tokensByKey)) return state;
|
|
252
|
-
const next = { ...state.tokensByKey };
|
|
253
|
-
delete next[key];
|
|
254
|
-
return { tokensByKey: next };
|
|
255
|
-
});
|
|
256
|
-
},
|
|
257
|
-
clearAll: () => {
|
|
258
|
-
set({ tokensByKey: {} });
|
|
219
|
+
}
|
|
259
220
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
function useSymmAuth(params) {
|
|
264
|
-
const ctx = useSymmContext();
|
|
265
|
-
const address = params?.address ?? ctx.address;
|
|
266
|
-
const chainId = params?.chainId ?? ctx.chainId ?? 42161;
|
|
267
|
-
const walletClient = params?.walletClient ?? ctx.walletClient;
|
|
268
|
-
const siweDomain = params?.siweDomain;
|
|
269
|
-
const accessToken = useSymmAuthStore((state) => {
|
|
270
|
-
if (address) {
|
|
271
|
-
return state.tokensByKey[symmAuthTokenKey(address, chainId)] ?? null;
|
|
221
|
+
ensureConnected() {
|
|
222
|
+
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
|
|
223
|
+
return;
|
|
272
224
|
}
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
|
|
284
|
-
const inMemory = getToken(resolvedAccountAddress, chainId);
|
|
285
|
-
if (inMemory) {
|
|
286
|
-
return inMemory;
|
|
225
|
+
this.connect();
|
|
226
|
+
}
|
|
227
|
+
connect() {
|
|
228
|
+
if (typeof WebSocket === "undefined") return;
|
|
229
|
+
this.intentionalClose = false;
|
|
230
|
+
this.ws = new WebSocket(BINANCE_WS_URL);
|
|
231
|
+
this.ws.onopen = () => {
|
|
232
|
+
this.reconnectAttempt = 0;
|
|
233
|
+
const activeStreams = Array.from(this.streams.keys());
|
|
234
|
+
if (activeStreams.length > 0) {
|
|
235
|
+
this.sendSubscribe(activeStreams);
|
|
287
236
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
return cached;
|
|
237
|
+
if (this.pendingSubscribes.length > 0) {
|
|
238
|
+
this.sendSubscribe(this.pendingSubscribes);
|
|
239
|
+
this.pendingSubscribes = [];
|
|
292
240
|
}
|
|
241
|
+
};
|
|
242
|
+
this.ws.onmessage = (event) => {
|
|
293
243
|
try {
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
address,
|
|
297
|
-
resolvedAccountAddress,
|
|
298
|
-
chainId,
|
|
299
|
-
siweDomain
|
|
300
|
-
);
|
|
301
|
-
setToken(resolvedAccountAddress, chainId, token);
|
|
302
|
-
return token;
|
|
244
|
+
const data = JSON.parse(event.data);
|
|
245
|
+
this.handleMessage(data);
|
|
303
246
|
} catch {
|
|
304
|
-
clearToken(resolvedAccountAddress, chainId);
|
|
305
|
-
return null;
|
|
306
247
|
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
const previousChainId = previousChainIdRef.current;
|
|
319
|
-
const addressChanged = previousAddress !== address;
|
|
320
|
-
const chainChanged = previousChainId !== chainId;
|
|
321
|
-
previousAddressRef.current = address;
|
|
322
|
-
previousChainIdRef.current = chainId;
|
|
323
|
-
if (!address || !walletClient) {
|
|
248
|
+
};
|
|
249
|
+
this.ws.onclose = () => {
|
|
250
|
+
if (this.intentionalClose) return;
|
|
251
|
+
this.scheduleReconnect();
|
|
252
|
+
};
|
|
253
|
+
this.ws.onerror = () => {
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
handleMessage(data) {
|
|
257
|
+
if (Array.isArray(data)) {
|
|
258
|
+
this.dispatchToStream("!markPrice@arr@1s", data);
|
|
324
259
|
return;
|
|
325
260
|
}
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
|
|
261
|
+
if (data.e === "kline") {
|
|
262
|
+
const k = data.k;
|
|
263
|
+
const streamName = `${data.s.toLowerCase()}@kline_${k.i}`;
|
|
264
|
+
this.dispatchToStream(streamName, data);
|
|
265
|
+
} else if (data.e === "markPriceUpdate") {
|
|
266
|
+
const streamName = `${data.s.toLowerCase()}@markPrice@1s`;
|
|
267
|
+
this.dispatchToStream(streamName, data);
|
|
329
268
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
269
|
+
}
|
|
270
|
+
dispatchToStream(streamName, data) {
|
|
271
|
+
const sub = this.streams.get(streamName);
|
|
272
|
+
if (!sub) return;
|
|
273
|
+
sub.callbacks.forEach((cb) => {
|
|
274
|
+
try {
|
|
275
|
+
cb(data);
|
|
276
|
+
} catch {
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
sendSubscribe(streams) {
|
|
281
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
282
|
+
this.pendingSubscribes.push(...streams);
|
|
283
|
+
return;
|
|
335
284
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
285
|
+
this.ws.send(JSON.stringify({
|
|
286
|
+
method: "SUBSCRIBE",
|
|
287
|
+
params: streams,
|
|
288
|
+
id: Date.now()
|
|
289
|
+
}));
|
|
290
|
+
}
|
|
291
|
+
sendUnsubscribe(streams) {
|
|
292
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
293
|
+
this.pendingSubscribes = this.pendingSubscribes.filter(
|
|
294
|
+
(s) => !streams.includes(s)
|
|
295
|
+
);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
this.ws.send(JSON.stringify({
|
|
299
|
+
method: "UNSUBSCRIBE",
|
|
300
|
+
params: streams,
|
|
301
|
+
id: Date.now()
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
scheduleReconnect() {
|
|
305
|
+
if (this.reconnectTimer) return;
|
|
306
|
+
if (this.streams.size === 0) return;
|
|
307
|
+
const delay = RECONNECT_DELAYS[Math.min(this.reconnectAttempt, RECONNECT_DELAYS.length - 1)];
|
|
308
|
+
this.reconnectAttempt++;
|
|
309
|
+
this.reconnectTimer = setTimeout(() => {
|
|
310
|
+
this.reconnectTimer = null;
|
|
311
|
+
this.connect();
|
|
312
|
+
}, delay);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
var _instance = null;
|
|
316
|
+
function getBinanceWsManager() {
|
|
317
|
+
if (!_instance) {
|
|
318
|
+
_instance = new BinanceWsManager();
|
|
319
|
+
}
|
|
320
|
+
return _instance;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/react/stores/use-binance-mark-price-store.ts
|
|
324
|
+
var refCounts = /* @__PURE__ */ new Map();
|
|
325
|
+
var streamSymbols = /* @__PURE__ */ new Map();
|
|
326
|
+
var allMarkPricesRefCount = 0;
|
|
327
|
+
var allMarkPricesUnsubscribe = null;
|
|
328
|
+
var STABLE_QUOTES2 = ["USDT0", "USDT", "USDC", "USDE", "USDH", "USD"];
|
|
329
|
+
function normalizeBinanceSymbol(symbol) {
|
|
330
|
+
const normalized = symbol.toUpperCase().trim();
|
|
331
|
+
for (const quote of STABLE_QUOTES2) {
|
|
332
|
+
if (normalized.endsWith(quote) && normalized.length > quote.length) {
|
|
333
|
+
return normalized.slice(0, -quote.length);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return normalized;
|
|
337
|
+
}
|
|
338
|
+
function getNextRefCount(binanceSymbol) {
|
|
339
|
+
return (refCounts.get(binanceSymbol) ?? 0) + 1;
|
|
340
|
+
}
|
|
341
|
+
function getPrevRefCount(binanceSymbol) {
|
|
342
|
+
return Math.max(0, (refCounts.get(binanceSymbol) ?? 0) - 1);
|
|
343
|
+
}
|
|
344
|
+
var useBinanceMarkPriceStore = zustand.create((set) => ({
|
|
345
|
+
markPrices: {},
|
|
346
|
+
subscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
|
|
347
|
+
const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
|
|
348
|
+
const nextRefCount = getNextRefCount(binanceSymbol);
|
|
349
|
+
refCounts.set(binanceSymbol, nextRefCount);
|
|
350
|
+
const symbols = streamSymbols.get(binanceSymbol) ?? /* @__PURE__ */ new Set();
|
|
351
|
+
symbols.add(symmSymbol);
|
|
352
|
+
streamSymbols.set(binanceSymbol, symbols);
|
|
353
|
+
if (allMarkPricesRefCount === 0) {
|
|
354
|
+
const wsManager = getBinanceWsManager();
|
|
355
|
+
allMarkPricesUnsubscribe = wsManager.subscribeAllMarkPrices((entries) => {
|
|
356
|
+
set((state) => {
|
|
357
|
+
let nextMarkPrices = null;
|
|
358
|
+
entries.forEach((entry) => {
|
|
359
|
+
const canonicalSymbol = normalizeBinanceSymbol(entry.symbol);
|
|
360
|
+
const mappedSymbols = streamSymbols.get(canonicalSymbol);
|
|
361
|
+
if (!mappedSymbols || mappedSymbols.size === 0) return;
|
|
362
|
+
nextMarkPrices ??= { ...state.markPrices };
|
|
363
|
+
mappedSymbols.forEach((mappedSymbol) => {
|
|
364
|
+
nextMarkPrices[mappedSymbol] = entry.markPrice;
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
return nextMarkPrices ? { markPrices: nextMarkPrices } : state;
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
allMarkPricesRefCount += 1;
|
|
372
|
+
},
|
|
373
|
+
unsubscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
|
|
374
|
+
const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
|
|
375
|
+
const symbols = streamSymbols.get(binanceSymbol);
|
|
376
|
+
if (symbols) {
|
|
377
|
+
symbols.delete(symmSymbol);
|
|
378
|
+
if (symbols.size === 0) {
|
|
379
|
+
streamSymbols.delete(binanceSymbol);
|
|
380
|
+
} else {
|
|
381
|
+
streamSymbols.set(binanceSymbol, symbols);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const nextRefCount = getPrevRefCount(binanceSymbol);
|
|
385
|
+
if (nextRefCount === 0) {
|
|
386
|
+
refCounts.delete(binanceSymbol);
|
|
387
|
+
} else {
|
|
388
|
+
refCounts.set(binanceSymbol, nextRefCount);
|
|
389
|
+
}
|
|
390
|
+
allMarkPricesRefCount = Math.max(0, allMarkPricesRefCount - 1);
|
|
391
|
+
if (allMarkPricesRefCount === 0) {
|
|
392
|
+
allMarkPricesUnsubscribe?.();
|
|
393
|
+
allMarkPricesUnsubscribe = null;
|
|
394
|
+
}
|
|
395
|
+
set((state) => {
|
|
396
|
+
if (state.markPrices[symmSymbol] == null) return state;
|
|
397
|
+
const nextMarkPrices = { ...state.markPrices };
|
|
398
|
+
delete nextMarkPrices[symmSymbol];
|
|
399
|
+
return { markPrices: nextMarkPrices };
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}));
|
|
403
|
+
|
|
404
|
+
// src/react/hooks/use-binance-ws.ts
|
|
405
|
+
function useBinanceWs(params) {
|
|
406
|
+
const { symmCoreClient, chainId } = params;
|
|
407
|
+
const subscribeSymbol = useBinanceMarkPriceStore((state) => state.subscribeSymbol);
|
|
408
|
+
const unsubscribeSymbol = useBinanceMarkPriceStore((state) => state.unsubscribeSymbol);
|
|
409
|
+
react.useEffect(() => {
|
|
410
|
+
if (!symmCoreClient) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
let cancelled = false;
|
|
414
|
+
let subscribedPairs = [];
|
|
415
|
+
const run = async () => {
|
|
416
|
+
try {
|
|
417
|
+
const result = await symmCoreClient.markets.listSymmHedger({ chainId });
|
|
418
|
+
if (cancelled) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const uniqueSymbols = Array.from(
|
|
422
|
+
new Set(
|
|
423
|
+
result.markets.map((market) => market.symbol).filter((symbol) => !!symbol)
|
|
424
|
+
)
|
|
425
|
+
);
|
|
426
|
+
subscribedPairs = uniqueSymbols.map((symbol) => {
|
|
427
|
+
const binanceSymbol = resolveBinanceSymbol(symbol).binanceSymbol;
|
|
428
|
+
if (!binanceSymbol) return null;
|
|
429
|
+
return [symbol, binanceSymbol];
|
|
430
|
+
}).filter((entry) => !!entry);
|
|
431
|
+
subscribedPairs.forEach(([symbol, binanceSymbol]) => {
|
|
432
|
+
subscribeSymbol(symbol, binanceSymbol);
|
|
433
|
+
});
|
|
434
|
+
} catch {
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
void run();
|
|
438
|
+
return () => {
|
|
439
|
+
cancelled = true;
|
|
440
|
+
subscribedPairs.forEach(([symbol, binanceSymbol]) => {
|
|
441
|
+
unsubscribeSymbol(symbol, binanceSymbol);
|
|
442
|
+
});
|
|
443
|
+
};
|
|
444
|
+
}, [symmCoreClient, chainId, subscribeSymbol, unsubscribeSymbol]);
|
|
445
|
+
}
|
|
446
|
+
function SymmProvider({
|
|
447
|
+
chainId = 42161,
|
|
448
|
+
address,
|
|
449
|
+
symmCoreConfig = {
|
|
450
|
+
apiUrl: "https://nginx-server-staging.up.railway.app",
|
|
451
|
+
wsUrl: "wss://nginx-server-staging.up.railway.app"
|
|
452
|
+
},
|
|
453
|
+
symmioConfig,
|
|
454
|
+
children
|
|
455
|
+
}) {
|
|
456
|
+
const symmCoreClient = react.useMemo(() => {
|
|
457
|
+
return symmCore.createSymmSDK({
|
|
458
|
+
apiUrl: symmCoreConfig.apiUrl,
|
|
459
|
+
wsUrl: symmCoreConfig.wsUrl,
|
|
460
|
+
defaultChainId: chainId
|
|
461
|
+
});
|
|
462
|
+
}, [chainId, symmCoreConfig.apiUrl, symmCoreConfig.wsUrl]);
|
|
463
|
+
const value = react.useMemo(
|
|
464
|
+
() => ({
|
|
465
|
+
symmCoreClient,
|
|
466
|
+
chainId,
|
|
467
|
+
address,
|
|
468
|
+
symmioConfig
|
|
469
|
+
}),
|
|
470
|
+
[symmCoreClient, chainId, address, symmioConfig]
|
|
471
|
+
);
|
|
472
|
+
useBinanceWs({
|
|
473
|
+
symmCoreClient,
|
|
474
|
+
chainId
|
|
475
|
+
});
|
|
476
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SymmContext.Provider, { value, children });
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/react/hooks/use-symm-core-client.ts
|
|
480
|
+
function useSymmCoreClient() {
|
|
481
|
+
return useSymmContext().symmCoreClient;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// src/constants/defaults.ts
|
|
485
|
+
var GAS_MARGIN_PERCENTAGE = 20n;
|
|
486
|
+
var MUON_BASE_URLS = [
|
|
487
|
+
"https://muon-oracle1.rasa.capital/v1/",
|
|
488
|
+
"https://muon-oracle2.rasa.capital/v1/",
|
|
489
|
+
"https://muon-oracle3.rasa.capital/v1/",
|
|
490
|
+
"https://muon-oracle4.rasa.capital/v1/"
|
|
491
|
+
];
|
|
492
|
+
var MUON_APP_NAME = "symmio";
|
|
493
|
+
var MUON_REQUEST_TIMEOUT = 15e3;
|
|
494
|
+
var HEDGER_BASE_URLS = {
|
|
495
|
+
[42161 /* ARBITRUM */]: "https://www.perps-streaming.com/v1/42161a/0x6273242a7E88b3De90822b31648C212215caaFE4",
|
|
496
|
+
[8453 /* BASE */]: "https://www.perps-streaming.com/v1/8453a/0x6273242a7E88b3De90822b31648C212215caaFE4"
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// src/actions/instant.ts
|
|
500
|
+
function getHedgerBaseUrl(chainId) {
|
|
501
|
+
const baseUrl = HEDGER_BASE_URLS[chainId];
|
|
502
|
+
if (!baseUrl) {
|
|
503
|
+
throw new Error(`No hedger base URL configured for chain ${chainId}.`);
|
|
504
|
+
}
|
|
505
|
+
return baseUrl;
|
|
506
|
+
}
|
|
507
|
+
function createSiweMessage(params) {
|
|
508
|
+
const version = params.version ?? "1";
|
|
509
|
+
const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
510
|
+
const expirationTime = new Date(
|
|
511
|
+
Date.now() + 30 * 24 * 60 * 60 * 1e3
|
|
512
|
+
).toISOString();
|
|
513
|
+
const message = [
|
|
514
|
+
`${params.domain} wants you to sign in with your Ethereum account:`,
|
|
515
|
+
params.address,
|
|
516
|
+
"",
|
|
517
|
+
params.statement,
|
|
518
|
+
"",
|
|
519
|
+
`URI: ${params.uri}`,
|
|
520
|
+
`Version: ${version}`,
|
|
521
|
+
`Chain ID: ${params.chainId}`,
|
|
522
|
+
`Nonce: ${params.nonce}`,
|
|
523
|
+
`Issued At: ${issuedAt}`,
|
|
524
|
+
`Expiration Time: ${expirationTime}`
|
|
525
|
+
].join("\n");
|
|
526
|
+
return { message, issuedAt, expirationTime };
|
|
527
|
+
}
|
|
528
|
+
async function getNonce(chainId, subAccount) {
|
|
529
|
+
const hedgerBaseUrl = getHedgerBaseUrl(chainId);
|
|
530
|
+
const url = new URL(`nonce/${subAccount}`, hedgerBaseUrl).href;
|
|
531
|
+
const response = await fetch(url);
|
|
532
|
+
if (!response.ok) {
|
|
533
|
+
throw new Error(`Failed to fetch nonce: ${response.statusText}`);
|
|
534
|
+
}
|
|
535
|
+
const data = await response.json();
|
|
536
|
+
return data.nonce;
|
|
537
|
+
}
|
|
538
|
+
async function login(chainId, params) {
|
|
539
|
+
const hedgerBaseUrl = getHedgerBaseUrl(chainId);
|
|
540
|
+
const url = new URL("login", hedgerBaseUrl).href;
|
|
541
|
+
const body = {
|
|
542
|
+
account_address: params.accountAddress,
|
|
543
|
+
expiration_time: params.expirationTime,
|
|
544
|
+
issued_at: params.issuedAt,
|
|
545
|
+
signature: params.signature,
|
|
546
|
+
nonce: params.nonce
|
|
547
|
+
};
|
|
548
|
+
const response = await fetch(url, {
|
|
549
|
+
method: "POST",
|
|
550
|
+
headers: { "Content-Type": "application/json" },
|
|
551
|
+
body: JSON.stringify(body)
|
|
552
|
+
});
|
|
553
|
+
if (!response.ok) {
|
|
554
|
+
const errorData = await response.json().catch(() => null);
|
|
555
|
+
throw new Error(
|
|
556
|
+
errorData?.error_message ?? `Login failed: ${response.statusText}`
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
const data = await response.json();
|
|
560
|
+
if (!data.access_token) {
|
|
561
|
+
throw new Error("No access token received");
|
|
562
|
+
}
|
|
563
|
+
return {
|
|
564
|
+
accessToken: data.access_token,
|
|
565
|
+
expirationTime: params.expirationTime,
|
|
566
|
+
issuedAt: params.issuedAt
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// src/react/auth.ts
|
|
571
|
+
var TOKEN_STORAGE_PREFIX = "symm_access_token";
|
|
572
|
+
var TOKEN_STORAGE_VERSION = 1;
|
|
573
|
+
var tokenCache = /* @__PURE__ */ new Map();
|
|
574
|
+
function cacheKey(address, chainId) {
|
|
575
|
+
return `${address}:${chainId}`;
|
|
576
|
+
}
|
|
577
|
+
function storageKey(address, chainId) {
|
|
578
|
+
return `${TOKEN_STORAGE_PREFIX}:${TOKEN_STORAGE_VERSION}:${cacheKey(address, chainId)}`;
|
|
579
|
+
}
|
|
580
|
+
function readStoredToken(address, chainId) {
|
|
581
|
+
if (typeof window === "undefined") return null;
|
|
582
|
+
try {
|
|
583
|
+
const raw = window.localStorage.getItem(storageKey(address, chainId));
|
|
584
|
+
if (!raw) return null;
|
|
585
|
+
const parsed = JSON.parse(raw);
|
|
586
|
+
if (!parsed || typeof parsed.token !== "string" || typeof parsed.expiresAt !== "number") {
|
|
587
|
+
window.localStorage.removeItem(storageKey(address, chainId));
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
if (Date.now() >= parsed.expiresAt) {
|
|
591
|
+
window.localStorage.removeItem(storageKey(address, chainId));
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
return parsed;
|
|
595
|
+
} catch {
|
|
596
|
+
window.localStorage.removeItem(storageKey(address, chainId));
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
function writeStoredToken(address, chainId, cached) {
|
|
601
|
+
if (typeof window === "undefined") return;
|
|
602
|
+
try {
|
|
603
|
+
window.localStorage.setItem(
|
|
604
|
+
storageKey(address, chainId),
|
|
605
|
+
JSON.stringify(cached)
|
|
606
|
+
);
|
|
607
|
+
} catch {
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
function getCachedToken(address, chainId) {
|
|
611
|
+
const cached = tokenCache.get(cacheKey(address, chainId));
|
|
612
|
+
if (cached && Date.now() < cached.expiresAt) {
|
|
613
|
+
return cached.token;
|
|
614
|
+
}
|
|
615
|
+
if (cached) {
|
|
616
|
+
tokenCache.delete(cacheKey(address, chainId));
|
|
617
|
+
}
|
|
618
|
+
const stored = readStoredToken(address, chainId);
|
|
619
|
+
if (!stored) return null;
|
|
620
|
+
tokenCache.set(cacheKey(address, chainId), stored);
|
|
621
|
+
return stored.token;
|
|
622
|
+
}
|
|
623
|
+
function clearCachedToken(address, chainId) {
|
|
624
|
+
tokenCache.delete(cacheKey(address, chainId));
|
|
625
|
+
if (typeof window !== "undefined") {
|
|
626
|
+
window.localStorage.removeItem(storageKey(address, chainId));
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
async function fetchAccessToken(walletClient, signerAddress, accountAddress, chainId, domain) {
|
|
630
|
+
const resolvedDomain = domain ?? (typeof window !== "undefined" ? window.location.host : "localhost");
|
|
631
|
+
const uri = typeof window !== "undefined" ? window.location.origin : `https://${resolvedDomain}`;
|
|
632
|
+
const nonce = await getNonce(chainId, accountAddress);
|
|
633
|
+
const { message, issuedAt, expirationTime } = createSiweMessage({
|
|
634
|
+
address: signerAddress,
|
|
635
|
+
statement: "Sign in to Symm Protocol",
|
|
636
|
+
chainId,
|
|
637
|
+
nonce,
|
|
638
|
+
domain: resolvedDomain,
|
|
639
|
+
uri
|
|
640
|
+
});
|
|
641
|
+
const signature = await walletClient.signMessage({
|
|
642
|
+
account: signerAddress,
|
|
643
|
+
message
|
|
644
|
+
});
|
|
645
|
+
const { accessToken } = await login(chainId, {
|
|
646
|
+
accountAddress,
|
|
647
|
+
signature,
|
|
648
|
+
expirationTime,
|
|
649
|
+
issuedAt,
|
|
650
|
+
nonce
|
|
651
|
+
});
|
|
652
|
+
const expiresAt = new Date(expirationTime).getTime();
|
|
653
|
+
const cachedToken = {
|
|
654
|
+
token: accessToken,
|
|
655
|
+
expiresAt: expiresAt - 6e4
|
|
656
|
+
};
|
|
657
|
+
tokenCache.set(cacheKey(accountAddress, chainId), cachedToken);
|
|
658
|
+
writeStoredToken(accountAddress, chainId, cachedToken);
|
|
659
|
+
return accessToken;
|
|
660
|
+
}
|
|
661
|
+
function symmAuthTokenKey(address, chainId) {
|
|
662
|
+
return `${address.toLowerCase()}:${chainId}`;
|
|
663
|
+
}
|
|
664
|
+
var useSymmAuthStore = zustand.create((set, get) => ({
|
|
665
|
+
tokensByKey: {},
|
|
666
|
+
setToken: (address, chainId, token) => {
|
|
667
|
+
const key = symmAuthTokenKey(address, chainId);
|
|
668
|
+
set((state) => ({
|
|
669
|
+
tokensByKey: {
|
|
670
|
+
...state.tokensByKey,
|
|
671
|
+
[key]: token
|
|
672
|
+
}
|
|
673
|
+
}));
|
|
674
|
+
},
|
|
675
|
+
getToken: (address, chainId) => {
|
|
676
|
+
const key = symmAuthTokenKey(address, chainId);
|
|
677
|
+
return get().tokensByKey[key] ?? null;
|
|
678
|
+
},
|
|
679
|
+
clearToken: (address, chainId) => {
|
|
680
|
+
const key = symmAuthTokenKey(address, chainId);
|
|
681
|
+
set((state) => {
|
|
682
|
+
if (!(key in state.tokensByKey)) return state;
|
|
683
|
+
const next = { ...state.tokensByKey };
|
|
684
|
+
delete next[key];
|
|
685
|
+
return { tokensByKey: next };
|
|
686
|
+
});
|
|
687
|
+
},
|
|
688
|
+
clearAll: () => {
|
|
689
|
+
set({ tokensByKey: {} });
|
|
690
|
+
}
|
|
691
|
+
}));
|
|
692
|
+
|
|
693
|
+
// src/react/hooks/use-symm-auth.ts
|
|
694
|
+
function useSymmAuth(params) {
|
|
695
|
+
const ctx = useSymmContext();
|
|
696
|
+
const address = params?.address ?? ctx.address;
|
|
697
|
+
const chainId = params?.chainId ?? ctx.chainId ?? 42161;
|
|
698
|
+
const walletClient = params?.walletClient ?? ctx.walletClient;
|
|
699
|
+
const siweDomain = params?.siweDomain;
|
|
700
|
+
const accessToken = useSymmAuthStore((state) => {
|
|
701
|
+
if (address) {
|
|
702
|
+
return state.tokensByKey[symmAuthTokenKey(address, chainId)] ?? null;
|
|
703
|
+
}
|
|
704
|
+
return ctx.accessToken ?? ctx.authToken ?? null;
|
|
705
|
+
});
|
|
706
|
+
const setToken = useSymmAuthStore((state) => state.setToken);
|
|
707
|
+
const getToken = useSymmAuthStore((state) => state.getToken);
|
|
708
|
+
const clearToken = useSymmAuthStore((state) => state.clearToken);
|
|
709
|
+
const previousAddressRef = react.useRef(address);
|
|
710
|
+
const previousChainIdRef = react.useRef(chainId);
|
|
711
|
+
const refreshAuth = react.useCallback(
|
|
712
|
+
async (accountAddress) => {
|
|
713
|
+
if (!walletClient || !address) return null;
|
|
714
|
+
const resolvedAccountAddress = accountAddress ?? address;
|
|
715
|
+
const inMemory = getToken(resolvedAccountAddress, chainId);
|
|
716
|
+
if (inMemory) {
|
|
717
|
+
return inMemory;
|
|
718
|
+
}
|
|
719
|
+
const cached = getCachedToken(resolvedAccountAddress, chainId);
|
|
720
|
+
if (cached) {
|
|
721
|
+
setToken(resolvedAccountAddress, chainId, cached);
|
|
722
|
+
return cached;
|
|
723
|
+
}
|
|
724
|
+
try {
|
|
725
|
+
const token = await fetchAccessToken(
|
|
726
|
+
walletClient,
|
|
727
|
+
address,
|
|
728
|
+
resolvedAccountAddress,
|
|
729
|
+
chainId,
|
|
730
|
+
siweDomain
|
|
731
|
+
);
|
|
732
|
+
setToken(resolvedAccountAddress, chainId, token);
|
|
733
|
+
return token;
|
|
734
|
+
} catch {
|
|
735
|
+
clearToken(resolvedAccountAddress, chainId);
|
|
736
|
+
return null;
|
|
737
|
+
}
|
|
738
|
+
},
|
|
739
|
+
[walletClient, address, chainId, siweDomain, getToken, setToken, clearToken]
|
|
740
|
+
);
|
|
741
|
+
const clearAuth = react.useCallback(() => {
|
|
742
|
+
if (address) {
|
|
743
|
+
clearCachedToken(address, chainId);
|
|
744
|
+
clearToken(address, chainId);
|
|
745
|
+
}
|
|
746
|
+
}, [address, chainId, clearToken]);
|
|
747
|
+
react.useEffect(() => {
|
|
748
|
+
const previousAddress = previousAddressRef.current;
|
|
749
|
+
const previousChainId = previousChainIdRef.current;
|
|
750
|
+
const addressChanged = previousAddress !== address;
|
|
751
|
+
const chainChanged = previousChainId !== chainId;
|
|
752
|
+
previousAddressRef.current = address;
|
|
753
|
+
previousChainIdRef.current = chainId;
|
|
754
|
+
if (!address || !walletClient) {
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
if (previousAddress && (addressChanged || chainChanged)) {
|
|
758
|
+
clearCachedToken(previousAddress, previousChainId);
|
|
759
|
+
clearToken(previousAddress, previousChainId);
|
|
760
|
+
}
|
|
761
|
+
const cached = getCachedToken(address, chainId);
|
|
762
|
+
if (cached) {
|
|
763
|
+
setToken(address, chainId, cached);
|
|
764
|
+
} else {
|
|
765
|
+
clearToken(address, chainId);
|
|
766
|
+
}
|
|
767
|
+
}, [address, walletClient, chainId, setToken, clearToken]);
|
|
768
|
+
return {
|
|
769
|
+
accessToken,
|
|
770
|
+
authToken: accessToken,
|
|
771
|
+
isAuthenticated: !!accessToken,
|
|
772
|
+
refresh: refreshAuth,
|
|
773
|
+
refreshAuth,
|
|
774
|
+
clear: clearAuth
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/constants/selectors.ts
|
|
779
|
+
var SEND_QUOTE_WITH_AFFILIATE_SELECTOR = "0x40f1310c";
|
|
780
|
+
var CLOSE_QUOTE_SELECTOR = "0x501e891f";
|
|
781
|
+
var ALL_TRADING_SELECTORS = [
|
|
782
|
+
SEND_QUOTE_WITH_AFFILIATE_SELECTOR,
|
|
783
|
+
CLOSE_QUOTE_SELECTOR
|
|
784
|
+
];
|
|
785
|
+
|
|
355
786
|
// src/constants/addresses.ts
|
|
356
787
|
var MULTI_ACCOUNT_ADDRESS = {
|
|
357
788
|
[42161 /* ARBITRUM */]: "0x6273242a7E88b3De90822b31648C212215caaFE4",
|
|
@@ -24524,852 +24955,540 @@ function useSymmAccountSummary(params) {
|
|
|
24524
24955
|
userAddress,
|
|
24525
24956
|
chainId
|
|
24526
24957
|
}),
|
|
24527
|
-
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24528
|
-
});
|
|
24529
|
-
}
|
|
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
|
|
24958
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24710
24959
|
});
|
|
24711
24960
|
}
|
|
24712
|
-
function
|
|
24961
|
+
function useSymmAccountData(params) {
|
|
24713
24962
|
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24714
|
-
const {
|
|
24715
|
-
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24963
|
+
const { address, upnl } = params;
|
|
24716
24964
|
const chainId = params.chainId ?? ctxChainId;
|
|
24717
|
-
const internalEnabled = !!symmCoreClient && !!
|
|
24965
|
+
const internalEnabled = !!symmCoreClient && !!address;
|
|
24718
24966
|
return reactQuery.useQuery({
|
|
24719
24967
|
...params.query,
|
|
24720
|
-
queryKey: symmKeys.
|
|
24721
|
-
|
|
24722
|
-
address
|
|
24723
|
-
chainId
|
|
24724
|
-
|
|
24725
|
-
queryFn: () => symmCoreClient.orders.list({
|
|
24726
|
-
address: accountAddress ?? resolvedAddress,
|
|
24727
|
-
chainId
|
|
24968
|
+
queryKey: symmKeys.accountData(address, chainId, upnl),
|
|
24969
|
+
queryFn: () => symmCoreClient.accounts.getData({
|
|
24970
|
+
address,
|
|
24971
|
+
chainId,
|
|
24972
|
+
upnl
|
|
24728
24973
|
}),
|
|
24729
24974
|
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24730
24975
|
});
|
|
24731
24976
|
}
|
|
24732
|
-
function
|
|
24977
|
+
function useSymmBalances(params) {
|
|
24733
24978
|
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24734
|
-
const {
|
|
24735
|
-
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24979
|
+
const { userAddress, multiAccountAddress } = params;
|
|
24736
24980
|
const chainId = params.chainId ?? ctxChainId;
|
|
24737
|
-
const internalEnabled = !!symmCoreClient && !!
|
|
24981
|
+
const internalEnabled = !!symmCoreClient && !!userAddress;
|
|
24738
24982
|
return reactQuery.useQuery({
|
|
24739
24983
|
...params.query,
|
|
24740
|
-
queryKey: symmKeys.
|
|
24741
|
-
|
|
24742
|
-
|
|
24743
|
-
chainId
|
|
24984
|
+
queryKey: symmKeys.balances(userAddress, chainId),
|
|
24985
|
+
queryFn: () => symmCoreClient.accounts.getBalanceInfo({
|
|
24986
|
+
userAddress,
|
|
24987
|
+
chainId,
|
|
24988
|
+
multiAccountAddress
|
|
24744
24989
|
}),
|
|
24745
|
-
queryFn: () => {
|
|
24746
|
-
const request = {
|
|
24747
|
-
accountAddress,
|
|
24748
|
-
address: resolvedAddress,
|
|
24749
|
-
chainId
|
|
24750
|
-
};
|
|
24751
|
-
return symmCoreClient.positions.getTradeHistory(request);
|
|
24752
|
-
},
|
|
24753
24990
|
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24754
24991
|
});
|
|
24755
24992
|
}
|
|
24756
|
-
function
|
|
24993
|
+
function useResolveTradeAuthToken() {
|
|
24994
|
+
const context = useSymmContext();
|
|
24995
|
+
const { address, chainId } = context;
|
|
24996
|
+
const { refreshAuth } = useSymmAuth({ address, chainId });
|
|
24997
|
+
const refreshAuthFromContext = context.refreshAuth;
|
|
24998
|
+
return react.useCallback(
|
|
24999
|
+
async (providedAuthToken, accountAddress) => {
|
|
25000
|
+
if (providedAuthToken) {
|
|
25001
|
+
return providedAuthToken;
|
|
25002
|
+
}
|
|
25003
|
+
const resolvedAccountAddress = accountAddress ?? address;
|
|
25004
|
+
if (!resolvedAccountAddress) {
|
|
25005
|
+
return null;
|
|
25006
|
+
}
|
|
25007
|
+
const inMemoryToken = useSymmAuthStore.getState().getToken(resolvedAccountAddress, chainId);
|
|
25008
|
+
if (inMemoryToken) {
|
|
25009
|
+
return inMemoryToken;
|
|
25010
|
+
}
|
|
25011
|
+
if (refreshAuthFromContext) {
|
|
25012
|
+
return refreshAuthFromContext(resolvedAccountAddress);
|
|
25013
|
+
}
|
|
25014
|
+
return refreshAuth(resolvedAccountAddress);
|
|
25015
|
+
},
|
|
25016
|
+
[address, chainId, refreshAuth, refreshAuthFromContext]
|
|
25017
|
+
);
|
|
25018
|
+
}
|
|
25019
|
+
function useSymmOpenBasketMutation(options) {
|
|
24757
25020
|
const { symmCoreClient } = useSymmContext();
|
|
24758
25021
|
const queryClient = reactQuery.useQueryClient();
|
|
24759
25022
|
return reactQuery.useMutation({
|
|
24760
25023
|
...withSymmMutationConfig(options?.mutation, {
|
|
24761
25024
|
onSuccess: () => {
|
|
24762
|
-
invalidateOrders(queryClient);
|
|
24763
25025
|
invalidatePositions(queryClient);
|
|
24764
25026
|
}
|
|
24765
25027
|
}),
|
|
24766
25028
|
mutationFn: async (request) => {
|
|
24767
25029
|
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
24768
|
-
return symmCoreClient.
|
|
25030
|
+
return symmCoreClient.positions.openBasket(request);
|
|
24769
25031
|
}
|
|
24770
25032
|
});
|
|
24771
25033
|
}
|
|
24772
|
-
function
|
|
25034
|
+
function useSymmClosePositionMutation(options) {
|
|
24773
25035
|
const { symmCoreClient } = useSymmContext();
|
|
24774
25036
|
const queryClient = reactQuery.useQueryClient();
|
|
25037
|
+
const resolveAuthToken = useResolveTradeAuthToken();
|
|
24775
25038
|
return reactQuery.useMutation({
|
|
24776
25039
|
...withSymmMutationConfig(options?.mutation, {
|
|
24777
25040
|
onSuccess: () => {
|
|
24778
|
-
invalidateOrders(queryClient);
|
|
24779
25041
|
invalidatePositions(queryClient);
|
|
24780
25042
|
}
|
|
24781
25043
|
}),
|
|
24782
25044
|
mutationFn: async (request) => {
|
|
24783
25045
|
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
24784
|
-
|
|
25046
|
+
const typedRequest = request;
|
|
25047
|
+
const authToken = await resolveAuthToken(
|
|
25048
|
+
typedRequest.authToken,
|
|
25049
|
+
typedRequest.accountAddress
|
|
25050
|
+
);
|
|
25051
|
+
if (!authToken) {
|
|
25052
|
+
throw new Error("auth token is required to close a position");
|
|
25053
|
+
}
|
|
25054
|
+
return symmCoreClient.positions.close({
|
|
25055
|
+
...request,
|
|
25056
|
+
authToken
|
|
25057
|
+
});
|
|
24785
25058
|
}
|
|
24786
25059
|
});
|
|
24787
25060
|
}
|
|
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) {
|
|
25061
|
+
function useSymmCloseAllPositionsMutation(options) {
|
|
24836
25062
|
const { symmCoreClient } = useSymmContext();
|
|
24837
25063
|
const queryClient = reactQuery.useQueryClient();
|
|
24838
25064
|
return reactQuery.useMutation({
|
|
24839
25065
|
...withSymmMutationConfig(options?.mutation, {
|
|
24840
25066
|
onSuccess: () => {
|
|
24841
|
-
|
|
25067
|
+
invalidatePositions(queryClient);
|
|
24842
25068
|
}
|
|
24843
25069
|
}),
|
|
24844
|
-
mutationFn: async (
|
|
25070
|
+
mutationFn: async (request) => {
|
|
24845
25071
|
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
24846
|
-
return symmCoreClient.
|
|
25072
|
+
return symmCoreClient.positions.closeAll(request);
|
|
24847
25073
|
}
|
|
24848
25074
|
});
|
|
24849
25075
|
}
|
|
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) {
|
|
25076
|
+
function useSymmCancelOpenMutation(options) {
|
|
24862
25077
|
const { symmCoreClient } = useSymmContext();
|
|
24863
|
-
const { orderId } = params;
|
|
24864
25078
|
const queryClient = reactQuery.useQueryClient();
|
|
24865
25079
|
return reactQuery.useMutation({
|
|
24866
25080
|
...withSymmMutationConfig(options?.mutation, {
|
|
24867
25081
|
onSuccess: () => {
|
|
24868
|
-
queryClient
|
|
24869
|
-
queryKey: symmKeys.triggerConfig(orderId)
|
|
24870
|
-
});
|
|
24871
|
-
invalidateOrders(queryClient);
|
|
25082
|
+
invalidatePositions(queryClient);
|
|
24872
25083
|
}
|
|
24873
25084
|
}),
|
|
24874
25085
|
mutationFn: async (request) => {
|
|
24875
|
-
if (!symmCoreClient
|
|
24876
|
-
|
|
24877
|
-
}
|
|
24878
|
-
return symmCoreClient.orders.setTriggerConfig(orderId, request);
|
|
25086
|
+
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
25087
|
+
return symmCoreClient.positions.cancelOpen(request);
|
|
24879
25088
|
}
|
|
24880
25089
|
});
|
|
24881
25090
|
}
|
|
24882
|
-
function
|
|
25091
|
+
function useSymmUpdatePositionMutation(options) {
|
|
24883
25092
|
const { symmCoreClient } = useSymmContext();
|
|
24884
|
-
const { orderId } = params;
|
|
24885
25093
|
const queryClient = reactQuery.useQueryClient();
|
|
25094
|
+
const resolveAuthToken = useResolveTradeAuthToken();
|
|
24886
25095
|
return reactQuery.useMutation({
|
|
24887
25096
|
...withSymmMutationConfig(options?.mutation, {
|
|
24888
25097
|
onSuccess: () => {
|
|
24889
|
-
queryClient
|
|
24890
|
-
queryKey: symmKeys.triggerConfig(orderId)
|
|
24891
|
-
});
|
|
24892
|
-
invalidateOrders(queryClient);
|
|
24893
|
-
}
|
|
24894
|
-
}),
|
|
24895
|
-
mutationFn: async () => {
|
|
24896
|
-
if (!symmCoreClient || !orderId) {
|
|
24897
|
-
throw new Error("symm-core client or orderId not available");
|
|
25098
|
+
invalidatePositions(queryClient);
|
|
24898
25099
|
}
|
|
24899
|
-
return symmCoreClient.orders.clearTriggerConfig(orderId);
|
|
24900
|
-
}
|
|
24901
|
-
});
|
|
24902
|
-
}
|
|
24903
|
-
function useSymmTriggerOrders(params) {
|
|
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
25100
|
}),
|
|
24926
|
-
|
|
24927
|
-
|
|
24928
|
-
|
|
24929
|
-
}
|
|
24930
|
-
|
|
24931
|
-
|
|
24932
|
-
|
|
24933
|
-
|
|
24934
|
-
|
|
24935
|
-
|
|
24936
|
-
|
|
24937
|
-
|
|
24938
|
-
|
|
24939
|
-
|
|
24940
|
-
|
|
24941
|
-
|
|
24942
|
-
|
|
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)
|
|
25101
|
+
mutationFn: async ({
|
|
25102
|
+
positionId,
|
|
25103
|
+
request
|
|
25104
|
+
}) => {
|
|
25105
|
+
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
25106
|
+
const typedRequest = request;
|
|
25107
|
+
const authToken = await resolveAuthToken(
|
|
25108
|
+
typedRequest.authToken,
|
|
25109
|
+
typedRequest.accountAddress
|
|
25110
|
+
);
|
|
25111
|
+
if (!authToken) {
|
|
25112
|
+
throw new Error("auth token is required to update a position");
|
|
25113
|
+
}
|
|
25114
|
+
return symmCoreClient.positions.update(positionId, {
|
|
25115
|
+
...request,
|
|
25116
|
+
authToken
|
|
25117
|
+
});
|
|
25118
|
+
}
|
|
24957
25119
|
});
|
|
24958
25120
|
}
|
|
24959
|
-
function
|
|
25121
|
+
function useSymmPositions(params) {
|
|
24960
25122
|
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24961
|
-
const {
|
|
25123
|
+
const { accountAddress, address } = params;
|
|
25124
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24962
25125
|
const chainId = params.chainId ?? ctxChainId;
|
|
24963
|
-
const internalEnabled = !!symmCoreClient && !!
|
|
25126
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
25127
|
+
const enabled = internalEnabled && (params.query?.enabled ?? true);
|
|
24964
25128
|
return reactQuery.useQuery({
|
|
24965
25129
|
...params.query,
|
|
24966
|
-
queryKey: symmKeys.
|
|
24967
|
-
|
|
24968
|
-
|
|
25130
|
+
queryKey: symmKeys.positions({
|
|
25131
|
+
accountAddress,
|
|
25132
|
+
address: resolvedAddress,
|
|
25133
|
+
chainId
|
|
25134
|
+
}),
|
|
25135
|
+
queryFn: () => symmCoreClient.positions.getOpen({
|
|
25136
|
+
accountAddress,
|
|
25137
|
+
address: resolvedAddress,
|
|
25138
|
+
chainId
|
|
25139
|
+
}),
|
|
25140
|
+
enabled
|
|
24969
25141
|
});
|
|
24970
25142
|
}
|
|
24971
|
-
function
|
|
25143
|
+
function useSymmOpenOrders(params) {
|
|
24972
25144
|
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24973
|
-
const {
|
|
25145
|
+
const { accountAddress, address } = params;
|
|
25146
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
24974
25147
|
const chainId = params.chainId ?? ctxChainId;
|
|
24975
|
-
const internalEnabled = !!symmCoreClient && !!
|
|
25148
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
24976
25149
|
return reactQuery.useQuery({
|
|
24977
25150
|
...params.query,
|
|
24978
|
-
queryKey: symmKeys.
|
|
24979
|
-
|
|
24980
|
-
|
|
24981
|
-
|
|
25151
|
+
queryKey: symmKeys.openOrders({
|
|
25152
|
+
accountAddress,
|
|
25153
|
+
address: resolvedAddress,
|
|
25154
|
+
chainId
|
|
25155
|
+
}),
|
|
25156
|
+
queryFn: () => symmCoreClient.orders.list({
|
|
25157
|
+
address: accountAddress ?? resolvedAddress,
|
|
24982
25158
|
chainId
|
|
24983
25159
|
}),
|
|
24984
25160
|
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
24985
|
-
});
|
|
24986
|
-
}
|
|
24987
|
-
function
|
|
24988
|
-
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
24989
|
-
const
|
|
24990
|
-
const
|
|
24991
|
-
const
|
|
24992
|
-
const
|
|
24993
|
-
|
|
24994
|
-
...
|
|
24995
|
-
|
|
24996
|
-
|
|
24997
|
-
|
|
24998
|
-
|
|
24999
|
-
|
|
25000
|
-
|
|
25001
|
-
|
|
25002
|
-
|
|
25003
|
-
|
|
25004
|
-
|
|
25005
|
-
}
|
|
25006
|
-
|
|
25007
|
-
|
|
25008
|
-
|
|
25009
|
-
|
|
25010
|
-
|
|
25011
|
-
|
|
25012
|
-
|
|
25013
|
-
|
|
25014
|
-
|
|
25015
|
-
|
|
25016
|
-
|
|
25017
|
-
|
|
25018
|
-
|
|
25019
|
-
throw new Error(`Binance markPriceKlines failed: ${response.status}`);
|
|
25020
|
-
}
|
|
25021
|
-
const data = await response.json();
|
|
25022
|
-
return data.map((k) => ({
|
|
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
|
-
|
|
25044
|
-
// src/utils/binance-symbol-map.ts
|
|
25045
|
-
var SYMBOL_OVERRIDES = {
|
|
25046
|
-
// Add overrides here as needed for SYMM markets that don't map 1:1 to Binance
|
|
25047
|
-
// e.g., 'SOME_SYMM_SYMBOL': 'BINANCE_SYMBOL',
|
|
25048
|
-
};
|
|
25049
|
-
var UNSUPPORTED_SYMBOLS = /* @__PURE__ */ new Set([
|
|
25050
|
-
// Add symbols here that have no Binance equivalent
|
|
25051
|
-
]);
|
|
25052
|
-
function resolveBinanceSymbol(symmSymbol) {
|
|
25053
|
-
if (!symmSymbol || !symmSymbol.trim()) {
|
|
25054
|
-
return {
|
|
25055
|
-
symmSymbol,
|
|
25056
|
-
normalizedSymbol: "",
|
|
25057
|
-
binanceSymbol: null,
|
|
25058
|
-
supported: false,
|
|
25059
|
-
reason: "missing_symbol"
|
|
25060
|
-
};
|
|
25061
|
-
}
|
|
25062
|
-
const normalized = symmSymbol.toUpperCase().trim();
|
|
25063
|
-
if (!/^[A-Z0-9]+$/.test(normalized)) {
|
|
25064
|
-
return {
|
|
25065
|
-
symmSymbol,
|
|
25066
|
-
normalizedSymbol: normalized,
|
|
25067
|
-
binanceSymbol: null,
|
|
25068
|
-
supported: false,
|
|
25069
|
-
reason: "invalid_symbol"
|
|
25070
|
-
};
|
|
25071
|
-
}
|
|
25072
|
-
if (UNSUPPORTED_SYMBOLS.has(normalized)) {
|
|
25073
|
-
return {
|
|
25074
|
-
symmSymbol,
|
|
25075
|
-
normalizedSymbol: normalized,
|
|
25076
|
-
binanceSymbol: null,
|
|
25077
|
-
supported: false,
|
|
25078
|
-
reason: "unsupported_symbol"
|
|
25079
|
-
};
|
|
25080
|
-
}
|
|
25081
|
-
const binanceSymbol = SYMBOL_OVERRIDES[normalized] ?? (normalized.endsWith("USDT") ? normalized : `${normalized}USDT`);
|
|
25082
|
-
return {
|
|
25083
|
-
symmSymbol,
|
|
25084
|
-
normalizedSymbol: normalized,
|
|
25085
|
-
binanceSymbol,
|
|
25086
|
-
supported: true,
|
|
25087
|
-
reason: null
|
|
25088
|
-
};
|
|
25089
|
-
}
|
|
25090
|
-
function getUnsupportedBinanceSymbols(symbols) {
|
|
25091
|
-
return symbols.filter((symbol) => !resolveBinanceSymbol(symbol).supported);
|
|
25092
|
-
}
|
|
25093
|
-
function toBinanceSymbol(symmSymbol) {
|
|
25094
|
-
return resolveBinanceSymbol(symmSymbol).binanceSymbol;
|
|
25095
|
-
}
|
|
25096
|
-
|
|
25097
|
-
// src/utils/binance-ws.ts
|
|
25098
|
-
var BINANCE_WS_URL = "wss://fstream.binance.com/ws";
|
|
25099
|
-
var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3, 3e4];
|
|
25100
|
-
var BinanceWsManager = class {
|
|
25101
|
-
ws = null;
|
|
25102
|
-
streams = /* @__PURE__ */ new Map();
|
|
25103
|
-
reconnectAttempt = 0;
|
|
25104
|
-
reconnectTimer = null;
|
|
25105
|
-
intentionalClose = false;
|
|
25106
|
-
pendingSubscribes = [];
|
|
25107
|
-
idCounter = 0;
|
|
25108
|
-
/**
|
|
25109
|
-
* Subscribe to a kline stream. Returns an unsubscribe function.
|
|
25110
|
-
*/
|
|
25111
|
-
subscribeKline(symbol, interval, cb) {
|
|
25112
|
-
const streamName = `${symbol.toLowerCase()}@kline_${interval}`;
|
|
25113
|
-
const id = this.generateId();
|
|
25114
|
-
const wrappedCb = (raw) => {
|
|
25115
|
-
const k = raw.k;
|
|
25116
|
-
if (!k) return;
|
|
25117
|
-
cb({
|
|
25118
|
-
symbol: raw.s,
|
|
25119
|
-
interval: k.i,
|
|
25120
|
-
openTime: k.t,
|
|
25121
|
-
closeTime: k.T,
|
|
25122
|
-
open: parseFloat(k.o),
|
|
25123
|
-
high: parseFloat(k.h),
|
|
25124
|
-
low: parseFloat(k.l),
|
|
25125
|
-
close: parseFloat(k.c),
|
|
25126
|
-
volume: parseFloat(k.v),
|
|
25127
|
-
isFinal: k.x
|
|
25128
|
-
});
|
|
25129
|
-
};
|
|
25130
|
-
this.addStreamCallback(streamName, id, wrappedCb);
|
|
25131
|
-
return () => this.removeStreamCallback(streamName, id);
|
|
25132
|
-
}
|
|
25133
|
-
/**
|
|
25134
|
-
* Subscribe to a mark price stream (1s updates). Returns an unsubscribe function.
|
|
25135
|
-
*/
|
|
25136
|
-
subscribeMarkPrice(symbol, cb) {
|
|
25137
|
-
const streamName = `${symbol.toLowerCase()}@markPrice@1s`;
|
|
25138
|
-
const id = this.generateId();
|
|
25139
|
-
const wrappedCb = (raw) => {
|
|
25140
|
-
cb({
|
|
25141
|
-
symbol: raw.s,
|
|
25142
|
-
markPrice: parseFloat(raw.p),
|
|
25143
|
-
indexPrice: parseFloat(raw.i),
|
|
25144
|
-
time: raw.E
|
|
25145
|
-
});
|
|
25146
|
-
};
|
|
25147
|
-
this.addStreamCallback(streamName, id, wrappedCb);
|
|
25148
|
-
return () => this.removeStreamCallback(streamName, id);
|
|
25149
|
-
}
|
|
25150
|
-
/**
|
|
25151
|
-
* Destroy the manager and close the connection.
|
|
25152
|
-
*/
|
|
25153
|
-
destroy() {
|
|
25154
|
-
this.intentionalClose = true;
|
|
25155
|
-
if (this.reconnectTimer) {
|
|
25156
|
-
clearTimeout(this.reconnectTimer);
|
|
25157
|
-
this.reconnectTimer = null;
|
|
25158
|
-
}
|
|
25159
|
-
if (this.ws) {
|
|
25160
|
-
this.ws.close();
|
|
25161
|
-
this.ws = null;
|
|
25162
|
-
}
|
|
25163
|
-
this.streams.clear();
|
|
25164
|
-
}
|
|
25165
|
-
// --- Private ---
|
|
25166
|
-
generateId() {
|
|
25167
|
-
return `sub_${++this.idCounter}_${Date.now()}`;
|
|
25168
|
-
}
|
|
25169
|
-
addStreamCallback(streamName, id, cb) {
|
|
25170
|
-
let sub = this.streams.get(streamName);
|
|
25171
|
-
const isNew = !sub;
|
|
25172
|
-
if (!sub) {
|
|
25173
|
-
sub = { callbacks: /* @__PURE__ */ new Map() };
|
|
25174
|
-
this.streams.set(streamName, sub);
|
|
25175
|
-
}
|
|
25176
|
-
sub.callbacks.set(id, cb);
|
|
25177
|
-
if (isNew) {
|
|
25178
|
-
this.ensureConnected();
|
|
25179
|
-
this.sendSubscribe([streamName]);
|
|
25180
|
-
}
|
|
25181
|
-
}
|
|
25182
|
-
removeStreamCallback(streamName, id) {
|
|
25183
|
-
const sub = this.streams.get(streamName);
|
|
25184
|
-
if (!sub) return;
|
|
25185
|
-
sub.callbacks.delete(id);
|
|
25186
|
-
if (sub.callbacks.size === 0) {
|
|
25187
|
-
this.streams.delete(streamName);
|
|
25188
|
-
this.sendUnsubscribe([streamName]);
|
|
25189
|
-
if (this.streams.size === 0 && this.ws) {
|
|
25190
|
-
this.intentionalClose = true;
|
|
25191
|
-
this.ws.close();
|
|
25192
|
-
this.ws = null;
|
|
25193
|
-
this.intentionalClose = false;
|
|
25161
|
+
});
|
|
25162
|
+
}
|
|
25163
|
+
function useSymmTradeHistory(params) {
|
|
25164
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25165
|
+
const { accountAddress, address } = params;
|
|
25166
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
25167
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25168
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
25169
|
+
return reactQuery.useQuery({
|
|
25170
|
+
...params.query,
|
|
25171
|
+
queryKey: symmKeys.tradeHistory({
|
|
25172
|
+
accountAddress,
|
|
25173
|
+
address: resolvedAddress,
|
|
25174
|
+
chainId
|
|
25175
|
+
}),
|
|
25176
|
+
queryFn: () => {
|
|
25177
|
+
const request = {
|
|
25178
|
+
accountAddress,
|
|
25179
|
+
address: resolvedAddress,
|
|
25180
|
+
chainId
|
|
25181
|
+
};
|
|
25182
|
+
return symmCoreClient.positions.getTradeHistory(request);
|
|
25183
|
+
},
|
|
25184
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25185
|
+
});
|
|
25186
|
+
}
|
|
25187
|
+
function useSymmSetTpslMutation(options) {
|
|
25188
|
+
const { symmCoreClient } = useSymmContext();
|
|
25189
|
+
const queryClient = reactQuery.useQueryClient();
|
|
25190
|
+
return reactQuery.useMutation({
|
|
25191
|
+
...withSymmMutationConfig(options?.mutation, {
|
|
25192
|
+
onSuccess: () => {
|
|
25193
|
+
invalidateOrders(queryClient);
|
|
25194
|
+
invalidatePositions(queryClient);
|
|
25194
25195
|
}
|
|
25196
|
+
}),
|
|
25197
|
+
mutationFn: async (request) => {
|
|
25198
|
+
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
25199
|
+
return symmCoreClient.orders.setTpsl(request);
|
|
25195
25200
|
}
|
|
25196
|
-
}
|
|
25197
|
-
|
|
25198
|
-
|
|
25199
|
-
|
|
25201
|
+
});
|
|
25202
|
+
}
|
|
25203
|
+
function useSymmCancelTpslMutation(options) {
|
|
25204
|
+
const { symmCoreClient } = useSymmContext();
|
|
25205
|
+
const queryClient = reactQuery.useQueryClient();
|
|
25206
|
+
return reactQuery.useMutation({
|
|
25207
|
+
...withSymmMutationConfig(options?.mutation, {
|
|
25208
|
+
onSuccess: () => {
|
|
25209
|
+
invalidateOrders(queryClient);
|
|
25210
|
+
invalidatePositions(queryClient);
|
|
25211
|
+
}
|
|
25212
|
+
}),
|
|
25213
|
+
mutationFn: async (request) => {
|
|
25214
|
+
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
25215
|
+
return symmCoreClient.orders.cancelTpsl(request);
|
|
25200
25216
|
}
|
|
25201
|
-
|
|
25202
|
-
|
|
25203
|
-
|
|
25204
|
-
|
|
25205
|
-
|
|
25206
|
-
|
|
25207
|
-
|
|
25208
|
-
|
|
25209
|
-
|
|
25210
|
-
|
|
25211
|
-
|
|
25217
|
+
});
|
|
25218
|
+
}
|
|
25219
|
+
function useSymmTpslOrders(params) {
|
|
25220
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25221
|
+
const { accountAddress, address, positionId, type, status } = params;
|
|
25222
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
25223
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25224
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress || positionId);
|
|
25225
|
+
const request = {
|
|
25226
|
+
address: accountAddress ?? resolvedAddress,
|
|
25227
|
+
positionId,
|
|
25228
|
+
type,
|
|
25229
|
+
status,
|
|
25230
|
+
chainId
|
|
25231
|
+
};
|
|
25232
|
+
return reactQuery.useQuery({
|
|
25233
|
+
...params.query,
|
|
25234
|
+
queryKey: symmKeys.tpslOrdersList({
|
|
25235
|
+
accountAddress,
|
|
25236
|
+
address: resolvedAddress,
|
|
25237
|
+
positionId,
|
|
25238
|
+
type,
|
|
25239
|
+
status,
|
|
25240
|
+
chainId
|
|
25241
|
+
}),
|
|
25242
|
+
queryFn: () => symmCoreClient.orders.getTpslOrders(request),
|
|
25243
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25244
|
+
});
|
|
25245
|
+
}
|
|
25246
|
+
function useResolvedTwapParams(params) {
|
|
25247
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25248
|
+
const { accountAddress, address } = params;
|
|
25249
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
25250
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25251
|
+
return { symmCoreClient, accountAddress, resolvedAddress, chainId, query: params.query };
|
|
25252
|
+
}
|
|
25253
|
+
function useSymmTwapOrdersQuery(params) {
|
|
25254
|
+
const { symmCoreClient, accountAddress, resolvedAddress, chainId, query } = useResolvedTwapParams(params);
|
|
25255
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
25256
|
+
return reactQuery.useQuery({
|
|
25257
|
+
...query,
|
|
25258
|
+
queryKey: symmKeys.twapOrders(accountAddress ?? resolvedAddress, chainId),
|
|
25259
|
+
queryFn: () => symmCoreClient.orders.getTwapOrders({
|
|
25260
|
+
address: accountAddress ?? resolvedAddress,
|
|
25261
|
+
chainId
|
|
25262
|
+
}),
|
|
25263
|
+
enabled: internalEnabled && (query?.enabled ?? true)
|
|
25264
|
+
});
|
|
25265
|
+
}
|
|
25266
|
+
function useSymmCancelTwapOrderMutation(options) {
|
|
25267
|
+
const { symmCoreClient } = useSymmContext();
|
|
25268
|
+
const queryClient = reactQuery.useQueryClient();
|
|
25269
|
+
return reactQuery.useMutation({
|
|
25270
|
+
...withSymmMutationConfig(options?.mutation, {
|
|
25271
|
+
onSuccess: () => {
|
|
25272
|
+
invalidateOrders(queryClient);
|
|
25212
25273
|
}
|
|
25213
|
-
|
|
25214
|
-
|
|
25215
|
-
|
|
25274
|
+
}),
|
|
25275
|
+
mutationFn: async (orderId) => {
|
|
25276
|
+
if (!symmCoreClient) throw new Error("symm-core client not available");
|
|
25277
|
+
return symmCoreClient.orders.cancelTwapOrder(orderId);
|
|
25278
|
+
}
|
|
25279
|
+
});
|
|
25280
|
+
}
|
|
25281
|
+
function useSymmTriggerConfigQuery(params) {
|
|
25282
|
+
const { symmCoreClient } = useSymmContext();
|
|
25283
|
+
const { orderId } = params;
|
|
25284
|
+
const internalEnabled = !!symmCoreClient && !!orderId;
|
|
25285
|
+
return reactQuery.useQuery({
|
|
25286
|
+
...params.query,
|
|
25287
|
+
queryKey: symmKeys.triggerConfig(orderId),
|
|
25288
|
+
queryFn: () => symmCoreClient.orders.getTriggerConfig(orderId),
|
|
25289
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25290
|
+
});
|
|
25291
|
+
}
|
|
25292
|
+
function useSymmSetTriggerConfigMutation(params, options) {
|
|
25293
|
+
const { symmCoreClient } = useSymmContext();
|
|
25294
|
+
const { orderId } = params;
|
|
25295
|
+
const queryClient = reactQuery.useQueryClient();
|
|
25296
|
+
return reactQuery.useMutation({
|
|
25297
|
+
...withSymmMutationConfig(options?.mutation, {
|
|
25298
|
+
onSuccess: () => {
|
|
25299
|
+
queryClient.invalidateQueries({
|
|
25300
|
+
queryKey: symmKeys.triggerConfig(orderId)
|
|
25301
|
+
});
|
|
25302
|
+
invalidateOrders(queryClient);
|
|
25216
25303
|
}
|
|
25217
|
-
}
|
|
25218
|
-
|
|
25219
|
-
|
|
25220
|
-
|
|
25221
|
-
this.handleMessage(data);
|
|
25222
|
-
} catch {
|
|
25304
|
+
}),
|
|
25305
|
+
mutationFn: async (request) => {
|
|
25306
|
+
if (!symmCoreClient || !orderId) {
|
|
25307
|
+
throw new Error("symm-core client or orderId not available");
|
|
25223
25308
|
}
|
|
25224
|
-
|
|
25225
|
-
this.ws.onclose = () => {
|
|
25226
|
-
if (this.intentionalClose) return;
|
|
25227
|
-
this.scheduleReconnect();
|
|
25228
|
-
};
|
|
25229
|
-
this.ws.onerror = () => {
|
|
25230
|
-
};
|
|
25231
|
-
}
|
|
25232
|
-
handleMessage(data) {
|
|
25233
|
-
if (data.e === "kline") {
|
|
25234
|
-
const k = data.k;
|
|
25235
|
-
const streamName = `${data.s.toLowerCase()}@kline_${k.i}`;
|
|
25236
|
-
this.dispatchToStream(streamName, data);
|
|
25237
|
-
} else if (data.e === "markPriceUpdate") {
|
|
25238
|
-
const streamName = `${data.s.toLowerCase()}@markPrice@1s`;
|
|
25239
|
-
this.dispatchToStream(streamName, data);
|
|
25309
|
+
return symmCoreClient.orders.setTriggerConfig(orderId, request);
|
|
25240
25310
|
}
|
|
25241
|
-
}
|
|
25242
|
-
|
|
25243
|
-
|
|
25244
|
-
|
|
25245
|
-
|
|
25246
|
-
|
|
25247
|
-
|
|
25248
|
-
|
|
25311
|
+
});
|
|
25312
|
+
}
|
|
25313
|
+
function useSymmClearTriggerConfigMutation(params, options) {
|
|
25314
|
+
const { symmCoreClient } = useSymmContext();
|
|
25315
|
+
const { orderId } = params;
|
|
25316
|
+
const queryClient = reactQuery.useQueryClient();
|
|
25317
|
+
return reactQuery.useMutation({
|
|
25318
|
+
...withSymmMutationConfig(options?.mutation, {
|
|
25319
|
+
onSuccess: () => {
|
|
25320
|
+
queryClient.invalidateQueries({
|
|
25321
|
+
queryKey: symmKeys.triggerConfig(orderId)
|
|
25322
|
+
});
|
|
25323
|
+
invalidateOrders(queryClient);
|
|
25249
25324
|
}
|
|
25250
|
-
})
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
return;
|
|
25256
|
-
}
|
|
25257
|
-
this.ws.send(JSON.stringify({
|
|
25258
|
-
method: "SUBSCRIBE",
|
|
25259
|
-
params: streams,
|
|
25260
|
-
id: Date.now()
|
|
25261
|
-
}));
|
|
25262
|
-
}
|
|
25263
|
-
sendUnsubscribe(streams) {
|
|
25264
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
25265
|
-
this.pendingSubscribes = this.pendingSubscribes.filter(
|
|
25266
|
-
(s) => !streams.includes(s)
|
|
25267
|
-
);
|
|
25268
|
-
return;
|
|
25325
|
+
}),
|
|
25326
|
+
mutationFn: async () => {
|
|
25327
|
+
if (!symmCoreClient || !orderId) {
|
|
25328
|
+
throw new Error("symm-core client or orderId not available");
|
|
25329
|
+
}
|
|
25330
|
+
return symmCoreClient.orders.clearTriggerConfig(orderId);
|
|
25269
25331
|
}
|
|
25270
|
-
|
|
25271
|
-
|
|
25272
|
-
|
|
25273
|
-
|
|
25274
|
-
|
|
25275
|
-
|
|
25276
|
-
|
|
25277
|
-
|
|
25278
|
-
|
|
25279
|
-
|
|
25280
|
-
|
|
25281
|
-
|
|
25282
|
-
|
|
25283
|
-
|
|
25284
|
-
|
|
25285
|
-
|
|
25286
|
-
|
|
25287
|
-
|
|
25288
|
-
|
|
25289
|
-
|
|
25290
|
-
|
|
25291
|
-
|
|
25292
|
-
|
|
25332
|
+
});
|
|
25333
|
+
}
|
|
25334
|
+
function useSymmTriggerOrders(params) {
|
|
25335
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25336
|
+
const { accountAddress, address, status, limit, offset } = params;
|
|
25337
|
+
const resolvedAddress = accountAddress ? void 0 : address;
|
|
25338
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25339
|
+
const internalEnabled = !!symmCoreClient && !!(accountAddress || resolvedAddress);
|
|
25340
|
+
const request = {
|
|
25341
|
+
address: accountAddress ?? resolvedAddress,
|
|
25342
|
+
chainId,
|
|
25343
|
+
status,
|
|
25344
|
+
limit,
|
|
25345
|
+
offset
|
|
25346
|
+
};
|
|
25347
|
+
return reactQuery.useQuery({
|
|
25348
|
+
...params.query,
|
|
25349
|
+
queryKey: symmKeys.triggerOrders({
|
|
25350
|
+
accountAddress,
|
|
25351
|
+
address: resolvedAddress,
|
|
25352
|
+
chainId,
|
|
25353
|
+
status,
|
|
25354
|
+
limit,
|
|
25355
|
+
offset
|
|
25356
|
+
}),
|
|
25357
|
+
queryFn: () => symmCoreClient.orders.listTriggerOrders(request),
|
|
25358
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25359
|
+
});
|
|
25293
25360
|
}
|
|
25294
|
-
|
|
25295
|
-
|
|
25296
|
-
|
|
25297
|
-
|
|
25298
|
-
|
|
25299
|
-
|
|
25300
|
-
|
|
25361
|
+
function useSymmMarkets(params) {
|
|
25362
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25363
|
+
const chainId = params?.chainId ?? ctxChainId;
|
|
25364
|
+
const searchText = params?.searchText;
|
|
25365
|
+
const internalEnabled = !!symmCoreClient;
|
|
25366
|
+
return reactQuery.useQuery({
|
|
25367
|
+
...params?.query,
|
|
25368
|
+
queryKey: symmKeys.markets(chainId, searchText),
|
|
25369
|
+
queryFn: () => {
|
|
25370
|
+
if (searchText) {
|
|
25371
|
+
return symmCoreClient.markets.search(searchText, { chainId });
|
|
25372
|
+
}
|
|
25373
|
+
return symmCoreClient.markets.list({ pageSize: "1000", chainId });
|
|
25374
|
+
},
|
|
25375
|
+
enabled: internalEnabled && (params?.query?.enabled ?? true)
|
|
25376
|
+
});
|
|
25301
25377
|
}
|
|
25302
|
-
function
|
|
25303
|
-
|
|
25378
|
+
function useSymmHedgerMarketById(params) {
|
|
25379
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25380
|
+
const { id } = params;
|
|
25381
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25382
|
+
const internalEnabled = !!symmCoreClient && id != null;
|
|
25383
|
+
return reactQuery.useQuery({
|
|
25384
|
+
...params.query,
|
|
25385
|
+
queryKey: symmKeys.hedgerMarketById(id, chainId),
|
|
25386
|
+
queryFn: () => symmCoreClient.markets.getById(id, chainId),
|
|
25387
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25388
|
+
});
|
|
25304
25389
|
}
|
|
25305
|
-
function
|
|
25306
|
-
|
|
25390
|
+
function useSymmHedgerMarketBySymbol(params) {
|
|
25391
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25392
|
+
const { symbol } = params;
|
|
25393
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25394
|
+
const internalEnabled = !!symmCoreClient && !!symbol;
|
|
25395
|
+
return reactQuery.useQuery({
|
|
25396
|
+
...params.query,
|
|
25397
|
+
queryKey: symmKeys.hedgerMarketBySymbol(symbol, chainId),
|
|
25398
|
+
queryFn: () => symmCoreClient.markets.getBySymbol(symbol, chainId),
|
|
25399
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25400
|
+
});
|
|
25401
|
+
}
|
|
25402
|
+
function useSymmLockedParams(params) {
|
|
25403
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25404
|
+
const { marketName, leverage } = params;
|
|
25405
|
+
const chainId = params.chainId ?? ctxChainId;
|
|
25406
|
+
const internalEnabled = !!symmCoreClient && !!marketName && leverage != null;
|
|
25407
|
+
return reactQuery.useQuery({
|
|
25408
|
+
...params.query,
|
|
25409
|
+
queryKey: symmKeys.lockedParams(marketName, leverage, chainId),
|
|
25410
|
+
queryFn: () => symmCoreClient.markets.getLockedParams({
|
|
25411
|
+
marketName,
|
|
25412
|
+
leverage,
|
|
25413
|
+
chainId
|
|
25414
|
+
}),
|
|
25415
|
+
enabled: internalEnabled && (params.query?.enabled ?? true)
|
|
25416
|
+
});
|
|
25417
|
+
}
|
|
25418
|
+
function useSymmHedgerMarkets(params) {
|
|
25419
|
+
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25420
|
+
const chainId = params?.chainId ?? ctxChainId;
|
|
25421
|
+
const searchText = params?.searchText?.trim();
|
|
25422
|
+
const consumerEnabled = params?.query?.enabled ?? params?.enabled ?? true;
|
|
25423
|
+
const { enabled: _, query: __, ...restParams } = params ?? {};
|
|
25424
|
+
const request = {
|
|
25425
|
+
...restParams,
|
|
25426
|
+
chainId,
|
|
25427
|
+
searchText: searchText || void 0
|
|
25428
|
+
};
|
|
25429
|
+
const internalEnabled = !!symmCoreClient;
|
|
25430
|
+
return reactQuery.useQuery({
|
|
25431
|
+
...params?.query,
|
|
25432
|
+
queryKey: symmKeys.hedgerMarkets(request),
|
|
25433
|
+
queryFn: () => symmCoreClient.markets.listSymmHedger(request),
|
|
25434
|
+
enabled: internalEnabled && consumerEnabled
|
|
25435
|
+
});
|
|
25307
25436
|
}
|
|
25308
|
-
var useBinanceMarkPriceStore = zustand.create((set) => ({
|
|
25309
|
-
markPrices: {},
|
|
25310
|
-
subscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
|
|
25311
|
-
const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
|
|
25312
|
-
const nextRefCount = getNextRefCount(binanceSymbol);
|
|
25313
|
-
refCounts.set(binanceSymbol, nextRefCount);
|
|
25314
|
-
const symbols = streamSymbols.get(binanceSymbol) ?? /* @__PURE__ */ new Set();
|
|
25315
|
-
symbols.add(symmSymbol);
|
|
25316
|
-
streamSymbols.set(binanceSymbol, symbols);
|
|
25317
|
-
if (nextRefCount === 1) {
|
|
25318
|
-
const wsManager = getBinanceWsManager();
|
|
25319
|
-
const unsubscribe = wsManager.subscribeMarkPrice(binanceSymbol, (data) => {
|
|
25320
|
-
const canonicalSymbol = normalizeBinanceSymbol(data.symbol);
|
|
25321
|
-
const mappedSymbols = streamSymbols.get(canonicalSymbol);
|
|
25322
|
-
if (!mappedSymbols || mappedSymbols.size === 0) return;
|
|
25323
|
-
set((state) => {
|
|
25324
|
-
const nextMarkPrices = { ...state.markPrices };
|
|
25325
|
-
mappedSymbols.forEach((mappedSymbol) => {
|
|
25326
|
-
nextMarkPrices[mappedSymbol] = data.markPrice;
|
|
25327
|
-
});
|
|
25328
|
-
return { markPrices: nextMarkPrices };
|
|
25329
|
-
});
|
|
25330
|
-
});
|
|
25331
|
-
streamUnsubs.set(binanceSymbol, unsubscribe);
|
|
25332
|
-
}
|
|
25333
|
-
},
|
|
25334
|
-
unsubscribeSymbol: (symmSymbol, rawBinanceSymbol) => {
|
|
25335
|
-
const binanceSymbol = normalizeBinanceSymbol(rawBinanceSymbol);
|
|
25336
|
-
const symbols = streamSymbols.get(binanceSymbol);
|
|
25337
|
-
if (symbols) {
|
|
25338
|
-
symbols.delete(symmSymbol);
|
|
25339
|
-
if (symbols.size === 0) {
|
|
25340
|
-
streamSymbols.delete(binanceSymbol);
|
|
25341
|
-
} else {
|
|
25342
|
-
streamSymbols.set(binanceSymbol, symbols);
|
|
25343
|
-
}
|
|
25344
|
-
}
|
|
25345
|
-
const nextRefCount = getPrevRefCount(binanceSymbol);
|
|
25346
|
-
if (nextRefCount === 0) {
|
|
25347
|
-
const unsubscribe = streamUnsubs.get(binanceSymbol);
|
|
25348
|
-
if (unsubscribe) unsubscribe();
|
|
25349
|
-
streamUnsubs.delete(binanceSymbol);
|
|
25350
|
-
refCounts.delete(binanceSymbol);
|
|
25351
|
-
} else {
|
|
25352
|
-
refCounts.set(binanceSymbol, nextRefCount);
|
|
25353
|
-
}
|
|
25354
|
-
set((state) => {
|
|
25355
|
-
if (state.markPrices[symmSymbol] == null) return state;
|
|
25356
|
-
const nextMarkPrices = { ...state.markPrices };
|
|
25357
|
-
delete nextMarkPrices[symmSymbol];
|
|
25358
|
-
return { markPrices: nextMarkPrices };
|
|
25359
|
-
});
|
|
25360
|
-
}
|
|
25361
|
-
}));
|
|
25362
25437
|
|
|
25363
|
-
// src/
|
|
25364
|
-
|
|
25365
|
-
|
|
25366
|
-
|
|
25367
|
-
|
|
25368
|
-
|
|
25438
|
+
// src/utils/binance-api.ts
|
|
25439
|
+
var BINANCE_FAPI_BASE = "https://fapi.binance.com";
|
|
25440
|
+
async function fetchMarkPriceKlines(symbol, interval, startTime, endTime, limit = 1500) {
|
|
25441
|
+
const params = new URLSearchParams({
|
|
25442
|
+
symbol,
|
|
25443
|
+
interval,
|
|
25444
|
+
startTime: String(startTime),
|
|
25445
|
+
endTime: String(endTime),
|
|
25446
|
+
limit: String(Math.min(limit, 1500))
|
|
25447
|
+
});
|
|
25448
|
+
const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/markPriceKlines?${params}`);
|
|
25449
|
+
if (!response.ok) {
|
|
25450
|
+
throw new Error(`Binance markPriceKlines failed: ${response.status}`);
|
|
25451
|
+
}
|
|
25452
|
+
const data = await response.json();
|
|
25453
|
+
return data.map((k) => ({
|
|
25454
|
+
openTime: Number(k[0]),
|
|
25455
|
+
open: parseFloat(k[1]),
|
|
25456
|
+
high: parseFloat(k[2]),
|
|
25457
|
+
low: parseFloat(k[3]),
|
|
25458
|
+
close: parseFloat(k[4]),
|
|
25459
|
+
volume: parseFloat(k[5]),
|
|
25460
|
+
closeTime: Number(k[6])
|
|
25461
|
+
}));
|
|
25462
|
+
}
|
|
25463
|
+
async function fetch24hrTicker(symbol) {
|
|
25464
|
+
const params = new URLSearchParams({ symbol });
|
|
25465
|
+
const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/ticker/24hr?${params}`);
|
|
25466
|
+
if (!response.ok) return null;
|
|
25467
|
+
const data = await response.json();
|
|
25369
25468
|
return {
|
|
25370
|
-
|
|
25469
|
+
lastPrice: parseFloat(data.lastPrice),
|
|
25470
|
+
prevClosePrice: parseFloat(data.prevClosePrice),
|
|
25471
|
+
priceChangePercent: parseFloat(data.priceChangePercent)
|
|
25371
25472
|
};
|
|
25372
25473
|
}
|
|
25474
|
+
async function fetch24hrTickers() {
|
|
25475
|
+
const response = await fetch(`${BINANCE_FAPI_BASE}/fapi/v1/ticker/24hr`);
|
|
25476
|
+
if (!response.ok) {
|
|
25477
|
+
throw new Error(`Binance 24hr tickers failed: ${response.status}`);
|
|
25478
|
+
}
|
|
25479
|
+
const data = await response.json();
|
|
25480
|
+
const result = {};
|
|
25481
|
+
for (const item of data) {
|
|
25482
|
+
result[item.symbol] = {
|
|
25483
|
+
lastPrice: parseFloat(item.lastPrice),
|
|
25484
|
+
prevClosePrice: parseFloat(item.prevClosePrice),
|
|
25485
|
+
priceChangePercent: parseFloat(item.priceChangePercent)
|
|
25486
|
+
};
|
|
25487
|
+
}
|
|
25488
|
+
return result;
|
|
25489
|
+
}
|
|
25490
|
+
|
|
25491
|
+
// src/react/hooks/use-symm-token-selection-markets.ts
|
|
25373
25492
|
function useSymmTokenSelectionMarkets(params) {
|
|
25374
25493
|
const query = useSymmHedgerMarkets(params);
|
|
25375
25494
|
const baseMarkets = query.data?.filteredMarkets ?? query.data?.markets ?? [];
|
|
@@ -25385,48 +25504,27 @@ function useSymmTokenSelectionMarkets(params) {
|
|
|
25385
25504
|
() => [...marketSymbols].sort().join(","),
|
|
25386
25505
|
[marketSymbols]
|
|
25387
25506
|
);
|
|
25388
|
-
const symbolToBinanceMap = react.useMemo(
|
|
25389
|
-
() => new Map(
|
|
25390
|
-
marketSymbols.map((symbol) => {
|
|
25391
|
-
const binanceSymbol = resolveBinanceSymbol(symbol).binanceSymbol;
|
|
25392
|
-
if (!binanceSymbol) return null;
|
|
25393
|
-
return [symbol, binanceSymbol];
|
|
25394
|
-
}).filter((entry) => !!entry)
|
|
25395
|
-
),
|
|
25396
|
-
[marketSymbols]
|
|
25397
|
-
);
|
|
25398
25507
|
const liveMarkPrices = useBinanceMarkPriceStore((state) => state.markPrices);
|
|
25399
|
-
const subscribeSymbol = useBinanceMarkPriceStore((state) => state.subscribeSymbol);
|
|
25400
|
-
const unsubscribeSymbol = useBinanceMarkPriceStore((state) => state.unsubscribeSymbol);
|
|
25401
25508
|
const priceQuery = reactQuery.useQuery({
|
|
25402
25509
|
queryKey: ["symm", "token-selection-markets-price", symbolsKey],
|
|
25403
25510
|
queryFn: async () => {
|
|
25511
|
+
const allTickers = await fetch24hrTickers();
|
|
25404
25512
|
const tickerSnapshots = {};
|
|
25405
|
-
|
|
25406
|
-
|
|
25407
|
-
|
|
25408
|
-
|
|
25409
|
-
|
|
25513
|
+
marketSymbols.forEach((symbol) => {
|
|
25514
|
+
const binanceSymbol = resolveBinanceSymbol(symbol).binanceSymbol;
|
|
25515
|
+
if (!binanceSymbol) {
|
|
25516
|
+
tickerSnapshots[symbol] = null;
|
|
25517
|
+
return;
|
|
25518
|
+
}
|
|
25519
|
+
const ticker = allTickers[binanceSymbol];
|
|
25520
|
+
tickerSnapshots[symbol] = ticker ? { prevClosePrice: ticker.prevClosePrice } : null;
|
|
25521
|
+
});
|
|
25410
25522
|
return { tickerSnapshots };
|
|
25411
25523
|
},
|
|
25412
25524
|
enabled: query.isSuccess && marketSymbols.length > 0,
|
|
25413
25525
|
staleTime: 6e4,
|
|
25414
25526
|
gcTime: 5 * 6e4
|
|
25415
25527
|
});
|
|
25416
|
-
react.useEffect(() => {
|
|
25417
|
-
if (!query.isSuccess || symbolToBinanceMap.size === 0) {
|
|
25418
|
-
return;
|
|
25419
|
-
}
|
|
25420
|
-
const symbolEntries = Array.from(symbolToBinanceMap.entries());
|
|
25421
|
-
symbolEntries.forEach(
|
|
25422
|
-
([symbol, binanceSymbol]) => subscribeSymbol(symbol, binanceSymbol)
|
|
25423
|
-
);
|
|
25424
|
-
return () => {
|
|
25425
|
-
symbolEntries.forEach(
|
|
25426
|
-
([symbol, binanceSymbol]) => unsubscribeSymbol(symbol, binanceSymbol)
|
|
25427
|
-
);
|
|
25428
|
-
};
|
|
25429
|
-
}, [query.isSuccess, symbolsKey, symbolToBinanceMap, subscribeSymbol, unsubscribeSymbol]);
|
|
25430
25528
|
const markets = react.useMemo(() => {
|
|
25431
25529
|
const snapshots = priceQuery.data?.tickerSnapshots ?? {};
|
|
25432
25530
|
return baseMarkets.map((market) => {
|
|
@@ -25455,6 +25553,61 @@ function useSymmTokenSelectionMarkets(params) {
|
|
|
25455
25553
|
() => new Map(markets.map((market) => [market.id, market])),
|
|
25456
25554
|
[markets]
|
|
25457
25555
|
);
|
|
25556
|
+
react.useEffect(() => {
|
|
25557
|
+
console.debug("[useSymmTokenSelectionMarkets] data flow", {
|
|
25558
|
+
params,
|
|
25559
|
+
query: {
|
|
25560
|
+
status: query.status,
|
|
25561
|
+
fetchStatus: query.fetchStatus,
|
|
25562
|
+
isLoading: query.isLoading,
|
|
25563
|
+
isSuccess: query.isSuccess,
|
|
25564
|
+
isError: query.isError,
|
|
25565
|
+
error: query.error
|
|
25566
|
+
},
|
|
25567
|
+
priceQuery: {
|
|
25568
|
+
status: priceQuery.status,
|
|
25569
|
+
fetchStatus: priceQuery.fetchStatus,
|
|
25570
|
+
isLoading: priceQuery.isLoading,
|
|
25571
|
+
isSuccess: priceQuery.isSuccess,
|
|
25572
|
+
isError: priceQuery.isError,
|
|
25573
|
+
error: priceQuery.error
|
|
25574
|
+
},
|
|
25575
|
+
marketSymbols,
|
|
25576
|
+
symbolsKey,
|
|
25577
|
+
liveMarkPrices,
|
|
25578
|
+
tickerSnapshots: priceQuery.data?.tickerSnapshots ?? {},
|
|
25579
|
+
result: {
|
|
25580
|
+
marketCount: markets.length,
|
|
25581
|
+
markets: markets.map((market) => ({
|
|
25582
|
+
id: market.id,
|
|
25583
|
+
symbol: market.symbol,
|
|
25584
|
+
markPrice: market.markPrice,
|
|
25585
|
+
prevDayPrice: market.prevDayPrice,
|
|
25586
|
+
priceChange24h: market.priceChange24h,
|
|
25587
|
+
priceChange24hPercent: market.priceChange24hPercent
|
|
25588
|
+
}))
|
|
25589
|
+
}
|
|
25590
|
+
});
|
|
25591
|
+
}, [
|
|
25592
|
+
liveMarkPrices,
|
|
25593
|
+
marketSymbols,
|
|
25594
|
+
markets,
|
|
25595
|
+
params,
|
|
25596
|
+
priceQuery.data,
|
|
25597
|
+
priceQuery.error,
|
|
25598
|
+
priceQuery.fetchStatus,
|
|
25599
|
+
priceQuery.isError,
|
|
25600
|
+
priceQuery.isLoading,
|
|
25601
|
+
priceQuery.isSuccess,
|
|
25602
|
+
priceQuery.status,
|
|
25603
|
+
query.error,
|
|
25604
|
+
query.fetchStatus,
|
|
25605
|
+
query.isError,
|
|
25606
|
+
query.isLoading,
|
|
25607
|
+
query.isSuccess,
|
|
25608
|
+
query.status,
|
|
25609
|
+
symbolsKey
|
|
25610
|
+
]);
|
|
25458
25611
|
return {
|
|
25459
25612
|
query,
|
|
25460
25613
|
priceQuery,
|
|
@@ -25465,6 +25618,22 @@ function useSymmTokenSelectionMarkets(params) {
|
|
|
25465
25618
|
isPriceLoading: priceQuery.isLoading
|
|
25466
25619
|
};
|
|
25467
25620
|
}
|
|
25621
|
+
function useSymmTokenMarkPrice(symbol) {
|
|
25622
|
+
const normalizedSymbol = symbol?.trim().toUpperCase() || null;
|
|
25623
|
+
const resolution = resolveBinanceSymbol(normalizedSymbol ?? "");
|
|
25624
|
+
const liveMarkPrices = useBinanceMarkPriceStore((state) => state.markPrices);
|
|
25625
|
+
const markPrice = react.useMemo(() => {
|
|
25626
|
+
if (!normalizedSymbol) return null;
|
|
25627
|
+
return liveMarkPrices[normalizedSymbol] ?? null;
|
|
25628
|
+
}, [liveMarkPrices, normalizedSymbol]);
|
|
25629
|
+
return {
|
|
25630
|
+
symbol: normalizedSymbol,
|
|
25631
|
+
binanceSymbol: resolution.binanceSymbol,
|
|
25632
|
+
markPrice,
|
|
25633
|
+
isSupported: resolution.supported,
|
|
25634
|
+
reason: resolution.reason
|
|
25635
|
+
};
|
|
25636
|
+
}
|
|
25468
25637
|
function useSymmFunding(params) {
|
|
25469
25638
|
const { symmCoreClient, chainId: ctxChainId } = useSymmContext();
|
|
25470
25639
|
const chainId = params?.chainId ?? ctxChainId;
|
|
@@ -25973,7 +26142,7 @@ function computeNetFundingSum({
|
|
|
25973
26142
|
}
|
|
25974
26143
|
|
|
25975
26144
|
// src/react/hooks/use-symm-token-selection-metadata.ts
|
|
25976
|
-
async function
|
|
26145
|
+
async function fetchTickerSnapshot(symbol) {
|
|
25977
26146
|
const resolution = resolveBinanceSymbol(symbol);
|
|
25978
26147
|
if (!resolution.binanceSymbol) return null;
|
|
25979
26148
|
const ticker = await fetch24hrTicker(resolution.binanceSymbol);
|
|
@@ -26005,17 +26174,7 @@ function useSymmTokenSelectionMetadata(selection, options = {}) {
|
|
|
26005
26174
|
const isUnsupported = unsupportedSymbols.length > 0;
|
|
26006
26175
|
const unavailableReason = isUnsupported ? `Binance market data is unavailable for ${unsupportedSymbols.join(", ")}.` : null;
|
|
26007
26176
|
const symbolsKey = [...selectedSymbols].sort().join(",");
|
|
26008
|
-
const symbolToBinanceMap = react.useMemo(
|
|
26009
|
-
() => new Map(
|
|
26010
|
-
selectedSymbols.map(
|
|
26011
|
-
(symbol) => [symbol, resolveBinanceSymbol(symbol).binanceSymbol]
|
|
26012
|
-
).filter((entry) => !!entry[1])
|
|
26013
|
-
),
|
|
26014
|
-
[selectedSymbols]
|
|
26015
|
-
);
|
|
26016
26177
|
const liveMarkPrices = useBinanceMarkPriceStore((state) => state.markPrices);
|
|
26017
|
-
const subscribeSymbol = useBinanceMarkPriceStore((state) => state.subscribeSymbol);
|
|
26018
|
-
const unsubscribeSymbol = useBinanceMarkPriceStore((state) => state.unsubscribeSymbol);
|
|
26019
26178
|
const query = reactQuery.useQuery({
|
|
26020
26179
|
queryKey: ["symm", "chart-metadata", symbolsKey],
|
|
26021
26180
|
queryFn: async () => {
|
|
@@ -26029,7 +26188,7 @@ function useSymmTokenSelectionMetadata(selection, options = {}) {
|
|
|
26029
26188
|
const results = await Promise.all(
|
|
26030
26189
|
allSymbols.map(async ({ symbol }) => ({
|
|
26031
26190
|
symbol,
|
|
26032
|
-
ticker: await
|
|
26191
|
+
ticker: await fetchTickerSnapshot(symbol)
|
|
26033
26192
|
}))
|
|
26034
26193
|
);
|
|
26035
26194
|
const tickerSnapshots = {};
|
|
@@ -26041,28 +26200,7 @@ function useSymmTokenSelectionMetadata(selection, options = {}) {
|
|
|
26041
26200
|
enabled: enabled && selectedSymbols.length > 0 && !isUnsupported,
|
|
26042
26201
|
gcTime: 5 * 6e4
|
|
26043
26202
|
});
|
|
26044
|
-
react.
|
|
26045
|
-
if (!enabled || isUnsupported || selectedSymbols.length === 0) {
|
|
26046
|
-
return;
|
|
26047
|
-
}
|
|
26048
|
-
const symbolEntries = Array.from(symbolToBinanceMap.entries());
|
|
26049
|
-
symbolEntries.forEach(
|
|
26050
|
-
([symbol, binanceSymbol]) => subscribeSymbol(symbol, binanceSymbol)
|
|
26051
|
-
);
|
|
26052
|
-
return () => {
|
|
26053
|
-
symbolEntries.forEach(
|
|
26054
|
-
([symbol, binanceSymbol]) => unsubscribeSymbol(symbol, binanceSymbol)
|
|
26055
|
-
);
|
|
26056
|
-
};
|
|
26057
|
-
}, [
|
|
26058
|
-
enabled,
|
|
26059
|
-
isUnsupported,
|
|
26060
|
-
selectedSymbols.length,
|
|
26061
|
-
symbolToBinanceMap,
|
|
26062
|
-
subscribeSymbol,
|
|
26063
|
-
unsubscribeSymbol
|
|
26064
|
-
]);
|
|
26065
|
-
return react.useMemo(() => {
|
|
26203
|
+
const result = react.useMemo(() => {
|
|
26066
26204
|
const tickerSnapshots = query.data?.tickerSnapshots ?? {};
|
|
26067
26205
|
const longMeta = {};
|
|
26068
26206
|
const shortMeta = {};
|
|
@@ -26121,6 +26259,53 @@ function useSymmTokenSelectionMetadata(selection, options = {}) {
|
|
|
26121
26259
|
selectedSymbols.length,
|
|
26122
26260
|
liveMarkPrices
|
|
26123
26261
|
]);
|
|
26262
|
+
react.useEffect(() => {
|
|
26263
|
+
console.debug("[useSymmTokenSelectionMetadata] data flow", {
|
|
26264
|
+
selection,
|
|
26265
|
+
options,
|
|
26266
|
+
query: {
|
|
26267
|
+
status: query.status,
|
|
26268
|
+
fetchStatus: query.fetchStatus,
|
|
26269
|
+
isLoading: query.isLoading,
|
|
26270
|
+
isSuccess: query.isSuccess,
|
|
26271
|
+
isError: query.isError,
|
|
26272
|
+
error: query.error
|
|
26273
|
+
},
|
|
26274
|
+
selectedSymbols,
|
|
26275
|
+
unsupportedSymbols,
|
|
26276
|
+
unavailableReason,
|
|
26277
|
+
liveMarkPrices,
|
|
26278
|
+
tickerSnapshots: query.data?.tickerSnapshots ?? {},
|
|
26279
|
+
result: {
|
|
26280
|
+
isLoading: result.isLoading,
|
|
26281
|
+
isPriceDataReady: result.isPriceDataReady,
|
|
26282
|
+
isUnsupported: result.isUnsupported,
|
|
26283
|
+
longTokensMetadata: result.longTokensMetadata,
|
|
26284
|
+
shortTokensMetadata: result.shortTokensMetadata,
|
|
26285
|
+
weightedRatio: result.weightedRatio,
|
|
26286
|
+
weightedRatio24h: result.weightedRatio24h,
|
|
26287
|
+
priceRatio: result.priceRatio,
|
|
26288
|
+
priceRatio24h: result.priceRatio24h,
|
|
26289
|
+
sumNetFunding: result.sumNetFunding
|
|
26290
|
+
}
|
|
26291
|
+
});
|
|
26292
|
+
}, [
|
|
26293
|
+
liveMarkPrices,
|
|
26294
|
+
options,
|
|
26295
|
+
query.data,
|
|
26296
|
+
query.error,
|
|
26297
|
+
query.fetchStatus,
|
|
26298
|
+
query.isError,
|
|
26299
|
+
query.isLoading,
|
|
26300
|
+
query.isSuccess,
|
|
26301
|
+
query.status,
|
|
26302
|
+
result,
|
|
26303
|
+
selectedSymbols,
|
|
26304
|
+
selection,
|
|
26305
|
+
unavailableReason,
|
|
26306
|
+
unsupportedSymbols
|
|
26307
|
+
]);
|
|
26308
|
+
return result;
|
|
26124
26309
|
}
|
|
26125
26310
|
|
|
26126
26311
|
// src/utils/binance-intervals.ts
|
|
@@ -26561,6 +26746,7 @@ exports.useSymmSetTpslMutation = useSymmSetTpslMutation;
|
|
|
26561
26746
|
exports.useSymmSetTriggerConfigMutation = useSymmSetTriggerConfigMutation;
|
|
26562
26747
|
exports.useSymmSignTermsMutation = useSymmSignTermsMutation;
|
|
26563
26748
|
exports.useSymmSignatureQuery = useSymmSignatureQuery;
|
|
26749
|
+
exports.useSymmTokenMarkPrice = useSymmTokenMarkPrice;
|
|
26564
26750
|
exports.useSymmTokenSelectionMarkets = useSymmTokenSelectionMarkets;
|
|
26565
26751
|
exports.useSymmTokenSelectionMetadata = useSymmTokenSelectionMetadata;
|
|
26566
26752
|
exports.useSymmTpslOrders = useSymmTpslOrders;
|