Forráskód Böngészése

feat(HermesClient): Add TWAP endpoint support (#2165)

* add latest twap endpoint

* refactor: add logs, clean up

* fix: update HermesClient usage in post_twap_update

* fix: fix window_seconds arg, better log
Tejas Badadare 10 hónapja
szülő
commit
512f6375bb

+ 1 - 1
apps/hermes/client/js/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@pythnetwork/hermes-client",
-  "version": "1.2.0",
+  "version": "1.3.0",
   "description": "Pyth Hermes Client",
   "author": {
     "name": "Pyth Data Association"

+ 47 - 1
apps/hermes/client/js/src/HermesClient.ts

@@ -10,6 +10,7 @@ export type EncodingType = z.infer<typeof schemas.EncodingType>;
 export type PriceFeedMetadata = z.infer<typeof schemas.PriceFeedMetadata>;
 export type PriceIdInput = z.infer<typeof schemas.PriceIdInput>;
 export type PriceUpdate = z.infer<typeof schemas.PriceUpdate>;
+export type TwapsResponse = z.infer<typeof schemas.TwapsResponse>;
 export type PublisherCaps = z.infer<
   typeof schemas.LatestPublisherStakeCapsUpdateDataResponse
 >;
@@ -80,7 +81,12 @@ export class HermesClient {
       const response = await fetch(url, options);
       clearTimeout(timeout); // Clear the timeout if the request completes in time
       if (!response.ok) {
-        throw new Error(`HTTP error! status: ${response.status}`);
+        const errorBody = await response.text();
+        throw new Error(
+          `HTTP error! status: ${response.status}${
+            errorBody ? `, body: ${errorBody}` : ""
+          }`
+        );
       }
       const data = await response.json();
       return schema.parse(data);
@@ -258,6 +264,46 @@ export class HermesClient {
     return new EventSource(url.toString(), { headers: this.headers });
   }
 
+  /**
+   * Fetch the latest TWAP (time weighted average price) for a set of price feed IDs.
+   * This endpoint can be customized by specifying the encoding type and whether the results should also return the calculated TWAP using the options object.
+   * This will throw an error if there is a network problem or the price service returns a non-ok response.
+   *
+   * @param ids Array of hex-encoded price feed IDs for which updates are requested.
+   * @param window_seconds The time window in seconds over which to calculate the TWAP, ending at the current time.
+   *  For example, a value of 300 would return the most recent 5 minute TWAP. Must be greater than 0 and less than or equal to 600 seconds (10 minutes).
+   * @param options Optional parameters:
+   *        - encoding: Encoding type. If specified, return the TWAP binary data in the encoding specified by the encoding parameter. Default is hex.
+   *        - parsed: Boolean to specify if the calculated TWAP should be included in the response. Default is false.
+   *        - ignoreInvalidPriceIds: Boolean to specify if invalid price IDs should be ignored instead of returning an error. Default is false.
+   *
+   * @returns TwapsResponse object containing the latest TWAPs.
+   */
+  async getLatestTwaps(
+    ids: HexString[],
+    window_seconds: number,
+    options?: {
+      encoding?: EncodingType;
+      parsed?: boolean;
+      ignoreInvalidPriceIds?: boolean;
+    }
+  ): Promise<TwapsResponse> {
+    const url = new URL(
+      `v2/updates/twap/${window_seconds}/latest`,
+      this.baseURL
+    );
+    for (const id of ids) {
+      url.searchParams.append("ids[]", id);
+    }
+
+    if (options) {
+      const transformedOptions = camelToSnakeCaseObject(options);
+      this.appendUrlSearchParams(url, transformedOptions);
+    }
+
+    return this.httpRequest(url.toString(), schemas.TwapsResponse);
+  }
+
   private appendUrlSearchParams(
     url: URL,
     params: Record<string, string | boolean>

+ 8 - 0
apps/hermes/client/js/src/examples/HermesClient.ts

@@ -61,6 +61,7 @@ async function run() {
   const priceIds = argv.priceIds as string[];
 
   // Get price feeds
+  console.log(`Price feeds matching "btc" with asset type "crypto":`);
   const priceFeeds = await connection.getPriceFeeds({
     query: "btc",
     filter: "crypto",
@@ -68,10 +69,17 @@ async function run() {
   console.log(priceFeeds);
 
   // Latest price updates
+  console.log(`Latest price updates for price IDs ${priceIds}:`);
   const priceUpdates = await connection.getLatestPriceUpdates(priceIds);
   console.log(priceUpdates);
 
+  // Get the latest 5 second TWAPs
+  console.log(`Latest 5 second TWAPs for price IDs ${priceIds}`);
+  const twapUpdates = await connection.getLatestTwaps(priceIds, 5);
+  console.log(twapUpdates);
+
   // Streaming price updates
+  console.log(`Streaming latest prices for price IDs ${priceIds}...`);
   const eventSource = await connection.getPriceUpdatesStream(priceIds, {
     encoding: "hex",
     parsed: true,

+ 6 - 5
target_chains/solana/sdk/js/pyth_solana_receiver/examples/post_twap_update.ts

@@ -40,8 +40,9 @@ async function main() {
   // Post the TWAP updates to ephemeral accounts, one per price feed
   await transactionBuilder.addPostTwapUpdates(twapUpdateData);
   console.log(
-    "The SOL/USD TWAP update will get posted to:",
-    transactionBuilder.getTwapUpdateAccount(SOL_PRICE_FEED_ID).toBase58()
+    `\nThe SOL/USD TWAP update will get posted to: ${transactionBuilder
+      .getTwapUpdateAccount(SOL_PRICE_FEED_ID)
+      .toBase58()}\n`
   );
 
   await transactionBuilder.addTwapConsumerInstructions(
@@ -69,10 +70,10 @@ async function main() {
 async function getTwapUpdateData() {
   const hermesConnection = new HermesClient("https://hermes.pyth.network/", {});
 
-  // Request TWAP updates for the last hour (3600 seconds)
-  const response = await hermesConnection.getLatestTwapUpdates(
+  // Request TWAP updates with a 5 minute window (300 seconds)
+  const response = await hermesConnection.getLatestTwaps(
     [SOL_PRICE_FEED_ID, ETH_PRICE_FEED_ID],
-    3600,
+    300,
     { encoding: "base64" }
   );
 

+ 1 - 1
target_chains/solana/sdk/js/pyth_solana_receiver/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@pythnetwork/pyth-solana-receiver",
-  "version": "0.9.0",
+  "version": "0.9.1",
   "description": "Pyth solana receiver SDK",
   "homepage": "https://pyth.network",
   "main": "lib/index.js",