Kaynağa Gözat

Add Price Feed to Price Service (#187)

* Update new PriceFeed in p2w-sdk based on sdk update
Ali Behjati 3 yıl önce
ebeveyn
işleme
e9807ade29

Dosya farkı çok büyük olduğundan ihmal edildi
+ 316 - 251
third_party/pyth/p2w-sdk/js/package-lock.json


+ 3 - 2
third_party/pyth/p2w-sdk/js/package.json

@@ -36,11 +36,12 @@
         "typescript": "^4.3.5"
     },
     "peerDependencies": {
-	"@solana/web3.js": "^1.24.0"
+        "@solana/web3.js": "^1.24.0"
     },
     "dependencies": {
         "@certusone/wormhole-sdk": "0.2.1",
-        "@improbable-eng/grpc-web-node-http-transport": "^0.14.1"
+        "@improbable-eng/grpc-web-node-http-transport": "^0.14.1",
+        "@pythnetwork/pyth-sdk-js": "^0.1.0"
     },
     "bugs": {
         "url": "https://github.com/certusone/wormhole/issues"

+ 36 - 2
third_party/pyth/p2w-sdk/js/src/index.ts

@@ -1,6 +1,7 @@
 import { getSignedVAA, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
 import { zeroPad } from "ethers/lib/utils";
 import { PublicKey } from "@solana/web3.js";
+import { PriceFeed, PriceStatus } from "@pythnetwork/pyth-sdk-js";
 
 
 /*
@@ -162,8 +163,8 @@ export function parsePriceAttestation(arr: Buffer): PriceAttestation {
             denominator: arr.readBigInt64BE(124),
         },
         confidenceInterval: arr.readBigUInt64BE(132),
-        status: arr.readUInt32BE(140),
-        corpAct: arr.readUInt32BE(141),
+        status: arr[140],
+        corpAct: arr[141],
         timestamp: arr.readBigUInt64BE(142),
     };
 }
@@ -257,6 +258,39 @@ export async function getSignedAttestation(host: string, p2w_addr: string, seque
     return await getSignedVAA(host, CHAIN_ID_SOLANA, emitterHex, "" + sequence, extraGrpcOpts);
 }
 
+export function priceAttestationToPriceFeed(priceAttestation: PriceAttestation): PriceFeed {
+    let status;
+    if (priceAttestation.status === 0) {
+        status = PriceStatus.Unknown;
+    } else if (priceAttestation.status === 1) {
+        status = PriceStatus.Trading;
+    } else if (priceAttestation.status === 2) {
+        status = PriceStatus.Halted;
+    } else if (priceAttestation.status === 3) {
+        status = PriceStatus.Auction;
+    } else {
+        throw(new Error(`Invalid attestation status: ${priceAttestation.status}`));
+    }
+
+    // FIXME: populate 0 fields once they are in priceAttestation
+    return new PriceFeed({
+        conf: priceAttestation.confidenceInterval.toString(),
+        emaConf: priceAttestation.emaConfidence.value.toString(),
+        emaPrice: priceAttestation.emaPrice.value.toString(),
+        expo: priceAttestation.exponent,
+        id: priceAttestation.priceId,
+        maxNumPublishers: 0,
+        numPublishers: 0,
+        prevConf: "0",
+        prevPrice: "0",
+        prevPublishTime: 0,
+        price: priceAttestation.price.toString(),
+        productId: priceAttestation.productId,
+        publishTime: 0,
+        status
+    })
+}
+
 function computePrice(rawPrice: BigInt, expo: number): number {
     return Number(rawPrice) * 10 ** expo;
 }

+ 171 - 168
third_party/pyth/price-service/package-lock.json

@@ -12,12 +12,14 @@
         "@certusone/p2w-sdk": "file:../p2w-sdk/js",
         "@certusone/wormhole-sdk": "^0.1.4",
         "@certusone/wormhole-spydk": "^0.0.1",
+        "@pythnetwork/pyth-sdk-js": "^0.1.0",
         "@types/cors": "^2.8.12",
         "@types/express": "^4.17.13",
         "cors": "^2.8.5",
         "dotenv": "^10.0.0",
         "ethers": "^5.4.4",
         "express": "^4.17.2",
+        "http-status-codes": "^2.2.0",
         "prom-client": "^14.0.1",
         "winston": "^3.3.3"
       },
@@ -36,7 +38,8 @@
       "license": "MIT",
       "dependencies": {
         "@certusone/wormhole-sdk": "0.2.1",
-        "@improbable-eng/grpc-web-node-http-transport": "^0.14.1"
+        "@improbable-eng/grpc-web-node-http-transport": "^0.14.1",
+        "@pythnetwork/pyth-sdk-js": "^0.1.0"
       },
       "devDependencies": {
         "@openzeppelin/contracts": "^4.2.0",
@@ -66,9 +69,9 @@
       }
     },
     "node_modules/@babel/generator": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
-      "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz",
+      "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==",
       "dependencies": {
         "@babel/types": "^7.17.0",
         "jsesc": "^2.5.1",
@@ -101,24 +104,12 @@
       }
     },
     "node_modules/@babel/helper-function-name": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz",
-      "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz",
+      "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==",
       "dependencies": {
-        "@babel/helper-get-function-arity": "^7.16.7",
         "@babel/template": "^7.16.7",
-        "@babel/types": "^7.16.7"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-get-function-arity": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz",
-      "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==",
-      "dependencies": {
-        "@babel/types": "^7.16.7"
+        "@babel/types": "^7.17.0"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -166,9 +157,9 @@
       }
     },
     "node_modules/@babel/highlight": {
-      "version": "7.16.10",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz",
-      "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz",
+      "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==",
       "dependencies": {
         "@babel/helper-validator-identifier": "^7.16.7",
         "chalk": "^2.0.0",
@@ -179,9 +170,9 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz",
-      "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz",
+      "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==",
       "bin": {
         "parser": "bin/babel-parser.js"
       },
@@ -190,9 +181,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz",
-      "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz",
+      "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==",
       "dependencies": {
         "regenerator-runtime": "^0.13.4"
       },
@@ -214,17 +205,17 @@
       }
     },
     "node_modules/@babel/traverse": {
-      "version": "7.17.3",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
-      "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz",
+      "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==",
       "dependencies": {
         "@babel/code-frame": "^7.16.7",
-        "@babel/generator": "^7.17.3",
+        "@babel/generator": "^7.17.9",
         "@babel/helper-environment-visitor": "^7.16.7",
-        "@babel/helper-function-name": "^7.16.7",
+        "@babel/helper-function-name": "^7.17.9",
         "@babel/helper-hoist-variables": "^7.16.7",
         "@babel/helper-split-export-declaration": "^7.16.7",
-        "@babel/parser": "^7.17.3",
+        "@babel/parser": "^7.17.9",
         "@babel/types": "^7.17.0",
         "debug": "^4.1.0",
         "globals": "^11.1.0"
@@ -338,9 +329,9 @@
       "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
     },
     "node_modules/@ethersproject/abi": {
-      "version": "5.6.0",
-      "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.6.0.tgz",
-      "integrity": "sha512-AhVByTwdXCc2YQ20v300w6KVHle9g2OFc28ZAFCPnJyEpkv1xKXjZcSTgWOlv1i+0dqlgF8RCF2Rn2KC1t+1Vg==",
+      "version": "5.6.1",
+      "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.6.1.tgz",
+      "integrity": "sha512-0cqssYh6FXjlwKWBmLm3+zH2BNARoS5u/hxbz+LpQmcDB3w0W553h2btWui1/uZp2GBM/SI3KniTuMcYyHpA5w==",
       "funding": [
         {
           "type": "individual",
@@ -680,9 +671,9 @@
       ]
     },
     "node_modules/@ethersproject/networks": {
-      "version": "5.6.1",
-      "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.6.1.tgz",
-      "integrity": "sha512-b2rrupf3kCTcc3jr9xOWBuHylSFtbpJf79Ga7QR98ienU2UqGimPGEsYMgbI29KHJfA5Us89XwGVmxrlxmSrMg==",
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.6.2.tgz",
+      "integrity": "sha512-9uEzaJY7j5wpYGTojGp8U89mSsgQLc40PCMJLMCnFXTs7nhBveZ0t7dbqWUNrepWTszDbFkYD6WlL8DKx5huHA==",
       "funding": [
         {
           "type": "individual",
@@ -735,9 +726,9 @@
       }
     },
     "node_modules/@ethersproject/providers": {
-      "version": "5.6.2",
-      "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.6.2.tgz",
-      "integrity": "sha512-6/EaFW/hNWz+224FXwl8+HdMRzVHt8DpPmu5MZaIQqx/K/ELnC9eY236SMV7mleCM3NnEArFwcAAxH5kUUgaRg==",
+      "version": "5.6.4",
+      "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.6.4.tgz",
+      "integrity": "sha512-WAdknnaZ52hpHV3qPiJmKx401BLpup47h36Axxgre9zT+doa/4GC/Ne48ICPxTm0BqndpToHjpLP1ZnaxyE+vw==",
       "funding": [
         {
           "type": "individual",
@@ -1047,9 +1038,9 @@
       }
     },
     "node_modules/@grpc/grpc-js": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.0.tgz",
-      "integrity": "sha512-KwNibKGx1qmAwsrYu75FhUo3+m6GMJoBfdnYZte9YQ2EM3hZ5Ez+8+Q+FAMONtfU0XJGUkGK5S+q4CXSjx5Ahw==",
+      "version": "1.6.7",
+      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz",
+      "integrity": "sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw==",
       "dependencies": {
         "@grpc/proto-loader": "^0.6.4",
         "@types/node": ">=12.12.47"
@@ -1141,6 +1132,11 @@
       "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
       "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
     },
+    "node_modules/@pythnetwork/pyth-sdk-js": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-js/-/pyth-sdk-js-0.1.0.tgz",
+      "integrity": "sha512-fsGx2vkXncoIpsrcjx6WY7JN0R74YG/lX2UA1Wz/m6MPgJrde1LHkmikOSdMZUU3KkpWGHT1ZdYoW+Ikv7Nv9g=="
+    },
     "node_modules/@solana/buffer-layout": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.0.tgz",
@@ -1169,9 +1165,9 @@
       }
     },
     "node_modules/@solana/web3.js": {
-      "version": "1.37.0",
-      "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.37.0.tgz",
-      "integrity": "sha512-O2iCcgkGdi2FXwVLztPIZHcBuZXdhbVLavMsG+RdEyFGzFD0tQN1rOJ+Xb5eaexjqtgcqRN+Fyg3wAhLcHJbiA==",
+      "version": "1.39.1",
+      "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.39.1.tgz",
+      "integrity": "sha512-Q7XnWTAiU7n7GcoINDAAMLO7CJHpm5kPK46HKwJi2x0cusHQ3WFa7QEp6aPzH7tuf7yl/Kw1lYitcwTVOvqARA==",
       "dependencies": {
         "@babel/runtime": "^7.12.5",
         "@ethersproject/sha2": "^5.5.0",
@@ -1489,9 +1485,9 @@
       }
     },
     "node_modules/@types/lodash": {
-      "version": "4.14.181",
-      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.181.tgz",
-      "integrity": "sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag=="
+      "version": "4.14.182",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
+      "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q=="
     },
     "node_modules/@types/long": {
       "version": "4.0.1",
@@ -1504,9 +1500,9 @@
       "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
     },
     "node_modules/@types/node": {
-      "version": "16.11.26",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz",
-      "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ=="
+      "version": "16.11.27",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.27.tgz",
+      "integrity": "sha512-C1pD3kgLoZ56Uuy5lhfOxie4aZlA3UMGLX9rXteq4WitEZH6Rl80mwactt9QG0w0gLFlN/kLBTFnGXtDVWvWQw=="
     },
     "node_modules/@types/qs": {
       "version": "6.9.7",
@@ -1756,9 +1752,9 @@
       }
     },
     "node_modules/babel-plugin-styled-components": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.6.tgz",
-      "integrity": "sha512-Sk+7o/oa2HfHv3Eh8sxoz75/fFvEdHsXV4grdeHufX0nauCmymlnN0rGhIvfpMQSJMvGutJ85gvCGea4iqmDpg==",
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz",
+      "integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==",
       "dependencies": {
         "@babel/helper-annotate-as-pure": "^7.16.0",
         "@babel/helper-module-imports": "^7.16.0",
@@ -2353,9 +2349,9 @@
       }
     },
     "node_modules/ethers": {
-      "version": "5.6.2",
-      "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.6.2.tgz",
-      "integrity": "sha512-EzGCbns24/Yluu7+ToWnMca3SXJ1Jk1BvWB7CCmVNxyOeM4LLvw2OLuIHhlkhQk1dtOcj9UMsdkxUh8RiG1dxQ==",
+      "version": "5.6.4",
+      "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.6.4.tgz",
+      "integrity": "sha512-62UIfxAQXdf67TeeOaoOoPctm5hUlYgfd0iW3wxfj7qRYKDcvvy0f+sJ3W2/Pyx77R8dblvejA8jokj+lS+ATQ==",
       "funding": [
         {
           "type": "individual",
@@ -2367,7 +2363,7 @@
         }
       ],
       "dependencies": {
-        "@ethersproject/abi": "5.6.0",
+        "@ethersproject/abi": "5.6.1",
         "@ethersproject/abstract-provider": "5.6.0",
         "@ethersproject/abstract-signer": "5.6.0",
         "@ethersproject/address": "5.6.0",
@@ -2382,10 +2378,10 @@
         "@ethersproject/json-wallets": "5.6.0",
         "@ethersproject/keccak256": "5.6.0",
         "@ethersproject/logger": "5.6.0",
-        "@ethersproject/networks": "5.6.1",
+        "@ethersproject/networks": "5.6.2",
         "@ethersproject/pbkdf2": "5.6.0",
         "@ethersproject/properties": "5.6.0",
-        "@ethersproject/providers": "5.6.2",
+        "@ethersproject/providers": "5.6.4",
         "@ethersproject/random": "5.6.0",
         "@ethersproject/rlp": "5.6.0",
         "@ethersproject/sha2": "5.6.0",
@@ -2458,9 +2454,9 @@
       "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
     },
     "node_modules/fecha": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz",
-      "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q=="
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+      "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
     },
     "node_modules/file-uri-to-path": {
       "version": "1.0.0",
@@ -2582,9 +2578,9 @@
       }
     },
     "node_modules/google-protobuf": {
-      "version": "3.19.4",
-      "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.19.4.tgz",
-      "integrity": "sha512-OIPNCxsG2lkIvf+P5FNfJ/Km95CsXOBecS9ZcAU6m2Rq3svc0Apl9nB3GMDNKfQ9asNv4KjyAqGwPQFrVle3Yg=="
+      "version": "3.20.1",
+      "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.20.1.tgz",
+      "integrity": "sha512-XMf1+O32FjYIV3CYu6Tuh5PNbfNEU5Xu22X+Xkdb/DUexFlCzhvv7d5Iirm4AOwn8lv4al1YvIhzGrg2j9Zfzw=="
     },
     "node_modules/has": {
       "version": "1.0.3",
@@ -2666,6 +2662,11 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/http-status-codes": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.2.0.tgz",
+      "integrity": "sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng=="
+    },
     "node_modules/iconv-lite": {
       "version": "0.4.24",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -2724,9 +2725,9 @@
       "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
     },
     "node_modules/is-core-module": {
-      "version": "2.8.1",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
-      "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+      "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
       "dev": true,
       "dependencies": {
         "has": "^1.0.3"
@@ -2796,9 +2797,9 @@
       }
     },
     "node_modules/jayson/node_modules/@types/node": {
-      "version": "12.20.47",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.47.tgz",
-      "integrity": "sha512-BzcaRsnFuznzOItW1WpQrDHM7plAa7GIDMZ6b5pnMbkqEtM/6WCOhvZar39oeMQP79gwvFUWjjptE7/KGcNqFg=="
+      "version": "12.20.48",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.48.tgz",
+      "integrity": "sha512-4kxzqkrpwYtn6okJUcb2lfUu9ilnb3yhUOH6qX3nug8D2DupZ2drIkff2yJzYcNJVl3begnlcaBJ7tqiTTzjnQ=="
     },
     "node_modules/js-base64": {
       "version": "3.7.2",
@@ -3179,9 +3180,9 @@
       "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
     },
     "node_modules/prettier": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz",
-      "integrity": "sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==",
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
+      "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
       "dev": true,
       "bin": {
         "prettier": "bin-prettier.js"
@@ -3817,9 +3818,9 @@
       "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
     },
     "node_modules/tslib": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-      "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+      "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
     },
     "node_modules/tslint": {
       "version": "6.1.3",
@@ -4003,9 +4004,9 @@
       }
     },
     "node_modules/winston": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/winston/-/winston-3.6.0.tgz",
-      "integrity": "sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==",
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz",
+      "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==",
       "dependencies": {
         "@dabh/diagnostics": "^2.0.2",
         "async": "^3.2.3",
@@ -4150,9 +4151,9 @@
       }
     },
     "@babel/generator": {
-      "version": "7.17.7",
-      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
-      "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz",
+      "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==",
       "requires": {
         "@babel/types": "^7.17.0",
         "jsesc": "^2.5.1",
@@ -4176,21 +4177,12 @@
       }
     },
     "@babel/helper-function-name": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz",
-      "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz",
+      "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==",
       "requires": {
-        "@babel/helper-get-function-arity": "^7.16.7",
         "@babel/template": "^7.16.7",
-        "@babel/types": "^7.16.7"
-      }
-    },
-    "@babel/helper-get-function-arity": {
-      "version": "7.16.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz",
-      "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==",
-      "requires": {
-        "@babel/types": "^7.16.7"
+        "@babel/types": "^7.17.0"
       }
     },
     "@babel/helper-hoist-variables": {
@@ -4223,9 +4215,9 @@
       "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
     },
     "@babel/highlight": {
-      "version": "7.16.10",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz",
-      "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz",
+      "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==",
       "requires": {
         "@babel/helper-validator-identifier": "^7.16.7",
         "chalk": "^2.0.0",
@@ -4233,14 +4225,14 @@
       }
     },
     "@babel/parser": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz",
-      "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ=="
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz",
+      "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg=="
     },
     "@babel/runtime": {
-      "version": "7.17.8",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz",
-      "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz",
+      "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==",
       "requires": {
         "regenerator-runtime": "^0.13.4"
       }
@@ -4256,17 +4248,17 @@
       }
     },
     "@babel/traverse": {
-      "version": "7.17.3",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
-      "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
+      "version": "7.17.9",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz",
+      "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==",
       "requires": {
         "@babel/code-frame": "^7.16.7",
-        "@babel/generator": "^7.17.3",
+        "@babel/generator": "^7.17.9",
         "@babel/helper-environment-visitor": "^7.16.7",
-        "@babel/helper-function-name": "^7.16.7",
+        "@babel/helper-function-name": "^7.17.9",
         "@babel/helper-hoist-variables": "^7.16.7",
         "@babel/helper-split-export-declaration": "^7.16.7",
-        "@babel/parser": "^7.17.3",
+        "@babel/parser": "^7.17.9",
         "@babel/types": "^7.17.0",
         "debug": "^4.1.0",
         "globals": "^11.1.0"
@@ -4302,6 +4294,7 @@
         "@certusone/wormhole-sdk": "0.2.1",
         "@improbable-eng/grpc-web-node-http-transport": "^0.14.1",
         "@openzeppelin/contracts": "^4.2.0",
+        "@pythnetwork/pyth-sdk-js": "^0.1.0",
         "@typechain/ethers-v5": "^7.1.2",
         "@types/long": "^4.0.1",
         "@types/node": "^16.6.1",
@@ -4378,9 +4371,9 @@
       "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
     },
     "@ethersproject/abi": {
-      "version": "5.6.0",
-      "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.6.0.tgz",
-      "integrity": "sha512-AhVByTwdXCc2YQ20v300w6KVHle9g2OFc28ZAFCPnJyEpkv1xKXjZcSTgWOlv1i+0dqlgF8RCF2Rn2KC1t+1Vg==",
+      "version": "5.6.1",
+      "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.6.1.tgz",
+      "integrity": "sha512-0cqssYh6FXjlwKWBmLm3+zH2BNARoS5u/hxbz+LpQmcDB3w0W553h2btWui1/uZp2GBM/SI3KniTuMcYyHpA5w==",
       "requires": {
         "@ethersproject/address": "^5.6.0",
         "@ethersproject/bignumber": "^5.6.0",
@@ -4574,9 +4567,9 @@
       "integrity": "sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg=="
     },
     "@ethersproject/networks": {
-      "version": "5.6.1",
-      "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.6.1.tgz",
-      "integrity": "sha512-b2rrupf3kCTcc3jr9xOWBuHylSFtbpJf79Ga7QR98ienU2UqGimPGEsYMgbI29KHJfA5Us89XwGVmxrlxmSrMg==",
+      "version": "5.6.2",
+      "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.6.2.tgz",
+      "integrity": "sha512-9uEzaJY7j5wpYGTojGp8U89mSsgQLc40PCMJLMCnFXTs7nhBveZ0t7dbqWUNrepWTszDbFkYD6WlL8DKx5huHA==",
       "requires": {
         "@ethersproject/logger": "^5.6.0"
       }
@@ -4599,9 +4592,9 @@
       }
     },
     "@ethersproject/providers": {
-      "version": "5.6.2",
-      "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.6.2.tgz",
-      "integrity": "sha512-6/EaFW/hNWz+224FXwl8+HdMRzVHt8DpPmu5MZaIQqx/K/ELnC9eY236SMV7mleCM3NnEArFwcAAxH5kUUgaRg==",
+      "version": "5.6.4",
+      "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.6.4.tgz",
+      "integrity": "sha512-WAdknnaZ52hpHV3qPiJmKx401BLpup47h36Axxgre9zT+doa/4GC/Ne48ICPxTm0BqndpToHjpLP1ZnaxyE+vw==",
       "requires": {
         "@ethersproject/abstract-provider": "^5.6.0",
         "@ethersproject/abstract-signer": "^5.6.0",
@@ -4781,9 +4774,9 @@
       }
     },
     "@grpc/grpc-js": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.0.tgz",
-      "integrity": "sha512-KwNibKGx1qmAwsrYu75FhUo3+m6GMJoBfdnYZte9YQ2EM3hZ5Ez+8+Q+FAMONtfU0XJGUkGK5S+q4CXSjx5Ahw==",
+      "version": "1.6.7",
+      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz",
+      "integrity": "sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw==",
       "requires": {
         "@grpc/proto-loader": "^0.6.4",
         "@types/node": ">=12.12.47"
@@ -4863,6 +4856,11 @@
       "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
       "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
     },
+    "@pythnetwork/pyth-sdk-js": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-js/-/pyth-sdk-js-0.1.0.tgz",
+      "integrity": "sha512-fsGx2vkXncoIpsrcjx6WY7JN0R74YG/lX2UA1Wz/m6MPgJrde1LHkmikOSdMZUU3KkpWGHT1ZdYoW+Ikv7Nv9g=="
+    },
     "@solana/buffer-layout": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.0.tgz",
@@ -4885,9 +4883,9 @@
       }
     },
     "@solana/web3.js": {
-      "version": "1.37.0",
-      "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.37.0.tgz",
-      "integrity": "sha512-O2iCcgkGdi2FXwVLztPIZHcBuZXdhbVLavMsG+RdEyFGzFD0tQN1rOJ+Xb5eaexjqtgcqRN+Fyg3wAhLcHJbiA==",
+      "version": "1.39.1",
+      "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.39.1.tgz",
+      "integrity": "sha512-Q7XnWTAiU7n7GcoINDAAMLO7CJHpm5kPK46HKwJi2x0cusHQ3WFa7QEp6aPzH7tuf7yl/Kw1lYitcwTVOvqARA==",
       "requires": {
         "@babel/runtime": "^7.12.5",
         "@ethersproject/sha2": "^5.5.0",
@@ -5115,9 +5113,9 @@
       }
     },
     "@types/lodash": {
-      "version": "4.14.181",
-      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.181.tgz",
-      "integrity": "sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag=="
+      "version": "4.14.182",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
+      "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q=="
     },
     "@types/long": {
       "version": "4.0.1",
@@ -5130,9 +5128,9 @@
       "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
     },
     "@types/node": {
-      "version": "16.11.26",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz",
-      "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ=="
+      "version": "16.11.27",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.27.tgz",
+      "integrity": "sha512-C1pD3kgLoZ56Uuy5lhfOxie4aZlA3UMGLX9rXteq4WitEZH6Rl80mwactt9QG0w0gLFlN/kLBTFnGXtDVWvWQw=="
     },
     "@types/qs": {
       "version": "6.9.7",
@@ -5363,9 +5361,9 @@
       }
     },
     "babel-plugin-styled-components": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.6.tgz",
-      "integrity": "sha512-Sk+7o/oa2HfHv3Eh8sxoz75/fFvEdHsXV4grdeHufX0nauCmymlnN0rGhIvfpMQSJMvGutJ85gvCGea4iqmDpg==",
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz",
+      "integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==",
       "requires": {
         "@babel/helper-annotate-as-pure": "^7.16.0",
         "@babel/helper-module-imports": "^7.16.0",
@@ -5860,11 +5858,11 @@
       "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
     },
     "ethers": {
-      "version": "5.6.2",
-      "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.6.2.tgz",
-      "integrity": "sha512-EzGCbns24/Yluu7+ToWnMca3SXJ1Jk1BvWB7CCmVNxyOeM4LLvw2OLuIHhlkhQk1dtOcj9UMsdkxUh8RiG1dxQ==",
+      "version": "5.6.4",
+      "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.6.4.tgz",
+      "integrity": "sha512-62UIfxAQXdf67TeeOaoOoPctm5hUlYgfd0iW3wxfj7qRYKDcvvy0f+sJ3W2/Pyx77R8dblvejA8jokj+lS+ATQ==",
       "requires": {
-        "@ethersproject/abi": "5.6.0",
+        "@ethersproject/abi": "5.6.1",
         "@ethersproject/abstract-provider": "5.6.0",
         "@ethersproject/abstract-signer": "5.6.0",
         "@ethersproject/address": "5.6.0",
@@ -5879,10 +5877,10 @@
         "@ethersproject/json-wallets": "5.6.0",
         "@ethersproject/keccak256": "5.6.0",
         "@ethersproject/logger": "5.6.0",
-        "@ethersproject/networks": "5.6.1",
+        "@ethersproject/networks": "5.6.2",
         "@ethersproject/pbkdf2": "5.6.0",
         "@ethersproject/properties": "5.6.0",
-        "@ethersproject/providers": "5.6.2",
+        "@ethersproject/providers": "5.6.4",
         "@ethersproject/random": "5.6.0",
         "@ethersproject/rlp": "5.6.0",
         "@ethersproject/sha2": "5.6.0",
@@ -5949,9 +5947,9 @@
       "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
     },
     "fecha": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz",
-      "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q=="
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+      "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
     },
     "file-uri-to-path": {
       "version": "1.0.0",
@@ -6035,9 +6033,9 @@
       "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
     },
     "google-protobuf": {
-      "version": "3.19.4",
-      "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.19.4.tgz",
-      "integrity": "sha512-OIPNCxsG2lkIvf+P5FNfJ/Km95CsXOBecS9ZcAU6m2Rq3svc0Apl9nB3GMDNKfQ9asNv4KjyAqGwPQFrVle3Yg=="
+      "version": "3.20.1",
+      "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.20.1.tgz",
+      "integrity": "sha512-XMf1+O32FjYIV3CYu6Tuh5PNbfNEU5Xu22X+Xkdb/DUexFlCzhvv7d5Iirm4AOwn8lv4al1YvIhzGrg2j9Zfzw=="
     },
     "has": {
       "version": "1.0.3",
@@ -6109,6 +6107,11 @@
         "toidentifier": "1.0.1"
       }
     },
+    "http-status-codes": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.2.0.tgz",
+      "integrity": "sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng=="
+    },
     "iconv-lite": {
       "version": "0.4.24",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -6147,9 +6150,9 @@
       "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
     },
     "is-core-module": {
-      "version": "2.8.1",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
-      "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+      "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
       "dev": true,
       "requires": {
         "has": "^1.0.3"
@@ -6199,9 +6202,9 @@
       },
       "dependencies": {
         "@types/node": {
-          "version": "12.20.47",
-          "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.47.tgz",
-          "integrity": "sha512-BzcaRsnFuznzOItW1WpQrDHM7plAa7GIDMZ6b5pnMbkqEtM/6WCOhvZar39oeMQP79gwvFUWjjptE7/KGcNqFg=="
+          "version": "12.20.48",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.48.tgz",
+          "integrity": "sha512-4kxzqkrpwYtn6okJUcb2lfUu9ilnb3yhUOH6qX3nug8D2DupZ2drIkff2yJzYcNJVl3begnlcaBJ7tqiTTzjnQ=="
         }
       }
     },
@@ -6498,9 +6501,9 @@
       "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
     },
     "prettier": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz",
-      "integrity": "sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==",
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
+      "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
       "dev": true
     },
     "prom-client": {
@@ -6982,9 +6985,9 @@
       "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
     },
     "tslib": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-      "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+      "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
     },
     "tslint": {
       "version": "6.1.3",
@@ -7127,9 +7130,9 @@
       }
     },
     "winston": {
-      "version": "3.6.0",
-      "resolved": "https://registry.npmjs.org/winston/-/winston-3.6.0.tgz",
-      "integrity": "sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==",
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz",
+      "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==",
       "requires": {
         "@dabh/diagnostics": "^2.0.2",
         "async": "^3.2.3",

+ 2 - 0
third_party/pyth/price-service/package.json

@@ -21,12 +21,14 @@
     "@certusone/p2w-sdk": "file:../p2w-sdk/js",
     "@certusone/wormhole-sdk": "^0.1.4",
     "@certusone/wormhole-spydk": "^0.0.1",
+    "@pythnetwork/pyth-sdk-js": "^0.1.0",
     "@types/cors": "^2.8.12",
     "@types/express": "^4.17.13",
     "cors": "^2.8.5",
     "dotenv": "^10.0.0",
     "ethers": "^5.4.4",
     "express": "^4.17.2",
+    "http-status-codes": "^2.2.0",
     "prom-client": "^14.0.1",
     "winston": "^3.3.3"
   },

+ 10 - 7
third_party/pyth/price-service/src/listen.ts

@@ -13,19 +13,21 @@ import { importCoreWasm } from "@certusone/wormhole-sdk/lib/cjs/solana/wasm";
 
 import { envOrErr, sleep, TimestampInSec } from "./helpers";
 import { PromClient } from "./promClient";
-import { getBatchSummary, parseBatchPriceAttestation } from "@certusone/p2w-sdk";
+import { getBatchSummary, parseBatchPriceAttestation, priceAttestationToPriceFeed } from "@certusone/p2w-sdk";
 import { ClientReadableStream } from "@grpc/grpc-js";
 import { FilterEntry, SubscribeSignedVAAResponse } from "@certusone/wormhole-spydk/lib/cjs/proto/spy/v1/spy";
 import { logger } from "./logging";
+import { PriceFeed } from "@pythnetwork/pyth-sdk-js";
 
-export type VaaInfo = {
+export type PriceInfo = {
   vaaBytes: string,
   seqNum: number,
   receiveTime: TimestampInSec,
+  priceFeed: PriceFeed
 };
 
-export interface PriceFeedVaaInfo {
-  getLatestVaaForPriceFeed(priceFeedId: string): VaaInfo | undefined;
+export interface PriceFeedPriceInfo {
+  getLatestPriceInfo(priceFeedId: string): PriceInfo | undefined;
 }
 
 type ListenerReadinessConfig = {
@@ -39,9 +41,9 @@ type ListenerConfig = {
   readiness: ListenerReadinessConfig,
 };
 
-export class Listener implements PriceFeedVaaInfo {
+export class Listener implements PriceFeedPriceInfo {
   // Mapping of Price Feed Id to Vaa
-  private priceFeedVaaMap = new Map<string, VaaInfo>();
+  private priceFeedVaaMap = new Map<string, PriceInfo>();
   private promClient: PromClient | undefined;
   private spyServiceHost: string;
   private filters: FilterEntry[] = [];
@@ -178,6 +180,7 @@ export class Listener implements PriceFeedVaaInfo {
           seqNum: parsedVAA.sequence,
           vaaBytes: vaaBytes,
           receiveTime: (new Date()).getTime() / 1000,
+          priceFeed: priceAttestationToPriceFeed(priceAttestation)
         });
       }
     }
@@ -196,7 +199,7 @@ export class Listener implements PriceFeedVaaInfo {
     this.promClient?.incReceivedVaa();
   }
 
-  getLatestVaaForPriceFeed(priceFeedId: string): VaaInfo | undefined {
+  getLatestPriceInfo(priceFeedId: string): PriceInfo | undefined {
     return this.priceFeedVaaMap.get(priceFeedId);
   }
 

+ 40 - 0
third_party/pyth/price-service/src/promClient.ts

@@ -36,6 +36,24 @@ export class PromClient {
     help: "Freshness time of Vaa (time difference of Vaa and request time)",
     buckets: [1, 5, 10, 15, 30, 60, 120, 180]
   });
+  private apiLatestPriceFeedRequestsCounter = new client.Counter({
+    name: `${SERVICE_PREFIX}api_latest_price_feed_requests_received`,
+    help: "Number of requests for latest Price Feed of a price feed id"
+  });
+  private apiLatestPriceFeedNotFoundResponseCounter = new client.Counter({
+    name: `${SERVICE_PREFIX}api_latest_price_feed_not_found_response`,
+    help: "Number of not found responses for latest Price Feed of a price feed id"
+  });
+  private apiLatestPriceFeedSuccessResponseCounter = new client.Counter({
+    name: `${SERVICE_PREFIX}api_latest_price_feed_success_response`,
+    help: "Number of successful responses for latest vaa of a price feed id"
+  });
+  private apiLatestPriceFeedFreshnessHistogram = new client.Histogram({
+    name: `${SERVICE_PREFIX}api_latest_price_feed_freshness`,
+    help: "Freshness time of Vaa (time difference of retrieval time and request time)",
+    buckets: [1, 5, 10, 15, 30, 60, 120, 180]
+  });
+
   // End metrics
 
   private server = http.createServer(async (req, res) => {
@@ -54,10 +72,16 @@ export class PromClient {
     this.collectDefaultMetrics({ register: this.register, prefix: SERVICE_PREFIX });
     // Register each metric
     this.register.registerMetric(this.receivedVaaCounter);
+    
     this.register.registerMetric(this.apiLatestVaaRequestsCounter);
     this.register.registerMetric(this.apiLatestVaaNotFoundResponseCounter);
     this.register.registerMetric(this.apiLatestVaaSuccessResponseCounter);
     this.register.registerMetric(this.apiLatestVaaFreshnessHistogram);
+
+    this.register.registerMetric(this.apiLatestPriceFeedRequestsCounter);
+    this.register.registerMetric(this.apiLatestPriceFeedNotFoundResponseCounter);
+    this.register.registerMetric(this.apiLatestPriceFeedSuccessResponseCounter);
+    this.register.registerMetric(this.apiLatestPriceFeedFreshnessHistogram);
     // End registering metric
 
     logger.info("prometheus client listening on port " + config.port);
@@ -83,4 +107,20 @@ export class PromClient {
   addApiLatestVaaFreshness(duration: DurationInSec) {
     this.apiLatestVaaFreshnessHistogram.observe(duration);
   }
+
+  incApiLatestPriceFeedRequests() {
+    this.apiLatestPriceFeedRequestsCounter.inc();
+  }
+
+  incApiLatestPriceFeedNotFoundResponse() {
+    this.apiLatestPriceFeedNotFoundResponseCounter.inc();
+  }
+
+  incApiLatestPriceFeedSuccessResponse() {
+    this.apiLatestPriceFeedSuccessResponseCounter.inc();
+  }
+
+  addApiLatestPriceFeedFreshness(duration: DurationInSec) {
+    this.apiLatestPriceFeedFreshnessHistogram.observe(duration);
+  }
 }

+ 63 - 14
third_party/pyth/price-service/src/rest.ts

@@ -1,20 +1,20 @@
 import express from "express";
 import cors from "cors";
 import { Request, Response } from "express";
-import { PriceFeedVaaInfo } from "./listen";
+import { PriceFeedPriceInfo } from "./listen";
 import { logger } from "./logging";
 import { PromClient } from "./promClient";
 import { DurationInSec } from "./helpers";
-
+import { StatusCodes } from "http-status-codes";
 
 export class RestAPI {
   private port: number;
-  private priceFeedVaaInfo: PriceFeedVaaInfo;
+  private priceFeedVaaInfo: PriceFeedPriceInfo;
   private isReady: (() => boolean) | undefined;
   private promClient: PromClient | undefined;
 
   constructor(config: { port: number; }, 
-    priceFeedVaaInfo: PriceFeedVaaInfo,
+    priceFeedVaaInfo: PriceFeedPriceInfo,
     isReady?: () => boolean,
     promClient?: PromClient) {
     this.port = config.port;
@@ -38,36 +38,85 @@ export class RestAPI {
       this.promClient?.incApiLatestVaaRequests();
       logger.info(`Received latest_vaa_bytes request for ${req.params.price_feed_id}`)
 
-      let latestVaa = this.priceFeedVaaInfo.getLatestVaaForPriceFeed(req.params.price_feed_id);
+      let latestPriceInfo = this.priceFeedVaaInfo.getLatestPriceInfo(req.params.price_feed_id);
 
-      if (latestVaa === undefined) {
+      if (latestPriceInfo === undefined) {
         this.promClient?.incApiLatestVaaNotFoundResponse();
-        res.sendStatus(404);
+        res.sendStatus(StatusCodes.NOT_FOUND);
         return;
       }
 
       this.promClient?.incApiLatestVaaSuccessResponse();
 
-      const freshness: DurationInSec = (new Date).getTime()/1000 - latestVaa.receiveTime;
+      const freshness: DurationInSec = (new Date).getTime()/1000 - latestPriceInfo.receiveTime;
       this.promClient?.addApiLatestVaaFreshness(freshness);
 
-      res.status(200);
-      res.write(latestVaa.vaaBytes);
-      res.end();
+      res.send(latestPriceInfo.vaaBytes);
     });
     endpoints.push("latest_vaa_bytes/<price_feed_id>");
 
+    // It will be called with query param `id` such as: `/latest_price_feed?id=xyz&id=abc
+    app.get("/latest_price_feed", (req: Request, res: Response) => {
+      this.promClient?.incApiLatestPriceFeedRequests();
+      logger.info(`Received latest_price_feed request for query: ${req.query}`);
+
+      if (req.query.id === undefined) {
+        res.status(StatusCodes.BAD_REQUEST).send("No id is provided");
+        return;
+      }
+
+      let priceIds: string[] = [];
+      if (typeof(req.query.id) === "string") {
+        priceIds.push(req.query.id);
+      } else if (Array.isArray(req.query.id)) {
+        for (let entry of req.query.id) {
+          if (typeof(entry) === "string") {
+            priceIds.push(entry);
+          } else {
+            res.status(StatusCodes.BAD_REQUEST).send("id is expected to be a hex string or an array of hex strings");
+            return;    
+          }
+        }
+      } else {
+        res.status(StatusCodes.BAD_REQUEST).send("id is expected to be a hex string or an array of hex strings");
+        return;
+      }
+
+      let responseJson = []
+
+      for (let id of priceIds) {
+        let latestPriceInfo = this.priceFeedVaaInfo.getLatestPriceInfo(id);
+
+        if (latestPriceInfo === undefined) {
+          this.promClient?.incApiLatestPriceFeedNotFoundResponse();
+          res.status(StatusCodes.NOT_FOUND).send(`Price Feed with id ${id} not found`);
+          return;
+        }
+    
+        const freshness: DurationInSec = (new Date).getTime()/1000 - latestPriceInfo.receiveTime;
+        this.promClient?.addApiLatestPriceFeedFreshness(freshness); 
+        
+        responseJson.push(latestPriceInfo.priceFeed.toJson());
+      }
+
+      this.promClient?.incApiLatestPriceFeedSuccessResponse();
+
+      res.json(responseJson);
+    });
+    endpoints.push("latest_price_feed/<price_feed_id>");
+
+
     app.get("/ready", (_, res: Response) => {
       if (this.isReady!()) {
-        res.sendStatus(200);
+        res.sendStatus(StatusCodes.OK);
       } else {
-        res.sendStatus(503);
+        res.sendStatus(StatusCodes.SERVICE_UNAVAILABLE);
       }
     });
     endpoints.push('ready');
 
     app.get("/live", (_, res: Response) => {
-      res.sendStatus(200);
+      res.sendStatus(StatusCodes.OK);
     });
     endpoints.push("live");
 

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor