From 09b43b34406e84dd5fc2a359f98ca55cb48e0f6d Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 18 Jun 2020 12:18:39 -0500 Subject: [PATCH 1/2] change default behavior to auto sign state when necessary --- src/client/client.ts | 64 ++++++++++++++++++- src/client/environments.ts | 1 + .../orders/fragments/orderPlacedFragment.ts | 1 + src/types/order.ts | 1 + 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/client/client.ts b/src/client/client.ts index 4b6a4c8..24f534b 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -272,8 +272,10 @@ const BLOCKCHAIN_TO_BIP44 = { [Blockchain.NEO]: BIP44.NEO } +const ORDERS_REMAINING_TO_AUTOSYNC_AT = 20 const NEP5_OLD_ASSETS = ['nos', 'phx', 'guard', 'lx', 'ava'] export const MISSING_NONCES = 'missing_asset_nonces' +export const MAX_ORDERS_REACHED = 'Maximal number of orders have been reached' export const MAX_SIGN_STATE_RECURSION = 5 export class Client { @@ -317,7 +319,7 @@ export class Client { private tradedAssets: string[] = [] private assetNonces: { [key: string]: number[] } private currentOrderNonce: number - + private signStateInProgress: boolean /** * Create a new instance of [[Client]] * @@ -337,6 +339,7 @@ export class Client { ...opts } this.clientOpts = { + autoSignState: true, runRequestsOverWebsockets: false, headers: {}, ...clientOpts @@ -1608,6 +1611,8 @@ export class Client { public async getSignAndSyncStates( sync = false ): Promise { + this.signStateInProgress = true + const emptyStates: GetStatesData = { states: [], recycledOrders: [], @@ -1616,6 +1621,8 @@ export class Client { const signStatesRecursive: SignStatesData = await this.signStates( emptyStates ) + this.signStateInProgress = false + if (sync) { const syncResult = await this.syncStates(signStatesRecursive) return syncResult @@ -1936,10 +1943,22 @@ export class Client { }) measurementPlaceOrder.end() measurementPlaceLimitOrder.end() + + await this.handleOrderPlaced(result.data.placeLimitOrder) + return result.data.placeLimitOrder } catch (e) { + let replaceOrder = false if (e.message.includes(MISSING_NONCES)) { + replaceOrder = true await this.updateTradedAssetNonces() + } else if (e.message.includes(MAX_ORDERS_REACHED)) { + if (this.clientOpts.autoSignState && !this.signStateInProgress) { + replaceOrder = true + await this.getSignAndSyncStates() + } + } + if (replaceOrder) { return await this.placeLimitOrder( allowTaker, amount, @@ -2026,12 +2045,23 @@ export class Client { }) measurementPlaceOrder.end() measurementPlaceMarketOrder.end() + await this.handleOrderPlaced(result.data.placeMarketOrder) return result.data.placeMarketOrder } catch (e) { + let replaceOrder = false if (e.message.includes(MISSING_NONCES)) { + replaceOrder = true await this.updateTradedAssetNonces() + } else if (e.message.includes(MAX_ORDERS_REACHED)) { + if (this.clientOpts.autoSignState && !this.signStateInProgress) { + replaceOrder = true + await this.getSignAndSyncStates() + } + } + if (replaceOrder) { return await this.placeMarketOrder(amount, buyOrSell, marketName) } + return this.handleOrderError(e, signedPayload) } } @@ -2136,11 +2166,20 @@ export class Client { }) measurementPlaceOrder.end() measurementPlaceMarketOrder.end() - + await this.handleOrderPlaced(result.data.placeStopLimitOrder) return result.data.placeStopLimitOrder } catch (e) { + let replaceOrder = false if (e.message.includes(MISSING_NONCES)) { + replaceOrder = true await this.updateTradedAssetNonces() + } else if (e.message.includes(MAX_ORDERS_REACHED)) { + if (this.clientOpts.autoSignState && !this.signStateInProgress) { + replaceOrder = true + await this.getSignAndSyncStates() + } + } + if (replaceOrder) { return await this.placeStopLimitOrder( allowTaker, amount, @@ -2238,10 +2277,20 @@ export class Client { }) measurementPlaceOrder.end() measurementPlaceMarketOrder.end() + await this.handleOrderPlaced(result.data.placeStopMarketOrder) return result.data.placeStopMarketOrder } catch (e) { + let replaceOrder = false if (e.message.includes(MISSING_NONCES)) { + replaceOrder = true await this.updateTradedAssetNonces() + } else if (e.message.includes(MAX_ORDERS_REACHED)) { + if (this.clientOpts.autoSignState && !this.signStateInProgress) { + replaceOrder = true + await this.getSignAndSyncStates() + } + } + if (replaceOrder) { return await this.placeStopMarketOrder( amount, buyOrSell, @@ -2254,6 +2303,17 @@ export class Client { } } + private handleOrderPlaced = async (order: OrderPlaced): Promise => { + if ( + this.clientOpts.autoSignState && + order.ordersTillSignState < ORDERS_REMAINING_TO_AUTOSYNC_AT && + !this.signStateInProgress + ) { + console.info('Will auto sign state: ', order.ordersTillSignState) + await this.getSignAndSyncStates() + } + } + private handleOrderError(error: Error, signedPayload: any): any { if (error.message.includes(MISSING_NONCES)) { this.updateTradedAssetNonces() diff --git a/src/client/environments.ts b/src/client/environments.ts index eb7213f..0a16f81 100644 --- a/src/client/environments.ts +++ b/src/client/environments.ts @@ -20,6 +20,7 @@ export interface ClientOptions { // The socketsocket conneciton supports 1 header. "User-Agent" // while the others support all, but you cannot override content-type nor the authorization token headers?: Record + autoSignState?: boolean } export const EnvironmentConfiguration = { production: { diff --git a/src/mutations/orders/fragments/orderPlacedFragment.ts b/src/mutations/orders/fragments/orderPlacedFragment.ts index ba16aad..ee29b9a 100644 --- a/src/mutations/orders/fragments/orderPlacedFragment.ts +++ b/src/mutations/orders/fragments/orderPlacedFragment.ts @@ -4,5 +4,6 @@ export const ORDER_PLACED_FRAGMENT = gql` fragment orderPlacedFields on OrderPlaced { id status + ordersTillSignState } ` diff --git a/src/types/order.ts b/src/types/order.ts index 6a47b05..a77b27d 100644 --- a/src/types/order.ts +++ b/src/types/order.ts @@ -55,4 +55,5 @@ export enum OrderType { export interface OrderPlaced { id: string status: OrderStatus + ordersTillSignState: number } From 05e4458b3537e95181d2af4d39ba9647344b0ce1 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 19 Jun 2020 10:32:23 -0500 Subject: [PATCH 2/2] Update readme --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 1370ada..a4b64d0 100644 --- a/README.md +++ b/README.md @@ -114,3 +114,25 @@ See also the [websockets example](https://gitlab.com/nash-io-public/api-client-t ## Managing API key policies For more information on how to set up policies for API keys, consult the [API key policies wiki page](https://gitlab.com/nash-io-public/api-client-typescript/-/wikis/Apikey-policies). + + +## State signing + +In order to assure your blockchain balances remain in sync with your trading balances, the client must 'sign' their state every so often before placing more orders. By default, the client will take care of this in the background for you and you will not need to worry about this. + +In special cases where a user has more than one client process running at once which is placing a high volume of orders, it is advisable to take a more custom approach. To turn of auto state syncing, initialize the client like so: + +``` +const nash = new Client(EnvironmentConfiguration.sandbox, {autoSignState: false}) +``` + +You will then be responsible for signing states when necessary. The current restriction is that states must be signed every 100 open orders, so the client should keep track and make sure to sign state before this limit is reached, otherwise placing an order will raise an error. + +This is done using the following call: + +``` +const states = await client.getSignAndSyncStates() +``` + +If you are running a high volume of orders from different clients on the same account and having difficulty managing this process, please reach out to support and we will be glad to help with an optimal solution. +