ccxt 4.5.29 → 4.5.31

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 (85) hide show
  1. package/README.md +4 -4
  2. package/dist/ccxt.browser.min.js +18 -18
  3. package/dist/cjs/ccxt.js +6 -4
  4. package/dist/cjs/src/base/Exchange.js +16 -1
  5. package/dist/cjs/src/base/ws/WsClient.js +1 -0
  6. package/dist/cjs/src/bingx.js +1 -1
  7. package/dist/cjs/src/bullish.js +1 -1
  8. package/dist/cjs/src/bybit.js +2 -0
  9. package/dist/cjs/src/bydfi.js +2907 -0
  10. package/dist/cjs/src/coinbase.js +34 -1
  11. package/dist/cjs/src/dydx.js +3 -3
  12. package/dist/cjs/src/gate.js +55 -8
  13. package/dist/cjs/src/hyperliquid.js +19 -4
  14. package/dist/cjs/src/kucoin.js +175 -68
  15. package/dist/cjs/src/pro/apex.js +7 -5
  16. package/dist/cjs/src/pro/ascendex.js +1 -1
  17. package/dist/cjs/src/pro/binance.js +10 -6
  18. package/dist/cjs/src/pro/bingx.js +6 -4
  19. package/dist/cjs/src/pro/bitmart.js +5 -3
  20. package/dist/cjs/src/pro/bybit.js +6 -4
  21. package/dist/cjs/src/pro/bydfi.js +1077 -0
  22. package/dist/cjs/src/pro/cryptocom.js +6 -4
  23. package/dist/cjs/src/pro/gate.js +5 -3
  24. package/dist/cjs/src/pro/hashkey.js +5 -3
  25. package/dist/cjs/src/pro/htx.js +1 -1
  26. package/dist/cjs/src/pro/hyperliquid.js +1 -1
  27. package/dist/cjs/src/pro/kucoinfutures.js +5 -3
  28. package/dist/cjs/src/pro/modetrade.js +5 -3
  29. package/dist/cjs/src/pro/p2b.js +1 -1
  30. package/dist/cjs/src/pro/toobit.js +12 -8
  31. package/dist/cjs/src/pro/woo.js +5 -3
  32. package/dist/cjs/src/pro/woofipro.js +5 -3
  33. package/dist/cjs/src/pro/xt.js +5 -3
  34. package/dist/cjs/src/toobit.js +2 -1
  35. package/js/ccxt.d.ts +8 -5
  36. package/js/ccxt.js +6 -4
  37. package/js/src/abstract/bydfi.d.ts +52 -0
  38. package/js/src/abstract/kucoin.d.ts +2 -0
  39. package/js/src/abstract/kucoinfutures.d.ts +2 -0
  40. package/js/src/base/Exchange.d.ts +3 -0
  41. package/js/src/base/Exchange.js +16 -1
  42. package/js/src/base/ws/WsClient.js +1 -0
  43. package/js/src/binance.d.ts +1 -1
  44. package/js/src/bingx.js +1 -1
  45. package/js/src/bullish.js +1 -1
  46. package/js/src/bybit.js +2 -0
  47. package/js/src/bydfi.d.ts +472 -0
  48. package/js/src/bydfi.js +2905 -0
  49. package/js/src/coinbase.d.ts +11 -0
  50. package/js/src/coinbase.js +34 -1
  51. package/js/src/dydx.js +3 -3
  52. package/js/src/exmo.d.ts +1 -1
  53. package/js/src/gate.js +55 -8
  54. package/js/src/hyperliquid.d.ts +1 -0
  55. package/js/src/hyperliquid.js +19 -4
  56. package/js/src/kucoin.d.ts +19 -3
  57. package/js/src/kucoin.js +175 -68
  58. package/js/src/pro/apex.js +7 -5
  59. package/js/src/pro/ascendex.js +1 -1
  60. package/js/src/pro/binance.js +10 -6
  61. package/js/src/pro/bingx.js +6 -4
  62. package/js/src/pro/bitmart.js +5 -3
  63. package/js/src/pro/bybit.js +6 -4
  64. package/js/src/pro/bydfi.d.ts +206 -0
  65. package/js/src/pro/bydfi.js +1076 -0
  66. package/js/src/pro/cryptocom.js +6 -4
  67. package/js/src/pro/gate.js +5 -3
  68. package/js/src/pro/hashkey.js +5 -3
  69. package/js/src/pro/htx.js +1 -1
  70. package/js/src/pro/hyperliquid.js +1 -1
  71. package/js/src/pro/kucoinfutures.js +5 -3
  72. package/js/src/pro/modetrade.js +5 -3
  73. package/js/src/pro/p2b.js +1 -1
  74. package/js/src/pro/toobit.js +12 -8
  75. package/js/src/pro/woo.js +5 -3
  76. package/js/src/pro/woofipro.js +5 -3
  77. package/js/src/pro/xt.js +5 -3
  78. package/js/src/toobit.js +2 -1
  79. package/package.json +1 -1
  80. package/dist/cjs/src/oceanex.js +0 -1125
  81. package/js/src/abstract/oceanex.d.ts +0 -30
  82. package/js/src/oceanex.d.ts +0 -231
  83. package/js/src/oceanex.js +0 -1124
  84. /package/dist/cjs/src/abstract/{oceanex.js → bydfi.js} +0 -0
  85. /package/js/src/abstract/{oceanex.js → bydfi.js} +0 -0
@@ -620,4 +620,15 @@ export default class coinbase extends Exchange {
620
620
  headers: any;
621
621
  };
622
622
  handleErrors(code: int, reason: string, url: string, method: string, headers: Dict, body: string, response: any, requestHeaders: any, requestBody: any): any;
623
+ /**
624
+ * @method
625
+ * @name coinbase#fetchDepositAddresses
626
+ * @description fetch deposit addresses for multiple currencies (when available)
627
+ * @see https://coinbase-migration.mintlify.app/coinbase-app/transfer-apis/onchain-addresses
628
+ * @param {string[]} [codes] list of unified currency codes, default is undefined (all currencies)
629
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
630
+ * @param {string} [params.accountId] account ID to fetch deposit addresses for
631
+ * @returns {object} a dictionary of [address structures]{@link https://docs.ccxt.com/#/?id=address-structure} indexed by currency code
632
+ */
633
+ fetchDepositAddresses(codes?: Strings, params?: {}): Promise<DepositAddress[]>;
623
634
  }
@@ -88,7 +88,7 @@ export default class coinbase extends Exchange {
88
88
  'fetchCurrencies': true,
89
89
  'fetchDeposit': true,
90
90
  'fetchDepositAddress': 'emulated',
91
- 'fetchDepositAddresses': false,
91
+ 'fetchDepositAddresses': true,
92
92
  'fetchDepositAddressesByNetwork': true,
93
93
  'fetchDepositMethodId': true,
94
94
  'fetchDepositMethodIds': true,
@@ -4302,6 +4302,19 @@ export default class coinbase extends Exchange {
4302
4302
  // }
4303
4303
  // }
4304
4304
  //
4305
+ // {
4306
+ // "id":"3f2434234943-8c1c-50ef-a5a1-342213bbf45d",
4307
+ // "address":"0x123123126F5921XXXXX",
4308
+ // "currency":"USDC",
4309
+ // "name":"",
4310
+ // "network":"ethereum",
4311
+ // "created_at":"2022-03-17T09:20:17.002Z",
4312
+ // "updated_at":"2022-03-17T09:20:17.002Z",
4313
+ // "resource":"addresses",
4314
+ // "resource_path":"v2/accounts/b1091c6e-9ef2-5e4d-b352-665d0cf8f742/addresses/32fd0943-8c1c-50ef-a5a1-342213bbf45d",
4315
+ // "destination_tag":""
4316
+ // }
4317
+ //
4305
4318
  const address = this.safeString(depositAddress, 'address');
4306
4319
  this.checkAddress(address);
4307
4320
  const networkId = this.safeString(depositAddress, 'network');
@@ -4312,6 +4325,9 @@ export default class coinbase extends Exchange {
4312
4325
  const splitAddressLabel = addressLabel.split(' ');
4313
4326
  currencyId = this.safeString(splitAddressLabel, 0);
4314
4327
  }
4328
+ else {
4329
+ currencyId = this.safeString(depositAddress, 'currency');
4330
+ }
4315
4331
  const addressInfo = this.safeDict(depositAddress, 'address_info');
4316
4332
  return {
4317
4333
  'info': depositAddress,
@@ -5236,4 +5252,21 @@ export default class coinbase extends Exchange {
5236
5252
  }
5237
5253
  return undefined;
5238
5254
  }
5255
+ /**
5256
+ * @method
5257
+ * @name coinbase#fetchDepositAddresses
5258
+ * @description fetch deposit addresses for multiple currencies (when available)
5259
+ * @see https://coinbase-migration.mintlify.app/coinbase-app/transfer-apis/onchain-addresses
5260
+ * @param {string[]} [codes] list of unified currency codes, default is undefined (all currencies)
5261
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
5262
+ * @param {string} [params.accountId] account ID to fetch deposit addresses for
5263
+ * @returns {object} a dictionary of [address structures]{@link https://docs.ccxt.com/#/?id=address-structure} indexed by currency code
5264
+ */
5265
+ async fetchDepositAddresses(codes = undefined, params = {}) {
5266
+ await this.loadMarkets();
5267
+ const request = this.prepareAccountRequest(undefined, params);
5268
+ const response = await this.v2PrivateGetAccountsAccountIdAddresses(this.extend(request, params));
5269
+ const data = this.safeList(response, 'data', []);
5270
+ return this.parseDepositAddresses(data, codes, false, {});
5271
+ }
5239
5272
  }
package/js/src/dydx.js CHANGED
@@ -506,9 +506,9 @@ export default class dydx extends Exchange {
506
506
  const marketId = this.safeString(market, 'ticker');
507
507
  const parts = marketId.split('-');
508
508
  const baseName = this.safeString(parts, 0);
509
- const base = this.safeCurrencyCode(baseName);
509
+ const baseId = this.safeString(market, 'baseId', baseName); // idk where 'baseId' comes from, but leaving as is
510
+ const base = this.safeCurrencyCode(baseId);
510
511
  const quote = this.safeCurrencyCode(quoteId);
511
- const baseId = this.safeString(market, 'baseId');
512
512
  const settleId = 'USDC';
513
513
  const settle = this.safeCurrencyCode(settleId);
514
514
  const symbol = base + '/' + quote + ':' + settle;
@@ -539,11 +539,11 @@ export default class dydx extends Exchange {
539
539
  'option': false,
540
540
  'active': active,
541
541
  'contract': contract,
542
+ 'contractSize': this.parseNumber('1'),
542
543
  'linear': true,
543
544
  'inverse': false,
544
545
  'taker': undefined,
545
546
  'maker': undefined,
546
- 'contractSize': undefined,
547
547
  'expiry': undefined,
548
548
  'expiryDatetime': undefined,
549
549
  'strike': undefined,
package/js/src/exmo.d.ts CHANGED
@@ -308,7 +308,7 @@ export default class exmo extends Exchange {
308
308
  * @param {string} [params.marginMode] set to "isolated" for margin orders
309
309
  * @returns {object} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure}
310
310
  */
311
- fetchCanceledOrders(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise<any[]>;
311
+ fetchCanceledOrders(symbol?: Str, since?: Int, limit?: Int, params?: {}): Promise<Order[]>;
312
312
  /**
313
313
  * @method
314
314
  * @name exmo#editOrder
package/js/src/gate.js CHANGED
@@ -1041,7 +1041,7 @@ export default class gate extends Exchange {
1041
1041
  // https://www.gate.com/docs/developers/apiv4/en/#label-list
1042
1042
  'exceptions': {
1043
1043
  'exact': {
1044
- 'INVALID_PARAM_VALUE': BadRequest,
1044
+ 'INVALID_PARAM_VALUE': InvalidOrder,
1045
1045
  'INVALID_PROTOCOL': BadRequest,
1046
1046
  'INVALID_ARGUMENT': BadRequest,
1047
1047
  'INVALID_REQUEST_BODY': BadRequest,
@@ -1136,7 +1136,8 @@ export default class gate extends Exchange {
1136
1136
  'AUTO_TRIGGER_PRICE_GREATE_LAST': InvalidOrder,
1137
1137
  'POSITION_HOLDING': BadRequest,
1138
1138
  'USER_LOAN_EXCEEDED': BadRequest,
1139
- 'NO_CHANGE': InvalidOrder, // {"label":"NO_CHANGE","message":"No change is made"}
1139
+ 'NO_CHANGE': InvalidOrder,
1140
+ 'PRICE_THRESHOLD_EXCEEDED': InvalidOrder, // {"label":"PRICE_THRESHOLD_EXCEEDED","message":": 0.45288"}
1140
1141
  },
1141
1142
  'broad': {},
1142
1143
  },
@@ -4972,6 +4973,24 @@ export default class gate extends Exchange {
4972
4973
  //
4973
4974
  // {"user_id":10406147,"id":"id","succeeded":false,"message":"INVALID_PROTOCOL","label":"INVALID_PROTOCOL"}
4974
4975
  //
4976
+ // cancel trigger order returns timestamps in ms
4977
+ // id: '2007047737421336576',
4978
+ // id_string: '2007047737421336576',
4979
+ // trigger_time: '0',
4980
+ // trade_id: '0',
4981
+ // trade_id_string: '',
4982
+ // status: 'finished',
4983
+ // finish_as: 'cancelled',
4984
+ // reason: '',
4985
+ // create_time: '1767352444402496'
4986
+ // finish_time: '1767352509535790',
4987
+ // is_stop_order: false,
4988
+ // stop_trigger: { rule: '0', trigger_price: '', order_price: '' },
4989
+ // me_order_id: '0',
4990
+ // me_order_id_string: '',
4991
+ // order_type: '',
4992
+ // in_dual_mode: false,
4993
+ // parent_id: '0',
4975
4994
  const succeeded = this.safeBool(order, 'succeeded', true);
4976
4995
  if (!succeeded) {
4977
4996
  // cancelOrders response
@@ -5014,13 +5033,33 @@ export default class gate extends Exchange {
5014
5033
  side = Precise.stringGt(amount, '0') ? 'buy' : 'sell';
5015
5034
  }
5016
5035
  const rawStatus = this.safeStringN(order, ['finish_as', 'status', 'open']);
5017
- let timestamp = this.safeInteger(order, 'create_time_ms');
5018
- if (timestamp === undefined) {
5019
- timestamp = this.safeTimestamp2(order, 'create_time', 'ctime');
5036
+ let timestampStr = this.safeString(order, 'create_time_ms');
5037
+ if (timestampStr === undefined) {
5038
+ timestampStr = this.safeString2(order, 'create_time', 'ctime');
5039
+ if (timestampStr !== undefined) {
5040
+ if (timestampStr.length === 10 || timestampStr.indexOf('.') >= 0) {
5041
+ // ts in seconds, multiply to ms
5042
+ timestampStr = Precise.stringMul(timestampStr, '1000');
5043
+ }
5044
+ else if (timestampStr.length === 16) {
5045
+ // ts in microseconds, divide to ms
5046
+ timestampStr = Precise.stringDiv(timestampStr, '1000');
5047
+ }
5048
+ }
5020
5049
  }
5021
- let lastTradeTimestamp = this.safeInteger(order, 'update_time_ms');
5022
- if (lastTradeTimestamp === undefined) {
5023
- lastTradeTimestamp = this.safeTimestamp2(order, 'update_time', 'finish_time');
5050
+ let lastTradeTimestampStr = this.safeString(order, 'update_time_ms');
5051
+ if (lastTradeTimestampStr === undefined) {
5052
+ lastTradeTimestampStr = this.safeString2(order, 'update_time', 'finish_time');
5053
+ if (lastTradeTimestampStr !== undefined) {
5054
+ if (lastTradeTimestampStr.length === 10 || lastTradeTimestampStr.indexOf('.') >= 0) {
5055
+ // ts in seconds, multiply to ms
5056
+ lastTradeTimestampStr = Precise.stringMul(lastTradeTimestampStr, '1000');
5057
+ }
5058
+ else if (lastTradeTimestampStr.length === 16) {
5059
+ // ts in microseconds, divide to ms
5060
+ lastTradeTimestampStr = Precise.stringDiv(lastTradeTimestampStr, '1000');
5061
+ }
5062
+ }
5024
5063
  }
5025
5064
  let marketType = 'contract';
5026
5065
  if (('currency_pair' in order) || ('market' in order)) {
@@ -5067,6 +5106,14 @@ export default class gate extends Exchange {
5067
5106
  amount = Precise.stringDiv(amount, averageString);
5068
5107
  }
5069
5108
  }
5109
+ let timestamp = undefined;
5110
+ let lastTradeTimestamp = undefined;
5111
+ if (timestampStr !== undefined) {
5112
+ timestamp = this.parseToInt(timestampStr);
5113
+ }
5114
+ if (lastTradeTimestampStr !== undefined) {
5115
+ lastTradeTimestamp = this.parseToInt(lastTradeTimestampStr);
5116
+ }
5070
5117
  return this.safeOrder({
5071
5118
  'id': this.safeString(order, 'id'),
5072
5119
  'clientOrderId': this.safeString(order, 'text'),
@@ -88,6 +88,7 @@ export default class hyperliquid extends Exchange {
88
88
  */
89
89
  fetchSpotMarkets(params?: {}): Promise<Market[]>;
90
90
  parseMarket(market: Dict): Market;
91
+ updateSpotCurrencyCode(code: string): string;
91
92
  /**
92
93
  * @method
93
94
  * @name hyperliquid#fetchBalance
@@ -579,17 +579,24 @@ export default class hyperliquid extends Exchange {
579
579
  let fetchDexesList = [];
580
580
  const options = this.safeDict(this.options, 'fetchMarkets', {});
581
581
  const hip3 = this.safeDict(options, 'hip3', {});
582
- const dexesProvided = this.safeList(hip3, 'dexes'); // let users provide their own list of dexes to load
582
+ const dexesProvided = this.safeList(hip3, 'dexes', []); // let users provide their own list of dexes to load
583
583
  const maxLimit = this.safeInteger(hip3, 'limit', 10);
584
- if (dexesProvided !== undefined) {
585
- const userProvidedDexesLength = dexesProvided.length;
584
+ const userProvidedDexesLength = dexesProvided.length;
585
+ if (userProvidedDexesLength > 0) {
586
586
  if (userProvidedDexesLength > 0) {
587
587
  fetchDexesList = dexesProvided;
588
588
  }
589
589
  }
590
590
  else {
591
+ const fetchDexesLength = fetchDexes.length;
591
592
  for (let i = 1; i < maxLimit; i++) {
593
+ if (i >= fetchDexesLength) {
594
+ break;
595
+ }
592
596
  const dex = this.safeDict(fetchDexes, i, {});
597
+ if (dex === undefined) {
598
+ continue;
599
+ }
593
600
  const dexName = this.safeString(dex, 'name');
594
601
  fetchDexesList.push(dexName);
595
602
  }
@@ -1056,6 +1063,13 @@ export default class hyperliquid extends Exchange {
1056
1063
  'info': market,
1057
1064
  });
1058
1065
  }
1066
+ updateSpotCurrencyCode(code) {
1067
+ if (code === undefined) {
1068
+ return code;
1069
+ }
1070
+ const spotCurrencyMapping = this.safeDict(this.options, 'spotCurrencyMapping', {});
1071
+ return this.safeString(spotCurrencyMapping, code, code);
1072
+ }
1059
1073
  /**
1060
1074
  * @method
1061
1075
  * @name hyperliquid#fetchBalance
@@ -1123,7 +1137,8 @@ export default class hyperliquid extends Exchange {
1123
1137
  const spotBalances = { 'info': response };
1124
1138
  for (let i = 0; i < balances.length; i++) {
1125
1139
  const balance = balances[i];
1126
- const code = this.safeCurrencyCode(this.safeString(balance, 'coin'));
1140
+ const unifiedCode = this.safeCurrencyCode(this.safeString(balance, 'coin'));
1141
+ const code = isSpot ? this.updateSpotCurrencyCode(unifiedCode) : unifiedCode;
1127
1142
  const account = this.account();
1128
1143
  const total = this.safeString(balance, 'total');
1129
1144
  const used = this.safeString(balance, 'hold');
@@ -569,17 +569,19 @@ export default class kucoin extends Exchange {
569
569
  * @method
570
570
  * @name kucoin#transfer
571
571
  * @description transfer currency internally between wallets on the same account
572
- * @see https://www.kucoin.com/docs/rest/funding/transfer/inner-transfer
573
- * @see https://docs.kucoin.com/futures/#transfer-funds-to-kucoin-main-account-2
574
- * @see https://docs.kucoin.com/spot-hf/#internal-funds-transfers-in-high-frequency-trading-accounts
572
+ * @see https://www.kucoin.com/docs-new/rest/account-info/transfer/flex-transfer?lang=en_US&
575
573
  * @param {string} code unified currency code
576
574
  * @param {float} amount amount to transfer
577
575
  * @param {string} fromAccount account to transfer from
578
576
  * @param {string} toAccount account to transfer to
579
577
  * @param {object} [params] extra parameters specific to the exchange API endpoint
578
+ * @param {string} [params.transferType] INTERNAL, PARENT_TO_SUB, SUB_TO_PARENT (default is INTERNAL)
579
+ * @param {string} [params.fromUserId] required if transferType is SUB_TO_PARENT
580
+ * @param {string} [params.toUserId] required if transferType is PARENT_TO_SUB
580
581
  * @returns {object} a [transfer structure]{@link https://docs.ccxt.com/?id=transfer-structure}
581
582
  */
582
583
  transfer(code: string, amount: number, fromAccount: string, toAccount: string, params?: {}): Promise<TransferEntry>;
584
+ isHfOrMining(fromId: Str, toId: Str): boolean;
583
585
  parseTransfer(transfer: Dict, currency?: Currency): TransferEntry;
584
586
  parseTransferStatus(status: Str): Str;
585
587
  parseLedgerEntryType(type: any): string;
@@ -803,4 +805,18 @@ export default class kucoin extends Exchange {
803
805
  headers: any;
804
806
  };
805
807
  handleErrors(code: int, reason: string, url: string, method: string, headers: Dict, body: string, response: any, requestHeaders: any, requestBody: any): any;
808
+ /**
809
+ * @method
810
+ * @name kucoin#fetchTransfers
811
+ * @description fetch a history of internal transfers made on an account
812
+ * @see https://www.kucoin.com/docs-new/rest/account-info/account-funding/get-account-ledgers-spot-margin
813
+ * @param {string} [code] unified currency code of the currency transferred
814
+ * @param {int} [since] the earliest time in ms to fetch transfers for
815
+ * @param {int} [limit] the maximum number of transfer structures to retrieve
816
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
817
+ * @param {int} [params.until] the latest time in ms to fetch transfers for
818
+ * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
819
+ * @returns {object[]} a list of [transfer structures]{@link https://docs.ccxt.com/?id=transfer-structure}
820
+ */
821
+ fetchTransfers(code?: Str, since?: Int, limit?: Int, params?: {}): Promise<TransferEntry[]>;
806
822
  }
package/js/src/kucoin.js CHANGED
@@ -106,7 +106,7 @@ export default class kucoin extends Exchange {
106
106
  'fetchTradingFee': true,
107
107
  'fetchTradingFees': false,
108
108
  'fetchTransactionFee': true,
109
- 'fetchTransfers': false,
109
+ 'fetchTransfers': true,
110
110
  'fetchWithdrawals': true,
111
111
  'repayCrossMargin': true,
112
112
  'repayIsolatedMargin': true,
@@ -183,6 +183,7 @@ export default class kucoin extends Exchange {
183
183
  'get': {
184
184
  // account
185
185
  'user-info': 30,
186
+ 'user/api-key': 30,
186
187
  'accounts': 7.5,
187
188
  'accounts/{accountId}': 7.5,
188
189
  'accounts/ledgers': 3,
@@ -267,6 +268,8 @@ export default class kucoin extends Exchange {
267
268
  'convert/limit/orders': 5,
268
269
  // affiliate
269
270
  'affiliate/inviter/statistics': 30,
271
+ // earn
272
+ 'earn/redeem-preview': 5, // 5EW
270
273
  },
271
274
  'post': {
272
275
  // account
@@ -525,6 +528,8 @@ export default class kucoin extends Exchange {
525
528
  'Unsuccessful! Exceeded the max. funds out-transfer limit': InsufficientFunds,
526
529
  'The amount increment is invalid.': BadRequest,
527
530
  'The quantity is below the minimum requirement.': InvalidOrder,
531
+ 'not in the given range!': BadRequest,
532
+ 'recAccountType not in the given range': BadRequest,
528
533
  '400': BadRequest,
529
534
  '401': AuthenticationError,
530
535
  '403': NotSupported,
@@ -712,6 +717,9 @@ export default class kucoin extends Exchange {
712
717
  'withdraw': {
713
718
  'includeFee': false,
714
719
  },
720
+ 'transfer': {
721
+ 'fillResponseFromRequest': true,
722
+ },
715
723
  // endpoint versions
716
724
  'versions': {
717
725
  'public': {
@@ -4582,96 +4590,89 @@ export default class kucoin extends Exchange {
4582
4590
  * @method
4583
4591
  * @name kucoin#transfer
4584
4592
  * @description transfer currency internally between wallets on the same account
4585
- * @see https://www.kucoin.com/docs/rest/funding/transfer/inner-transfer
4586
- * @see https://docs.kucoin.com/futures/#transfer-funds-to-kucoin-main-account-2
4587
- * @see https://docs.kucoin.com/spot-hf/#internal-funds-transfers-in-high-frequency-trading-accounts
4593
+ * @see https://www.kucoin.com/docs-new/rest/account-info/transfer/flex-transfer?lang=en_US&
4588
4594
  * @param {string} code unified currency code
4589
4595
  * @param {float} amount amount to transfer
4590
4596
  * @param {string} fromAccount account to transfer from
4591
4597
  * @param {string} toAccount account to transfer to
4592
4598
  * @param {object} [params] extra parameters specific to the exchange API endpoint
4599
+ * @param {string} [params.transferType] INTERNAL, PARENT_TO_SUB, SUB_TO_PARENT (default is INTERNAL)
4600
+ * @param {string} [params.fromUserId] required if transferType is SUB_TO_PARENT
4601
+ * @param {string} [params.toUserId] required if transferType is PARENT_TO_SUB
4593
4602
  * @returns {object} a [transfer structure]{@link https://docs.ccxt.com/?id=transfer-structure}
4594
4603
  */
4595
4604
  async transfer(code, amount, fromAccount, toAccount, params = {}) {
4596
4605
  await this.loadMarkets();
4597
4606
  const currency = this.currency(code);
4598
4607
  const requestedAmount = this.currencyToPrecision(code, amount);
4608
+ const request = {
4609
+ 'currency': currency['id'],
4610
+ 'amount': requestedAmount,
4611
+ };
4612
+ let transferType = 'INTERNAL';
4613
+ [transferType, params] = this.handleParamString2(params, 'transferType', 'type', transferType);
4614
+ if (transferType === 'PARENT_TO_SUB') {
4615
+ if (!('toUserId' in params)) {
4616
+ throw new ExchangeError(this.id + ' transfer() requires a toUserId param for PARENT_TO_SUB transfers');
4617
+ }
4618
+ }
4619
+ else if (transferType === 'SUB_TO_PARENT') {
4620
+ if (!('fromUserId' in params)) {
4621
+ throw new ExchangeError(this.id + ' transfer() requires a fromUserId param for SUB_TO_PARENT transfers');
4622
+ }
4623
+ }
4624
+ if (!('clientOid' in params)) {
4625
+ request['clientOid'] = this.uuid();
4626
+ }
4599
4627
  let fromId = this.convertTypeToAccount(fromAccount);
4600
4628
  let toId = this.convertTypeToAccount(toAccount);
4601
4629
  const fromIsolated = this.inArray(fromId, this.ids);
4602
4630
  const toIsolated = this.inArray(toId, this.ids);
4603
- if (fromId === 'contract') {
4604
- if (toId !== 'main') {
4605
- throw new ExchangeError(this.id + ' transfer() only supports transferring from futures account to main account');
4606
- }
4607
- const request = {
4608
- 'currency': currency['id'],
4609
- 'amount': requestedAmount,
4610
- };
4611
- if (!('bizNo' in params)) {
4612
- // it doesn't like more than 24 characters
4613
- request['bizNo'] = this.uuid22();
4614
- }
4615
- const response = await this.futuresPrivatePostTransferOut(this.extend(request, params));
4616
- //
4617
- // {
4618
- // "code": "200000",
4619
- // "data": {
4620
- // "applyId": "605a87217dff1500063d485d",
4621
- // "bizNo": "bcd6e5e1291f4905af84dc",
4622
- // "payAccountType": "CONTRACT",
4623
- // "payTag": "DEFAULT",
4624
- // "remark": '',
4625
- // "recAccountType": "MAIN",
4626
- // "recTag": "DEFAULT",
4627
- // "recRemark": '',
4628
- // "recSystem": "KUCOIN",
4629
- // "status": "PROCESSING",
4630
- // "currency": "XBT",
4631
- // "amount": "0.00001",
4632
- // "fee": "0",
4633
- // "sn": "573688685663948",
4634
- // "reason": '',
4635
- // "createdAt": 1616545569000,
4636
- // "updatedAt": 1616545569000
4637
- // }
4638
- // }
4639
- //
4640
- const data = this.safeDict(response, 'data');
4641
- return this.parseTransfer(data, currency);
4631
+ if (fromIsolated) {
4632
+ request['fromAccountTag'] = fromId;
4633
+ fromId = 'isolated';
4642
4634
  }
4643
- else {
4644
- const request = {
4645
- 'currency': currency['id'],
4646
- 'amount': requestedAmount,
4647
- };
4648
- if (fromIsolated || toIsolated) {
4649
- if (this.inArray(fromId, this.ids)) {
4650
- request['fromTag'] = fromId;
4651
- fromId = 'isolated';
4652
- }
4653
- if (this.inArray(toId, this.ids)) {
4654
- request['toTag'] = toId;
4655
- toId = 'isolated';
4656
- }
4657
- }
4635
+ if (toIsolated) {
4636
+ request['toAccountTag'] = toId;
4637
+ toId = 'isolated';
4638
+ }
4639
+ const hfOrMining = this.isHfOrMining(fromId, toId);
4640
+ let response = undefined;
4641
+ if (hfOrMining) {
4642
+ // new endpoint does not support hf and mining transfers
4643
+ // use old endpoint for hf and mining transfers
4658
4644
  request['from'] = fromId;
4659
4645
  request['to'] = toId;
4660
- if (!('clientOid' in params)) {
4661
- request['clientOid'] = this.uuid();
4662
- }
4663
- const response = await this.privatePostAccountsInnerTransfer(this.extend(request, params));
4646
+ response = await this.privatePostAccountsInnerTransfer(this.extend(request, params));
4647
+ }
4648
+ else {
4649
+ request['type'] = transferType;
4650
+ request['fromAccountType'] = fromId.toUpperCase();
4651
+ request['toAccountType'] = toId.toUpperCase();
4664
4652
  //
4665
4653
  // {
4666
4654
  // "code": "200000",
4667
4655
  // "data": {
4668
- // "orderId": "605a6211e657f00006ad0ad6"
4656
+ // "orderId": "694fcb5b08bb1600015cda75"
4669
4657
  // }
4670
4658
  // }
4671
4659
  //
4672
- const data = this.safeDict(response, 'data');
4673
- return this.parseTransfer(data, currency);
4660
+ response = await this.privatePostAccountsUniversalTransfer(this.extend(request, params));
4674
4661
  }
4662
+ const data = this.safeDict(response, 'data');
4663
+ const transfer = this.parseTransfer(data, currency);
4664
+ const transferOptions = this.safeDict(this.options, 'transfer', {});
4665
+ const fillResponseFromRequest = this.safeBool(transferOptions, 'fillResponseFromRequest', true);
4666
+ if (fillResponseFromRequest) {
4667
+ transfer['amount'] = amount;
4668
+ transfer['fromAccount'] = fromAccount;
4669
+ transfer['toAccount'] = toAccount;
4670
+ transfer['status'] = 'ok';
4671
+ }
4672
+ return transfer;
4673
+ }
4674
+ isHfOrMining(fromId, toId) {
4675
+ return (fromId === 'trade_hf' || toId === 'trade_hf' || fromId === 'pool' || toId === 'pool');
4675
4676
  }
4676
4677
  parseTransfer(transfer, currency = undefined) {
4677
4678
  //
@@ -4708,16 +4709,49 @@ export default class kucoin extends Exchange {
4708
4709
  // "updatedAt": 1616545569000
4709
4710
  // }
4710
4711
  //
4712
+ // ledger entry - from account ledgers API (for fetchTransfers)
4713
+ //
4714
+ // {
4715
+ // "id": "611a1e7c6a053300067a88d9",
4716
+ // "currency": "USDT",
4717
+ // "amount": "10.00059547",
4718
+ // "fee": "0",
4719
+ // "balance": "0",
4720
+ // "accountType": "MAIN",
4721
+ // "bizType": "Transfer",
4722
+ // "direction": "in",
4723
+ // "createdAt": 1629101692950,
4724
+ // "context": "{\"orderId\":\"611a1e7c6a053300067a88d9\"}"
4725
+ // }
4726
+ //
4711
4727
  const timestamp = this.safeInteger(transfer, 'createdAt');
4712
4728
  const currencyId = this.safeString(transfer, 'currency');
4713
4729
  const rawStatus = this.safeString(transfer, 'status');
4714
- const accountFromRaw = this.safeStringLower(transfer, 'payAccountType');
4715
- const accountToRaw = this.safeStringLower(transfer, 'recAccountType');
4730
+ const bizType = this.safeString(transfer, 'bizType');
4731
+ const isLedgerEntry = (bizType !== undefined);
4732
+ let accountFromRaw = undefined;
4733
+ let accountToRaw = undefined;
4734
+ if (isLedgerEntry) {
4735
+ // Ledger entry format: uses accountType + direction
4736
+ const accountType = this.safeStringLower(transfer, 'accountType');
4737
+ const direction = this.safeString(transfer, 'direction');
4738
+ if (direction === 'out') {
4739
+ accountFromRaw = accountType;
4740
+ }
4741
+ else if (direction === 'in') {
4742
+ accountToRaw = accountType;
4743
+ }
4744
+ }
4745
+ else {
4746
+ // Transfer API format: uses payAccountType/recAccountType
4747
+ accountFromRaw = this.safeStringLower(transfer, 'payAccountType');
4748
+ accountToRaw = this.safeStringLower(transfer, 'recAccountType');
4749
+ }
4716
4750
  const accountsByType = this.safeDict(this.options, 'accountsByType');
4717
4751
  const accountFrom = this.safeString(accountsByType, accountFromRaw, accountFromRaw);
4718
4752
  const accountTo = this.safeString(accountsByType, accountToRaw, accountToRaw);
4719
4753
  return {
4720
- 'id': this.safeString2(transfer, 'applyId', 'orderId'),
4754
+ 'id': this.safeStringN(transfer, ['id', 'applyId', 'orderId']),
4721
4755
  'currency': this.safeCurrencyCode(currencyId, currency),
4722
4756
  'timestamp': timestamp,
4723
4757
  'datetime': this.iso8601(timestamp),
@@ -5810,4 +5844,77 @@ export default class kucoin extends Exchange {
5810
5844
  }
5811
5845
  return undefined;
5812
5846
  }
5847
+ /**
5848
+ * @method
5849
+ * @name kucoin#fetchTransfers
5850
+ * @description fetch a history of internal transfers made on an account
5851
+ * @see https://www.kucoin.com/docs-new/rest/account-info/account-funding/get-account-ledgers-spot-margin
5852
+ * @param {string} [code] unified currency code of the currency transferred
5853
+ * @param {int} [since] the earliest time in ms to fetch transfers for
5854
+ * @param {int} [limit] the maximum number of transfer structures to retrieve
5855
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
5856
+ * @param {int} [params.until] the latest time in ms to fetch transfers for
5857
+ * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
5858
+ * @returns {object[]} a list of [transfer structures]{@link https://docs.ccxt.com/?id=transfer-structure}
5859
+ */
5860
+ async fetchTransfers(code = undefined, since = undefined, limit = undefined, params = {}) {
5861
+ await this.loadMarkets();
5862
+ let paginate = false;
5863
+ [paginate, params] = this.handleOptionAndParams(params, 'fetchTransfers', 'paginate');
5864
+ if (paginate) {
5865
+ return await this.fetchPaginatedCallDynamic('fetchTransfers', code, since, limit, params);
5866
+ }
5867
+ let request = {
5868
+ 'bizType': 'TRANSFER',
5869
+ };
5870
+ const until = this.safeInteger(params, 'until');
5871
+ if (until !== undefined) {
5872
+ params = this.omit(params, 'until');
5873
+ request['endAt'] = until;
5874
+ }
5875
+ let currency = undefined;
5876
+ if (code !== undefined) {
5877
+ currency = this.currency(code);
5878
+ request['currency'] = currency['id'];
5879
+ }
5880
+ if (since !== undefined) {
5881
+ request['startAt'] = since;
5882
+ }
5883
+ if (limit !== undefined) {
5884
+ request['pageSize'] = limit;
5885
+ }
5886
+ else {
5887
+ request['pageSize'] = 500;
5888
+ }
5889
+ [request, params] = this.handleUntilOption('endAt', request, params);
5890
+ const response = await this.privateGetAccountsLedgers(this.extend(request, params));
5891
+ //
5892
+ // {
5893
+ // "code": "200000",
5894
+ // "data": {
5895
+ // "currentPage": 1,
5896
+ // "pageSize": 50,
5897
+ // "totalNum": 1,
5898
+ // "totalPage": 1,
5899
+ // "items": [
5900
+ // {
5901
+ // "id": "611a1e7c6a053300067a88d9",
5902
+ // "currency": "USDT",
5903
+ // "amount": "10.00059547",
5904
+ // "fee": "0",
5905
+ // "balance": "0",
5906
+ // "accountType": "MAIN",
5907
+ // "bizType": "Transfer",
5908
+ // "direction": "in",
5909
+ // "createdAt": 1629101692950,
5910
+ // "context": "{\"orderId\":\"611a1e7c6a053300067a88d9\"}"
5911
+ // }
5912
+ // ]
5913
+ // }
5914
+ // }
5915
+ //
5916
+ const data = this.safeDict(response, 'data', {});
5917
+ const items = this.safeList(data, 'items', []);
5918
+ return this.parseTransfers(items, currency, since, limit);
5919
+ }
5813
5920
  }