ccxt 4.3.52 → 4.3.53

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.
@@ -0,0 +1,2942 @@
1
+ // ----------------------------------------------------------------------------
2
+
3
+ // PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
4
+ // https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
5
+ // EDIT THE CORRESPONDENT .ts FILE INSTEAD
6
+
7
+ // ---------------------------------------------------------------------------
8
+ import Exchange from './abstract/vertex.js';
9
+ import { ExchangeError, RateLimitExceeded, PermissionDenied, InsufficientFunds, AuthenticationError, ArgumentsRequired, NotSupported, InvalidOrder, BadRequest } from './base/errors.js';
10
+ import { Precise } from './base/Precise.js';
11
+ import { TICK_SIZE } from './base/functions/number.js';
12
+ import { keccak_256 as keccak } from './static_dependencies/noble-hashes/sha3.js';
13
+ import { secp256k1 } from './static_dependencies/noble-curves/secp256k1.js';
14
+ import { ecdsa } from './base/functions/crypto.js';
15
+ // ---------------------------------------------------------------------------
16
+ /**
17
+ * @class vertex
18
+ * @augments Exchange
19
+ */
20
+ export default class vertex extends Exchange {
21
+ describe() {
22
+ return this.deepExtend(super.describe(), {
23
+ 'id': 'vertex',
24
+ 'name': 'Vertex',
25
+ 'countries': [],
26
+ 'version': 'v1',
27
+ 'rateLimit': 50,
28
+ 'certified': false,
29
+ 'pro': true,
30
+ 'dex': true,
31
+ 'has': {
32
+ 'CORS': undefined,
33
+ 'spot': true,
34
+ 'margin': false,
35
+ 'swap': true,
36
+ 'future': true,
37
+ 'option': false,
38
+ 'addMargin': false,
39
+ 'borrowCrossMargin': false,
40
+ 'borrowIsolatedMargin': false,
41
+ 'cancelAllOrders': true,
42
+ 'cancelAllOrdersAfter': false,
43
+ 'cancelOrder': true,
44
+ 'cancelOrders': true,
45
+ 'cancelOrdersForSymbols': false,
46
+ 'closeAllPositions': false,
47
+ 'closePosition': false,
48
+ 'createMarketBuyOrderWithCost': false,
49
+ 'createMarketOrderWithCost': false,
50
+ 'createMarketSellOrderWithCost': false,
51
+ 'createOrder': true,
52
+ 'createOrders': true,
53
+ 'createReduceOnlyOrder': true,
54
+ 'editOrder': false,
55
+ 'fetchAccounts': false,
56
+ 'fetchBalance': true,
57
+ 'fetchBorrowInterest': false,
58
+ 'fetchBorrowRateHistories': false,
59
+ 'fetchBorrowRateHistory': false,
60
+ 'fetchCanceledOrders': false,
61
+ 'fetchClosedOrders': false,
62
+ 'fetchCrossBorrowRate': false,
63
+ 'fetchCrossBorrowRates': false,
64
+ 'fetchCurrencies': true,
65
+ 'fetchDepositAddress': false,
66
+ 'fetchDepositAddresses': false,
67
+ 'fetchDeposits': false,
68
+ 'fetchDepositWithdrawFee': false,
69
+ 'fetchDepositWithdrawFees': false,
70
+ 'fetchFundingHistory': false,
71
+ 'fetchFundingRate': true,
72
+ 'fetchFundingRateHistory': false,
73
+ 'fetchFundingRates': true,
74
+ 'fetchIndexOHLCV': false,
75
+ 'fetchIsolatedBorrowRate': false,
76
+ 'fetchIsolatedBorrowRates': false,
77
+ 'fetchLedger': false,
78
+ 'fetchLeverage': false,
79
+ 'fetchLeverageTiers': false,
80
+ 'fetchLiquidations': false,
81
+ 'fetchMarginMode': undefined,
82
+ 'fetchMarketLeverageTiers': false,
83
+ 'fetchMarkets': true,
84
+ 'fetchMarkOHLCV': false,
85
+ 'fetchMyLiquidations': false,
86
+ 'fetchMyTrades': true,
87
+ 'fetchOHLCV': true,
88
+ 'fetchOpenInterest': true,
89
+ 'fetchOpenInterestHistory': false,
90
+ 'fetchOpenOrders': true,
91
+ 'fetchOrder': true,
92
+ 'fetchOrderBook': true,
93
+ 'fetchOrders': true,
94
+ 'fetchOrderTrades': false,
95
+ 'fetchPosition': false,
96
+ 'fetchPositionMode': false,
97
+ 'fetchPositions': true,
98
+ 'fetchPositionsRisk': false,
99
+ 'fetchPremiumIndexOHLCV': false,
100
+ 'fetchStatus': true,
101
+ 'fetchTicker': false,
102
+ 'fetchTickers': true,
103
+ 'fetchTime': true,
104
+ 'fetchTrades': true,
105
+ 'fetchTradingFee': false,
106
+ 'fetchTradingFees': true,
107
+ 'fetchTransfer': false,
108
+ 'fetchTransfers': false,
109
+ 'fetchWithdrawal': false,
110
+ 'fetchWithdrawals': false,
111
+ 'reduceMargin': false,
112
+ 'repayCrossMargin': false,
113
+ 'repayIsolatedMargin': false,
114
+ 'sandbox': true,
115
+ 'setLeverage': false,
116
+ 'setMarginMode': false,
117
+ 'setPositionMode': false,
118
+ 'transfer': false,
119
+ 'withdraw': true,
120
+ },
121
+ 'timeframes': {
122
+ '1m': 60,
123
+ '5m': 300,
124
+ '15m': 900,
125
+ '1h': 3600,
126
+ '2h': 7200,
127
+ '4h': 14400,
128
+ '1d': 86400,
129
+ '1w': 604800,
130
+ '1M': 604800,
131
+ },
132
+ 'hostname': 'vertexprotocol.com',
133
+ 'urls': {
134
+ 'logo': 'https://github.com/ccxt/ccxt/assets/43336371/bd04a0fa-3b48-47b6-9d8b-124954d520a8',
135
+ 'api': {
136
+ 'v1': {
137
+ 'archive': 'https://archive.prod.{hostname}/v1',
138
+ 'gateway': 'https://gateway.prod.{hostname}/v1',
139
+ 'trigger': 'https://trigger.prod.{hostname}/v1',
140
+ },
141
+ 'v2': {
142
+ 'archive': 'https://archive.prod.{hostname}/v2',
143
+ 'gateway': 'https://gateway.prod.{hostname}/v2',
144
+ },
145
+ },
146
+ 'test': {
147
+ 'v1': {
148
+ 'archive': 'https://archive.sepolia-test.{hostname}/v1',
149
+ 'gateway': 'https://gateway.sepolia-test.{hostname}/v1',
150
+ 'trigger': 'https://trigger.sepolia-test.{hostname}/v1',
151
+ },
152
+ 'v2': {
153
+ 'archive': 'https://archive.sepolia-test.{hostname}/v2',
154
+ 'gateway': 'https://gateway.sepolia-test.{hostname}/v2',
155
+ },
156
+ },
157
+ 'www': 'https://vertexprotocol.com/',
158
+ 'doc': 'https://docs.vertexprotocol.com/',
159
+ 'fees': 'https://docs.vertexprotocol.com/basics/fees',
160
+ 'referral': 'https://app.vertexprotocol.com?referrer=0xCfC9BaB96a2eA3d3c3F031c005e82E1D9F295aC1',
161
+ },
162
+ 'api': {
163
+ 'v1': {
164
+ 'archive': {
165
+ 'post': {
166
+ '': 1,
167
+ },
168
+ },
169
+ 'gateway': {
170
+ 'get': {
171
+ 'query': 1,
172
+ 'symbols': 1,
173
+ 'time': 1,
174
+ },
175
+ 'post': {
176
+ 'query': 1,
177
+ 'execute': 1,
178
+ },
179
+ },
180
+ 'trigger': {
181
+ 'post': {
182
+ 'execute': 1,
183
+ 'query': 1,
184
+ },
185
+ },
186
+ },
187
+ 'v2': {
188
+ 'archive': {
189
+ 'get': {
190
+ 'tickers': 1,
191
+ 'contracts': 1,
192
+ 'trades': 1,
193
+ 'vrtx': 1,
194
+ },
195
+ },
196
+ 'gateway': {
197
+ 'get': {
198
+ 'assets': 0.6667,
199
+ 'pairs': 1,
200
+ 'orderbook': 1,
201
+ },
202
+ },
203
+ },
204
+ },
205
+ 'fees': {
206
+ 'swap': {
207
+ 'taker': this.parseNumber('0.0002'),
208
+ 'maker': this.parseNumber('0.0002'),
209
+ },
210
+ 'spot': {
211
+ 'taker': this.parseNumber('0.0002'),
212
+ 'maker': this.parseNumber('0.0002'),
213
+ },
214
+ },
215
+ 'requiredCredentials': {
216
+ 'apiKey': false,
217
+ 'secret': false,
218
+ 'walletAddress': true,
219
+ 'privateKey': true,
220
+ },
221
+ 'exceptions': {
222
+ 'exact': {
223
+ '1000': RateLimitExceeded,
224
+ '1015': RateLimitExceeded,
225
+ '1001': PermissionDenied,
226
+ '1002': PermissionDenied,
227
+ '1003': PermissionDenied,
228
+ '2000': InvalidOrder,
229
+ '2001': InvalidOrder,
230
+ '2002': InvalidOrder,
231
+ '2003': InvalidOrder,
232
+ '2004': InvalidOrder,
233
+ '2005': InvalidOrder,
234
+ '2006': InvalidOrder,
235
+ '2007': InvalidOrder,
236
+ '2008': InvalidOrder,
237
+ '2009': InvalidOrder,
238
+ '2010': InvalidOrder,
239
+ '2011': BadRequest,
240
+ '2012': BadRequest,
241
+ '2013': InvalidOrder,
242
+ '2014': PermissionDenied,
243
+ '2015': InvalidOrder,
244
+ '2016': InvalidOrder,
245
+ '2017': InvalidOrder,
246
+ '2019': InvalidOrder,
247
+ '2020': InvalidOrder,
248
+ '2021': InvalidOrder,
249
+ '2022': InvalidOrder,
250
+ '2023': InvalidOrder,
251
+ '2024': InsufficientFunds,
252
+ '2025': InsufficientFunds,
253
+ '2026': BadRequest,
254
+ '2027': AuthenticationError,
255
+ '2028': AuthenticationError,
256
+ '2029': AuthenticationError,
257
+ '2030': BadRequest,
258
+ '2031': InvalidOrder,
259
+ '2033': InvalidOrder,
260
+ '2034': InvalidOrder,
261
+ '2035': InvalidOrder,
262
+ '2036': InvalidOrder,
263
+ '2037': InvalidOrder,
264
+ '2038': InvalidOrder,
265
+ '2039': InvalidOrder,
266
+ '2040': InvalidOrder,
267
+ '2041': InvalidOrder,
268
+ '2042': InvalidOrder,
269
+ '2043': InvalidOrder,
270
+ '2044': InvalidOrder,
271
+ '2045': InvalidOrder,
272
+ '2046': InvalidOrder,
273
+ '2047': InvalidOrder,
274
+ '2048': InvalidOrder,
275
+ '2049': ExchangeError,
276
+ '2050': PermissionDenied,
277
+ '2051': InvalidOrder,
278
+ '2052': InvalidOrder,
279
+ '2053': InvalidOrder,
280
+ '2054': InvalidOrder,
281
+ '2055': InvalidOrder,
282
+ '2056': InvalidOrder,
283
+ '2057': InvalidOrder,
284
+ '2058': InvalidOrder,
285
+ '2059': InvalidOrder,
286
+ '2060': InvalidOrder,
287
+ '2061': InvalidOrder,
288
+ '2062': InvalidOrder,
289
+ '2063': InvalidOrder,
290
+ '2064': InvalidOrder,
291
+ '2065': InvalidOrder,
292
+ '2066': InvalidOrder,
293
+ '2067': InvalidOrder,
294
+ '2068': InvalidOrder,
295
+ '2069': InvalidOrder,
296
+ '2070': InvalidOrder,
297
+ '2071': InvalidOrder,
298
+ '2072': InvalidOrder,
299
+ '2073': InvalidOrder,
300
+ '2074': InvalidOrder,
301
+ '2075': InvalidOrder,
302
+ '2076': InvalidOrder,
303
+ '3000': BadRequest,
304
+ '3001': BadRequest,
305
+ '3002': BadRequest,
306
+ '3003': BadRequest,
307
+ '4000': BadRequest,
308
+ '4001': ExchangeError,
309
+ '4002': ExchangeError,
310
+ '4003': ExchangeError,
311
+ '4004': InvalidOrder,
312
+ '5000': ExchangeError,
313
+ },
314
+ 'broad': {},
315
+ },
316
+ 'precisionMode': TICK_SIZE,
317
+ 'commonCurrencies': {},
318
+ 'options': {
319
+ 'defaultType': 'swap',
320
+ 'sandboxMode': false,
321
+ 'timeDifference': 0,
322
+ 'brokerId': 5930043274845996,
323
+ },
324
+ });
325
+ }
326
+ setSandboxMode(enabled) {
327
+ super.setSandboxMode(enabled);
328
+ this.options['sandboxMode'] = enabled;
329
+ }
330
+ convertToX18(num) {
331
+ if (typeof num === 'string') {
332
+ return Precise.stringMul(num, '1000000000000000000');
333
+ }
334
+ const numStr = this.numberToString(num);
335
+ return Precise.stringMul(numStr, '1000000000000000000');
336
+ }
337
+ convertFromX18(num) {
338
+ if (typeof num === 'string') {
339
+ return Precise.stringDiv(num, '1000000000000000000');
340
+ }
341
+ const numStr = this.numberToString(num);
342
+ return Precise.stringDiv(numStr, '1000000000000000000');
343
+ }
344
+ async fetchCurrencies(params = {}) {
345
+ /**
346
+ * @method
347
+ * @name vertex#fetchCurrencies
348
+ * @description fetches all available currencies on an exchange
349
+ * @see https://docs.vertexprotocol.com/developer-resources/api/v2/assets
350
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
351
+ * @returns {object} an associative dictionary of currencies
352
+ */
353
+ const request = {};
354
+ const response = await this.v2GatewayGetAssets(this.extend(request, params));
355
+ //
356
+ // [
357
+ // {
358
+ // "product_id": 2,
359
+ // "ticker_id": "BTC-PERP_USDC",
360
+ // "market_type": "perp",
361
+ // "name": "Bitcoin Perp",
362
+ // "symbol": "BTC-PERP",
363
+ // "maker_fee": 0.0002,
364
+ // "taker_fee": 0,
365
+ // "can_withdraw": false,
366
+ // "can_deposit": false
367
+ // },
368
+ // {
369
+ // "product_id": 1,
370
+ // "ticker_id": "BTC_USDC",
371
+ // "market_type": "spot",
372
+ // "name": "Bitcoin",
373
+ // "symbol": "BTC",
374
+ // "taker_fee": 0.0003,
375
+ // "maker_fee": 0,
376
+ // "can_withdraw": true,
377
+ // "can_deposit": true
378
+ // }
379
+ // ]
380
+ //
381
+ const result = {};
382
+ for (let i = 0; i < response.length; i++) {
383
+ const data = this.safeDict(response, i, {});
384
+ const tickerId = this.safeString(data, 'ticker_id');
385
+ if ((tickerId !== undefined) && (tickerId.indexOf('PERP') > 0)) {
386
+ continue;
387
+ }
388
+ const id = this.safeString(data, 'product_id');
389
+ const name = this.safeString(data, 'symbol');
390
+ const code = this.safeCurrencyCode(name);
391
+ result[code] = {
392
+ 'id': id,
393
+ 'name': name,
394
+ 'code': code,
395
+ 'precision': undefined,
396
+ 'info': data,
397
+ 'active': undefined,
398
+ 'deposit': this.safeBool(data, 'can_deposit'),
399
+ 'withdraw': this.safeBool(data, 'can_withdraw'),
400
+ 'networks': undefined,
401
+ 'fee': undefined,
402
+ 'limits': undefined,
403
+ };
404
+ }
405
+ return result;
406
+ }
407
+ parseMarket(market) {
408
+ //
409
+ // {
410
+ // "type": "spot",
411
+ // "product_id": 3,
412
+ // "symbol": "WETH",
413
+ // "price_increment_x18": "100000000000000000",
414
+ // "size_increment": "10000000000000000",
415
+ // "min_size": "100000000000000000",
416
+ // "min_depth_x18": "5000000000000000000000",
417
+ // "max_spread_rate_x18": "2000000000000000",
418
+ // "maker_fee_rate_x18": "0",
419
+ // "taker_fee_rate_x18": "300000000000000",
420
+ // "long_weight_initial_x18": "900000000000000000",
421
+ // "long_weight_maintenance_x18": "950000000000000000"
422
+ // }
423
+ //
424
+ const marketType = this.safeString(market, 'type');
425
+ const quoteId = 'USDC';
426
+ const quote = this.safeCurrencyCode(quoteId);
427
+ const baseId = this.safeString(market, 'symbol');
428
+ const base = this.safeCurrencyCode(baseId);
429
+ const settleId = quoteId;
430
+ const settle = this.safeCurrencyCode(settleId);
431
+ let symbol = base + '/' + quote;
432
+ const spot = marketType === 'spot';
433
+ const contract = !spot;
434
+ const swap = !spot;
435
+ if (swap) {
436
+ const splitSymbol = base.split('-');
437
+ symbol = splitSymbol[0] + '/' + quote + ':' + settle;
438
+ }
439
+ const priceIncrementX18 = this.safeString(market, 'price_increment_x18');
440
+ const sizeIncrementX18 = this.safeString(market, 'size_increment');
441
+ const minSizeX18 = this.safeString(market, 'min_size');
442
+ const takerX18 = this.safeNumber(market, 'taker_fee_rate_x18');
443
+ const makerX18 = this.safeNumber(market, 'maker_fee_rate_x18');
444
+ const isInverse = (spot) ? undefined : false;
445
+ const isLinear = (spot) ? undefined : true;
446
+ const contractSize = (spot) ? undefined : this.parseNumber('1');
447
+ return {
448
+ 'id': this.safeString(market, 'product_id'),
449
+ 'symbol': symbol,
450
+ 'base': base,
451
+ 'quote': quote,
452
+ 'settle': (spot) ? undefined : settle,
453
+ 'baseId': baseId,
454
+ 'quoteId': quoteId,
455
+ 'settleId': (spot) ? undefined : settleId,
456
+ 'type': (spot) ? 'spot' : 'swap',
457
+ 'spot': spot,
458
+ 'margin': undefined,
459
+ 'swap': swap,
460
+ 'future': false,
461
+ 'option': false,
462
+ 'active': true,
463
+ 'contract': contract,
464
+ 'linear': isLinear,
465
+ 'inverse': isInverse,
466
+ 'taker': this.parseNumber(this.convertFromX18(takerX18)),
467
+ 'maker': this.parseNumber(this.convertFromX18(makerX18)),
468
+ 'contractSize': contractSize,
469
+ 'expiry': undefined,
470
+ 'expiryDatetime': undefined,
471
+ 'strike': undefined,
472
+ 'optionType': undefined,
473
+ 'precision': {
474
+ 'amount': this.parseNumber(this.convertFromX18(sizeIncrementX18)),
475
+ 'price': this.parseNumber(this.convertFromX18(priceIncrementX18)),
476
+ },
477
+ 'limits': {
478
+ 'leverage': {
479
+ 'min': undefined,
480
+ 'max': undefined,
481
+ },
482
+ 'amount': {
483
+ 'min': this.parseNumber(this.convertFromX18(minSizeX18)),
484
+ 'max': undefined,
485
+ },
486
+ 'price': {
487
+ 'min': undefined,
488
+ 'max': undefined,
489
+ },
490
+ 'cost': {
491
+ 'min': undefined,
492
+ 'max': undefined,
493
+ },
494
+ },
495
+ 'created': undefined,
496
+ 'info': market,
497
+ };
498
+ }
499
+ async fetchMarkets(params = {}) {
500
+ /**
501
+ * @method
502
+ * @name vertex#fetchMarkets
503
+ * @description retrieves data on all markets for vertex
504
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/queries/symbols
505
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
506
+ * @returns {object[]} an array of objects representing market data
507
+ */
508
+ const request = {
509
+ 'type': 'symbols',
510
+ };
511
+ const response = await this.v1GatewayGetQuery(this.extend(request, params));
512
+ //
513
+ // {
514
+ // "status": "success",
515
+ // "data": {
516
+ // "symbols": {
517
+ // "WETH": {
518
+ // "type": "spot",
519
+ // "product_id": 3,
520
+ // "symbol": "WETH",
521
+ // "price_increment_x18": "100000000000000000",
522
+ // "size_increment": "10000000000000000",
523
+ // "min_size": "100000000000000000",
524
+ // "min_depth_x18": "5000000000000000000000",
525
+ // "max_spread_rate_x18": "2000000000000000",
526
+ // "maker_fee_rate_x18": "0",
527
+ // "taker_fee_rate_x18": "300000000000000",
528
+ // "long_weight_initial_x18": "900000000000000000",
529
+ // "long_weight_maintenance_x18": "950000000000000000"
530
+ // }
531
+ // }
532
+ // },
533
+ // "request_type": "query_symbols"
534
+ // }
535
+ //
536
+ const data = this.safeDict(response, 'data', {});
537
+ const markets = this.safeDict(data, 'symbols', {});
538
+ const symbols = Object.keys(markets);
539
+ const result = [];
540
+ for (let i = 0; i < symbols.length; i++) {
541
+ const symbol = symbols[i];
542
+ const rawMarket = this.safeDict(markets, symbol, {});
543
+ result.push(this.parseMarket(rawMarket));
544
+ }
545
+ return result;
546
+ }
547
+ async fetchTime(params = {}) {
548
+ /**
549
+ * @method
550
+ * @name vertex#fetchTime
551
+ * @description fetches the current integer timestamp in milliseconds from the exchange server
552
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
553
+ * @returns {int} the current integer timestamp in milliseconds from the exchange server
554
+ */
555
+ const response = await this.v1GatewayGetTime(params);
556
+ // 1717481623452
557
+ return this.parseNumber(response);
558
+ }
559
+ async fetchStatus(params = {}) {
560
+ /**
561
+ * @method
562
+ * @name vertex#fetchStatus
563
+ * @description the latest known information on the availability of the exchange API
564
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/queries/status
565
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
566
+ * @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure}
567
+ */
568
+ const request = {
569
+ 'type': 'status',
570
+ };
571
+ const response = await this.v1GatewayGetQuery(this.extend(request, params));
572
+ //
573
+ // {
574
+ // "status": "success",
575
+ // "data": "active",
576
+ // "request_type": "query_status",
577
+ // }
578
+ //
579
+ let status = this.safeString(response, 'data');
580
+ if (status === 'active') {
581
+ status = 'ok';
582
+ }
583
+ else {
584
+ status = 'error';
585
+ }
586
+ return {
587
+ 'status': status,
588
+ 'updated': undefined,
589
+ 'eta': undefined,
590
+ 'url': undefined,
591
+ 'info': response,
592
+ };
593
+ }
594
+ parseTrade(trade, market = undefined) {
595
+ //
596
+ // {
597
+ // "ticker_id": "ARB_USDC",
598
+ // "trade_id": 999994,
599
+ // "price": 1.1366122408151016,
600
+ // "base_filled": 175,
601
+ // "quote_filled": -198.90714214264278,
602
+ // "timestamp": 1691068943,
603
+ // "trade_type": "buy"
604
+ // }
605
+ // fetchMytrades
606
+ // {
607
+ // "digest": "0x80ce789702b670b7d33f2aa67e12c85f124395c3f9acdb422dde3b4973ccd50c",
608
+ // "order": {
609
+ // "sender": "0x12a0b4888021576eb10a67616dd3dd3d9ce206b664656661756c740000000000",
610
+ // "priceX18": "27544000000000000000000",
611
+ // "amount": "2000000000000000000",
612
+ // "expiration": "4611686020107119633",
613
+ // "nonce": "1761322608857448448"
614
+ // },
615
+ // "base_filled": "736000000000000000",
616
+ // "quote_filled": "-20276464287857571514302",
617
+ // "fee": "4055287857571514302",
618
+ // "sequencer_fee": "0"
619
+ // "cumulative_fee": "4055287857571514302",
620
+ // "cumulative_base_filled": "736000000000000000",
621
+ // "cumulative_quote_filled": "-20276464287857571514302",
622
+ // "submission_idx": "563012",
623
+ // "pre_balance": {
624
+ // "base": {
625
+ // "perp": {
626
+ // "product_id": 2,
627
+ // "lp_balance": {
628
+ // "amount": "0",
629
+ // "last_cumulative_funding_x18": "1823351297710837"
630
+ // },
631
+ // "balance": {
632
+ // "amount": "2686684000000000000000",
633
+ // "v_quote_balance": "-76348662407149297671587247",
634
+ // "last_cumulative_funding_x18": "134999841911604906604576"
635
+ // }
636
+ // }
637
+ // },
638
+ // "quote": null
639
+ // },
640
+ // "post_balance": {
641
+ // "base": {
642
+ // "perp": {
643
+ // "product_id": 2,
644
+ // "lp_balance": {
645
+ // "amount": "0",
646
+ // "last_cumulative_funding_x18": "1823351297710837"
647
+ // },
648
+ // "balance": {
649
+ // "amount": "2686013000000000000000",
650
+ // "v_quote_balance": "-76328351274188497671587247",
651
+ // "last_cumulative_funding_x18": "134999841911604906604576"
652
+ // }
653
+ // }
654
+ // },
655
+ // "quote": null
656
+ // }
657
+ // }
658
+ let price = undefined;
659
+ let amount = undefined;
660
+ let side = undefined;
661
+ let fee = undefined;
662
+ const id = this.safeString2(trade, 'trade_id', 'submission_idx');
663
+ const order = this.safeString(trade, 'digest');
664
+ const timestamp = this.safeTimestamp(trade, 'timestamp');
665
+ if (timestamp === undefined) {
666
+ // fetchMyTrades
667
+ const baseBalance = this.safeDict(this.safeDict(trade, 'pre_balance', {}), 'base', {});
668
+ let marketId = undefined;
669
+ if ('perp' in baseBalance) {
670
+ marketId = this.safeString(this.safeDict(baseBalance, 'perp', {}), 'product_id');
671
+ }
672
+ else {
673
+ marketId = this.safeString(this.safeDict(baseBalance, 'spot', {}), 'product_id');
674
+ }
675
+ market = this.safeMarket(marketId);
676
+ const subOrder = this.safeDict(trade, 'order', {});
677
+ price = this.convertFromX18(this.safeString(subOrder, 'priceX18'));
678
+ amount = this.convertFromX18(this.safeString(trade, 'base_filled'));
679
+ fee = {
680
+ 'cost': this.convertFromX18(this.safeString(trade, 'fee')),
681
+ 'currency': undefined,
682
+ };
683
+ if (Precise.stringLt(amount, '0')) {
684
+ side = 'sell';
685
+ }
686
+ else {
687
+ side = 'buy';
688
+ }
689
+ }
690
+ else {
691
+ const tickerId = this.safeString(trade, 'ticker_id');
692
+ const splitTickerId = tickerId.split('_');
693
+ const splitSymbol = splitTickerId[0].split('-');
694
+ const marketId = splitSymbol[0] + splitTickerId[1];
695
+ market = this.safeMarket(marketId, market);
696
+ price = this.safeString(trade, 'price');
697
+ amount = this.safeString(trade, 'base_filled');
698
+ side = this.safeStringLower(trade, 'trade_type');
699
+ }
700
+ amount = Precise.stringAbs(amount);
701
+ const symbol = market['symbol'];
702
+ return this.safeTrade({
703
+ 'id': id,
704
+ 'timestamp': timestamp,
705
+ 'datetime': this.iso8601(timestamp),
706
+ 'symbol': symbol,
707
+ 'side': side,
708
+ 'price': price,
709
+ 'amount': amount,
710
+ 'cost': undefined,
711
+ 'order': order,
712
+ 'takerOrMaker': undefined,
713
+ 'type': undefined,
714
+ 'fee': fee,
715
+ 'info': trade,
716
+ }, market);
717
+ }
718
+ async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
719
+ /**
720
+ * @method
721
+ * @name vertex#fetchTrades
722
+ * @description get the list of most recent trades for a particular symbol
723
+ * @see https://docs.vertexprotocol.com/developer-resources/api/v2/trades
724
+ * @param {string} symbol unified symbol of the market to fetch trades for
725
+ * @param {int} [since] timestamp in ms of the earliest trade to fetch
726
+ * @param {int} [limit] the maximum amount of trades to fetch
727
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
728
+ * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
729
+ */
730
+ await this.loadMarkets();
731
+ const market = this.market(symbol);
732
+ const marketId = market['baseId'] + '_USDC';
733
+ const request = {
734
+ 'ticker_id': marketId,
735
+ };
736
+ if (limit !== undefined) {
737
+ request['limit'] = limit;
738
+ }
739
+ const response = await this.v2ArchiveGetTrades(this.extend(request, params));
740
+ //
741
+ // [
742
+ // {
743
+ // "ticker_id": "ARB_USDC",
744
+ // "trade_id": 999994,
745
+ // "price": 1.1366122408151016,
746
+ // "base_filled": 175,
747
+ // "quote_filled": -198.90714214264278,
748
+ // "timestamp": 1691068943,
749
+ // "trade_type": "buy"
750
+ // },
751
+ // {
752
+ // "ticker_id": "ARB_USDC",
753
+ // "trade_id": 999978,
754
+ // "price": 1.136512210806099,
755
+ // "base_filled": 175,
756
+ // "quote_filled": -198.8896368910673,
757
+ // "timestamp": 1691068882,
758
+ // "trade_type": "buy"
759
+ // }
760
+ // ]
761
+ //
762
+ return this.parseTrades(response, market, since, limit);
763
+ }
764
+ async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
765
+ /**
766
+ * @method
767
+ * @name vertex#fetchMyTrades
768
+ * @description fetch all trades made by the user
769
+ * @see https://docs.vertexprotocol.com/developer-resources/api/archive-indexer/matches
770
+ * @param {string} symbol unified market symbol
771
+ * @param {int} [since] the earliest time in ms to fetch trades for
772
+ * @param {int} [limit] the maximum number of trades structures to retrieve
773
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
774
+ * @param {string} [params.user] user address, will default to this.walletAddress if not provided
775
+ * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
776
+ */
777
+ await this.loadMarkets();
778
+ let userAddress = undefined;
779
+ [userAddress, params] = this.handlePublicAddress('fetchMyTrades', params);
780
+ let market = undefined;
781
+ const matchesRequest = {
782
+ 'subaccount': this.convertAddressToSender(userAddress),
783
+ };
784
+ if (symbol !== undefined) {
785
+ market = this.market(symbol);
786
+ matchesRequest['product_ids'] = [this.parseToNumeric(market['id'])];
787
+ }
788
+ const until = this.safeInteger(params, 'until');
789
+ if (until !== undefined) {
790
+ params = this.omit(params, 'until');
791
+ matchesRequest['max_time'] = until;
792
+ }
793
+ if (limit !== undefined) {
794
+ matchesRequest['limit'] = limit;
795
+ }
796
+ const request = {
797
+ 'matches': matchesRequest,
798
+ };
799
+ const response = await this.v1ArchivePost(this.extend(request, params));
800
+ //
801
+ // {
802
+ // "matches": [
803
+ // {
804
+ // "digest": "0x80ce789702b670b7d33f2aa67e12c85f124395c3f9acdb422dde3b4973ccd50c",
805
+ // "order": {
806
+ // "sender": "0x12a0b4888021576eb10a67616dd3dd3d9ce206b664656661756c740000000000",
807
+ // "priceX18": "27544000000000000000000",
808
+ // "amount": "2000000000000000000",
809
+ // "expiration": "4611686020107119633",
810
+ // "nonce": "1761322608857448448"
811
+ // },
812
+ // "base_filled": "736000000000000000",
813
+ // "quote_filled": "-20276464287857571514302",
814
+ // "fee": "4055287857571514302",
815
+ // "sequencer_fee": "0"
816
+ // "cumulative_fee": "4055287857571514302",
817
+ // "cumulative_base_filled": "736000000000000000",
818
+ // "cumulative_quote_filled": "-20276464287857571514302",
819
+ // "submission_idx": "563012",
820
+ // "pre_balance": {
821
+ // "base": {
822
+ // "perp": {
823
+ // "product_id": 2,
824
+ // "lp_balance": {
825
+ // "amount": "0",
826
+ // "last_cumulative_funding_x18": "1823351297710837"
827
+ // },
828
+ // "balance": {
829
+ // "amount": "2686684000000000000000",
830
+ // "v_quote_balance": "-76348662407149297671587247",
831
+ // "last_cumulative_funding_x18": "134999841911604906604576"
832
+ // }
833
+ // }
834
+ // },
835
+ // "quote": null
836
+ // },
837
+ // "post_balance": {
838
+ // "base": {
839
+ // "perp": {
840
+ // "product_id": 2,
841
+ // "lp_balance": {
842
+ // "amount": "0",
843
+ // "last_cumulative_funding_x18": "1823351297710837"
844
+ // },
845
+ // "balance": {
846
+ // "amount": "2686013000000000000000",
847
+ // "v_quote_balance": "-76328351274188497671587247",
848
+ // "last_cumulative_funding_x18": "134999841911604906604576"
849
+ // }
850
+ // }
851
+ // },
852
+ // "quote": null
853
+ // }
854
+ // },
855
+ // {
856
+ // "digest": "0x0f6e5a0434e36d8e6d4fed950d3624b0d8c91a8a84efd156bb25c1382561c0c2",
857
+ // "order": {
858
+ // "sender": "0x12a0b4888021576eb10a67616dd3dd3d9ce206b664656661756c740000000000",
859
+ // "priceX18": "27540000000000000000000",
860
+ // "amount": "2000000000000000000",
861
+ // "expiration": "4611686020107119623",
862
+ // "nonce": "1761322602510417920"
863
+ // },
864
+ // "base_filled": "723999999999999999",
865
+ // "quote_filled": "-19944943483044913474043",
866
+ // "fee": "5983483044913474042",
867
+ // "cumulative_fee": "11958484645393618085",
868
+ // "cumulative_base_filled": "1446999999999999998",
869
+ // "cumulative_quote_filled": "-39861640484645393618087",
870
+ // "submission_idx": "563011",
871
+ // "pre_balance": {
872
+ // "base": {
873
+ // "perp": {
874
+ // "product_id": 2,
875
+ // "lp_balance": {
876
+ // "amount": "0",
877
+ // "last_cumulative_funding_x18": "1823351297710837"
878
+ // },
879
+ // "balance": {
880
+ // "amount": "2686684000000000000000",
881
+ // "v_quote_balance": "-76348662407149297671587247",
882
+ // "last_cumulative_funding_x18": "134999841911604906604576"
883
+ // }
884
+ // }
885
+ // },
886
+ // "quote": null
887
+ // },
888
+ // "post_balance": {
889
+ // "base": {
890
+ // "perp": {
891
+ // "product_id": 2,
892
+ // "lp_balance": {
893
+ // "amount": "0",
894
+ // "last_cumulative_funding_x18": "1823351297710837"
895
+ // },
896
+ // "balance": {
897
+ // "amount": "2686013000000000000000",
898
+ // "v_quote_balance": "-76328351274188497671587247",
899
+ // "last_cumulative_funding_x18": "134999841911604906604576"
900
+ // }
901
+ // }
902
+ // },
903
+ // "quote": null
904
+ // }
905
+ // }
906
+ // ],
907
+ // "txs": [
908
+ // {
909
+ // "tx": {
910
+ // "match_orders": {
911
+ // "product_id": 2,
912
+ // "amm": true,
913
+ // "taker": {
914
+ // "order": {
915
+ // "sender": "0x12a0b4888021576eb10a67616dd3dd3d9ce206b664656661756c740000000000",
916
+ // "price_x18": "27544000000000000000000",
917
+ // "amount": "2000000000000000000",
918
+ // "expiration": 4611686020107120000,
919
+ // "nonce": 1761322608857448400
920
+ // },
921
+ // "signature": "0xe8fa7151bde348afa3b46dc52798046b7c8318f1b0a7f689710debbc094658cc1bf5a7e478ccc8278b625da0b9402c86b580d2e31e13831337dfd6153f4b37811b"
922
+ // },
923
+ // "maker": {
924
+ // "order": {
925
+ // "sender": "0xebdbbcdbd2646c5f23a1e0806027eee5f71b074664656661756c740000000000",
926
+ // "price_x18": "27544000000000000000000",
927
+ // "amount": "-736000000000000000",
928
+ // "expiration": 1679731669,
929
+ // "nonce": 1761322585591644200
930
+ // },
931
+ // "signature": "0x47f9d47f0777f3ca0b13f07b7682dbeea098c0e377b87dcb025754fe34c900e336b8c7744e021fb9c46a4f8c6a1478bafa28bf0d023ae496aa3efa4d8e81df181c"
932
+ // }
933
+ // }
934
+ // },
935
+ // "submission_idx": "563012",
936
+ // "timestamp": "1679728133"
937
+ // },
938
+ // {
939
+ // "tx": {
940
+ // "match_orders": {
941
+ // "product_id": 1,
942
+ // "amm": true,
943
+ // "taker": {
944
+ // "order": {
945
+ // "sender": "0x12a0b4888021576eb10a67616dd3dd3d9ce206b664656661756c740000000000",
946
+ // "price_x18": "27540000000000000000000",
947
+ // "amount": "2000000000000000000",
948
+ // "expiration": 4611686020107120000,
949
+ // "nonce": 1761322602510418000
950
+ // },
951
+ // "signature": "0x826c68f1a3f76d9ffbe8041f8d45e969d31f1ab6f2ae2f6379d1493e479e56436091d6cf4c72e212dd2f1d2fa17c627c4c21bd6d281c77172b8af030488478b71c"
952
+ // },
953
+ // "maker": {
954
+ // "order": {
955
+ // "sender": "0xf8d240d9514c9a4715d66268d7af3b53d619642564656661756c740000000000",
956
+ // "price_x18": "27540000000000000000000",
957
+ // "amount": "-724000000000000000",
958
+ // "expiration": 1679731656,
959
+ // "nonce": 1761322565506171000
960
+ // },
961
+ // "signature": "0xd8b6505b8d9b8c3cbfe793080976388035682c02a27893fb26b48a5b2bfe943f4162dea3a42e24e0dff5e2f74fbf77e33d83619140a2a581117c55e6cc236bdb1c"
962
+ // }
963
+ // }
964
+ // },
965
+ // "submission_idx": "563011",
966
+ // "timestamp": "1679728127"
967
+ // }
968
+ // ]
969
+ // }
970
+ //
971
+ const trades = this.safeList(response, 'matches', []);
972
+ return this.parseTrades(trades, market, since, limit, params);
973
+ }
974
+ async fetchOrderBook(symbol, limit = undefined, params = {}) {
975
+ /**
976
+ * @method
977
+ * @name vertex#fetchOrderBook
978
+ * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
979
+ * @see https://docs.vertexprotocol.com/developer-resources/api/v2/orderbook
980
+ * @param {string} symbol unified symbol of the market to fetch the order book for
981
+ * @param {int} [limit] the maximum amount of order book entries to return
982
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
983
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
984
+ */
985
+ await this.loadMarkets();
986
+ const market = this.market(symbol);
987
+ const marketId = market['baseId'] + '_USDC';
988
+ if (limit === undefined) {
989
+ limit = 100;
990
+ }
991
+ const request = {
992
+ 'ticker_id': marketId,
993
+ 'depth': limit,
994
+ };
995
+ const response = await this.v2GatewayGetOrderbook(this.extend(request, params));
996
+ //
997
+ // {
998
+ // "ticker_id": "ETH-PERP_USDC",
999
+ // "bids": [
1000
+ // [
1001
+ // 1612.3,
1002
+ // 0.31
1003
+ // ],
1004
+ // [
1005
+ // 1612.0,
1006
+ // 0.93
1007
+ // ],
1008
+ // [
1009
+ // 1611.5,
1010
+ // 1.55
1011
+ // ],
1012
+ // [
1013
+ // 1610.8,
1014
+ // 2.17
1015
+ // ]
1016
+ // ],
1017
+ // "asks": [
1018
+ // [
1019
+ // 1612.9,
1020
+ // 0.93
1021
+ // ],
1022
+ // [
1023
+ // 1613.4,
1024
+ // 1.55
1025
+ // ],
1026
+ // [
1027
+ // 1614.1,
1028
+ // 2.17
1029
+ // ]
1030
+ // ],
1031
+ // "timestamp": 1694375362016
1032
+ // }
1033
+ //
1034
+ const timestamp = this.safeInteger(response, 'timestamp');
1035
+ return this.parseOrderBook(response, symbol, timestamp, 'bids', 'asks');
1036
+ }
1037
+ async fetchTradingFees(params = {}) {
1038
+ /**
1039
+ * @method
1040
+ * @name vertex#fetchTradingFees
1041
+ * @description fetch the trading fees for multiple markets
1042
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/queries/fee-rates
1043
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1044
+ * @param {string} [params.user] user address, will default to this.walletAddress if not provided
1045
+ * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols
1046
+ */
1047
+ await this.loadMarkets();
1048
+ let userAddress = undefined;
1049
+ [userAddress, params] = this.handlePublicAddress('fetchTradingFees', params);
1050
+ const request = {
1051
+ 'type': 'fee_rates',
1052
+ 'sender': this.convertAddressToSender(userAddress),
1053
+ };
1054
+ const response = await this.v1GatewayGetQuery(this.extend(request, params));
1055
+ //
1056
+ // {
1057
+ // "status": "success",
1058
+ // "data": {
1059
+ // "taker_fee_rates_x18": [
1060
+ // "0",
1061
+ // "300000000000000",
1062
+ // "200000000000000",
1063
+ // "300000000000000",
1064
+ // "200000000000000"
1065
+ // ],
1066
+ // "maker_fee_rates_x18": [
1067
+ // "0",
1068
+ // "0",
1069
+ // "0",
1070
+ // "0",
1071
+ // "0"
1072
+ // ],
1073
+ // "liquidation_sequencer_fee": "250000000000000000",
1074
+ // "health_check_sequencer_fee": "100000000000000000",
1075
+ // "taker_sequencer_fee": "25000000000000000",
1076
+ // "withdraw_sequencer_fees": [
1077
+ // "10000000000000000",
1078
+ // "40000000000000",
1079
+ // "0",
1080
+ // "600000000000000",
1081
+ // "0"
1082
+ // ]
1083
+ // },
1084
+ // "request_type": "query_fee_rates",
1085
+ // }
1086
+ //
1087
+ const data = this.safeDict(response, 'data', {});
1088
+ const maker = this.safeList(data, 'maker_fee_rates_x18', []);
1089
+ const taker = this.safeList(data, 'taker_fee_rates_x18', []);
1090
+ const result = {};
1091
+ for (let i = 0; i < taker.length; i++) {
1092
+ const market = this.safeMarket(this.numberToString(i));
1093
+ if (market['id'] === undefined) {
1094
+ continue;
1095
+ }
1096
+ const symbol = market['symbol'];
1097
+ result[symbol] = {
1098
+ 'info': response,
1099
+ 'symbol': symbol,
1100
+ 'maker': this.parseNumber(this.convertFromX18(maker[i])),
1101
+ 'taker': this.parseNumber(this.convertFromX18(taker[i])),
1102
+ 'percentage': true,
1103
+ 'tierBased': false,
1104
+ };
1105
+ }
1106
+ return result;
1107
+ }
1108
+ parseOHLCV(ohlcv, market = undefined) {
1109
+ // example response in fetchOHLCV
1110
+ return [
1111
+ this.safeTimestamp(ohlcv, 'timestamp'),
1112
+ this.parseNumber(this.convertFromX18(this.safeString(ohlcv, 'open_x18'))),
1113
+ this.parseNumber(this.convertFromX18(this.safeString(ohlcv, 'high_x18'))),
1114
+ this.parseNumber(this.convertFromX18(this.safeString(ohlcv, 'low_x18'))),
1115
+ this.parseNumber(this.convertFromX18(this.safeString(ohlcv, 'close_x18'))),
1116
+ this.parseNumber(this.convertFromX18(this.safeString(ohlcv, 'volume'))),
1117
+ ];
1118
+ }
1119
+ async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
1120
+ /**
1121
+ * @method
1122
+ * @name vertex#fetchOHLCV
1123
+ * @see https://docs.vertexprotocol.com/developer-resources/api/archive-indexer/candlesticks
1124
+ * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
1125
+ * @param {string} symbol unified symbol of the market to fetch OHLCV data for
1126
+ * @param {string} timeframe the length of time each candle represents
1127
+ * @param {int} [since] timestamp in ms of the earliest candle to fetch
1128
+ * @param {int} [limit] max=1000, max=100 when since is defined and is less than (now - (999 * (timeframe in ms)))
1129
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1130
+ * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
1131
+ */
1132
+ await this.loadMarkets();
1133
+ const market = this.market(symbol);
1134
+ const ohlcvRequest = {
1135
+ 'product_id': this.parseToInt(market['id']),
1136
+ 'granularity': this.safeInteger(this.timeframes, timeframe),
1137
+ };
1138
+ const until = this.safeInteger(params, 'until');
1139
+ if (until !== undefined) {
1140
+ params = this.omit(params, 'until');
1141
+ ohlcvRequest['max_time'] = until;
1142
+ }
1143
+ if (limit !== undefined) {
1144
+ ohlcvRequest['limit'] = Math.min(limit, 1000);
1145
+ }
1146
+ const request = {
1147
+ 'candlesticks': ohlcvRequest,
1148
+ };
1149
+ const response = await this.v1ArchivePost(this.extend(request, params));
1150
+ //
1151
+ // {
1152
+ // "candlesticks": [
1153
+ // {
1154
+ // "product_id": 1,
1155
+ // "granularity": 60,
1156
+ // "submission_idx": "627709",
1157
+ // "timestamp": "1680118140",
1158
+ // "open_x18": "27235000000000000000000",
1159
+ // "high_x18": "27298000000000000000000",
1160
+ // "low_x18": "27235000000000000000000",
1161
+ // "close_x18": "27298000000000000000000",
1162
+ // "volume": "1999999999999999998"
1163
+ // },
1164
+ // {
1165
+ // "product_id": 1,
1166
+ // "granularity": 60,
1167
+ // "submission_idx": "627699",
1168
+ // "timestamp": "1680118080",
1169
+ // "open_x18": "27218000000000000000000",
1170
+ // "high_x18": "27245000000000000000000",
1171
+ // "low_x18": "27218000000000000000000",
1172
+ // "close_x18": "27245000000000000000000",
1173
+ // "volume": "11852999999999999995"
1174
+ // }
1175
+ // ]
1176
+ // }
1177
+ //
1178
+ const rows = this.safeList(response, 'candlesticks', []);
1179
+ return this.parseOHLCVs(rows, market, timeframe, since, limit);
1180
+ }
1181
+ parseFundingRate(ticker, market = undefined) {
1182
+ //
1183
+ // {
1184
+ // "product_id": 4,
1185
+ // "funding_rate_x18": "2447900598160952",
1186
+ // "update_time": "1680116326"
1187
+ // }
1188
+ //
1189
+ // {
1190
+ // "ETH-PERP_USDC": {
1191
+ // "ticker_id": "ETH-PERP_USDC",
1192
+ // "base_currency": "ETH-PERP",
1193
+ // "quote_currency": "USDC",
1194
+ // "last_price": 1620.3,
1195
+ // "base_volume": 1309.2,
1196
+ // "quote_volume": 2117828.093867611,
1197
+ // "product_type": "perpetual",
1198
+ // "contract_price": 1620.372642114429,
1199
+ // "contract_price_currency": "USD",
1200
+ // "open_interest": 1635.2,
1201
+ // "open_interest_usd": 2649633.3443855145,
1202
+ // "index_price": 1623.293496279935,
1203
+ // "mark_price": 1623.398589416731,
1204
+ // "funding_rate": 0.000068613217104332,
1205
+ // "next_funding_rate_timestamp": 1694379600,
1206
+ // "price_change_percent_24h": -0.6348599635253989
1207
+ // }
1208
+ // }
1209
+ //
1210
+ let fundingRate = this.safeNumber(ticker, 'funding_rate');
1211
+ if (fundingRate === undefined) {
1212
+ const fundingRateX18 = this.safeString(ticker, 'funding_rate_x18');
1213
+ fundingRate = this.parseNumber(this.convertFromX18(fundingRateX18));
1214
+ }
1215
+ const fundingTimestamp = this.safeTimestamp2(ticker, 'update_time', 'next_funding_rate_timestamp');
1216
+ const markPrice = this.safeNumber(ticker, 'mark_price');
1217
+ const indexPrice = this.safeNumber(ticker, 'index_price');
1218
+ return {
1219
+ 'info': ticker,
1220
+ 'symbol': market['symbol'],
1221
+ 'markPrice': markPrice,
1222
+ 'indexPrice': indexPrice,
1223
+ 'interestRate': undefined,
1224
+ 'estimatedSettlePrice': undefined,
1225
+ 'timestamp': undefined,
1226
+ 'datetime': undefined,
1227
+ 'fundingRate': fundingRate,
1228
+ 'fundingTimestamp': fundingTimestamp,
1229
+ 'fundingDatetime': this.iso8601(fundingTimestamp),
1230
+ 'nextFundingRate': undefined,
1231
+ 'nextFundingTimestamp': undefined,
1232
+ 'nextFundingDatetime': undefined,
1233
+ 'previousFundingRate': undefined,
1234
+ 'previousFundingTimestamp': undefined,
1235
+ 'previousFundingDatetime': undefined,
1236
+ };
1237
+ }
1238
+ async fetchFundingRate(symbol, params = {}) {
1239
+ /**
1240
+ * @method
1241
+ * @name vertex#fetchFundingRate
1242
+ * @description fetch the current funding rate
1243
+ * @see https://docs.vertexprotocol.com/developer-resources/api/archive-indexer/funding-rate
1244
+ * @param {string} symbol unified market symbol
1245
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1246
+ * @returns {object} a [funding rate structure]{@link https://docs.ccxt.com/#/?id=funding-rate-structure}
1247
+ */
1248
+ await this.loadMarkets();
1249
+ const market = this.market(symbol);
1250
+ const request = {
1251
+ 'funding_rate': {
1252
+ 'product_id': this.parseToInt(market['id']),
1253
+ },
1254
+ };
1255
+ const response = await this.v1ArchivePost(this.extend(request, params));
1256
+ //
1257
+ // {
1258
+ // "product_id": 4,
1259
+ // "funding_rate_x18": "2447900598160952",
1260
+ // "update_time": "1680116326"
1261
+ // }
1262
+ //
1263
+ return this.parseFundingRate(response, market);
1264
+ }
1265
+ async fetchFundingRates(symbols = undefined, params = {}) {
1266
+ /**
1267
+ * @method
1268
+ * @name vertex#fetchFundingRates
1269
+ * @description fetches funding rates for multiple markets
1270
+ * @see https://docs.vertexprotocol.com/developer-resources/api/v2/contracts
1271
+ * @param {string[]} symbols unified symbols of the markets to fetch the funding rates for, all market funding rates are returned if not assigned
1272
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1273
+ * @returns {object} an array of [funding rate structures]{@link https://docs.ccxt.com/#/?id=funding-rate-structure}
1274
+ */
1275
+ await this.loadMarkets();
1276
+ const request = {};
1277
+ if (symbols !== undefined) {
1278
+ symbols = this.marketSymbols(symbols);
1279
+ }
1280
+ const response = await this.v2ArchiveGetContracts(this.extend(request, params));
1281
+ //
1282
+ // {
1283
+ // "ETH-PERP_USDC": {
1284
+ // "ticker_id": "ETH-PERP_USDC",
1285
+ // "base_currency": "ETH-PERP",
1286
+ // "quote_currency": "USDC",
1287
+ // "last_price": 1620.3,
1288
+ // "base_volume": 1309.2,
1289
+ // "quote_volume": 2117828.093867611,
1290
+ // "product_type": "perpetual",
1291
+ // "contract_price": 1620.372642114429,
1292
+ // "contract_price_currency": "USD",
1293
+ // "open_interest": 1635.2,
1294
+ // "open_interest_usd": 2649633.3443855145,
1295
+ // "index_price": 1623.293496279935,
1296
+ // "mark_price": 1623.398589416731,
1297
+ // "funding_rate": 0.000068613217104332,
1298
+ // "next_funding_rate_timestamp": 1694379600,
1299
+ // "price_change_percent_24h": -0.6348599635253989
1300
+ // }
1301
+ // }
1302
+ //
1303
+ const keys = Object.keys(response);
1304
+ const fundingRates = {};
1305
+ for (let i = 0; i < keys.length; i++) {
1306
+ const tickerId = keys[i];
1307
+ const parsedTickerId = tickerId.split('-');
1308
+ const data = response[tickerId];
1309
+ const marketId = parsedTickerId[0] + '/USDC:USDC';
1310
+ const market = this.market(marketId);
1311
+ const ticker = this.parseFundingRate(data, market);
1312
+ const symbol = ticker['symbol'];
1313
+ fundingRates[symbol] = ticker;
1314
+ }
1315
+ return this.filterByArray(fundingRates, 'symbol', symbols);
1316
+ }
1317
+ parseOpenInterest(interest, market = undefined) {
1318
+ //
1319
+ // {
1320
+ // "ETH-PERP_USDC": {
1321
+ // "ticker_id": "ETH-PERP_USDC",
1322
+ // "base_currency": "ETH-PERP",
1323
+ // "quote_currency": "USDC",
1324
+ // "last_price": 1620.3,
1325
+ // "base_volume": 1309.2,
1326
+ // "quote_volume": 2117828.093867611,
1327
+ // "product_type": "perpetual",
1328
+ // "contract_price": 1620.372642114429,
1329
+ // "contract_price_currency": "USD",
1330
+ // "open_interest": 1635.2,
1331
+ // "open_interest_usd": 2649633.3443855145,
1332
+ // "index_price": 1623.293496279935,
1333
+ // "mark_price": 1623.398589416731,
1334
+ // "funding_rate": 0.000068613217104332,
1335
+ // "next_funding_rate_timestamp": 1694379600,
1336
+ // "price_change_percent_24h": -0.6348599635253989
1337
+ // }
1338
+ // }
1339
+ //
1340
+ const value = this.safeNumber(interest, 'open_interest_usd');
1341
+ return this.safeOpenInterest({
1342
+ 'symbol': market['symbol'],
1343
+ 'openInterestAmount': undefined,
1344
+ 'openInterestValue': value,
1345
+ 'timestamp': undefined,
1346
+ 'datetime': undefined,
1347
+ 'info': interest,
1348
+ }, market);
1349
+ }
1350
+ async fetchOpenInterest(symbol, params = {}) {
1351
+ /**
1352
+ * @method
1353
+ * @name vertex#fetchOpenInterest
1354
+ * @description Retrieves the open interest of a derivative trading pair
1355
+ * @see https://docs.vertexprotocol.com/developer-resources/api/v2/contracts
1356
+ * @param {string} symbol Unified CCXT market symbol
1357
+ * @param {object} [params] exchange specific parameters
1358
+ * @returns {object} an open interest structure{@link https://docs.ccxt.com/#/?id=open-interest-structure}
1359
+ */
1360
+ await this.loadMarkets();
1361
+ const market = this.market(symbol);
1362
+ if (!market['contract']) {
1363
+ throw new BadRequest(this.id + ' fetchOpenInterest() supports contract markets only');
1364
+ }
1365
+ const request = {};
1366
+ const response = await this.v2ArchiveGetContracts(this.extend(request, params));
1367
+ //
1368
+ // {
1369
+ // "ETH-PERP_USDC": {
1370
+ // "ticker_id": "ETH-PERP_USDC",
1371
+ // "base_currency": "ETH-PERP",
1372
+ // "quote_currency": "USDC",
1373
+ // "last_price": 1620.3,
1374
+ // "base_volume": 1309.2,
1375
+ // "quote_volume": 2117828.093867611,
1376
+ // "product_type": "perpetual",
1377
+ // "contract_price": 1620.372642114429,
1378
+ // "contract_price_currency": "USD",
1379
+ // "open_interest": 1635.2,
1380
+ // "open_interest_usd": 2649633.3443855145,
1381
+ // "index_price": 1623.293496279935,
1382
+ // "mark_price": 1623.398589416731,
1383
+ // "funding_rate": 0.000068613217104332,
1384
+ // "next_funding_rate_timestamp": 1694379600,
1385
+ // "price_change_percent_24h": -0.6348599635253989
1386
+ // }
1387
+ // }
1388
+ //
1389
+ const tickerId = market['base'] + '_USDC';
1390
+ const openInterest = this.safeDict(response, tickerId, {});
1391
+ return this.parseOpenInterest(openInterest, market);
1392
+ }
1393
+ parseTicker(ticker, market = undefined) {
1394
+ //
1395
+ // {
1396
+ // "ticker_id": "BTC_USDC",
1397
+ // "base_currency": "BTC",
1398
+ // "quote_currency": "USDC",
1399
+ // "last_price": 25728.0,
1400
+ // "base_volume": 552.048,
1401
+ // "quote_volume": 14238632.207250029,
1402
+ // "price_change_percent_24h": -0.6348599635253989
1403
+ // }
1404
+ //
1405
+ const base = this.safeString(ticker, 'base_currency');
1406
+ const quote = this.safeString(ticker, 'quote_currency');
1407
+ let marketId = base + '/' + quote;
1408
+ if (base.indexOf('PERP') > 0) {
1409
+ marketId = marketId.replace('-PERP', '') + ':USDC';
1410
+ }
1411
+ market = this.market(marketId);
1412
+ const last = this.safeString(ticker, 'last_price');
1413
+ return this.safeTicker({
1414
+ 'symbol': market['symbol'],
1415
+ 'timestamp': undefined,
1416
+ 'datetime': undefined,
1417
+ 'high': undefined,
1418
+ 'low': undefined,
1419
+ 'bid': undefined,
1420
+ 'bidVolume': undefined,
1421
+ 'ask': undefined,
1422
+ 'askVolume': undefined,
1423
+ 'vwap': undefined,
1424
+ 'open': undefined,
1425
+ 'close': last,
1426
+ 'last': last,
1427
+ 'previousClose': undefined,
1428
+ 'change': undefined,
1429
+ 'percentage': this.safeString(ticker, 'price_change_percent_24h'),
1430
+ 'average': undefined,
1431
+ 'baseVolume': this.safeString(ticker, 'base_volume'),
1432
+ 'quoteVolume': this.safeString(ticker, 'quote_volume'),
1433
+ 'info': ticker,
1434
+ }, market);
1435
+ }
1436
+ async fetchTickers(symbols = undefined, params = {}) {
1437
+ /**
1438
+ * @method
1439
+ * @name vertex#fetchTickers
1440
+ * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
1441
+ * @see https://docs.vertexprotocol.com/developer-resources/api/v2/tickers
1442
+ * @param {string[]} [symbols] unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
1443
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1444
+ * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
1445
+ */
1446
+ await this.loadMarkets();
1447
+ symbols = this.marketSymbols(symbols, undefined, true, true, true);
1448
+ const request = {};
1449
+ const response = await this.v2ArchiveGetTickers(this.extend(request, params));
1450
+ //
1451
+ // {
1452
+ // "ETH_USDC": {
1453
+ // "ticker_id": "ETH_USDC",
1454
+ // "base_currency": "ETH",
1455
+ // "quote_currency": "USDC",
1456
+ // "last_price": 1619.1,
1457
+ // "base_volume": 1428.32,
1458
+ // "quote_volume": 2310648.316391866,
1459
+ // "price_change_percent_24h": -1.0509394462969588
1460
+ // },
1461
+ // "BTC_USDC": {
1462
+ // "ticker_id": "BTC_USDC",
1463
+ // "base_currency": "BTC",
1464
+ // "quote_currency": "USDC",
1465
+ // "last_price": 25728.0,
1466
+ // "base_volume": 552.048,
1467
+ // "quote_volume": 14238632.207250029,
1468
+ // "price_change_percent_24h": -0.6348599635253989
1469
+ // }
1470
+ // }
1471
+ //
1472
+ const tickers = Object.values(response);
1473
+ return this.parseTickers(tickers, symbols);
1474
+ }
1475
+ async queryContracts(params = {}) {
1476
+ // query contract addresses for sending order
1477
+ const cachedContracts = this.safeDict(this.options, 'v1contracts');
1478
+ if (cachedContracts !== undefined) {
1479
+ return cachedContracts;
1480
+ }
1481
+ const request = {
1482
+ 'type': 'contracts',
1483
+ };
1484
+ const response = await this.v1GatewayGetQuery(this.extend(request, params));
1485
+ const data = this.safeDict(response, 'data', {});
1486
+ this.options['v1contracts'] = data;
1487
+ return data;
1488
+ }
1489
+ nonce() {
1490
+ return this.milliseconds() - this.options['timeDifference'];
1491
+ }
1492
+ hashMessage(message) {
1493
+ return '0x' + this.hash(message, keccak, 'hex');
1494
+ }
1495
+ signHash(hash, privateKey) {
1496
+ const signature = ecdsa(hash.slice(-64), privateKey.slice(-64), secp256k1, undefined);
1497
+ const r = signature['r'];
1498
+ const s = signature['s'];
1499
+ const v = this.intToBase16(this.sum(27, signature['v']));
1500
+ return '0x' + r.padStart(64, '0') + s.padStart(64, '0') + v;
1501
+ }
1502
+ signMessage(message, privateKey) {
1503
+ return this.signHash(this.hashMessage(message), privateKey.slice(-64));
1504
+ }
1505
+ buildSig(chainId, messageTypes, message, verifyingContractAddress = '') {
1506
+ const domain = {
1507
+ 'chainId': chainId,
1508
+ 'name': 'Vertex',
1509
+ 'verifyingContract': verifyingContractAddress,
1510
+ 'version': '0.0.1',
1511
+ };
1512
+ const msg = this.ethEncodeStructuredData(domain, messageTypes, message);
1513
+ const signature = this.signMessage(msg, this.privateKey);
1514
+ return signature;
1515
+ }
1516
+ buildCreateOrderSig(message, chainId, verifyingContractAddress) {
1517
+ const messageTypes = {
1518
+ 'Order': [
1519
+ { 'name': 'sender', 'type': 'bytes32' },
1520
+ { 'name': 'priceX18', 'type': 'int128' },
1521
+ { 'name': 'amount', 'type': 'int128' },
1522
+ { 'name': 'expiration', 'type': 'uint64' },
1523
+ { 'name': 'nonce', 'type': 'uint64' },
1524
+ ],
1525
+ };
1526
+ return this.buildSig(chainId, messageTypes, message, verifyingContractAddress);
1527
+ }
1528
+ buildListTriggerTxSig(message, chainId, verifyingContractAddress) {
1529
+ const messageTypes = {
1530
+ 'ListTriggerOrders': [
1531
+ { 'name': 'sender', 'type': 'bytes32' },
1532
+ { 'name': 'recvTime', 'type': 'uint64' },
1533
+ ],
1534
+ };
1535
+ return this.buildSig(chainId, messageTypes, message, verifyingContractAddress);
1536
+ }
1537
+ buildCancelAllOrdersSig(message, chainId, verifyingContractAddress) {
1538
+ const messageTypes = {
1539
+ 'CancellationProducts': [
1540
+ { 'name': 'sender', 'type': 'bytes32' },
1541
+ { 'name': 'productIds', 'type': 'uint32[]' },
1542
+ { 'name': 'nonce', 'type': 'uint64' },
1543
+ ],
1544
+ };
1545
+ return this.buildSig(chainId, messageTypes, message, verifyingContractAddress);
1546
+ }
1547
+ buildCancelOrdersSig(message, chainId, verifyingContractAddress) {
1548
+ const messageTypes = {
1549
+ 'Cancellation': [
1550
+ { 'name': 'sender', 'type': 'bytes32' },
1551
+ { 'name': 'productIds', 'type': 'uint32[]' },
1552
+ { 'name': 'digests', 'type': 'bytes32[]' },
1553
+ { 'name': 'nonce', 'type': 'uint64' },
1554
+ ],
1555
+ };
1556
+ return this.buildSig(chainId, messageTypes, message, verifyingContractAddress);
1557
+ }
1558
+ buildWithdrawSig(message, chainId, verifyingContractAddress) {
1559
+ const messageTypes = {
1560
+ 'WithdrawCollateral': [
1561
+ { 'name': 'sender', 'type': 'bytes32' },
1562
+ { 'name': 'productId', 'type': 'uint32' },
1563
+ { 'name': 'amount', 'type': 'uint128' },
1564
+ { 'name': 'nonce', 'type': 'uint64' },
1565
+ ],
1566
+ };
1567
+ return this.buildSig(chainId, messageTypes, message, verifyingContractAddress);
1568
+ }
1569
+ convertAddressToSender(address) {
1570
+ const sender = address + '64656661756c74';
1571
+ return sender.padEnd(66, '0');
1572
+ }
1573
+ getNonce(now, expiration) {
1574
+ if (now === undefined) {
1575
+ now = this.nonce();
1576
+ }
1577
+ // nonce = ((now + expiration) << 20) + 1000
1578
+ // 1 << 20 = 1048576
1579
+ return Precise.stringAdd(Precise.stringMul(Precise.stringAdd(this.numberToString(now), this.numberToString(expiration)), '1048576'), '1000');
1580
+ }
1581
+ getExpiration(now, timeInForce, postOnly, reduceOnly) {
1582
+ let expiration = Precise.stringAdd(this.numberToString(now), '86400');
1583
+ if (timeInForce === 'ioc') {
1584
+ // 1 << 62 = 4611686018427387904
1585
+ expiration = Precise.stringOr(expiration, '4611686018427387904');
1586
+ }
1587
+ else if (timeInForce === 'fok') {
1588
+ // 2 << 62 = 9223372036854775808
1589
+ expiration = Precise.stringOr(expiration, '9223372036854775808');
1590
+ }
1591
+ else if (postOnly) {
1592
+ // 3 << 62 = 13835058055282163712
1593
+ expiration = Precise.stringOr(expiration, '13835058055282163712');
1594
+ }
1595
+ if (reduceOnly) {
1596
+ // 1 << 61 = 2305843009213693952
1597
+ expiration = Precise.stringOr(expiration, '2305843009213693952');
1598
+ }
1599
+ return expiration;
1600
+ }
1601
+ getAmount(amount, side) {
1602
+ let amountString = this.numberToString(amount);
1603
+ if (side === 'sell') {
1604
+ if (amount > 0) {
1605
+ // amount *= -1;
1606
+ amountString = Precise.stringMul(amountString, '-1');
1607
+ }
1608
+ }
1609
+ else {
1610
+ if (amount < 0) {
1611
+ // amount *= -1;
1612
+ amountString = Precise.stringMul(amountString, '-1');
1613
+ }
1614
+ }
1615
+ return amountString;
1616
+ }
1617
+ async createOrder(symbol, type, side, amount, price = undefined, params = {}) {
1618
+ /**
1619
+ * @method
1620
+ * @name vertex#createOrder
1621
+ * @description create a trade order
1622
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/executes/place-order
1623
+ * @see https://docs.vertexprotocol.com/developer-resources/api/trigger/executes/place-order
1624
+ * @param {string} symbol unified symbol of the market to create an order in
1625
+ * @param {string} type 'market' or 'limit'
1626
+ * @param {string} side 'buy' or 'sell'
1627
+ * @param {float} amount how much of currency you want to trade in units of base currency
1628
+ * @param {float} [price] the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
1629
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1630
+ * @param {string} [params.timeInForce] ioc, fok
1631
+ * @param {bool} [params.postOnly] true or false whether the order is post-only
1632
+ * @param {bool} [params.reduceOnly] true or false whether the order is reduce-only, only works for ioc and fok order
1633
+ * @param {float} [params.triggerPrice] The price at which a trigger order is triggered at
1634
+ * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
1635
+ */
1636
+ this.checkRequiredCredentials();
1637
+ const marketType = type.toLowerCase();
1638
+ const isMarketOrder = marketType === 'market';
1639
+ if (isMarketOrder && price === undefined) {
1640
+ throw new ArgumentsRequired(this.id + ' createOrder() requires a price argument for market order');
1641
+ }
1642
+ await this.loadMarkets();
1643
+ const market = this.market(symbol);
1644
+ const marketId = this.parseToInt(market['id']);
1645
+ const contracts = await this.queryContracts();
1646
+ const chainId = this.safeString(contracts, 'chain_id');
1647
+ const bookAddresses = this.safeList(contracts, 'book_addrs', []);
1648
+ const verifyingContractAddress = this.safeString(bookAddresses, marketId);
1649
+ const defaultTimeInForce = (isMarketOrder) ? 'fok' : undefined;
1650
+ const timeInForce = this.safeStringLower(params, 'timeInForce', defaultTimeInForce);
1651
+ const postOnly = this.safeBool(params, 'postOnly', false);
1652
+ const reduceOnly = this.safeBool(params, 'reduceOnly', false);
1653
+ const triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice');
1654
+ const stopLossPrice = this.safeString(params, 'stopLossPrice', triggerPrice);
1655
+ const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
1656
+ const isTrigger = (stopLossPrice || takeProfitPrice);
1657
+ const now = this.nonce();
1658
+ let nonce = this.getNonce(now, 90000);
1659
+ if (postOnly && reduceOnly) {
1660
+ throw new NotSupported(this.id + ' reduceOnly not supported when postOnly is enabled');
1661
+ }
1662
+ const expiration = this.getExpiration(now, timeInForce, postOnly, reduceOnly);
1663
+ if (isTrigger) {
1664
+ // 1 << 63 = 9223372036854775808
1665
+ nonce = Precise.stringOr(nonce, '9223372036854775808');
1666
+ }
1667
+ const amountString = this.getAmount(amount, side);
1668
+ const order = {
1669
+ 'sender': this.convertAddressToSender(this.walletAddress),
1670
+ 'priceX18': this.convertToX18(this.priceToPrecision(symbol, price)),
1671
+ 'amount': this.convertToX18(this.amountToPrecision(symbol, amountString)),
1672
+ 'expiration': expiration,
1673
+ 'nonce': nonce,
1674
+ };
1675
+ const request = {
1676
+ 'place_order': {
1677
+ 'product_id': marketId,
1678
+ 'order': {
1679
+ 'sender': order['sender'],
1680
+ 'priceX18': order['priceX18'],
1681
+ 'amount': order['amount'],
1682
+ 'expiration': this.numberToString(order['expiration']),
1683
+ 'nonce': order['nonce'],
1684
+ },
1685
+ 'signature': this.buildCreateOrderSig(order, chainId, verifyingContractAddress),
1686
+ 'id': this.safeInteger(this.options, 'brokerId', 5930043274845996),
1687
+ },
1688
+ };
1689
+ params = this.omit(params, ['timeInForce', 'reduceOnly', 'postOnly', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice']);
1690
+ let response = undefined;
1691
+ if (isTrigger) {
1692
+ const trigger = {};
1693
+ if (stopLossPrice !== undefined) {
1694
+ trigger['last_price_below'] = this.convertToX18(stopLossPrice);
1695
+ }
1696
+ else if (takeProfitPrice !== undefined) {
1697
+ trigger['last_price_above'] = this.convertToX18(takeProfitPrice);
1698
+ }
1699
+ request['place_order']['trigger'] = trigger;
1700
+ response = await this.v1TriggerPostExecute(this.extend(request, params));
1701
+ }
1702
+ else {
1703
+ response = await this.v1GatewayPostExecute(this.extend(request, params));
1704
+ }
1705
+ //
1706
+ // {
1707
+ // "status": "success",
1708
+ // "signature": {signature},
1709
+ // "data": {
1710
+ // "digest": {order digest}
1711
+ // },
1712
+ // "request_type": "execute_place_order"
1713
+ // "id": 100
1714
+ // }
1715
+ //
1716
+ const data = this.safeDict(response, 'data', {});
1717
+ return this.safeOrder({
1718
+ 'id': this.safeString(data, 'digest'),
1719
+ });
1720
+ }
1721
+ async editOrder(id, symbol, type, side, amount = undefined, price = undefined, params = {}) {
1722
+ /**
1723
+ * @method
1724
+ * @name vertex#editOrder
1725
+ * @description edit a trade order
1726
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/executes/cancel-and-place
1727
+ * @param {string} id cancel order id
1728
+ * @param {string} symbol unified symbol of the market to create an order in
1729
+ * @param {string} type 'market' or 'limit'
1730
+ * @param {string} side 'buy' or 'sell'
1731
+ * @param {float} amount how much of currency you want to trade in units of base currency
1732
+ * @param {float} [price] the price at which the order is to be fullfilled, in units of the base currency, ignored in market orders
1733
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1734
+ * @param {string} [params.timeInForce] ioc, fok
1735
+ * @param {bool} [params.postOnly] true or false whether the order is post-only
1736
+ * @param {bool} [params.reduceOnly] true or false whether the order is reduce-only, only works for ioc and fok order
1737
+ * @param {float} [params.triggerPrice] The price at which a trigger order is triggered at
1738
+ * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
1739
+ */
1740
+ this.checkRequiredCredentials();
1741
+ const marketType = type.toLowerCase();
1742
+ const isMarketOrder = marketType === 'market';
1743
+ if (isMarketOrder && price === undefined) {
1744
+ throw new ArgumentsRequired(this.id + ' editOrder() requires a price argument for market order');
1745
+ }
1746
+ await this.loadMarkets();
1747
+ const market = this.market(symbol);
1748
+ const marketId = this.parseToInt(market['id']);
1749
+ const defaultTimeInForce = (isMarketOrder) ? 'fok' : undefined;
1750
+ const timeInForce = this.safeStringLower(params, 'timeInForce', defaultTimeInForce);
1751
+ const postOnly = this.safeBool(params, 'postOnly', false);
1752
+ const reduceOnly = this.safeBool(params, 'reduceOnly', false);
1753
+ const triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice');
1754
+ const stopLossPrice = this.safeString(params, 'stopLossPrice', triggerPrice);
1755
+ const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
1756
+ const isTrigger = (stopLossPrice || takeProfitPrice);
1757
+ const contracts = await this.queryContracts();
1758
+ const chainId = this.safeString(contracts, 'chain_id');
1759
+ const bookAddresses = this.safeList(contracts, 'book_addrs', []);
1760
+ const verifyingContractAddressOrder = this.safeString(bookAddresses, marketId);
1761
+ const verifyingContractAddressCancel = this.safeString(contracts, 'endpoint_addr');
1762
+ const now = this.nonce();
1763
+ const nonce = this.getNonce(now, 90000);
1764
+ const sender = this.convertAddressToSender(this.walletAddress);
1765
+ if (postOnly && reduceOnly) {
1766
+ throw new NotSupported(this.id + ' reduceOnly not supported when postOnly is enabled');
1767
+ }
1768
+ if (isTrigger) {
1769
+ throw new NotSupported(this.id + ' editOrder() not supported for trigger order');
1770
+ }
1771
+ const expiration = this.getExpiration(now, timeInForce, postOnly, reduceOnly);
1772
+ const amountString = this.getAmount(amount, side);
1773
+ const order = {
1774
+ 'sender': sender,
1775
+ 'priceX18': this.convertToX18(this.priceToPrecision(symbol, price)),
1776
+ 'amount': this.convertToX18(this.amountToPrecision(symbol, amountString)),
1777
+ 'expiration': expiration,
1778
+ 'nonce': nonce,
1779
+ };
1780
+ const cancels = {
1781
+ 'sender': sender,
1782
+ 'productIds': [marketId],
1783
+ 'digests': [id],
1784
+ 'nonce': nonce,
1785
+ };
1786
+ const request = {
1787
+ 'cancel_and_place': {
1788
+ 'cancel_tx': {
1789
+ 'sender': cancels['sender'],
1790
+ 'productIds': cancels['productIds'],
1791
+ 'digests': cancels['digests'],
1792
+ 'nonce': this.numberToString(cancels['nonce']),
1793
+ },
1794
+ 'cancel_signature': this.buildCancelOrdersSig(cancels, chainId, verifyingContractAddressCancel),
1795
+ 'place_order': {
1796
+ 'product_id': marketId,
1797
+ 'order': {
1798
+ 'sender': order['sender'],
1799
+ 'priceX18': order['priceX18'],
1800
+ 'amount': order['amount'],
1801
+ 'expiration': this.numberToString(order['expiration']),
1802
+ 'nonce': order['nonce'],
1803
+ },
1804
+ 'signature': this.buildCreateOrderSig(order, chainId, verifyingContractAddressOrder),
1805
+ 'id': this.safeInteger(this.options, 'brokerId', 5930043274845996),
1806
+ },
1807
+ },
1808
+ };
1809
+ params = this.omit(params, ['timeInForce', 'reduceOnly', 'postOnly', 'triggerPrice', 'stopPrice', 'stopLossPrice', 'takeProfitPrice']);
1810
+ const response = await this.v1GatewayPostExecute(this.extend(request, params));
1811
+ //
1812
+ // {
1813
+ // "status": "success",
1814
+ // "signature": {signature},
1815
+ // "data": {
1816
+ // "digest": {order digest}
1817
+ // },
1818
+ // "request_type": "execute_cancel_and_place"
1819
+ // }
1820
+ //
1821
+ const data = this.safeDict(response, 'data', {});
1822
+ return this.safeOrder({
1823
+ 'id': this.safeString(data, 'digest'),
1824
+ });
1825
+ }
1826
+ parseOrderStatus(status) {
1827
+ if (status !== undefined) {
1828
+ const statuses = {
1829
+ 'pending': 'open',
1830
+ };
1831
+ if (typeof status === 'string') {
1832
+ return this.safeString(statuses, status, status);
1833
+ }
1834
+ const statusCancelled = this.safeDict(status, 'cancelled');
1835
+ if (statusCancelled !== undefined) {
1836
+ return 'canceled';
1837
+ }
1838
+ const statusTriggered = this.safeDict(status, 'triggered', {});
1839
+ const triggeredStatus = this.safeString(statusTriggered, 'status', 'failure');
1840
+ if (triggeredStatus === 'success') {
1841
+ return 'closed';
1842
+ }
1843
+ return 'canceled';
1844
+ }
1845
+ return status;
1846
+ }
1847
+ parseOrder(order, market = undefined) {
1848
+ //
1849
+ // {
1850
+ // "product_id": 1,
1851
+ // "sender": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43000000000000000000000000",
1852
+ // "price_x18": "1000000000000000000",
1853
+ // "amount": "1000000000000000000",
1854
+ // "expiration": "2000000000",
1855
+ // "nonce": "1",
1856
+ // "unfilled_amount": "1000000000000000000",
1857
+ // "digest": "0x0000000000000000000000000000000000000000000000000000000000000000",
1858
+ // "placed_at": 1681951347,
1859
+ // "order_type": "ioc"
1860
+ // }
1861
+ // stop order
1862
+ // {
1863
+ // "order": {
1864
+ // "order": {
1865
+ // "sender": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43000000000000000000000000",
1866
+ // "priceX18": "1000000000000000000",
1867
+ // "amount": "1000000000000000000",
1868
+ // "expiration": "2000000000",
1869
+ // "nonce": "1",
1870
+ // },
1871
+ // "signature": "0x...",
1872
+ // "product_id": 1,
1873
+ // "spot_leverage": true,
1874
+ // "trigger": {
1875
+ // "price_above": "1000000000000000000"
1876
+ // },
1877
+ // "digest": "0x..."
1878
+ // },
1879
+ // "status": "pending",
1880
+ // "updated_at": 1688768157050
1881
+ // }
1882
+ //
1883
+ let marketId = this.safeString(order, 'product_id');
1884
+ let timestamp = this.safeTimestamp(order, 'placed_at');
1885
+ let amount = this.safeString(order, 'amount');
1886
+ let price = this.safeString(order, 'price_x18');
1887
+ const remaining = this.safeString(order, 'unfilled_amount');
1888
+ let triggerPriceNum = undefined;
1889
+ const status = this.safeValue(order, 'status');
1890
+ if (status !== undefined) {
1891
+ // trigger order
1892
+ const outerOrder = this.safeDict(order, 'order', {});
1893
+ const innerOrder = this.safeDict(outerOrder, 'order', {});
1894
+ marketId = this.safeString(outerOrder, 'product_id');
1895
+ amount = this.safeString(innerOrder, 'amount');
1896
+ price = this.safeString(innerOrder, 'priceX18');
1897
+ timestamp = this.safeTimestamp(order, 'updated_at');
1898
+ const trigger = this.safeDict(outerOrder, 'trigger', {});
1899
+ const triggerPrice = this.safeStringN(trigger, ['price_above', 'price_below', 'last_price_above', 'last_price_below']);
1900
+ if (triggerPrice !== undefined) {
1901
+ triggerPriceNum = this.parseToNumeric(this.convertFromX18(triggerPrice));
1902
+ }
1903
+ }
1904
+ market = this.safeMarket(marketId, market);
1905
+ const symbol = market['symbol'];
1906
+ let priceNum = undefined;
1907
+ if (price !== undefined) {
1908
+ priceNum = this.parseToNumeric(this.convertFromX18(price));
1909
+ }
1910
+ let amountNum = undefined;
1911
+ if (amount !== undefined) {
1912
+ amountNum = this.parseToNumeric(this.convertFromX18(amount));
1913
+ }
1914
+ let remainingNum = undefined;
1915
+ if (remaining !== undefined) {
1916
+ remainingNum = this.parseToNumeric(this.convertFromX18(remaining));
1917
+ }
1918
+ let side = undefined;
1919
+ if (amountNum !== undefined && remainingNum !== undefined) {
1920
+ side = (amountNum < 0 || remainingNum < 0) ? 'sell' : 'buy';
1921
+ }
1922
+ const tif = this.parseTimeInForce(this.safeString(order, 'order_type'));
1923
+ const isPostOnly = (tif === 'PO');
1924
+ return this.safeOrder({
1925
+ 'info': order,
1926
+ 'id': this.safeString(order, 'digest'),
1927
+ 'clientOrderId': undefined,
1928
+ 'timestamp': timestamp,
1929
+ 'datetime': this.iso8601(timestamp),
1930
+ 'lastTradeTimestamp': undefined,
1931
+ 'lastUpdateTimestamp': undefined,
1932
+ 'symbol': symbol,
1933
+ 'type': undefined,
1934
+ 'timeInForce': tif,
1935
+ 'postOnly': isPostOnly,
1936
+ 'reduceOnly': undefined,
1937
+ 'side': side,
1938
+ 'price': priceNum,
1939
+ 'triggerPrice': triggerPriceNum,
1940
+ 'amount': amountNum,
1941
+ 'cost': undefined,
1942
+ 'average': undefined,
1943
+ 'filled': undefined,
1944
+ 'remaining': remainingNum,
1945
+ 'status': this.parseOrderStatus(status),
1946
+ 'fee': undefined,
1947
+ 'trades': undefined,
1948
+ }, market);
1949
+ }
1950
+ parseTimeInForce(timeInForce) {
1951
+ const timeInForces = {
1952
+ 'POST_ONLY': 'PO',
1953
+ };
1954
+ return this.safeStringUpper(timeInForces, timeInForce, timeInForce);
1955
+ }
1956
+ async fetchOrder(id, symbol = undefined, params = {}) {
1957
+ /**
1958
+ * @method
1959
+ * @name vertex#fetchOrder
1960
+ * @description fetches information on an order made by the user
1961
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/queries/order
1962
+ * @param {string} symbol unified symbol of the market the order was made in
1963
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1964
+ * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
1965
+ */
1966
+ await this.loadMarkets();
1967
+ const market = this.market(symbol);
1968
+ const request = {
1969
+ 'type': 'order',
1970
+ 'product_id': this.parseToInt(market['id']),
1971
+ 'digest': id,
1972
+ };
1973
+ const response = await this.v1GatewayGetQuery(this.extend(request, params));
1974
+ //
1975
+ // {
1976
+ // "status": "success",
1977
+ // "data": {
1978
+ // "product_id": 1,
1979
+ // "sender": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43000000000000000000000000",
1980
+ // "price_x18": "1000000000000000000",
1981
+ // "amount": "1000000000000000000",
1982
+ // "expiration": "2000000000",
1983
+ // "nonce": "1",
1984
+ // "unfilled_amount": "1000000000000000000",
1985
+ // "digest": "0x0000000000000000000000000000000000000000000000000000000000000000",
1986
+ // "placed_at": 1681951347,
1987
+ // "order_type": "ioc"
1988
+ // },
1989
+ // "request_type": "query_order",
1990
+ // }
1991
+ //
1992
+ const data = this.safeDict(response, 'data');
1993
+ return this.parseOrder(data, market);
1994
+ }
1995
+ async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
1996
+ /**
1997
+ * @method
1998
+ * @name vertex#fetchOpenOrders
1999
+ * @description fetch all unfilled currently open orders
2000
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/queries/orders
2001
+ * @see https://docs.vertexprotocol.com/developer-resources/api/trigger/queries/list-trigger-orders
2002
+ * @param {string} symbol unified market symbol
2003
+ * @param {int} [since] the earliest time in ms to fetch open orders for
2004
+ * @param {int} [limit] the maximum number of open orders structures to retrieve
2005
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
2006
+ * @param {boolean} [params.stop] whether the order is a stop/algo order
2007
+ * @param {string} [params.user] user address, will default to this.walletAddress if not provided
2008
+ * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
2009
+ */
2010
+ this.checkRequiredCredentials();
2011
+ await this.loadMarkets();
2012
+ let userAddress = undefined;
2013
+ [userAddress, params] = this.handlePublicAddress('fetchOpenOrders', params);
2014
+ const request = {};
2015
+ let market = undefined;
2016
+ const stop = this.safeBool2(params, 'stop', 'trigger');
2017
+ params = this.omit(params, ['stop', 'trigger']);
2018
+ if (symbol !== undefined) {
2019
+ market = this.market(symbol);
2020
+ request['product_id'] = this.parseToNumeric(market['id']);
2021
+ }
2022
+ let response = undefined;
2023
+ if (stop) {
2024
+ const contracts = await this.queryContracts();
2025
+ const chainId = this.safeString(contracts, 'chain_id');
2026
+ const verifyingContractAddress = this.safeString(contracts, 'endpoint_addr');
2027
+ const tx = {
2028
+ 'sender': this.convertAddressToSender(userAddress),
2029
+ 'recvTime': this.nonce() + 90000,
2030
+ };
2031
+ request['signature'] = this.buildListTriggerTxSig(tx, chainId, verifyingContractAddress);
2032
+ request['tx'] = {
2033
+ 'sender': tx['sender'],
2034
+ 'recvTime': this.numberToString(tx['recvTime']),
2035
+ };
2036
+ request['type'] = 'list_trigger_orders';
2037
+ request['pending'] = true;
2038
+ const until = this.safeInteger(params, 'until');
2039
+ params = this.omit(params, 'until');
2040
+ if (until !== undefined) {
2041
+ request['max_update_time'] = until;
2042
+ }
2043
+ if (limit !== undefined) {
2044
+ request['limit'] = limit;
2045
+ }
2046
+ response = await this.v1TriggerPostQuery(this.extend(request, params));
2047
+ //
2048
+ // {
2049
+ // "status": "success",
2050
+ // "data": {
2051
+ // "orders": [
2052
+ // {
2053
+ // "order": {
2054
+ // "order": {
2055
+ // "sender": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43000000000000000000000000",
2056
+ // "priceX18": "1000000000000000000",
2057
+ // "amount": "1000000000000000000",
2058
+ // "expiration": "2000000000",
2059
+ // "nonce": "1",
2060
+ // },
2061
+ // "signature": "0x...",
2062
+ // "product_id": 1,
2063
+ // "spot_leverage": true,
2064
+ // "trigger": {
2065
+ // "price_above": "1000000000000000000"
2066
+ // },
2067
+ // "digest": "0x..."
2068
+ // },
2069
+ // "status": "pending",
2070
+ // "updated_at": 1688768157050
2071
+ // }
2072
+ // ]
2073
+ // },
2074
+ // "request_type": "query_list_trigger_orders"
2075
+ // }
2076
+ //
2077
+ }
2078
+ else {
2079
+ this.checkRequiredArgument('fetchOpenOrders', symbol, 'symbol');
2080
+ request['type'] = 'subaccount_orders';
2081
+ request['sender'] = this.convertAddressToSender(userAddress);
2082
+ response = await this.v1GatewayPostQuery(this.extend(request, params));
2083
+ //
2084
+ // {
2085
+ // "status": "success",
2086
+ // "data": {
2087
+ // "sender": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43000000000000000000000000",
2088
+ // "product_id": 1,
2089
+ // "orders": [
2090
+ // {
2091
+ // "product_id": 1,
2092
+ // "sender": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43000000000000000000000000",
2093
+ // "price_x18": "1000000000000000000",
2094
+ // "amount": "1000000000000000000",
2095
+ // "expiration": "2000000000",
2096
+ // "nonce": "1",
2097
+ // "order_type": "default",
2098
+ // "unfilled_amount": "1000000000000000000",
2099
+ // "digest": "0x0000000000000000000000000000000000000000000000000000000000000000",
2100
+ // "placed_at": 1682437739,
2101
+ // "order_type": "ioc"
2102
+ // }
2103
+ // ]
2104
+ // },
2105
+ // "request_type": "query_subaccount_orders"
2106
+ // }
2107
+ //
2108
+ }
2109
+ const data = this.safeDict(response, 'data', {});
2110
+ const orders = this.safeList(data, 'orders');
2111
+ return this.parseOrders(orders, market, since, limit);
2112
+ }
2113
+ async fetchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
2114
+ /**
2115
+ * @method
2116
+ * @name vertex#fetchOrders
2117
+ * @description fetches information on multiple orders made by the user
2118
+ * @see https://docs.vertexprotocol.com/developer-resources/api/trigger/queries/list-trigger-orders
2119
+ * @param {string} symbol unified market symbol
2120
+ * @param {int} [since] the earliest time in ms to fetch open orders for
2121
+ * @param {int} [limit] the maximum number of open orders structures to retrieve
2122
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
2123
+ * @param {boolean} [params.stop] whether the order is a stop/algo order
2124
+ * @param {string} [params.user] user address, will default to this.walletAddress if not provided
2125
+ * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
2126
+ */
2127
+ this.checkRequiredCredentials();
2128
+ const stop = this.safeBool2(params, 'stop', 'trigger');
2129
+ params = this.omit(params, ['stop', 'trigger']);
2130
+ if (!stop) {
2131
+ throw new NotSupported(this.id + ' fetchOrders only support trigger orders');
2132
+ }
2133
+ let userAddress = undefined;
2134
+ [userAddress, params] = this.handlePublicAddress('fetchOrders', params);
2135
+ await this.loadMarkets();
2136
+ let market = undefined;
2137
+ const request = {
2138
+ 'type': 'list_trigger_orders',
2139
+ 'pending': false,
2140
+ };
2141
+ if (symbol !== undefined) {
2142
+ market = this.market(symbol);
2143
+ request['product_id'] = this.parseToNumeric(market['id']);
2144
+ }
2145
+ const contracts = await this.queryContracts();
2146
+ const chainId = this.safeString(contracts, 'chain_id');
2147
+ const verifyingContractAddress = this.safeString(contracts, 'endpoint_addr');
2148
+ const tx = {
2149
+ 'sender': this.convertAddressToSender(userAddress),
2150
+ 'recvTime': this.nonce() + 90000,
2151
+ };
2152
+ request['signature'] = this.buildListTriggerTxSig(tx, chainId, verifyingContractAddress);
2153
+ request['tx'] = {
2154
+ 'sender': tx['sender'],
2155
+ 'recvTime': this.numberToString(tx['recvTime']),
2156
+ };
2157
+ const until = this.safeInteger(params, 'until');
2158
+ params = this.omit(params, 'until');
2159
+ if (until !== undefined) {
2160
+ request['max_update_time'] = until;
2161
+ }
2162
+ if (limit !== undefined) {
2163
+ request['limit'] = limit;
2164
+ }
2165
+ const response = await this.v1TriggerPostQuery(this.extend(request, params));
2166
+ //
2167
+ // {
2168
+ // "status": "success",
2169
+ // "data": {
2170
+ // "orders": [
2171
+ // {
2172
+ // "order": {
2173
+ // "order": {
2174
+ // "sender": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43000000000000000000000000",
2175
+ // "priceX18": "1000000000000000000",
2176
+ // "amount": "1000000000000000000",
2177
+ // "expiration": "2000000000",
2178
+ // "nonce": "1",
2179
+ // },
2180
+ // "signature": "0x...",
2181
+ // "product_id": 1,
2182
+ // "spot_leverage": true,
2183
+ // "trigger": {
2184
+ // "price_above": "1000000000000000000"
2185
+ // },
2186
+ // "digest": "0x..."
2187
+ // },
2188
+ // "status": "pending",
2189
+ // "updated_at": 1688768157050
2190
+ // }
2191
+ // ]
2192
+ // },
2193
+ // "request_type": "query_list_trigger_orders"
2194
+ // }
2195
+ //
2196
+ const data = this.safeDict(response, 'data', {});
2197
+ const orders = this.safeList(data, 'orders');
2198
+ return this.parseOrders(orders, market, since, limit);
2199
+ }
2200
+ async cancelAllOrders(symbol = undefined, params = {}) {
2201
+ /**
2202
+ * @method
2203
+ * @name vertex#cancelAllOrders
2204
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/executes/cancel-product-orders
2205
+ * @see https://docs.vertexprotocol.com/developer-resources/api/trigger/executes/cancel-product-orders
2206
+ * @description cancel all open orders in a market
2207
+ * @param {string} symbol unified market symbol
2208
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
2209
+ * @param {boolean} [params.stop] whether the order is a stop/algo order
2210
+ * @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
2211
+ */
2212
+ this.checkRequiredCredentials();
2213
+ await this.loadMarkets();
2214
+ if (symbol === undefined) {
2215
+ throw new ArgumentsRequired(this.id + ' cancelAllOrders() requires a symbol argument');
2216
+ }
2217
+ const market = this.market(symbol);
2218
+ const marketId = market['id'];
2219
+ const contracts = await this.queryContracts();
2220
+ const chainId = this.safeString(contracts, 'chain_id');
2221
+ const verifyingContractAddress = this.safeString(contracts, 'endpoint_addr');
2222
+ const now = this.nonce();
2223
+ const nonce = this.getNonce(now, 90000);
2224
+ const cancels = {
2225
+ 'sender': this.convertAddressToSender(this.walletAddress),
2226
+ 'productIds': [
2227
+ this.parseToNumeric(marketId),
2228
+ ],
2229
+ 'nonce': nonce,
2230
+ };
2231
+ const request = {
2232
+ 'cancel_product_orders': {
2233
+ 'tx': {
2234
+ 'sender': cancels['sender'],
2235
+ 'productIds': cancels['productIds'],
2236
+ 'nonce': this.numberToString(cancels['nonce']),
2237
+ },
2238
+ 'signature': this.buildCancelAllOrdersSig(cancels, chainId, verifyingContractAddress),
2239
+ },
2240
+ };
2241
+ const stop = this.safeBool2(params, 'stop', 'trigger');
2242
+ params = this.omit(params, ['stop', 'trigger']);
2243
+ let response = undefined;
2244
+ if (stop) {
2245
+ response = await this.v1TriggerPostExecute(this.extend(request, params));
2246
+ //
2247
+ // {
2248
+ // "status": "success",
2249
+ // "signature": {signature},
2250
+ // "request_type": "execute_cancel_product_orders"
2251
+ // }
2252
+ //
2253
+ }
2254
+ else {
2255
+ response = await this.v1GatewayPostExecute(this.extend(request, params));
2256
+ //
2257
+ // {
2258
+ // "status": "success",
2259
+ // "signature": {signature},
2260
+ // "data": {
2261
+ // "cancelled_orders": [
2262
+ // {
2263
+ // "product_id": 2,
2264
+ // "sender": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43746573743000000000000000",
2265
+ // "price_x18": "20000000000000000000000",
2266
+ // "amount": "-100000000000000000",
2267
+ // "expiration": "1686332748",
2268
+ // "order_type": "post_only",
2269
+ // "nonce": "1768248100142339392",
2270
+ // "unfilled_amount": "-100000000000000000",
2271
+ // "digest": "0x3195a7929feb8307edecf9c045j5ced68925108f0aa305f0ee5773854159377c",
2272
+ // "placed_at": 1686332708
2273
+ // },
2274
+ // ...
2275
+ // ]
2276
+ // },
2277
+ // "request_type": "execute_cancel_product_orders"
2278
+ // }
2279
+ //
2280
+ }
2281
+ return response;
2282
+ }
2283
+ async cancelOrder(id, symbol = undefined, params = {}) {
2284
+ /**
2285
+ * @method
2286
+ * @name vertex#cancelOrder
2287
+ * @description cancels an open order
2288
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/executes/cancel-orders
2289
+ * @see https://docs.vertexprotocol.com/developer-resources/api/trigger/executes/cancel-orders
2290
+ * @param {string} id order id
2291
+ * @param {string} symbol unified symbol of the market the order was made in
2292
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
2293
+ * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
2294
+ */
2295
+ return await this.cancelOrders([id], symbol, params);
2296
+ }
2297
+ async cancelOrders(ids, symbol = undefined, params = {}) {
2298
+ /**
2299
+ * @method
2300
+ * @name vertex#cancelOrders
2301
+ * @description cancel multiple orders
2302
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/executes/cancel-orders
2303
+ * @see https://docs.vertexprotocol.com/developer-resources/api/trigger/executes/cancel-orders
2304
+ * @param {string[]} ids order ids
2305
+ * @param {string} [symbol] unified market symbol
2306
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
2307
+ * @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
2308
+ */
2309
+ this.checkRequiredCredentials();
2310
+ if (symbol === undefined) {
2311
+ throw new ArgumentsRequired(this.id + ' cancelOrders() requires a symbol argument');
2312
+ }
2313
+ await this.loadMarkets();
2314
+ const market = this.market(symbol);
2315
+ const marketId = market['id'];
2316
+ const contracts = await this.queryContracts();
2317
+ const chainId = this.safeString(contracts, 'chain_id');
2318
+ const verifyingContractAddress = this.safeString(contracts, 'endpoint_addr');
2319
+ const now = this.nonce();
2320
+ const nonce = this.getNonce(now, 90000);
2321
+ const cancels = {
2322
+ 'sender': this.convertAddressToSender(this.walletAddress),
2323
+ 'productIds': [],
2324
+ 'digests': ids,
2325
+ 'nonce': nonce,
2326
+ };
2327
+ const marketIdNum = this.parseToNumeric(marketId);
2328
+ for (let i = 0; i < ids.length; i++) {
2329
+ cancels['productIds'].push(marketIdNum);
2330
+ }
2331
+ const request = {
2332
+ 'cancel_orders': {
2333
+ 'tx': {
2334
+ 'sender': cancels['sender'],
2335
+ 'productIds': cancels['productIds'],
2336
+ 'digests': cancels['digests'],
2337
+ 'nonce': this.numberToString(cancels['nonce']),
2338
+ },
2339
+ 'signature': this.buildCancelOrdersSig(cancels, chainId, verifyingContractAddress),
2340
+ },
2341
+ };
2342
+ const stop = this.safeBool2(params, 'stop', 'trigger');
2343
+ params = this.omit(params, ['stop', 'trigger']);
2344
+ let response = undefined;
2345
+ if (stop) {
2346
+ response = await this.v1TriggerPostExecute(this.extend(request, params));
2347
+ //
2348
+ // {
2349
+ // "status": "success",
2350
+ // "signature": {signature},
2351
+ // "request_type": "execute_cancel_orders"
2352
+ // }
2353
+ //
2354
+ }
2355
+ else {
2356
+ response = await this.v1GatewayPostExecute(this.extend(request, params));
2357
+ //
2358
+ // {
2359
+ // "status": "success",
2360
+ // "signature": {signature},
2361
+ // "data": {
2362
+ // "cancelled_orders": [
2363
+ // {
2364
+ // "product_id": 2,
2365
+ // "sender": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43746573743000000000000000",
2366
+ // "price_x18": "20000000000000000000000",
2367
+ // "amount": "-100000000000000000",
2368
+ // "expiration": "1686332748",
2369
+ // "order_type": "post_only",
2370
+ // "nonce": "1768248100142339392",
2371
+ // "unfilled_amount": "-100000000000000000",
2372
+ // "digest": "0x3195a7929feb8307edecf9c045j5ced68925108f0aa305f0ee5773854159377c",
2373
+ // "placed_at": 1686332708
2374
+ // },
2375
+ // ...
2376
+ // ]
2377
+ // },
2378
+ // "request_type": "execute_cancel_orders"
2379
+ // }
2380
+ //
2381
+ }
2382
+ return response;
2383
+ }
2384
+ async fetchBalance(params = {}) {
2385
+ /**
2386
+ * @method
2387
+ * @name vertex#fetchBalance
2388
+ * @description query for balance and get the amount of funds available for trading or funds locked in orders
2389
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/queries/subaccount-info
2390
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
2391
+ * @param {string} [params.user] user address, will default to this.walletAddress if not provided
2392
+ * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
2393
+ */
2394
+ let userAddress = undefined;
2395
+ [userAddress, params] = this.handlePublicAddress('fetchBalance', params);
2396
+ const request = {
2397
+ 'type': 'subaccount_info',
2398
+ 'subaccount': this.convertAddressToSender(userAddress),
2399
+ };
2400
+ const response = await this.v1GatewayGetQuery(this.extend(request, params));
2401
+ //
2402
+ // {
2403
+ // "status": "success",
2404
+ // "data": {
2405
+ // "subaccount": "0x265167ddfac55365d6ff07fc5943276319aa6b9f64656661756c740000000000",
2406
+ // "exists": true,
2407
+ // "healths": [
2408
+ // {
2409
+ // "assets": "75323297691833342306",
2410
+ // "liabilities": "46329556869051092241",
2411
+ // "health": "28993740822782250065"
2412
+ // },
2413
+ // {
2414
+ // "assets": "75323297691833342306",
2415
+ // "liabilities": "35968911700887320741",
2416
+ // "health": "39354385990946021565"
2417
+ // },
2418
+ // {
2419
+ // "assets": "80796966663601107565",
2420
+ // "liabilities": "0",
2421
+ // "health": "80796966663601107565"
2422
+ // }
2423
+ // ],
2424
+ // "health_contributions": [
2425
+ // [
2426
+ // "75323297691833340000",
2427
+ // "75323297691833340000",
2428
+ // "75323297691833340000"
2429
+ // ],
2430
+ // [
2431
+ // "0",
2432
+ // "0",
2433
+ // "0"
2434
+ // ],
2435
+ // [
2436
+ // "0",
2437
+ // "0",
2438
+ // "0"
2439
+ // ],
2440
+ // [
2441
+ // "0",
2442
+ // "0",
2443
+ // "0"
2444
+ // ],
2445
+ // [
2446
+ // "-46329556869051090000",
2447
+ // "-35968911700887323000",
2448
+ // "5473668971767765000"
2449
+ // ]
2450
+ // ],
2451
+ // "spot_count": 3,
2452
+ // "perp_count": 2,
2453
+ // "spot_balances": [
2454
+ // {
2455
+ // "product_id": 1,
2456
+ // "lp_balance": {
2457
+ // "amount": "0"
2458
+ // },
2459
+ // "balance": {
2460
+ // "amount": "0",
2461
+ // "last_cumulative_multiplier_x18": "1003419811982007193"
2462
+ // }
2463
+ // },
2464
+ // {
2465
+ // "product_id": 3,
2466
+ // "lp_balance": {
2467
+ // "amount": "0"
2468
+ // },
2469
+ // "balance": {
2470
+ // "amount": "0",
2471
+ // "last_cumulative_multiplier_x18": "1007584195035969404"
2472
+ // }
2473
+ // },
2474
+ // {
2475
+ // "product_id": 0,
2476
+ // "lp_balance": {
2477
+ // "amount": "0"
2478
+ // },
2479
+ // "balance": {
2480
+ // "amount": "75323297691833342306",
2481
+ // "last_cumulative_multiplier_x18": "1000000002391497578"
2482
+ // }
2483
+ // }
2484
+ // ],
2485
+ // "perp_balances": [
2486
+ // {
2487
+ // "product_id": 2,
2488
+ // "lp_balance": {
2489
+ // "amount": "0",
2490
+ // "last_cumulative_funding_x18": "-284321955122859921"
2491
+ // },
2492
+ // "balance": {
2493
+ // "amount": "0",
2494
+ // "v_quote_balance": "0",
2495
+ // "last_cumulative_funding_x18": "6363466629611946777168"
2496
+ // }
2497
+ // },
2498
+ // {
2499
+ // "product_id": 4,
2500
+ // "lp_balance": {
2501
+ // "amount": "0",
2502
+ // "last_cumulative_funding_x18": "-90979748449893411"
2503
+ // },
2504
+ // "balance": {
2505
+ // "amount": "-200000000000000000",
2506
+ // "v_quote_balance": "419899475698318625259",
2507
+ // "last_cumulative_funding_x18": "141182516563970577208"
2508
+ // }
2509
+ // }
2510
+ // ],
2511
+ // "spot_products": [
2512
+ // {
2513
+ // "product_id": 1,
2514
+ // "oracle_price_x18": "30217830336443750750000",
2515
+ // "risk": {
2516
+ // "long_weight_initial_x18": "750000000000000000",
2517
+ // "short_weight_initial_x18": "1250000000000000000",
2518
+ // "long_weight_maintenance_x18": "800000000000000000",
2519
+ // "short_weight_maintenance_x18": "1200000000000000000",
2520
+ // "large_position_penalty_x18": "0"
2521
+ // },
2522
+ // "config": {
2523
+ // "token": "0x5cc7c91690b2cbaee19a513473d73403e13fb431",
2524
+ // "interest_inflection_util_x18": "800000000000000000",
2525
+ // "interest_floor_x18": "10000000000000000",
2526
+ // "interest_small_cap_x18": "40000000000000000",
2527
+ // "interest_large_cap_x18": "1000000000000000000"
2528
+ // },
2529
+ // "state": {
2530
+ // "cumulative_deposits_multiplier_x18": "1001304691727847318",
2531
+ // "cumulative_borrows_multiplier_x18": "1003419811982007193",
2532
+ // "total_deposits_normalized": "213107447159798397806318",
2533
+ // "total_borrows_normalized": "4907820740150097483532"
2534
+ // },
2535
+ // "lp_state": {
2536
+ // "supply": "1304981417419495030893348",
2537
+ // "quote": {
2538
+ // "amount": "2048495687410669565222259",
2539
+ // "last_cumulative_multiplier_x18": "1000000002391497578"
2540
+ // },
2541
+ // "base": {
2542
+ // "amount": "67623029247538886515",
2543
+ // "last_cumulative_multiplier_x18": "1001304691727847318"
2544
+ // }
2545
+ // },
2546
+ // "book_info": {
2547
+ // "size_increment": "1000000000000000",
2548
+ // "price_increment_x18": "1000000000000000000",
2549
+ // "min_size": "10000000000000000",
2550
+ // "collected_fees": "8865582805773573662738183",
2551
+ // "lp_spread_x18": "3000000000000000"
2552
+ // }
2553
+ // },
2554
+ // {
2555
+ // "product_id": 3,
2556
+ // "oracle_price_x18": "2075217009708333333333",
2557
+ // "risk": {
2558
+ // "long_weight_initial_x18": "750000000000000000",
2559
+ // "short_weight_initial_x18": "1250000000000000000",
2560
+ // "long_weight_maintenance_x18": "800000000000000000",
2561
+ // "short_weight_maintenance_x18": "1200000000000000000",
2562
+ // "large_position_penalty_x18": "0"
2563
+ // },
2564
+ // "config": {
2565
+ // "token": "0xcc59686e3a32fb104c8ff84dd895676265efb8a6",
2566
+ // "interest_inflection_util_x18": "800000000000000000",
2567
+ // "interest_floor_x18": "10000000000000000",
2568
+ // "interest_small_cap_x18": "40000000000000000",
2569
+ // "interest_large_cap_x18": "1000000000000000000"
2570
+ // },
2571
+ // "state": {
2572
+ // "cumulative_deposits_multiplier_x18": "1003722507760089346",
2573
+ // "cumulative_borrows_multiplier_x18": "1007584195035969404",
2574
+ // "total_deposits_normalized": "232750303205807326418622",
2575
+ // "total_borrows_normalized": "110730726549469855171025"
2576
+ // },
2577
+ // "lp_state": {
2578
+ // "supply": "902924999999999999774268",
2579
+ // "quote": {
2580
+ // "amount": "1165328092090344104989049",
2581
+ // "last_cumulative_multiplier_x18": "1000000002391497578"
2582
+ // },
2583
+ // "base": {
2584
+ // "amount": "563265647183403990588",
2585
+ // "last_cumulative_multiplier_x18": "1003722507760089346"
2586
+ // }
2587
+ // },
2588
+ // "book_info": {
2589
+ // "size_increment": "10000000000000000",
2590
+ // "price_increment_x18": "100000000000000000",
2591
+ // "min_size": "100000000000000000",
2592
+ // "collected_fees": "1801521329724633001446457",
2593
+ // "lp_spread_x18": "3000000000000000"
2594
+ // }
2595
+ // },
2596
+ // {
2597
+ // "product_id": 0,
2598
+ // "oracle_price_x18": "1000000000000000000",
2599
+ // "risk": {
2600
+ // "long_weight_initial_x18": "1000000000000000000",
2601
+ // "short_weight_initial_x18": "1000000000000000000",
2602
+ // "long_weight_maintenance_x18": "1000000000000000000",
2603
+ // "short_weight_maintenance_x18": "1000000000000000000",
2604
+ // "large_position_penalty_x18": "0"
2605
+ // },
2606
+ // "config": {
2607
+ // "token": "0x179522635726710dd7d2035a81d856de4aa7836c",
2608
+ // "interest_inflection_util_x18": "800000000000000000",
2609
+ // "interest_floor_x18": "10000000000000000",
2610
+ // "interest_small_cap_x18": "40000000000000000",
2611
+ // "interest_large_cap_x18": "1000000000000000000"
2612
+ // },
2613
+ // "state": {
2614
+ // "cumulative_deposits_multiplier_x18": "1000000002391497578",
2615
+ // "cumulative_borrows_multiplier_x18": "1001593395547514024",
2616
+ // "total_deposits_normalized": "60000256267437588885818752247843",
2617
+ // "total_borrows_normalized": "391445043137305055810336885"
2618
+ // },
2619
+ // "lp_state": {
2620
+ // "supply": "0",
2621
+ // "quote": {
2622
+ // "amount": "0",
2623
+ // "last_cumulative_multiplier_x18": "0"
2624
+ // },
2625
+ // "base": {
2626
+ // "amount": "0",
2627
+ // "last_cumulative_multiplier_x18": "0"
2628
+ // }
2629
+ // },
2630
+ // "book_info": {
2631
+ // "size_increment": "0",
2632
+ // "price_increment_x18": "0",
2633
+ // "min_size": "0",
2634
+ // "collected_fees": "0",
2635
+ // "lp_spread_x18": "0"
2636
+ // }
2637
+ // }
2638
+ // ],
2639
+ // "perp_products": [
2640
+ // {
2641
+ // "product_id": 2,
2642
+ // "oracle_price_x18": "30219079716463070000000",
2643
+ // "risk": {
2644
+ // "long_weight_initial_x18": "875000000000000000",
2645
+ // "short_weight_initial_x18": "1125000000000000000",
2646
+ // "long_weight_maintenance_x18": "900000000000000000",
2647
+ // "short_weight_maintenance_x18": "1100000000000000000",
2648
+ // "large_position_penalty_x18": "0"
2649
+ // },
2650
+ // "state": {
2651
+ // "cumulative_funding_long_x18": "6363466629611946777168",
2652
+ // "cumulative_funding_short_x18": "6363466629611946777168",
2653
+ // "available_settle": "100612314098927536086702448",
2654
+ // "open_interest": "57975708279961875623240"
2655
+ // },
2656
+ // "lp_state": {
2657
+ // "supply": "783207415944433511804197",
2658
+ // "last_cumulative_funding_x18": "6363466629611946777168",
2659
+ // "cumulative_funding_per_lp_x18": "-284321955122859921",
2660
+ // "base": "37321000000000000000",
2661
+ // "quote": "1150991638943862165224593"
2662
+ // },
2663
+ // "book_info": {
2664
+ // "size_increment": "1000000000000000",
2665
+ // "price_increment_x18": "1000000000000000000",
2666
+ // "min_size": "10000000000000000",
2667
+ // "collected_fees": "7738341933653651206856235",
2668
+ // "lp_spread_x18": "3000000000000000"
2669
+ // }
2670
+ // },
2671
+ // {
2672
+ // "product_id": 4,
2673
+ // "oracle_price_x18": "2072129033632754300000",
2674
+ // "risk": {
2675
+ // "long_weight_initial_x18": "875000000000000000",
2676
+ // "short_weight_initial_x18": "1125000000000000000",
2677
+ // "long_weight_maintenance_x18": "900000000000000000",
2678
+ // "short_weight_maintenance_x18": "1100000000000000000",
2679
+ // "large_position_penalty_x18": "0"
2680
+ // },
2681
+ // "state": {
2682
+ // "cumulative_funding_long_x18": "141182516563970577208",
2683
+ // "cumulative_funding_short_x18": "141182516563970577208",
2684
+ // "available_settle": "33807443862986950288685582",
2685
+ // "open_interest": "316343836992291503987611"
2686
+ // },
2687
+ // "lp_state": {
2688
+ // "supply": "541756546038144467864559",
2689
+ // "last_cumulative_funding_x18": "141182516563970577208",
2690
+ // "cumulative_funding_per_lp_x18": "-90979748449893411",
2691
+ // "base": "362320000000000000000",
2692
+ // "quote": "750080187685127907834038"
2693
+ // },
2694
+ // "book_info": {
2695
+ // "size_increment": "10000000000000000",
2696
+ // "price_increment_x18": "100000000000000000",
2697
+ // "min_size": "100000000000000000",
2698
+ // "collected_fees": "1893278317732551619694831",
2699
+ // "lp_spread_x18": "3000000000000000"
2700
+ // }
2701
+ // }
2702
+ // ]
2703
+ // },
2704
+ // "request_type": "query_subaccount_info"
2705
+ // }
2706
+ //
2707
+ const data = this.safeDict(response, 'data', {});
2708
+ const balances = this.safeList(data, 'spot_balances', []);
2709
+ const result = { 'info': response };
2710
+ for (let i = 0; i < balances.length; i++) {
2711
+ const balance = balances[i];
2712
+ const marketId = this.safeString(balance, 'product_id');
2713
+ const market = this.safeMarket(marketId);
2714
+ const isUsdcMarketId = marketId === '0';
2715
+ if (market['id'] === undefined && !isUsdcMarketId) {
2716
+ continue;
2717
+ }
2718
+ const baseId = (isUsdcMarketId) ? 'USDC' : this.safeString(market, 'baseId');
2719
+ const code = this.safeCurrencyCode(baseId);
2720
+ const account = this.account();
2721
+ const tokenBalance = this.safeDict(balance, 'balance', {});
2722
+ const total = this.convertFromX18(this.safeString(tokenBalance, 'amount'));
2723
+ account['total'] = total;
2724
+ result[code] = account;
2725
+ }
2726
+ return this.safeBalance(result);
2727
+ }
2728
+ parsePosition(position, market = undefined) {
2729
+ //
2730
+ // {
2731
+ // "product_id": 2,
2732
+ // "lp_balance": {
2733
+ // "amount": "0",
2734
+ // "last_cumulative_funding_x18": "-284321955122859921"
2735
+ // },
2736
+ // "balance": {
2737
+ // "amount": "0",
2738
+ // "v_quote_balance": "0",
2739
+ // "last_cumulative_funding_x18": "6363466629611946777168"
2740
+ // }
2741
+ // },
2742
+ // {
2743
+ // "product_id": 4,
2744
+ // "lp_balance": {
2745
+ // "amount": "0",
2746
+ // "last_cumulative_funding_x18": "-90979748449893411"
2747
+ // },
2748
+ // "balance": {
2749
+ // "amount": "-200000000000000000",
2750
+ // "v_quote_balance": "419899475698318625259",
2751
+ // "last_cumulative_funding_x18": "141182516563970577208"
2752
+ // }
2753
+ // }
2754
+ //
2755
+ const marketId = this.safeString(position, 'product_id');
2756
+ market = this.safeMarket(marketId);
2757
+ const balance = this.safeDict(position, 'balance', {});
2758
+ const contractSize = this.convertFromX18(this.safeString(balance, 'amount'));
2759
+ let side = 'buy';
2760
+ if (Precise.stringLt(contractSize, '1')) {
2761
+ side = 'sell';
2762
+ }
2763
+ return this.safePosition({
2764
+ 'info': position,
2765
+ 'id': undefined,
2766
+ 'symbol': this.safeString(market, 'symbol'),
2767
+ 'timestamp': undefined,
2768
+ 'datetime': undefined,
2769
+ 'lastUpdateTimestamp': undefined,
2770
+ 'initialMargin': undefined,
2771
+ 'initialMarginPercentage': undefined,
2772
+ 'maintenanceMargin': undefined,
2773
+ 'maintenanceMarginPercentage': undefined,
2774
+ 'entryPrice': undefined,
2775
+ 'notional': undefined,
2776
+ 'leverage': undefined,
2777
+ 'unrealizedPnl': undefined,
2778
+ 'contracts': undefined,
2779
+ 'contractSize': this.parseToNumeric(contractSize),
2780
+ 'marginRatio': undefined,
2781
+ 'liquidationPrice': undefined,
2782
+ 'markPrice': undefined,
2783
+ 'lastPrice': undefined,
2784
+ 'collateral': undefined,
2785
+ 'marginMode': 'cross',
2786
+ 'marginType': undefined,
2787
+ 'side': side,
2788
+ 'percentage': undefined,
2789
+ 'hedged': undefined,
2790
+ 'stopLossPrice': undefined,
2791
+ 'takeProfitPrice': undefined,
2792
+ });
2793
+ }
2794
+ async fetchPositions(symbols = undefined, params = {}) {
2795
+ /**
2796
+ * @method
2797
+ * @name vertex#fetchPositions
2798
+ * @description fetch all open positions
2799
+ * @see https://docs.vertexprotocol.com/developer-resources/api/gateway/queries/subaccount-info
2800
+ * @param {string[]} [symbols] list of unified market symbols
2801
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
2802
+ * @param {string} [params.user] user address, will default to this.walletAddress if not provided
2803
+ * @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/#/?id=position-structure}
2804
+ */
2805
+ let userAddress = undefined;
2806
+ [userAddress, params] = this.handlePublicAddress('fetchPositions', params);
2807
+ const request = {
2808
+ 'type': 'subaccount_info',
2809
+ 'subaccount': this.convertAddressToSender(userAddress),
2810
+ };
2811
+ const response = await this.v1GatewayGetQuery(this.extend(request, params));
2812
+ // the response is the same as fetchBalance
2813
+ const data = this.safeDict(response, 'data', {});
2814
+ const positions = this.safeList(data, 'perp_balances', []);
2815
+ symbols = this.marketSymbols(symbols);
2816
+ const result = [];
2817
+ for (let i = 0; i < positions.length; i++) {
2818
+ const position = this.extend(this.parsePosition(positions[i], undefined), params);
2819
+ if (position['contractSize'] === 0) {
2820
+ continue;
2821
+ }
2822
+ result.push(position);
2823
+ }
2824
+ return this.filterByArrayPositions(result, 'symbol', symbols, false);
2825
+ }
2826
+ async queryNonces() {
2827
+ const request = {
2828
+ 'type': 'nonces',
2829
+ 'address': this.walletAddress,
2830
+ };
2831
+ const response = await this.v1GatewayGetQuery(request);
2832
+ //
2833
+ // {
2834
+ // "status":"success",
2835
+ // "data":{
2836
+ // "tx_nonce": 0,
2837
+ // "order_nonce": 1753048133299863552
2838
+ // },
2839
+ // "request_type": "query_nonces",
2840
+ // }
2841
+ //
2842
+ return this.safeDict(response, 'data', {});
2843
+ }
2844
+ async withdraw(code, amount, address, tag = undefined, params = {}) {
2845
+ /**
2846
+ * @method
2847
+ * @name vertex#withdraw
2848
+ * @description make a withdrawal
2849
+ * @see https://docs.vertexprotocol.com/developer-resources/api/withdrawing-on-chain
2850
+ * @param {string} code unified currency code
2851
+ * @param {float} amount the amount to withdraw
2852
+ * @param {string} address the address to withdraw to
2853
+ * @param {string} tag
2854
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
2855
+ * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
2856
+ */
2857
+ this.checkRequiredCredentials();
2858
+ await this.loadMarkets();
2859
+ const currency = this.currency(code);
2860
+ const contracts = await this.queryContracts();
2861
+ const chainId = this.safeString(contracts, 'chain_id');
2862
+ const verifyingContractAddress = this.safeString(contracts, 'endpoint_addr');
2863
+ const nonces = await this.queryNonces();
2864
+ const nonce = this.safeNumber(nonces, 'tx_nonce');
2865
+ const withdraw = {
2866
+ 'sender': this.convertAddressToSender(this.walletAddress),
2867
+ 'productId': this.parseToNumeric(currency['id']),
2868
+ 'amount': amount.toString(),
2869
+ 'nonce': nonce,
2870
+ };
2871
+ const request = {
2872
+ 'withdraw_collateral': {
2873
+ 'tx': {
2874
+ 'sender': withdraw['sender'],
2875
+ 'productId': withdraw['productId'],
2876
+ 'amount': withdraw['amount'],
2877
+ 'nonce': this.numberToString(withdraw['nonce']),
2878
+ },
2879
+ 'signature': this.buildWithdrawSig(withdraw, chainId, verifyingContractAddress),
2880
+ },
2881
+ };
2882
+ const response = await this.v1GatewayPostExecute(this.extend(request, params));
2883
+ //
2884
+ // {
2885
+ // "status": "success",
2886
+ // "signature": {signature},
2887
+ // "request_type": "execute_withdraw_collateral"
2888
+ // }
2889
+ //
2890
+ return response;
2891
+ }
2892
+ handlePublicAddress(methodName, params) {
2893
+ let userAux = undefined;
2894
+ [userAux, params] = this.handleOptionAndParams(params, methodName, 'user');
2895
+ let user = userAux;
2896
+ [user, params] = this.handleOptionAndParams(params, methodName, 'address', userAux);
2897
+ if ((user !== undefined) && (user !== '')) {
2898
+ return [user, params];
2899
+ }
2900
+ if ((this.walletAddress !== undefined) && (this.walletAddress !== '')) {
2901
+ return [this.walletAddress, params];
2902
+ }
2903
+ throw new ArgumentsRequired(this.id + ' ' + methodName + '() requires a user parameter inside \'params\' or the wallet address set');
2904
+ }
2905
+ handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
2906
+ if (!response) {
2907
+ return undefined; // fallback to default error handler
2908
+ }
2909
+ //
2910
+ //
2911
+ const status = this.safeString(response, 'status', '');
2912
+ if (status === 'failure') {
2913
+ const message = this.safeString(response, 'error');
2914
+ const feedback = this.id + ' ' + body;
2915
+ const errorCode = this.safeString(response, 'error_code');
2916
+ this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback);
2917
+ this.throwBroadlyMatchedException(this.exceptions['broad'], message, feedback);
2918
+ throw new ExchangeError(feedback);
2919
+ }
2920
+ return undefined;
2921
+ }
2922
+ sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
2923
+ const version = this.safeString(api, 0);
2924
+ const type = this.safeString(api, 1);
2925
+ let url = this.implodeHostname(this.urls['api'][version][type]);
2926
+ if (version !== 'v1' || type !== 'archive') {
2927
+ url = url + '/' + path;
2928
+ }
2929
+ if (method === 'POST') {
2930
+ headers = {
2931
+ 'Content-Type': 'application/json',
2932
+ };
2933
+ body = this.json(params);
2934
+ }
2935
+ else {
2936
+ if (Object.keys(params).length) {
2937
+ url += '?' + this.urlencode(params);
2938
+ }
2939
+ }
2940
+ return { 'url': url, 'method': method, 'body': body, 'headers': headers };
2941
+ }
2942
+ }