From 378e99b345e9a8529840ad745c29a0fb2ad4605d Mon Sep 17 00:00:00 2001 From: Daniel Chew Date: Mon, 2 Jan 2023 17:38:52 +0900 Subject: [PATCH] add binary query string to /latest_price_feeds (#445) * add binary query string to /latest_price_feeds * fix precommit * fix both rest and ws tests * bump package version * fix tests to use the latest schema --- .../pyth/price-service/package-lock.json | 4 +-- third_party/pyth/price-service/package.json | 2 +- .../price-service/src/__tests__/rest.test.ts | 25 +++++++++++++++--- .../price-service/src/__tests__/ws.test.ts | 21 ++++++++------- third_party/pyth/price-service/src/rest.ts | 26 ++++++++++++------- 5 files changed, 52 insertions(+), 26 deletions(-) diff --git a/third_party/pyth/price-service/package-lock.json b/third_party/pyth/price-service/package-lock.json index af0a70c7..2cd8ef60 100644 --- a/third_party/pyth/price-service/package-lock.json +++ b/third_party/pyth/price-service/package-lock.json @@ -1,12 +1,12 @@ { "name": "@pythnetwork/pyth-price-service", - "version": "2.2.3", + "version": "2.2.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@pythnetwork/pyth-price-service", - "version": "2.2.3", + "version": "2.2.4", "license": "Apache-2.0", "dependencies": { "@certusone/wormhole-sdk": "^0.1.4", diff --git a/third_party/pyth/price-service/package.json b/third_party/pyth/price-service/package.json index bd1ed157..c87f2f23 100644 --- a/third_party/pyth/price-service/package.json +++ b/third_party/pyth/price-service/package.json @@ -1,6 +1,6 @@ { "name": "@pythnetwork/pyth-price-service", - "version": "2.2.3", + "version": "2.2.4", "description": "Pyth Price Service", "main": "index.js", "scripts": { diff --git a/third_party/pyth/price-service/src/__tests__/rest.test.ts b/third_party/pyth/price-service/src/__tests__/rest.test.ts index d5a1c724..80fd22b4 100644 --- a/third_party/pyth/price-service/src/__tests__/rest.test.ts +++ b/third_party/pyth/price-service/src/__tests__/rest.test.ts @@ -1,9 +1,9 @@ -import { HexString, PriceFeed, Price } from "@pythnetwork/pyth-sdk-js"; -import { PriceStore, PriceInfo } from "../listen"; -import { RestAPI } from "../rest"; +import { HexString, Price, PriceFeed } from "@pythnetwork/pyth-sdk-js"; import { Express } from "express"; -import request from "supertest"; import { StatusCodes } from "http-status-codes"; +import request from "supertest"; +import { PriceInfo, PriceStore } from "../listen"; +import { RestAPI } from "../rest"; let app: Express; let priceInfoMap: Map; @@ -82,6 +82,23 @@ describe("Latest Price Feed Endpoint", () => { expect(resp.body).toContainEqual(dummyPriceFeed(ids[1]).toJson()); }); + test("When called with valid ids and binary flag set to true, returns correct price feed with binary vaa", async () => { + const ids = [expandTo64Len("abcd"), expandTo64Len("3456")]; + const resp = await request(app) + .get("/api/latest_price_feeds") + .query({ ids, binary: true }); + expect(resp.status).toBe(StatusCodes.OK); + expect(resp.body.length).toBe(2); + expect(resp.body).toContainEqual({ + ...priceInfoMap.get(ids[0])!.priceFeed.toJson(), + vaa: priceInfoMap.get(ids[0])!.vaa.toString("base64"), + }); + expect(resp.body).toContainEqual({ + ...priceInfoMap.get(ids[1])!.priceFeed.toJson(), + vaa: priceInfoMap.get(ids[1])!.vaa.toString("base64"), + }); + }); + test("When called with some non-existent ids within ids, returns error mentioning non-existent ids", async () => { const ids = [ expandTo64Len("ab01"), diff --git a/third_party/pyth/price-service/src/__tests__/ws.test.ts b/third_party/pyth/price-service/src/__tests__/ws.test.ts index bf53b1a0..a397a1c9 100644 --- a/third_party/pyth/price-service/src/__tests__/ws.test.ts +++ b/third_party/pyth/price-service/src/__tests__/ws.test.ts @@ -1,4 +1,4 @@ -import { HexString, PriceFeed } from "@pythnetwork/pyth-sdk-js"; +import { HexString, Price, PriceFeed } from "@pythnetwork/pyth-sdk-js"; import { Server } from "http"; import { WebSocket, WebSocketServer } from "ws"; import { sleep } from "../helpers"; @@ -49,20 +49,20 @@ function dummyPriceInfo( } function dummyPriceFeed(id: string): PriceFeed { - return PriceFeed.fromJson({ - ema_price: { + return new PriceFeed({ + emaPrice: new Price({ conf: "1", expo: 2, price: "3", - publish_time: 4, - }, + publishTime: 4, + }), id, - price: { + price: new Price({ conf: "5", expo: 6, price: "7", - publish_time: 8, - }, + publishTime: 8, + }), }); } @@ -273,7 +273,10 @@ describe("Client receives data", () => { expect(serverMessages[1]).toEqual({ type: "price_update", - price_feed: priceInfos[0].priceFeed.toJson(), + price_feed: { + ...priceInfos[0].priceFeed.toJson(), + vaa: priceInfos[0].vaa.toString("base64"), + }, }); api.dispatchPriceFeedUpdate(priceInfos[1]); diff --git a/third_party/pyth/price-service/src/rest.ts b/third_party/pyth/price-service/src/rest.ts index 2e29361c..597b8463 100644 --- a/third_party/pyth/price-service/src/rest.ts +++ b/third_party/pyth/price-service/src/rest.ts @@ -1,15 +1,14 @@ +import { HexString } from "@pythnetwork/pyth-sdk-js"; import cors from "cors"; import express, { NextFunction, Request, Response } from "express"; import { Joi, schema, validate, ValidationError } from "express-validation"; import { Server } from "http"; import { StatusCodes } from "http-status-codes"; import morgan from "morgan"; -import responseTime from "response-time"; -import { DurationInMs, DurationInSec, TimestampInSec } from "./helpers"; +import { TimestampInSec } from "./helpers"; import { PriceStore } from "./listen"; import { logger } from "./logging"; import { PromClient } from "./promClient"; -import { HexString } from "@pythnetwork/pyth-sdk-js"; const MORGAN_LOG_FORMAT = ':remote-addr - :remote-user ":method :url HTTP/:http-version"' + @@ -120,6 +119,7 @@ export class RestAPI { .items(Joi.string().regex(/^(0x)?[a-f0-9]{64}$/)) .required(), verbose: Joi.boolean(), + binary: Joi.boolean(), }).required(), }; app.get( @@ -129,6 +129,8 @@ export class RestAPI { const priceIds = req.query.ids as string[]; // verbose is optional, default to false const verbose = req.query.verbose === "true"; + // binary is optional, default to false + const binary = req.query.binary === "true"; const responseJson = []; @@ -146,9 +148,9 @@ export class RestAPI { continue; } - if (verbose) { - responseJson.push({ - ...latestPriceInfo.priceFeed.toJson(), + responseJson.push({ + ...latestPriceInfo.priceFeed.toJson(), + ...(verbose && { metadata: { emitter_chain: latestPriceInfo.emitterChainId, attestation_time: latestPriceInfo.attestationTime, @@ -156,10 +158,11 @@ export class RestAPI { price_service_receive_time: latestPriceInfo.priceServiceReceiveTime, }, - }); - } else { - responseJson.push(latestPriceInfo.priceFeed.toJson()); - } + }), + ...(binary && { + vaa: latestPriceInfo.vaa.toString("base64"), + }), + }); } if (notFoundIds.length > 0) { @@ -175,6 +178,9 @@ export class RestAPI { endpoints.push( "api/latest_price_feeds?ids[]=&ids[]=&..&verbose=true" ); + endpoints.push( + "api/latest_price_feeds?ids[]=&ids[]=&..&verbose=true&binary=true" + ); app.get("/api/price_feed_ids", (req: Request, res: Response) => { const availableIds = this.priceFeedVaaInfo.getPriceIds();