ccxt 4.5.2 → 4.5.4

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.
Files changed (66) hide show
  1. package/README.md +4 -4
  2. package/dist/ccxt.browser.min.js +2 -2
  3. package/dist/cjs/ccxt.js +1 -1
  4. package/dist/cjs/src/base/Exchange.js +78 -5
  5. package/dist/cjs/src/base/functions/encode.js +8 -0
  6. package/dist/cjs/src/base/functions/rsa.js +14 -1
  7. package/dist/cjs/src/base/functions.js +1 -0
  8. package/dist/cjs/src/binance.js +12 -15
  9. package/dist/cjs/src/bitget.js +1 -1
  10. package/dist/cjs/src/bitvavo.js +8 -0
  11. package/dist/cjs/src/bybit.js +20 -6
  12. package/dist/cjs/src/coinbase.js +28 -10
  13. package/dist/cjs/src/coincatch.js +34 -21
  14. package/dist/cjs/src/delta.js +1 -0
  15. package/dist/cjs/src/gate.js +27 -12
  16. package/dist/cjs/src/gemini.js +3 -3
  17. package/dist/cjs/src/htx.js +4 -4
  18. package/dist/cjs/src/kucoinfutures.js +11 -10
  19. package/dist/cjs/src/mexc.js +30 -1
  20. package/dist/cjs/src/okx.js +19 -4
  21. package/dist/cjs/src/pro/binance.js +3 -3
  22. package/dist/cjs/src/pro/bitfinex.js +140 -0
  23. package/dist/cjs/src/pro/bitget.js +168 -26
  24. package/dist/cjs/src/pro/bybit.js +67 -11
  25. package/dist/cjs/src/pro/coinex.js +10 -11
  26. package/dist/cjs/src/pro/kucoin.js +64 -0
  27. package/dist/cjs/src/pro/mexc.js +7 -3
  28. package/js/ccxt.d.ts +1 -1
  29. package/js/ccxt.js +1 -1
  30. package/js/src/abstract/myokx.d.ts +1 -0
  31. package/js/src/abstract/okx.d.ts +1 -0
  32. package/js/src/abstract/okxus.d.ts +1 -0
  33. package/js/src/base/Exchange.d.ts +6 -0
  34. package/js/src/base/Exchange.js +78 -5
  35. package/js/src/base/functions/encode.d.ts +2 -1
  36. package/js/src/base/functions/encode.js +8 -1
  37. package/js/src/base/functions/rsa.js +16 -3
  38. package/js/src/binance.js +12 -15
  39. package/js/src/bitget.js +1 -1
  40. package/js/src/bitvavo.js +8 -0
  41. package/js/src/bybit.js +20 -6
  42. package/js/src/coinbase.d.ts +1 -1
  43. package/js/src/coinbase.js +28 -10
  44. package/js/src/coincatch.d.ts +2 -0
  45. package/js/src/coincatch.js +34 -21
  46. package/js/src/delta.js +1 -0
  47. package/js/src/gate.js +27 -12
  48. package/js/src/gemini.js +3 -3
  49. package/js/src/htx.js +4 -4
  50. package/js/src/kucoinfutures.js +11 -10
  51. package/js/src/mexc.d.ts +3 -0
  52. package/js/src/mexc.js +30 -1
  53. package/js/src/okx.d.ts +4 -2
  54. package/js/src/okx.js +19 -4
  55. package/js/src/pro/binance.js +3 -3
  56. package/js/src/pro/bitfinex.d.ts +30 -0
  57. package/js/src/pro/bitfinex.js +140 -0
  58. package/js/src/pro/bitget.d.ts +9 -1
  59. package/js/src/pro/bitget.js +168 -26
  60. package/js/src/pro/bybit.d.ts +6 -2
  61. package/js/src/pro/bybit.js +67 -11
  62. package/js/src/pro/coinex.js +11 -12
  63. package/js/src/pro/kucoin.d.ts +22 -0
  64. package/js/src/pro/kucoin.js +64 -0
  65. package/js/src/pro/mexc.js +7 -3
  66. package/package.json +3 -3
package/dist/cjs/ccxt.js CHANGED
@@ -192,7 +192,7 @@ var xt$1 = require('./src/pro/xt.js');
192
192
 
193
193
  //-----------------------------------------------------------------------------
194
194
  // this is updated by vss.js when building
195
- const version = '4.5.2';
195
+ const version = '4.5.4';
196
196
  Exchange["default"].ccxtVersion = version;
197
197
  const exchanges = {
198
198
  'alpaca': alpaca["default"],
@@ -1161,6 +1161,7 @@ class Exchange {
1161
1161
  }
1162
1162
  async close() {
1163
1163
  // test by running ts/src/pro/test/base/test.close.ts
1164
+ await this.sleep(0); // allow other futures to run
1164
1165
  const clients = Object.values(this.clients || {});
1165
1166
  const closedClients = [];
1166
1167
  for (let i = 0; i < clients.length; i++) {
@@ -1199,6 +1200,7 @@ class Exchange {
1199
1200
  }
1200
1201
  client.reject(new errors.ExchangeError(this.id + ' nonce is behind the cache after ' + maxRetries.toString() + ' tries.'), messageHash);
1201
1202
  delete this.clients[client.url];
1203
+ this.orderbooks[symbol] = this.orderBook(); // clear the orderbook and its cache - issue https://github.com/ccxt/ccxt/issues/26753
1202
1204
  }
1203
1205
  catch (e) {
1204
1206
  client.reject(e, messageHash);
@@ -2210,6 +2212,9 @@ class Exchange {
2210
2212
  async unWatchPositions(symbols = undefined, params = {}) {
2211
2213
  throw new errors.NotSupported(this.id + ' unWatchPositions() is not supported yet');
2212
2214
  }
2215
+ async unWatchTicker(symbol, params = {}) {
2216
+ throw new errors.NotSupported(this.id + ' unWatchTicker() is not supported yet');
2217
+ }
2213
2218
  async fetchDepositAddresses(codes = undefined, params = {}) {
2214
2219
  throw new errors.NotSupported(this.id + ' fetchDepositAddresses() is not supported yet');
2215
2220
  }
@@ -2443,9 +2448,9 @@ class Exchange {
2443
2448
  parseToNumeric(number) {
2444
2449
  const stringVersion = this.numberToString(number); // this will convert 1.0 and 1 to "1" and 1.1 to "1.1"
2445
2450
  // keep this in mind:
2446
- // in JS: 1 == 1.0 is true; 1 === 1.0 is true
2451
+ // in JS: 1 === 1.0 is true
2447
2452
  // in Python: 1 == 1.0 is true
2448
- // in PHP 1 == 1.0 is true, but 1 === 1.0 is false.
2453
+ // in PHP: 1 == 1.0 is true, but 1 === 1.0 is false.
2449
2454
  if (stringVersion.indexOf('.') >= 0) {
2450
2455
  return parseFloat(stringVersion);
2451
2456
  }
@@ -2960,6 +2965,26 @@ class Exchange {
2960
2965
  this.codes = Object.keys(currenciesSortedByCode);
2961
2966
  return this.markets;
2962
2967
  }
2968
+ setMarketsFromExchange(sourceExchange) {
2969
+ // Validate that both exchanges are of the same type
2970
+ if (this.id !== sourceExchange.id) {
2971
+ throw new errors.ArgumentsRequired(this.id + ' shareMarkets() can only share markets with exchanges of the same type (got ' + sourceExchange['id'] + ')');
2972
+ }
2973
+ // Validate that source exchange has loaded markets
2974
+ if (!sourceExchange.markets) {
2975
+ throw new errors.ExchangeError('setMarketsFromExchange() source exchange must have loaded markets first. Can call by using loadMarkets function');
2976
+ }
2977
+ // Set all market-related data
2978
+ this.markets = sourceExchange.markets;
2979
+ this.markets_by_id = sourceExchange.markets_by_id;
2980
+ this.symbols = sourceExchange.symbols;
2981
+ this.ids = sourceExchange.ids;
2982
+ this.currencies = sourceExchange.currencies;
2983
+ this.baseCurrencies = sourceExchange.baseCurrencies;
2984
+ this.quoteCurrencies = sourceExchange.quoteCurrencies;
2985
+ this.codes = sourceExchange.codes;
2986
+ return this;
2987
+ }
2963
2988
  getDescribeForExtendedWsExchange(currentRestInstance, parentRestInstance, wsBaseDescribe) {
2964
2989
  const extendedRestDescribe = this.deepExtend(parentRestInstance.describe(), currentRestInstance.describe());
2965
2990
  const superWithRestDescribe = this.deepExtend(extendedRestDescribe, wsBaseDescribe);
@@ -4238,17 +4263,30 @@ class Exchange {
4238
4263
  }
4239
4264
  return result;
4240
4265
  }
4241
- parseTrades(trades, market = undefined, since = undefined, limit = undefined, params = {}) {
4266
+ parseTradesHelper(isWs, trades, market = undefined, since = undefined, limit = undefined, params = {}) {
4242
4267
  trades = this.toArray(trades);
4243
4268
  let result = [];
4244
4269
  for (let i = 0; i < trades.length; i++) {
4245
- const trade = this.extend(this.parseTrade(trades[i], market), params);
4270
+ let parsed = undefined;
4271
+ if (isWs) {
4272
+ parsed = this.parseWsTrade(trades[i], market);
4273
+ }
4274
+ else {
4275
+ parsed = this.parseTrade(trades[i], market);
4276
+ }
4277
+ const trade = this.extend(parsed, params);
4246
4278
  result.push(trade);
4247
4279
  }
4248
4280
  result = this.sortBy2(result, 'timestamp', 'id');
4249
4281
  const symbol = (market !== undefined) ? market['symbol'] : undefined;
4250
4282
  return this.filterBySymbolSinceLimit(result, symbol, since, limit);
4251
4283
  }
4284
+ parseTrades(trades, market = undefined, since = undefined, limit = undefined, params = {}) {
4285
+ return this.parseTradesHelper(false, trades, market, since, limit, params);
4286
+ }
4287
+ parseWsTrades(trades, market = undefined, since = undefined, limit = undefined, params = {}) {
4288
+ return this.parseTradesHelper(true, trades, market, since, limit, params);
4289
+ }
4252
4290
  parseTransactions(transactions, currency = undefined, since = undefined, limit = undefined, params = {}) {
4253
4291
  transactions = this.toArray(transactions);
4254
4292
  let result = [];
@@ -4699,6 +4737,12 @@ class Exchange {
4699
4737
  }
4700
4738
  return result;
4701
4739
  }
4740
+ marketOrNull(symbol) {
4741
+ if (symbol === undefined) {
4742
+ return undefined;
4743
+ }
4744
+ return this.market(symbol);
4745
+ }
4702
4746
  checkRequiredCredentials(error = true) {
4703
4747
  /**
4704
4748
  * @ignore
@@ -6198,6 +6242,35 @@ class Exchange {
6198
6242
  const symbol = (market === undefined) ? undefined : market['symbol'];
6199
6243
  return this.filterBySymbolSinceLimit(sorted, symbol, since, limit);
6200
6244
  }
6245
+ handleTriggerPricesAndParams(symbol, params, omitParams = true) {
6246
+ //
6247
+ const triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice');
6248
+ let triggerPriceStr = undefined;
6249
+ const stopLossPrice = this.safeString(params, 'stopLossPrice');
6250
+ let stopLossPriceStr = undefined;
6251
+ const takeProfitPrice = this.safeString(params, 'takeProfitPrice');
6252
+ let takeProfitPriceStr = undefined;
6253
+ //
6254
+ if (triggerPrice !== undefined) {
6255
+ if (omitParams) {
6256
+ params = this.omit(params, ['triggerPrice', 'stopPrice']);
6257
+ }
6258
+ triggerPriceStr = this.priceToPrecision(symbol, parseFloat(triggerPrice));
6259
+ }
6260
+ if (stopLossPrice !== undefined) {
6261
+ if (omitParams) {
6262
+ params = this.omit(params, 'stopLossPrice');
6263
+ }
6264
+ stopLossPriceStr = this.priceToPrecision(symbol, parseFloat(stopLossPrice));
6265
+ }
6266
+ if (takeProfitPrice !== undefined) {
6267
+ if (omitParams) {
6268
+ params = this.omit(params, 'takeProfitPrice');
6269
+ }
6270
+ takeProfitPriceStr = this.priceToPrecision(symbol, parseFloat(takeProfitPrice));
6271
+ }
6272
+ return [triggerPriceStr, stopLossPriceStr, takeProfitPriceStr, params];
6273
+ }
6201
6274
  handleTriggerDirectionAndParams(params, exchangeSpecificKey = undefined, allowEmpty = false) {
6202
6275
  /**
6203
6276
  * @ignore
@@ -7394,7 +7467,7 @@ class Exchange {
7394
7467
  const clients = Object.values(this.clients);
7395
7468
  for (let i = 0; i < clients.length; i++) {
7396
7469
  const client = clients[i];
7397
- const futures = this.safeDict(client, 'futures');
7470
+ const futures = client.futures;
7398
7471
  if ((futures !== undefined) && ('fetchPositionsSnapshot' in futures)) {
7399
7472
  delete futures['fetchPositionsSnapshot'];
7400
7473
  }
@@ -24,10 +24,18 @@ const json = (data, params = undefined) => JSON.stringify(data), isJsonEncodedOb
24
24
  function packb(req) {
25
25
  return msgpack.serialize(req);
26
26
  }
27
+ function base64ToBase64Url(base64, stripPadding = true) {
28
+ let base64url = base64.replace(/\+/g, "-").replace(/\//g, "_");
29
+ if (stripPadding) {
30
+ base64url = base64url.replace(/=+$/, "");
31
+ }
32
+ return base64url;
33
+ }
27
34
  /* ------------------------------------------------------------------------ */
28
35
 
29
36
  exports.base16ToBinary = base16ToBinary;
30
37
  exports.base58ToBinary = base58ToBinary;
38
+ exports.base64ToBase64Url = base64ToBase64Url;
31
39
  exports.base64ToBinary = base64ToBinary;
32
40
  exports.base64ToString = base64ToString;
33
41
  exports.binaryConcat = binaryConcat;
@@ -7,6 +7,7 @@ var index = require('../../static_dependencies/scure-base/index.js');
7
7
  var encode = require('./encode.js');
8
8
  var crypto = require('./crypto.js');
9
9
  var p256 = require('../../static_dependencies/noble-curves/p256.js');
10
+ var ed25519 = require('../../static_dependencies/noble-curves/ed25519.js');
10
11
 
11
12
  // ----------------------------------------------------------------------------
12
13
  function rsa(request, secret, hash) {
@@ -28,7 +29,7 @@ function jwt(request, secret, hash, isRSA = false, opts = {}) {
28
29
  }
29
30
  const encodedHeader = encode.urlencodeBase64(JSON.stringify(header));
30
31
  const encodedData = encode.urlencodeBase64(JSON.stringify(request));
31
- const token = [encodedHeader, encodedData].join('.');
32
+ let token = [encodedHeader, encodedData].join('.');
32
33
  const algoType = alg.slice(0, 2);
33
34
  let signature = undefined;
34
35
  if (algoType === 'HS') {
@@ -43,8 +44,20 @@ function jwt(request, secret, hash, isRSA = false, opts = {}) {
43
44
  const s = signedHash.s.padStart(64, '0');
44
45
  signature = encode.urlencodeBase64(encode.base16ToBinary(r + s));
45
46
  }
47
+ else if (algoType === 'ED') {
48
+ const base64str = crypto.eddsa(toHex(token), secret, ed25519.ed25519);
49
+ // we need urlencoded64 not base64
50
+ signature = encode.base64ToBase64Url(base64str);
51
+ }
46
52
  return [token, signature].join('.');
47
53
  }
54
+ function toHex(str) {
55
+ var result = '';
56
+ for (var i = 0; i < str.length; i++) {
57
+ result += str.charCodeAt(i).toString(16);
58
+ }
59
+ return result;
60
+ }
48
61
 
49
62
  exports.jwt = jwt;
50
63
  exports.rsa = rsa;
@@ -105,6 +105,7 @@ exports.truncate = number.truncate;
105
105
  exports.truncate_to_string = number.truncate_to_string;
106
106
  exports.base16ToBinary = encode.base16ToBinary;
107
107
  exports.base58ToBinary = encode.base58ToBinary;
108
+ exports.base64ToBase64Url = encode.base64ToBase64Url;
108
109
  exports.base64ToBinary = encode.base64ToBinary;
109
110
  exports.base64ToString = encode.base64ToString;
110
111
  exports.binaryConcat = encode.binaryConcat;
@@ -6611,19 +6611,13 @@ class binance extends binance$1["default"] {
6611
6611
  }
6612
6612
  }
6613
6613
  if (quantityIsRequired) {
6614
- // portfolio margin has a different amount precision
6615
- if (isPortfolioMargin) {
6616
- request['quantity'] = this.parseToNumeric(amount);
6614
+ const marketAmountPrecision = this.safeString(market['precision'], 'amount');
6615
+ const isPrecisionAvailable = (marketAmountPrecision !== undefined);
6616
+ if (isPrecisionAvailable) {
6617
+ request['quantity'] = this.amountToPrecision(symbol, amount);
6617
6618
  }
6618
6619
  else {
6619
- const marketAmountPrecision = this.safeString(market['precision'], 'amount');
6620
- const isPrecisionAvailable = (marketAmountPrecision !== undefined);
6621
- if (isPrecisionAvailable) {
6622
- request['quantity'] = this.amountToPrecision(symbol, amount);
6623
- }
6624
- else {
6625
- request['quantity'] = this.parseToNumeric(amount); // some options don't have the precision available
6626
- }
6620
+ request['quantity'] = this.parseToNumeric(amount); // some options don't have the precision available
6627
6621
  }
6628
6622
  }
6629
6623
  if (priceIsRequired && !isPriceMatch) {
@@ -11409,11 +11403,14 @@ class binance extends binance$1["default"] {
11409
11403
  * @returns {object} response from the exchange
11410
11404
  */
11411
11405
  async setPositionMode(hedged, symbol = undefined, params = {}) {
11412
- const defaultType = this.safeString(this.options, 'defaultType', 'future');
11413
- const type = this.safeString(params, 'type', defaultType);
11414
- params = this.omit(params, ['type']);
11406
+ let market = undefined;
11407
+ if (symbol !== undefined) {
11408
+ market = this.market(symbol);
11409
+ }
11410
+ let type = undefined;
11411
+ [type, params] = this.handleMarketTypeAndParams('setPositionMode', market, params);
11415
11412
  let subType = undefined;
11416
- [subType, params] = this.handleSubTypeAndParams('setPositionMode', undefined, params);
11413
+ [subType, params] = this.handleSubTypeAndParams('setPositionMode', market, params);
11417
11414
  let isPortfolioMargin = undefined;
11418
11415
  [isPortfolioMargin, params] = this.handleOptionAndParams2(params, 'setPositionMode', 'papi', 'portfolioMargin', false);
11419
11416
  let dualSidePosition = undefined;
@@ -7708,7 +7708,7 @@ class bitget extends bitget$1["default"] {
7708
7708
  // "requestTime": 1700802995406,
7709
7709
  // "data": [
7710
7710
  // {
7711
- // "userId": "7264631750",
7711
+ // "userId": "7264631751",
7712
7712
  // "symbol": "BTCUSDT",
7713
7713
  // "orderId": "1098394344925597696",
7714
7714
  // "tradeId": "1098394344974925824",
@@ -1423,6 +1423,14 @@ class bitvavo extends bitvavo$1["default"] {
1423
1423
  market = this.market(symbol);
1424
1424
  request['market'] = market['id'];
1425
1425
  }
1426
+ let operatorId = undefined;
1427
+ [operatorId, params] = this.handleOptionAndParams(params, 'cancelAllOrders', 'operatorId');
1428
+ if (operatorId !== undefined) {
1429
+ request['operatorId'] = this.parseToInt(operatorId);
1430
+ }
1431
+ else {
1432
+ throw new errors.ArgumentsRequired(this.id + ' canceAllOrders() requires an operatorId in params or options, eg: exchange.options[\'operatorId\'] = 1234567890');
1433
+ }
1426
1434
  const response = await this.privateDeleteOrders(this.extend(request, params));
1427
1435
  //
1428
1436
  // [
@@ -2996,17 +2996,31 @@ class bybit extends bybit$1["default"] {
2996
2996
  // "tradeId": "0e94eaf5-b08e-5505-b43f-7f1f30b1ca80"
2997
2997
  // }
2998
2998
  //
2999
+ // watchMyTrades execution.fast
3000
+ //
3001
+ // {
3002
+ // "category": "linear",
3003
+ // "symbol": "ICPUSDT",
3004
+ // "execId": "3510f361-0add-5c7b-a2e7-9679810944fc",
3005
+ // "execPrice": "12.015",
3006
+ // "execQty": "3000",
3007
+ // "orderId": "443d63fa-b4c3-4297-b7b1-23bca88b04dc",
3008
+ // "isMaker": false,
3009
+ // "orderLinkId": "test-00001",
3010
+ // "side": "Sell",
3011
+ // "execTime": "1716800399334",
3012
+ // "seq": 34771365464
3013
+ // }
3014
+ //
2999
3015
  const id = this.safeStringN(trade, ['execId', 'id', 'tradeId']);
3000
3016
  const marketId = this.safeString(trade, 'symbol');
3001
3017
  let marketType = ('createType' in trade) ? 'contract' : 'spot';
3002
- if (market !== undefined) {
3003
- marketType = market['type'];
3004
- }
3005
3018
  const category = this.safeString(trade, 'category');
3006
3019
  if (category !== undefined) {
3007
- if (category === 'spot') {
3008
- marketType = 'spot';
3009
- }
3020
+ marketType = (category === 'spot') ? 'spot' : 'contract';
3021
+ }
3022
+ if (market !== undefined) {
3023
+ marketType = market['type'];
3010
3024
  }
3011
3025
  market = this.safeMarket(marketId, market, undefined, marketType);
3012
3026
  const symbol = market['symbol'];
@@ -5014,8 +5014,9 @@ class coinbase extends coinbase$1["default"] {
5014
5014
  }
5015
5015
  return parsedPositions;
5016
5016
  }
5017
- createAuthToken(seconds, method = undefined, url = undefined) {
5018
- // it may not work for v2
5017
+ createAuthToken(seconds, method = undefined, url = undefined, useEddsa = false) {
5018
+ // v1 https://docs.cdp.coinbase.com/api-reference/authentication#php-2
5019
+ // v2 https://docs.cdp.coinbase.com/api-reference/v2/authentication
5019
5020
  let uri = undefined;
5020
5021
  if (url !== undefined) {
5021
5022
  uri = method + ' ' + url.replace('https://', '');
@@ -5026,20 +5027,35 @@ class coinbase extends coinbase$1["default"] {
5026
5027
  uri = uri.slice(0, quesPos);
5027
5028
  }
5028
5029
  }
5030
+ // eddsa {"sub":"d2efa49a-369c-43d7-a60e-ae26e28853c2","iss":"cdp","aud":["cdp_service"],"uris":["GET api.coinbase.com/api/v3/brokerage/transaction_summary"]}
5029
5031
  const nonce = this.randomBytes(16);
5032
+ const aud = useEddsa ? 'cdp_service' : 'retail_rest_api_proxy';
5033
+ const iss = useEddsa ? 'cdp' : 'coinbase-cloud';
5030
5034
  const request = {
5031
- 'aud': ['retail_rest_api_proxy'],
5032
- 'iss': 'coinbase-cloud',
5035
+ 'aud': [aud],
5036
+ 'iss': iss,
5033
5037
  'nbf': seconds,
5034
5038
  'exp': seconds + 120,
5035
5039
  'sub': this.apiKey,
5036
5040
  'iat': seconds,
5037
5041
  };
5038
5042
  if (uri !== undefined) {
5039
- request['uri'] = uri;
5043
+ if (!useEddsa) {
5044
+ request['uri'] = uri;
5045
+ }
5046
+ else {
5047
+ request['uris'] = [uri];
5048
+ }
5049
+ }
5050
+ if (useEddsa) {
5051
+ const byteArray = this.base64ToBinary(this.secret);
5052
+ const seed = this.arraySlice(byteArray, 0, 32);
5053
+ return rsa.jwt(request, seed, sha256.sha256, false, { 'kid': this.apiKey, 'nonce': nonce, 'alg': 'EdDSA' });
5054
+ }
5055
+ else {
5056
+ // ecdsa with p256
5057
+ return rsa.jwt(request, this.encode(this.secret), sha256.sha256, false, { 'kid': this.apiKey, 'nonce': nonce, 'alg': 'ES256' });
5040
5058
  }
5041
- const token = rsa.jwt(request, this.encode(this.secret), sha256.sha256, false, { 'kid': this.apiKey, 'nonce': nonce, 'alg': 'ES256' });
5042
- return token;
5043
5059
  }
5044
5060
  nonce() {
5045
5061
  return this.milliseconds() - this.options['timeDifference'];
@@ -5089,8 +5105,10 @@ class coinbase extends coinbase$1["default"] {
5089
5105
  // v2: 'GET' require payload in the signature
5090
5106
  // https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-key-authentication
5091
5107
  const isCloudAPiKey = (this.apiKey.indexOf('organizations/') >= 0) || (this.secret.startsWith('-----BEGIN'));
5092
- if (isCloudAPiKey) {
5093
- if (this.apiKey.startsWith('-----BEGIN')) {
5108
+ // using the size might be fragile, so we add an option to force v2 cloud api key if needed
5109
+ const isV2CloudAPiKey = this.secret.length === 88 || this.safeBool(this.options, 'v2CloudAPiKey', false) || this.secret.endsWith('=');
5110
+ if (isCloudAPiKey || isV2CloudAPiKey) {
5111
+ if (isCloudAPiKey && this.apiKey.startsWith('-----BEGIN')) {
5094
5112
  throw new errors.ArgumentsRequired(this.id + ' apiKey should contain the name (eg: organizations/3b910e93....) and not the public key');
5095
5113
  }
5096
5114
  // // it may not work for v2
@@ -5111,7 +5129,7 @@ class coinbase extends coinbase$1["default"] {
5111
5129
  // 'uri': uri,
5112
5130
  // 'iat': seconds,
5113
5131
  // };
5114
- const token = this.createAuthToken(seconds, method, url);
5132
+ const token = this.createAuthToken(seconds, method, url, isV2CloudAPiKey);
5115
5133
  // const token = jwt (request, this.encode (this.secret), sha256, false, { 'kid': this.apiKey, 'nonce': nonce, 'alg': 'ES256' });
5116
5134
  authorizationString = 'Bearer ' + token;
5117
5135
  }
@@ -610,8 +610,8 @@ class coincatch extends coincatch$1["default"] {
610
610
  for (let j = 0; j < networks.length; j++) {
611
611
  const network = networks[j];
612
612
  const networkId = this.safeString(network, 'chain');
613
- const networkCode = this.networkCodeToId(networkId);
614
- parsedNetworks[networkId] = {
613
+ const networkCode = this.networkIdToCode(networkId);
614
+ parsedNetworks[networkCode] = {
615
615
  'id': networkId,
616
616
  'network': networkCode,
617
617
  'limits': {
@@ -2306,6 +2306,7 @@ class coincatch extends coincatch$1["default"] {
2306
2306
  * @param {float} amount how much of you want to trade in units of the base currency
2307
2307
  * @param {float} [price] the price that the order is to be fulfilled, in units of the quote currency, ignored in market orders
2308
2308
  * @param {object} [params] extra parameters specific to the exchange API endpoint
2309
+ * @param {bool} [params.hedged] *swap markets only* must be set to true if position mode is hedged (default false)
2309
2310
  * @param {float} [params.cost] *spot market buy only* the quote quantity that can be used as an alternative for the amount
2310
2311
  * @param {float} [params.triggerPrice] the price that the order is to be triggered
2311
2312
  * @param {bool} [params.postOnly] if true, the order will only be posted to the order book and not executed immediately
@@ -2508,6 +2509,7 @@ class coincatch extends coincatch$1["default"] {
2508
2509
  * @param {float} amount how much of you want to trade in units of the base currency
2509
2510
  * @param {float} [price] the price that the order is to be fulfilled, in units of the quote currency, ignored in market orders
2510
2511
  * @param {object} [params] extra parameters specific to the exchange API endpoint
2512
+ * @param {bool} [params.hedged] must be set to true if position mode is hedged (default false)
2511
2513
  * @param {bool} [params.postOnly] *non-trigger orders only* if true, the order will only be posted to the order book and not executed immediately
2512
2514
  * @param {bool} [params.reduceOnly] true or false whether the order is reduce only
2513
2515
  * @param {string} [params.timeInForce] *non-trigger orders only* 'GTC', 'FOK', 'IOC' or 'PO'
@@ -2565,7 +2567,7 @@ class coincatch extends coincatch$1["default"] {
2565
2567
  * @param {float} amount how much of you want to trade in units of the base currency
2566
2568
  * @param {float} [price] the price that the order is to be fulfilled, in units of the quote currency, ignored in market orders
2567
2569
  * @param {object} [params] extra parameters specific to the exchange API endpoint
2568
- * @param {bool} [params.hedged] default false
2570
+ * @param {bool} [params.hedged] must be set to true if position mode is hedged (default false)
2569
2571
  * @param {bool} [params.postOnly] *non-trigger orders only* if true, the order will only be posted to the order book and not executed immediately
2570
2572
  * @param {bool} [params.reduceOnly] true or false whether the order is reduce only
2571
2573
  * @param {string} [params.timeInForce] *non-trigger orders only* 'GTC', 'FOK', 'IOC' or 'PO'
@@ -2604,31 +2606,42 @@ class coincatch extends coincatch$1["default"] {
2604
2606
  }
2605
2607
  if ((endpointType !== 'tpsl')) {
2606
2608
  request['orderType'] = type;
2609
+ let sideIsExchangeSpecific = false;
2607
2610
  let hedged = false;
2608
- [hedged, params] = this.handleOptionAndParams(params, methodName, 'hedged', hedged);
2609
- // hedged and non-hedged orders have different side values and reduceOnly handling
2610
- let reduceOnly = false;
2611
- [reduceOnly, params] = this.handleParamBool(params, 'reduceOnly', reduceOnly);
2612
- if (hedged) {
2613
- if (reduceOnly) {
2614
- if (side === 'buy') {
2615
- side = 'close_short';
2611
+ if ((side === 'buy_single') || (side === 'sell_single') || (side === 'open_long') || (side === 'open_short') || (side === 'close_long') || (side === 'close_short')) {
2612
+ sideIsExchangeSpecific = true;
2613
+ if ((side !== 'buy_single') && (side !== 'sell_single')) {
2614
+ hedged = true;
2615
+ }
2616
+ }
2617
+ if (!sideIsExchangeSpecific) {
2618
+ [hedged, params] = this.handleOptionAndParams(params, methodName, 'hedged', hedged);
2619
+ // hedged and non-hedged orders have different side values and reduceOnly handling
2620
+ const reduceOnly = this.safeBool(params, 'reduceOnly');
2621
+ if (hedged) {
2622
+ if ((reduceOnly !== undefined) && reduceOnly) {
2623
+ if (side === 'buy') {
2624
+ side = 'close_short';
2625
+ }
2626
+ else if (side === 'sell') {
2627
+ side = 'close_long';
2628
+ }
2616
2629
  }
2617
- else if (side === 'sell') {
2618
- side = 'close_long';
2630
+ else {
2631
+ if (side === 'buy') {
2632
+ side = 'open_long';
2633
+ }
2634
+ else if (side === 'sell') {
2635
+ side = 'open_short';
2636
+ }
2619
2637
  }
2620
2638
  }
2621
2639
  else {
2622
- if (side === 'buy') {
2623
- side = 'open_long';
2624
- }
2625
- else if (side === 'sell') {
2626
- side = 'open_short';
2627
- }
2640
+ side = side.toLowerCase() + '_single';
2628
2641
  }
2629
2642
  }
2630
- else {
2631
- side = side.toLowerCase() + '_single';
2643
+ if (hedged) {
2644
+ params = this.omit(params, 'reduceOnly');
2632
2645
  }
2633
2646
  request['side'] = side;
2634
2647
  }
@@ -215,6 +215,7 @@ class delta extends delta$1["default"] {
215
215
  },
216
216
  },
217
217
  },
218
+ 'userAgent': this.userAgents['chrome39'],
218
219
  'options': {
219
220
  'networks': {
220
221
  'TRC20': 'TRC20(TRON)',
@@ -56,14 +56,24 @@ class gate extends gate$1["default"] {
56
56
  },
57
57
  'test': {
58
58
  'public': {
59
- 'futures': 'https://fx-api-testnet.gateio.ws/api/v4',
60
- 'delivery': 'https://fx-api-testnet.gateio.ws/api/v4',
61
- 'options': 'https://fx-api-testnet.gateio.ws/api/v4',
59
+ 'futures': 'https://api-testnet.gateapi.io/api/v4',
60
+ 'delivery': 'https://api-testnet.gateapi.io/api/v4',
61
+ 'options': 'https://api-testnet.gateapi.io/api/v4',
62
+ 'spot': 'https://api-testnet.gateapi.io/api/v4',
63
+ 'wallet': 'https://api-testnet.gateapi.io/api/v4',
64
+ 'margin': 'https://api-testnet.gateapi.io/api/v4',
65
+ 'sub_accounts': 'https://api-testnet.gateapi.io/api/v4',
66
+ 'account': 'https://api-testnet.gateapi.io/api/v4',
62
67
  },
63
68
  'private': {
64
- 'futures': 'https://fx-api-testnet.gateio.ws/api/v4',
65
- 'delivery': 'https://fx-api-testnet.gateio.ws/api/v4',
66
- 'options': 'https://fx-api-testnet.gateio.ws/api/v4',
69
+ 'futures': 'https://api-testnet.gateapi.io/api/v4',
70
+ 'delivery': 'https://api-testnet.gateapi.io/api/v4',
71
+ 'options': 'https://api-testnet.gateapi.io/api/v4',
72
+ 'spot': 'https://api-testnet.gateapi.io/api/v4',
73
+ 'wallet': 'https://api-testnet.gateapi.io/api/v4',
74
+ 'margin': 'https://api-testnet.gateapi.io/api/v4',
75
+ 'sub_accounts': 'https://api-testnet.gateapi.io/api/v4',
76
+ 'account': 'https://api-testnet.gateapi.io/api/v4',
67
77
  },
68
78
  },
69
79
  'referral': {
@@ -1230,16 +1240,15 @@ class gate extends gate$1["default"] {
1230
1240
  await this.loadUnifiedStatus();
1231
1241
  }
1232
1242
  const rawPromises = [];
1233
- const sandboxMode = this.safeBool(this.options, 'sandboxMode', false);
1234
1243
  const fetchMarketsOptions = this.safeDict(this.options, 'fetchMarkets');
1235
1244
  const types = this.safeList(fetchMarketsOptions, 'types', ['spot', 'swap', 'future', 'option']);
1236
1245
  for (let i = 0; i < types.length; i++) {
1237
1246
  const marketType = types[i];
1238
1247
  if (marketType === 'spot') {
1239
- if (!sandboxMode) {
1240
- // gate doesn't have a sandbox for spot markets
1241
- rawPromises.push(this.fetchSpotMarkets(params));
1242
- }
1248
+ // if (!sandboxMode) {
1249
+ // gate doesn't have a sandbox for spot markets
1250
+ rawPromises.push(this.fetchSpotMarkets(params));
1251
+ // }
1243
1252
  }
1244
1253
  else if (marketType === 'swap') {
1245
1254
  rawPromises.push(this.fetchSwapMarkets(params));
@@ -1370,7 +1379,10 @@ class gate extends gate$1["default"] {
1370
1379
  }
1371
1380
  async fetchSwapMarkets(params = {}) {
1372
1381
  const result = [];
1373
- const swapSettlementCurrencies = this.getSettlementCurrencies('swap', 'fetchMarkets');
1382
+ let swapSettlementCurrencies = this.getSettlementCurrencies('swap', 'fetchMarkets');
1383
+ if (this.options['sandboxMode']) {
1384
+ swapSettlementCurrencies = ['usdt']; // gate sandbox only has usdt-margined swaps
1385
+ }
1374
1386
  for (let c = 0; c < swapSettlementCurrencies.length; c++) {
1375
1387
  const settleId = swapSettlementCurrencies[c];
1376
1388
  const request = {
@@ -1385,6 +1397,9 @@ class gate extends gate$1["default"] {
1385
1397
  return result;
1386
1398
  }
1387
1399
  async fetchFutureMarkets(params = {}) {
1400
+ if (this.options['sandboxMode']) {
1401
+ return []; // right now sandbox does not have inverse swaps
1402
+ }
1388
1403
  const result = [];
1389
1404
  const futureSettlementCurrencies = this.getSettlementCurrencies('future', 'fetchMarkets');
1390
1405
  for (let c = 0; c < futureSettlementCurrencies.length; c++) {
@@ -693,8 +693,8 @@ class gemini extends gemini$1["default"] {
693
693
  //
694
694
  // [
695
695
  // 'BTCUSD', // symbol
696
- // 2, // priceTickDecimalPlaces
697
- // 8, // quantityTickDecimalPlaces
696
+ // 2, // tick precision (priceTickDecimalPlaces)
697
+ // 8, // amount precision (quantityTickDecimalPlaces)
698
698
  // '0.00001', // quantityMinimum
699
699
  // 10, // quantityRoundDecimalPlaces
700
700
  // true // minimumsAreInclusive
@@ -713,7 +713,7 @@ class gemini extends gemini$1["default"] {
713
713
  // "wrap_enabled": false
714
714
  // "product_type": "swap", // only in perps
715
715
  // "contract_type": "linear", // only in perps
716
- // "contract_price_currency": "GUSD" // only in perps
716
+ // "contract_price_currency": "GUSD"
717
717
  // }
718
718
  //
719
719
  let marketId = undefined;