UNPKG

ccxt

Version:

A cryptocurrency trading API with more than 100 exchanges in JavaScript / TypeScript / Python / C# / PHP / Go

1,043 lines (1,039 loc) • 144 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var nado$1 = require('./abstract/nado.js'); var Precise = require('./base/Precise.js'); var number = require('./base/functions/number.js'); var crypto = require('./base/functions/crypto.js'); var sha3_js = require('@noble/hashes/sha3.js'); var secp256k1_js = require('@noble/curves/secp256k1.js'); var errors = require('./base/errors.js'); // ---------------------------------------------------------------------------- // --------------------------------------------------------------------------- /** * @class nado * @augments Exchange */ class nado extends nado$1["default"] { describe() { return this.deepExtend(super.describe(), { 'id': 'nado', 'name': 'Nado', 'countries': ['KY'], // Cayman Islands 'rateLimit': 25, 'version': 'v1', 'precisionMode': number.TICK_SIZE, 'certified': false, 'pro': true, 'dex': true, 'has': { 'CORS': undefined, 'spot': true, 'margin': true, 'swap': true, 'future': false, 'option': false, 'cancelAllOrders': true, 'cancelOrder': true, 'cancelOrders': true, 'createOrder': true, 'editOrder': true, 'fetchBalance': true, 'fetchCanceledAndClosedOrders': true, 'fetchCanceledOrders': true, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDeposits': true, 'fetchFundingHistory': true, 'fetchFundingRate': true, 'fetchFundingRates': true, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenInterest': true, 'fetchOpenInterests': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchPositions': true, 'fetchStatus': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchWithdrawals': true, 'withdraw': false, }, 'urls': { 'logo': 'https://github.com/user-attachments/assets/811f4e1a-a8b5-4b9e-84c2-0f88997bd274', 'api': { 'gateway': 'https://gateway.prod.nado.xyz/v1', 'gatewayV2': 'https://gateway.prod.nado.xyz/v2', 'archive': 'https://archive.prod.nado.xyz/v1', 'archiveV2': 'https://archive.prod.nado.xyz/v2', 'trigger': 'https://trigger.prod.nado.xyz/v1', }, 'test': { 'gateway': 'https://gateway.test.nado.xyz/v1', 'gatewayV2': 'https://gateway.test.nado.xyz/v2', 'archive': 'https://archive.test.nado.xyz/v1', 'archiveV2': 'https://archive.test.nado.xyz/v2', 'trigger': 'https://trigger.test.nado.xyz/v1', }, 'www': 'https://nado.xyz', 'doc': 'https://docs.nado.xyz/', }, 'api': { 'gateway': { 'public': { 'get': { 'symbols': 2, 'query': 1, 'edge/query': 1, }, 'post': { 'query': 1, }, }, 'private': { 'post': { 'execute': 1, }, }, }, 'gatewayV2': { 'public': { 'get': { 'assets': 2, 'pairs': 1, 'orderbook': 1, }, }, }, 'archive': { 'post': { '': 1, }, }, 'archiveV2': { 'public': { 'get': { 'tickers': 1, 'contracts': 1, 'trades': 1, }, }, }, 'trigger': { 'private': { 'post': { 'execute': 1, 'query': 1, }, }, }, }, 'requiredCredentials': { 'apiKey': false, 'secret': false, 'walletAddress': true, 'privateKey': true, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'maker': this.parseNumber('0.0001'), 'taker': this.parseNumber('0.00035'), }, }, 'options': { 'defaultType': 'swap', 'recvWindow': 5000, 'expiration': '4294967295', 'subaccount': 'default', 'editOrder': { 'placeRequiresUnfilled': true, }, 'builderFee': true, 'builder': '4500', 'feeRate': '10', }, 'timeframes': { '1m': 60, '5m': 300, '15m': 900, '1h': 3600, '2h': 7200, '4h': 14400, '1d': 86400, '1w': 604800, '4w': 2419200, }, 'features': {}, 'exceptions': { 'exact': { '1000': errors.RateLimitExceeded, '1015': errors.RateLimitExceeded, '1001': errors.PermissionDenied, '1002': errors.RestrictedLocation, '1003': errors.RestrictedLocation, '1004': errors.OnMaintenance, '2000': errors.InvalidOrder, '2001': errors.InvalidOrder, '2002': errors.InvalidOrder, '2003': errors.InvalidOrder, '2004': errors.InvalidOrder, '2005': errors.OperationRejected, '2006': errors.InsufficientFunds, '2007': errors.InvalidOrder, '2008': errors.OrderImmediatelyFillable, '2009': errors.InvalidOrder, '2010': errors.InvalidOrder, '2011': errors.InvalidNonce, '2012': errors.InvalidNonce, '2013': errors.DuplicateOrderId, '2014': errors.PermissionDenied, '2015': errors.BadSymbol, '2016': errors.BadSymbol, '2017': errors.InsufficientFunds, '2019': errors.InvalidOrder, '2020': errors.OrderNotFound, '2021': errors.PermissionDenied, '2022': errors.InvalidNonce, '2023': errors.OperationRejected, '2024': errors.InvalidAddress, '2025': errors.InsufficientFunds, '2026': errors.BadRequest, '2027': errors.BadRequest, '2028': errors.AuthenticationError, '2029': errors.BadRequest, '2030': errors.RateLimitExceeded, '2031': errors.OrderNotFillable, '2033': errors.InvalidNonce, '2034': errors.AuthenticationError, '2035': errors.AuthenticationError, '2036': errors.InsufficientFunds, '2037': errors.InsufficientFunds, '2038': errors.BadRequest, '2039': errors.BadRequest, '2040': errors.BadRequest, '2041': errors.BadRequest, '2042': errors.OperationRejected, '2043': errors.InsufficientFunds, '2044': errors.OperationRejected, '2045': errors.InvalidOrder, '2046': errors.InvalidOrder, '2047': errors.InvalidOrder, '2048': errors.InvalidOrder, '2049': errors.OperationFailed, '2050': errors.PermissionDenied, '2051': errors.OperationRejected, '2052': errors.InvalidOrder, '2053': errors.OperationFailed, '2054': errors.InvalidOrder, '2055': errors.InvalidOrder, '2056': errors.OrderNotFillable, '2057': errors.OperationRejected, '2058': errors.OrderNotFound, '2059': errors.InvalidOrder, '2060': errors.BadSymbol, '2061': errors.BadRequest, '2062': errors.ArgumentsRequired, '2063': errors.BadResponse, '2064': errors.InvalidOrder, '2065': errors.InvalidOrder, '2066': errors.InvalidOrder, '2067': errors.InvalidOrder, '2068': errors.OnMaintenance, '2069': errors.OperationRejected, '2070': errors.OperationRejected, '2071': errors.OperationRejected, '2072': errors.InvalidOrder, '2073': errors.InvalidOrder, '2074': errors.BadRequest, '2075': errors.InvalidOrder, '2076': errors.InvalidOrder, '2077': errors.BadRequest, '2078': errors.RateLimitExceeded, '2079': errors.BadRequest, '2080': errors.BadRequest, '2081': errors.InvalidOrder, '2082': errors.BadSymbol, '2083': errors.InvalidOrder, '2084': errors.InvalidOrder, '2085': errors.InvalidOrder, '2086': errors.BadRequest, '2087': errors.OperationFailed, '2088': errors.InvalidOrder, '2089': errors.BadRequest, '2090': errors.BadRequest, '2091': errors.BadRequest, '2092': errors.InsufficientFunds, '2093': errors.OperationRejected, '2094': errors.InvalidOrder, '2095': errors.InvalidOrder, '2096': errors.InsufficientFunds, '2097': errors.InvalidOrder, '2098': errors.InvalidOrder, '2099': errors.InvalidOrder, '2100': errors.InvalidOrder, '2101': errors.InvalidOrder, '2102': errors.InvalidOrder, '2103': errors.InvalidOrder, '2104': errors.InvalidOrder, '2105': errors.InvalidOrder, '2106': errors.InvalidOrder, '2107': errors.InvalidOrder, '2108': errors.InvalidOrder, '2109': errors.InvalidOrder, '2110': errors.InvalidOrder, '2111': errors.InvalidOrder, '2112': errors.InvalidOrder, '2113': errors.InvalidOrder, '2114': errors.InvalidOrder, '2115': errors.OperationFailed, '2117': errors.InvalidOrder, '2118': errors.BadRequest, '2119': errors.InvalidOrder, '2120': errors.OperationFailed, '2121': errors.OperationFailed, '2122': errors.InvalidOrder, '2123': errors.BadRequest, '2124': errors.InvalidOrder, '2125': errors.OperationRejected, '3000': errors.BadRequest, '3001': errors.BadRequest, '3002': errors.ArgumentsRequired, '3003': errors.BadRequest, '3004': errors.BadRequest, '3005': errors.OperationFailed, '4000': errors.BadRequest, '4001': errors.NotSupported, '4002': errors.ExchangeNotAvailable, '4003': errors.OperationFailed, '4004': errors.OperationRejected, '5000': errors.ExchangeNotAvailable, }, 'broad': {}, }, }); } /** * @method * @name nado#createOrder * @description create a trade order * @see https://docs.nado.xyz/developer-resources/api/gateway/executes/place-order * @see https://docs.nado.xyz/developer-resources/api/trigger/executes/place-order * @param {string} symbol unified symbol of the market to create an order in * @param {string} type must be 'limit' * @param {string} side 'buy' or 'sell' * @param {float} amount how much of currency you want to trade in units of base currency * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.subaccount] the 12-byte subaccount identifier, defaults to 'default' * @param {string|int} [params.expiration] order expiration timestamp in seconds, defaults to 4294967295 * @param {string|int} [params.appendix] pre-encoded order appendix * @param {boolean} [params.reduceOnly] true if the order should only reduce position * @param {boolean} [params.postOnly] true to create a post-only order * @param {string} [params.timeInForce] 'GTC', 'IOC', 'FOK', or 'PO' * @param {boolean} [params.spotLeverage] whether leverage should be used for spot, defaults to true, exchange-specific alias params.spot_leverage * @param {float} [params.triggerPrice] *swap only* The price at which a trigger order is triggered at * @param {float} [params.stopLossPrice] *swap only* The price at which a stop loss order is triggered at * @param {float} [params.takeProfitPrice] *swap only* The price at which a take profit order is triggered at * @param {string} [params.triggerDirection] trigger direction, above, below * @param {int} [params.id] client-provided request id, returned by the exchange in the response * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async createOrder(symbol, type, side, amount, price = undefined, params = {}) { this.checkRequiredCredentials(); await this.loadMarkets(); const market = this.market(symbol); const request = await this.createOrderRequest(symbol, type, side, amount, price, params); const placeOrder = this.safeDict(request, 'place_order', {}); const isTriggerOrder = ('trigger' in placeOrder); let response = undefined; if (isTriggerOrder) { response = await this.triggerPrivatePostExecute(request); } else { response = await this.gatewayPrivatePostExecute(request); } // // { // "status": "success", // "signature": "0x...", // "data": { // "digest": "0x..." // }, // "request_type": "execute_place_order", // "id": 100 // } // return this.parseOrder(this.extend({ 'place_order': placeOrder }, response), market); } /** * @method * @ignore * @name nado#createOrderRequest * @description build and sign the place_order execute payload * @param {string} symbol unified symbol of the market to create an order in * @param {string} type must be 'limit' * @param {string} side 'buy' or 'sell' * @param {float} amount how much of currency you want to trade in units of base currency * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} the request payload for the place_order execute */ async createOrderRequest(symbol, type, side, amount, price = undefined, params = {}) { const market = this.market(symbol); if (type !== 'limit') { throw new errors.InvalidOrder(this.id + ' createOrder() supports limit orders only'); } if (price === undefined) { throw new errors.ArgumentsRequired(this.id + ' createOrder() requires a price argument'); } const productId = this.parseToInt(market['id']); const priceString = this.priceToPrecision(symbol, price); const amountString = this.amountToPrecision(symbol, amount); const priceX18 = this.convertToX18(priceString); let amountX18 = this.convertToX18(amountString); if (side === 'sell') { amountX18 = Precise["default"].stringMul(amountX18, '-1'); } let subaccount = undefined; [subaccount, params] = this.handleOptionAndParams(params, 'createOrder', 'subaccount', 'default'); let expiration = undefined; [expiration, params] = this.handleOptionAndParams(params, 'createOrder', 'expiration', '4294967295'); let recvWindow = undefined; [recvWindow, params] = this.handleOptionAndParams(params, 'createOrder', 'recvWindow', 5000); const nonce = this.createOrderNonce(recvWindow); const requestId = this.safeInteger(params, 'id'); const spotLeverage = this.safeBool2(params, 'spotLeverage', 'spot_leverage'); const sender = this.createSubaccount(this.walletAddress, subaccount); const order = { 'sender': sender, 'priceX18': priceX18, 'amount': amountX18, 'expiration': expiration, 'nonce': nonce, }; const placeOrder = { 'product_id': productId, }; if (requestId !== undefined) { placeOrder['id'] = requestId; } if (spotLeverage !== undefined) { placeOrder['spot_leverage'] = spotLeverage; } const isBuy = (side === 'buy'); let triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice'); const stopLossTriggerPrice = this.safeString(params, 'stopLossPrice'); const takeProfitTriggerPrice = this.safeString(params, 'takeProfitPrice'); const isStopLossOrder = stopLossTriggerPrice !== undefined; const isTakeProfitOrder = takeProfitTriggerPrice !== undefined; const isStopOrder = triggerPrice !== undefined; const isTriggerOrder = isStopOrder || isStopLossOrder || isTakeProfitOrder; if (isStopOrder) { const triggerDirection = this.safeStringLower(params, 'triggerDirection'); if (triggerDirection === undefined) { throw new errors.ArgumentsRequired(this.id + ' createOrder() requires triggerDirection for trigger order'); } const triggerPriceX18 = this.convertToX18(triggerPrice); const priceRequirement = {}; priceRequirement['oracle_price_' + triggerDirection] = triggerPriceX18; const trigger = { 'price_trigger': { 'price_requirement': priceRequirement, }, }; placeOrder['trigger'] = trigger; } else if (isStopLossOrder || isTakeProfitOrder) { let triggerDirection = ''; if (isBuy) { triggerDirection = isStopLossOrder ? 'above' : 'below'; } else { triggerDirection = isStopLossOrder ? 'below' : 'above'; } triggerPrice = isStopLossOrder ? stopLossTriggerPrice : takeProfitTriggerPrice; const triggerPriceX18 = this.convertToX18(triggerPrice); const priceRequirement = {}; priceRequirement['oracle_price_' + triggerDirection] = triggerPriceX18; const trigger = { 'price_trigger': { 'price_requirement': priceRequirement, }, }; placeOrder['trigger'] = trigger; } let appendix = this.safeString(params, 'appendix'); if (appendix === undefined) { appendix = this.createOrderAppendix(isTriggerOrder, params); } order['appendix'] = appendix; const contracts = await this.queryContracts(); const chainId = this.safeString(contracts, 'chain_id'); const signature = this.signOrder(order, productId, chainId); placeOrder['order'] = order; placeOrder['signature'] = signature; params = this.omit(params, ['expiration', 'nonce', 'appendix', 'reduceOnly', 'postOnly', 'timeInForce', 'id', 'spotLeverage', 'spot_leverage', 'triggerPrice', 'stopPrice', 'triggerDirection', 'stopLossPrice', 'takeProfitPrice']); const request = { 'place_order': placeOrder, }; return this.extend(request, params); } /** * @method * @name nado#editOrder * @description edit a trade order * @see https://docs.nado.xyz/developer-resources/api/gateway/executes/cancel-and-place * @param {string} id order id * @param {string} symbol unified symbol of the market to edit an order in * @param {string} type must be 'limit' * @param {string} side 'buy' or 'sell' * @param {float} amount how much of currency you want to trade in units of base currency * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.subaccount] the 12-byte subaccount identifier, defaults to 'default' * @param {string|int} [params.expiration] order expiration timestamp in seconds, defaults to 4294967295 * @param {string|int} [params.appendix] pre-encoded order appendix * @param {boolean} [params.reduceOnly] true if the order should only reduce position * @param {boolean} [params.postOnly] true to create a post-only order * @param {string} [params.timeInForce] 'GTC', 'IOC', 'FOK', or 'PO' * @param {boolean} [params.spotLeverage] whether leverage should be used for spot, defaults to true, exchange-specific alias params.spot_leverage * @param {boolean} [params.placeRequiresUnfilled] when true, aborts the new order if the canceled order had partial fills or the cancel failed, exchange-specific alias params.place_requires_unfilled, defaults to true * @param {int} [params.id] client-provided request id, returned by the exchange in the response * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async editOrder(id, symbol, type, side, amount = undefined, price = undefined, params = {}) { this.checkRequiredCredentials(); await this.loadMarkets(); const market = this.market(symbol); const request = await this.editOrderRequest(id, symbol, type, side, amount, price, params); const response = await this.gatewayPrivatePostExecute(request); // // { // "status": "success", // "signature": "0x...", // "data": { // "digest": "0x..." // }, // "request_type": "execute_cancel_and_place" // } // const cancelAndPlace = this.safeDict(request, 'cancel_and_place', {}); const placeOrder = this.safeDict(cancelAndPlace, 'place_order', {}); return this.parseOrder(this.extend({ 'place_order': placeOrder }, response), market); } /** * @method * @ignore * @name nado#editOrderRequest * @description build and sign the cancel_and_place execute payload * @param {string} id order id * @param {string} symbol unified symbol of the market to edit an order in * @param {string} type must be 'limit' * @param {string} side 'buy' or 'sell' * @param {float} amount how much of currency you want to trade in units of base currency * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} the request payload for the cancel_and_place execute */ async editOrderRequest(id, symbol, type, side, amount = undefined, price = undefined, params = {}) { const market = this.market(symbol); if (type !== 'limit') { throw new errors.InvalidOrder(this.id + ' editOrder() supports limit orders only'); } if (amount === undefined) { throw new errors.ArgumentsRequired(this.id + ' editOrder() requires an amount argument'); } if (price === undefined) { throw new errors.ArgumentsRequired(this.id + ' editOrder() requires a price argument'); } const productId = this.parseToInt(market['id']); const priceString = this.priceToPrecision(symbol, price); const amountString = this.amountToPrecision(symbol, amount); const priceX18 = this.convertToX18(priceString); let amountX18 = this.convertToX18(amountString); if (side === 'sell') { amountX18 = Precise["default"].stringMul(amountX18, '-1'); } const editOrderOptions = this.safeDict(this.options, 'editOrder', {}); let subaccount = undefined; [subaccount, params] = this.handleOptionAndParams(params, 'editOrder', 'subaccount', 'default'); let expiration = undefined; [expiration, params] = this.handleOptionAndParams(params, 'editOrder', 'expiration', '4294967295'); let recvWindow = undefined; [recvWindow, params] = this.handleOptionAndParams(params, 'editOrder', 'recvWindow', 5000); const cancelNonce = this.createOrderNonce(recvWindow); const orderNonce = Precise["default"].stringAdd(cancelNonce, '1'); let appendix = this.safeString(params, 'appendix'); if (appendix === undefined) { appendix = this.createOrderAppendix(false, params); } const requestId = this.safeInteger(params, 'id'); const spotLeverage = this.safeBool2(params, 'spotLeverage', 'spot_leverage'); const placeRequiresUnfilled = this.safeBool2(params, 'placeRequiresUnfilled', 'place_requires_unfilled', this.safeBool(editOrderOptions, 'placeRequiresUnfilled', true)); params = this.omit(params, ['expiration', 'nonce', 'appendix', 'reduceOnly', 'postOnly', 'timeInForce', 'id', 'spotLeverage', 'spot_leverage', 'placeRequiresUnfilled', 'place_requires_unfilled']); const sender = this.createSubaccount(this.walletAddress, subaccount); const cancelTx = { 'sender': sender, 'productIds': [productId], 'digests': [id], 'nonce': cancelNonce, }; const order = { 'sender': sender, 'priceX18': priceX18, 'amount': amountX18, 'expiration': expiration, 'nonce': orderNonce, 'appendix': appendix, }; const contracts = await this.queryContracts(); const chainId = this.safeString(contracts, 'chain_id'); const endpointAddress = this.safeString(contracts, 'endpoint_addr'); if (endpointAddress === undefined) { throw new errors.ExchangeError(this.id + ' editOrder() requires endpoint_addr from contracts query'); } const cancelSignature = this.signCancellation(cancelTx, chainId, endpointAddress); const orderSignature = this.signOrder(order, productId, chainId); const placeOrder = { 'product_id': productId, 'order': order, 'signature': orderSignature, }; if (requestId !== undefined) { placeOrder['id'] = requestId; } if (spotLeverage !== undefined) { placeOrder['spot_leverage'] = spotLeverage; } const cancelAndPlace = { 'cancel_tx': cancelTx, 'cancel_signature': cancelSignature, 'place_order': placeOrder, 'place_requires_unfilled': placeRequiresUnfilled, }; const request = { 'cancel_and_place': cancelAndPlace, }; return this.extend(request, params); } /** * @method * @name nado#cancelOrder * @description cancels an open order * @see https://docs.nado.xyz/developer-resources/api/gateway/executes/cancel-orders * @param {string} id order id * @param {string} symbol unified symbol of the market the order was made in * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.subaccount] the 12-byte subaccount identifier, defaults to 'default' * @param {string} [params.requiredUnfilledAmount] cancel only if the order's absolute remaining unfilled amount matches this amount, exchange-specific raw x18 alias params.required_unfilled_amount * @param {int} [params.id] client-provided request id, returned by the exchange in the response * @returns {object} An [order structure]{@link https://docs.ccxt.com/?id=order-structure} */ async cancelOrder(id, symbol = undefined, params = {}) { const orders = await this.cancelOrders([id], symbol, params); return this.safeDict(orders, 0); } /** * @method * @name nado#cancelAllOrders * @description cancel all open orders * @see https://docs.nado.xyz/developer-resources/api/gateway/executes/cancel-product-orders * @param {string} [symbol] unified market symbol, when undefined all orders for all products are canceled * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.subaccount] the 12-byte subaccount identifier, defaults to 'default' * @param {int} [params.id] client-provided request id, returned by the exchange in the response * @param {boolean} [params.trigger] set to true if you would like to fetch portfolio margin account trigger or conditional orders * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure} */ async cancelAllOrders(symbol = undefined, params = {}) { this.checkRequiredCredentials(); await this.loadMarkets(); let market = undefined; if (symbol !== undefined) { market = this.market(symbol); } const trigger = this.safeBool2(params, 'stop', 'trigger'); params = this.omit(params, ['stop', 'trigger']); const request = await this.cancelAllOrdersRequest(symbol, params); let response = undefined; if (trigger) { response = await this.triggerPrivatePostExecute(request); // // { // "status": "success", // "signature": {signature}, // "request_type": "execute_cancel_product_orders" // } // } else { response = await this.gatewayPrivatePostExecute(request); // // { // "status": "success", // "signature": "0x...", // "data": { // "cancelled_orders": [ // { // "product_id": 2, // "sender": "0x...", // "price_x18": "20000000000000000000000", // "amount": "-100000000000000000", // "expiration": "1686332748", // "order_type": "post_only", // "nonce": "1768248100142339392", // "unfilled_amount": "-100000000000000000", // "digest": "0x...", // "appendix": "1537", // "placed_at": 1686332708 // } // ] // }, // "request_type": "execute_cancel_product_orders", // "id": 100 // } // } const data = this.safeDict(response, 'data', {}); const cancelledOrders = this.safeList(data, 'cancelled_orders', []); const result = []; for (let i = 0; i < cancelledOrders.length; i++) { result.push(this.parseOrder(this.extend({ 'status': 'canceled' }, cancelledOrders[i]), market)); } return result; } /** * @method * @ignore * @name nado#cancelAllOrdersRequest * @description build and sign the cancel_product_orders execute payload * @param {string} [symbol] unified market symbol, when undefined all orders for all products are canceled * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} the request payload for the cancel_product_orders execute */ async cancelAllOrdersRequest(symbol = undefined, params = {}) { const productIds = []; if (symbol !== undefined) { const market = this.market(symbol); productIds.push(this.parseToInt(market['id'])); } let subaccount = undefined; [subaccount, params] = this.handleOptionAndParams(params, 'cancelAllOrders', 'subaccount', 'default'); const sender = this.createSubaccount(this.walletAddress, subaccount); let recvWindow = undefined; [recvWindow, params] = this.handleOptionAndParams(params, 'cancelAllOrders', 'recvWindow', 5000); const nonce = this.createOrderNonce(recvWindow); const tx = { 'sender': sender, 'productIds': productIds, 'nonce': nonce, }; const contracts = await this.queryContracts(); const chainId = this.safeString(contracts, 'chain_id'); const endpointAddress = this.safeString(contracts, 'endpoint_addr'); if (endpointAddress === undefined) { throw new errors.ExchangeError(this.id + ' cancelAllOrders() requires endpoint_addr from contracts query'); } const signature = this.signCancellationProducts(tx, chainId, endpointAddress); const requestId = this.safeInteger(params, 'id'); params = this.omit(params, ['id']); const cancelProductOrders = { 'tx': tx, 'signature': signature, }; if (requestId !== undefined) { cancelProductOrders['id'] = requestId; } const request = { 'cancel_product_orders': cancelProductOrders, }; return this.extend(request, params); } /** * @method * @name nado#cancelOrders * @description cancel multiple orders * @see https://docs.nado.xyz/developer-resources/api/gateway/executes/cancel-orders * @param {string[]} ids order ids * @param {string} symbol unified market symbol * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.subaccount] the 12-byte subaccount identifier, defaults to 'default' * @param {string} [params.requiredUnfilledAmount] cancel only if the order's absolute remaining unfilled amount matches this amount, exchange-specific raw x18 alias params.required_unfilled_amount * @param {int} [params.id] client-provided request id, returned by the exchange in the response * @param {boolean} [params.trigger] set to true if you would like to fetch portfolio margin account trigger or conditional orders * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure} */ async cancelOrders(ids, symbol = undefined, params = {}) { this.checkRequiredCredentials(); if (symbol === undefined) { throw new errors.ArgumentsRequired(this.id + ' cancelOrders() requires a symbol argument'); } await this.loadMarkets(); const market = this.market(symbol); const trigger = this.safeBool2(params, 'stop', 'trigger'); params = this.omit(params, ['stop', 'trigger']); const request = await this.cancelOrdersRequest(ids, symbol, params); let response = undefined; if (trigger) { response = await this.triggerPrivatePostExecute(request); // // { // "status": "success", // "signature": {signature}, // "request_type": "execute_cancel_orders" // } // } else { response = await this.gatewayPrivatePostExecute(request); // // { // "status": "success", // "signature": "0x...", // "data": { // "cancelled_orders": [ // { // "product_id": 2, // "sender": "0x...", // "price_x18": "20000000000000000000000", // "amount": "-100000000000000000", // "expiration": "1686332748", // "order_type": "post_only", // "nonce": "1768248100142339392", // "unfilled_amount": "-100000000000000000", // "digest": "0x...", // "appendix": "1537", // "placed_at": 1686332708 // } // ] // }, // "request_type": "execute_cancel_orders", // "id": 100 // } // } const data = this.safeDict(response, 'data', {}); const cancelledOrders = this.safeList(data, 'cancelled_orders', []); const result = []; for (let i = 0; i < cancelledOrders.length; i++) { result.push(this.parseOrder(this.extend({ 'status': 'canceled' }, cancelledOrders[i]), market)); } return result; } /** * @method * @ignore * @name nado#cancelOrdersRequest * @description build and sign the cancel_orders execute payload * @param {string[]} ids order ids * @param {string} symbol unified market symbol * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} the request payload for the cancel_orders execute */ async cancelOrdersRequest(ids, symbol = undefined, params = {}) { const market = this.market(symbol); const productId = this.parseToInt(market['id']); let subaccount = undefined; [subaccount, params] = this.handleOptionAndParams(params, 'cancelOrders', 'subaccount', 'default'); const sender = this.createSubaccount(this.walletAddress, subaccount); const productIds = []; for (let i = 0; i < ids.length; i++) { productIds.push(productId); } let recvWindow = undefined; [recvWindow, params] = this.handleOptionAndParams(params, 'cancelOrders', 'recvWindow', 5000); const nonce = this.createOrderNonce(recvWindow); const tx = { 'sender': sender, 'productIds': productIds, 'digests': ids, 'nonce': nonce, }; const contracts = await this.queryContracts(); const chainId = this.safeString(contracts, 'chain_id'); const endpointAddress = this.safeString(contracts, 'endpoint_addr'); if (endpointAddress === undefined) { throw new errors.ExchangeError(this.id + ' cancelOrders() requires endpoint_addr from contracts query'); } const signature = this.signCancellation(tx, chainId, endpointAddress); const requestId = this.safeInteger(params, 'id'); const requiredUnfilledAmountRaw = this.safeString(params, 'required_unfilled_amount'); const requiredUnfilledAmount = this.safeString(params, 'requiredUnfilledAmount'); params = this.omit(params, ['id', 'requiredUnfilledAmount', 'required_unfilled_amount']); const cancelOrders = { 'tx': tx, 'signature': signature, }; if (requiredUnfilledAmountRaw !== undefined) { cancelOrders['required_unfilled_amount'] = requiredUnfilledAmountRaw; } else if (requiredUnfilledAmount !== undefined) { cancelOrders['required_unfilled_amount'] = this.convertToX18(requiredUnfilledAmount); } if (requestId !== undefined) { cancelOrders['id'] = requestId; } const request = { 'cancel_orders': cancelOrders, }; return this.extend(request, params); } /** * @method * @name nado#fetchOrder * @description fetches information on an order made by the user * @see https://docs.nado.xyz/developer-resources/api/gateway/queries/order * @param {string} id order id * @param {string} symbol unified symbol of the market the order was made in * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} An [order structure]{@link https://docs.ccxt.com/?id=order-structure} */ async fetchOrder(id, symbol = undefined, params = {}) { if (symbol === undefined) { throw new errors.ArgumentsRequired(this.id + ' fetchOrder() requires a symbol argument'); } await this.loadMarkets(); const market = this.market(symbol); const request = { 'type': 'order', 'product_id': this.parseToInt(market['id']), 'digest': id, }; const response = await this.gatewayPublicGetQuery(this.extend(request, params)); // // { // "status": "success", // "data": { // "product_id": 1, // "sender": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43000000000000000000000000", // "price_x18": "1000000000000000000", // "amount": "1000000000000000000", // "expiration": "2000000000", // "nonce": "1", // "unfilled_amount": "1000000000000000000", // "digest": "0x0000000000000000000000000000000000000000000000000000000000000000", // "placed_at": 1681951347, // "appendix": "1537", // "order_type": "ioc" // }, // "request_type": "query_order" // } // const data = this.safeDict(response, 'data', {}); return this.parseOrder(data, market); } /** * @method * @name nado#fetchOrders * @description fetches information on multiple orders made by the user * @see https://docs.nado.xyz/developer-resources/api/archive-indexer/orders * @see https://docs.nado.xyz/developer-resources/api/trigger/queries/list-trigger-orders * @param {string} symbol unified market symbol of the market orders were made in * @param {int} [since] the earliest time in ms to fetch orders for * @param {int} [limit] the maximum number of order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {boolean} [params.trigger] set to true if you would like to fetch portfolio margin account trigger or conditional orders * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure} */ async fetchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); const productIds = []; let market = undefined; if (symbol !== undefined) { market = this.market(symbol); productIds.push(this.parseToInt(market['id'])); } let subaccount = undefined; [subaccount, params] = this.handleOptionAndParams(params, 'fetchOpenOrders', 'subaccount', 'default'); const sender = this.createSubaccount(this.walletAddress, subaccount); const trigger = this.safeBool2(params, 'stop', 'trigger'); params = this.omit(params, ['stop', 'trigger']); if (!trigger) { throw new errors.NotSupported(this.id + ' fetchOrders only support trigger'); } let recvWindow = undefined; [recvWindow, params] = this.handleOptionAndParams(params, 'fetchOrders', 'recvWindow', 5000); const tx = { 'sender': sender, 'recvTime': this.numberToString(this.milliseconds() + recvWindow), }; const request = { 'tx': tx, 'type': 'list_trigger_orders', 'product_ids': productIds, }; if (limit !== undefined) { request['limit'] = limit; } const contracts = await this.queryContracts(); const chainId = this.safeString(contracts, 'chain_id'); const endpointAddress = this.safeString(contracts, 'endpoint_addr'); const signature = this.signFetchTriggerOrders(tx, chainId, endpointAddress); request['signature'] = signature; const response = await this.triggerPrivatePostQuery(this.extend(request, params)); // // { // "status": "success", // "data": { // "orders": [{ // "order": { // "order": { // "sender": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43000000000000000000000000", // "priceX18": "1000000000000000000", // "amount": "1000000000000000000", // "expiration": "2000000000", // "nonce": "1", // }, // "signature": "0x...", // "product_id": 1, // "spot_leverage": true, // "trigger": { // "price_above": "1000000000000000000" // }, // "digest": "0x..." // }, // "status": "pending", // "placed_at": 1688768157000, // "updated_at": 1688768157050 // }] // }, // "request_type": "query_list_trigger_orders" // } // const data = this.safeDict(response, 'data', {}); const orders = this.safeList(data, 'orders', []); return this.parseOrders(orders, market, since, limit); } /** * @method * @name nado#fetchOpenOrders * @description fetch all unfilled currently open orders * @see https://docs.nado.xyz/developer-resources/api/gateway/queries/orders * @see https://docs.nado.xyz/developer-resources/api/trigger/queries/list-trigger-orders * @param {string} symbol unified market symbol * @param {int} [since] the earliest time in ms to fetch open orders for * @param {int} [limit] the maximum number of open order structures to retrieve * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.subaccount] the 12-byte subaccount identifier, defaults to 'default' * @param {boolean} [params.trigger] whether the order is a trigger order * @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure} */ async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { if (this.walletAddress === undefined) { throw new errors.ArgumentsRequired(this.id + ' fetchOpenOrders() requires walletAddress'); } await this.loadMarkets(); let subaccount = undefined; [subaccount, params] = this.handleOptionAndParams(params, 'fetchOpenOrders', 'subaccount', 'default'); const sender = this.createSubaccount(this.walletAddress, subaccount); const trigger = this.safeBool2(params, 'stop', 'trigger'); if (trigger) { return await this.fetchOrders(symbol, since, undefined, this.extend(params, { 'status_types': [