@ostium/builder-sdk 0.1.0
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/README.md +589 -0
- package/dist/cli.js +6835 -0
- package/dist/index.cjs +5013 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1449 -0
- package/dist/index.d.ts +1449 -0
- package/dist/index.js +4988 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,5013 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var viem = require('viem');
|
|
4
|
+
var accounts = require('viem/accounts');
|
|
5
|
+
var chains = require('viem/chains');
|
|
6
|
+
var accountAbstraction = require('viem/account-abstraction');
|
|
7
|
+
var permissionless = require('permissionless');
|
|
8
|
+
var pimlico = require('permissionless/clients/pimlico');
|
|
9
|
+
var accounts$1 = require('permissionless/accounts');
|
|
10
|
+
var graphqlRequest = require('graphql-request');
|
|
11
|
+
var formulae = require('@ostium/formulae');
|
|
12
|
+
var WS = require('ws');
|
|
13
|
+
|
|
14
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
|
|
16
|
+
var WS__default = /*#__PURE__*/_interopDefault(WS);
|
|
17
|
+
|
|
18
|
+
// src/client.ts
|
|
19
|
+
|
|
20
|
+
// src/internal/erc20.ts
|
|
21
|
+
var ERC20_ABI = [
|
|
22
|
+
{
|
|
23
|
+
inputs: [
|
|
24
|
+
{ internalType: "address", name: "owner", type: "address" },
|
|
25
|
+
{ internalType: "address", name: "spender", type: "address" }
|
|
26
|
+
],
|
|
27
|
+
name: "allowance",
|
|
28
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
29
|
+
stateMutability: "view",
|
|
30
|
+
type: "function"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
inputs: [
|
|
34
|
+
{ internalType: "address", name: "spender", type: "address" },
|
|
35
|
+
{ internalType: "uint256", name: "amount", type: "uint256" }
|
|
36
|
+
],
|
|
37
|
+
name: "approve",
|
|
38
|
+
outputs: [{ internalType: "bool", name: "", type: "bool" }],
|
|
39
|
+
stateMutability: "nonpayable",
|
|
40
|
+
type: "function"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
inputs: [
|
|
44
|
+
{ internalType: "address", name: "account", type: "address" }
|
|
45
|
+
],
|
|
46
|
+
name: "balanceOf",
|
|
47
|
+
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
48
|
+
stateMutability: "view",
|
|
49
|
+
type: "function"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
inputs: [
|
|
53
|
+
{ internalType: "address", name: "to", type: "address" },
|
|
54
|
+
{ internalType: "uint256", name: "amount", type: "uint256" }
|
|
55
|
+
],
|
|
56
|
+
name: "transfer",
|
|
57
|
+
outputs: [{ internalType: "bool", name: "", type: "bool" }],
|
|
58
|
+
stateMutability: "nonpayable",
|
|
59
|
+
type: "function"
|
|
60
|
+
}
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// src/internal/decimal.ts
|
|
64
|
+
function parseUsdc(amount) {
|
|
65
|
+
const amountStr = typeof amount === "number" ? amount.toString() : amount;
|
|
66
|
+
if (!amountStr || amountStr === "0") return 0n;
|
|
67
|
+
if (!/^\d+(\.\d+)?$/.test(amountStr)) {
|
|
68
|
+
throw new Error(`Invalid USDC amount: ${amountStr}`);
|
|
69
|
+
}
|
|
70
|
+
const [intPart, decPart = ""] = amountStr.split(".");
|
|
71
|
+
const paddedDec = decPart.padEnd(6, "0").slice(0, 6);
|
|
72
|
+
return BigInt(intPart + paddedDec);
|
|
73
|
+
}
|
|
74
|
+
function parsePrice(price) {
|
|
75
|
+
const priceStr = typeof price === "number" ? price.toString() : price;
|
|
76
|
+
if (!priceStr || priceStr === "0") return 0n;
|
|
77
|
+
if (!/^-?\d+(\.\d+)?$/.test(priceStr)) {
|
|
78
|
+
throw new Error(`Invalid price: ${priceStr}`);
|
|
79
|
+
}
|
|
80
|
+
const isNegative = priceStr.startsWith("-");
|
|
81
|
+
const absStr = isNegative ? priceStr.slice(1) : priceStr;
|
|
82
|
+
const [intPart, decPart = ""] = absStr.split(".");
|
|
83
|
+
const paddedDec = decPart.padEnd(18, "0").slice(0, 18);
|
|
84
|
+
const result = BigInt(intPart + paddedDec);
|
|
85
|
+
return isNegative ? -result : result;
|
|
86
|
+
}
|
|
87
|
+
function parseLeverage(leverage) {
|
|
88
|
+
if (leverage <= 0 || !Number.isFinite(leverage)) {
|
|
89
|
+
throw new Error(`Invalid leverage: ${leverage}`);
|
|
90
|
+
}
|
|
91
|
+
return BigInt(Math.round(leverage * 100));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/internal/precision.ts
|
|
95
|
+
var PRECISION_6 = 1000000n;
|
|
96
|
+
var PRECISION_18 = 1000000000000000000n;
|
|
97
|
+
var DEFAULT_SLIPPAGE_PERCENTAGE = 0.25;
|
|
98
|
+
var MIN_COLLATERAL_USD = 5;
|
|
99
|
+
var MAX_COLLATERAL_USD = 2e6;
|
|
100
|
+
var ORDER_TYPE_MAP = {
|
|
101
|
+
"market": 0,
|
|
102
|
+
// OpenOrderType.MARKET
|
|
103
|
+
"limit": 1,
|
|
104
|
+
// OpenOrderType.LIMIT
|
|
105
|
+
"stop": 2
|
|
106
|
+
// OpenOrderType.STOP
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// src/internal/validation.ts
|
|
110
|
+
var DECIMAL_RE = /^-?\d+(\.\d+)?$/;
|
|
111
|
+
function assertDecimal(value, label) {
|
|
112
|
+
if (!DECIMAL_RE.test(value)) {
|
|
113
|
+
throw new Error(`Invalid ${label}: "${value}" is not a valid decimal number`);
|
|
114
|
+
}
|
|
115
|
+
if (parseFloat(value) <= 0) {
|
|
116
|
+
throw new Error(`Invalid ${label}: must be greater than zero, got "${value}"`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function assertNonNegativeDecimal(value, label) {
|
|
120
|
+
if (!DECIMAL_RE.test(value)) {
|
|
121
|
+
throw new Error(`Invalid ${label}: "${value}" is not a valid decimal number`);
|
|
122
|
+
}
|
|
123
|
+
if (parseFloat(value) < 0) {
|
|
124
|
+
throw new Error(`Invalid ${label}: must be non-negative, got "${value}"`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function extractOpenType(open) {
|
|
128
|
+
return ORDER_TYPE_MAP[open.t];
|
|
129
|
+
}
|
|
130
|
+
function validateTpSlDirection(open, tp, sl, openPrice) {
|
|
131
|
+
if (open.b) {
|
|
132
|
+
if (tp > 0n && tp <= openPrice) {
|
|
133
|
+
throw new Error(`Take profit (${open.tp}) must be above open price (${open.p}) for long positions`);
|
|
134
|
+
}
|
|
135
|
+
if (sl > 0n && sl >= openPrice) {
|
|
136
|
+
throw new Error(`Stop loss (${open.sl}) must be below open price (${open.p}) for long positions`);
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
if (tp > 0n && tp >= openPrice) {
|
|
140
|
+
throw new Error(`Take profit (${open.tp}) must be below open price (${open.p}) for short positions`);
|
|
141
|
+
}
|
|
142
|
+
if (sl > 0n && sl <= openPrice) {
|
|
143
|
+
throw new Error(`Stop loss (${open.sl}) must be above open price (${open.p}) for short positions`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function validateCloseParams(close) {
|
|
148
|
+
if (close.r < 1 || close.r > 100) {
|
|
149
|
+
throw new Error(`Close percentage must be 1\u2013100, got ${close.r}`);
|
|
150
|
+
}
|
|
151
|
+
assertDecimal(close.p, "close price");
|
|
152
|
+
}
|
|
153
|
+
function validateModifyParams(input) {
|
|
154
|
+
if (input.a < 0) {
|
|
155
|
+
throw new Error(`Invalid pair index: ${input.a}`);
|
|
156
|
+
}
|
|
157
|
+
if (input.i < 0) {
|
|
158
|
+
throw new Error(`Invalid trade/order index: ${input.i}`);
|
|
159
|
+
}
|
|
160
|
+
if (!input.p && !input.tp && !input.sl) {
|
|
161
|
+
throw new Error("At least one of price, takeProfit, or stopLoss must be provided");
|
|
162
|
+
}
|
|
163
|
+
if (input.p) {
|
|
164
|
+
assertDecimal(input.p, "price");
|
|
165
|
+
}
|
|
166
|
+
if (input.tp) {
|
|
167
|
+
assertNonNegativeDecimal(input.tp, "take profit");
|
|
168
|
+
}
|
|
169
|
+
if (input.sl) {
|
|
170
|
+
assertNonNegativeDecimal(input.sl, "stop loss");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/internal/open-builder.ts
|
|
175
|
+
var DEFAULT_CONFIG = {
|
|
176
|
+
defaultLeverage: 1,
|
|
177
|
+
maxLeverage: 100,
|
|
178
|
+
minOpenSize: MIN_COLLATERAL_USD
|
|
179
|
+
};
|
|
180
|
+
function buildTrade(open, traderAddress, config = {}) {
|
|
181
|
+
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
182
|
+
const collateralUsd = parseFloat(open.s);
|
|
183
|
+
if (collateralUsd < mergedConfig.minOpenSize) {
|
|
184
|
+
throw new Error(`Collateral ${open.s} below minimum ${mergedConfig.minOpenSize}`);
|
|
185
|
+
}
|
|
186
|
+
const leverageNum = parseFloat(open.l);
|
|
187
|
+
if (leverageNum > mergedConfig.maxLeverage) {
|
|
188
|
+
throw new Error(`Leverage ${open.l} exceeds maximum ${mergedConfig.maxLeverage}`);
|
|
189
|
+
}
|
|
190
|
+
const collateral = parseUsdc(open.s);
|
|
191
|
+
const openPrice = parsePrice(open.p);
|
|
192
|
+
const leverage = parseLeverage(leverageNum);
|
|
193
|
+
const tp = open.tp ? parsePrice(open.tp) : 0n;
|
|
194
|
+
const sl = open.sl ? parsePrice(open.sl) : 0n;
|
|
195
|
+
if (tp > 0n || sl > 0n) {
|
|
196
|
+
validateTpSlDirection(open, tp, sl, openPrice);
|
|
197
|
+
}
|
|
198
|
+
const trade = {
|
|
199
|
+
collateral,
|
|
200
|
+
openPrice,
|
|
201
|
+
tp,
|
|
202
|
+
sl,
|
|
203
|
+
trader: traderAddress,
|
|
204
|
+
leverage,
|
|
205
|
+
pairIndex: BigInt(open.a),
|
|
206
|
+
index: 0n,
|
|
207
|
+
buy: open.b,
|
|
208
|
+
isDayTrade: open.d ?? false
|
|
209
|
+
};
|
|
210
|
+
return { trade, orderType: extractOpenType(open) };
|
|
211
|
+
}
|
|
212
|
+
function buildBuilderFee(builder) {
|
|
213
|
+
if (!builder) {
|
|
214
|
+
return {
|
|
215
|
+
builder: "0x0000000000000000000000000000000000000000",
|
|
216
|
+
builderFee: 0n
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
builder: builder.b,
|
|
221
|
+
builderFee: BigInt(builder.f)
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/internal/contracts.ts
|
|
226
|
+
var ARBITRUM_MAINNET = {
|
|
227
|
+
chainId: 42161,
|
|
228
|
+
contracts: {
|
|
229
|
+
usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
230
|
+
trading: "0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411",
|
|
231
|
+
tradingStorage: "0xcCd5891083A8acD2074690F65d3024E7D13d66E7"
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
var ARBITRUM_TESTNET = {
|
|
235
|
+
chainId: 421614,
|
|
236
|
+
contracts: {
|
|
237
|
+
usdc: "0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548",
|
|
238
|
+
trading: "0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe",
|
|
239
|
+
tradingStorage: "0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8"
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
function getNetworkConfig(isTestnet = false) {
|
|
243
|
+
return isTestnet ? ARBITRUM_TESTNET : ARBITRUM_MAINNET;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/types.ts
|
|
247
|
+
var OrderType = /* @__PURE__ */ ((OrderType2) => {
|
|
248
|
+
OrderType2["Market"] = "market";
|
|
249
|
+
OrderType2["Limit"] = "limit";
|
|
250
|
+
OrderType2["Stop"] = "stop";
|
|
251
|
+
return OrderType2;
|
|
252
|
+
})(OrderType || {});
|
|
253
|
+
var CancelOrderType = /* @__PURE__ */ ((CancelOrderType2) => {
|
|
254
|
+
CancelOrderType2["Limit"] = "limit";
|
|
255
|
+
CancelOrderType2["PendingOpen"] = "pendingOpen";
|
|
256
|
+
CancelOrderType2["PendingClose"] = "pendingClose";
|
|
257
|
+
return CancelOrderType2;
|
|
258
|
+
})(CancelOrderType || {});
|
|
259
|
+
|
|
260
|
+
// src/config.ts
|
|
261
|
+
var DEFAULT_PIMLICO_URL = "https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161";
|
|
262
|
+
var DEFAULT_PIMLICO_URL_TESTNET = "https://builder.ostium.io/v1/pimlico/sponsor?chainId=421614";
|
|
263
|
+
function isGasless(config) {
|
|
264
|
+
return !!config.pimlicoUrl;
|
|
265
|
+
}
|
|
266
|
+
function isDelegated(config) {
|
|
267
|
+
return !!config.traderAddress;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// src/errors.ts
|
|
271
|
+
var OstiumErrorCode = /* @__PURE__ */ ((OstiumErrorCode2) => {
|
|
272
|
+
OstiumErrorCode2["INVALID_CONFIG"] = "INVALID_CONFIG";
|
|
273
|
+
OstiumErrorCode2["VALIDATION_FAILED"] = "VALIDATION_FAILED";
|
|
274
|
+
OstiumErrorCode2["ALLOWANCE_INSUFFICIENT"] = "ALLOWANCE_INSUFFICIENT";
|
|
275
|
+
OstiumErrorCode2["SUBMISSION_FAILED"] = "SUBMISSION_FAILED";
|
|
276
|
+
OstiumErrorCode2["DELEGATION_FAILED"] = "DELEGATION_FAILED";
|
|
277
|
+
OstiumErrorCode2["CONTRACT_ERROR"] = "CONTRACT_ERROR";
|
|
278
|
+
OstiumErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
279
|
+
return OstiumErrorCode2;
|
|
280
|
+
})(OstiumErrorCode || {});
|
|
281
|
+
var OstiumError = class extends Error {
|
|
282
|
+
constructor(message, code, cause) {
|
|
283
|
+
super(message);
|
|
284
|
+
this.code = code;
|
|
285
|
+
this.cause = cause;
|
|
286
|
+
this.name = "OstiumError";
|
|
287
|
+
}
|
|
288
|
+
code;
|
|
289
|
+
cause;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// src/signer/self.ts
|
|
293
|
+
var SelfSignerStrategy = class {
|
|
294
|
+
traderAddress;
|
|
295
|
+
constructor(traderAddress) {
|
|
296
|
+
this.traderAddress = traderAddress;
|
|
297
|
+
}
|
|
298
|
+
prepare(encoded) {
|
|
299
|
+
return encoded;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// src/internal/contract.ts
|
|
304
|
+
var TRADING_ABI = [
|
|
305
|
+
{
|
|
306
|
+
"inputs": [],
|
|
307
|
+
"stateMutability": "nonpayable",
|
|
308
|
+
"type": "constructor"
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
"inputs": [],
|
|
312
|
+
"name": "AboveMaxAllowedCollateral",
|
|
313
|
+
"type": "error"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"inputs": [
|
|
317
|
+
{
|
|
318
|
+
"internalType": "address",
|
|
319
|
+
"name": "trader",
|
|
320
|
+
"type": "address"
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"internalType": "uint16",
|
|
324
|
+
"name": "pairIndex",
|
|
325
|
+
"type": "uint16"
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"internalType": "uint8",
|
|
329
|
+
"name": "index",
|
|
330
|
+
"type": "uint8"
|
|
331
|
+
}
|
|
332
|
+
],
|
|
333
|
+
"name": "AlreadyMarketClosed",
|
|
334
|
+
"type": "error"
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
"inputs": [],
|
|
338
|
+
"name": "BelowFees",
|
|
339
|
+
"type": "error"
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"inputs": [],
|
|
343
|
+
"name": "BelowMinLevPos",
|
|
344
|
+
"type": "error"
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"inputs": [],
|
|
348
|
+
"name": "DelegateForbidden",
|
|
349
|
+
"type": "error"
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"inputs": [],
|
|
353
|
+
"name": "DelegateReentrant",
|
|
354
|
+
"type": "error"
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"inputs": [],
|
|
358
|
+
"name": "DelegatedActionFailed",
|
|
359
|
+
"type": "error"
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
"inputs": [],
|
|
363
|
+
"name": "ExposureLimits",
|
|
364
|
+
"type": "error"
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"inputs": [],
|
|
368
|
+
"name": "InvalidCallData",
|
|
369
|
+
"type": "error"
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"inputs": [],
|
|
373
|
+
"name": "InvalidInitialization",
|
|
374
|
+
"type": "error"
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
"inputs": [],
|
|
378
|
+
"name": "InvalidNonce",
|
|
379
|
+
"type": "error"
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
"inputs": [],
|
|
383
|
+
"name": "InvalidSignature",
|
|
384
|
+
"type": "error"
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"inputs": [
|
|
388
|
+
{
|
|
389
|
+
"internalType": "address",
|
|
390
|
+
"name": "a",
|
|
391
|
+
"type": "address"
|
|
392
|
+
}
|
|
393
|
+
],
|
|
394
|
+
"name": "IsContract",
|
|
395
|
+
"type": "error"
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
"inputs": [],
|
|
399
|
+
"name": "IsDone",
|
|
400
|
+
"type": "error"
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
"inputs": [],
|
|
404
|
+
"name": "IsPaused",
|
|
405
|
+
"type": "error"
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
"inputs": [
|
|
409
|
+
{
|
|
410
|
+
"internalType": "address",
|
|
411
|
+
"name": "trader",
|
|
412
|
+
"type": "address"
|
|
413
|
+
}
|
|
414
|
+
],
|
|
415
|
+
"name": "MaxPendingMarketOrdersReached",
|
|
416
|
+
"type": "error"
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
"inputs": [
|
|
420
|
+
{
|
|
421
|
+
"internalType": "address",
|
|
422
|
+
"name": "trader",
|
|
423
|
+
"type": "address"
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
"internalType": "uint16",
|
|
427
|
+
"name": "pairIndex",
|
|
428
|
+
"type": "uint16"
|
|
429
|
+
}
|
|
430
|
+
],
|
|
431
|
+
"name": "MaxTradesPerPairReached",
|
|
432
|
+
"type": "error"
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
"inputs": [
|
|
436
|
+
{
|
|
437
|
+
"internalType": "address",
|
|
438
|
+
"name": "a",
|
|
439
|
+
"type": "address"
|
|
440
|
+
}
|
|
441
|
+
],
|
|
442
|
+
"name": "NoDelegate",
|
|
443
|
+
"type": "error"
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
"inputs": [
|
|
447
|
+
{
|
|
448
|
+
"internalType": "address",
|
|
449
|
+
"name": "trader",
|
|
450
|
+
"type": "address"
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
"internalType": "uint16",
|
|
454
|
+
"name": "pairIndex",
|
|
455
|
+
"type": "uint16"
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
"internalType": "uint8",
|
|
459
|
+
"name": "index",
|
|
460
|
+
"type": "uint8"
|
|
461
|
+
}
|
|
462
|
+
],
|
|
463
|
+
"name": "NoLimitFound",
|
|
464
|
+
"type": "error"
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
"inputs": [
|
|
468
|
+
{
|
|
469
|
+
"internalType": "address",
|
|
470
|
+
"name": "trader",
|
|
471
|
+
"type": "address"
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"internalType": "uint16",
|
|
475
|
+
"name": "pairIndex",
|
|
476
|
+
"type": "uint16"
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
"internalType": "uint8",
|
|
480
|
+
"name": "index",
|
|
481
|
+
"type": "uint8"
|
|
482
|
+
}
|
|
483
|
+
],
|
|
484
|
+
"name": "NoTradeFound",
|
|
485
|
+
"type": "error"
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
"inputs": [
|
|
489
|
+
{
|
|
490
|
+
"internalType": "uint256",
|
|
491
|
+
"name": "orderId",
|
|
492
|
+
"type": "uint256"
|
|
493
|
+
}
|
|
494
|
+
],
|
|
495
|
+
"name": "NoTradeToTimeoutFound",
|
|
496
|
+
"type": "error"
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
"inputs": [
|
|
500
|
+
{
|
|
501
|
+
"internalType": "uint256",
|
|
502
|
+
"name": "orderId",
|
|
503
|
+
"type": "uint256"
|
|
504
|
+
}
|
|
505
|
+
],
|
|
506
|
+
"name": "NotCloseMarketTimeoutOrder",
|
|
507
|
+
"type": "error"
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
"inputs": [
|
|
511
|
+
{
|
|
512
|
+
"internalType": "address",
|
|
513
|
+
"name": "trader",
|
|
514
|
+
"type": "address"
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
"internalType": "address",
|
|
518
|
+
"name": "caller",
|
|
519
|
+
"type": "address"
|
|
520
|
+
}
|
|
521
|
+
],
|
|
522
|
+
"name": "NotDelegate",
|
|
523
|
+
"type": "error"
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
"inputs": [
|
|
527
|
+
{
|
|
528
|
+
"internalType": "address",
|
|
529
|
+
"name": "a",
|
|
530
|
+
"type": "address"
|
|
531
|
+
}
|
|
532
|
+
],
|
|
533
|
+
"name": "NotGov",
|
|
534
|
+
"type": "error"
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
"inputs": [],
|
|
538
|
+
"name": "NotInitializing",
|
|
539
|
+
"type": "error"
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
"inputs": [
|
|
543
|
+
{
|
|
544
|
+
"internalType": "address",
|
|
545
|
+
"name": "a",
|
|
546
|
+
"type": "address"
|
|
547
|
+
}
|
|
548
|
+
],
|
|
549
|
+
"name": "NotManager",
|
|
550
|
+
"type": "error"
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
"inputs": [
|
|
554
|
+
{
|
|
555
|
+
"internalType": "uint256",
|
|
556
|
+
"name": "orderId",
|
|
557
|
+
"type": "uint256"
|
|
558
|
+
}
|
|
559
|
+
],
|
|
560
|
+
"name": "NotOpenMarketTimeoutOrder",
|
|
561
|
+
"type": "error"
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
"inputs": [
|
|
565
|
+
{
|
|
566
|
+
"internalType": "address",
|
|
567
|
+
"name": "a",
|
|
568
|
+
"type": "address"
|
|
569
|
+
}
|
|
570
|
+
],
|
|
571
|
+
"name": "NotTradesUpKeep",
|
|
572
|
+
"type": "error"
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
"inputs": [
|
|
576
|
+
{
|
|
577
|
+
"internalType": "uint256",
|
|
578
|
+
"name": "orderId",
|
|
579
|
+
"type": "uint256"
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
"internalType": "address",
|
|
583
|
+
"name": "trader",
|
|
584
|
+
"type": "address"
|
|
585
|
+
}
|
|
586
|
+
],
|
|
587
|
+
"name": "NotYourOrder",
|
|
588
|
+
"type": "error"
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
"inputs": [],
|
|
592
|
+
"name": "NullAddr",
|
|
593
|
+
"type": "error"
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
"inputs": [
|
|
597
|
+
{
|
|
598
|
+
"internalType": "uint16",
|
|
599
|
+
"name": "index",
|
|
600
|
+
"type": "uint16"
|
|
601
|
+
}
|
|
602
|
+
],
|
|
603
|
+
"name": "PairNotListed",
|
|
604
|
+
"type": "error"
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
"inputs": [
|
|
608
|
+
{
|
|
609
|
+
"internalType": "uint8",
|
|
610
|
+
"name": "bits",
|
|
611
|
+
"type": "uint8"
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
"internalType": "uint256",
|
|
615
|
+
"name": "value",
|
|
616
|
+
"type": "uint256"
|
|
617
|
+
}
|
|
618
|
+
],
|
|
619
|
+
"name": "SafeCastOverflowedUintDowncast",
|
|
620
|
+
"type": "error"
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
"inputs": [],
|
|
624
|
+
"name": "SignatureExpired",
|
|
625
|
+
"type": "error"
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
"inputs": [
|
|
629
|
+
{
|
|
630
|
+
"internalType": "address",
|
|
631
|
+
"name": "sender",
|
|
632
|
+
"type": "address"
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
"internalType": "uint16",
|
|
636
|
+
"name": "pairIndex",
|
|
637
|
+
"type": "uint16"
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
"internalType": "uint8",
|
|
641
|
+
"name": "index",
|
|
642
|
+
"type": "uint8"
|
|
643
|
+
}
|
|
644
|
+
],
|
|
645
|
+
"name": "TriggerPending",
|
|
646
|
+
"type": "error"
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
"inputs": [
|
|
650
|
+
{
|
|
651
|
+
"internalType": "uint256",
|
|
652
|
+
"name": "orderId",
|
|
653
|
+
"type": "uint256"
|
|
654
|
+
}
|
|
655
|
+
],
|
|
656
|
+
"name": "WaitTimeout",
|
|
657
|
+
"type": "error"
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
"inputs": [
|
|
661
|
+
{
|
|
662
|
+
"internalType": "uint32",
|
|
663
|
+
"name": "leverage",
|
|
664
|
+
"type": "uint32"
|
|
665
|
+
}
|
|
666
|
+
],
|
|
667
|
+
"name": "WrongLeverage",
|
|
668
|
+
"type": "error"
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
"inputs": [],
|
|
672
|
+
"name": "WrongParams",
|
|
673
|
+
"type": "error"
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
"inputs": [],
|
|
677
|
+
"name": "WrongSL",
|
|
678
|
+
"type": "error"
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
"inputs": [],
|
|
682
|
+
"name": "WrongTP",
|
|
683
|
+
"type": "error"
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
"anonymous": false,
|
|
687
|
+
"inputs": [
|
|
688
|
+
{
|
|
689
|
+
"indexed": true,
|
|
690
|
+
"internalType": "uint256",
|
|
691
|
+
"name": "orderId",
|
|
692
|
+
"type": "uint256"
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
"indexed": true,
|
|
696
|
+
"internalType": "uint256",
|
|
697
|
+
"name": "tradeId",
|
|
698
|
+
"type": "uint256"
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
"indexed": true,
|
|
702
|
+
"internalType": "address",
|
|
703
|
+
"name": "trader",
|
|
704
|
+
"type": "address"
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
"indexed": false,
|
|
708
|
+
"internalType": "uint16",
|
|
709
|
+
"name": "pairIndex",
|
|
710
|
+
"type": "uint16"
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
"indexed": false,
|
|
714
|
+
"internalType": "enum IOstiumTradingStorage.LimitOrder",
|
|
715
|
+
"name": "",
|
|
716
|
+
"type": "uint8"
|
|
717
|
+
}
|
|
718
|
+
],
|
|
719
|
+
"name": "AutomationCloseOrderInitiated",
|
|
720
|
+
"type": "event"
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
"anonymous": false,
|
|
724
|
+
"inputs": [
|
|
725
|
+
{
|
|
726
|
+
"indexed": true,
|
|
727
|
+
"internalType": "uint256",
|
|
728
|
+
"name": "orderId",
|
|
729
|
+
"type": "uint256"
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
"indexed": true,
|
|
733
|
+
"internalType": "address",
|
|
734
|
+
"name": "trader",
|
|
735
|
+
"type": "address"
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
"indexed": true,
|
|
739
|
+
"internalType": "uint16",
|
|
740
|
+
"name": "pairIndex",
|
|
741
|
+
"type": "uint16"
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
"indexed": false,
|
|
745
|
+
"internalType": "uint8",
|
|
746
|
+
"name": "index",
|
|
747
|
+
"type": "uint8"
|
|
748
|
+
}
|
|
749
|
+
],
|
|
750
|
+
"name": "AutomationOpenOrderInitiated",
|
|
751
|
+
"type": "event"
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
"anonymous": false,
|
|
755
|
+
"inputs": [
|
|
756
|
+
{
|
|
757
|
+
"indexed": true,
|
|
758
|
+
"internalType": "address",
|
|
759
|
+
"name": "delegator",
|
|
760
|
+
"type": "address"
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
"indexed": true,
|
|
764
|
+
"internalType": "address",
|
|
765
|
+
"name": "delegate",
|
|
766
|
+
"type": "address"
|
|
767
|
+
}
|
|
768
|
+
],
|
|
769
|
+
"name": "DelegateAdded",
|
|
770
|
+
"type": "event"
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
"anonymous": false,
|
|
774
|
+
"inputs": [
|
|
775
|
+
{
|
|
776
|
+
"indexed": true,
|
|
777
|
+
"internalType": "address",
|
|
778
|
+
"name": "delegator",
|
|
779
|
+
"type": "address"
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
"indexed": true,
|
|
783
|
+
"internalType": "address",
|
|
784
|
+
"name": "delegate",
|
|
785
|
+
"type": "address"
|
|
786
|
+
}
|
|
787
|
+
],
|
|
788
|
+
"name": "DelegateRemoved",
|
|
789
|
+
"type": "event"
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
"anonymous": false,
|
|
793
|
+
"inputs": [
|
|
794
|
+
{
|
|
795
|
+
"indexed": false,
|
|
796
|
+
"internalType": "bool",
|
|
797
|
+
"name": "done",
|
|
798
|
+
"type": "bool"
|
|
799
|
+
}
|
|
800
|
+
],
|
|
801
|
+
"name": "Done",
|
|
802
|
+
"type": "event"
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
"anonymous": false,
|
|
806
|
+
"inputs": [
|
|
807
|
+
{
|
|
808
|
+
"indexed": false,
|
|
809
|
+
"internalType": "uint64",
|
|
810
|
+
"name": "version",
|
|
811
|
+
"type": "uint64"
|
|
812
|
+
}
|
|
813
|
+
],
|
|
814
|
+
"name": "Initialized",
|
|
815
|
+
"type": "event"
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
"anonymous": false,
|
|
819
|
+
"inputs": [
|
|
820
|
+
{
|
|
821
|
+
"indexed": true,
|
|
822
|
+
"internalType": "uint256",
|
|
823
|
+
"name": "tradeId",
|
|
824
|
+
"type": "uint256"
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
"indexed": true,
|
|
828
|
+
"internalType": "address",
|
|
829
|
+
"name": "trader",
|
|
830
|
+
"type": "address"
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
"indexed": true,
|
|
834
|
+
"internalType": "uint16",
|
|
835
|
+
"name": "pairIndex",
|
|
836
|
+
"type": "uint16"
|
|
837
|
+
}
|
|
838
|
+
],
|
|
839
|
+
"name": "MarketCloseFailed",
|
|
840
|
+
"type": "event"
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
"anonymous": false,
|
|
844
|
+
"inputs": [
|
|
845
|
+
{
|
|
846
|
+
"indexed": true,
|
|
847
|
+
"internalType": "uint256",
|
|
848
|
+
"name": "orderId",
|
|
849
|
+
"type": "uint256"
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
"indexed": true,
|
|
853
|
+
"internalType": "uint256",
|
|
854
|
+
"name": "tradeId",
|
|
855
|
+
"type": "uint256"
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
"indexed": true,
|
|
859
|
+
"internalType": "address",
|
|
860
|
+
"name": "trader",
|
|
861
|
+
"type": "address"
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
"indexed": false,
|
|
865
|
+
"internalType": "uint16",
|
|
866
|
+
"name": "pairIndex",
|
|
867
|
+
"type": "uint16"
|
|
868
|
+
}
|
|
869
|
+
],
|
|
870
|
+
"name": "MarketCloseOrderInitiated",
|
|
871
|
+
"type": "event"
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
"anonymous": false,
|
|
875
|
+
"inputs": [
|
|
876
|
+
{
|
|
877
|
+
"indexed": true,
|
|
878
|
+
"internalType": "uint256",
|
|
879
|
+
"name": "orderId",
|
|
880
|
+
"type": "uint256"
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
"indexed": true,
|
|
884
|
+
"internalType": "uint256",
|
|
885
|
+
"name": "tradeId",
|
|
886
|
+
"type": "uint256"
|
|
887
|
+
},
|
|
888
|
+
{
|
|
889
|
+
"indexed": true,
|
|
890
|
+
"internalType": "address",
|
|
891
|
+
"name": "trader",
|
|
892
|
+
"type": "address"
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
"indexed": false,
|
|
896
|
+
"internalType": "uint16",
|
|
897
|
+
"name": "pairIndex",
|
|
898
|
+
"type": "uint16"
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
"indexed": false,
|
|
902
|
+
"internalType": "uint16",
|
|
903
|
+
"name": "closePercentage",
|
|
904
|
+
"type": "uint16"
|
|
905
|
+
}
|
|
906
|
+
],
|
|
907
|
+
"name": "MarketCloseOrderInitiatedV2",
|
|
908
|
+
"type": "event"
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
"anonymous": false,
|
|
912
|
+
"inputs": [
|
|
913
|
+
{
|
|
914
|
+
"indexed": true,
|
|
915
|
+
"internalType": "uint256",
|
|
916
|
+
"name": "orderId",
|
|
917
|
+
"type": "uint256"
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
"indexed": true,
|
|
921
|
+
"internalType": "uint256",
|
|
922
|
+
"name": "tradeId",
|
|
923
|
+
"type": "uint256"
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
"components": [
|
|
927
|
+
{
|
|
928
|
+
"internalType": "uint256",
|
|
929
|
+
"name": "block",
|
|
930
|
+
"type": "uint256"
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
"internalType": "uint192",
|
|
934
|
+
"name": "wantedPrice",
|
|
935
|
+
"type": "uint192"
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
"internalType": "uint32",
|
|
939
|
+
"name": "slippageP",
|
|
940
|
+
"type": "uint32"
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
"components": [
|
|
944
|
+
{
|
|
945
|
+
"internalType": "uint256",
|
|
946
|
+
"name": "collateral",
|
|
947
|
+
"type": "uint256"
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
"internalType": "uint192",
|
|
951
|
+
"name": "openPrice",
|
|
952
|
+
"type": "uint192"
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
"internalType": "uint192",
|
|
956
|
+
"name": "tp",
|
|
957
|
+
"type": "uint192"
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
"internalType": "uint192",
|
|
961
|
+
"name": "sl",
|
|
962
|
+
"type": "uint192"
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
"internalType": "address",
|
|
966
|
+
"name": "trader",
|
|
967
|
+
"type": "address"
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
"internalType": "uint32",
|
|
971
|
+
"name": "leverage",
|
|
972
|
+
"type": "uint32"
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
"internalType": "uint16",
|
|
976
|
+
"name": "pairIndex",
|
|
977
|
+
"type": "uint16"
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
"internalType": "uint8",
|
|
981
|
+
"name": "index",
|
|
982
|
+
"type": "uint8"
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
"internalType": "bool",
|
|
986
|
+
"name": "buy",
|
|
987
|
+
"type": "bool"
|
|
988
|
+
},
|
|
989
|
+
{
|
|
990
|
+
"internalType": "bool",
|
|
991
|
+
"name": "isDayTrade",
|
|
992
|
+
"type": "bool"
|
|
993
|
+
}
|
|
994
|
+
],
|
|
995
|
+
"internalType": "struct IOstiumTradingStorage.Trade",
|
|
996
|
+
"name": "trade",
|
|
997
|
+
"type": "tuple"
|
|
998
|
+
}
|
|
999
|
+
],
|
|
1000
|
+
"indexed": false,
|
|
1001
|
+
"internalType": "struct IOstiumTradingStorage.PendingMarketOrder",
|
|
1002
|
+
"name": "order",
|
|
1003
|
+
"type": "tuple"
|
|
1004
|
+
}
|
|
1005
|
+
],
|
|
1006
|
+
"name": "MarketCloseTimeoutExecuted",
|
|
1007
|
+
"type": "event"
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
"anonymous": false,
|
|
1011
|
+
"inputs": [
|
|
1012
|
+
{
|
|
1013
|
+
"indexed": true,
|
|
1014
|
+
"internalType": "uint256",
|
|
1015
|
+
"name": "orderId",
|
|
1016
|
+
"type": "uint256"
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
"indexed": true,
|
|
1020
|
+
"internalType": "uint256",
|
|
1021
|
+
"name": "tradeId",
|
|
1022
|
+
"type": "uint256"
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
"components": [
|
|
1026
|
+
{
|
|
1027
|
+
"internalType": "uint256",
|
|
1028
|
+
"name": "block",
|
|
1029
|
+
"type": "uint256"
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
"internalType": "uint192",
|
|
1033
|
+
"name": "wantedPrice",
|
|
1034
|
+
"type": "uint192"
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
"internalType": "uint32",
|
|
1038
|
+
"name": "slippageP",
|
|
1039
|
+
"type": "uint32"
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
"components": [
|
|
1043
|
+
{
|
|
1044
|
+
"internalType": "uint256",
|
|
1045
|
+
"name": "collateral",
|
|
1046
|
+
"type": "uint256"
|
|
1047
|
+
},
|
|
1048
|
+
{
|
|
1049
|
+
"internalType": "uint192",
|
|
1050
|
+
"name": "openPrice",
|
|
1051
|
+
"type": "uint192"
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
"internalType": "uint192",
|
|
1055
|
+
"name": "tp",
|
|
1056
|
+
"type": "uint192"
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
"internalType": "uint192",
|
|
1060
|
+
"name": "sl",
|
|
1061
|
+
"type": "uint192"
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
"internalType": "address",
|
|
1065
|
+
"name": "trader",
|
|
1066
|
+
"type": "address"
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
"internalType": "uint32",
|
|
1070
|
+
"name": "leverage",
|
|
1071
|
+
"type": "uint32"
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
"internalType": "uint16",
|
|
1075
|
+
"name": "pairIndex",
|
|
1076
|
+
"type": "uint16"
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
"internalType": "uint8",
|
|
1080
|
+
"name": "index",
|
|
1081
|
+
"type": "uint8"
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
"internalType": "bool",
|
|
1085
|
+
"name": "buy",
|
|
1086
|
+
"type": "bool"
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
"internalType": "bool",
|
|
1090
|
+
"name": "isDayTrade",
|
|
1091
|
+
"type": "bool"
|
|
1092
|
+
}
|
|
1093
|
+
],
|
|
1094
|
+
"internalType": "struct IOstiumTradingStorage.Trade",
|
|
1095
|
+
"name": "trade",
|
|
1096
|
+
"type": "tuple"
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
"internalType": "uint16",
|
|
1100
|
+
"name": "percentage",
|
|
1101
|
+
"type": "uint16"
|
|
1102
|
+
}
|
|
1103
|
+
],
|
|
1104
|
+
"indexed": false,
|
|
1105
|
+
"internalType": "struct IOstiumTradingStorage.PendingMarketOrderV2",
|
|
1106
|
+
"name": "order",
|
|
1107
|
+
"type": "tuple"
|
|
1108
|
+
}
|
|
1109
|
+
],
|
|
1110
|
+
"name": "MarketCloseTimeoutExecutedV2",
|
|
1111
|
+
"type": "event"
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
"anonymous": false,
|
|
1115
|
+
"inputs": [
|
|
1116
|
+
{
|
|
1117
|
+
"indexed": true,
|
|
1118
|
+
"internalType": "uint256",
|
|
1119
|
+
"name": "orderId",
|
|
1120
|
+
"type": "uint256"
|
|
1121
|
+
},
|
|
1122
|
+
{
|
|
1123
|
+
"indexed": true,
|
|
1124
|
+
"internalType": "address",
|
|
1125
|
+
"name": "trader",
|
|
1126
|
+
"type": "address"
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
"indexed": true,
|
|
1130
|
+
"internalType": "uint16",
|
|
1131
|
+
"name": "pairIndex",
|
|
1132
|
+
"type": "uint16"
|
|
1133
|
+
}
|
|
1134
|
+
],
|
|
1135
|
+
"name": "MarketOpenOrderInitiated",
|
|
1136
|
+
"type": "event"
|
|
1137
|
+
},
|
|
1138
|
+
{
|
|
1139
|
+
"anonymous": false,
|
|
1140
|
+
"inputs": [
|
|
1141
|
+
{
|
|
1142
|
+
"indexed": true,
|
|
1143
|
+
"internalType": "uint256",
|
|
1144
|
+
"name": "orderId",
|
|
1145
|
+
"type": "uint256"
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
"components": [
|
|
1149
|
+
{
|
|
1150
|
+
"internalType": "uint256",
|
|
1151
|
+
"name": "block",
|
|
1152
|
+
"type": "uint256"
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
"internalType": "uint192",
|
|
1156
|
+
"name": "wantedPrice",
|
|
1157
|
+
"type": "uint192"
|
|
1158
|
+
},
|
|
1159
|
+
{
|
|
1160
|
+
"internalType": "uint32",
|
|
1161
|
+
"name": "slippageP",
|
|
1162
|
+
"type": "uint32"
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
"components": [
|
|
1166
|
+
{
|
|
1167
|
+
"internalType": "uint256",
|
|
1168
|
+
"name": "collateral",
|
|
1169
|
+
"type": "uint256"
|
|
1170
|
+
},
|
|
1171
|
+
{
|
|
1172
|
+
"internalType": "uint192",
|
|
1173
|
+
"name": "openPrice",
|
|
1174
|
+
"type": "uint192"
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
"internalType": "uint192",
|
|
1178
|
+
"name": "tp",
|
|
1179
|
+
"type": "uint192"
|
|
1180
|
+
},
|
|
1181
|
+
{
|
|
1182
|
+
"internalType": "uint192",
|
|
1183
|
+
"name": "sl",
|
|
1184
|
+
"type": "uint192"
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
"internalType": "address",
|
|
1188
|
+
"name": "trader",
|
|
1189
|
+
"type": "address"
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
"internalType": "uint32",
|
|
1193
|
+
"name": "leverage",
|
|
1194
|
+
"type": "uint32"
|
|
1195
|
+
},
|
|
1196
|
+
{
|
|
1197
|
+
"internalType": "uint16",
|
|
1198
|
+
"name": "pairIndex",
|
|
1199
|
+
"type": "uint16"
|
|
1200
|
+
},
|
|
1201
|
+
{
|
|
1202
|
+
"internalType": "uint8",
|
|
1203
|
+
"name": "index",
|
|
1204
|
+
"type": "uint8"
|
|
1205
|
+
},
|
|
1206
|
+
{
|
|
1207
|
+
"internalType": "bool",
|
|
1208
|
+
"name": "buy",
|
|
1209
|
+
"type": "bool"
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
"internalType": "bool",
|
|
1213
|
+
"name": "isDayTrade",
|
|
1214
|
+
"type": "bool"
|
|
1215
|
+
}
|
|
1216
|
+
],
|
|
1217
|
+
"internalType": "struct IOstiumTradingStorage.Trade",
|
|
1218
|
+
"name": "trade",
|
|
1219
|
+
"type": "tuple"
|
|
1220
|
+
}
|
|
1221
|
+
],
|
|
1222
|
+
"indexed": false,
|
|
1223
|
+
"internalType": "struct IOstiumTradingStorage.PendingMarketOrder",
|
|
1224
|
+
"name": "order",
|
|
1225
|
+
"type": "tuple"
|
|
1226
|
+
}
|
|
1227
|
+
],
|
|
1228
|
+
"name": "MarketOpenTimeoutExecuted",
|
|
1229
|
+
"type": "event"
|
|
1230
|
+
},
|
|
1231
|
+
{
|
|
1232
|
+
"anonymous": false,
|
|
1233
|
+
"inputs": [
|
|
1234
|
+
{
|
|
1235
|
+
"indexed": true,
|
|
1236
|
+
"internalType": "uint256",
|
|
1237
|
+
"name": "orderId",
|
|
1238
|
+
"type": "uint256"
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
"components": [
|
|
1242
|
+
{
|
|
1243
|
+
"internalType": "uint256",
|
|
1244
|
+
"name": "block",
|
|
1245
|
+
"type": "uint256"
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
"internalType": "uint192",
|
|
1249
|
+
"name": "wantedPrice",
|
|
1250
|
+
"type": "uint192"
|
|
1251
|
+
},
|
|
1252
|
+
{
|
|
1253
|
+
"internalType": "uint32",
|
|
1254
|
+
"name": "slippageP",
|
|
1255
|
+
"type": "uint32"
|
|
1256
|
+
},
|
|
1257
|
+
{
|
|
1258
|
+
"components": [
|
|
1259
|
+
{
|
|
1260
|
+
"internalType": "uint256",
|
|
1261
|
+
"name": "collateral",
|
|
1262
|
+
"type": "uint256"
|
|
1263
|
+
},
|
|
1264
|
+
{
|
|
1265
|
+
"internalType": "uint192",
|
|
1266
|
+
"name": "openPrice",
|
|
1267
|
+
"type": "uint192"
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
"internalType": "uint192",
|
|
1271
|
+
"name": "tp",
|
|
1272
|
+
"type": "uint192"
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
"internalType": "uint192",
|
|
1276
|
+
"name": "sl",
|
|
1277
|
+
"type": "uint192"
|
|
1278
|
+
},
|
|
1279
|
+
{
|
|
1280
|
+
"internalType": "address",
|
|
1281
|
+
"name": "trader",
|
|
1282
|
+
"type": "address"
|
|
1283
|
+
},
|
|
1284
|
+
{
|
|
1285
|
+
"internalType": "uint32",
|
|
1286
|
+
"name": "leverage",
|
|
1287
|
+
"type": "uint32"
|
|
1288
|
+
},
|
|
1289
|
+
{
|
|
1290
|
+
"internalType": "uint16",
|
|
1291
|
+
"name": "pairIndex",
|
|
1292
|
+
"type": "uint16"
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
"internalType": "uint8",
|
|
1296
|
+
"name": "index",
|
|
1297
|
+
"type": "uint8"
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
"internalType": "bool",
|
|
1301
|
+
"name": "buy",
|
|
1302
|
+
"type": "bool"
|
|
1303
|
+
},
|
|
1304
|
+
{
|
|
1305
|
+
"internalType": "bool",
|
|
1306
|
+
"name": "isDayTrade",
|
|
1307
|
+
"type": "bool"
|
|
1308
|
+
}
|
|
1309
|
+
],
|
|
1310
|
+
"internalType": "struct IOstiumTradingStorage.Trade",
|
|
1311
|
+
"name": "trade",
|
|
1312
|
+
"type": "tuple"
|
|
1313
|
+
},
|
|
1314
|
+
{
|
|
1315
|
+
"internalType": "uint16",
|
|
1316
|
+
"name": "percentage",
|
|
1317
|
+
"type": "uint16"
|
|
1318
|
+
}
|
|
1319
|
+
],
|
|
1320
|
+
"indexed": false,
|
|
1321
|
+
"internalType": "struct IOstiumTradingStorage.PendingMarketOrderV2",
|
|
1322
|
+
"name": "order",
|
|
1323
|
+
"type": "tuple"
|
|
1324
|
+
}
|
|
1325
|
+
],
|
|
1326
|
+
"name": "MarketOpenTimeoutExecutedV2",
|
|
1327
|
+
"type": "event"
|
|
1328
|
+
},
|
|
1329
|
+
{
|
|
1330
|
+
"anonymous": false,
|
|
1331
|
+
"inputs": [
|
|
1332
|
+
{
|
|
1333
|
+
"indexed": false,
|
|
1334
|
+
"internalType": "uint16",
|
|
1335
|
+
"name": "value",
|
|
1336
|
+
"type": "uint16"
|
|
1337
|
+
}
|
|
1338
|
+
],
|
|
1339
|
+
"name": "MarketOrdersTimeoutUpdated",
|
|
1340
|
+
"type": "event"
|
|
1341
|
+
},
|
|
1342
|
+
{
|
|
1343
|
+
"anonymous": false,
|
|
1344
|
+
"inputs": [
|
|
1345
|
+
{
|
|
1346
|
+
"indexed": false,
|
|
1347
|
+
"internalType": "uint256",
|
|
1348
|
+
"name": "value",
|
|
1349
|
+
"type": "uint256"
|
|
1350
|
+
}
|
|
1351
|
+
],
|
|
1352
|
+
"name": "MaxAllowedCollateralUpdated",
|
|
1353
|
+
"type": "event"
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
"anonymous": false,
|
|
1357
|
+
"inputs": [
|
|
1358
|
+
{
|
|
1359
|
+
"indexed": true,
|
|
1360
|
+
"internalType": "address",
|
|
1361
|
+
"name": "trader",
|
|
1362
|
+
"type": "address"
|
|
1363
|
+
},
|
|
1364
|
+
{
|
|
1365
|
+
"indexed": true,
|
|
1366
|
+
"internalType": "uint16",
|
|
1367
|
+
"name": "pairIndex",
|
|
1368
|
+
"type": "uint16"
|
|
1369
|
+
},
|
|
1370
|
+
{
|
|
1371
|
+
"indexed": false,
|
|
1372
|
+
"internalType": "uint8",
|
|
1373
|
+
"name": "index",
|
|
1374
|
+
"type": "uint8"
|
|
1375
|
+
}
|
|
1376
|
+
],
|
|
1377
|
+
"name": "OpenLimitCanceled",
|
|
1378
|
+
"type": "event"
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
"anonymous": false,
|
|
1382
|
+
"inputs": [
|
|
1383
|
+
{
|
|
1384
|
+
"indexed": true,
|
|
1385
|
+
"internalType": "address",
|
|
1386
|
+
"name": "trader",
|
|
1387
|
+
"type": "address"
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
"indexed": true,
|
|
1391
|
+
"internalType": "uint16",
|
|
1392
|
+
"name": "pairIndex",
|
|
1393
|
+
"type": "uint16"
|
|
1394
|
+
},
|
|
1395
|
+
{
|
|
1396
|
+
"indexed": false,
|
|
1397
|
+
"internalType": "uint8",
|
|
1398
|
+
"name": "index",
|
|
1399
|
+
"type": "uint8"
|
|
1400
|
+
}
|
|
1401
|
+
],
|
|
1402
|
+
"name": "OpenLimitPlaced",
|
|
1403
|
+
"type": "event"
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
"anonymous": false,
|
|
1407
|
+
"inputs": [
|
|
1408
|
+
{
|
|
1409
|
+
"indexed": true,
|
|
1410
|
+
"internalType": "address",
|
|
1411
|
+
"name": "trader",
|
|
1412
|
+
"type": "address"
|
|
1413
|
+
},
|
|
1414
|
+
{
|
|
1415
|
+
"indexed": true,
|
|
1416
|
+
"internalType": "uint16",
|
|
1417
|
+
"name": "pairIndex",
|
|
1418
|
+
"type": "uint16"
|
|
1419
|
+
},
|
|
1420
|
+
{
|
|
1421
|
+
"indexed": false,
|
|
1422
|
+
"internalType": "uint8",
|
|
1423
|
+
"name": "index",
|
|
1424
|
+
"type": "uint8"
|
|
1425
|
+
},
|
|
1426
|
+
{
|
|
1427
|
+
"components": [
|
|
1428
|
+
{
|
|
1429
|
+
"internalType": "uint256",
|
|
1430
|
+
"name": "collateral",
|
|
1431
|
+
"type": "uint256"
|
|
1432
|
+
},
|
|
1433
|
+
{
|
|
1434
|
+
"internalType": "uint192",
|
|
1435
|
+
"name": "openPrice",
|
|
1436
|
+
"type": "uint192"
|
|
1437
|
+
},
|
|
1438
|
+
{
|
|
1439
|
+
"internalType": "uint192",
|
|
1440
|
+
"name": "tp",
|
|
1441
|
+
"type": "uint192"
|
|
1442
|
+
},
|
|
1443
|
+
{
|
|
1444
|
+
"internalType": "uint192",
|
|
1445
|
+
"name": "sl",
|
|
1446
|
+
"type": "uint192"
|
|
1447
|
+
},
|
|
1448
|
+
{
|
|
1449
|
+
"internalType": "address",
|
|
1450
|
+
"name": "trader",
|
|
1451
|
+
"type": "address"
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
"internalType": "uint32",
|
|
1455
|
+
"name": "leverage",
|
|
1456
|
+
"type": "uint32"
|
|
1457
|
+
},
|
|
1458
|
+
{
|
|
1459
|
+
"internalType": "uint16",
|
|
1460
|
+
"name": "pairIndex",
|
|
1461
|
+
"type": "uint16"
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
"internalType": "uint8",
|
|
1465
|
+
"name": "index",
|
|
1466
|
+
"type": "uint8"
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
"internalType": "bool",
|
|
1470
|
+
"name": "buy",
|
|
1471
|
+
"type": "bool"
|
|
1472
|
+
},
|
|
1473
|
+
{
|
|
1474
|
+
"internalType": "bool",
|
|
1475
|
+
"name": "isDayTrade",
|
|
1476
|
+
"type": "bool"
|
|
1477
|
+
}
|
|
1478
|
+
],
|
|
1479
|
+
"indexed": false,
|
|
1480
|
+
"internalType": "struct IOstiumTradingStorage.Trade",
|
|
1481
|
+
"name": "trade",
|
|
1482
|
+
"type": "tuple"
|
|
1483
|
+
},
|
|
1484
|
+
{
|
|
1485
|
+
"indexed": false,
|
|
1486
|
+
"internalType": "enum IOstiumTradingStorage.OpenOrderType",
|
|
1487
|
+
"name": "orderType",
|
|
1488
|
+
"type": "uint8"
|
|
1489
|
+
},
|
|
1490
|
+
{
|
|
1491
|
+
"components": [
|
|
1492
|
+
{
|
|
1493
|
+
"internalType": "address",
|
|
1494
|
+
"name": "builder",
|
|
1495
|
+
"type": "address"
|
|
1496
|
+
},
|
|
1497
|
+
{
|
|
1498
|
+
"internalType": "uint32",
|
|
1499
|
+
"name": "builderFee",
|
|
1500
|
+
"type": "uint32"
|
|
1501
|
+
}
|
|
1502
|
+
],
|
|
1503
|
+
"indexed": false,
|
|
1504
|
+
"internalType": "struct IOstiumTradingStorage.BuilderFee",
|
|
1505
|
+
"name": "builderFee",
|
|
1506
|
+
"type": "tuple"
|
|
1507
|
+
}
|
|
1508
|
+
],
|
|
1509
|
+
"name": "OpenLimitPlacedV2",
|
|
1510
|
+
"type": "event"
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
"anonymous": false,
|
|
1514
|
+
"inputs": [
|
|
1515
|
+
{
|
|
1516
|
+
"indexed": true,
|
|
1517
|
+
"internalType": "address",
|
|
1518
|
+
"name": "trader",
|
|
1519
|
+
"type": "address"
|
|
1520
|
+
},
|
|
1521
|
+
{
|
|
1522
|
+
"indexed": true,
|
|
1523
|
+
"internalType": "uint16",
|
|
1524
|
+
"name": "pairIndex",
|
|
1525
|
+
"type": "uint16"
|
|
1526
|
+
},
|
|
1527
|
+
{
|
|
1528
|
+
"indexed": false,
|
|
1529
|
+
"internalType": "uint8",
|
|
1530
|
+
"name": "index",
|
|
1531
|
+
"type": "uint8"
|
|
1532
|
+
},
|
|
1533
|
+
{
|
|
1534
|
+
"indexed": false,
|
|
1535
|
+
"internalType": "uint192",
|
|
1536
|
+
"name": "newPrice",
|
|
1537
|
+
"type": "uint192"
|
|
1538
|
+
},
|
|
1539
|
+
{
|
|
1540
|
+
"indexed": false,
|
|
1541
|
+
"internalType": "uint192",
|
|
1542
|
+
"name": "newTp",
|
|
1543
|
+
"type": "uint192"
|
|
1544
|
+
},
|
|
1545
|
+
{
|
|
1546
|
+
"indexed": false,
|
|
1547
|
+
"internalType": "uint192",
|
|
1548
|
+
"name": "newSl",
|
|
1549
|
+
"type": "uint192"
|
|
1550
|
+
}
|
|
1551
|
+
],
|
|
1552
|
+
"name": "OpenLimitUpdated",
|
|
1553
|
+
"type": "event"
|
|
1554
|
+
},
|
|
1555
|
+
{
|
|
1556
|
+
"anonymous": false,
|
|
1557
|
+
"inputs": [
|
|
1558
|
+
{
|
|
1559
|
+
"indexed": true,
|
|
1560
|
+
"internalType": "uint256",
|
|
1561
|
+
"name": "tradeId",
|
|
1562
|
+
"type": "uint256"
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
"indexed": true,
|
|
1566
|
+
"internalType": "address",
|
|
1567
|
+
"name": "trader",
|
|
1568
|
+
"type": "address"
|
|
1569
|
+
},
|
|
1570
|
+
{
|
|
1571
|
+
"indexed": false,
|
|
1572
|
+
"internalType": "uint16",
|
|
1573
|
+
"name": "pairIndex",
|
|
1574
|
+
"type": "uint16"
|
|
1575
|
+
},
|
|
1576
|
+
{
|
|
1577
|
+
"indexed": false,
|
|
1578
|
+
"internalType": "uint256",
|
|
1579
|
+
"name": "amount",
|
|
1580
|
+
"type": "uint256"
|
|
1581
|
+
}
|
|
1582
|
+
],
|
|
1583
|
+
"name": "OracleFeeCharged",
|
|
1584
|
+
"type": "event"
|
|
1585
|
+
},
|
|
1586
|
+
{
|
|
1587
|
+
"anonymous": false,
|
|
1588
|
+
"inputs": [
|
|
1589
|
+
{
|
|
1590
|
+
"indexed": true,
|
|
1591
|
+
"internalType": "address",
|
|
1592
|
+
"name": "trader",
|
|
1593
|
+
"type": "address"
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
"indexed": false,
|
|
1597
|
+
"internalType": "uint16",
|
|
1598
|
+
"name": "pairIndex",
|
|
1599
|
+
"type": "uint16"
|
|
1600
|
+
},
|
|
1601
|
+
{
|
|
1602
|
+
"indexed": false,
|
|
1603
|
+
"internalType": "uint256",
|
|
1604
|
+
"name": "amount",
|
|
1605
|
+
"type": "uint256"
|
|
1606
|
+
}
|
|
1607
|
+
],
|
|
1608
|
+
"name": "OracleFeeChargedLimitCancelled",
|
|
1609
|
+
"type": "event"
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
"anonymous": false,
|
|
1613
|
+
"inputs": [
|
|
1614
|
+
{
|
|
1615
|
+
"indexed": true,
|
|
1616
|
+
"internalType": "uint256",
|
|
1617
|
+
"name": "tradeId",
|
|
1618
|
+
"type": "uint256"
|
|
1619
|
+
},
|
|
1620
|
+
{
|
|
1621
|
+
"indexed": true,
|
|
1622
|
+
"internalType": "address",
|
|
1623
|
+
"name": "trader",
|
|
1624
|
+
"type": "address"
|
|
1625
|
+
},
|
|
1626
|
+
{
|
|
1627
|
+
"indexed": false,
|
|
1628
|
+
"internalType": "uint16",
|
|
1629
|
+
"name": "pairIndex",
|
|
1630
|
+
"type": "uint16"
|
|
1631
|
+
},
|
|
1632
|
+
{
|
|
1633
|
+
"indexed": false,
|
|
1634
|
+
"internalType": "uint256",
|
|
1635
|
+
"name": "amount",
|
|
1636
|
+
"type": "uint256"
|
|
1637
|
+
}
|
|
1638
|
+
],
|
|
1639
|
+
"name": "OracleFeeRefunded",
|
|
1640
|
+
"type": "event"
|
|
1641
|
+
},
|
|
1642
|
+
{
|
|
1643
|
+
"anonymous": false,
|
|
1644
|
+
"inputs": [
|
|
1645
|
+
{
|
|
1646
|
+
"indexed": false,
|
|
1647
|
+
"internalType": "bool",
|
|
1648
|
+
"name": "paused",
|
|
1649
|
+
"type": "bool"
|
|
1650
|
+
}
|
|
1651
|
+
],
|
|
1652
|
+
"name": "Paused",
|
|
1653
|
+
"type": "event"
|
|
1654
|
+
},
|
|
1655
|
+
{
|
|
1656
|
+
"anonymous": false,
|
|
1657
|
+
"inputs": [
|
|
1658
|
+
{
|
|
1659
|
+
"indexed": true,
|
|
1660
|
+
"internalType": "uint256",
|
|
1661
|
+
"name": "tradeId",
|
|
1662
|
+
"type": "uint256"
|
|
1663
|
+
},
|
|
1664
|
+
{
|
|
1665
|
+
"indexed": true,
|
|
1666
|
+
"internalType": "uint256",
|
|
1667
|
+
"name": "orderId",
|
|
1668
|
+
"type": "uint256"
|
|
1669
|
+
},
|
|
1670
|
+
{
|
|
1671
|
+
"indexed": true,
|
|
1672
|
+
"internalType": "address",
|
|
1673
|
+
"name": "trader",
|
|
1674
|
+
"type": "address"
|
|
1675
|
+
},
|
|
1676
|
+
{
|
|
1677
|
+
"indexed": false,
|
|
1678
|
+
"internalType": "uint16",
|
|
1679
|
+
"name": "pairIndex",
|
|
1680
|
+
"type": "uint16"
|
|
1681
|
+
},
|
|
1682
|
+
{
|
|
1683
|
+
"indexed": false,
|
|
1684
|
+
"internalType": "uint256",
|
|
1685
|
+
"name": "removeAmount",
|
|
1686
|
+
"type": "uint256"
|
|
1687
|
+
}
|
|
1688
|
+
],
|
|
1689
|
+
"name": "RemoveCollateralInitiated",
|
|
1690
|
+
"type": "event"
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
"anonymous": false,
|
|
1694
|
+
"inputs": [
|
|
1695
|
+
{
|
|
1696
|
+
"indexed": true,
|
|
1697
|
+
"internalType": "uint256",
|
|
1698
|
+
"name": "tradeId",
|
|
1699
|
+
"type": "uint256"
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
"indexed": true,
|
|
1703
|
+
"internalType": "uint256",
|
|
1704
|
+
"name": "orderId",
|
|
1705
|
+
"type": "uint256"
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
"indexed": true,
|
|
1709
|
+
"internalType": "address",
|
|
1710
|
+
"name": "trader",
|
|
1711
|
+
"type": "address"
|
|
1712
|
+
},
|
|
1713
|
+
{
|
|
1714
|
+
"indexed": false,
|
|
1715
|
+
"internalType": "uint16",
|
|
1716
|
+
"name": "pairIndex",
|
|
1717
|
+
"type": "uint16"
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
"indexed": false,
|
|
1721
|
+
"internalType": "uint256",
|
|
1722
|
+
"name": "removeAmount",
|
|
1723
|
+
"type": "uint256"
|
|
1724
|
+
},
|
|
1725
|
+
{
|
|
1726
|
+
"indexed": false,
|
|
1727
|
+
"internalType": "string",
|
|
1728
|
+
"name": "reason",
|
|
1729
|
+
"type": "string"
|
|
1730
|
+
}
|
|
1731
|
+
],
|
|
1732
|
+
"name": "RemoveCollateralRejected",
|
|
1733
|
+
"type": "event"
|
|
1734
|
+
},
|
|
1735
|
+
{
|
|
1736
|
+
"anonymous": false,
|
|
1737
|
+
"inputs": [
|
|
1738
|
+
{
|
|
1739
|
+
"indexed": true,
|
|
1740
|
+
"internalType": "uint256",
|
|
1741
|
+
"name": "tradeId",
|
|
1742
|
+
"type": "uint256"
|
|
1743
|
+
},
|
|
1744
|
+
{
|
|
1745
|
+
"indexed": true,
|
|
1746
|
+
"internalType": "address",
|
|
1747
|
+
"name": "trader",
|
|
1748
|
+
"type": "address"
|
|
1749
|
+
},
|
|
1750
|
+
{
|
|
1751
|
+
"indexed": true,
|
|
1752
|
+
"internalType": "uint16",
|
|
1753
|
+
"name": "pairIndex",
|
|
1754
|
+
"type": "uint16"
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
"indexed": false,
|
|
1758
|
+
"internalType": "uint8",
|
|
1759
|
+
"name": "index",
|
|
1760
|
+
"type": "uint8"
|
|
1761
|
+
},
|
|
1762
|
+
{
|
|
1763
|
+
"indexed": false,
|
|
1764
|
+
"internalType": "uint192",
|
|
1765
|
+
"name": "newSl",
|
|
1766
|
+
"type": "uint192"
|
|
1767
|
+
}
|
|
1768
|
+
],
|
|
1769
|
+
"name": "SlUpdated",
|
|
1770
|
+
"type": "event"
|
|
1771
|
+
},
|
|
1772
|
+
{
|
|
1773
|
+
"anonymous": false,
|
|
1774
|
+
"inputs": [
|
|
1775
|
+
{
|
|
1776
|
+
"indexed": true,
|
|
1777
|
+
"internalType": "uint256",
|
|
1778
|
+
"name": "tradeId",
|
|
1779
|
+
"type": "uint256"
|
|
1780
|
+
},
|
|
1781
|
+
{
|
|
1782
|
+
"indexed": true,
|
|
1783
|
+
"internalType": "address",
|
|
1784
|
+
"name": "trader",
|
|
1785
|
+
"type": "address"
|
|
1786
|
+
},
|
|
1787
|
+
{
|
|
1788
|
+
"indexed": true,
|
|
1789
|
+
"internalType": "uint16",
|
|
1790
|
+
"name": "pairIndex",
|
|
1791
|
+
"type": "uint16"
|
|
1792
|
+
},
|
|
1793
|
+
{
|
|
1794
|
+
"indexed": false,
|
|
1795
|
+
"internalType": "uint256",
|
|
1796
|
+
"name": "topUpAmount",
|
|
1797
|
+
"type": "uint256"
|
|
1798
|
+
},
|
|
1799
|
+
{
|
|
1800
|
+
"indexed": false,
|
|
1801
|
+
"internalType": "uint32",
|
|
1802
|
+
"name": "newLeverage",
|
|
1803
|
+
"type": "uint32"
|
|
1804
|
+
}
|
|
1805
|
+
],
|
|
1806
|
+
"name": "TopUpCollateralExecuted",
|
|
1807
|
+
"type": "event"
|
|
1808
|
+
},
|
|
1809
|
+
{
|
|
1810
|
+
"anonymous": false,
|
|
1811
|
+
"inputs": [
|
|
1812
|
+
{
|
|
1813
|
+
"indexed": true,
|
|
1814
|
+
"internalType": "uint256",
|
|
1815
|
+
"name": "tradeId",
|
|
1816
|
+
"type": "uint256"
|
|
1817
|
+
},
|
|
1818
|
+
{
|
|
1819
|
+
"indexed": true,
|
|
1820
|
+
"internalType": "address",
|
|
1821
|
+
"name": "trader",
|
|
1822
|
+
"type": "address"
|
|
1823
|
+
},
|
|
1824
|
+
{
|
|
1825
|
+
"indexed": true,
|
|
1826
|
+
"internalType": "uint16",
|
|
1827
|
+
"name": "pairIndex",
|
|
1828
|
+
"type": "uint16"
|
|
1829
|
+
},
|
|
1830
|
+
{
|
|
1831
|
+
"indexed": false,
|
|
1832
|
+
"internalType": "uint8",
|
|
1833
|
+
"name": "index",
|
|
1834
|
+
"type": "uint8"
|
|
1835
|
+
},
|
|
1836
|
+
{
|
|
1837
|
+
"indexed": false,
|
|
1838
|
+
"internalType": "uint192",
|
|
1839
|
+
"name": "newTp",
|
|
1840
|
+
"type": "uint192"
|
|
1841
|
+
}
|
|
1842
|
+
],
|
|
1843
|
+
"name": "TpUpdated",
|
|
1844
|
+
"type": "event"
|
|
1845
|
+
},
|
|
1846
|
+
{
|
|
1847
|
+
"anonymous": false,
|
|
1848
|
+
"inputs": [
|
|
1849
|
+
{
|
|
1850
|
+
"indexed": false,
|
|
1851
|
+
"internalType": "uint16",
|
|
1852
|
+
"name": "value",
|
|
1853
|
+
"type": "uint16"
|
|
1854
|
+
}
|
|
1855
|
+
],
|
|
1856
|
+
"name": "TriggerTimeoutUpdated",
|
|
1857
|
+
"type": "event"
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
"inputs": [],
|
|
1861
|
+
"name": "DELEGATION_TYPEHASH",
|
|
1862
|
+
"outputs": [
|
|
1863
|
+
{
|
|
1864
|
+
"internalType": "bytes32",
|
|
1865
|
+
"name": "",
|
|
1866
|
+
"type": "bytes32"
|
|
1867
|
+
}
|
|
1868
|
+
],
|
|
1869
|
+
"stateMutability": "view",
|
|
1870
|
+
"type": "function"
|
|
1871
|
+
},
|
|
1872
|
+
{
|
|
1873
|
+
"inputs": [],
|
|
1874
|
+
"name": "DOMAIN_SEPARATOR",
|
|
1875
|
+
"outputs": [
|
|
1876
|
+
{
|
|
1877
|
+
"internalType": "bytes32",
|
|
1878
|
+
"name": "",
|
|
1879
|
+
"type": "bytes32"
|
|
1880
|
+
}
|
|
1881
|
+
],
|
|
1882
|
+
"stateMutability": "view",
|
|
1883
|
+
"type": "function"
|
|
1884
|
+
},
|
|
1885
|
+
{
|
|
1886
|
+
"inputs": [],
|
|
1887
|
+
"name": "_msgSender",
|
|
1888
|
+
"outputs": [
|
|
1889
|
+
{
|
|
1890
|
+
"internalType": "address",
|
|
1891
|
+
"name": "",
|
|
1892
|
+
"type": "address"
|
|
1893
|
+
}
|
|
1894
|
+
],
|
|
1895
|
+
"stateMutability": "view",
|
|
1896
|
+
"type": "function"
|
|
1897
|
+
},
|
|
1898
|
+
{
|
|
1899
|
+
"inputs": [
|
|
1900
|
+
{
|
|
1901
|
+
"internalType": "uint16",
|
|
1902
|
+
"name": "pairIndex",
|
|
1903
|
+
"type": "uint16"
|
|
1904
|
+
},
|
|
1905
|
+
{
|
|
1906
|
+
"internalType": "uint8",
|
|
1907
|
+
"name": "index",
|
|
1908
|
+
"type": "uint8"
|
|
1909
|
+
}
|
|
1910
|
+
],
|
|
1911
|
+
"name": "cancelOpenLimitOrder",
|
|
1912
|
+
"outputs": [],
|
|
1913
|
+
"stateMutability": "nonpayable",
|
|
1914
|
+
"type": "function"
|
|
1915
|
+
},
|
|
1916
|
+
{
|
|
1917
|
+
"inputs": [
|
|
1918
|
+
{
|
|
1919
|
+
"internalType": "uint16",
|
|
1920
|
+
"name": "pairIndex",
|
|
1921
|
+
"type": "uint16"
|
|
1922
|
+
},
|
|
1923
|
+
{
|
|
1924
|
+
"internalType": "uint8",
|
|
1925
|
+
"name": "index",
|
|
1926
|
+
"type": "uint8"
|
|
1927
|
+
},
|
|
1928
|
+
{
|
|
1929
|
+
"internalType": "uint16",
|
|
1930
|
+
"name": "closePercentage",
|
|
1931
|
+
"type": "uint16"
|
|
1932
|
+
},
|
|
1933
|
+
{
|
|
1934
|
+
"internalType": "uint192",
|
|
1935
|
+
"name": "marketPrice",
|
|
1936
|
+
"type": "uint192"
|
|
1937
|
+
},
|
|
1938
|
+
{
|
|
1939
|
+
"internalType": "uint32",
|
|
1940
|
+
"name": "slippageP",
|
|
1941
|
+
"type": "uint32"
|
|
1942
|
+
}
|
|
1943
|
+
],
|
|
1944
|
+
"name": "closeTradeMarket",
|
|
1945
|
+
"outputs": [],
|
|
1946
|
+
"stateMutability": "nonpayable",
|
|
1947
|
+
"type": "function"
|
|
1948
|
+
},
|
|
1949
|
+
{
|
|
1950
|
+
"inputs": [
|
|
1951
|
+
{
|
|
1952
|
+
"internalType": "uint256",
|
|
1953
|
+
"name": "_order",
|
|
1954
|
+
"type": "uint256"
|
|
1955
|
+
},
|
|
1956
|
+
{
|
|
1957
|
+
"internalType": "bool",
|
|
1958
|
+
"name": "retry",
|
|
1959
|
+
"type": "bool"
|
|
1960
|
+
}
|
|
1961
|
+
],
|
|
1962
|
+
"name": "closeTradeMarketTimeout",
|
|
1963
|
+
"outputs": [],
|
|
1964
|
+
"stateMutability": "nonpayable",
|
|
1965
|
+
"type": "function"
|
|
1966
|
+
},
|
|
1967
|
+
{
|
|
1968
|
+
"inputs": [
|
|
1969
|
+
{
|
|
1970
|
+
"internalType": "address",
|
|
1971
|
+
"name": "",
|
|
1972
|
+
"type": "address"
|
|
1973
|
+
}
|
|
1974
|
+
],
|
|
1975
|
+
"name": "delegatableNonces",
|
|
1976
|
+
"outputs": [
|
|
1977
|
+
{
|
|
1978
|
+
"internalType": "uint256",
|
|
1979
|
+
"name": "",
|
|
1980
|
+
"type": "uint256"
|
|
1981
|
+
}
|
|
1982
|
+
],
|
|
1983
|
+
"stateMutability": "view",
|
|
1984
|
+
"type": "function"
|
|
1985
|
+
},
|
|
1986
|
+
{
|
|
1987
|
+
"inputs": [
|
|
1988
|
+
{
|
|
1989
|
+
"internalType": "address",
|
|
1990
|
+
"name": "trader",
|
|
1991
|
+
"type": "address"
|
|
1992
|
+
},
|
|
1993
|
+
{
|
|
1994
|
+
"internalType": "bytes",
|
|
1995
|
+
"name": "call_data",
|
|
1996
|
+
"type": "bytes"
|
|
1997
|
+
}
|
|
1998
|
+
],
|
|
1999
|
+
"name": "delegatedAction",
|
|
2000
|
+
"outputs": [
|
|
2001
|
+
{
|
|
2002
|
+
"internalType": "bytes",
|
|
2003
|
+
"name": "",
|
|
2004
|
+
"type": "bytes"
|
|
2005
|
+
}
|
|
2006
|
+
],
|
|
2007
|
+
"stateMutability": "nonpayable",
|
|
2008
|
+
"type": "function"
|
|
2009
|
+
},
|
|
2010
|
+
{
|
|
2011
|
+
"inputs": [
|
|
2012
|
+
{
|
|
2013
|
+
"internalType": "address",
|
|
2014
|
+
"name": "delegator",
|
|
2015
|
+
"type": "address"
|
|
2016
|
+
}
|
|
2017
|
+
],
|
|
2018
|
+
"name": "delegations",
|
|
2019
|
+
"outputs": [
|
|
2020
|
+
{
|
|
2021
|
+
"internalType": "address",
|
|
2022
|
+
"name": "",
|
|
2023
|
+
"type": "address"
|
|
2024
|
+
}
|
|
2025
|
+
],
|
|
2026
|
+
"stateMutability": "view",
|
|
2027
|
+
"type": "function"
|
|
2028
|
+
},
|
|
2029
|
+
{
|
|
2030
|
+
"inputs": [],
|
|
2031
|
+
"name": "done",
|
|
2032
|
+
"outputs": [],
|
|
2033
|
+
"stateMutability": "nonpayable",
|
|
2034
|
+
"type": "function"
|
|
2035
|
+
},
|
|
2036
|
+
{
|
|
2037
|
+
"inputs": [
|
|
2038
|
+
{
|
|
2039
|
+
"internalType": "enum IOstiumTradingStorage.LimitOrder",
|
|
2040
|
+
"name": "orderType",
|
|
2041
|
+
"type": "uint8"
|
|
2042
|
+
},
|
|
2043
|
+
{
|
|
2044
|
+
"internalType": "address",
|
|
2045
|
+
"name": "trader",
|
|
2046
|
+
"type": "address"
|
|
2047
|
+
},
|
|
2048
|
+
{
|
|
2049
|
+
"internalType": "uint16",
|
|
2050
|
+
"name": "pairIndex",
|
|
2051
|
+
"type": "uint16"
|
|
2052
|
+
},
|
|
2053
|
+
{
|
|
2054
|
+
"internalType": "uint8",
|
|
2055
|
+
"name": "index",
|
|
2056
|
+
"type": "uint8"
|
|
2057
|
+
},
|
|
2058
|
+
{
|
|
2059
|
+
"internalType": "uint256",
|
|
2060
|
+
"name": "priceTimestamp",
|
|
2061
|
+
"type": "uint256"
|
|
2062
|
+
}
|
|
2063
|
+
],
|
|
2064
|
+
"name": "executeAutomationOrder",
|
|
2065
|
+
"outputs": [
|
|
2066
|
+
{
|
|
2067
|
+
"internalType": "enum IOstiumTrading.AutomationOrderStatus",
|
|
2068
|
+
"name": "",
|
|
2069
|
+
"type": "uint8"
|
|
2070
|
+
}
|
|
2071
|
+
],
|
|
2072
|
+
"stateMutability": "nonpayable",
|
|
2073
|
+
"type": "function"
|
|
2074
|
+
},
|
|
2075
|
+
{
|
|
2076
|
+
"inputs": [
|
|
2077
|
+
{
|
|
2078
|
+
"internalType": "contract IOstiumRegistry",
|
|
2079
|
+
"name": "_registry",
|
|
2080
|
+
"type": "address"
|
|
2081
|
+
},
|
|
2082
|
+
{
|
|
2083
|
+
"internalType": "uint256",
|
|
2084
|
+
"name": "_maxAllowedCollateral",
|
|
2085
|
+
"type": "uint256"
|
|
2086
|
+
},
|
|
2087
|
+
{
|
|
2088
|
+
"internalType": "uint16",
|
|
2089
|
+
"name": "_marketOrdersTimeout",
|
|
2090
|
+
"type": "uint16"
|
|
2091
|
+
},
|
|
2092
|
+
{
|
|
2093
|
+
"internalType": "uint16",
|
|
2094
|
+
"name": "_triggerTimeout",
|
|
2095
|
+
"type": "uint16"
|
|
2096
|
+
}
|
|
2097
|
+
],
|
|
2098
|
+
"name": "initialize",
|
|
2099
|
+
"outputs": [],
|
|
2100
|
+
"stateMutability": "nonpayable",
|
|
2101
|
+
"type": "function"
|
|
2102
|
+
},
|
|
2103
|
+
{
|
|
2104
|
+
"inputs": [],
|
|
2105
|
+
"name": "isDone",
|
|
2106
|
+
"outputs": [
|
|
2107
|
+
{
|
|
2108
|
+
"internalType": "bool",
|
|
2109
|
+
"name": "",
|
|
2110
|
+
"type": "bool"
|
|
2111
|
+
}
|
|
2112
|
+
],
|
|
2113
|
+
"stateMutability": "view",
|
|
2114
|
+
"type": "function"
|
|
2115
|
+
},
|
|
2116
|
+
{
|
|
2117
|
+
"inputs": [],
|
|
2118
|
+
"name": "isPaused",
|
|
2119
|
+
"outputs": [
|
|
2120
|
+
{
|
|
2121
|
+
"internalType": "bool",
|
|
2122
|
+
"name": "",
|
|
2123
|
+
"type": "bool"
|
|
2124
|
+
}
|
|
2125
|
+
],
|
|
2126
|
+
"stateMutability": "view",
|
|
2127
|
+
"type": "function"
|
|
2128
|
+
},
|
|
2129
|
+
{
|
|
2130
|
+
"inputs": [],
|
|
2131
|
+
"name": "marketOrdersTimeout",
|
|
2132
|
+
"outputs": [
|
|
2133
|
+
{
|
|
2134
|
+
"internalType": "uint16",
|
|
2135
|
+
"name": "",
|
|
2136
|
+
"type": "uint16"
|
|
2137
|
+
}
|
|
2138
|
+
],
|
|
2139
|
+
"stateMutability": "view",
|
|
2140
|
+
"type": "function"
|
|
2141
|
+
},
|
|
2142
|
+
{
|
|
2143
|
+
"inputs": [],
|
|
2144
|
+
"name": "maxAllowedCollateral",
|
|
2145
|
+
"outputs": [
|
|
2146
|
+
{
|
|
2147
|
+
"internalType": "uint256",
|
|
2148
|
+
"name": "",
|
|
2149
|
+
"type": "uint256"
|
|
2150
|
+
}
|
|
2151
|
+
],
|
|
2152
|
+
"stateMutability": "view",
|
|
2153
|
+
"type": "function"
|
|
2154
|
+
},
|
|
2155
|
+
{
|
|
2156
|
+
"inputs": [
|
|
2157
|
+
{
|
|
2158
|
+
"components": [
|
|
2159
|
+
{
|
|
2160
|
+
"internalType": "uint256",
|
|
2161
|
+
"name": "collateral",
|
|
2162
|
+
"type": "uint256"
|
|
2163
|
+
},
|
|
2164
|
+
{
|
|
2165
|
+
"internalType": "uint192",
|
|
2166
|
+
"name": "openPrice",
|
|
2167
|
+
"type": "uint192"
|
|
2168
|
+
},
|
|
2169
|
+
{
|
|
2170
|
+
"internalType": "uint192",
|
|
2171
|
+
"name": "tp",
|
|
2172
|
+
"type": "uint192"
|
|
2173
|
+
},
|
|
2174
|
+
{
|
|
2175
|
+
"internalType": "uint192",
|
|
2176
|
+
"name": "sl",
|
|
2177
|
+
"type": "uint192"
|
|
2178
|
+
},
|
|
2179
|
+
{
|
|
2180
|
+
"internalType": "address",
|
|
2181
|
+
"name": "trader",
|
|
2182
|
+
"type": "address"
|
|
2183
|
+
},
|
|
2184
|
+
{
|
|
2185
|
+
"internalType": "uint32",
|
|
2186
|
+
"name": "leverage",
|
|
2187
|
+
"type": "uint32"
|
|
2188
|
+
},
|
|
2189
|
+
{
|
|
2190
|
+
"internalType": "uint16",
|
|
2191
|
+
"name": "pairIndex",
|
|
2192
|
+
"type": "uint16"
|
|
2193
|
+
},
|
|
2194
|
+
{
|
|
2195
|
+
"internalType": "uint8",
|
|
2196
|
+
"name": "index",
|
|
2197
|
+
"type": "uint8"
|
|
2198
|
+
},
|
|
2199
|
+
{
|
|
2200
|
+
"internalType": "bool",
|
|
2201
|
+
"name": "buy",
|
|
2202
|
+
"type": "bool"
|
|
2203
|
+
},
|
|
2204
|
+
{
|
|
2205
|
+
"internalType": "bool",
|
|
2206
|
+
"name": "isDayTrade",
|
|
2207
|
+
"type": "bool"
|
|
2208
|
+
}
|
|
2209
|
+
],
|
|
2210
|
+
"internalType": "struct IOstiumTradingStorage.Trade",
|
|
2211
|
+
"name": "t",
|
|
2212
|
+
"type": "tuple"
|
|
2213
|
+
},
|
|
2214
|
+
{
|
|
2215
|
+
"components": [
|
|
2216
|
+
{
|
|
2217
|
+
"internalType": "address",
|
|
2218
|
+
"name": "builder",
|
|
2219
|
+
"type": "address"
|
|
2220
|
+
},
|
|
2221
|
+
{
|
|
2222
|
+
"internalType": "uint32",
|
|
2223
|
+
"name": "builderFee",
|
|
2224
|
+
"type": "uint32"
|
|
2225
|
+
}
|
|
2226
|
+
],
|
|
2227
|
+
"internalType": "struct IOstiumTradingStorage.BuilderFee",
|
|
2228
|
+
"name": "bf",
|
|
2229
|
+
"type": "tuple"
|
|
2230
|
+
},
|
|
2231
|
+
{
|
|
2232
|
+
"internalType": "enum IOstiumTradingStorage.OpenOrderType",
|
|
2233
|
+
"name": "orderType",
|
|
2234
|
+
"type": "uint8"
|
|
2235
|
+
},
|
|
2236
|
+
{
|
|
2237
|
+
"internalType": "uint256",
|
|
2238
|
+
"name": "slippageP",
|
|
2239
|
+
"type": "uint256"
|
|
2240
|
+
}
|
|
2241
|
+
],
|
|
2242
|
+
"name": "openTrade",
|
|
2243
|
+
"outputs": [],
|
|
2244
|
+
"stateMutability": "nonpayable",
|
|
2245
|
+
"type": "function"
|
|
2246
|
+
},
|
|
2247
|
+
{
|
|
2248
|
+
"inputs": [
|
|
2249
|
+
{
|
|
2250
|
+
"internalType": "uint256",
|
|
2251
|
+
"name": "_order",
|
|
2252
|
+
"type": "uint256"
|
|
2253
|
+
}
|
|
2254
|
+
],
|
|
2255
|
+
"name": "openTradeMarketTimeout",
|
|
2256
|
+
"outputs": [],
|
|
2257
|
+
"stateMutability": "nonpayable",
|
|
2258
|
+
"type": "function"
|
|
2259
|
+
},
|
|
2260
|
+
{
|
|
2261
|
+
"inputs": [],
|
|
2262
|
+
"name": "pause",
|
|
2263
|
+
"outputs": [],
|
|
2264
|
+
"stateMutability": "nonpayable",
|
|
2265
|
+
"type": "function"
|
|
2266
|
+
},
|
|
2267
|
+
{
|
|
2268
|
+
"inputs": [],
|
|
2269
|
+
"name": "registry",
|
|
2270
|
+
"outputs": [
|
|
2271
|
+
{
|
|
2272
|
+
"internalType": "contract IOstiumRegistry",
|
|
2273
|
+
"name": "",
|
|
2274
|
+
"type": "address"
|
|
2275
|
+
}
|
|
2276
|
+
],
|
|
2277
|
+
"stateMutability": "view",
|
|
2278
|
+
"type": "function"
|
|
2279
|
+
},
|
|
2280
|
+
{
|
|
2281
|
+
"inputs": [
|
|
2282
|
+
{
|
|
2283
|
+
"internalType": "uint16",
|
|
2284
|
+
"name": "pairIndex",
|
|
2285
|
+
"type": "uint16"
|
|
2286
|
+
},
|
|
2287
|
+
{
|
|
2288
|
+
"internalType": "uint8",
|
|
2289
|
+
"name": "index",
|
|
2290
|
+
"type": "uint8"
|
|
2291
|
+
},
|
|
2292
|
+
{
|
|
2293
|
+
"internalType": "uint256",
|
|
2294
|
+
"name": "removeAmount",
|
|
2295
|
+
"type": "uint256"
|
|
2296
|
+
}
|
|
2297
|
+
],
|
|
2298
|
+
"name": "removeCollateral",
|
|
2299
|
+
"outputs": [],
|
|
2300
|
+
"stateMutability": "nonpayable",
|
|
2301
|
+
"type": "function"
|
|
2302
|
+
},
|
|
2303
|
+
{
|
|
2304
|
+
"inputs": [],
|
|
2305
|
+
"name": "removeDelegate",
|
|
2306
|
+
"outputs": [],
|
|
2307
|
+
"stateMutability": "nonpayable",
|
|
2308
|
+
"type": "function"
|
|
2309
|
+
},
|
|
2310
|
+
{
|
|
2311
|
+
"inputs": [
|
|
2312
|
+
{
|
|
2313
|
+
"internalType": "address",
|
|
2314
|
+
"name": "delegate",
|
|
2315
|
+
"type": "address"
|
|
2316
|
+
}
|
|
2317
|
+
],
|
|
2318
|
+
"name": "setDelegate",
|
|
2319
|
+
"outputs": [],
|
|
2320
|
+
"stateMutability": "nonpayable",
|
|
2321
|
+
"type": "function"
|
|
2322
|
+
},
|
|
2323
|
+
{
|
|
2324
|
+
"inputs": [
|
|
2325
|
+
{
|
|
2326
|
+
"internalType": "address",
|
|
2327
|
+
"name": "delegator",
|
|
2328
|
+
"type": "address"
|
|
2329
|
+
},
|
|
2330
|
+
{
|
|
2331
|
+
"internalType": "address",
|
|
2332
|
+
"name": "delegate",
|
|
2333
|
+
"type": "address"
|
|
2334
|
+
},
|
|
2335
|
+
{
|
|
2336
|
+
"internalType": "uint256",
|
|
2337
|
+
"name": "nonce",
|
|
2338
|
+
"type": "uint256"
|
|
2339
|
+
},
|
|
2340
|
+
{
|
|
2341
|
+
"internalType": "uint256",
|
|
2342
|
+
"name": "expiry",
|
|
2343
|
+
"type": "uint256"
|
|
2344
|
+
},
|
|
2345
|
+
{
|
|
2346
|
+
"internalType": "uint8",
|
|
2347
|
+
"name": "v",
|
|
2348
|
+
"type": "uint8"
|
|
2349
|
+
},
|
|
2350
|
+
{
|
|
2351
|
+
"internalType": "bytes32",
|
|
2352
|
+
"name": "r",
|
|
2353
|
+
"type": "bytes32"
|
|
2354
|
+
},
|
|
2355
|
+
{
|
|
2356
|
+
"internalType": "bytes32",
|
|
2357
|
+
"name": "s",
|
|
2358
|
+
"type": "bytes32"
|
|
2359
|
+
}
|
|
2360
|
+
],
|
|
2361
|
+
"name": "setDelegateWithSignature",
|
|
2362
|
+
"outputs": [],
|
|
2363
|
+
"stateMutability": "nonpayable",
|
|
2364
|
+
"type": "function"
|
|
2365
|
+
},
|
|
2366
|
+
{
|
|
2367
|
+
"inputs": [
|
|
2368
|
+
{
|
|
2369
|
+
"internalType": "uint256",
|
|
2370
|
+
"name": "value",
|
|
2371
|
+
"type": "uint256"
|
|
2372
|
+
}
|
|
2373
|
+
],
|
|
2374
|
+
"name": "setMarketOrdersTimeout",
|
|
2375
|
+
"outputs": [],
|
|
2376
|
+
"stateMutability": "nonpayable",
|
|
2377
|
+
"type": "function"
|
|
2378
|
+
},
|
|
2379
|
+
{
|
|
2380
|
+
"inputs": [
|
|
2381
|
+
{
|
|
2382
|
+
"internalType": "uint256",
|
|
2383
|
+
"name": "value",
|
|
2384
|
+
"type": "uint256"
|
|
2385
|
+
}
|
|
2386
|
+
],
|
|
2387
|
+
"name": "setMaxAllowedCollateral",
|
|
2388
|
+
"outputs": [],
|
|
2389
|
+
"stateMutability": "nonpayable",
|
|
2390
|
+
"type": "function"
|
|
2391
|
+
},
|
|
2392
|
+
{
|
|
2393
|
+
"inputs": [
|
|
2394
|
+
{
|
|
2395
|
+
"internalType": "uint256",
|
|
2396
|
+
"name": "value",
|
|
2397
|
+
"type": "uint256"
|
|
2398
|
+
}
|
|
2399
|
+
],
|
|
2400
|
+
"name": "setTriggerTimeout",
|
|
2401
|
+
"outputs": [],
|
|
2402
|
+
"stateMutability": "nonpayable",
|
|
2403
|
+
"type": "function"
|
|
2404
|
+
},
|
|
2405
|
+
{
|
|
2406
|
+
"inputs": [
|
|
2407
|
+
{
|
|
2408
|
+
"internalType": "uint16",
|
|
2409
|
+
"name": "pairIndex",
|
|
2410
|
+
"type": "uint16"
|
|
2411
|
+
},
|
|
2412
|
+
{
|
|
2413
|
+
"internalType": "uint8",
|
|
2414
|
+
"name": "index",
|
|
2415
|
+
"type": "uint8"
|
|
2416
|
+
},
|
|
2417
|
+
{
|
|
2418
|
+
"internalType": "uint256",
|
|
2419
|
+
"name": "topUpAmount",
|
|
2420
|
+
"type": "uint256"
|
|
2421
|
+
}
|
|
2422
|
+
],
|
|
2423
|
+
"name": "topUpCollateral",
|
|
2424
|
+
"outputs": [],
|
|
2425
|
+
"stateMutability": "nonpayable",
|
|
2426
|
+
"type": "function"
|
|
2427
|
+
},
|
|
2428
|
+
{
|
|
2429
|
+
"inputs": [],
|
|
2430
|
+
"name": "triggerTimeout",
|
|
2431
|
+
"outputs": [
|
|
2432
|
+
{
|
|
2433
|
+
"internalType": "uint16",
|
|
2434
|
+
"name": "",
|
|
2435
|
+
"type": "uint16"
|
|
2436
|
+
}
|
|
2437
|
+
],
|
|
2438
|
+
"stateMutability": "view",
|
|
2439
|
+
"type": "function"
|
|
2440
|
+
},
|
|
2441
|
+
{
|
|
2442
|
+
"inputs": [
|
|
2443
|
+
{
|
|
2444
|
+
"internalType": "uint16",
|
|
2445
|
+
"name": "pairIndex",
|
|
2446
|
+
"type": "uint16"
|
|
2447
|
+
},
|
|
2448
|
+
{
|
|
2449
|
+
"internalType": "uint8",
|
|
2450
|
+
"name": "index",
|
|
2451
|
+
"type": "uint8"
|
|
2452
|
+
},
|
|
2453
|
+
{
|
|
2454
|
+
"internalType": "uint192",
|
|
2455
|
+
"name": "price",
|
|
2456
|
+
"type": "uint192"
|
|
2457
|
+
},
|
|
2458
|
+
{
|
|
2459
|
+
"internalType": "uint192",
|
|
2460
|
+
"name": "tp",
|
|
2461
|
+
"type": "uint192"
|
|
2462
|
+
},
|
|
2463
|
+
{
|
|
2464
|
+
"internalType": "uint192",
|
|
2465
|
+
"name": "sl",
|
|
2466
|
+
"type": "uint192"
|
|
2467
|
+
}
|
|
2468
|
+
],
|
|
2469
|
+
"name": "updateOpenLimitOrder",
|
|
2470
|
+
"outputs": [],
|
|
2471
|
+
"stateMutability": "nonpayable",
|
|
2472
|
+
"type": "function"
|
|
2473
|
+
},
|
|
2474
|
+
{
|
|
2475
|
+
"inputs": [
|
|
2476
|
+
{
|
|
2477
|
+
"internalType": "uint16",
|
|
2478
|
+
"name": "pairIndex",
|
|
2479
|
+
"type": "uint16"
|
|
2480
|
+
},
|
|
2481
|
+
{
|
|
2482
|
+
"internalType": "uint8",
|
|
2483
|
+
"name": "index",
|
|
2484
|
+
"type": "uint8"
|
|
2485
|
+
},
|
|
2486
|
+
{
|
|
2487
|
+
"internalType": "uint192",
|
|
2488
|
+
"name": "newSl",
|
|
2489
|
+
"type": "uint192"
|
|
2490
|
+
}
|
|
2491
|
+
],
|
|
2492
|
+
"name": "updateSl",
|
|
2493
|
+
"outputs": [],
|
|
2494
|
+
"stateMutability": "nonpayable",
|
|
2495
|
+
"type": "function"
|
|
2496
|
+
},
|
|
2497
|
+
{
|
|
2498
|
+
"inputs": [
|
|
2499
|
+
{
|
|
2500
|
+
"internalType": "uint16",
|
|
2501
|
+
"name": "pairIndex",
|
|
2502
|
+
"type": "uint16"
|
|
2503
|
+
},
|
|
2504
|
+
{
|
|
2505
|
+
"internalType": "uint8",
|
|
2506
|
+
"name": "index",
|
|
2507
|
+
"type": "uint8"
|
|
2508
|
+
},
|
|
2509
|
+
{
|
|
2510
|
+
"internalType": "uint192",
|
|
2511
|
+
"name": "newTp",
|
|
2512
|
+
"type": "uint192"
|
|
2513
|
+
}
|
|
2514
|
+
],
|
|
2515
|
+
"name": "updateTp",
|
|
2516
|
+
"outputs": [],
|
|
2517
|
+
"stateMutability": "nonpayable",
|
|
2518
|
+
"type": "function"
|
|
2519
|
+
}
|
|
2520
|
+
];
|
|
2521
|
+
|
|
2522
|
+
// src/encoder.ts
|
|
2523
|
+
function tx(to, data) {
|
|
2524
|
+
return { to, data, value: 0n };
|
|
2525
|
+
}
|
|
2526
|
+
function encodeOpenTrade(trade, builderFee, orderType, slippageP, tradingAddress) {
|
|
2527
|
+
const data = viem.encodeFunctionData({
|
|
2528
|
+
abi: TRADING_ABI,
|
|
2529
|
+
functionName: "openTrade",
|
|
2530
|
+
args: [
|
|
2531
|
+
{
|
|
2532
|
+
collateral: trade.collateral,
|
|
2533
|
+
openPrice: trade.openPrice,
|
|
2534
|
+
tp: trade.tp,
|
|
2535
|
+
sl: trade.sl,
|
|
2536
|
+
trader: trade.trader,
|
|
2537
|
+
leverage: Number(trade.leverage),
|
|
2538
|
+
pairIndex: Number(trade.pairIndex),
|
|
2539
|
+
index: Number(trade.index),
|
|
2540
|
+
buy: trade.buy,
|
|
2541
|
+
isDayTrade: trade.isDayTrade ?? false
|
|
2542
|
+
},
|
|
2543
|
+
{
|
|
2544
|
+
builder: builderFee.builder,
|
|
2545
|
+
builderFee: Number(builderFee.builderFee)
|
|
2546
|
+
},
|
|
2547
|
+
orderType,
|
|
2548
|
+
slippageP
|
|
2549
|
+
]
|
|
2550
|
+
});
|
|
2551
|
+
return tx(tradingAddress, data);
|
|
2552
|
+
}
|
|
2553
|
+
function encodeCloseTradeMarket(pairIndex, index, closePercentage, marketPrice, slippageP, tradingAddress) {
|
|
2554
|
+
const data = viem.encodeFunctionData({
|
|
2555
|
+
abi: TRADING_ABI,
|
|
2556
|
+
functionName: "closeTradeMarket",
|
|
2557
|
+
args: [Number(pairIndex), Number(index), Number(closePercentage), marketPrice, Number(slippageP)]
|
|
2558
|
+
});
|
|
2559
|
+
return tx(tradingAddress, data);
|
|
2560
|
+
}
|
|
2561
|
+
function encodeCancelLimitOrder(pairIndex, orderIndex, tradingAddress) {
|
|
2562
|
+
const data = viem.encodeFunctionData({
|
|
2563
|
+
abi: TRADING_ABI,
|
|
2564
|
+
functionName: "cancelOpenLimitOrder",
|
|
2565
|
+
args: [Number(pairIndex), Number(orderIndex)]
|
|
2566
|
+
});
|
|
2567
|
+
return tx(tradingAddress, data);
|
|
2568
|
+
}
|
|
2569
|
+
function encodeCloseTradeMarketTimeout(orderId, retry, tradingAddress) {
|
|
2570
|
+
const data = viem.encodeFunctionData({
|
|
2571
|
+
abi: TRADING_ABI,
|
|
2572
|
+
functionName: "closeTradeMarketTimeout",
|
|
2573
|
+
args: [orderId, retry]
|
|
2574
|
+
});
|
|
2575
|
+
return tx(tradingAddress, data);
|
|
2576
|
+
}
|
|
2577
|
+
function encodeOpenTradeMarketTimeout(orderId, tradingAddress) {
|
|
2578
|
+
const data = viem.encodeFunctionData({
|
|
2579
|
+
abi: TRADING_ABI,
|
|
2580
|
+
functionName: "openTradeMarketTimeout",
|
|
2581
|
+
args: [orderId]
|
|
2582
|
+
});
|
|
2583
|
+
return tx(tradingAddress, data);
|
|
2584
|
+
}
|
|
2585
|
+
function encodeUpdateOpenLimitOrder(pairIndex, orderIndex, price, tp, sl, tradingAddress) {
|
|
2586
|
+
const data = viem.encodeFunctionData({
|
|
2587
|
+
abi: TRADING_ABI,
|
|
2588
|
+
functionName: "updateOpenLimitOrder",
|
|
2589
|
+
args: [Number(pairIndex), Number(orderIndex), price, tp, sl]
|
|
2590
|
+
});
|
|
2591
|
+
return tx(tradingAddress, data);
|
|
2592
|
+
}
|
|
2593
|
+
function encodeUpdateTp(pairIndex, index, newTp, tradingAddress) {
|
|
2594
|
+
const data = viem.encodeFunctionData({
|
|
2595
|
+
abi: TRADING_ABI,
|
|
2596
|
+
functionName: "updateTp",
|
|
2597
|
+
args: [Number(pairIndex), Number(index), newTp]
|
|
2598
|
+
});
|
|
2599
|
+
return tx(tradingAddress, data);
|
|
2600
|
+
}
|
|
2601
|
+
function encodeUpdateSl(pairIndex, index, newSl, tradingAddress) {
|
|
2602
|
+
const data = viem.encodeFunctionData({
|
|
2603
|
+
abi: TRADING_ABI,
|
|
2604
|
+
functionName: "updateSl",
|
|
2605
|
+
args: [Number(pairIndex), Number(index), newSl]
|
|
2606
|
+
});
|
|
2607
|
+
return tx(tradingAddress, data);
|
|
2608
|
+
}
|
|
2609
|
+
function encodeTopUpCollateral(pairIndex, index, amount, tradingAddress) {
|
|
2610
|
+
const data = viem.encodeFunctionData({
|
|
2611
|
+
abi: TRADING_ABI,
|
|
2612
|
+
functionName: "topUpCollateral",
|
|
2613
|
+
args: [Number(pairIndex), Number(index), amount]
|
|
2614
|
+
});
|
|
2615
|
+
return tx(tradingAddress, data);
|
|
2616
|
+
}
|
|
2617
|
+
function encodeRemoveCollateral(pairIndex, index, amount, tradingAddress) {
|
|
2618
|
+
const data = viem.encodeFunctionData({
|
|
2619
|
+
abi: TRADING_ABI,
|
|
2620
|
+
functionName: "removeCollateral",
|
|
2621
|
+
args: [Number(pairIndex), Number(index), amount]
|
|
2622
|
+
});
|
|
2623
|
+
return tx(tradingAddress, data);
|
|
2624
|
+
}
|
|
2625
|
+
function encodeSetDelegate(delegateAddress, tradingAddress) {
|
|
2626
|
+
const data = viem.encodeFunctionData({
|
|
2627
|
+
abi: TRADING_ABI,
|
|
2628
|
+
functionName: "setDelegate",
|
|
2629
|
+
args: [viem.getAddress(delegateAddress)]
|
|
2630
|
+
});
|
|
2631
|
+
return tx(tradingAddress, data);
|
|
2632
|
+
}
|
|
2633
|
+
function encodeRemoveDelegate(tradingAddress) {
|
|
2634
|
+
const data = viem.encodeFunctionData({
|
|
2635
|
+
abi: TRADING_ABI,
|
|
2636
|
+
functionName: "removeDelegate",
|
|
2637
|
+
args: []
|
|
2638
|
+
});
|
|
2639
|
+
return tx(tradingAddress, data);
|
|
2640
|
+
}
|
|
2641
|
+
function encodeDelegatedAction(traderAddress, callData, tradingAddress) {
|
|
2642
|
+
const data = viem.encodeFunctionData({
|
|
2643
|
+
abi: TRADING_ABI,
|
|
2644
|
+
functionName: "delegatedAction",
|
|
2645
|
+
args: [viem.getAddress(traderAddress), callData]
|
|
2646
|
+
});
|
|
2647
|
+
return tx(tradingAddress, data);
|
|
2648
|
+
}
|
|
2649
|
+
function encodeUsdcApprove(spender, amount, usdcAddress) {
|
|
2650
|
+
const data = viem.encodeFunctionData({
|
|
2651
|
+
abi: ERC20_ABI,
|
|
2652
|
+
functionName: "approve",
|
|
2653
|
+
args: [viem.getAddress(spender), amount]
|
|
2654
|
+
});
|
|
2655
|
+
return tx(usdcAddress, data);
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
// src/signer/delegated.ts
|
|
2659
|
+
var DelegatedSignerStrategy = class {
|
|
2660
|
+
traderAddress;
|
|
2661
|
+
constructor(traderAddress) {
|
|
2662
|
+
this.traderAddress = traderAddress;
|
|
2663
|
+
}
|
|
2664
|
+
prepare(encoded) {
|
|
2665
|
+
return encodeDelegatedAction(this.traderAddress, encoded.data, encoded.to);
|
|
2666
|
+
}
|
|
2667
|
+
};
|
|
2668
|
+
|
|
2669
|
+
// src/signer/index.ts
|
|
2670
|
+
function createSigner(config, traderAddress, selfGasless = false) {
|
|
2671
|
+
if (isDelegated(config) || selfGasless) {
|
|
2672
|
+
return new DelegatedSignerStrategy(traderAddress);
|
|
2673
|
+
}
|
|
2674
|
+
return new SelfSignerStrategy(traderAddress);
|
|
2675
|
+
}
|
|
2676
|
+
function classifySubmitError(err) {
|
|
2677
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2678
|
+
const lower = msg.toLowerCase();
|
|
2679
|
+
if (lower.includes("execution reverted") || lower.includes("reverted with reason") || lower.includes("contract function") || lower.includes("revert")) {
|
|
2680
|
+
return new OstiumError(`Contract reverted: ${msg}`, "CONTRACT_ERROR" /* CONTRACT_ERROR */, err);
|
|
2681
|
+
}
|
|
2682
|
+
if (lower.includes("fetch") || lower.includes("etimedout") || lower.includes("econnrefused") || lower.includes("enotfound") || lower.includes("network") || lower.includes("timeout") || lower.includes("connection")) {
|
|
2683
|
+
return new OstiumError(`Network error: ${msg}`, "NETWORK_ERROR" /* NETWORK_ERROR */, err);
|
|
2684
|
+
}
|
|
2685
|
+
return new OstiumError(
|
|
2686
|
+
`Transaction submission failed: ${msg}`,
|
|
2687
|
+
"SUBMISSION_FAILED" /* SUBMISSION_FAILED */,
|
|
2688
|
+
err
|
|
2689
|
+
);
|
|
2690
|
+
}
|
|
2691
|
+
var SelfSubmissionStrategy = class {
|
|
2692
|
+
effectiveSender;
|
|
2693
|
+
send;
|
|
2694
|
+
constructor(config, testnet) {
|
|
2695
|
+
if (!config.rpcUrl) {
|
|
2696
|
+
throw new OstiumError(
|
|
2697
|
+
"rpcUrl is required for self-submission mode",
|
|
2698
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
2699
|
+
);
|
|
2700
|
+
}
|
|
2701
|
+
const chain = testnet ? chains.arbitrumSepolia : chains.arbitrum;
|
|
2702
|
+
const account = accounts.privateKeyToAccount(config.privateKey);
|
|
2703
|
+
const walletClient = viem.createWalletClient({ account, chain, transport: viem.http(config.rpcUrl) });
|
|
2704
|
+
this.effectiveSender = account.address;
|
|
2705
|
+
this.send = (encoded) => walletClient.sendTransaction({ account, to: encoded.to, data: encoded.data, value: encoded.value });
|
|
2706
|
+
}
|
|
2707
|
+
async submit(encoded) {
|
|
2708
|
+
try {
|
|
2709
|
+
const txHash = await this.send(encoded);
|
|
2710
|
+
return { txHash };
|
|
2711
|
+
} catch (err) {
|
|
2712
|
+
throw classifySubmitError(err);
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
};
|
|
2716
|
+
function classifyGaslessError(err) {
|
|
2717
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2718
|
+
const lower = msg.toLowerCase();
|
|
2719
|
+
if (lower.includes("execution reverted") || lower.includes("reverted with reason") || lower.includes("revert") || lower.includes("userop reverted") || lower.includes("aa")) {
|
|
2720
|
+
return new OstiumError(`Contract reverted: ${msg}`, "CONTRACT_ERROR" /* CONTRACT_ERROR */, err);
|
|
2721
|
+
}
|
|
2722
|
+
if (lower.includes("429") || lower.includes("rate limit") || lower.includes("too many requests")) {
|
|
2723
|
+
return new OstiumError(
|
|
2724
|
+
`Pimlico rate limit exceeded \u2014 retry after 60 seconds. ${msg}`,
|
|
2725
|
+
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
2726
|
+
err
|
|
2727
|
+
);
|
|
2728
|
+
}
|
|
2729
|
+
if (lower.includes("sponsorship") || lower.includes("policy")) {
|
|
2730
|
+
return new OstiumError(
|
|
2731
|
+
`Pimlico sponsorship policy error \u2014 check sponsorshipPolicyId config. ${msg}`,
|
|
2732
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */,
|
|
2733
|
+
err
|
|
2734
|
+
);
|
|
2735
|
+
}
|
|
2736
|
+
if (lower.includes("fetch") || lower.includes("etimedout") || lower.includes("econnrefused") || lower.includes("network") || lower.includes("timeout") || lower.includes("bundle") || lower.includes("connection")) {
|
|
2737
|
+
return new OstiumError(`Network error: ${msg}`, "NETWORK_ERROR" /* NETWORK_ERROR */, err);
|
|
2738
|
+
}
|
|
2739
|
+
return new OstiumError(
|
|
2740
|
+
`Gasless submission failed: ${msg}`,
|
|
2741
|
+
"SUBMISSION_FAILED" /* SUBMISSION_FAILED */,
|
|
2742
|
+
err
|
|
2743
|
+
);
|
|
2744
|
+
}
|
|
2745
|
+
var GaslessSubmissionStrategy = class _GaslessSubmissionStrategy {
|
|
2746
|
+
effectiveSender;
|
|
2747
|
+
testnet;
|
|
2748
|
+
pimlicoUrl;
|
|
2749
|
+
sponsorshipPolicyId;
|
|
2750
|
+
// Cached Safe account — derived once in create(), reused on every submit().
|
|
2751
|
+
safeAccount;
|
|
2752
|
+
constructor(testnet, pimlicoUrl, sponsorshipPolicyId, safeAccount) {
|
|
2753
|
+
this.testnet = testnet;
|
|
2754
|
+
this.pimlicoUrl = pimlicoUrl;
|
|
2755
|
+
this.sponsorshipPolicyId = sponsorshipPolicyId;
|
|
2756
|
+
this.safeAccount = safeAccount;
|
|
2757
|
+
this.effectiveSender = safeAccount.address;
|
|
2758
|
+
}
|
|
2759
|
+
static async create(config, testnet) {
|
|
2760
|
+
if (!config.pimlicoUrl) {
|
|
2761
|
+
throw new OstiumError(
|
|
2762
|
+
"pimlicoUrl is required for gasless mode",
|
|
2763
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
2764
|
+
);
|
|
2765
|
+
}
|
|
2766
|
+
const chain = testnet ? chains.arbitrumSepolia : chains.arbitrum;
|
|
2767
|
+
const pimlicoUrl = config.pimlicoUrl;
|
|
2768
|
+
const chainRpc = config.rpcUrl ?? (testnet ? "https://sepolia-rollup.arbitrum.io/rpc" : "https://arb1.arbitrum.io/rpc");
|
|
2769
|
+
const rpcTransport = viem.http(chainRpc);
|
|
2770
|
+
const publicClient = viem.createPublicClient({ chain, transport: rpcTransport });
|
|
2771
|
+
const owner = accounts.privateKeyToAccount(config.privateKey);
|
|
2772
|
+
try {
|
|
2773
|
+
const safeAccount = await accounts$1.toSafeSmartAccount({
|
|
2774
|
+
client: publicClient,
|
|
2775
|
+
owners: [owner],
|
|
2776
|
+
entryPoint: {
|
|
2777
|
+
address: accountAbstraction.entryPoint07Address,
|
|
2778
|
+
version: "0.7"
|
|
2779
|
+
},
|
|
2780
|
+
version: "1.4.1"
|
|
2781
|
+
});
|
|
2782
|
+
return new _GaslessSubmissionStrategy(testnet, pimlicoUrl, config.sponsorshipPolicyId, safeAccount);
|
|
2783
|
+
} catch (err) {
|
|
2784
|
+
throw new OstiumError(
|
|
2785
|
+
`Failed to derive Safe smart account address: ${err instanceof Error ? err.message : String(err)}`,
|
|
2786
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */,
|
|
2787
|
+
err
|
|
2788
|
+
);
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
async submit(encoded) {
|
|
2792
|
+
const chain = this.testnet ? chains.arbitrumSepolia : chains.arbitrum;
|
|
2793
|
+
const pimlicoClient = pimlico.createPimlicoClient({
|
|
2794
|
+
transport: viem.http(this.pimlicoUrl),
|
|
2795
|
+
entryPoint: {
|
|
2796
|
+
address: accountAbstraction.entryPoint07Address,
|
|
2797
|
+
version: "0.7"
|
|
2798
|
+
}
|
|
2799
|
+
});
|
|
2800
|
+
try {
|
|
2801
|
+
const smartAccountClient = permissionless.createSmartAccountClient({
|
|
2802
|
+
account: this.safeAccount,
|
|
2803
|
+
chain,
|
|
2804
|
+
bundlerTransport: viem.http(this.pimlicoUrl),
|
|
2805
|
+
paymaster: pimlicoClient,
|
|
2806
|
+
userOperation: {
|
|
2807
|
+
estimateFeesPerGas: async () => (await pimlicoClient.getUserOperationGasPrice()).fast
|
|
2808
|
+
},
|
|
2809
|
+
...this.sponsorshipPolicyId && {
|
|
2810
|
+
paymasterContext: {
|
|
2811
|
+
sponsorshipPolicyId: this.sponsorshipPolicyId
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
});
|
|
2815
|
+
const txHash = await smartAccountClient.sendTransaction({
|
|
2816
|
+
to: encoded.to,
|
|
2817
|
+
data: encoded.data,
|
|
2818
|
+
value: encoded.value
|
|
2819
|
+
});
|
|
2820
|
+
return { txHash, smartAccountAddress: this.safeAccount.address };
|
|
2821
|
+
} catch (err) {
|
|
2822
|
+
throw classifyGaslessError(err);
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
};
|
|
2826
|
+
|
|
2827
|
+
// src/submitter/index.ts
|
|
2828
|
+
async function createSubmitter(config, testnet) {
|
|
2829
|
+
if (isGasless(config)) {
|
|
2830
|
+
return GaslessSubmissionStrategy.create(config, testnet);
|
|
2831
|
+
}
|
|
2832
|
+
if (!config.rpcUrl) {
|
|
2833
|
+
throw new Error("rpcUrl is required for self-submission mode");
|
|
2834
|
+
}
|
|
2835
|
+
return new SelfSubmissionStrategy(config, testnet);
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
// src/data/config.ts
|
|
2839
|
+
var DEFAULT_SUBGRAPH_ENDPOINT = "https://api.subgraph.ormilabs.com/api/public/67a599d5-c8d2-4cc4-9c4d-2975a97bc5d8/subgraphs/ost-prod/live/gn";
|
|
2840
|
+
var DEFAULT_SUBGRAPH_ENDPOINT_TESTNET = "https://api.subgraph.ormilabs.com/api/public/67a599d5-c8d2-4cc4-9c4d-2975a97bc5d8/subgraphs/ost-sep/live/gn";
|
|
2841
|
+
var DEFAULT_BUILDER_API_URL = "https://builder.ostium.io";
|
|
2842
|
+
|
|
2843
|
+
// src/data/errors.ts
|
|
2844
|
+
var OstiumSubgraphErrorCode = /* @__PURE__ */ ((OstiumSubgraphErrorCode2) => {
|
|
2845
|
+
OstiumSubgraphErrorCode2["INVALID_CONFIG"] = "INVALID_CONFIG";
|
|
2846
|
+
OstiumSubgraphErrorCode2["INVALID_PARAMS"] = "INVALID_PARAMS";
|
|
2847
|
+
OstiumSubgraphErrorCode2["FETCH_FAILED"] = "FETCH_FAILED";
|
|
2848
|
+
OstiumSubgraphErrorCode2["NOT_FOUND"] = "NOT_FOUND";
|
|
2849
|
+
OstiumSubgraphErrorCode2["PARSE_ERROR"] = "PARSE_ERROR";
|
|
2850
|
+
return OstiumSubgraphErrorCode2;
|
|
2851
|
+
})(OstiumSubgraphErrorCode || {});
|
|
2852
|
+
var OstiumSubgraphError = class extends Error {
|
|
2853
|
+
constructor(message, code, cause) {
|
|
2854
|
+
super(message);
|
|
2855
|
+
this.code = code;
|
|
2856
|
+
this.cause = cause;
|
|
2857
|
+
this.name = "OstiumSubgraphError";
|
|
2858
|
+
}
|
|
2859
|
+
code;
|
|
2860
|
+
cause;
|
|
2861
|
+
};
|
|
2862
|
+
|
|
2863
|
+
// src/data/queries.ts
|
|
2864
|
+
var PAIR_FIELDS = `
|
|
2865
|
+
id
|
|
2866
|
+
from
|
|
2867
|
+
to
|
|
2868
|
+
maxLeverage
|
|
2869
|
+
overnightMaxLeverage
|
|
2870
|
+
takerFeeP
|
|
2871
|
+
maxOI
|
|
2872
|
+
longOI
|
|
2873
|
+
shortOI
|
|
2874
|
+
group { id name maxLeverage }
|
|
2875
|
+
lastUpdateTimestamp
|
|
2876
|
+
buyVolume
|
|
2877
|
+
sellVolume
|
|
2878
|
+
decayRate
|
|
2879
|
+
netVolThreshold
|
|
2880
|
+
priceImpactK
|
|
2881
|
+
accRolloverLong
|
|
2882
|
+
accRolloverShort
|
|
2883
|
+
lastRolloverBlock
|
|
2884
|
+
lastRolloverLongPure
|
|
2885
|
+
brokerPremium
|
|
2886
|
+
isNegativeRolloverAllowed
|
|
2887
|
+
lastTradePrice
|
|
2888
|
+
spreadP`;
|
|
2889
|
+
var GetPairsAndGroupsQuery = `
|
|
2890
|
+
query GetPairsAndGroups {
|
|
2891
|
+
pairs(orderBy: id, orderDirection: asc, subgraphError: allow) {
|
|
2892
|
+
${PAIR_FIELDS}
|
|
2893
|
+
}
|
|
2894
|
+
groups { id name maxLeverage }
|
|
2895
|
+
}`;
|
|
2896
|
+
var PAIR_FULL = `
|
|
2897
|
+
pair {
|
|
2898
|
+
id from to
|
|
2899
|
+
maxLeverage overnightMaxLeverage
|
|
2900
|
+
takerFeeP
|
|
2901
|
+
maxOI longOI shortOI
|
|
2902
|
+
group { id name maxLeverage }
|
|
2903
|
+
lastUpdateTimestamp
|
|
2904
|
+
buyVolume sellVolume decayRate netVolThreshold priceImpactK
|
|
2905
|
+
accRolloverLong accRolloverShort lastRolloverBlock lastRolloverLongPure
|
|
2906
|
+
brokerPremium isNegativeRolloverAllowed lastTradePrice
|
|
2907
|
+
}`;
|
|
2908
|
+
var TRADE_CORE = `
|
|
2909
|
+
id tradeID trader
|
|
2910
|
+
isOpen isBuy isDayTrade
|
|
2911
|
+
index tradeType
|
|
2912
|
+
collateral notional tradeNotional leverage highestLeverage
|
|
2913
|
+
openPrice stopLossPrice takeProfitPrice
|
|
2914
|
+
rollover timestamp`;
|
|
2915
|
+
var GetTraderOpenTradesQuery = `
|
|
2916
|
+
query GetTraderOpenTrades($trader: String!, $skip: Int!, $first: Int!) {
|
|
2917
|
+
trades(
|
|
2918
|
+
where: { isOpen: true, trader: $trader }
|
|
2919
|
+
skip: $skip first: $first
|
|
2920
|
+
orderBy: timestamp orderDirection: desc
|
|
2921
|
+
) {
|
|
2922
|
+
${TRADE_CORE}
|
|
2923
|
+
${PAIR_FULL}
|
|
2924
|
+
}
|
|
2925
|
+
}`;
|
|
2926
|
+
var ORDER_FIELDS = `
|
|
2927
|
+
id tradeID limitID trader
|
|
2928
|
+
pair { id from to group { id name } }
|
|
2929
|
+
orderAction orderType isBuy
|
|
2930
|
+
isPending isCancelled cancelReason
|
|
2931
|
+
collateral notional tradeNotional leverage
|
|
2932
|
+
price priceAfterImpact priceImpactP
|
|
2933
|
+
vaultFee devFee oracleFee rolloverFee liquidationFee
|
|
2934
|
+
builder builderFee
|
|
2935
|
+
profitPercent totalProfitPercent amountSentToTrader
|
|
2936
|
+
closePercent
|
|
2937
|
+
initiatedTx initiatedBlock initiatedAt
|
|
2938
|
+
executedTx executedBlock executedAt`;
|
|
2939
|
+
function buildExecutedFillsQuery(opts) {
|
|
2940
|
+
const args = ["$skip: Int!", "$first: Int!"];
|
|
2941
|
+
const conditions = ["isPending: false", "isCancelled: false"];
|
|
2942
|
+
if (opts.hasTrader) {
|
|
2943
|
+
args.push("$trader: String!");
|
|
2944
|
+
conditions.push("trader: $trader");
|
|
2945
|
+
}
|
|
2946
|
+
if (opts.hasPairId) {
|
|
2947
|
+
args.push("$pairId: String!");
|
|
2948
|
+
conditions.push("pair: $pairId");
|
|
2949
|
+
}
|
|
2950
|
+
if (opts.hasTimeRange) {
|
|
2951
|
+
args.push("$startSec: BigInt!", "$endSec: BigInt!");
|
|
2952
|
+
conditions.push("executedAt_gte: $startSec", "executedAt_lte: $endSec");
|
|
2953
|
+
}
|
|
2954
|
+
return `
|
|
2955
|
+
query GetExecutedFills(${args.join(", ")}) {
|
|
2956
|
+
orders(
|
|
2957
|
+
where: { ${conditions.join(", ")} }
|
|
2958
|
+
skip: $skip first: $first
|
|
2959
|
+
orderBy: executedAt orderDirection: desc
|
|
2960
|
+
) { ${ORDER_FIELDS} }
|
|
2961
|
+
}`;
|
|
2962
|
+
}
|
|
2963
|
+
function buildGetOrdersQuery(opts) {
|
|
2964
|
+
const args = ["$skip: Int!", "$first: Int!"];
|
|
2965
|
+
const conditions = [];
|
|
2966
|
+
if (opts.hasOrderIds) {
|
|
2967
|
+
args.push("$ids: [String!]!");
|
|
2968
|
+
conditions.push("id_in: $ids");
|
|
2969
|
+
}
|
|
2970
|
+
if (opts.hasInitiatedTxHashes) {
|
|
2971
|
+
args.push("$txHashes: [String!]!");
|
|
2972
|
+
conditions.push("initiatedTx_in: $txHashes");
|
|
2973
|
+
}
|
|
2974
|
+
if (opts.hasTrader) {
|
|
2975
|
+
args.push("$trader: String!");
|
|
2976
|
+
conditions.push("trader: $trader");
|
|
2977
|
+
}
|
|
2978
|
+
const where = conditions.length > 0 ? `where: { ${conditions.join(", ")} }` : "";
|
|
2979
|
+
return `
|
|
2980
|
+
query GetOrders(${args.join(", ")}) {
|
|
2981
|
+
orders(
|
|
2982
|
+
${where}
|
|
2983
|
+
skip: $skip first: $first
|
|
2984
|
+
orderBy: initiatedAt orderDirection: desc
|
|
2985
|
+
) { ${ORDER_FIELDS} }
|
|
2986
|
+
}`;
|
|
2987
|
+
}
|
|
2988
|
+
var LIMIT_FIELDS = `
|
|
2989
|
+
id uniqueId orderId trader
|
|
2990
|
+
pair { id from to group { id name } }
|
|
2991
|
+
isBuy limitType isActive executionStarted
|
|
2992
|
+
collateral notional tradeNotional leverage
|
|
2993
|
+
openPrice takeProfitPrice stopLossPrice
|
|
2994
|
+
block initiatedAt updatedAt`;
|
|
2995
|
+
var GetTraderActiveLimitsQuery = `
|
|
2996
|
+
query GetTraderActiveLimits($trader: String!, $skip: Int!, $first: Int!) {
|
|
2997
|
+
limits(
|
|
2998
|
+
where: { isActive: true, trader: $trader }
|
|
2999
|
+
skip: $skip first: $first
|
|
3000
|
+
orderBy: updatedAt orderDirection: desc
|
|
3001
|
+
) { ${LIMIT_FIELDS} }
|
|
3002
|
+
}`;
|
|
3003
|
+
|
|
3004
|
+
// src/data/internal/pagination.ts
|
|
3005
|
+
async function paginateAll(fetcher, batchSize = 1e3, max = Infinity) {
|
|
3006
|
+
const results = [];
|
|
3007
|
+
let skip = 0;
|
|
3008
|
+
while (results.length < max) {
|
|
3009
|
+
const remaining = max - results.length;
|
|
3010
|
+
const fetchSize = Math.min(batchSize, remaining);
|
|
3011
|
+
const page = await fetcher(skip, fetchSize);
|
|
3012
|
+
results.push(...page);
|
|
3013
|
+
if (page.length < fetchSize) break;
|
|
3014
|
+
skip += fetchSize;
|
|
3015
|
+
}
|
|
3016
|
+
return results;
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
// src/data/internal/precision.ts
|
|
3020
|
+
var USDC_DECIMALS = 6;
|
|
3021
|
+
var TOKEN_DECIMALS = 18;
|
|
3022
|
+
var LEVERAGE_DECIMALS = 2;
|
|
3023
|
+
|
|
3024
|
+
// src/data/internal/decimal.ts
|
|
3025
|
+
function formatUsdc(value) {
|
|
3026
|
+
if (value === void 0 || value === null) return 0;
|
|
3027
|
+
if (typeof value === "number") return value;
|
|
3028
|
+
if (typeof value === "string" && value.includes(".")) return parseFloat(value);
|
|
3029
|
+
return Number(viem.formatUnits(BigInt(value), USDC_DECIMALS));
|
|
3030
|
+
}
|
|
3031
|
+
function formatTokens(value) {
|
|
3032
|
+
if (value === void 0 || value === null) return 0;
|
|
3033
|
+
if (typeof value === "number") return value;
|
|
3034
|
+
if (typeof value === "string" && value.includes(".")) return parseFloat(value);
|
|
3035
|
+
return Number(viem.formatUnits(BigInt(value), TOKEN_DECIMALS));
|
|
3036
|
+
}
|
|
3037
|
+
function formatLeverage(value) {
|
|
3038
|
+
if (value === void 0 || value === null) return 0;
|
|
3039
|
+
if (typeof value === "number") return value;
|
|
3040
|
+
if (typeof value === "string" && value.includes(".")) return parseFloat(value);
|
|
3041
|
+
return Number(viem.formatUnits(BigInt(value), LEVERAGE_DECIMALS));
|
|
3042
|
+
}
|
|
3043
|
+
function parseUsdc2(value) {
|
|
3044
|
+
return viem.parseUnits(value.toString(), USDC_DECIMALS);
|
|
3045
|
+
}
|
|
3046
|
+
function parseTokens(value) {
|
|
3047
|
+
return viem.parseUnits(value.toString(), TOKEN_DECIMALS);
|
|
3048
|
+
}
|
|
3049
|
+
function parseLeverage2(value) {
|
|
3050
|
+
return viem.parseUnits(value.toString(), LEVERAGE_DECIMALS);
|
|
3051
|
+
}
|
|
3052
|
+
function getMaxLeverage(trade) {
|
|
3053
|
+
return trade.pair.maxLeverage && trade.pair.maxLeverage !== "0" ? trade.pair.maxLeverage : trade.pair.group?.maxLeverage ?? "1";
|
|
3054
|
+
}
|
|
3055
|
+
function timeElapsedSince(pair) {
|
|
3056
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
3057
|
+
const last = parseInt(pair.lastUpdateTimestamp ?? "0") || parseInt(pair.lastFundingTime ?? "0");
|
|
3058
|
+
return (now - last).toString();
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
// src/data/internal/calculations.ts
|
|
3062
|
+
var EMPTY_PNL = {
|
|
3063
|
+
pnl: 0,
|
|
3064
|
+
pnlPercent: 0,
|
|
3065
|
+
rollover: 0,
|
|
3066
|
+
netPnl: 0,
|
|
3067
|
+
netValue: 0,
|
|
3068
|
+
liquidationPrice: 0,
|
|
3069
|
+
priceAfterImpact: 0
|
|
3070
|
+
};
|
|
3071
|
+
function getRolloverAccumulator(pair, isBuy, blockNumber) {
|
|
3072
|
+
const accRollover = isBuy ? pair.accRolloverLong ?? "0" : pair.accRolloverShort ?? "0";
|
|
3073
|
+
return formulae.GetCurrentRolloverFee(
|
|
3074
|
+
accRollover,
|
|
3075
|
+
pair.lastRolloverBlock ?? "0",
|
|
3076
|
+
pair.brokerPremium ?? "0",
|
|
3077
|
+
pair.lastRolloverLongPure ?? "0",
|
|
3078
|
+
isBuy,
|
|
3079
|
+
pair.isNegativeRolloverAllowed ?? false,
|
|
3080
|
+
blockNumber.toString()
|
|
3081
|
+
);
|
|
3082
|
+
}
|
|
3083
|
+
function getTradeRolloverFeeRaw(trade, currentRolloverAccumulator) {
|
|
3084
|
+
return BigInt(
|
|
3085
|
+
formulae.GetTradeRolloverFee(
|
|
3086
|
+
trade.rollover,
|
|
3087
|
+
currentRolloverAccumulator,
|
|
3088
|
+
trade.collateral,
|
|
3089
|
+
trade.leverage
|
|
3090
|
+
)
|
|
3091
|
+
);
|
|
3092
|
+
}
|
|
3093
|
+
function getLiquidationPrice(trade, rolloverRaw) {
|
|
3094
|
+
if (!trade.leverage || trade.leverage === "0") return 0;
|
|
3095
|
+
if (!trade.openPrice || trade.openPrice === "0") return 0;
|
|
3096
|
+
if (!trade.collateral || trade.collateral === "0") return 0;
|
|
3097
|
+
try {
|
|
3098
|
+
const maxLeverage = formulae.GetEffectiveMaxLeverage(
|
|
3099
|
+
getMaxLeverage(trade),
|
|
3100
|
+
trade.pair.overnightMaxLeverage ?? "0",
|
|
3101
|
+
trade.isDayTrade ?? false
|
|
3102
|
+
);
|
|
3103
|
+
return formatTokens(
|
|
3104
|
+
formulae.GetTradeLiquidationPrice(
|
|
3105
|
+
trade.openPrice,
|
|
3106
|
+
trade.isBuy,
|
|
3107
|
+
trade.collateral,
|
|
3108
|
+
trade.leverage,
|
|
3109
|
+
rolloverRaw.toString(),
|
|
3110
|
+
"0",
|
|
3111
|
+
// funding excluded
|
|
3112
|
+
maxLeverage
|
|
3113
|
+
)
|
|
3114
|
+
);
|
|
3115
|
+
} catch {
|
|
3116
|
+
return 0;
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
function getPriceImpact(marketPrice, isBuy, isOpen, pair, collateral, leverage) {
|
|
3120
|
+
if (!marketPrice.mid) return { priceImpactP: "0", priceAfterImpact: "0" };
|
|
3121
|
+
return formulae.CalculateDynamicPriceImpact(
|
|
3122
|
+
parseTokens(marketPrice.mid).toString(),
|
|
3123
|
+
parseTokens(marketPrice.ask).toString(),
|
|
3124
|
+
parseTokens(marketPrice.bid).toString(),
|
|
3125
|
+
isOpen,
|
|
3126
|
+
isBuy,
|
|
3127
|
+
parseUsdc2(collateral).toString(),
|
|
3128
|
+
parseLeverage2(leverage).toString(),
|
|
3129
|
+
pair.buyVolume ?? "0",
|
|
3130
|
+
pair.sellVolume ?? "0",
|
|
3131
|
+
pair.decayRate ?? "0",
|
|
3132
|
+
timeElapsedSince(pair),
|
|
3133
|
+
pair.netVolThreshold ?? "0",
|
|
3134
|
+
pair.priceImpactK ?? "0"
|
|
3135
|
+
);
|
|
3136
|
+
}
|
|
3137
|
+
function getTradePnL(trade, marketPrice, blockNumber) {
|
|
3138
|
+
if (!trade?.pair || !marketPrice?.mid || !blockNumber) return EMPTY_PNL;
|
|
3139
|
+
const rolloverAcc = getRolloverAccumulator(trade.pair, trade.isBuy, blockNumber);
|
|
3140
|
+
const rolloverRaw = getTradeRolloverFeeRaw(trade, rolloverAcc);
|
|
3141
|
+
const liquidationPrice = getLiquidationPrice(trade, rolloverRaw);
|
|
3142
|
+
const impactResult = getPriceImpact(
|
|
3143
|
+
marketPrice,
|
|
3144
|
+
trade.isBuy,
|
|
3145
|
+
false,
|
|
3146
|
+
trade.pair,
|
|
3147
|
+
formatUsdc(trade.collateral),
|
|
3148
|
+
formatLeverage(trade.leverage)
|
|
3149
|
+
);
|
|
3150
|
+
const priceForPnL = parseTokens(marketPrice.mid).toString();
|
|
3151
|
+
const pnlRaw = BigInt(
|
|
3152
|
+
formulae.CurrentTradeProfitRaw(
|
|
3153
|
+
trade.openPrice,
|
|
3154
|
+
priceForPnL,
|
|
3155
|
+
trade.isBuy,
|
|
3156
|
+
trade.leverage,
|
|
3157
|
+
trade.highestLeverage,
|
|
3158
|
+
trade.collateral
|
|
3159
|
+
)
|
|
3160
|
+
);
|
|
3161
|
+
const totalProfitRaw = BigInt(
|
|
3162
|
+
formulae.CurrentTotalProfitRaw(
|
|
3163
|
+
trade.openPrice,
|
|
3164
|
+
priceForPnL,
|
|
3165
|
+
trade.isBuy,
|
|
3166
|
+
trade.leverage,
|
|
3167
|
+
trade.highestLeverage,
|
|
3168
|
+
trade.collateral,
|
|
3169
|
+
rolloverRaw.toString(),
|
|
3170
|
+
"0"
|
|
3171
|
+
// funding excluded
|
|
3172
|
+
)
|
|
3173
|
+
);
|
|
3174
|
+
const pnlPercentRaw = BigInt(
|
|
3175
|
+
formulae.CurrentTotalProfitP(totalProfitRaw.toString(), trade.collateral)
|
|
3176
|
+
);
|
|
3177
|
+
return {
|
|
3178
|
+
pnl: formatUsdc(pnlRaw),
|
|
3179
|
+
pnlPercent: formatUsdc(pnlPercentRaw),
|
|
3180
|
+
rollover: formatUsdc(rolloverRaw),
|
|
3181
|
+
netPnl: formatUsdc(totalProfitRaw),
|
|
3182
|
+
netValue: formatUsdc(totalProfitRaw) + formatUsdc(trade.collateral),
|
|
3183
|
+
liquidationPrice,
|
|
3184
|
+
priceAfterImpact: formatTokens(impactResult.priceAfterImpact)
|
|
3185
|
+
};
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
// src/data/internal/pair-names.ts
|
|
3189
|
+
var PAIR_NAME_MAP = {
|
|
3190
|
+
CL: "WTI",
|
|
3191
|
+
HG: "XCU",
|
|
3192
|
+
SPX: "US500",
|
|
3193
|
+
NDX: "US100",
|
|
3194
|
+
DJI: "US30",
|
|
3195
|
+
DAX: "GER40",
|
|
3196
|
+
FTSE: "UK100",
|
|
3197
|
+
HSI: "HK50",
|
|
3198
|
+
NIK: "JP225"
|
|
3199
|
+
};
|
|
3200
|
+
function normalizePairName(symbol) {
|
|
3201
|
+
return PAIR_NAME_MAP[symbol] ?? symbol;
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3204
|
+
// src/data/internal/pricing.ts
|
|
3205
|
+
async function fetchLivePrices(builderApiUrl) {
|
|
3206
|
+
const res = await fetch(`${builderApiUrl}/v1/prices`);
|
|
3207
|
+
if (res.status === 503) return {};
|
|
3208
|
+
if (!res.ok) {
|
|
3209
|
+
throw new Error(`Live prices endpoint returned ${res.status} ${res.statusText}`);
|
|
3210
|
+
}
|
|
3211
|
+
const json = await res.json();
|
|
3212
|
+
if (!Array.isArray(json.prices)) {
|
|
3213
|
+
throw new Error("Live prices endpoint returned an unexpected payload");
|
|
3214
|
+
}
|
|
3215
|
+
const result = {};
|
|
3216
|
+
for (const item of json.prices) {
|
|
3217
|
+
if (!item.from || !item.to) continue;
|
|
3218
|
+
result[`${item.from}/${item.to}`] = {
|
|
3219
|
+
mid: item.mid,
|
|
3220
|
+
bid: item.bid,
|
|
3221
|
+
ask: item.ask,
|
|
3222
|
+
isMarketOpen: item.isMarketOpen ?? true,
|
|
3223
|
+
isDayTradingClosed: item.isDayTradingClosed ?? false,
|
|
3224
|
+
secondsToToggleIsDayTradingClosed: item.secondsToToggleIsDayTradingClosed ?? -1
|
|
3225
|
+
};
|
|
3226
|
+
}
|
|
3227
|
+
return result;
|
|
3228
|
+
}
|
|
3229
|
+
function rawTickToPublic(item) {
|
|
3230
|
+
const from = normalizePairName(item.from);
|
|
3231
|
+
const to = normalizePairName(item.to);
|
|
3232
|
+
return {
|
|
3233
|
+
feedId: item.feed_id,
|
|
3234
|
+
pair: `${from}-${to}`,
|
|
3235
|
+
from,
|
|
3236
|
+
to,
|
|
3237
|
+
bid: item.bid,
|
|
3238
|
+
mid: item.mid,
|
|
3239
|
+
ask: item.ask,
|
|
3240
|
+
isMarketOpen: item.isMarketOpen ?? true,
|
|
3241
|
+
isDayTradingClosed: item.isDayTradingClosed ?? false,
|
|
3242
|
+
secondsToToggleIsDayTradingClosed: item.secondsToToggleIsDayTradingClosed ?? -1,
|
|
3243
|
+
timestampSeconds: item.timestampSeconds
|
|
3244
|
+
};
|
|
3245
|
+
}
|
|
3246
|
+
async function fetchCandles(builderApiUrl, rawPair, fromMs, toMs, resolution) {
|
|
3247
|
+
const res = await fetch(`${builderApiUrl}/v1/ohlc`, {
|
|
3248
|
+
method: "POST",
|
|
3249
|
+
headers: { "Content-Type": "application/json" },
|
|
3250
|
+
body: JSON.stringify({
|
|
3251
|
+
pair: rawPair,
|
|
3252
|
+
fromTimestampSeconds: Math.floor(fromMs / 1e3),
|
|
3253
|
+
toTimestampSeconds: Math.floor(toMs / 1e3),
|
|
3254
|
+
resolution
|
|
3255
|
+
})
|
|
3256
|
+
});
|
|
3257
|
+
if (!res.ok) {
|
|
3258
|
+
throw new Error(`OHLC endpoint returned ${res.status} ${res.statusText}`);
|
|
3259
|
+
}
|
|
3260
|
+
const json = await res.json();
|
|
3261
|
+
return json.data.map((c) => ({
|
|
3262
|
+
pairFrom: normalizePairName(c.from),
|
|
3263
|
+
pairTo: normalizePairName(c.to),
|
|
3264
|
+
time: c.time,
|
|
3265
|
+
open: c.open,
|
|
3266
|
+
high: c.high,
|
|
3267
|
+
low: c.low,
|
|
3268
|
+
close: c.close
|
|
3269
|
+
}));
|
|
3270
|
+
}
|
|
3271
|
+
var OstiumPriceStream = class _OstiumPriceStream {
|
|
3272
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3273
|
+
ws;
|
|
3274
|
+
// ws.WebSocket — typed as any to stay compatible with both Node and Bun
|
|
3275
|
+
tickHandlers = /* @__PURE__ */ new Set();
|
|
3276
|
+
snapshotHandlers = /* @__PURE__ */ new Set();
|
|
3277
|
+
/** pairId (string) → raw "FROM-TO" name for the WS API. */
|
|
3278
|
+
pairRawNameCache;
|
|
3279
|
+
constructor(ws, pairRawNameCache, initialSubscribeRawPairs) {
|
|
3280
|
+
this.ws = ws;
|
|
3281
|
+
this.pairRawNameCache = pairRawNameCache;
|
|
3282
|
+
if (initialSubscribeRawPairs?.length) {
|
|
3283
|
+
ws.once("open", () => {
|
|
3284
|
+
ws.send(JSON.stringify({ type: "subscribe", pairs: initialSubscribeRawPairs }));
|
|
3285
|
+
});
|
|
3286
|
+
}
|
|
3287
|
+
ws.on("message", (data) => {
|
|
3288
|
+
let msg;
|
|
3289
|
+
try {
|
|
3290
|
+
msg = JSON.parse(typeof data === "string" ? data : data.toString());
|
|
3291
|
+
} catch {
|
|
3292
|
+
return;
|
|
3293
|
+
}
|
|
3294
|
+
if (msg.type === "snapshot" && Array.isArray(msg.data)) {
|
|
3295
|
+
const ticks = msg.data.map(rawTickToPublic);
|
|
3296
|
+
for (const h of this.snapshotHandlers) h(ticks);
|
|
3297
|
+
} else if (msg.type === "tick" && msg.data) {
|
|
3298
|
+
const tick = rawTickToPublic(msg.data);
|
|
3299
|
+
for (const h of this.tickHandlers) h(tick);
|
|
3300
|
+
}
|
|
3301
|
+
});
|
|
3302
|
+
}
|
|
3303
|
+
/**
|
|
3304
|
+
* Open a WebSocket connection to the live price stream.
|
|
3305
|
+
*
|
|
3306
|
+
* Uses the `ws` package which correctly handles case-insensitive `Connection`
|
|
3307
|
+
* header values (required for Cloudflare-proxied endpoints).
|
|
3308
|
+
*
|
|
3309
|
+
* @param builderApiUrl - Base URL (e.g. `"https://builder.ostium.io"`). The `http`/`https`
|
|
3310
|
+
* scheme is automatically converted to `ws`/`wss`.
|
|
3311
|
+
* @param rawPairs - Optional raw `"FROM-TO"` names (from
|
|
3312
|
+
* `OstiumSubgraphClient.streamPrices` pairIds). Sent as
|
|
3313
|
+
* `{ type: "subscribe", pairs }` after the socket opens.
|
|
3314
|
+
* @param pairRawNameCache - pairId → raw name map; passed in by the subgraph client so
|
|
3315
|
+
* `subscribe` / `unsubscribe` can accept pairIds.
|
|
3316
|
+
*/
|
|
3317
|
+
static connect(builderApiUrl, rawPairs, pairRawNameCache = /* @__PURE__ */ new Map()) {
|
|
3318
|
+
const base = builderApiUrl.replace(/\/$/, "").replace(/^https?:\/\//, (match) => match === "https://" ? "wss://" : "ws://");
|
|
3319
|
+
const url = `${base}/v1/prices/stream`;
|
|
3320
|
+
const initial = rawPairs && rawPairs.length > 0 ? rawPairs : void 0;
|
|
3321
|
+
const ws = new WS__default.default(url, {
|
|
3322
|
+
headers: { "User-Agent": "ostium-sdk", "Accept": "*/*" }
|
|
3323
|
+
});
|
|
3324
|
+
return new _OstiumPriceStream(ws, pairRawNameCache, initial);
|
|
3325
|
+
}
|
|
3326
|
+
/**
|
|
3327
|
+
* Register a callback that fires on every incoming price tick.
|
|
3328
|
+
* Returns an unsubscribe function.
|
|
3329
|
+
*/
|
|
3330
|
+
onTick(handler) {
|
|
3331
|
+
this.tickHandlers.add(handler);
|
|
3332
|
+
return () => {
|
|
3333
|
+
this.tickHandlers.delete(handler);
|
|
3334
|
+
};
|
|
3335
|
+
}
|
|
3336
|
+
/**
|
|
3337
|
+
* Register a callback that fires once with the full snapshot sent on connect.
|
|
3338
|
+
* Returns an unsubscribe function.
|
|
3339
|
+
*/
|
|
3340
|
+
onSnapshot(handler) {
|
|
3341
|
+
this.snapshotHandlers.add(handler);
|
|
3342
|
+
return () => {
|
|
3343
|
+
this.snapshotHandlers.delete(handler);
|
|
3344
|
+
};
|
|
3345
|
+
}
|
|
3346
|
+
/** Register a one-time callback that fires when the connection is established. */
|
|
3347
|
+
onOpen(handler) {
|
|
3348
|
+
this.ws.once("open", handler);
|
|
3349
|
+
return this;
|
|
3350
|
+
}
|
|
3351
|
+
/** Register an error callback. */
|
|
3352
|
+
onError(handler) {
|
|
3353
|
+
this.ws.on("error", handler);
|
|
3354
|
+
return this;
|
|
3355
|
+
}
|
|
3356
|
+
/** Register a callback that fires when the connection closes. */
|
|
3357
|
+
onClose(handler) {
|
|
3358
|
+
this.ws.on("close", handler);
|
|
3359
|
+
return this;
|
|
3360
|
+
}
|
|
3361
|
+
/**
|
|
3362
|
+
* Add pairs to the active filter. Accepts the same `pairId` values used
|
|
3363
|
+
* throughout the SDK (numeric string or number).
|
|
3364
|
+
* Sends `{ type: "subscribe", pairs: [...rawNames] }` to the server.
|
|
3365
|
+
*/
|
|
3366
|
+
subscribe(pairIds) {
|
|
3367
|
+
this.send({ type: "subscribe", pairs: this.resolveRawNames(pairIds) });
|
|
3368
|
+
return this;
|
|
3369
|
+
}
|
|
3370
|
+
/**
|
|
3371
|
+
* Remove pairs from the active filter. Accepts the same `pairId` values used
|
|
3372
|
+
* throughout the SDK.
|
|
3373
|
+
* Sends `{ type: "unsubscribe", pairs: [...rawNames] }` to the server.
|
|
3374
|
+
*/
|
|
3375
|
+
unsubscribe(pairIds) {
|
|
3376
|
+
this.send({ type: "unsubscribe", pairs: this.resolveRawNames(pairIds) });
|
|
3377
|
+
return this;
|
|
3378
|
+
}
|
|
3379
|
+
/** Close the WebSocket connection. */
|
|
3380
|
+
close() {
|
|
3381
|
+
this.ws.close();
|
|
3382
|
+
}
|
|
3383
|
+
/** Underlying WebSocket ready state (`0=CONNECTING`, `1=OPEN`, `2=CLOSING`, `3=CLOSED`). */
|
|
3384
|
+
get readyState() {
|
|
3385
|
+
return this.ws.readyState;
|
|
3386
|
+
}
|
|
3387
|
+
resolveRawNames(pairIds) {
|
|
3388
|
+
return pairIds.map((id) => {
|
|
3389
|
+
const cached = this.pairRawNameCache.get(String(id));
|
|
3390
|
+
return cached ?? String(id);
|
|
3391
|
+
});
|
|
3392
|
+
}
|
|
3393
|
+
send(msg) {
|
|
3394
|
+
if (this.ws.readyState === WS__default.default.OPEN) {
|
|
3395
|
+
this.ws.send(JSON.stringify(msg));
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
};
|
|
3399
|
+
|
|
3400
|
+
// src/data/internal/formatters.ts
|
|
3401
|
+
var MIN_NOTIONAL = "5.0";
|
|
3402
|
+
var MIN_NOTIONAL_NUM = 5;
|
|
3403
|
+
var BLOCKS_PER_SECOND_ARBITRUM = 4;
|
|
3404
|
+
var ROLLOVER_RATE_HOURS = 8;
|
|
3405
|
+
function lowerHex(value) {
|
|
3406
|
+
if (!value) return null;
|
|
3407
|
+
const lower = value.toLowerCase();
|
|
3408
|
+
return lower.startsWith("0x") ? lower : `0x${lower}`;
|
|
3409
|
+
}
|
|
3410
|
+
function pairMaxLeverage(pair) {
|
|
3411
|
+
const maxLevString = !pair.maxLeverage || pair.maxLeverage === "0" ? pair.group.maxLeverage ?? "0" : pair.maxLeverage;
|
|
3412
|
+
return formatLeverage(maxLevString);
|
|
3413
|
+
}
|
|
3414
|
+
function maxWithdrawForPosition(collateral, currentLeverage, maxLeverage) {
|
|
3415
|
+
if (collateral <= 0) return 0;
|
|
3416
|
+
if (currentLeverage <= 0) return 0;
|
|
3417
|
+
if (maxLeverage <= 0) return 0;
|
|
3418
|
+
if (currentLeverage >= maxLeverage) return 0;
|
|
3419
|
+
const max = collateral * (1 - currentLeverage / maxLeverage);
|
|
3420
|
+
if (max < 0) return 0;
|
|
3421
|
+
if (max > collateral) return collateral;
|
|
3422
|
+
return max;
|
|
3423
|
+
}
|
|
3424
|
+
function formatPair(raw, price) {
|
|
3425
|
+
const lastPrice = formatTokens(raw.lastTradePrice);
|
|
3426
|
+
const mid = price?.mid;
|
|
3427
|
+
const refPrice = mid && mid > 0 ? mid : lastPrice;
|
|
3428
|
+
const buyOI_usd = formatTokens(raw.longOI) * lastPrice;
|
|
3429
|
+
const sellOI_usd = formatTokens(raw.shortOI) * lastPrice;
|
|
3430
|
+
const maxOI_usd = formatUsdc(raw.maxOI);
|
|
3431
|
+
const minSz = refPrice > 0 ? MIN_NOTIONAL_NUM / refPrice : 0;
|
|
3432
|
+
const maxBSz = refPrice > 0 ? Math.max(0, (maxOI_usd - buyOI_usd) / refPrice) : 0;
|
|
3433
|
+
const maxSSz = refPrice > 0 ? Math.max(0, (maxOI_usd - sellOI_usd) / refPrice) : 0;
|
|
3434
|
+
const rolloverRate = getRolloverRateDisplay(raw);
|
|
3435
|
+
return {
|
|
3436
|
+
pairId: String(raw.id),
|
|
3437
|
+
pairTo: normalizePairName(raw.to),
|
|
3438
|
+
pairFrom: normalizePairName(raw.from),
|
|
3439
|
+
minSz: minSz.toString(),
|
|
3440
|
+
maxBSz: maxBSz.toString(),
|
|
3441
|
+
maxSSz: maxSSz.toString(),
|
|
3442
|
+
minNtl: MIN_NOTIONAL,
|
|
3443
|
+
maxLeverage: pairMaxLeverage(raw),
|
|
3444
|
+
overnightMaxLeverage: raw.overnightMaxLeverage ? formatLeverage(raw.overnightMaxLeverage) : 0,
|
|
3445
|
+
rolloverFeePerBlock: formatTokens(raw.lastRolloverLongPure ?? "0").toString(),
|
|
3446
|
+
openInterest: (buyOI_usd + sellOI_usd).toString(),
|
|
3447
|
+
buyOpenInterest: buyOI_usd.toString(),
|
|
3448
|
+
sellOpenInterest: sellOI_usd.toString(),
|
|
3449
|
+
maxOpenInterest: maxOI_usd.toString(),
|
|
3450
|
+
category: raw.group.name,
|
|
3451
|
+
rolloverRate,
|
|
3452
|
+
midPx: price ? price.mid.toString() : "0",
|
|
3453
|
+
askPx: price ? price.ask.toString() : "0",
|
|
3454
|
+
bidPx: price ? price.bid.toString() : "0",
|
|
3455
|
+
isMarketOpen: price?.isMarketOpen ?? true,
|
|
3456
|
+
isDayTradingClosed: price?.isDayTradingClosed ?? false,
|
|
3457
|
+
secondsToToggleIsDayTradingClosed: price?.secondsToToggleIsDayTradingClosed ?? -1
|
|
3458
|
+
};
|
|
3459
|
+
}
|
|
3460
|
+
function getRolloverRateDisplay(raw) {
|
|
3461
|
+
const longPure = formatTokens(raw.lastRolloverLongPure ?? "0");
|
|
3462
|
+
const premium = formatTokens(raw.brokerPremium ?? "0");
|
|
3463
|
+
let contractLong = longPure + premium;
|
|
3464
|
+
let contractShort = -longPure + premium;
|
|
3465
|
+
if (!raw.isNegativeRolloverAllowed) {
|
|
3466
|
+
contractLong = Math.max(contractLong, 0);
|
|
3467
|
+
contractShort = Math.max(contractShort, 0);
|
|
3468
|
+
}
|
|
3469
|
+
const displayPerBlockLong = -contractLong;
|
|
3470
|
+
const displayPerBlockShort = -contractShort;
|
|
3471
|
+
const blocksInPeriod = ROLLOVER_RATE_HOURS * 60 * 60 * BLOCKS_PER_SECOND_ARBITRUM;
|
|
3472
|
+
const periodMultiplier = blocksInPeriod * 100;
|
|
3473
|
+
return {
|
|
3474
|
+
long: (displayPerBlockLong * periodMultiplier).toString(),
|
|
3475
|
+
short: (displayPerBlockShort * periodMultiplier).toString()
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
function formatPosition(raw, price, pnl, maxLeverage) {
|
|
3479
|
+
const collateral = formatUsdc(raw.collateral);
|
|
3480
|
+
const tradeNotional = formatTokens(raw.tradeNotional);
|
|
3481
|
+
const leverage = formatLeverage(raw.leverage);
|
|
3482
|
+
const mid = price?.mid ?? formatTokens(raw.pair.lastTradePrice);
|
|
3483
|
+
const ntl = tradeNotional * mid;
|
|
3484
|
+
const roe = collateral > 0 ? pnl.netPnl / collateral : 0;
|
|
3485
|
+
return {
|
|
3486
|
+
position: {
|
|
3487
|
+
pairTo: normalizePairName(raw.pair.to),
|
|
3488
|
+
pairFrom: normalizePairName(raw.pair.from),
|
|
3489
|
+
pairId: String(raw.pair.id),
|
|
3490
|
+
pid: raw.tradeID,
|
|
3491
|
+
idx: parseInt(raw.index || "0") || 0,
|
|
3492
|
+
side: raw.isBuy ? "B" : "S",
|
|
3493
|
+
szi: tradeNotional.toString(),
|
|
3494
|
+
entryPx: formatTokens(raw.openPrice).toString(),
|
|
3495
|
+
leverage: leverage.toString(),
|
|
3496
|
+
ntl: ntl.toString(),
|
|
3497
|
+
unrealizedPnl: pnl.netPnl.toString(),
|
|
3498
|
+
returnOnEquity: roe.toString(),
|
|
3499
|
+
liquidationPx: pnl.liquidationPrice.toString(),
|
|
3500
|
+
collateralUsed: collateral.toString(),
|
|
3501
|
+
cumRollover: pnl.rollover.toString(),
|
|
3502
|
+
...raw.takeProfitPrice && raw.takeProfitPrice !== "0" ? { tpPx: formatTokens(raw.takeProfitPrice).toString() } : {},
|
|
3503
|
+
...raw.stopLossPrice && raw.stopLossPrice !== "0" ? { slPx: formatTokens(raw.stopLossPrice).toString() } : {},
|
|
3504
|
+
openTimestamp: parseInt(raw.timestamp || "0") * 1e3,
|
|
3505
|
+
isDayTrade: raw.isDayTrade ?? false,
|
|
3506
|
+
maxLeverage: maxLeverage.toString()
|
|
3507
|
+
},
|
|
3508
|
+
maxWithdrawable: maxWithdrawForPosition(collateral, leverage, maxLeverage).toString()
|
|
3509
|
+
};
|
|
3510
|
+
}
|
|
3511
|
+
function formatFill(raw) {
|
|
3512
|
+
const collateral = formatUsdc(raw.collateral);
|
|
3513
|
+
const tradeNotional = formatTokens(raw.tradeNotional);
|
|
3514
|
+
const priceImpactP = formatTokens(raw.priceImpactP);
|
|
3515
|
+
const devFee = formatUsdc(raw.devFee);
|
|
3516
|
+
const vaultFee = formatUsdc(raw.vaultFee);
|
|
3517
|
+
const oracleFee = formatUsdc(raw.oracleFee);
|
|
3518
|
+
const rolloverFee = formatUsdc(raw.rolloverFee);
|
|
3519
|
+
const liquidation = formatUsdc(raw.liquidationFee);
|
|
3520
|
+
const builderFee = formatUsdc(raw.builderFee);
|
|
3521
|
+
const totalProfitPercent = formatUsdc(raw.totalProfitPercent);
|
|
3522
|
+
const action = raw.orderAction;
|
|
3523
|
+
const type = raw.orderType;
|
|
3524
|
+
const isClose = action === "Close" || action === "StopLoss" || action === "TakeProfit" || action === "CloseDayTrade";
|
|
3525
|
+
const opening = action === "Open" ? (devFee + vaultFee).toString() : "0";
|
|
3526
|
+
const rollover = isClose ? rolloverFee.toString() : "0";
|
|
3527
|
+
const liquidationFee = action === "Liquidation" ? liquidation.toString() : "0";
|
|
3528
|
+
const closedPnl = isClose || action === "Liquidation" ? (collateral * totalProfitPercent / 100 - devFee - vaultFee - oracleFee).toString() : "0";
|
|
3529
|
+
const px = formatTokens(raw.priceAfterImpact);
|
|
3530
|
+
const szi = tradeNotional;
|
|
3531
|
+
const pxBeforeImpact = raw.isBuy ? px / (1 + priceImpactP / 100) : px / (1 - priceImpactP / 100);
|
|
3532
|
+
const priceImpactFee = Math.abs((px - pxBeforeImpact) * szi);
|
|
3533
|
+
return {
|
|
3534
|
+
pairTo: normalizePairName(raw.pair.to),
|
|
3535
|
+
pairFrom: normalizePairName(raw.pair.from),
|
|
3536
|
+
pairId: String(raw.pair.id),
|
|
3537
|
+
oid: raw.id,
|
|
3538
|
+
pid: raw.tradeID,
|
|
3539
|
+
side: raw.isBuy ? "B" : "S",
|
|
3540
|
+
action,
|
|
3541
|
+
type,
|
|
3542
|
+
px: px.toString(),
|
|
3543
|
+
szi: szi.toString(),
|
|
3544
|
+
collateralUsed: collateral.toString(),
|
|
3545
|
+
fees: {
|
|
3546
|
+
opening,
|
|
3547
|
+
rollover,
|
|
3548
|
+
liquidation: liquidationFee,
|
|
3549
|
+
builder: builderFee.toString(),
|
|
3550
|
+
priceImpact: priceImpactFee.toString()
|
|
3551
|
+
},
|
|
3552
|
+
closedPnl,
|
|
3553
|
+
hash: lowerHex(raw.executedTx) ?? "",
|
|
3554
|
+
time: parseInt(raw.executedAt || "0") * 1e3
|
|
3555
|
+
};
|
|
3556
|
+
}
|
|
3557
|
+
function formatOrder(raw) {
|
|
3558
|
+
return {
|
|
3559
|
+
...formatFill(raw),
|
|
3560
|
+
initiatedTx: lowerHex(raw.initiatedTx) ?? "",
|
|
3561
|
+
initiatedTime: parseInt(raw.initiatedAt || "0") * 1e3,
|
|
3562
|
+
isPending: raw.isPending,
|
|
3563
|
+
isCancelled: raw.isCancelled,
|
|
3564
|
+
...raw.cancelReason ? { cancelReason: raw.cancelReason } : {}
|
|
3565
|
+
};
|
|
3566
|
+
}
|
|
3567
|
+
function formatOpenOrder(raw) {
|
|
3568
|
+
const tp = raw.takeProfitPrice && raw.takeProfitPrice !== "0" ? formatTokens(raw.takeProfitPrice) : null;
|
|
3569
|
+
const sl = raw.stopLossPrice && raw.stopLossPrice !== "0" ? formatTokens(raw.stopLossPrice) : null;
|
|
3570
|
+
const orderType = raw.limitType === "STOP" ? "Stop" : "Limit";
|
|
3571
|
+
return {
|
|
3572
|
+
pairTo: normalizePairName(raw.pair.to),
|
|
3573
|
+
pairFrom: normalizePairName(raw.pair.from),
|
|
3574
|
+
pairId: String(raw.pair.id),
|
|
3575
|
+
idx: parseInt(raw.orderId || "0") || 0,
|
|
3576
|
+
side: raw.isBuy ? "B" : "S",
|
|
3577
|
+
limitPx: formatTokens(raw.openPrice).toString(),
|
|
3578
|
+
szi: formatTokens(raw.tradeNotional).toString(),
|
|
3579
|
+
...tp !== null ? { tpPx: tp.toString() } : {},
|
|
3580
|
+
...sl !== null ? { slPx: sl.toString() } : {},
|
|
3581
|
+
orderType,
|
|
3582
|
+
timestamp: parseInt(raw.initiatedAt || "0") * 1e3
|
|
3583
|
+
};
|
|
3584
|
+
}
|
|
3585
|
+
var EMPTY_RESULT = { long: [], short: [] };
|
|
3586
|
+
var ORDERBOOK_MAX_LEVELS = 20;
|
|
3587
|
+
function generateLogSpacedNotionals(levels, capacity) {
|
|
3588
|
+
const n = Math.min(ORDERBOOK_MAX_LEVELS, Math.max(1, Math.floor(levels)));
|
|
3589
|
+
if (capacity <= 0) return [];
|
|
3590
|
+
if (n === 1) return [capacity];
|
|
3591
|
+
const minNtl = Math.max(1, capacity / 3300);
|
|
3592
|
+
if (minNtl >= capacity) return [capacity];
|
|
3593
|
+
const logMin = Math.log(minNtl);
|
|
3594
|
+
const logMax = Math.log(capacity);
|
|
3595
|
+
return Array.from({ length: n }, (_, i) => {
|
|
3596
|
+
if (i === n - 1) return capacity;
|
|
3597
|
+
const t = i / (n - 1);
|
|
3598
|
+
return Math.exp(logMin + t * (logMax - logMin));
|
|
3599
|
+
});
|
|
3600
|
+
}
|
|
3601
|
+
function buildPairContext(raw, price) {
|
|
3602
|
+
const maxOIUsd = formatUsdc(raw.maxOI);
|
|
3603
|
+
const longOIUsd = formatTokens(raw.longOI) * price.mid;
|
|
3604
|
+
const shortOIUsd = formatTokens(raw.shortOI) * price.mid;
|
|
3605
|
+
return {
|
|
3606
|
+
remLong: Math.max(0, maxOIUsd - longOIUsd),
|
|
3607
|
+
remShort: Math.max(0, maxOIUsd - shortOIUsd),
|
|
3608
|
+
midBN: parseTokens(price.mid).toString(),
|
|
3609
|
+
askBN: parseTokens(price.ask).toString(),
|
|
3610
|
+
bidBN: parseTokens(price.bid).toString(),
|
|
3611
|
+
timeElapsed: timeElapsedSince(raw)
|
|
3612
|
+
};
|
|
3613
|
+
}
|
|
3614
|
+
function priceImpactForLevel(raw, ctx, ntl, isBuy) {
|
|
3615
|
+
return formulae.CalculateDynamicPriceImpact(
|
|
3616
|
+
ctx.midBN,
|
|
3617
|
+
ctx.askBN,
|
|
3618
|
+
ctx.bidBN,
|
|
3619
|
+
true,
|
|
3620
|
+
// isOpen
|
|
3621
|
+
isBuy,
|
|
3622
|
+
parseUsdc2(ntl).toString(),
|
|
3623
|
+
parseLeverage2(1).toString(),
|
|
3624
|
+
raw.buyVolume ?? "0",
|
|
3625
|
+
raw.sellVolume ?? "0",
|
|
3626
|
+
raw.decayRate ?? "0",
|
|
3627
|
+
ctx.timeElapsed,
|
|
3628
|
+
raw.netVolThreshold ?? "0",
|
|
3629
|
+
raw.priceImpactK ?? "0"
|
|
3630
|
+
);
|
|
3631
|
+
}
|
|
3632
|
+
function simulateSlippageForPair(raw, ntls, price) {
|
|
3633
|
+
if (!price || !price.mid) return EMPTY_RESULT;
|
|
3634
|
+
const ctx = buildPairContext(raw, price);
|
|
3635
|
+
const compute = (isBuy, capacity) => ntls.filter((ntl) => {
|
|
3636
|
+
const n = Number(ntl);
|
|
3637
|
+
return Number.isFinite(n) && n > 0 && n <= capacity;
|
|
3638
|
+
}).map((ntl) => {
|
|
3639
|
+
const result = priceImpactForLevel(raw, ctx, Number(ntl), isBuy);
|
|
3640
|
+
return { ntl, slippage: formatTokens(result.priceImpactP).toString() };
|
|
3641
|
+
});
|
|
3642
|
+
return {
|
|
3643
|
+
long: compute(true, ctx.remLong),
|
|
3644
|
+
short: compute(false, ctx.remShort)
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3647
|
+
function simulateOrderbookForPair(raw, price, levels) {
|
|
3648
|
+
const safeLevels = Math.min(ORDERBOOK_MAX_LEVELS, Math.max(1, Math.floor(levels)));
|
|
3649
|
+
const ctx = buildPairContext(raw, price);
|
|
3650
|
+
const askNtls = generateLogSpacedNotionals(safeLevels, ctx.remLong);
|
|
3651
|
+
const bidNtls = generateLogSpacedNotionals(safeLevels, ctx.remShort);
|
|
3652
|
+
const toLevel = (ntl, isBuy) => {
|
|
3653
|
+
const result = priceImpactForLevel(raw, ctx, ntl, isBuy);
|
|
3654
|
+
const px = formatTokens(result.priceAfterImpact);
|
|
3655
|
+
return {
|
|
3656
|
+
px: px.toString(),
|
|
3657
|
+
sz: (px > 0 ? ntl / px : 0).toString(),
|
|
3658
|
+
n: 1
|
|
3659
|
+
};
|
|
3660
|
+
};
|
|
3661
|
+
return {
|
|
3662
|
+
pairId: String(raw.id),
|
|
3663
|
+
pairFrom: normalizePairName(raw.from),
|
|
3664
|
+
pairTo: normalizePairName(raw.to),
|
|
3665
|
+
levels: [
|
|
3666
|
+
bidNtls.map((n) => toLevel(n, false)),
|
|
3667
|
+
// bids: short entries, best (closest to mid) first
|
|
3668
|
+
askNtls.map((n) => toLevel(n, true))
|
|
3669
|
+
// asks: long entries, best (closest to mid) first
|
|
3670
|
+
],
|
|
3671
|
+
time: Date.now()
|
|
3672
|
+
};
|
|
3673
|
+
}
|
|
3674
|
+
|
|
3675
|
+
// src/data/client.ts
|
|
3676
|
+
var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
3677
|
+
gql;
|
|
3678
|
+
builderApiUrl;
|
|
3679
|
+
/** Cached pair-name (`${from}/${to}`) → `pairId` map. Refreshed lazily. */
|
|
3680
|
+
pairIdCache = null;
|
|
3681
|
+
/** Cached `pairId` → `"FROM-TO"` in raw subgraph dash format (used for OHLC API). */
|
|
3682
|
+
pairRawNameCache = null;
|
|
3683
|
+
constructor(gql, builderApiUrl) {
|
|
3684
|
+
this.gql = gql;
|
|
3685
|
+
this.builderApiUrl = builderApiUrl;
|
|
3686
|
+
}
|
|
3687
|
+
/**
|
|
3688
|
+
* Create a client connected to the Ostium subgraph.
|
|
3689
|
+
*
|
|
3690
|
+
* Fetches the full pair list at startup so that `streamPrices`, `getCandles`,
|
|
3691
|
+
* and `getAllPrices` can resolve pair ids to raw names without an extra round-trip.
|
|
3692
|
+
*
|
|
3693
|
+
* ```ts
|
|
3694
|
+
* // Mainnet (default)
|
|
3695
|
+
* const client = await OstiumSubgraphClient.create();
|
|
3696
|
+
*
|
|
3697
|
+
* // Testnet (Arbitrum Sepolia)
|
|
3698
|
+
* const client = await OstiumSubgraphClient.create({ testnet: true });
|
|
3699
|
+
* ```
|
|
3700
|
+
*/
|
|
3701
|
+
static async create(config = {}) {
|
|
3702
|
+
const endpoint = config.endpoint ?? (config.testnet ? DEFAULT_SUBGRAPH_ENDPOINT_TESTNET : DEFAULT_SUBGRAPH_ENDPOINT);
|
|
3703
|
+
if (!endpoint.startsWith("http")) {
|
|
3704
|
+
throw new OstiumSubgraphError(
|
|
3705
|
+
`Invalid subgraph endpoint: ${endpoint}`,
|
|
3706
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
3707
|
+
);
|
|
3708
|
+
}
|
|
3709
|
+
const client = new _OstiumSubgraphClient(
|
|
3710
|
+
new graphqlRequest.GraphQLClient(endpoint),
|
|
3711
|
+
config.builderApiUrl ?? DEFAULT_BUILDER_API_URL
|
|
3712
|
+
);
|
|
3713
|
+
await client.initPairCache();
|
|
3714
|
+
return client;
|
|
3715
|
+
}
|
|
3716
|
+
async initPairCache() {
|
|
3717
|
+
const rawPairs = await this.fetchRawPairs();
|
|
3718
|
+
this.refreshPairIdCache(rawPairs);
|
|
3719
|
+
}
|
|
3720
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3721
|
+
// Pairs
|
|
3722
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3723
|
+
/**
|
|
3724
|
+
* All trading pairs with computed `minSz`/`maxBSz`/`maxSSz`, live prices, and
|
|
3725
|
+
* market-status flags. Pass `pairIds` to restrict to a subset.
|
|
3726
|
+
*/
|
|
3727
|
+
async getPairs(params) {
|
|
3728
|
+
const [rawPairs, prices] = await Promise.all([
|
|
3729
|
+
this.fetchRawPairs(),
|
|
3730
|
+
this.fetchLivePricesSafe()
|
|
3731
|
+
]);
|
|
3732
|
+
this.refreshPairIdCache(rawPairs);
|
|
3733
|
+
let filtered = rawPairs;
|
|
3734
|
+
if (params?.pairIds && params.pairIds.length > 0) {
|
|
3735
|
+
const requested = new Set(params.pairIds.map(String));
|
|
3736
|
+
filtered = rawPairs.filter((p) => requested.has(String(p.id)));
|
|
3737
|
+
}
|
|
3738
|
+
return { pairs: filtered.map((p) => formatPair(p, prices[`${p.from}/${p.to}`])) };
|
|
3739
|
+
}
|
|
3740
|
+
/** Live mid/bid/ask prices keyed by `pairId`. */
|
|
3741
|
+
async getAllPrices() {
|
|
3742
|
+
const [pairIdMap, prices] = await Promise.all([
|
|
3743
|
+
this.ensurePairIdCache(),
|
|
3744
|
+
this.fetchLivePricesSafe()
|
|
3745
|
+
]);
|
|
3746
|
+
const out = {};
|
|
3747
|
+
for (const [name, price] of Object.entries(prices)) {
|
|
3748
|
+
const id = pairIdMap.get(name);
|
|
3749
|
+
if (!id) continue;
|
|
3750
|
+
out[id] = {
|
|
3751
|
+
ask: price.ask.toString(),
|
|
3752
|
+
bid: price.bid.toString(),
|
|
3753
|
+
mid: price.mid.toString()
|
|
3754
|
+
};
|
|
3755
|
+
}
|
|
3756
|
+
return { prices: out };
|
|
3757
|
+
}
|
|
3758
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3759
|
+
// Open positions
|
|
3760
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3761
|
+
/**
|
|
3762
|
+
* Open positions for a trader, sorted newest first, with margin summary.
|
|
3763
|
+
*
|
|
3764
|
+
* - Fetches live prices automatically.
|
|
3765
|
+
* - `blockNumber` is required for live PnL (rollover projection). When omitted,
|
|
3766
|
+
* PnL fields are zeroed.
|
|
3767
|
+
*/
|
|
3768
|
+
async getOpenPositions(params) {
|
|
3769
|
+
const { user, blockNumber } = params;
|
|
3770
|
+
const [trades, prices] = await Promise.all([
|
|
3771
|
+
paginateAll(async (skip, first) => {
|
|
3772
|
+
const data = await this.query(
|
|
3773
|
+
GetTraderOpenTradesQuery,
|
|
3774
|
+
{ trader: user.toLowerCase(), skip, first }
|
|
3775
|
+
);
|
|
3776
|
+
return data.trades;
|
|
3777
|
+
}),
|
|
3778
|
+
this.fetchLivePricesSafe()
|
|
3779
|
+
]);
|
|
3780
|
+
let totalWithdrawable = 0;
|
|
3781
|
+
const pairPositions = trades.map((trade) => {
|
|
3782
|
+
const price = prices[`${trade.pair.from}/${trade.pair.to}`];
|
|
3783
|
+
const pnl = price && blockNumber ? getTradePnL(trade, price, blockNumber) : EMPTY_PNL;
|
|
3784
|
+
const maxLev = trade.isDayTrade ? formatLeverage(trade.pair.overnightMaxLeverage) : pairMaxLeverage(trade.pair);
|
|
3785
|
+
const pairPos = formatPosition(trade, price, pnl, maxLev);
|
|
3786
|
+
totalWithdrawable += parseFloat(pairPos.maxWithdrawable);
|
|
3787
|
+
return pairPos;
|
|
3788
|
+
});
|
|
3789
|
+
const marginSummary = aggregateMarginSummary(pairPositions);
|
|
3790
|
+
return {
|
|
3791
|
+
pairPositions,
|
|
3792
|
+
marginSummary,
|
|
3793
|
+
withdrawable: totalWithdrawable.toString(),
|
|
3794
|
+
time: Date.now()
|
|
3795
|
+
};
|
|
3796
|
+
}
|
|
3797
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3798
|
+
// Fills
|
|
3799
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3800
|
+
/**
|
|
3801
|
+
* Executed fills, sorted by `executedAt desc`.
|
|
3802
|
+
*
|
|
3803
|
+
* - `user`: pass an address to scope to a single trader, or `'ALL'` to fetch
|
|
3804
|
+
* fills across every trader (no trader filter).
|
|
3805
|
+
* - `pairId`: optional pair filter — when set, only fills on this pair are returned.
|
|
3806
|
+
*/
|
|
3807
|
+
async getFills(params) {
|
|
3808
|
+
return this.fetchFills(params, void 0);
|
|
3809
|
+
}
|
|
3810
|
+
/** Executed fills within an inclusive time range (Unix ms). Same filters as `getFills`. */
|
|
3811
|
+
async getFillsByTime(params) {
|
|
3812
|
+
const endTime = params.endTime ?? Date.now();
|
|
3813
|
+
return this.fetchFills(params, {
|
|
3814
|
+
startSec: Math.floor(params.startTime / 1e3).toString(),
|
|
3815
|
+
endSec: Math.floor(endTime / 1e3).toString()
|
|
3816
|
+
});
|
|
3817
|
+
}
|
|
3818
|
+
async fetchFills(filters, timeRange) {
|
|
3819
|
+
const hasTrader = filters.user !== "ALL";
|
|
3820
|
+
const hasPairId = filters.pairId !== void 0;
|
|
3821
|
+
const limit = filters.limit ?? 1e3;
|
|
3822
|
+
const document = buildExecutedFillsQuery({
|
|
3823
|
+
hasTrader,
|
|
3824
|
+
hasPairId,
|
|
3825
|
+
hasTimeRange: timeRange !== void 0
|
|
3826
|
+
});
|
|
3827
|
+
const orders = await paginateAll(
|
|
3828
|
+
async (skip, first) => {
|
|
3829
|
+
const variables = { skip, first };
|
|
3830
|
+
if (hasTrader) variables.trader = filters.user.toLowerCase();
|
|
3831
|
+
if (hasPairId) variables.pairId = String(filters.pairId);
|
|
3832
|
+
if (timeRange) {
|
|
3833
|
+
variables.startSec = timeRange.startSec;
|
|
3834
|
+
variables.endSec = timeRange.endSec;
|
|
3835
|
+
}
|
|
3836
|
+
const data = await this.query(document, variables);
|
|
3837
|
+
return data.orders;
|
|
3838
|
+
},
|
|
3839
|
+
1e3,
|
|
3840
|
+
limit
|
|
3841
|
+
);
|
|
3842
|
+
return orders.map(formatFill);
|
|
3843
|
+
}
|
|
3844
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3845
|
+
// Open limit orders
|
|
3846
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3847
|
+
/** Active limit orders for a trader, sorted by `updatedAt desc`. */
|
|
3848
|
+
async getOpenOrders({ user }) {
|
|
3849
|
+
const limits = await paginateAll(async (skip, first) => {
|
|
3850
|
+
const data = await this.query(
|
|
3851
|
+
GetTraderActiveLimitsQuery,
|
|
3852
|
+
{ trader: user.toLowerCase(), skip, first }
|
|
3853
|
+
);
|
|
3854
|
+
return data.limits;
|
|
3855
|
+
});
|
|
3856
|
+
return limits.map(formatOpenOrder);
|
|
3857
|
+
}
|
|
3858
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3859
|
+
// Orders (any status)
|
|
3860
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3861
|
+
/**
|
|
3862
|
+
* Fetch orders at any status — pending, executed, or cancelled.
|
|
3863
|
+
*
|
|
3864
|
+
* - Pass `initiatedTxHashes` (e.g. `[result.txHash]` from `openTrade` / `closeTrade`)
|
|
3865
|
+
* to poll by the subgraph `initiatedTx` field.
|
|
3866
|
+
* - Pass `orderIds` to look up by on-chain order id (mutually exclusive with
|
|
3867
|
+
* `initiatedTxHashes`).
|
|
3868
|
+
* - Pass only `user` (or omit on `OstiumClient`) to get recent orders for
|
|
3869
|
+
* a trader regardless of status.
|
|
3870
|
+
*/
|
|
3871
|
+
async getOrders(params = {}) {
|
|
3872
|
+
const hasOrderIds = !!(params.orderIds && params.orderIds.length > 0);
|
|
3873
|
+
const hasInitiatedTxHashes = !!(params.initiatedTxHashes && params.initiatedTxHashes.length > 0);
|
|
3874
|
+
if (hasOrderIds && hasInitiatedTxHashes) {
|
|
3875
|
+
throw new OstiumSubgraphError(
|
|
3876
|
+
"getOrders: pass either orderIds or initiatedTxHashes, not both",
|
|
3877
|
+
"INVALID_PARAMS" /* INVALID_PARAMS */
|
|
3878
|
+
);
|
|
3879
|
+
}
|
|
3880
|
+
const hasTrader = !hasOrderIds && !hasInitiatedTxHashes && !!params.user;
|
|
3881
|
+
const limit = params.limit ?? 100;
|
|
3882
|
+
const document = buildGetOrdersQuery({
|
|
3883
|
+
hasOrderIds,
|
|
3884
|
+
hasInitiatedTxHashes,
|
|
3885
|
+
hasTrader
|
|
3886
|
+
});
|
|
3887
|
+
const orders = await paginateAll(
|
|
3888
|
+
async (skip, first) => {
|
|
3889
|
+
const variables = { skip, first };
|
|
3890
|
+
if (hasOrderIds) variables.ids = params.orderIds.map(String);
|
|
3891
|
+
if (hasInitiatedTxHashes) {
|
|
3892
|
+
variables.txHashes = params.initiatedTxHashes.map(
|
|
3893
|
+
(h) => typeof h === "string" ? h.toLowerCase() : h
|
|
3894
|
+
);
|
|
3895
|
+
}
|
|
3896
|
+
if (hasTrader) variables.trader = params.user.toLowerCase();
|
|
3897
|
+
const data = await this.query(document, variables);
|
|
3898
|
+
return data.orders;
|
|
3899
|
+
},
|
|
3900
|
+
100,
|
|
3901
|
+
limit
|
|
3902
|
+
);
|
|
3903
|
+
return orders.map(formatOrder);
|
|
3904
|
+
}
|
|
3905
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3906
|
+
// Simulated slippage
|
|
3907
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3908
|
+
/**
|
|
3909
|
+
* Simulate the dynamic-spread slippage a long and a short entry of each
|
|
3910
|
+
* notional would experience on the given pairs. Notionals exceeding the
|
|
3911
|
+
* side's remaining open-interest capacity (`maxOI − sideOI`) are dropped.
|
|
3912
|
+
*
|
|
3913
|
+
* Computed via `CalculateDynamicPriceImpact` from `@ostium/formulae` using
|
|
3914
|
+
* the pair's live OI / decay / threshold params + the current bid/mid/ask
|
|
3915
|
+
* from the live-prices feed (collateral = `ntl`, leverage = 1).
|
|
3916
|
+
*
|
|
3917
|
+
* ```ts
|
|
3918
|
+
* const sim = await client.getSimSlippage({
|
|
3919
|
+
* pairIds: ['0', '1'],
|
|
3920
|
+
* ntls: ['10000', '100000', '1000000'],
|
|
3921
|
+
* });
|
|
3922
|
+
* // → { '0': { long: [{ ntl, slippage }...], short: [...] }, '1': ... }
|
|
3923
|
+
* ```
|
|
3924
|
+
*/
|
|
3925
|
+
async getSimSlippage(params) {
|
|
3926
|
+
const [rawPairs, prices] = await Promise.all([
|
|
3927
|
+
this.fetchRawPairs(),
|
|
3928
|
+
this.fetchLivePricesSafe()
|
|
3929
|
+
]);
|
|
3930
|
+
this.refreshPairIdCache(rawPairs);
|
|
3931
|
+
const requested = new Set(params.pairIds.map(String));
|
|
3932
|
+
const out = {};
|
|
3933
|
+
for (const pair of rawPairs) {
|
|
3934
|
+
const id = String(pair.id);
|
|
3935
|
+
if (!requested.has(id)) continue;
|
|
3936
|
+
const price = prices[`${pair.from}/${pair.to}`];
|
|
3937
|
+
out[id] = simulateSlippageForPair(pair, params.ntls, price);
|
|
3938
|
+
}
|
|
3939
|
+
return out;
|
|
3940
|
+
}
|
|
3941
|
+
/**
|
|
3942
|
+
* Build a synthetic bid/ask orderbook for a single pair using the on-chain
|
|
3943
|
+
* dynamic-spread params. Each side gets up to `levels` (capped at 20)
|
|
3944
|
+
* exponentially-spaced notional levels (`[1, 2, 3, 5] × 10ⁿ`), stopping at
|
|
3945
|
+
* the side's remaining open-interest capacity.
|
|
3946
|
+
*
|
|
3947
|
+
* ```ts
|
|
3948
|
+
* const ob = await client.getSimOrderbook({ pairId: '0', levels: 20 });
|
|
3949
|
+
* // ob.asks: long-entry levels (you'd buy at `px`)
|
|
3950
|
+
* // ob.bids: short-entry levels (you'd sell at `px`)
|
|
3951
|
+
* ```
|
|
3952
|
+
*/
|
|
3953
|
+
async getSimOrderbook(params) {
|
|
3954
|
+
const targetId = String(params.pairId);
|
|
3955
|
+
const levels = params.levels ?? 20;
|
|
3956
|
+
const [rawPairs, prices] = await Promise.all([
|
|
3957
|
+
this.fetchRawPairs(),
|
|
3958
|
+
this.fetchLivePricesSafe()
|
|
3959
|
+
]);
|
|
3960
|
+
this.refreshPairIdCache(rawPairs);
|
|
3961
|
+
const pair = rawPairs.find((p) => String(p.id) === targetId);
|
|
3962
|
+
if (!pair) {
|
|
3963
|
+
throw new OstiumSubgraphError(
|
|
3964
|
+
`Pair ${targetId} not found in subgraph response`,
|
|
3965
|
+
"NOT_FOUND" /* NOT_FOUND */
|
|
3966
|
+
);
|
|
3967
|
+
}
|
|
3968
|
+
const price = prices[`${pair.from}/${pair.to}`];
|
|
3969
|
+
if (!price || !price.mid) {
|
|
3970
|
+
throw new OstiumSubgraphError(
|
|
3971
|
+
`No live price available for ${pair.from}/${pair.to}`,
|
|
3972
|
+
"NOT_FOUND" /* NOT_FOUND */
|
|
3973
|
+
);
|
|
3974
|
+
}
|
|
3975
|
+
return simulateOrderbookForPair(pair, price, levels);
|
|
3976
|
+
}
|
|
3977
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3978
|
+
// OHLC candles
|
|
3979
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3980
|
+
/**
|
|
3981
|
+
* Fetch OHLC candles for a pair.
|
|
3982
|
+
*
|
|
3983
|
+
* `pairId` maps to the raw subgraph pair name internally (e.g. pair `0` →
|
|
3984
|
+
* `"BTC-USD"`). `from` / `to` are Unix milliseconds; `resolution` is one of
|
|
3985
|
+
* `"1"`, `"5"`, `"15"`, `"60"`, `"240"`, `"1D"`.
|
|
3986
|
+
*
|
|
3987
|
+
* ```ts
|
|
3988
|
+
* const candles = await client.getCandles({
|
|
3989
|
+
* pairId: 0,
|
|
3990
|
+
* from: Date.now() - 7 * 86_400_000,
|
|
3991
|
+
* resolution: '1D',
|
|
3992
|
+
* });
|
|
3993
|
+
* ```
|
|
3994
|
+
*/
|
|
3995
|
+
async getCandles(params) {
|
|
3996
|
+
const rawPair = await this.ensurePairRawName(params.pairId);
|
|
3997
|
+
return fetchCandles(
|
|
3998
|
+
this.builderApiUrl,
|
|
3999
|
+
rawPair,
|
|
4000
|
+
params.from,
|
|
4001
|
+
params.to ?? Date.now(),
|
|
4002
|
+
params.resolution
|
|
4003
|
+
);
|
|
4004
|
+
}
|
|
4005
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4006
|
+
// Live price stream (WebSocket)
|
|
4007
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4008
|
+
/**
|
|
4009
|
+
* Open a WebSocket connection to the live price stream.
|
|
4010
|
+
*
|
|
4011
|
+
* Optionally filter to a subset of pairs by `pairId` — same values as returned by
|
|
4012
|
+
* `getPairs()`. When provided, the client sends `{ type: "subscribe", pairs }` on
|
|
4013
|
+
* `open` (no `?pairs=` query on the URL). Omit `pairIds` to receive the full feed.
|
|
4014
|
+
*
|
|
4015
|
+
* The returned `OstiumPriceStream` fires an initial `snapshot` event with all
|
|
4016
|
+
* cached ticks, then a `tick` event on every update. `PriceTick.from` / `.to`
|
|
4017
|
+
* are normalized display names (e.g. `"WTI"` not `"CL"`).
|
|
4018
|
+
*
|
|
4019
|
+
* ```ts
|
|
4020
|
+
* const stream = client.streamPrices([0, 1]); // BTC and ETH by pairId
|
|
4021
|
+
*
|
|
4022
|
+
* stream.onOpen(() => console.log('connected'));
|
|
4023
|
+
* stream.onSnapshot(ticks => console.log('snapshot', ticks.length));
|
|
4024
|
+
* stream.onTick(tick => console.log(tick.pair, tick.mid));
|
|
4025
|
+
*
|
|
4026
|
+
* // Dynamically add / remove by pairId:
|
|
4027
|
+
* stream.subscribe([2]);
|
|
4028
|
+
* stream.unsubscribe([0]);
|
|
4029
|
+
* stream.close();
|
|
4030
|
+
* ```
|
|
4031
|
+
*
|
|
4032
|
+
* Requires a runtime with native `WebSocket` (Node.js 18+, Bun, browsers). Bun’s
|
|
4033
|
+
* `WebSocket` may fail some `https://` upgrades (e.g. CloudFront); use Node if so.
|
|
4034
|
+
*/
|
|
4035
|
+
streamPrices(pairIds) {
|
|
4036
|
+
const cache = this.pairRawNameCache ?? /* @__PURE__ */ new Map();
|
|
4037
|
+
const rawNames = pairIds?.map((id) => {
|
|
4038
|
+
const name = cache.get(String(id));
|
|
4039
|
+
if (!name) throw new OstiumSubgraphError(`Pair ${id} not found`, "NOT_FOUND" /* NOT_FOUND */);
|
|
4040
|
+
return name;
|
|
4041
|
+
});
|
|
4042
|
+
return OstiumPriceStream.connect(this.builderApiUrl, rawNames, cache);
|
|
4043
|
+
}
|
|
4044
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4045
|
+
// Internal — fetchers, cache, query wrapper
|
|
4046
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4047
|
+
async fetchRawPairs() {
|
|
4048
|
+
const data = await this.query(GetPairsAndGroupsQuery);
|
|
4049
|
+
return data.pairs;
|
|
4050
|
+
}
|
|
4051
|
+
refreshPairIdCache(rawPairs) {
|
|
4052
|
+
this.pairIdCache = new Map(rawPairs.map((p) => [`${p.from}/${p.to}`, String(p.id)]));
|
|
4053
|
+
this.pairRawNameCache = new Map(rawPairs.map((p) => [String(p.id), `${p.from}-${p.to}`]));
|
|
4054
|
+
}
|
|
4055
|
+
async ensurePairIdCache() {
|
|
4056
|
+
if (this.pairIdCache) return this.pairIdCache;
|
|
4057
|
+
this.refreshPairIdCache(await this.fetchRawPairs());
|
|
4058
|
+
return this.pairIdCache;
|
|
4059
|
+
}
|
|
4060
|
+
async ensurePairRawName(pairId) {
|
|
4061
|
+
if (!this.pairRawNameCache) {
|
|
4062
|
+
this.refreshPairIdCache(await this.fetchRawPairs());
|
|
4063
|
+
}
|
|
4064
|
+
const name = this.pairRawNameCache.get(String(pairId));
|
|
4065
|
+
if (!name) {
|
|
4066
|
+
throw new OstiumSubgraphError(
|
|
4067
|
+
`Pair ${pairId} not found`,
|
|
4068
|
+
"NOT_FOUND" /* NOT_FOUND */
|
|
4069
|
+
);
|
|
4070
|
+
}
|
|
4071
|
+
return name;
|
|
4072
|
+
}
|
|
4073
|
+
/** Wrapped `fetchLivePrices` that swallows network errors and returns `{}`. */
|
|
4074
|
+
async fetchLivePricesSafe() {
|
|
4075
|
+
try {
|
|
4076
|
+
return await fetchLivePrices(this.builderApiUrl);
|
|
4077
|
+
} catch {
|
|
4078
|
+
return {};
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
async query(document, variables) {
|
|
4082
|
+
try {
|
|
4083
|
+
return await this.gql.request(document, variables);
|
|
4084
|
+
} catch (err) {
|
|
4085
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4086
|
+
const lower = msg.toLowerCase();
|
|
4087
|
+
if (lower.includes("network") || lower.includes("timeout") || lower.includes("fetch")) {
|
|
4088
|
+
throw new OstiumSubgraphError(
|
|
4089
|
+
`Subgraph network error: ${msg}`,
|
|
4090
|
+
"FETCH_FAILED" /* FETCH_FAILED */,
|
|
4091
|
+
err
|
|
4092
|
+
);
|
|
4093
|
+
}
|
|
4094
|
+
throw new OstiumSubgraphError(
|
|
4095
|
+
`Subgraph query failed: ${msg}`,
|
|
4096
|
+
"FETCH_FAILED" /* FETCH_FAILED */,
|
|
4097
|
+
err
|
|
4098
|
+
);
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
};
|
|
4102
|
+
function aggregateMarginSummary(pairPositions) {
|
|
4103
|
+
let accountValue = 0;
|
|
4104
|
+
let totalCollateralUsed = 0;
|
|
4105
|
+
let totalNtlPos = 0;
|
|
4106
|
+
let totalRawPnlUsd = 0;
|
|
4107
|
+
for (const { position } of pairPositions) {
|
|
4108
|
+
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
4109
|
+
const ntl = parseFloat(position.ntl) || 0;
|
|
4110
|
+
const pnl = parseFloat(position.unrealizedPnl) || 0;
|
|
4111
|
+
accountValue += collateral + pnl;
|
|
4112
|
+
totalCollateralUsed += collateral;
|
|
4113
|
+
totalNtlPos += ntl;
|
|
4114
|
+
totalRawPnlUsd += pnl;
|
|
4115
|
+
}
|
|
4116
|
+
return {
|
|
4117
|
+
accountValue: accountValue.toString(),
|
|
4118
|
+
totalCollateralUsed: totalCollateralUsed.toString(),
|
|
4119
|
+
totalNtlPos: totalNtlPos.toString(),
|
|
4120
|
+
totalRawPnlUsd: totalRawPnlUsd.toString()
|
|
4121
|
+
};
|
|
4122
|
+
}
|
|
4123
|
+
|
|
4124
|
+
// src/client.ts
|
|
4125
|
+
var HEX64_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
4126
|
+
var ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
4127
|
+
function validateConfig(config) {
|
|
4128
|
+
if (!HEX64_RE.test(config.privateKey)) {
|
|
4129
|
+
throw new OstiumError(
|
|
4130
|
+
"privateKey must be a 64-character hex string prefixed with 0x",
|
|
4131
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4132
|
+
);
|
|
4133
|
+
}
|
|
4134
|
+
if (config.traderAddress && !ADDRESS_RE.test(config.traderAddress)) {
|
|
4135
|
+
throw new OstiumError(
|
|
4136
|
+
"traderAddress must be a valid 42-character Ethereum address (0x + 40 hex chars)",
|
|
4137
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4138
|
+
);
|
|
4139
|
+
}
|
|
4140
|
+
if (config.slippageBps !== void 0 && (config.slippageBps < 0 || config.slippageBps > 500)) {
|
|
4141
|
+
throw new OstiumError(
|
|
4142
|
+
"slippageBps must be between 0 and 500 (0%\u20135%)",
|
|
4143
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4144
|
+
);
|
|
4145
|
+
}
|
|
4146
|
+
if (config.builder !== void 0 && (config.builder.feeBps < 0 || config.builder.feeBps > 50)) {
|
|
4147
|
+
throw new OstiumError(
|
|
4148
|
+
"builder.feeBps must be between 0 and 50 (max 0.5%)",
|
|
4149
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4150
|
+
);
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
var OstiumClient = class _OstiumClient {
|
|
4154
|
+
signer;
|
|
4155
|
+
submitter;
|
|
4156
|
+
contracts;
|
|
4157
|
+
publicClient;
|
|
4158
|
+
defaultSlippageBps;
|
|
4159
|
+
delegated;
|
|
4160
|
+
gasless;
|
|
4161
|
+
selfGasless;
|
|
4162
|
+
eoaSubmit;
|
|
4163
|
+
builderAddress;
|
|
4164
|
+
builderFeeBps;
|
|
4165
|
+
/** Internal subgraph client backing all read methods (`getPairs`, `getOpenPositions`, …). */
|
|
4166
|
+
subgraph;
|
|
4167
|
+
constructor(internals) {
|
|
4168
|
+
this.signer = internals.signer;
|
|
4169
|
+
this.submitter = internals.submitter;
|
|
4170
|
+
this.contracts = internals.contracts;
|
|
4171
|
+
this.publicClient = internals.publicClient;
|
|
4172
|
+
this.defaultSlippageBps = internals.defaultSlippageBps;
|
|
4173
|
+
this.delegated = internals.delegated;
|
|
4174
|
+
this.gasless = internals.gasless;
|
|
4175
|
+
this.selfGasless = internals.selfGasless;
|
|
4176
|
+
this.eoaSubmit = internals.eoaSubmit;
|
|
4177
|
+
this.builderAddress = internals.builderAddress;
|
|
4178
|
+
this.builderFeeBps = internals.builderFeeBps;
|
|
4179
|
+
this.subgraph = internals.subgraph;
|
|
4180
|
+
}
|
|
4181
|
+
/**
|
|
4182
|
+
* **Self + Self** — your EOA signs every transaction and pays gas directly.
|
|
4183
|
+
*
|
|
4184
|
+
* Use this when you want the simplest possible setup: one key, no smart accounts,
|
|
4185
|
+
* no delegation. Your EOA holds USDC and owns all positions.
|
|
4186
|
+
*
|
|
4187
|
+
* ```ts
|
|
4188
|
+
* const client = await OstiumClient.createSelfAndSelf({
|
|
4189
|
+
* traderPrivateKey: '0x...',
|
|
4190
|
+
* rpcUrl: 'https://arb-mainnet.g.alchemy.com/v2/...',
|
|
4191
|
+
* });
|
|
4192
|
+
* await client.openTrade({ ... });
|
|
4193
|
+
* ```
|
|
4194
|
+
*/
|
|
4195
|
+
static async createSelfAndSelf(params) {
|
|
4196
|
+
return _OstiumClient._fromConfig({
|
|
4197
|
+
privateKey: params.traderPrivateKey,
|
|
4198
|
+
rpcUrl: params.rpcUrl,
|
|
4199
|
+
testnet: params.testnet,
|
|
4200
|
+
slippageBps: params.slippageBps,
|
|
4201
|
+
builder: params.builder,
|
|
4202
|
+
subgraphUrl: params.subgraphUrl,
|
|
4203
|
+
builderApiUrl: params.builderApiUrl
|
|
4204
|
+
});
|
|
4205
|
+
}
|
|
4206
|
+
/**
|
|
4207
|
+
* **Self + Gasless** — your EOA owns everything; a Safe relays trades for free.
|
|
4208
|
+
*
|
|
4209
|
+
* Your EOA holds USDC and owns all positions. A Safe smart account is derived
|
|
4210
|
+
* deterministically from your private key and registered as your on-chain delegate.
|
|
4211
|
+
* After a one-time setup (approve USDC + `setupGaslessDelegation()`), all trades
|
|
4212
|
+
* are submitted as sponsored UserOperations — no ETH required.
|
|
4213
|
+
*
|
|
4214
|
+
* ```ts
|
|
4215
|
+
* const client = await OstiumClient.createSelfAndGasless({
|
|
4216
|
+
* traderPrivateKey: '0x...',
|
|
4217
|
+
* pimlicoUrl: 'https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161',
|
|
4218
|
+
* });
|
|
4219
|
+
* // One-time setup — EOA pays gas once for each:
|
|
4220
|
+
* await client.approveUsdc('max');
|
|
4221
|
+
* await client.setupGaslessDelegation();
|
|
4222
|
+
* // All subsequent trading is gasless:
|
|
4223
|
+
* await client.openTrade({ ... });
|
|
4224
|
+
* ```
|
|
4225
|
+
*/
|
|
4226
|
+
static async createSelfAndGasless(params) {
|
|
4227
|
+
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4228
|
+
return _OstiumClient._fromConfig({
|
|
4229
|
+
privateKey: params.traderPrivateKey,
|
|
4230
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4231
|
+
rpcUrl: params.rpcUrl,
|
|
4232
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4233
|
+
testnet: params.testnet,
|
|
4234
|
+
slippageBps: params.slippageBps,
|
|
4235
|
+
builder: params.builder,
|
|
4236
|
+
subgraphUrl: params.subgraphUrl,
|
|
4237
|
+
builderApiUrl: params.builderApiUrl
|
|
4238
|
+
});
|
|
4239
|
+
}
|
|
4240
|
+
/**
|
|
4241
|
+
* **Delegated + Self** — a delegate EOA signs and pays gas on behalf of a trader address.
|
|
4242
|
+
*
|
|
4243
|
+
* The trader retains custody of USDC and positions. The delegate only needs ETH
|
|
4244
|
+
* for gas. Every trade is wrapped in `delegatedAction(traderAddress, callData)`.
|
|
4245
|
+
* The trader must call `setDelegate(delegateAddress)` once before this client
|
|
4246
|
+
* can submit trades.
|
|
4247
|
+
*
|
|
4248
|
+
* ```ts
|
|
4249
|
+
* const client = await OstiumClient.createDelegatedAndSelf({
|
|
4250
|
+
* delegatePrivateKey: '0xDelegateKey',
|
|
4251
|
+
* traderAddress: '0xTraderAddress',
|
|
4252
|
+
* rpcUrl: 'https://arb-mainnet.g.alchemy.com/v2/...',
|
|
4253
|
+
* });
|
|
4254
|
+
* await client.openTrade({ ... });
|
|
4255
|
+
* ```
|
|
4256
|
+
*/
|
|
4257
|
+
static async createDelegatedAndSelf(params) {
|
|
4258
|
+
return _OstiumClient._fromConfig({
|
|
4259
|
+
privateKey: params.delegatePrivateKey,
|
|
4260
|
+
traderAddress: params.traderAddress,
|
|
4261
|
+
rpcUrl: params.rpcUrl,
|
|
4262
|
+
testnet: params.testnet,
|
|
4263
|
+
slippageBps: params.slippageBps,
|
|
4264
|
+
builder: params.builder,
|
|
4265
|
+
subgraphUrl: params.subgraphUrl,
|
|
4266
|
+
builderApiUrl: params.builderApiUrl
|
|
4267
|
+
});
|
|
4268
|
+
}
|
|
4269
|
+
/**
|
|
4270
|
+
* **Delegated + Gasless** — a Safe derived from the delegate key submits sponsored
|
|
4271
|
+
* UserOperations on behalf of the trader. No ETH needed after setup.
|
|
4272
|
+
*
|
|
4273
|
+
* The trader retains custody of USDC and positions. The delegate's Safe submits
|
|
4274
|
+
* trades as UserOperations via Pimlico. The trader must call `setDelegate(safeAddress)`
|
|
4275
|
+
* once — use `client.getSmartAccountAddress()` to retrieve the Safe address.
|
|
4276
|
+
*
|
|
4277
|
+
* ```ts
|
|
4278
|
+
* const client = await OstiumClient.createDelegatedAndGasless({
|
|
4279
|
+
* delegatePrivateKey: '0xDelegateKey',
|
|
4280
|
+
* traderAddress: '0xTraderAddress',
|
|
4281
|
+
* pimlicoUrl: 'https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161',
|
|
4282
|
+
* });
|
|
4283
|
+
* await client.openTrade({ ... }); // gasless delegatedAction UserOp
|
|
4284
|
+
* ```
|
|
4285
|
+
*/
|
|
4286
|
+
static async createDelegatedAndGasless(params) {
|
|
4287
|
+
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4288
|
+
return _OstiumClient._fromConfig({
|
|
4289
|
+
privateKey: params.delegatePrivateKey,
|
|
4290
|
+
traderAddress: params.traderAddress,
|
|
4291
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4292
|
+
rpcUrl: params.rpcUrl,
|
|
4293
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4294
|
+
testnet: params.testnet,
|
|
4295
|
+
slippageBps: params.slippageBps,
|
|
4296
|
+
builder: params.builder,
|
|
4297
|
+
subgraphUrl: params.subgraphUrl,
|
|
4298
|
+
builderApiUrl: params.builderApiUrl
|
|
4299
|
+
});
|
|
4300
|
+
}
|
|
4301
|
+
/**
|
|
4302
|
+
* **Read-only client** — no signer, no submitter, no privateKey required.
|
|
4303
|
+
*
|
|
4304
|
+
* All read methods (`getPairs`, `getOpenPositions`, `getFills`, `getBalances`, …)
|
|
4305
|
+
* are available. Methods that take an optional `user` argument require it to be
|
|
4306
|
+
* passed explicitly. Calling any write method throws `INVALID_CONFIG`.
|
|
4307
|
+
*
|
|
4308
|
+
* ```ts
|
|
4309
|
+
* const reader = await OstiumClient.createReadOnly();
|
|
4310
|
+
* const { pairs } = await reader.getPairs();
|
|
4311
|
+
* const balances = await reader.getBalances('0xTrader...');
|
|
4312
|
+
* const positions = await reader.getOpenPositions({ user: '0xTrader...' });
|
|
4313
|
+
* ```
|
|
4314
|
+
*/
|
|
4315
|
+
static async createReadOnly(params = {}) {
|
|
4316
|
+
const testnet = params.testnet ?? false;
|
|
4317
|
+
const networkConfig = getNetworkConfig(testnet);
|
|
4318
|
+
const chain = testnet ? chains.arbitrumSepolia : chains.arbitrum;
|
|
4319
|
+
const rpcUrl = params.rpcUrl ?? chain.rpcUrls.default.http[0];
|
|
4320
|
+
const contracts = {
|
|
4321
|
+
trading: networkConfig.contracts.trading,
|
|
4322
|
+
tradingStorage: networkConfig.contracts.tradingStorage,
|
|
4323
|
+
usdc: networkConfig.contracts.usdc
|
|
4324
|
+
};
|
|
4325
|
+
const publicClient = viem.createPublicClient({ chain, transport: viem.http(rpcUrl) });
|
|
4326
|
+
const subgraph = await OstiumSubgraphClient.create({
|
|
4327
|
+
testnet,
|
|
4328
|
+
endpoint: params.subgraphUrl,
|
|
4329
|
+
builderApiUrl: params.builderApiUrl
|
|
4330
|
+
});
|
|
4331
|
+
return new _OstiumClient({
|
|
4332
|
+
contracts,
|
|
4333
|
+
publicClient,
|
|
4334
|
+
defaultSlippageBps: Math.round(DEFAULT_SLIPPAGE_PERCENTAGE * 100),
|
|
4335
|
+
delegated: false,
|
|
4336
|
+
gasless: false,
|
|
4337
|
+
selfGasless: false,
|
|
4338
|
+
subgraph
|
|
4339
|
+
});
|
|
4340
|
+
}
|
|
4341
|
+
/** Internal factory shared by all public constructors. */
|
|
4342
|
+
static async _fromConfig(config) {
|
|
4343
|
+
validateConfig(config);
|
|
4344
|
+
const testnet = config.testnet ?? false;
|
|
4345
|
+
const networkConfig = getNetworkConfig(testnet);
|
|
4346
|
+
const chain = testnet ? chains.arbitrumSepolia : chains.arbitrum;
|
|
4347
|
+
const defaultSlippageBps = config.slippageBps ?? Math.round(DEFAULT_SLIPPAGE_PERCENTAGE * 100);
|
|
4348
|
+
const contracts = {
|
|
4349
|
+
trading: networkConfig.contracts.trading,
|
|
4350
|
+
tradingStorage: networkConfig.contracts.tradingStorage,
|
|
4351
|
+
usdc: networkConfig.contracts.usdc
|
|
4352
|
+
};
|
|
4353
|
+
const selfGasless = isGasless(config) && !isDelegated(config);
|
|
4354
|
+
const submitter = await createSubmitter(config, testnet);
|
|
4355
|
+
let traderAddress;
|
|
4356
|
+
if (isDelegated(config)) {
|
|
4357
|
+
traderAddress = config.traderAddress;
|
|
4358
|
+
} else {
|
|
4359
|
+
traderAddress = accounts.privateKeyToAccount(config.privateKey).address;
|
|
4360
|
+
}
|
|
4361
|
+
const signer = createSigner(config, traderAddress, selfGasless);
|
|
4362
|
+
const publicRpc = testnet ? "https://sepolia-rollup.arbitrum.io/rpc" : "https://arb1.arbitrum.io/rpc";
|
|
4363
|
+
const rpcUrl = config.rpcUrl ?? (isGasless(config) ? publicRpc : void 0);
|
|
4364
|
+
if (!isGasless(config) && !config.rpcUrl) {
|
|
4365
|
+
throw new OstiumError(
|
|
4366
|
+
"rpcUrl is required for self-submission mode",
|
|
4367
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4368
|
+
);
|
|
4369
|
+
}
|
|
4370
|
+
const publicClient = viem.createPublicClient({ chain, transport: viem.http(rpcUrl) });
|
|
4371
|
+
let eoaSubmit;
|
|
4372
|
+
if (selfGasless) {
|
|
4373
|
+
const eoaAccount = accounts.privateKeyToAccount(config.privateKey);
|
|
4374
|
+
const eoaRpcUrl = config.rpcUrl ?? publicRpc;
|
|
4375
|
+
const eoaWalletClient = viem.createWalletClient({ account: eoaAccount, chain, transport: viem.http(eoaRpcUrl) });
|
|
4376
|
+
eoaSubmit = async (encoded) => {
|
|
4377
|
+
const txHash = await eoaWalletClient.sendTransaction({
|
|
4378
|
+
account: eoaAccount,
|
|
4379
|
+
to: encoded.to,
|
|
4380
|
+
data: encoded.data,
|
|
4381
|
+
value: encoded.value
|
|
4382
|
+
});
|
|
4383
|
+
return { txHash };
|
|
4384
|
+
};
|
|
4385
|
+
}
|
|
4386
|
+
const subgraph = await OstiumSubgraphClient.create({
|
|
4387
|
+
testnet,
|
|
4388
|
+
endpoint: config.subgraphUrl,
|
|
4389
|
+
builderApiUrl: config.builderApiUrl
|
|
4390
|
+
});
|
|
4391
|
+
return new _OstiumClient({
|
|
4392
|
+
signer,
|
|
4393
|
+
submitter,
|
|
4394
|
+
contracts,
|
|
4395
|
+
publicClient,
|
|
4396
|
+
defaultSlippageBps,
|
|
4397
|
+
delegated: isDelegated(config),
|
|
4398
|
+
gasless: isGasless(config),
|
|
4399
|
+
selfGasless,
|
|
4400
|
+
eoaSubmit,
|
|
4401
|
+
builderAddress: config.builder?.address,
|
|
4402
|
+
builderFeeBps: config.builder?.feeBps,
|
|
4403
|
+
subgraph
|
|
4404
|
+
});
|
|
4405
|
+
}
|
|
4406
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4407
|
+
// Getters
|
|
4408
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4409
|
+
/**
|
|
4410
|
+
* The on-chain address that owns trades and USDC allowances.
|
|
4411
|
+
*
|
|
4412
|
+
* - Self + Self / Delegated modes: trader's EOA or configured traderAddress.
|
|
4413
|
+
* - Self + Gasless: EOA derived from privateKey (NOT the Safe — USDC lives here).
|
|
4414
|
+
*/
|
|
4415
|
+
getTraderAddress() {
|
|
4416
|
+
if (!this.signer) {
|
|
4417
|
+
throw new OstiumError(
|
|
4418
|
+
"No connected trader \u2014 pass `user` explicitly or use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
4419
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4420
|
+
);
|
|
4421
|
+
}
|
|
4422
|
+
return this.signer.traderAddress;
|
|
4423
|
+
}
|
|
4424
|
+
/** True when the client was created via `createReadOnly` and cannot submit transactions. */
|
|
4425
|
+
isReadOnly() {
|
|
4426
|
+
return !this.signer || !this.submitter;
|
|
4427
|
+
}
|
|
4428
|
+
/**
|
|
4429
|
+
* Returns the Safe smart-account address used for gasless submission.
|
|
4430
|
+
* Returns undefined when not in gasless mode.
|
|
4431
|
+
*
|
|
4432
|
+
* - Self + Gasless: Safe that submits on behalf of the EOA (the delegate).
|
|
4433
|
+
* Pass this address to setupGaslessDelegation().
|
|
4434
|
+
* - Delegated + Gasless: delegate's Safe address.
|
|
4435
|
+
* - Self + Self / Delegated + Self: returns undefined.
|
|
4436
|
+
*/
|
|
4437
|
+
getSmartAccountAddress() {
|
|
4438
|
+
return this.gasless && this.submitter ? this.submitter.effectiveSender : void 0;
|
|
4439
|
+
}
|
|
4440
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4441
|
+
// Self + Gasless setup
|
|
4442
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4443
|
+
/**
|
|
4444
|
+
* One-time setup for Self + Gasless mode.
|
|
4445
|
+
*
|
|
4446
|
+
* Registers the Safe smart account as a delegate on the trading contract so
|
|
4447
|
+
* the Safe can submit gasless trades on behalf of your EOA. The EOA pays gas
|
|
4448
|
+
* for this single transaction; all subsequent trading is gasless.
|
|
4449
|
+
*
|
|
4450
|
+
* Only callable in Self + Gasless mode. Throws INVALID_CONFIG otherwise.
|
|
4451
|
+
*
|
|
4452
|
+
* @example
|
|
4453
|
+
* ```ts
|
|
4454
|
+
* const client = await OstiumClient.create({ privateKey: '0x...', pimlicoUrl: 'https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161' });
|
|
4455
|
+
* await client.approveUsdc('max'); // EOA approves USDC (gas, one-time)
|
|
4456
|
+
* await client.setupGaslessDelegation(); // EOA sets Safe as delegate (gas, one-time)
|
|
4457
|
+
* // From here: all trading is gasless
|
|
4458
|
+
* await client.openTrade({ ... });
|
|
4459
|
+
* ```
|
|
4460
|
+
*/
|
|
4461
|
+
async setupGaslessDelegation() {
|
|
4462
|
+
if (!this.selfGasless || !this.eoaSubmit || !this.submitter) {
|
|
4463
|
+
throw new OstiumError(
|
|
4464
|
+
"setupGaslessDelegation() is only available in Self + Gasless mode. Create the client with pimlicoUrl but without traderAddress.",
|
|
4465
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4466
|
+
);
|
|
4467
|
+
}
|
|
4468
|
+
const safeAddress = this.submitter.effectiveSender;
|
|
4469
|
+
const encoded = encodeSetDelegate(safeAddress, this.contracts.trading);
|
|
4470
|
+
return this.submitDirectFromEoa(encoded);
|
|
4471
|
+
}
|
|
4472
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4473
|
+
// USDC helpers
|
|
4474
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4475
|
+
/**
|
|
4476
|
+
* Check whether the trader's USDC allowance covers the required amount.
|
|
4477
|
+
* The allowance is always checked against signer.traderAddress (the EOA in
|
|
4478
|
+
* Self + Gasless mode, the Safe in Delegated + Gasless).
|
|
4479
|
+
*/
|
|
4480
|
+
async checkUsdcAllowance(requiredUsd) {
|
|
4481
|
+
const required = parseUsdc(requiredUsd);
|
|
4482
|
+
const current = await this.publicClient.readContract({
|
|
4483
|
+
address: this.contracts.usdc,
|
|
4484
|
+
abi: ERC20_ABI,
|
|
4485
|
+
functionName: "allowance",
|
|
4486
|
+
args: [this.getTraderAddress(), this.contracts.tradingStorage]
|
|
4487
|
+
});
|
|
4488
|
+
return { current, required, sufficient: current >= required };
|
|
4489
|
+
}
|
|
4490
|
+
/**
|
|
4491
|
+
* USDC and ETH balances plus the USDC allowance to TradingStorage.
|
|
4492
|
+
* All three values are decimal strings so large values (e.g. max USDC approval)
|
|
4493
|
+
* are preserved exactly. Parse with `parseFloat` / `Number` only for display.
|
|
4494
|
+
*
|
|
4495
|
+
* In read-only mode, pass `user` explicitly. With a connected trader, omit it
|
|
4496
|
+
* to read the connected address.
|
|
4497
|
+
*/
|
|
4498
|
+
async getBalances(user) {
|
|
4499
|
+
const trader = user ?? this.getTraderAddress();
|
|
4500
|
+
const [usdcRaw, ethRaw, allowanceRaw] = await Promise.all([
|
|
4501
|
+
this.publicClient.readContract({
|
|
4502
|
+
address: this.contracts.usdc,
|
|
4503
|
+
abi: ERC20_ABI,
|
|
4504
|
+
functionName: "balanceOf",
|
|
4505
|
+
args: [trader]
|
|
4506
|
+
}),
|
|
4507
|
+
this.publicClient.getBalance({ address: trader }),
|
|
4508
|
+
this.publicClient.readContract({
|
|
4509
|
+
address: this.contracts.usdc,
|
|
4510
|
+
abi: ERC20_ABI,
|
|
4511
|
+
functionName: "allowance",
|
|
4512
|
+
args: [trader, this.contracts.tradingStorage]
|
|
4513
|
+
})
|
|
4514
|
+
]);
|
|
4515
|
+
return {
|
|
4516
|
+
usdc: viem.formatUnits(usdcRaw, 6),
|
|
4517
|
+
eth: viem.formatUnits(ethRaw, 18),
|
|
4518
|
+
allowance: viem.formatUnits(allowanceRaw, 6)
|
|
4519
|
+
};
|
|
4520
|
+
}
|
|
4521
|
+
/**
|
|
4522
|
+
* Approve USDC spending by the TradingStorage contract.
|
|
4523
|
+
*
|
|
4524
|
+
* - Self + Self: submitted gaslessly from EOA.
|
|
4525
|
+
* - Self + Gasless: submitted directly from EOA (the delegate cannot approve
|
|
4526
|
+
* on behalf of the trader). The EOA pays gas for this one-time operation.
|
|
4527
|
+
* - Delegated modes: throws — the trader must approve from their own account.
|
|
4528
|
+
*
|
|
4529
|
+
* @param amount USD amount as decimal string (e.g. "1000"), or "max" for MaxUint256.
|
|
4530
|
+
*/
|
|
4531
|
+
async approveUsdc(amount) {
|
|
4532
|
+
if (this.delegated) {
|
|
4533
|
+
throw new OstiumError(
|
|
4534
|
+
"approveUsdc is not available in delegated mode. The trader must approve USDC directly from their own account.",
|
|
4535
|
+
"DELEGATION_FAILED" /* DELEGATION_FAILED */
|
|
4536
|
+
);
|
|
4537
|
+
}
|
|
4538
|
+
const rawAmount = amount === "max" ? viem.maxUint256 : parseUsdc(amount);
|
|
4539
|
+
const encoded = encodeUsdcApprove(this.contracts.tradingStorage, rawAmount, this.contracts.usdc);
|
|
4540
|
+
if (this.selfGasless) {
|
|
4541
|
+
return this.submitDirectFromEoa(encoded);
|
|
4542
|
+
}
|
|
4543
|
+
return this.requireWriter().submitter.submit(encoded);
|
|
4544
|
+
}
|
|
4545
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4546
|
+
// Delegation helpers
|
|
4547
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4548
|
+
/**
|
|
4549
|
+
* Set a delegate on the trading contract.
|
|
4550
|
+
*
|
|
4551
|
+
* - Self + Self: EOA directly registers a delegate.
|
|
4552
|
+
* - Self + Gasless: NOT recommended — use setupGaslessDelegation() instead.
|
|
4553
|
+
* - Delegated modes: wrapped in delegatedAction so the existing delegate can
|
|
4554
|
+
* update the trader's delegation on their behalf.
|
|
4555
|
+
*/
|
|
4556
|
+
async setDelegate(delegateAddress) {
|
|
4557
|
+
const encoded = encodeSetDelegate(delegateAddress, this.contracts.trading);
|
|
4558
|
+
return this.execute(encoded);
|
|
4559
|
+
}
|
|
4560
|
+
/**
|
|
4561
|
+
* Remove the current delegate on the trading contract.
|
|
4562
|
+
* Follows the same delegation-wrapping rules as setDelegate().
|
|
4563
|
+
*/
|
|
4564
|
+
async removeDelegate() {
|
|
4565
|
+
const encoded = encodeRemoveDelegate(this.contracts.trading);
|
|
4566
|
+
return this.execute(encoded);
|
|
4567
|
+
}
|
|
4568
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4569
|
+
// Core trading methods
|
|
4570
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4571
|
+
/**
|
|
4572
|
+
* Open a new trade position.
|
|
4573
|
+
*
|
|
4574
|
+
* Call `approveUsdc()` (once) before the first trade — the contract will revert
|
|
4575
|
+
* on-chain if the USDC allowance is insufficient. Use `getBalances()` to check
|
|
4576
|
+
* the current allowance.
|
|
4577
|
+
*/
|
|
4578
|
+
async openTrade(params) {
|
|
4579
|
+
const collateralNum = parseFloat(params.collateral);
|
|
4580
|
+
if (collateralNum > MAX_COLLATERAL_USD) {
|
|
4581
|
+
throw new OstiumError(
|
|
4582
|
+
`Collateral ${params.collateral} exceeds maximum ${MAX_COLLATERAL_USD}`,
|
|
4583
|
+
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4584
|
+
);
|
|
4585
|
+
}
|
|
4586
|
+
const apiOpen = {
|
|
4587
|
+
a: params.pairIndex,
|
|
4588
|
+
b: params.buy,
|
|
4589
|
+
p: params.price,
|
|
4590
|
+
s: params.collateral,
|
|
4591
|
+
l: params.leverage,
|
|
4592
|
+
t: params.type,
|
|
4593
|
+
tp: params.takeProfit,
|
|
4594
|
+
sl: params.stopLoss,
|
|
4595
|
+
d: params.isDayTrade
|
|
4596
|
+
};
|
|
4597
|
+
const apiBuilder = this.builderAddress !== void 0 && this.builderFeeBps !== void 0 ? { b: this.builderAddress, f: this.builderFeeBps } : void 0;
|
|
4598
|
+
let trade, orderType;
|
|
4599
|
+
try {
|
|
4600
|
+
({ trade, orderType } = buildTrade(apiOpen, this.getTraderAddress()));
|
|
4601
|
+
} catch (err) {
|
|
4602
|
+
throw new OstiumError(
|
|
4603
|
+
`Trade validation failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
4604
|
+
"VALIDATION_FAILED" /* VALIDATION_FAILED */,
|
|
4605
|
+
err
|
|
4606
|
+
);
|
|
4607
|
+
}
|
|
4608
|
+
const builderFee = buildBuilderFee(apiBuilder);
|
|
4609
|
+
const slippageP = params.type === "market" /* Market */ ? BigInt(this.resolveSlippage(params.slippage)) : 0n;
|
|
4610
|
+
const encoded = encodeOpenTrade(trade, builderFee, orderType, slippageP, this.contracts.trading);
|
|
4611
|
+
return this.execute(encoded);
|
|
4612
|
+
}
|
|
4613
|
+
/**
|
|
4614
|
+
* Close an open position (full or partial).
|
|
4615
|
+
*
|
|
4616
|
+
* `pairId` and `idx` come straight from a `Position` returned by
|
|
4617
|
+
* `getOpenPositions` — pass them through directly:
|
|
4618
|
+
*
|
|
4619
|
+
* ```ts
|
|
4620
|
+
* const { pairPositions } = await client.getOpenPositions();
|
|
4621
|
+
* const { pairId, idx } = pairPositions[0].position;
|
|
4622
|
+
* await client.closeTrade({ pairId, idx, price: '...', closePercent: 100 });
|
|
4623
|
+
* ```
|
|
4624
|
+
*/
|
|
4625
|
+
async closeTrade(params) {
|
|
4626
|
+
try {
|
|
4627
|
+
validateCloseParams({
|
|
4628
|
+
a: Number(params.pairId),
|
|
4629
|
+
t: params.idx,
|
|
4630
|
+
p: params.price,
|
|
4631
|
+
r: params.closePercent
|
|
4632
|
+
});
|
|
4633
|
+
} catch (err) {
|
|
4634
|
+
throw new OstiumError(
|
|
4635
|
+
`Close validation failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
4636
|
+
"VALIDATION_FAILED" /* VALIDATION_FAILED */,
|
|
4637
|
+
err
|
|
4638
|
+
);
|
|
4639
|
+
}
|
|
4640
|
+
const pairIndex = BigInt(params.pairId);
|
|
4641
|
+
const index = BigInt(params.idx);
|
|
4642
|
+
const closePercentage = BigInt(Math.round(params.closePercent * 100));
|
|
4643
|
+
const marketPrice = parsePrice(params.price);
|
|
4644
|
+
const slippageP = BigInt(this.resolveSlippage(params.slippage));
|
|
4645
|
+
const encoded = encodeCloseTradeMarket(
|
|
4646
|
+
pairIndex,
|
|
4647
|
+
index,
|
|
4648
|
+
closePercentage,
|
|
4649
|
+
marketPrice,
|
|
4650
|
+
slippageP,
|
|
4651
|
+
this.contracts.trading
|
|
4652
|
+
);
|
|
4653
|
+
return this.execute(encoded);
|
|
4654
|
+
}
|
|
4655
|
+
/**
|
|
4656
|
+
* Cancel a pending order.
|
|
4657
|
+
*
|
|
4658
|
+
* TypeScript discriminates the params shape by `type` — required fields are
|
|
4659
|
+
* enforced at compile time.
|
|
4660
|
+
*
|
|
4661
|
+
* @example Cancel a pending limit order
|
|
4662
|
+
* ```ts
|
|
4663
|
+
* const [order] = await client.getOpenOrders();
|
|
4664
|
+
* await client.cancelOrder({ type: CancelOrderType.Limit, pairId: order.pairId, idx: order.idx });
|
|
4665
|
+
* ```
|
|
4666
|
+
* @example Cancel a timed-out market open
|
|
4667
|
+
* ```ts
|
|
4668
|
+
* await client.cancelOrder({ type: CancelOrderType.PendingOpen, orderId: 123 });
|
|
4669
|
+
* ```
|
|
4670
|
+
* @example Cancel a timed-out market close (retry: true re-submits the close)
|
|
4671
|
+
* ```ts
|
|
4672
|
+
* await client.cancelOrder({ type: CancelOrderType.PendingClose, orderId: 456, retry: true });
|
|
4673
|
+
* ```
|
|
4674
|
+
*/
|
|
4675
|
+
async cancelOrder(params) {
|
|
4676
|
+
let encoded;
|
|
4677
|
+
if (params.type === "limit" /* Limit */) {
|
|
4678
|
+
const pairIdNum = Number(params.pairId);
|
|
4679
|
+
const idxNum = Number(params.idx);
|
|
4680
|
+
if (!Number.isFinite(pairIdNum) || !Number.isInteger(pairIdNum) || pairIdNum < 0 || !Number.isFinite(idxNum) || !Number.isInteger(idxNum) || idxNum < 0) {
|
|
4681
|
+
throw new OstiumError(
|
|
4682
|
+
`Invalid pairId (${params.pairId}) or idx (${params.idx}) for limit cancel`,
|
|
4683
|
+
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4684
|
+
);
|
|
4685
|
+
}
|
|
4686
|
+
encoded = encodeCancelLimitOrder(
|
|
4687
|
+
BigInt(pairIdNum),
|
|
4688
|
+
BigInt(idxNum),
|
|
4689
|
+
this.contracts.trading
|
|
4690
|
+
);
|
|
4691
|
+
} else if (params.type === "pendingClose" /* PendingClose */) {
|
|
4692
|
+
if (params.orderId < 0) {
|
|
4693
|
+
throw new OstiumError(
|
|
4694
|
+
`Invalid orderId (${params.orderId}) for pending close cancel`,
|
|
4695
|
+
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4696
|
+
);
|
|
4697
|
+
}
|
|
4698
|
+
encoded = encodeCloseTradeMarketTimeout(
|
|
4699
|
+
BigInt(params.orderId),
|
|
4700
|
+
params.retry ?? false,
|
|
4701
|
+
this.contracts.trading
|
|
4702
|
+
);
|
|
4703
|
+
} else {
|
|
4704
|
+
if (params.orderId < 0) {
|
|
4705
|
+
throw new OstiumError(
|
|
4706
|
+
`Invalid orderId (${params.orderId}) for pending open cancel`,
|
|
4707
|
+
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4708
|
+
);
|
|
4709
|
+
}
|
|
4710
|
+
encoded = encodeOpenTradeMarketTimeout(BigInt(params.orderId), this.contracts.trading);
|
|
4711
|
+
}
|
|
4712
|
+
return this.execute(encoded);
|
|
4713
|
+
}
|
|
4714
|
+
/**
|
|
4715
|
+
* Modify an open trade or a pending limit order.
|
|
4716
|
+
*
|
|
4717
|
+
* `pairId` and `idx` come straight from `getOpenPositions` (for TP/SL on open
|
|
4718
|
+
* positions) or `getOpenOrders` (for limit-order edits):
|
|
4719
|
+
*
|
|
4720
|
+
* ```ts
|
|
4721
|
+
* // Update TP/SL on an open position
|
|
4722
|
+
* const { pairPositions } = await client.getOpenPositions();
|
|
4723
|
+
* const { pairId, idx } = pairPositions[0].position;
|
|
4724
|
+
* await client.modifyOrder({ pairId, idx, takeProfit: '...', stopLoss: '...' });
|
|
4725
|
+
*
|
|
4726
|
+
* // Re-price a pending limit order
|
|
4727
|
+
* const [order] = await client.getOpenOrders();
|
|
4728
|
+
* await client.modifyOrder({ pairId: order.pairId, idx: order.idx, price: '...' });
|
|
4729
|
+
* ```
|
|
4730
|
+
*
|
|
4731
|
+
* - `price` provided → updates limit-order trigger price (with optional TP/SL).
|
|
4732
|
+
* - `takeProfit` only → calls updateTp on an open trade.
|
|
4733
|
+
* - `stopLoss` only → calls updateSl on an open trade.
|
|
4734
|
+
* - Both `takeProfit` and `stopLoss` without `price` → throws (send two calls).
|
|
4735
|
+
*/
|
|
4736
|
+
async modifyOrder(params) {
|
|
4737
|
+
try {
|
|
4738
|
+
validateModifyParams({
|
|
4739
|
+
a: Number(params.pairId),
|
|
4740
|
+
i: params.idx,
|
|
4741
|
+
p: params.price,
|
|
4742
|
+
tp: params.takeProfit,
|
|
4743
|
+
sl: params.stopLoss
|
|
4744
|
+
});
|
|
4745
|
+
} catch (err) {
|
|
4746
|
+
throw new OstiumError(
|
|
4747
|
+
`Modify validation failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
4748
|
+
"VALIDATION_FAILED" /* VALIDATION_FAILED */,
|
|
4749
|
+
err
|
|
4750
|
+
);
|
|
4751
|
+
}
|
|
4752
|
+
if (params.takeProfit && params.stopLoss && !params.price) {
|
|
4753
|
+
throw new OstiumError(
|
|
4754
|
+
"Cannot update both takeProfit and stopLoss simultaneously without a price. Send two separate modifyOrder calls.",
|
|
4755
|
+
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4756
|
+
);
|
|
4757
|
+
}
|
|
4758
|
+
const pairIndex = BigInt(params.pairId);
|
|
4759
|
+
const index = BigInt(params.idx);
|
|
4760
|
+
let encoded;
|
|
4761
|
+
if (params.price) {
|
|
4762
|
+
const price = parsePrice(params.price);
|
|
4763
|
+
const tp = params.takeProfit ? parsePrice(params.takeProfit) : 0n;
|
|
4764
|
+
const sl = params.stopLoss ? parsePrice(params.stopLoss) : 0n;
|
|
4765
|
+
encoded = encodeUpdateOpenLimitOrder(pairIndex, index, price, tp, sl, this.contracts.trading);
|
|
4766
|
+
} else if (params.takeProfit) {
|
|
4767
|
+
encoded = encodeUpdateTp(pairIndex, index, parsePrice(params.takeProfit), this.contracts.trading);
|
|
4768
|
+
} else if (params.stopLoss) {
|
|
4769
|
+
encoded = encodeUpdateSl(pairIndex, index, parsePrice(params.stopLoss), this.contracts.trading);
|
|
4770
|
+
} else {
|
|
4771
|
+
throw new OstiumError(
|
|
4772
|
+
"Either takeProfit or stopLoss must be provided when price is not specified",
|
|
4773
|
+
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4774
|
+
);
|
|
4775
|
+
}
|
|
4776
|
+
return this.execute(encoded);
|
|
4777
|
+
}
|
|
4778
|
+
/**
|
|
4779
|
+
* Update collateral on an open position (isolated margin).
|
|
4780
|
+
*
|
|
4781
|
+
* `pairId` and `idx` come straight from a `Position` returned by `getOpenPositions`:
|
|
4782
|
+
*
|
|
4783
|
+
* ```ts
|
|
4784
|
+
* const { pairPositions } = await client.getOpenPositions();
|
|
4785
|
+
* const { pairId, idx } = pairPositions[0].position;
|
|
4786
|
+
* await client.updateCollateral({ pairId, idx, amount: '50' }); // top up
|
|
4787
|
+
* await client.updateCollateral({ pairId, idx, amount: '-25' }); // remove
|
|
4788
|
+
* ```
|
|
4789
|
+
*
|
|
4790
|
+
* Positive amount → topUpCollateral (adds margin, reduces effective leverage).
|
|
4791
|
+
* Negative amount → removeCollateral (removes margin, increases effective leverage).
|
|
4792
|
+
*
|
|
4793
|
+
* Checks USDC allowance before top-up operations.
|
|
4794
|
+
*/
|
|
4795
|
+
async updateCollateral(params) {
|
|
4796
|
+
const amount = parseFloat(params.amount);
|
|
4797
|
+
if (Number.isNaN(amount) || amount === 0) {
|
|
4798
|
+
throw new OstiumError(
|
|
4799
|
+
`Invalid collateral amount: ${params.amount}`,
|
|
4800
|
+
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4801
|
+
);
|
|
4802
|
+
}
|
|
4803
|
+
const pairIndex = BigInt(params.pairId);
|
|
4804
|
+
const index = BigInt(params.idx);
|
|
4805
|
+
const absAmount = parseUsdc(Math.abs(amount).toString());
|
|
4806
|
+
let encoded;
|
|
4807
|
+
if (amount > 0) {
|
|
4808
|
+
const allowance = await this.checkUsdcAllowance(Math.abs(amount).toString());
|
|
4809
|
+
if (!allowance.sufficient) {
|
|
4810
|
+
throw new OstiumError(
|
|
4811
|
+
`Insufficient USDC allowance for collateral top-up. Current: ${allowance.current}, Required: ${allowance.required}. Call approveUsdc() first.`,
|
|
4812
|
+
"ALLOWANCE_INSUFFICIENT" /* ALLOWANCE_INSUFFICIENT */
|
|
4813
|
+
);
|
|
4814
|
+
}
|
|
4815
|
+
encoded = encodeTopUpCollateral(pairIndex, index, absAmount, this.contracts.trading);
|
|
4816
|
+
} else {
|
|
4817
|
+
encoded = encodeRemoveCollateral(pairIndex, index, absAmount, this.contracts.trading);
|
|
4818
|
+
}
|
|
4819
|
+
return this.execute(encoded);
|
|
4820
|
+
}
|
|
4821
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4822
|
+
// Subgraph reads (Hyperliquid-style API)
|
|
4823
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4824
|
+
/**
|
|
4825
|
+
* All trading pairs with computed `minSz`/`maxBSz`/`maxSSz`, live prices, and
|
|
4826
|
+
* market-status flags. Pass `pairIds` to restrict to a subset.
|
|
4827
|
+
*/
|
|
4828
|
+
getPairs(params) {
|
|
4829
|
+
return this.subgraph.getPairs(params);
|
|
4830
|
+
}
|
|
4831
|
+
/** Live mid/bid/ask prices keyed by `pairId`. */
|
|
4832
|
+
getAllPrices() {
|
|
4833
|
+
return this.subgraph.getAllPrices();
|
|
4834
|
+
}
|
|
4835
|
+
/**
|
|
4836
|
+
* Open positions + margin summary for a user. Defaults to the connected
|
|
4837
|
+
* trader. Live prices and the current block number are fetched automatically.
|
|
4838
|
+
*/
|
|
4839
|
+
async getOpenPositions(params) {
|
|
4840
|
+
return this.subgraph.getOpenPositions({
|
|
4841
|
+
user: params?.user ?? this.getTraderAddress(),
|
|
4842
|
+
blockNumber: params?.blockNumber ?? await this.publicClient.getBlockNumber()
|
|
4843
|
+
});
|
|
4844
|
+
}
|
|
4845
|
+
/**
|
|
4846
|
+
* Executed fills, newest first.
|
|
4847
|
+
*
|
|
4848
|
+
* - `user` defaults to the connected trader. Pass `'ALL'` to fetch fills
|
|
4849
|
+
* across every trader (no trader filter).
|
|
4850
|
+
* - `pairId` (optional) — restricts to a single pair.
|
|
4851
|
+
* - `limit` (optional, default `1000`) — caps the result. Pass `Infinity` to
|
|
4852
|
+
* fetch every matching fill.
|
|
4853
|
+
*/
|
|
4854
|
+
getFills(params) {
|
|
4855
|
+
return this.subgraph.getFills({
|
|
4856
|
+
user: params?.user ?? this.getTraderAddress(),
|
|
4857
|
+
pairId: params?.pairId,
|
|
4858
|
+
limit: params?.limit
|
|
4859
|
+
});
|
|
4860
|
+
}
|
|
4861
|
+
/**
|
|
4862
|
+
* Executed fills within a time range (Unix ms). Same filters as `getFills`,
|
|
4863
|
+
* including the default `limit: 1000`.
|
|
4864
|
+
*/
|
|
4865
|
+
getFillsByTime(params) {
|
|
4866
|
+
return this.subgraph.getFillsByTime({
|
|
4867
|
+
user: params.user ?? this.getTraderAddress(),
|
|
4868
|
+
pairId: params.pairId,
|
|
4869
|
+
limit: params.limit,
|
|
4870
|
+
startTime: params.startTime,
|
|
4871
|
+
endTime: params.endTime
|
|
4872
|
+
});
|
|
4873
|
+
}
|
|
4874
|
+
/** Active limit orders for a user. Defaults to the connected trader. */
|
|
4875
|
+
getOpenOrders(params) {
|
|
4876
|
+
return this.subgraph.getOpenOrders({ user: params?.user ?? this.getTraderAddress() });
|
|
4877
|
+
}
|
|
4878
|
+
/**
|
|
4879
|
+
* Fetch orders at any status — pending, executed, or cancelled.
|
|
4880
|
+
*
|
|
4881
|
+
* Pass `initiatedTxHashes: [result.txHash]` to poll by the submission hash, or
|
|
4882
|
+
* `orderIds` by on-chain id. Without either, returns recent orders for the
|
|
4883
|
+
* connected trader.
|
|
4884
|
+
*/
|
|
4885
|
+
getOrders(params) {
|
|
4886
|
+
const byId = params?.orderIds?.length;
|
|
4887
|
+
const byTx = params?.initiatedTxHashes?.length;
|
|
4888
|
+
return this.subgraph.getOrders({
|
|
4889
|
+
...params,
|
|
4890
|
+
user: params?.user ?? (byId || byTx ? void 0 : this.getTraderAddress())
|
|
4891
|
+
});
|
|
4892
|
+
}
|
|
4893
|
+
/**
|
|
4894
|
+
* Simulate per-side slippage for a list of pairs and notionals.
|
|
4895
|
+
* See {@link OstiumSubgraphClient.getSimSlippage} for full semantics.
|
|
4896
|
+
*/
|
|
4897
|
+
getSimSlippage(params) {
|
|
4898
|
+
return this.subgraph.getSimSlippage(params);
|
|
4899
|
+
}
|
|
4900
|
+
/**
|
|
4901
|
+
* Build a synthetic bid/ask orderbook for one pair using exponentially-spaced
|
|
4902
|
+
* notional levels capped at each side's remaining OI capacity.
|
|
4903
|
+
* See {@link OstiumSubgraphClient.getSimOrderbook} for full semantics.
|
|
4904
|
+
*/
|
|
4905
|
+
getSimOrderbook(params) {
|
|
4906
|
+
return this.subgraph.getSimOrderbook(params);
|
|
4907
|
+
}
|
|
4908
|
+
/**
|
|
4909
|
+
* Fetch OHLC candles for a pair from the builder API.
|
|
4910
|
+
*
|
|
4911
|
+
* `from` / `to` are Unix milliseconds; `resolution` is one of
|
|
4912
|
+
* `"1"`, `"5"`, `"15"`, `"60"`, `"240"`, `"1D"`.
|
|
4913
|
+
*
|
|
4914
|
+
* ```ts
|
|
4915
|
+
* const candles = await client.getCandles({
|
|
4916
|
+
* pairId: 0,
|
|
4917
|
+
* from: Date.now() - 30 * 86_400_000,
|
|
4918
|
+
* resolution: '1D',
|
|
4919
|
+
* });
|
|
4920
|
+
* ```
|
|
4921
|
+
*/
|
|
4922
|
+
getCandles(params) {
|
|
4923
|
+
return this.subgraph.getCandles(params);
|
|
4924
|
+
}
|
|
4925
|
+
/**
|
|
4926
|
+
* Open a WebSocket connection to the live price stream.
|
|
4927
|
+
*
|
|
4928
|
+
* Optionally filter to a subset of pairs by `pairId` — same values as returned by
|
|
4929
|
+
* `getPairs()`. When provided, the client sends `{ type: "subscribe", pairs }` on
|
|
4930
|
+
* `open` (no `?pairs=` query on the URL). Omit `pairIds` to receive the full feed.
|
|
4931
|
+
*
|
|
4932
|
+
* ```ts
|
|
4933
|
+
* const stream = client.streamPrices([0, 1]); // BTC and ETH by pairId
|
|
4934
|
+
* stream.onSnapshot(ticks => console.log(ticks));
|
|
4935
|
+
* stream.onTick(tick => console.log(tick.pair, tick.mid));
|
|
4936
|
+
* stream.subscribe([2]); // add pair
|
|
4937
|
+
* stream.unsubscribe([0]); // remove pair
|
|
4938
|
+
* stream.close();
|
|
4939
|
+
* ```
|
|
4940
|
+
*
|
|
4941
|
+
* Requires Node.js 18+, Bun, or a browser environment. Bun’s `WebSocket` may fail
|
|
4942
|
+
* some `https://` upgrades; use Node if the connection never reaches `open`.
|
|
4943
|
+
*/
|
|
4944
|
+
streamPrices(pairIds) {
|
|
4945
|
+
return this.subgraph.streamPrices(pairIds);
|
|
4946
|
+
}
|
|
4947
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4948
|
+
// Internal helpers
|
|
4949
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4950
|
+
/**
|
|
4951
|
+
* Core execution pipeline:
|
|
4952
|
+
* 1. signer.prepare() — optionally wraps in delegatedAction.
|
|
4953
|
+
* 2. submitter.submit() — sends via EOA walletClient or Pimlico UserOp.
|
|
4954
|
+
*/
|
|
4955
|
+
async execute(encoded) {
|
|
4956
|
+
const { signer, submitter } = this.requireWriter();
|
|
4957
|
+
const prepared = signer.prepare(encoded);
|
|
4958
|
+
return submitter.submit(prepared);
|
|
4959
|
+
}
|
|
4960
|
+
/** Throws if the client is read-only; otherwise returns the signer + submitter. */
|
|
4961
|
+
requireWriter() {
|
|
4962
|
+
if (!this.signer || !this.submitter) {
|
|
4963
|
+
throw new OstiumError(
|
|
4964
|
+
"This client is read-only. Use one of the createSelfAnd*/createDelegatedAnd* factories to enable writes.",
|
|
4965
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4966
|
+
);
|
|
4967
|
+
}
|
|
4968
|
+
return { signer: this.signer, submitter: this.submitter };
|
|
4969
|
+
}
|
|
4970
|
+
/** Direct EOA submission — used only in Self + Gasless for approveUsdc and setupGaslessDelegation. */
|
|
4971
|
+
async submitDirectFromEoa(encoded) {
|
|
4972
|
+
try {
|
|
4973
|
+
return await this.eoaSubmit(encoded);
|
|
4974
|
+
} catch (err) {
|
|
4975
|
+
if (err instanceof OstiumError) throw err;
|
|
4976
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4977
|
+
const lower = msg.toLowerCase();
|
|
4978
|
+
if (lower.includes("revert")) {
|
|
4979
|
+
throw new OstiumError(`Contract reverted: ${msg}`, "CONTRACT_ERROR" /* CONTRACT_ERROR */, err);
|
|
4980
|
+
}
|
|
4981
|
+
if (lower.includes("network") || lower.includes("timeout") || lower.includes("fetch")) {
|
|
4982
|
+
throw new OstiumError(`Network error: ${msg}`, "NETWORK_ERROR" /* NETWORK_ERROR */, err);
|
|
4983
|
+
}
|
|
4984
|
+
throw new OstiumError(`Transaction failed: ${msg}`, "SUBMISSION_FAILED" /* SUBMISSION_FAILED */, err);
|
|
4985
|
+
}
|
|
4986
|
+
}
|
|
4987
|
+
resolveSlippage(override) {
|
|
4988
|
+
return override ?? this.defaultSlippageBps;
|
|
4989
|
+
}
|
|
4990
|
+
};
|
|
4991
|
+
|
|
4992
|
+
exports.CancelOrderType = CancelOrderType;
|
|
4993
|
+
exports.DEFAULT_BUILDER_API_URL = DEFAULT_BUILDER_API_URL;
|
|
4994
|
+
exports.DEFAULT_SLIPPAGE_PERCENTAGE = DEFAULT_SLIPPAGE_PERCENTAGE;
|
|
4995
|
+
exports.DEFAULT_SUBGRAPH_ENDPOINT = DEFAULT_SUBGRAPH_ENDPOINT;
|
|
4996
|
+
exports.DEFAULT_SUBGRAPH_ENDPOINT_TESTNET = DEFAULT_SUBGRAPH_ENDPOINT_TESTNET;
|
|
4997
|
+
exports.MAX_COLLATERAL_USD = MAX_COLLATERAL_USD;
|
|
4998
|
+
exports.MIN_COLLATERAL_USD = MIN_COLLATERAL_USD;
|
|
4999
|
+
exports.OrderType = OrderType;
|
|
5000
|
+
exports.OstiumClient = OstiumClient;
|
|
5001
|
+
exports.OstiumError = OstiumError;
|
|
5002
|
+
exports.OstiumErrorCode = OstiumErrorCode;
|
|
5003
|
+
exports.OstiumPriceStream = OstiumPriceStream;
|
|
5004
|
+
exports.OstiumSubgraphClient = OstiumSubgraphClient;
|
|
5005
|
+
exports.OstiumSubgraphError = OstiumSubgraphError;
|
|
5006
|
+
exports.OstiumSubgraphErrorCode = OstiumSubgraphErrorCode;
|
|
5007
|
+
exports.PRECISION_18 = PRECISION_18;
|
|
5008
|
+
exports.PRECISION_6 = PRECISION_6;
|
|
5009
|
+
exports.parseLeverage = parseLeverage;
|
|
5010
|
+
exports.parsePrice = parsePrice;
|
|
5011
|
+
exports.parseUsdc = parseUsdc;
|
|
5012
|
+
//# sourceMappingURL=index.cjs.map
|
|
5013
|
+
//# sourceMappingURL=index.cjs.map
|