From d5c3090442940a6a1d7c254be9896fa38b827e40 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Mon, 18 Sep 2023 20:32:03 +0200 Subject: [PATCH] feat(price-service/client): add out-of-order flag support --- package-lock.json | 10 +++-- price_service/client/js/package.json | 2 +- .../client/js/src/PriceServiceConnection.ts | 6 +++ .../js/src/__tests__/connection.e2e.test.ts | 43 +++++++++++++++++-- target_chains/aptos/sdk/js/package.json | 2 +- target_chains/cosmwasm/sdk/js/package.json | 2 +- target_chains/sui/sdk/js/package.json | 2 +- 7 files changed, 55 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5c56c06a..ab247a76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30590,6 +30590,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", + "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "^4.3.0" @@ -30919,6 +30920,7 @@ "version": "5.0.7", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", + "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "^4.3.0" @@ -57438,7 +57440,7 @@ }, "price_service/client/js": { "name": "@pythnetwork/price-service-client", - "version": "1.6.2", + "version": "1.7.0", "license": "Apache-2.0", "dependencies": { "@pythnetwork/price-service-sdk": "*", @@ -57964,7 +57966,7 @@ }, "target_chains/aptos/sdk/js": { "name": "@pythnetwork/pyth-aptos-js", - "version": "1.2.0", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { "@pythnetwork/price-service-client": "*", @@ -58133,7 +58135,7 @@ }, "target_chains/cosmwasm/sdk/js": { "name": "@pythnetwork/pyth-terra-js", - "version": "1.3.1", + "version": "1.4.0", "license": "Apache-2.0", "dependencies": { "@pythnetwork/price-service-client": "*", @@ -59678,7 +59680,7 @@ }, "target_chains/sui/sdk/js": { "name": "@pythnetwork/pyth-sui-js", - "version": "1.0.2", + "version": "1.1.0", "license": "Apache-2.0", "dependencies": { "@mysten/sui.js": "^0.37.1", diff --git a/price_service/client/js/package.json b/price_service/client/js/package.json index f45123f2..10d7f4dd 100644 --- a/price_service/client/js/package.json +++ b/price_service/client/js/package.json @@ -1,6 +1,6 @@ { "name": "@pythnetwork/price-service-client", - "version": "1.6.2", + "version": "1.7.0", "description": "Pyth price service client", "author": { "name": "Pyth Data Association" diff --git a/price_service/client/js/src/PriceServiceConnection.ts b/price_service/client/js/src/PriceServiceConnection.ts index 108f1139..d4505851 100644 --- a/price_service/client/js/src/PriceServiceConnection.ts +++ b/price_service/client/js/src/PriceServiceConnection.ts @@ -13,6 +13,8 @@ export type PriceFeedRequestConfig = { verbose?: boolean; /* Optional binary to include the price feeds binary update data */ binary?: boolean; + /* Optional config for the websocket subscription to receive out of order updates */ + allowOutOfOrder?: boolean; }; export type PriceServiceConnectionConfig = { @@ -38,6 +40,7 @@ type ClientMessage = { ids: HexString[]; verbose?: boolean; binary?: boolean; + allow_out_of_order?: boolean; }; type ServerResponse = { @@ -92,6 +95,7 @@ export class PriceServiceConnection { this.priceFeedRequestConfig = { binary: config?.priceFeedRequestConfig?.binary, verbose: config?.priceFeedRequestConfig?.verbose ?? config?.verbose, + allowOutOfOrder: config?.priceFeedRequestConfig?.allowOutOfOrder, }; this.priceFeedCallbacks = new Map(); @@ -269,6 +273,7 @@ export class PriceServiceConnection { type: "subscribe", verbose: this.priceFeedRequestConfig.verbose, binary: this.priceFeedRequestConfig.binary, + allow_out_of_order: this.priceFeedRequestConfig.allowOutOfOrder, }; await this.wsClient?.send(JSON.stringify(message)); @@ -352,6 +357,7 @@ export class PriceServiceConnection { type: "subscribe", verbose: this.priceFeedRequestConfig.verbose, binary: this.priceFeedRequestConfig.binary, + allow_out_of_order: this.priceFeedRequestConfig.allowOutOfOrder, }; this.logger.info("Resubscribing to existing price feeds."); diff --git a/price_service/client/js/src/__tests__/connection.e2e.test.ts b/price_service/client/js/src/__tests__/connection.e2e.test.ts index 2f8783fd..5d26d74a 100644 --- a/price_service/client/js/src/__tests__/connection.e2e.test.ts +++ b/price_service/client/js/src/__tests__/connection.e2e.test.ts @@ -10,11 +10,11 @@ async function sleep(duration: DurationInMs): Promise { return new Promise((res) => setTimeout(res, duration)); } -// The endpoint is set to the price service endpoint in Tilt. +// The endpoint is set to the price service endpoint. // Please note that if you change it to a mainnet/testnet endpoint -// some tests might fail due to the huge response size of a request -// , i.e. requesting latest price feeds or vaas of all price ids. -const PRICE_SERVICE_ENDPOINT = "http://pyth-price-server:4200"; +// some tests might fail due to the huge response size of a request. +// i.e. requesting latest price feeds or vaas of all price ids. +const PRICE_SERVICE_ENDPOINT = "http://127.0.0.1:7575"; describe("Test http endpoints", () => { test("Get price feed (without verbose/binary) works", async () => { @@ -193,4 +193,39 @@ describe("Test websocket endpoints", () => { expect(observedFeeds.has(id)).toBe(true); } }); + + // This test only works on Hermes and is not stable because there might + // be no out of order updates. Hence the last check is commented out. + test.concurrent( + "websocket subscription works with allow out of order", + async () => { + const connection = new PriceServiceConnection(PRICE_SERVICE_ENDPOINT, { + priceFeedRequestConfig: { allowOutOfOrder: true, verbose: true }, + }); + + const ids = await connection.getPriceFeedIds(); + expect(ids.length).toBeGreaterThan(0); + + const observedSlots: number[] = []; + + await connection.subscribePriceFeedUpdates(ids, (priceFeed) => { + expect(priceFeed.getMetadata()).toBeDefined(); + expect(priceFeed.getVAA()).toBeUndefined(); + observedSlots.push(priceFeed.getMetadata()!.slot!); + }); + + // Wait for 20 seconds + await sleep(20000); + connection.closeWebSocket(); + + let seenOutOfOrder = false; + for (let i = 1; i < observedSlots.length; i++) { + if (observedSlots[i] < observedSlots[i - 1]) { + seenOutOfOrder = true; + } + } + + // expect(seenOutOfOrder).toBe(true); + } + ); }); diff --git a/target_chains/aptos/sdk/js/package.json b/target_chains/aptos/sdk/js/package.json index 4e45efaa..797fbd2d 100644 --- a/target_chains/aptos/sdk/js/package.json +++ b/target_chains/aptos/sdk/js/package.json @@ -1,6 +1,6 @@ { "name": "@pythnetwork/pyth-aptos-js", - "version": "1.2.0", + "version": "1.3.0", "description": "Pyth Network Aptos Utilities", "homepage": "https://pyth.network", "author": { diff --git a/target_chains/cosmwasm/sdk/js/package.json b/target_chains/cosmwasm/sdk/js/package.json index cc5e2430..a5d3d65a 100644 --- a/target_chains/cosmwasm/sdk/js/package.json +++ b/target_chains/cosmwasm/sdk/js/package.json @@ -1,6 +1,6 @@ { "name": "@pythnetwork/pyth-terra-js", - "version": "1.3.1", + "version": "1.4.0", "description": "Pyth Network Terra Utils in JS", "homepage": "https://pyth.network", "author": { diff --git a/target_chains/sui/sdk/js/package.json b/target_chains/sui/sdk/js/package.json index f229648d..2aba6be9 100644 --- a/target_chains/sui/sdk/js/package.json +++ b/target_chains/sui/sdk/js/package.json @@ -1,6 +1,6 @@ { "name": "@pythnetwork/pyth-sui-js", - "version": "1.0.2", + "version": "1.1.0", "description": "Pyth Network Sui Utilities", "homepage": "https://pyth.network", "author": {