ccxt
Version:
1,043 lines (1,039 loc) • 144 kB
JavaScript
'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': [