@basedone/core 0.0.1 → 0.0.7
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/chunk-4UEJOM6W.mjs +9 -0
- package/dist/index.d.mts +488 -0
- package/dist/index.d.ts +488 -2
- package/dist/index.js +37574 -2
- package/dist/index.mjs +1097 -0
- package/dist/lib/cloid.d.ts.map +1 -1
- package/dist/lib/cloid.js +11 -1
- package/dist/lib/cloid.js.map +1 -1
- package/dist/meta-52Q5UUQ4.mjs +1474 -0
- package/dist/meta-FTWJX4LV.mjs +1445 -0
- package/dist/meta-IKWYLG3Q.mjs +1316 -0
- package/dist/meta-UUXKK7IB.mjs +1355 -0
- package/dist/perpDexs-PSE3LEVV.mjs +9 -0
- package/dist/perpDexs-S3TK25EU.mjs +17 -0
- package/dist/perpDexs-TZIQ57IW.mjs +537 -0
- package/dist/perpDexs-YNEAJ3R5.mjs +7 -0
- package/dist/perpDexs-YS3QQSHW.mjs +338 -0
- package/dist/spotMeta-7IJT3W6H.mjs +6442 -0
- package/dist/spotMeta-LEO5QFNS.mjs +26392 -0
- package/dist/spotMeta-MC5UYLQ7.mjs +6335 -0
- package/dist/spotMeta-TXJWYTKI.mjs +26403 -0
- package/dist/spotMeta-VAANYV77.mjs +6346 -0
- package/dist/spotMeta-ZVBZNUUE.mjs +26559 -0
- package/dist/staticMeta-HRXST42O.mjs +24 -0
- package/dist/staticMeta-QWPQK3MD.mjs +22 -0
- package/index.ts +6 -0
- package/lib/cloid/README.md +233 -0
- package/lib/cloid/cloid.ts +368 -0
- package/lib/cloid/encoder.ts +60 -0
- package/lib/constants/fee.ts +2 -0
- package/lib/constants/tokens.ts +28 -0
- package/lib/fee.ts +105 -0
- package/lib/hip3/market-info.ts +25 -0
- package/lib/hip3/utils.ts +9 -0
- package/lib/meta/README.md +471 -0
- package/lib/meta/data/mainnet/dexs/xyz.json +26 -0
- package/lib/meta/data/mainnet/meta.json +1462 -0
- package/lib/meta/data/mainnet/perpDexs.json +11 -0
- package/lib/meta/data/mainnet/spotMeta.json +6432 -0
- package/lib/meta/data/mainnet/staticMeta.json +14 -0
- package/lib/meta/data/testnet/dexs/rrrrr.json +33 -0
- package/lib/meta/data/testnet/meta.json +1343 -0
- package/lib/meta/data/testnet/perpDexs.json +531 -0
- package/lib/meta/data/testnet/spotMeta.json +26547 -0
- package/lib/meta/data/testnet/staticMeta.json +12 -0
- package/lib/meta/metadata.ts +717 -0
- package/lib/pup/calculator.ts +221 -0
- package/lib/pup/index.ts +9 -0
- package/lib/pup/types.ts +94 -0
- package/lib/utils/formatter.ts +97 -0
- package/package.json +21 -17
- package/readme.md +0 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1097 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__glob
|
|
3
|
+
} from "./chunk-4UEJOM6W.mjs";
|
|
4
|
+
|
|
5
|
+
// lib/cloid/encoder.ts
|
|
6
|
+
var mask = (bits) => (1n << bits) - 1n;
|
|
7
|
+
function getEncodingLength(nbits, alphabet) {
|
|
8
|
+
return Math.floor(Number(nbits) / Math.log2(Number(alphabet.length)));
|
|
9
|
+
}
|
|
10
|
+
function encodeValue(value, nbits, alphabet) {
|
|
11
|
+
const encodingLength = getEncodingLength(nbits, alphabet);
|
|
12
|
+
if (value.length > encodingLength) {
|
|
13
|
+
value = value.slice(0, encodingLength);
|
|
14
|
+
}
|
|
15
|
+
if (value.length < encodingLength) {
|
|
16
|
+
value = value.padEnd(encodingLength, alphabet[alphabet.length - 1]);
|
|
17
|
+
}
|
|
18
|
+
const base = BigInt(alphabet.length);
|
|
19
|
+
let encoded = 0n;
|
|
20
|
+
for (const ch of value) {
|
|
21
|
+
const idx = alphabet.indexOf(ch);
|
|
22
|
+
if (idx === -1) throw new Error(`Invalid slug character: ${ch}`);
|
|
23
|
+
encoded = encoded * base + BigInt(idx);
|
|
24
|
+
}
|
|
25
|
+
if (encoded > Number(mask(nbits))) {
|
|
26
|
+
throw new Error(`Encoded value exceeds ${nbits} bits`);
|
|
27
|
+
}
|
|
28
|
+
return encoded;
|
|
29
|
+
}
|
|
30
|
+
function decodeValue(value, nbits, alphabet) {
|
|
31
|
+
if (value < 0 || value > Number(mask(nbits))) {
|
|
32
|
+
throw new Error(`Slug value out of range (must fit in ${nbits} bits)`);
|
|
33
|
+
}
|
|
34
|
+
const base = BigInt(alphabet.length);
|
|
35
|
+
let decoded = "";
|
|
36
|
+
for (let i = 0; i < getEncodingLength(nbits, alphabet); i++) {
|
|
37
|
+
const idx = value % base;
|
|
38
|
+
decoded = alphabet[Number(idx)] + decoded;
|
|
39
|
+
value = value / base;
|
|
40
|
+
}
|
|
41
|
+
return decoded.trim();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// lib/cloid/cloid.ts
|
|
45
|
+
var LEGACY_CLOID = "0x62baee7262baee7262baee7262baee72";
|
|
46
|
+
var BASED_VERSION = 1;
|
|
47
|
+
var DEFAULT_TENANT_CODE = "BASED";
|
|
48
|
+
var DEFAULT_OID = 0xba5ed10000ba5ed1n;
|
|
49
|
+
var BASED_HEX_PREFIX = 12213969;
|
|
50
|
+
var BASED_HEX_PREFIX_STR = "0xba5ed1";
|
|
51
|
+
var PREFIX_BITS = 24n;
|
|
52
|
+
var VERSION_BITS = 4n;
|
|
53
|
+
var SLUG_BITS = 26n;
|
|
54
|
+
var CLIENT_BITS = 5n;
|
|
55
|
+
var IS_MINI_APP_BITS = 1n;
|
|
56
|
+
var MINI_APP_TRIGGERED_BITS = 1n;
|
|
57
|
+
var WIDGET_TYPE_BITS = 3n;
|
|
58
|
+
var OID_BITS = 64n;
|
|
59
|
+
var TOTAL_BITS = 128n;
|
|
60
|
+
var SLUG_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
61
|
+
var OID_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789|_- ";
|
|
62
|
+
var mask2 = (bits) => (1n << bits) - 1n;
|
|
63
|
+
function encodeSlug(slug) {
|
|
64
|
+
if (!slug) slug = DEFAULT_TENANT_CODE;
|
|
65
|
+
return encodeValue(slug, SLUG_BITS, SLUG_ALPHABET);
|
|
66
|
+
}
|
|
67
|
+
function decodeSlug(value) {
|
|
68
|
+
return decodeValue(value, SLUG_BITS, SLUG_ALPHABET);
|
|
69
|
+
}
|
|
70
|
+
function buildCloid(version, slug, clientId, oid, options) {
|
|
71
|
+
if (version > 15) version = BASED_VERSION;
|
|
72
|
+
if (clientId > Number(mask2(CLIENT_BITS))) {
|
|
73
|
+
clientId = CloidClientCode.Unset;
|
|
74
|
+
}
|
|
75
|
+
const isMiniApp = options?.isMiniApp ? 1 : 0;
|
|
76
|
+
const miniAppTriggered = options?.miniAppTriggered ? 1 : 0;
|
|
77
|
+
const widgetType = typeof options?.widgetType === "number" ? options.widgetType : WidgetType[options?.widgetType ?? "Unset"];
|
|
78
|
+
if (widgetType > Number(mask2(WIDGET_TYPE_BITS))) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Component ID ${widgetType} exceeds maximum value ${Number(mask2(WIDGET_TYPE_BITS))}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
let oidBigInt;
|
|
84
|
+
if (typeof oid === "string") {
|
|
85
|
+
if (oid.startsWith("0x") || !isNaN(Number(oid))) {
|
|
86
|
+
oidBigInt = BigInt(oid);
|
|
87
|
+
} else {
|
|
88
|
+
oidBigInt = encodeValue(oid.toLowerCase(), OID_BITS, OID_ALPHABET);
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
oidBigInt = oid;
|
|
92
|
+
}
|
|
93
|
+
if (oidBigInt > mask2(OID_BITS)) {
|
|
94
|
+
oidBigInt = DEFAULT_OID;
|
|
95
|
+
}
|
|
96
|
+
const slugVal = encodeSlug(slug.toUpperCase());
|
|
97
|
+
let cloid = BigInt(BASED_HEX_PREFIX) << TOTAL_BITS - PREFIX_BITS;
|
|
98
|
+
cloid |= BigInt(version & 15) << TOTAL_BITS - PREFIX_BITS - VERSION_BITS;
|
|
99
|
+
cloid |= BigInt(slugVal) << TOTAL_BITS - PREFIX_BITS - VERSION_BITS - SLUG_BITS;
|
|
100
|
+
cloid |= BigInt(clientId) << OID_BITS;
|
|
101
|
+
cloid |= BigInt(isMiniApp) << OID_BITS + CLIENT_BITS;
|
|
102
|
+
cloid |= BigInt(miniAppTriggered) << OID_BITS + CLIENT_BITS + IS_MINI_APP_BITS;
|
|
103
|
+
cloid |= BigInt(widgetType) << OID_BITS + CLIENT_BITS + IS_MINI_APP_BITS + MINI_APP_TRIGGERED_BITS;
|
|
104
|
+
cloid |= oidBigInt & mask2(OID_BITS);
|
|
105
|
+
return `0x${cloid.toString(16)}`;
|
|
106
|
+
}
|
|
107
|
+
function parseCloid(cloidHex) {
|
|
108
|
+
if (cloidHex === LEGACY_CLOID) {
|
|
109
|
+
return {
|
|
110
|
+
prefix: LEGACY_CLOID,
|
|
111
|
+
version: 0,
|
|
112
|
+
slug: "BASED",
|
|
113
|
+
clientId: CloidClientCode.Unset,
|
|
114
|
+
isMiniApp: false,
|
|
115
|
+
miniAppTriggered: false,
|
|
116
|
+
widgetTypeId: WidgetType.Unset,
|
|
117
|
+
oid: 0xba5ed10000ba5ed1n,
|
|
118
|
+
decodedOid: decodeValue(0xba5ed10000ba5ed1n, OID_BITS, OID_ALPHABET),
|
|
119
|
+
clientName: "Unset",
|
|
120
|
+
widgetTypeName: "Unset"
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const cloid = BigInt(cloidHex);
|
|
125
|
+
const prefix = Number(cloid >> TOTAL_BITS - PREFIX_BITS);
|
|
126
|
+
const version = Number(
|
|
127
|
+
cloid >> TOTAL_BITS - PREFIX_BITS - VERSION_BITS & mask2(VERSION_BITS)
|
|
128
|
+
);
|
|
129
|
+
const slugVal = Number(
|
|
130
|
+
cloid >> OID_BITS + WIDGET_TYPE_BITS + MINI_APP_TRIGGERED_BITS + IS_MINI_APP_BITS + CLIENT_BITS & mask2(SLUG_BITS)
|
|
131
|
+
);
|
|
132
|
+
const clientId = Number(cloid >> OID_BITS & mask2(CLIENT_BITS));
|
|
133
|
+
const isMiniApp = Number(cloid >> OID_BITS + CLIENT_BITS & mask2(IS_MINI_APP_BITS)) === 1;
|
|
134
|
+
const miniAppTriggered = Number(
|
|
135
|
+
cloid >> OID_BITS + CLIENT_BITS + IS_MINI_APP_BITS & mask2(MINI_APP_TRIGGERED_BITS)
|
|
136
|
+
) === 1;
|
|
137
|
+
const widgetType = Number(
|
|
138
|
+
cloid >> OID_BITS + CLIENT_BITS + IS_MINI_APP_BITS + MINI_APP_TRIGGERED_BITS & mask2(WIDGET_TYPE_BITS)
|
|
139
|
+
);
|
|
140
|
+
const oid = cloid & mask2(OID_BITS);
|
|
141
|
+
return {
|
|
142
|
+
prefix: `0x${prefix.toString(16)}`,
|
|
143
|
+
// should equal 0xBA5ED1
|
|
144
|
+
version,
|
|
145
|
+
slug: decodeSlug(BigInt(slugVal)),
|
|
146
|
+
clientId,
|
|
147
|
+
isMiniApp,
|
|
148
|
+
miniAppTriggered,
|
|
149
|
+
widgetTypeId: widgetType,
|
|
150
|
+
oid,
|
|
151
|
+
decodedOid: decodeValue(oid, OID_BITS, OID_ALPHABET),
|
|
152
|
+
clientName: getClientCodeNameById(clientId),
|
|
153
|
+
widgetTypeName: getWidgetTypeById(widgetType)
|
|
154
|
+
};
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error("Invalid cloid", { cloidHex });
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function isBasedCloid(cloidHex) {
|
|
161
|
+
if (!cloidHex) return false;
|
|
162
|
+
if (cloidHex === LEGACY_CLOID) return true;
|
|
163
|
+
return cloidHex.toLowerCase().startsWith(BASED_HEX_PREFIX_STR);
|
|
164
|
+
}
|
|
165
|
+
function isTenantCloid(tenantTrackingSlug, cloidHex) {
|
|
166
|
+
if (!cloidHex || !isBasedCloid(cloidHex)) return false;
|
|
167
|
+
const cloidData = parseCloid(cloidHex);
|
|
168
|
+
if (!cloidData) return false;
|
|
169
|
+
const { slug } = cloidData;
|
|
170
|
+
if (tenantTrackingSlug.length > 5) {
|
|
171
|
+
tenantTrackingSlug = tenantTrackingSlug.slice(0, 5);
|
|
172
|
+
}
|
|
173
|
+
const normalisedSlug = decodeSlug(encodeSlug(tenantTrackingSlug));
|
|
174
|
+
return slug === normalisedSlug;
|
|
175
|
+
}
|
|
176
|
+
function isTrackingIdCloid(trackingId, cloidHex) {
|
|
177
|
+
if (!cloidHex || !isBasedCloid(cloidHex)) return false;
|
|
178
|
+
const cloidData = parseCloid(cloidHex);
|
|
179
|
+
if (!cloidData) return false;
|
|
180
|
+
if (typeof trackingId === "string") {
|
|
181
|
+
const encodedTrackingId = normaliseTrackingId(trackingId);
|
|
182
|
+
return encodedTrackingId === cloidData.decodedOid;
|
|
183
|
+
} else {
|
|
184
|
+
const { oid } = cloidData;
|
|
185
|
+
return oid === trackingId;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function normaliseSlug(slug) {
|
|
189
|
+
if (!slug) return "";
|
|
190
|
+
return decodeSlug(encodeSlug(slug.toUpperCase()));
|
|
191
|
+
}
|
|
192
|
+
function normaliseTrackingId(trackingId) {
|
|
193
|
+
if (!trackingId) return "";
|
|
194
|
+
return decodeValue(
|
|
195
|
+
encodeValue(trackingId.toLowerCase(), OID_BITS, OID_ALPHABET),
|
|
196
|
+
OID_BITS,
|
|
197
|
+
OID_ALPHABET
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
var CloidClientCode = {
|
|
201
|
+
Unset: 0,
|
|
202
|
+
// 0x00
|
|
203
|
+
Web: 1,
|
|
204
|
+
App: 2,
|
|
205
|
+
TgBot: 3,
|
|
206
|
+
TSL: 4,
|
|
207
|
+
// Trailing Stop Loss
|
|
208
|
+
Grid: 5,
|
|
209
|
+
Chase: 6,
|
|
210
|
+
Desktop: 7,
|
|
211
|
+
API: 8
|
|
212
|
+
};
|
|
213
|
+
var CloidClientCodeNameById = {
|
|
214
|
+
[CloidClientCode.Unset]: "Unset",
|
|
215
|
+
[CloidClientCode.Web]: "Web",
|
|
216
|
+
[CloidClientCode.App]: "App",
|
|
217
|
+
[CloidClientCode.TgBot]: "TgBot",
|
|
218
|
+
[CloidClientCode.TSL]: "TSL",
|
|
219
|
+
[CloidClientCode.Grid]: "Grid",
|
|
220
|
+
[CloidClientCode.Chase]: "Chase",
|
|
221
|
+
[CloidClientCode.Desktop]: "Desktop",
|
|
222
|
+
[CloidClientCode.API]: "API"
|
|
223
|
+
};
|
|
224
|
+
function getClientCodeNameById(id) {
|
|
225
|
+
return CloidClientCodeNameById[id] ?? `client_${id}`;
|
|
226
|
+
}
|
|
227
|
+
var WidgetType = {
|
|
228
|
+
Unset: 0,
|
|
229
|
+
// 0x00
|
|
230
|
+
SidePanel: 1,
|
|
231
|
+
Widget: 2,
|
|
232
|
+
FloatingWidget: 3
|
|
233
|
+
};
|
|
234
|
+
var WidgetTypeById = {
|
|
235
|
+
[WidgetType.Unset]: "Unset",
|
|
236
|
+
[WidgetType.SidePanel]: "SidePanel",
|
|
237
|
+
[WidgetType.Widget]: "Widget",
|
|
238
|
+
[WidgetType.FloatingWidget]: "FloatingWidget"
|
|
239
|
+
};
|
|
240
|
+
function getWidgetTypeById(id) {
|
|
241
|
+
return WidgetTypeById[id] ?? `widget_${id}`;
|
|
242
|
+
}
|
|
243
|
+
function getCloid(tenantCode, clientCode, oid, attribution) {
|
|
244
|
+
try {
|
|
245
|
+
const clientIdx = typeof clientCode === "number" ? clientCode : CloidClientCode[clientCode ?? "Unset"];
|
|
246
|
+
return buildCloid(
|
|
247
|
+
BASED_VERSION,
|
|
248
|
+
tenantCode ?? DEFAULT_TENANT_CODE,
|
|
249
|
+
clientIdx,
|
|
250
|
+
oid ?? DEFAULT_OID,
|
|
251
|
+
attribution
|
|
252
|
+
);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
console.error(
|
|
255
|
+
"Invalid cloid parameters, defaulting to legacy cloid",
|
|
256
|
+
error,
|
|
257
|
+
{ tenantCode, clientCode, oid, options: attribution }
|
|
258
|
+
);
|
|
259
|
+
return LEGACY_CLOID;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function isClientCode(clientCode, cloidHex) {
|
|
263
|
+
const cloidData = parseCloid(cloidHex);
|
|
264
|
+
if (!cloidData) return false;
|
|
265
|
+
return cloidData.clientId === clientCode;
|
|
266
|
+
}
|
|
267
|
+
function isWidgetType(widgetType, cloidHex) {
|
|
268
|
+
const cloidData = parseCloid(cloidHex);
|
|
269
|
+
if (!cloidData) return false;
|
|
270
|
+
return cloidData.widgetTypeId === widgetType;
|
|
271
|
+
}
|
|
272
|
+
function isMiniAppCloid(cloidHex) {
|
|
273
|
+
const cloidData = parseCloid(cloidHex);
|
|
274
|
+
if (!cloidData) return false;
|
|
275
|
+
return cloidData.isMiniApp;
|
|
276
|
+
}
|
|
277
|
+
function isMiniAppTriggeredCloid(cloidHex) {
|
|
278
|
+
const cloidData = parseCloid(cloidHex);
|
|
279
|
+
if (!cloidData) return false;
|
|
280
|
+
return cloidData.miniAppTriggered;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// lib/constants/fee.ts
|
|
284
|
+
var BASED_FEE_WALLET = "0x1924b8561eeF20e70Ede628A296175D358BE80e5";
|
|
285
|
+
var BASED_REFERRAL_CODE = "SHIFU";
|
|
286
|
+
|
|
287
|
+
// lib/fee.ts
|
|
288
|
+
var TARGET_SPOT_BUILDER_FEE = 100;
|
|
289
|
+
var TARGET_FUTURES_BUILDER_FEE = 25;
|
|
290
|
+
var TARGET_APPROVED_MAX_BUILDER_FEE = Math.max(
|
|
291
|
+
TARGET_SPOT_BUILDER_FEE,
|
|
292
|
+
TARGET_FUTURES_BUILDER_FEE
|
|
293
|
+
);
|
|
294
|
+
var TARGET_APPROVED_MAX_BUILDER_FEE_PERCENT = `0.1%`;
|
|
295
|
+
var getApprovalAmount = ({
|
|
296
|
+
customFeeEnabled,
|
|
297
|
+
perpetualTradingFee,
|
|
298
|
+
spotTradingFee
|
|
299
|
+
}) => {
|
|
300
|
+
if (!customFeeEnabled) {
|
|
301
|
+
return {
|
|
302
|
+
approvalAmount: TARGET_APPROVED_MAX_BUILDER_FEE,
|
|
303
|
+
perpFee: TARGET_FUTURES_BUILDER_FEE,
|
|
304
|
+
spotFee: TARGET_SPOT_BUILDER_FEE,
|
|
305
|
+
approvalPercent: TARGET_APPROVED_MAX_BUILDER_FEE_PERCENT,
|
|
306
|
+
builder: BASED_FEE_WALLET,
|
|
307
|
+
referralCode: BASED_REFERRAL_CODE
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
let validatedPerpFeePct = perpetualTradingFee;
|
|
311
|
+
if (validatedPerpFeePct === void 0) {
|
|
312
|
+
validatedPerpFeePct = TARGET_FUTURES_BUILDER_FEE / 1e3;
|
|
313
|
+
}
|
|
314
|
+
if (validatedPerpFeePct > 0 && validatedPerpFeePct < 0.01) {
|
|
315
|
+
console.warn("Perp fee is less than 0.01%, setting to 0.01%");
|
|
316
|
+
validatedPerpFeePct = 0.01;
|
|
317
|
+
}
|
|
318
|
+
if (validatedPerpFeePct < 0) {
|
|
319
|
+
console.warn("Perp fee is less than 0, setting to 0");
|
|
320
|
+
validatedPerpFeePct = 0;
|
|
321
|
+
}
|
|
322
|
+
if (validatedPerpFeePct > 0.1) {
|
|
323
|
+
console.warn("Perp fee is greater than 0.1%, setting to 0.1%");
|
|
324
|
+
validatedPerpFeePct = 0.1;
|
|
325
|
+
}
|
|
326
|
+
let validatedSpotFeePct = spotTradingFee;
|
|
327
|
+
if (validatedSpotFeePct === void 0) {
|
|
328
|
+
validatedSpotFeePct = TARGET_SPOT_BUILDER_FEE / 1e3;
|
|
329
|
+
}
|
|
330
|
+
if (validatedSpotFeePct > 0 && validatedSpotFeePct < 0.025) {
|
|
331
|
+
console.warn("Spot fee is less than 0.025%, setting to 0.025%");
|
|
332
|
+
validatedSpotFeePct = 0.025;
|
|
333
|
+
}
|
|
334
|
+
if (validatedSpotFeePct < 0) {
|
|
335
|
+
console.warn("Spot fee is less than 0, setting to 0");
|
|
336
|
+
validatedSpotFeePct = 0;
|
|
337
|
+
}
|
|
338
|
+
if (validatedSpotFeePct > 1) {
|
|
339
|
+
console.warn("Spot fee is greater than 1%, setting to 1%");
|
|
340
|
+
validatedSpotFeePct = 1;
|
|
341
|
+
}
|
|
342
|
+
const perpFee = Math.floor(validatedPerpFeePct * 1e3);
|
|
343
|
+
const spotFee = Math.floor(validatedSpotFeePct * 1e3);
|
|
344
|
+
const requiredPercent = validatedPerpFeePct > validatedSpotFeePct ? validatedPerpFeePct : validatedSpotFeePct;
|
|
345
|
+
const requiredAmount = Math.max(Math.floor(requiredPercent * 1e3), 1);
|
|
346
|
+
const requiredPercentString = `${requiredAmount / 1e3}%`;
|
|
347
|
+
return {
|
|
348
|
+
approvalAmount: requiredAmount,
|
|
349
|
+
approvalPercent: requiredPercentString,
|
|
350
|
+
perpFee,
|
|
351
|
+
spotFee,
|
|
352
|
+
builder: BASED_FEE_WALLET,
|
|
353
|
+
referralCode: BASED_REFERRAL_CODE
|
|
354
|
+
};
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
// lib/pup/types.ts
|
|
358
|
+
var PUP_TOKEN_ADDRESS = "0x876e7f2f30935118a654fc0e1f807afc49efe500";
|
|
359
|
+
var PUP_TOKEN_THRESHOLDS = {
|
|
360
|
+
AIRDROP_70_PERCENT: 0.7,
|
|
361
|
+
AIRDROP_35_PERCENT: 0.35,
|
|
362
|
+
AIRDROP_110_PERCENT: 1.1,
|
|
363
|
+
NON_AIRDROP_TOKENS: 2e6
|
|
364
|
+
};
|
|
365
|
+
var XP_BOOST_PERCENTAGES = {
|
|
366
|
+
NO_BOOST: 0,
|
|
367
|
+
TIER_1: 25,
|
|
368
|
+
TIER_2: 50,
|
|
369
|
+
TIER_3: 60
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// lib/pup/calculator.ts
|
|
373
|
+
function calculateTotalPupAmount(positions) {
|
|
374
|
+
let totalPupAmount = 0;
|
|
375
|
+
const pupAddress = PUP_TOKEN_ADDRESS.toLowerCase();
|
|
376
|
+
for (const position of positions) {
|
|
377
|
+
try {
|
|
378
|
+
if (position.version === "erc20" && position.pair === "PUP") {
|
|
379
|
+
const amount = parseFloat(position.amount) / 1e18;
|
|
380
|
+
totalPupAmount += amount;
|
|
381
|
+
console.log(`Found pure PUP position: ${amount} PUP`);
|
|
382
|
+
} else if (position.version === "v3" && position.v3LPTokenInfo) {
|
|
383
|
+
const { token0, token1 } = position.v3LPTokenInfo;
|
|
384
|
+
if (token0.address.toLowerCase() === pupAddress || token0.symbol === "PUP") {
|
|
385
|
+
totalPupAmount += token0.amount;
|
|
386
|
+
console.log(`Found V3 LP position with PUP as token0: ${token0.amount} PUP`);
|
|
387
|
+
} else if (token1.address.toLowerCase() === pupAddress || token1.symbol === "PUP") {
|
|
388
|
+
totalPupAmount += token1.amount;
|
|
389
|
+
console.log(`Found V3 LP position with PUP as token1: ${token1.amount} PUP`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
} catch (error) {
|
|
393
|
+
console.error(`Error processing position ${position.pair}:`, error);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
console.log(`Total PUP amount calculated: ${totalPupAmount}`);
|
|
397
|
+
return totalPupAmount;
|
|
398
|
+
}
|
|
399
|
+
function calculateBoostPercentage(pupTokenAmount, normalizedAirDropAmount, isAirdropRecipient) {
|
|
400
|
+
if (isAirdropRecipient && normalizedAirDropAmount > 0) {
|
|
401
|
+
const retentionRatio = pupTokenAmount / normalizedAirDropAmount;
|
|
402
|
+
if (retentionRatio >= PUP_TOKEN_THRESHOLDS.AIRDROP_110_PERCENT) {
|
|
403
|
+
return XP_BOOST_PERCENTAGES.TIER_3;
|
|
404
|
+
}
|
|
405
|
+
if (retentionRatio >= PUP_TOKEN_THRESHOLDS.AIRDROP_70_PERCENT) {
|
|
406
|
+
return XP_BOOST_PERCENTAGES.TIER_2;
|
|
407
|
+
}
|
|
408
|
+
if (retentionRatio >= PUP_TOKEN_THRESHOLDS.AIRDROP_35_PERCENT) {
|
|
409
|
+
return XP_BOOST_PERCENTAGES.TIER_1;
|
|
410
|
+
}
|
|
411
|
+
return XP_BOOST_PERCENTAGES.NO_BOOST;
|
|
412
|
+
} else {
|
|
413
|
+
if (pupTokenAmount >= PUP_TOKEN_THRESHOLDS.NON_AIRDROP_TOKENS) {
|
|
414
|
+
return XP_BOOST_PERCENTAGES.TIER_1;
|
|
415
|
+
}
|
|
416
|
+
return XP_BOOST_PERCENTAGES.NO_BOOST;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function normalizeAirdropAmount(amount) {
|
|
420
|
+
if (!amount) return 0;
|
|
421
|
+
if (typeof amount === "bigint") {
|
|
422
|
+
return Number(amount) / 1e18;
|
|
423
|
+
} else if (amount && typeof amount.div === "function") {
|
|
424
|
+
return amount.div(1e18).toNumber();
|
|
425
|
+
}
|
|
426
|
+
return 0;
|
|
427
|
+
}
|
|
428
|
+
function getNextTierInfo(pupTokenAmount, normalizedAirDropAmount, isAirdropRecipient) {
|
|
429
|
+
const currentBoost = calculateBoostPercentage(pupTokenAmount, normalizedAirDropAmount, isAirdropRecipient);
|
|
430
|
+
let currentTier = 0;
|
|
431
|
+
if (currentBoost === XP_BOOST_PERCENTAGES.TIER_3) currentTier = 3;
|
|
432
|
+
else if (currentBoost === XP_BOOST_PERCENTAGES.TIER_2) currentTier = 2;
|
|
433
|
+
else if (currentBoost === XP_BOOST_PERCENTAGES.TIER_1) currentTier = 1;
|
|
434
|
+
if (isAirdropRecipient && normalizedAirDropAmount > 0) {
|
|
435
|
+
const retentionRatio = pupTokenAmount / normalizedAirDropAmount;
|
|
436
|
+
if (currentTier === 0) {
|
|
437
|
+
const threshold = normalizedAirDropAmount * PUP_TOKEN_THRESHOLDS.AIRDROP_35_PERCENT;
|
|
438
|
+
return {
|
|
439
|
+
currentTier: 0,
|
|
440
|
+
currentBoost: XP_BOOST_PERCENTAGES.NO_BOOST,
|
|
441
|
+
nextTier: 1,
|
|
442
|
+
nextBoost: XP_BOOST_PERCENTAGES.TIER_1,
|
|
443
|
+
amountToNextTier: Math.max(0, threshold - pupTokenAmount),
|
|
444
|
+
nextTierThreshold: threshold,
|
|
445
|
+
progressPercentage: Math.min(100, pupTokenAmount / threshold * 100)
|
|
446
|
+
};
|
|
447
|
+
} else if (currentTier === 1) {
|
|
448
|
+
const threshold = normalizedAirDropAmount * PUP_TOKEN_THRESHOLDS.AIRDROP_70_PERCENT;
|
|
449
|
+
return {
|
|
450
|
+
currentTier: 1,
|
|
451
|
+
currentBoost: XP_BOOST_PERCENTAGES.TIER_1,
|
|
452
|
+
nextTier: 2,
|
|
453
|
+
nextBoost: XP_BOOST_PERCENTAGES.TIER_2,
|
|
454
|
+
amountToNextTier: Math.max(0, threshold - pupTokenAmount),
|
|
455
|
+
nextTierThreshold: threshold,
|
|
456
|
+
progressPercentage: Math.min(100, (pupTokenAmount - normalizedAirDropAmount * PUP_TOKEN_THRESHOLDS.AIRDROP_35_PERCENT) / (threshold - normalizedAirDropAmount * PUP_TOKEN_THRESHOLDS.AIRDROP_35_PERCENT) * 100)
|
|
457
|
+
};
|
|
458
|
+
} else if (currentTier === 2) {
|
|
459
|
+
const threshold = normalizedAirDropAmount * PUP_TOKEN_THRESHOLDS.AIRDROP_110_PERCENT;
|
|
460
|
+
return {
|
|
461
|
+
currentTier: 2,
|
|
462
|
+
currentBoost: XP_BOOST_PERCENTAGES.TIER_2,
|
|
463
|
+
nextTier: 3,
|
|
464
|
+
nextBoost: XP_BOOST_PERCENTAGES.TIER_3,
|
|
465
|
+
amountToNextTier: Math.max(0, threshold - pupTokenAmount),
|
|
466
|
+
nextTierThreshold: threshold,
|
|
467
|
+
progressPercentage: Math.min(100, (pupTokenAmount - normalizedAirDropAmount * PUP_TOKEN_THRESHOLDS.AIRDROP_70_PERCENT) / (threshold - normalizedAirDropAmount * PUP_TOKEN_THRESHOLDS.AIRDROP_70_PERCENT) * 100)
|
|
468
|
+
};
|
|
469
|
+
} else {
|
|
470
|
+
return {
|
|
471
|
+
currentTier: 3,
|
|
472
|
+
currentBoost: XP_BOOST_PERCENTAGES.TIER_3,
|
|
473
|
+
nextTier: null,
|
|
474
|
+
nextBoost: null,
|
|
475
|
+
amountToNextTier: null,
|
|
476
|
+
nextTierThreshold: null,
|
|
477
|
+
progressPercentage: 100
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
const threshold = PUP_TOKEN_THRESHOLDS.NON_AIRDROP_TOKENS;
|
|
482
|
+
if (currentTier === 0) {
|
|
483
|
+
return {
|
|
484
|
+
currentTier: 0,
|
|
485
|
+
currentBoost: XP_BOOST_PERCENTAGES.NO_BOOST,
|
|
486
|
+
nextTier: 1,
|
|
487
|
+
nextBoost: XP_BOOST_PERCENTAGES.TIER_1,
|
|
488
|
+
amountToNextTier: Math.max(0, threshold - pupTokenAmount),
|
|
489
|
+
nextTierThreshold: threshold,
|
|
490
|
+
progressPercentage: Math.min(100, pupTokenAmount / threshold * 100)
|
|
491
|
+
};
|
|
492
|
+
} else {
|
|
493
|
+
return {
|
|
494
|
+
currentTier: 1,
|
|
495
|
+
currentBoost: XP_BOOST_PERCENTAGES.TIER_1,
|
|
496
|
+
nextTier: null,
|
|
497
|
+
nextBoost: null,
|
|
498
|
+
amountToNextTier: null,
|
|
499
|
+
nextTierThreshold: null,
|
|
500
|
+
progressPercentage: 100
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// lib/constants/tokens.ts
|
|
507
|
+
var USDC_SPOT_TOKEN = {
|
|
508
|
+
name: "USDC",
|
|
509
|
+
szDecimals: 8,
|
|
510
|
+
weiDecimals: 8,
|
|
511
|
+
index: 0,
|
|
512
|
+
tokenId: "0x6d1e7cde53ba9467b783cb7c530ce054",
|
|
513
|
+
isCanonical: true,
|
|
514
|
+
evmContract: null,
|
|
515
|
+
fullName: null,
|
|
516
|
+
deployerTradingFeeShare: "0.0"
|
|
517
|
+
};
|
|
518
|
+
var TESTNET_USDC_SPOT_TOKEN = {
|
|
519
|
+
name: "USDC",
|
|
520
|
+
szDecimals: 8,
|
|
521
|
+
weiDecimals: 8,
|
|
522
|
+
index: 0,
|
|
523
|
+
tokenId: "0xeb62eee3685fc4c43992febcd9e75443",
|
|
524
|
+
isCanonical: true,
|
|
525
|
+
evmContract: {
|
|
526
|
+
address: "0xd9cbec81df392a88aeff575e962d149d57f4d6bc",
|
|
527
|
+
evm_extra_wei_decimals: 0
|
|
528
|
+
},
|
|
529
|
+
fullName: null,
|
|
530
|
+
deployerTradingFeeShare: "0.0"
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
// lib/meta/metadata.ts
|
|
534
|
+
import {
|
|
535
|
+
HttpTransport,
|
|
536
|
+
InfoClient
|
|
537
|
+
} from "@nktkas/hyperliquid";
|
|
538
|
+
|
|
539
|
+
// lib/hip3/utils.ts
|
|
540
|
+
function isHip3Symbol(symbol) {
|
|
541
|
+
if (!symbol) return false;
|
|
542
|
+
return symbol.includes(":");
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// import("./data/**/*/staticMeta.json") in lib/meta/metadata.ts
|
|
546
|
+
var globImport_data_staticMeta_json = __glob({
|
|
547
|
+
"./data/mainnet/staticMeta.json": () => import("./staticMeta-HRXST42O.mjs"),
|
|
548
|
+
"./data/testnet/staticMeta.json": () => import("./staticMeta-QWPQK3MD.mjs")
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
// import("./data/**/*/spotMeta.json") in lib/meta/metadata.ts
|
|
552
|
+
var globImport_data_spotMeta_json = __glob({
|
|
553
|
+
"./data/mainnet/spotMeta.json": () => import("./spotMeta-7IJT3W6H.mjs"),
|
|
554
|
+
"./data/testnet/spotMeta.json": () => import("./spotMeta-ZVBZNUUE.mjs")
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// import("./data/**/*/meta.json") in lib/meta/metadata.ts
|
|
558
|
+
var globImport_data_meta_json = __glob({
|
|
559
|
+
"./data/mainnet/meta.json": () => import("./meta-52Q5UUQ4.mjs"),
|
|
560
|
+
"./data/testnet/meta.json": () => import("./meta-UUXKK7IB.mjs")
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// import("./data/**/*/perpDexs.json") in lib/meta/metadata.ts
|
|
564
|
+
var globImport_data_perpDexs_json = __glob({
|
|
565
|
+
"./data/mainnet/perpDexs.json": () => import("./perpDexs-S3TK25EU.mjs"),
|
|
566
|
+
"./data/testnet/perpDexs.json": () => import("./perpDexs-TZIQ57IW.mjs")
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
// lib/meta/metadata.ts
|
|
570
|
+
var ROOT_DEX = "hyperliquid";
|
|
571
|
+
var MetadataClient = class {
|
|
572
|
+
constructor(config = {}) {
|
|
573
|
+
// Core metadata
|
|
574
|
+
this.spotMeta = null;
|
|
575
|
+
this.perpsMeta = null;
|
|
576
|
+
this.perpDexs = [];
|
|
577
|
+
this.staticMeta = null;
|
|
578
|
+
// HIP-3 metadata cache
|
|
579
|
+
this.hip3DexsMeta = /* @__PURE__ */ new Map();
|
|
580
|
+
// Pre-computed lookup maps (populated on initialize)
|
|
581
|
+
this.perpsSymbolToIndex = /* @__PURE__ */ new Map();
|
|
582
|
+
this.spotTokenNameToIndex = /* @__PURE__ */ new Map();
|
|
583
|
+
this.spotPairToMarket = /* @__PURE__ */ new Map();
|
|
584
|
+
this.baseTokenToMarkets = /* @__PURE__ */ new Map();
|
|
585
|
+
this.quoteAssets = [];
|
|
586
|
+
// Unified symbol lookup (used by getMarketBySymbol for O(1) access)
|
|
587
|
+
// Maps symbol to MarketInfo for quick lookups
|
|
588
|
+
this.coinToMarket = /* @__PURE__ */ new Map();
|
|
589
|
+
// HIP-3 optimized lookups
|
|
590
|
+
// Maps "dex:coin" symbol to MarketInfo
|
|
591
|
+
this.hip3SymbolToMarket = /* @__PURE__ */ new Map();
|
|
592
|
+
// Maps dex name to dex index for quick lookups
|
|
593
|
+
this.dexNameToIndex = /* @__PURE__ */ new Map();
|
|
594
|
+
// Lazy init flag
|
|
595
|
+
this.initialized = false;
|
|
596
|
+
const transport = new HttpTransport({
|
|
597
|
+
isTestnet: config.isTestnet ?? false
|
|
598
|
+
});
|
|
599
|
+
this.infoClient = new InfoClient({ transport });
|
|
600
|
+
this.config = {
|
|
601
|
+
...config,
|
|
602
|
+
hip3Dexs: config.hip3Dexs?.filter((dex) => dex !== ROOT_DEX),
|
|
603
|
+
useStaticFallback: config.useStaticFallback ?? true
|
|
604
|
+
};
|
|
605
|
+
this.isTestnet = config.isTestnet ?? false;
|
|
606
|
+
this.initialize();
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Initialize metadata by fetching from Hyperliquid
|
|
610
|
+
*/
|
|
611
|
+
async initialize() {
|
|
612
|
+
if (this.initialized) return;
|
|
613
|
+
await this.loadStaticMetaOverrides();
|
|
614
|
+
if (this.config.onlyUseStaticFallback) {
|
|
615
|
+
await this.loadStaticMetadata();
|
|
616
|
+
this.buildLookupMaps();
|
|
617
|
+
if (this.config.hip3Dexs && this.config.hip3Dexs.length > 0) {
|
|
618
|
+
await Promise.all(
|
|
619
|
+
this.config.hip3Dexs.map((dex) => this.loadHip3Metadata(dex))
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
this.initialized = true;
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
try {
|
|
626
|
+
[this.spotMeta, this.perpsMeta, this.perpDexs] = await Promise.all([
|
|
627
|
+
this.infoClient.spotMeta(),
|
|
628
|
+
this.infoClient.meta(),
|
|
629
|
+
this.infoClient.perpDexs()
|
|
630
|
+
]);
|
|
631
|
+
this.buildLookupMaps();
|
|
632
|
+
if (this.config.hip3Dexs && this.config.hip3Dexs.length > 0) {
|
|
633
|
+
await Promise.all(
|
|
634
|
+
this.config.hip3Dexs.map((dex) => this.loadHip3Metadata(dex))
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
this.initialized = true;
|
|
638
|
+
} catch (error) {
|
|
639
|
+
if (this.config.useStaticFallback) {
|
|
640
|
+
console.warn(
|
|
641
|
+
"Failed to fetch metadata from API, using static fallback data",
|
|
642
|
+
error
|
|
643
|
+
);
|
|
644
|
+
await this.loadStaticMetadata();
|
|
645
|
+
this.buildLookupMaps();
|
|
646
|
+
this.initialized = true;
|
|
647
|
+
} else {
|
|
648
|
+
throw error;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Load staticMeta.json for display overrides
|
|
654
|
+
* This is always loaded regardless of config.useStaticFallback
|
|
655
|
+
*/
|
|
656
|
+
async loadStaticMetaOverrides() {
|
|
657
|
+
const network = this.isTestnet ? "testnet" : "mainnet";
|
|
658
|
+
try {
|
|
659
|
+
const staticMetaModule = await globImport_data_staticMeta_json(`./data/${network}/staticMeta.json`);
|
|
660
|
+
this.staticMeta = staticMetaModule.default;
|
|
661
|
+
} catch (error) {
|
|
662
|
+
console.warn(`Failed to load staticMeta.json for ${network}:`, error);
|
|
663
|
+
this.staticMeta = null;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Load static metadata from bundled JSON files
|
|
668
|
+
*/
|
|
669
|
+
async loadStaticMetadata() {
|
|
670
|
+
const network = this.isTestnet ? "testnet" : "mainnet";
|
|
671
|
+
try {
|
|
672
|
+
const [
|
|
673
|
+
spotMetaModule,
|
|
674
|
+
perpsMetaModule,
|
|
675
|
+
perpDexsModule,
|
|
676
|
+
staticMetaModule
|
|
677
|
+
] = await Promise.all([
|
|
678
|
+
globImport_data_spotMeta_json(`./data/${network}/spotMeta.json`),
|
|
679
|
+
globImport_data_meta_json(`./data/${network}/meta.json`),
|
|
680
|
+
globImport_data_perpDexs_json(`./data/${network}/perpDexs.json`),
|
|
681
|
+
globImport_data_staticMeta_json(`./data/${network}/staticMeta.json`)
|
|
682
|
+
]);
|
|
683
|
+
this.spotMeta = spotMetaModule.default;
|
|
684
|
+
this.perpsMeta = perpsMetaModule.default;
|
|
685
|
+
this.perpDexs = perpDexsModule.default;
|
|
686
|
+
this.staticMeta = staticMetaModule.default;
|
|
687
|
+
console.warn(`Using static ${network} metadata`);
|
|
688
|
+
} catch (error) {
|
|
689
|
+
console.error(`Failed to load static ${network} metadata:`, error);
|
|
690
|
+
throw new Error(`Could not load metadata for ${network}`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Build optimized lookup maps from raw metadata
|
|
695
|
+
* Called after metadata is loaded (from API or static files)
|
|
696
|
+
*/
|
|
697
|
+
buildLookupMaps() {
|
|
698
|
+
this.perpsSymbolToIndex.clear();
|
|
699
|
+
this.spotTokenNameToIndex.clear();
|
|
700
|
+
this.spotPairToMarket.clear();
|
|
701
|
+
this.baseTokenToMarkets.clear();
|
|
702
|
+
this.coinToMarket.clear();
|
|
703
|
+
this.hip3SymbolToMarket.clear();
|
|
704
|
+
this.dexNameToIndex.clear();
|
|
705
|
+
this.quoteAssets = [];
|
|
706
|
+
if (this.perpDexs) {
|
|
707
|
+
this.perpDexs.forEach((dex, index) => {
|
|
708
|
+
if (dex && dex.name) {
|
|
709
|
+
this.dexNameToIndex.set(dex.name.toLowerCase(), index);
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
if (this.perpsMeta) {
|
|
714
|
+
this.perpsMeta.universe.forEach((market, index) => {
|
|
715
|
+
const marketInfo = {
|
|
716
|
+
symbol: market.name,
|
|
717
|
+
coin: market.name,
|
|
718
|
+
assetId: index,
|
|
719
|
+
szDecimals: market.szDecimals,
|
|
720
|
+
type: "perps",
|
|
721
|
+
maxLeverage: market.maxLeverage
|
|
722
|
+
};
|
|
723
|
+
const staticOverrides = this.staticMeta?.coins?.[market.name];
|
|
724
|
+
if (staticOverrides) {
|
|
725
|
+
if (staticOverrides.displayName) {
|
|
726
|
+
marketInfo.displayName = staticOverrides.displayName;
|
|
727
|
+
}
|
|
728
|
+
if (staticOverrides.imageUrl) {
|
|
729
|
+
marketInfo.imageUrl = staticOverrides.imageUrl;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
this.perpsSymbolToIndex.set(market.name.toUpperCase(), index);
|
|
733
|
+
this.coinToMarket.set(market.name, marketInfo);
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
if (this.spotMeta) {
|
|
737
|
+
this.spotMeta.tokens.forEach((token) => {
|
|
738
|
+
this.spotTokenNameToIndex.set(token.name.toUpperCase(), token.index);
|
|
739
|
+
});
|
|
740
|
+
const quoteIndices = /* @__PURE__ */ new Set();
|
|
741
|
+
this.spotMeta.universe.forEach((universe) => {
|
|
742
|
+
const baseToken = this.spotMeta.tokens[universe.tokens[0]];
|
|
743
|
+
const quoteToken = this.spotMeta.tokens[universe.tokens[1]];
|
|
744
|
+
if (!baseToken || !quoteToken) return;
|
|
745
|
+
const coin = universe.name;
|
|
746
|
+
quoteIndices.add(quoteToken.index);
|
|
747
|
+
const marketInfo = {
|
|
748
|
+
coin,
|
|
749
|
+
symbol: `${baseToken.name}/${quoteToken.name}`,
|
|
750
|
+
assetId: 1e4 + universe.index,
|
|
751
|
+
szDecimals: baseToken.szDecimals,
|
|
752
|
+
type: "spot",
|
|
753
|
+
baseToken,
|
|
754
|
+
quoteToken
|
|
755
|
+
};
|
|
756
|
+
const staticOverrides = this.staticMeta?.coins?.[coin];
|
|
757
|
+
if (staticOverrides) {
|
|
758
|
+
if (staticOverrides.displayName) {
|
|
759
|
+
marketInfo.displayName = staticOverrides.displayName;
|
|
760
|
+
}
|
|
761
|
+
if (staticOverrides.imageUrl) {
|
|
762
|
+
marketInfo.imageUrl = staticOverrides.imageUrl;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
const pairKey = `${baseToken.name}/${quoteToken.name}`.toUpperCase();
|
|
766
|
+
this.spotPairToMarket.set(pairKey, marketInfo);
|
|
767
|
+
this.coinToMarket.set(pairKey, marketInfo);
|
|
768
|
+
this.coinToMarket.set(coin, marketInfo);
|
|
769
|
+
const baseKey = baseToken.name.toUpperCase();
|
|
770
|
+
const existing = this.baseTokenToMarkets.get(baseKey) || [];
|
|
771
|
+
existing.push(marketInfo);
|
|
772
|
+
this.baseTokenToMarkets.set(baseKey, existing);
|
|
773
|
+
});
|
|
774
|
+
this.quoteAssets = Array.from(quoteIndices).map((idx) => this.spotMeta.tokens[idx].name).sort();
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Load metadata for a specific HIP-3 DEX
|
|
779
|
+
* Also builds optimized lookups for this DEX's markets
|
|
780
|
+
*/
|
|
781
|
+
async loadHip3Metadata(dexName) {
|
|
782
|
+
if (this.hip3DexsMeta.has(dexName)) return this.hip3DexsMeta.get(dexName);
|
|
783
|
+
try {
|
|
784
|
+
const [meta, contexts] = await this.infoClient.metaAndAssetCtxs({
|
|
785
|
+
dex: dexName
|
|
786
|
+
});
|
|
787
|
+
let dexIndex = this.dexNameToIndex.get(dexName.toLowerCase());
|
|
788
|
+
if (dexIndex === void 0) {
|
|
789
|
+
dexIndex = this.perpDexs.findIndex(
|
|
790
|
+
(d) => d && d.name.toLowerCase() === dexName.toLowerCase()
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
if (dexIndex === -1 || dexIndex === void 0) {
|
|
794
|
+
throw new Error(`DEX ${dexName} not found`);
|
|
795
|
+
}
|
|
796
|
+
const dex = this.perpDexs[dexIndex];
|
|
797
|
+
const collateralTokenIndex = meta.collateralToken ?? 0;
|
|
798
|
+
const spotMetaTokens = this.spotMeta?.tokens;
|
|
799
|
+
const collateralTokenSymbol = spotMetaTokens?.[collateralTokenIndex]?.name ?? "USDC";
|
|
800
|
+
const dexInfo = {
|
|
801
|
+
meta,
|
|
802
|
+
assetContext: contexts,
|
|
803
|
+
collateralTokenSymbol,
|
|
804
|
+
dexFullName: dex?.fullName ?? dexName,
|
|
805
|
+
dexName: dex?.name ?? dexName,
|
|
806
|
+
dexIndex
|
|
807
|
+
};
|
|
808
|
+
const staticDexOverrides = this.staticMeta?.dexs?.[dexName];
|
|
809
|
+
if (staticDexOverrides) {
|
|
810
|
+
if (staticDexOverrides.displayName) {
|
|
811
|
+
dexInfo.displayName = staticDexOverrides.displayName;
|
|
812
|
+
}
|
|
813
|
+
if (staticDexOverrides.imageUrl) {
|
|
814
|
+
dexInfo.imageUrl = staticDexOverrides.imageUrl;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
this.hip3DexsMeta.set(dexName, dexInfo);
|
|
818
|
+
this.buildHip3MarketsForDex(dexName, dexInfo);
|
|
819
|
+
return dexInfo;
|
|
820
|
+
} catch (error) {
|
|
821
|
+
console.error(`Failed to load HIP-3 metadata for ${dexName}:`, error);
|
|
822
|
+
throw error;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Build optimized lookups for HIP-3 markets of a specific DEX
|
|
827
|
+
*/
|
|
828
|
+
buildHip3MarketsForDex(dexName, dexInfo) {
|
|
829
|
+
dexInfo.meta.universe.forEach((market, index) => {
|
|
830
|
+
const symbol = market.name;
|
|
831
|
+
const marketInfo = {
|
|
832
|
+
coin: symbol,
|
|
833
|
+
symbol,
|
|
834
|
+
assetId: 1e5 + dexInfo.dexIndex * 1e4 + index,
|
|
835
|
+
szDecimals: market.szDecimals,
|
|
836
|
+
type: "hip3",
|
|
837
|
+
maxLeverage: market.maxLeverage,
|
|
838
|
+
dexName,
|
|
839
|
+
dexIndex: dexInfo.dexIndex,
|
|
840
|
+
dexDisplayName: dexInfo.displayName,
|
|
841
|
+
dexImageUrl: dexInfo.imageUrl
|
|
842
|
+
};
|
|
843
|
+
const staticOverrides = this.staticMeta?.coins?.[symbol];
|
|
844
|
+
if (staticOverrides) {
|
|
845
|
+
if (staticOverrides.displayName) {
|
|
846
|
+
marketInfo.displayName = staticOverrides.displayName;
|
|
847
|
+
}
|
|
848
|
+
if (staticOverrides.imageUrl) {
|
|
849
|
+
marketInfo.imageUrl = staticOverrides.imageUrl;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
this.coinToMarket.set(symbol, marketInfo);
|
|
853
|
+
this.hip3SymbolToMarket.set(symbol, marketInfo);
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Ensure metadata is loaded (for lazy init)
|
|
858
|
+
*/
|
|
859
|
+
async ensureInitialized() {
|
|
860
|
+
if (!this.initialized) {
|
|
861
|
+
await this.initialize();
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Get market information by symbol
|
|
866
|
+
* Optimized: O(1) direct map lookup for most cases
|
|
867
|
+
* Lazily initialize hip-3 metadata if not already initialized
|
|
868
|
+
*
|
|
869
|
+
* @param symbol - Market symbol (e.g., "BTC", "PURR/USDC", "vntls:ABC")
|
|
870
|
+
* @param quoteAsset - Quote asset for spot markets (default: "USDC")
|
|
871
|
+
* @returns Market information or null if not found
|
|
872
|
+
*/
|
|
873
|
+
async getMarketBySymbolAsync(symbol, quoteAsset = "USDC") {
|
|
874
|
+
await this.ensureInitialized();
|
|
875
|
+
if (symbol.includes(":")) {
|
|
876
|
+
return this.getHip3Market(symbol);
|
|
877
|
+
}
|
|
878
|
+
let lookupKey = symbol.toUpperCase();
|
|
879
|
+
if (!symbol.includes("/") && !symbol.includes("@")) {
|
|
880
|
+
const perpsMarket = this.coinToMarket.get(symbol);
|
|
881
|
+
if (perpsMarket?.type === "perps") {
|
|
882
|
+
return perpsMarket;
|
|
883
|
+
}
|
|
884
|
+
lookupKey = `${symbol}/${quoteAsset}`.toUpperCase();
|
|
885
|
+
}
|
|
886
|
+
return this.coinToMarket.get(lookupKey) || null;
|
|
887
|
+
}
|
|
888
|
+
getMarketByCoin(coin) {
|
|
889
|
+
return this.coinToMarket.get(coin) || null;
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Get perpetuals market information
|
|
893
|
+
* Optimized: O(1) map lookup instead of O(n) array search
|
|
894
|
+
*/
|
|
895
|
+
getPerpsMarket(symbol) {
|
|
896
|
+
if (!this.perpsMeta) return null;
|
|
897
|
+
if (isHip3Symbol(symbol)) {
|
|
898
|
+
const [dexName, coinName] = symbol.split(":");
|
|
899
|
+
if (!dexName || !coinName) return null;
|
|
900
|
+
let cachedMarket = this.hip3SymbolToMarket.get(symbol);
|
|
901
|
+
return cachedMarket || null;
|
|
902
|
+
}
|
|
903
|
+
const index = this.perpsSymbolToIndex.get(symbol.toUpperCase());
|
|
904
|
+
if (index === void 0) return null;
|
|
905
|
+
const market = this.perpsMeta.universe[index];
|
|
906
|
+
return {
|
|
907
|
+
coin: symbol,
|
|
908
|
+
symbol: market.name,
|
|
909
|
+
assetId: index,
|
|
910
|
+
// Perps asset ID is just the index
|
|
911
|
+
szDecimals: market.szDecimals,
|
|
912
|
+
type: "perps",
|
|
913
|
+
maxLeverage: market.maxLeverage
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Get spot market information
|
|
918
|
+
* Optimized: O(1) map lookup instead of O(n) array searches
|
|
919
|
+
*
|
|
920
|
+
* @param baseSymbol - Base token symbol (e.g., "PURR", "UHYPE")
|
|
921
|
+
* @param quoteSymbol - Quote token symbol (default: "USDC")
|
|
922
|
+
*/
|
|
923
|
+
getSpotMarket(baseSymbol, quoteSymbol = "USDC") {
|
|
924
|
+
if (!this.spotMeta) return null;
|
|
925
|
+
const pairKey = `${baseSymbol}/${quoteSymbol}`.toUpperCase();
|
|
926
|
+
const market = this.spotPairToMarket.get(pairKey);
|
|
927
|
+
return market || null;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Get HIP-3 market information
|
|
931
|
+
* Optimized: O(1) map lookup after DEX metadata is loaded
|
|
932
|
+
*
|
|
933
|
+
* @param symbol - HIP-3 market symbol (format: "dex:coin")
|
|
934
|
+
*/
|
|
935
|
+
async getHip3Market(symbol) {
|
|
936
|
+
const [dexName, coinName] = symbol.split(":");
|
|
937
|
+
if (!dexName || !coinName) return null;
|
|
938
|
+
let cachedMarket = this.hip3SymbolToMarket.get(symbol);
|
|
939
|
+
if (cachedMarket) return cachedMarket;
|
|
940
|
+
const dexMeta = this.hip3DexsMeta.get(dexName);
|
|
941
|
+
if (!dexMeta && this.config.lazyInit) {
|
|
942
|
+
await this.loadHip3Metadata(dexName);
|
|
943
|
+
cachedMarket = this.hip3SymbolToMarket.get(symbol);
|
|
944
|
+
return cachedMarket || null;
|
|
945
|
+
}
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
async getHip3Dex(dexName) {
|
|
949
|
+
await this.ensureInitialized();
|
|
950
|
+
let dexInfo = this.hip3DexsMeta.get(dexName) ?? null;
|
|
951
|
+
if (this.config.lazyInit && !dexInfo) {
|
|
952
|
+
dexInfo = await this.loadHip3Metadata(dexName);
|
|
953
|
+
}
|
|
954
|
+
return dexInfo;
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Get all available markets for a base token
|
|
958
|
+
* Optimized: O(1) map lookup instead of O(n) filter + map
|
|
959
|
+
* Useful for showing all quote asset options
|
|
960
|
+
*/
|
|
961
|
+
getAllMarketsForBase(baseSymbol) {
|
|
962
|
+
if (!this.spotMeta) return [];
|
|
963
|
+
const baseKey = baseSymbol.toUpperCase();
|
|
964
|
+
return this.baseTokenToMarkets.get(baseKey) || [];
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Get spot token information
|
|
968
|
+
* Optimized: O(1) map lookup instead of O(n) array search
|
|
969
|
+
*/
|
|
970
|
+
getSpotTokenInfo(tokenSymbol) {
|
|
971
|
+
if (!this.spotMeta) return null;
|
|
972
|
+
const tokenIndex = this.spotTokenNameToIndex.get(tokenSymbol.toUpperCase());
|
|
973
|
+
if (tokenIndex === void 0) return null;
|
|
974
|
+
const token = this.spotMeta.tokens[tokenIndex];
|
|
975
|
+
return {
|
|
976
|
+
name: token.name,
|
|
977
|
+
index: token.index,
|
|
978
|
+
szDecimals: token.szDecimals,
|
|
979
|
+
weiDecimals: token.weiDecimals,
|
|
980
|
+
tokenId: token.tokenId
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Get all available quote assets
|
|
985
|
+
* Optimized: O(1) pre-computed array instead of O(n) computation
|
|
986
|
+
*/
|
|
987
|
+
getAvailableQuoteAssets() {
|
|
988
|
+
return this.quoteAssets;
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Get raw metadata (for advanced use cases)
|
|
992
|
+
*/
|
|
993
|
+
getRawMetadata() {
|
|
994
|
+
return {
|
|
995
|
+
spotMeta: this.spotMeta,
|
|
996
|
+
perpsMeta: this.perpsMeta,
|
|
997
|
+
perpDexs: this.perpDexs,
|
|
998
|
+
hip3DexsMeta: Object.fromEntries(this.hip3DexsMeta)
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Get network configuration
|
|
1003
|
+
*/
|
|
1004
|
+
getNetworkInfo() {
|
|
1005
|
+
return {
|
|
1006
|
+
isTestnet: this.isTestnet,
|
|
1007
|
+
useStaticFallback: this.config.useStaticFallback,
|
|
1008
|
+
initialized: this.initialized
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
// lib/utils/formatter.ts
|
|
1014
|
+
import { Decimal } from "decimal.js";
|
|
1015
|
+
var formatPriceAndSize = ({
|
|
1016
|
+
px,
|
|
1017
|
+
sz,
|
|
1018
|
+
szDecimals,
|
|
1019
|
+
isSpot
|
|
1020
|
+
}) => {
|
|
1021
|
+
const priceDecimals = getPriceDecimals(px, szDecimals, isSpot);
|
|
1022
|
+
const price = new Decimal(px).toDP(priceDecimals).toNumber();
|
|
1023
|
+
const size = new Decimal(sz).toDP(szDecimals, Decimal.ROUND_DOWN).toNumber();
|
|
1024
|
+
return {
|
|
1025
|
+
price,
|
|
1026
|
+
size
|
|
1027
|
+
};
|
|
1028
|
+
};
|
|
1029
|
+
var formatPriceForOrder = ({
|
|
1030
|
+
px,
|
|
1031
|
+
szDecimals,
|
|
1032
|
+
isSpot
|
|
1033
|
+
}) => {
|
|
1034
|
+
const priceDecimals = getPriceDecimals(px, szDecimals, isSpot);
|
|
1035
|
+
const price = new Decimal(px).toDP(priceDecimals).toNumber();
|
|
1036
|
+
return price;
|
|
1037
|
+
};
|
|
1038
|
+
var formatSizeForOrder = ({
|
|
1039
|
+
sz,
|
|
1040
|
+
szDecimals
|
|
1041
|
+
}) => {
|
|
1042
|
+
return new Decimal(sz).toDP(szDecimals, Decimal.ROUND_DOWN).toNumber();
|
|
1043
|
+
};
|
|
1044
|
+
function getPriceDecimals(price, szDecimals, isSpot) {
|
|
1045
|
+
const baseDecimals = isSpot ? 8 : 6;
|
|
1046
|
+
const maxDP = Math.max(baseDecimals - szDecimals, 0);
|
|
1047
|
+
const maxSigFigs = 5;
|
|
1048
|
+
let minDecimals;
|
|
1049
|
+
if (price >= 1e5) {
|
|
1050
|
+
minDecimals = 0;
|
|
1051
|
+
} else {
|
|
1052
|
+
const exp = Math.floor(Math.log10(price));
|
|
1053
|
+
const dp = Math.max(maxSigFigs - exp - 1, 0);
|
|
1054
|
+
minDecimals = Math.min(dp, maxDP);
|
|
1055
|
+
}
|
|
1056
|
+
return minDecimals;
|
|
1057
|
+
}
|
|
1058
|
+
export {
|
|
1059
|
+
CloidClientCode,
|
|
1060
|
+
CloidClientCodeNameById,
|
|
1061
|
+
MetadataClient,
|
|
1062
|
+
PUP_TOKEN_ADDRESS,
|
|
1063
|
+
PUP_TOKEN_THRESHOLDS,
|
|
1064
|
+
ROOT_DEX,
|
|
1065
|
+
TARGET_APPROVED_MAX_BUILDER_FEE,
|
|
1066
|
+
TARGET_APPROVED_MAX_BUILDER_FEE_PERCENT,
|
|
1067
|
+
TESTNET_USDC_SPOT_TOKEN,
|
|
1068
|
+
USDC_SPOT_TOKEN,
|
|
1069
|
+
WidgetType,
|
|
1070
|
+
WidgetTypeById,
|
|
1071
|
+
XP_BOOST_PERCENTAGES,
|
|
1072
|
+
buildCloid,
|
|
1073
|
+
calculateBoostPercentage,
|
|
1074
|
+
calculateTotalPupAmount,
|
|
1075
|
+
decodeSlug,
|
|
1076
|
+
encodeSlug,
|
|
1077
|
+
formatPriceAndSize,
|
|
1078
|
+
formatPriceForOrder,
|
|
1079
|
+
formatSizeForOrder,
|
|
1080
|
+
getApprovalAmount,
|
|
1081
|
+
getClientCodeNameById,
|
|
1082
|
+
getCloid,
|
|
1083
|
+
getNextTierInfo,
|
|
1084
|
+
getPriceDecimals,
|
|
1085
|
+
getWidgetTypeById,
|
|
1086
|
+
isBasedCloid,
|
|
1087
|
+
isClientCode,
|
|
1088
|
+
isMiniAppCloid,
|
|
1089
|
+
isMiniAppTriggeredCloid,
|
|
1090
|
+
isTenantCloid,
|
|
1091
|
+
isTrackingIdCloid,
|
|
1092
|
+
isWidgetType,
|
|
1093
|
+
normaliseSlug,
|
|
1094
|
+
normaliseTrackingId,
|
|
1095
|
+
normalizeAirdropAmount,
|
|
1096
|
+
parseCloid
|
|
1097
|
+
};
|