@haven-fi/solauto-sdk 1.0.833 → 1.0.834

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"priceUtils.d.ts","sourceRoot":"","sources":["../../src/utils/priceUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,IAAI,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAiBrE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,UAAU,WAAW;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,SAAS,EAAE,EAClB,SAAS,GAAE,SAA8B,EACzC,SAAS,CAAC,EAAE,SAAS,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CA8CnB;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,EAAE,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAuDtC;AAkBD,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,SAAS,EAAE,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAsDtC;AAED,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,SAAS,EAAE,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAsBtC;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,EACnD,SAAS,GAAE,SAA8B,EACzC,SAAS,CAAC,EAAE,SAAS,GACpB,MAAM,GAAG,SAAS,CAyBpB"}
1
+ {"version":3,"file":"priceUtils.d.ts","sourceRoot":"","sources":["../../src/utils/priceUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,IAAI,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAiBrE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,UAAU,WAAW;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,SAAS,EAAE,EAClB,SAAS,GAAE,SAA8B,EACzC,SAAS,CAAC,EAAE,SAAS,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CA8CnB;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,EAAE,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CA0EtC;AAkBD,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,SAAS,EAAE,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAsDtC;AAED,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,SAAS,EAAE,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAsBtC;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,EACnD,SAAS,GAAE,SAA8B,EACzC,SAAS,CAAC,EAAE,SAAS,GACpB,MAAM,GAAG,SAAS,CAyBpB"}
@@ -80,9 +80,16 @@ async function getPythPrices(mints) {
80
80
  return {};
81
81
  }
82
82
  const priceFeedIds = mints.map((mint) => constants_1.PYTH_PRICE_FEED_IDS[mint.toString()]);
83
- const getReq = async () => await fetch(`https://hermes.pyth.network/v2/updates/price/latest?${priceFeedIds
83
+ const pythApiKey = process.env.PYTH_API_KEY;
84
+ // Hermes requires authentication as of August 26, 2026. Jupiter keeps price
85
+ // fetching available for browser clients and services without a Pyth API key.
86
+ if (!pythApiKey) {
87
+ (0, generalUtils_1.consoleLog)("PYTH_API_KEY is not configured; using Jupiter prices");
88
+ return await getJupTokenPrices(mints);
89
+ }
90
+ const getReq = async () => await fetch(`https://pyth.dourolabs.app/hermes/v2/updates/price/latest?${priceFeedIds
84
91
  .map((x) => `ids%5B%5D=${x}`)
85
- .join("&")}`);
92
+ .join("&")}`, { headers: { Authorization: `Bearer ${pythApiKey}` } });
86
93
  const deriveValue = (price, exponent) => {
87
94
  if (exponent > 0) {
88
95
  return Number((0, numberUtils_1.toBaseUnit)(Number(price), exponent));
@@ -94,23 +101,30 @@ async function getPythPrices(mints) {
94
101
  return Number(price);
95
102
  }
96
103
  };
97
- const prices = await (0, generalUtils_1.retryWithExponentialBackoff)(async () => {
98
- let resp = await getReq();
99
- let status = resp.status;
100
- if (status !== 200) {
101
- throw new Error(JSON.stringify(resp));
102
- }
103
- const json = await resp.json();
104
- const prices = json.parsed.map((x) => {
105
- return {
106
- realtimePrice: deriveValue(x.price.price, x.price.expo),
107
- confInterval: deriveValue(x.price.conf, x.price.expo) * 2.12,
108
- emaPrice: deriveValue(x.ema_price.price, x.ema_price.expo),
109
- emaConfInterval: deriveValue(x.ema_price.conf, x.ema_price.expo) * 2.12,
110
- };
111
- });
112
- return prices;
113
- }, 5, 250);
104
+ let prices;
105
+ try {
106
+ prices = await (0, generalUtils_1.retryWithExponentialBackoff)(async () => {
107
+ const resp = await getReq();
108
+ if (!resp.ok) {
109
+ const responseBody = await resp.text();
110
+ throw new Error(`Pyth Hermes request failed (${resp.status} ${resp.statusText}): ${responseBody}`);
111
+ }
112
+ const json = await resp.json();
113
+ const parsedPrices = json.parsed.map((x) => {
114
+ return {
115
+ realtimePrice: deriveValue(x.price.price, x.price.expo),
116
+ confInterval: deriveValue(x.price.conf, x.price.expo) * 2.12,
117
+ emaPrice: deriveValue(x.ema_price.price, x.ema_price.expo),
118
+ emaConfInterval: deriveValue(x.ema_price.conf, x.ema_price.expo) * 2.12,
119
+ };
120
+ });
121
+ return parsedPrices;
122
+ }, 5, 250);
123
+ }
124
+ catch (error) {
125
+ (0, generalUtils_1.consoleLog)(`Failed to fetch Pyth prices; using Jupiter prices: ${error?.message ?? error}`);
126
+ return await getJupTokenPrices(mints);
127
+ }
114
128
  return (0, generalUtils_1.createRecord)(mints.map((x) => x.toString()), prices);
115
129
  }
116
130
  function getSortedPriceData(prices, mints) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haven-fi/solauto-sdk",
3
- "version": "1.0.833",
3
+ "version": "1.0.834",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "description": "Typescript SDK for the Solauto program on the Solana blockchain",
@@ -88,12 +88,21 @@ export async function getPythPrices(
88
88
  const priceFeedIds = mints.map(
89
89
  (mint) => PYTH_PRICE_FEED_IDS[mint.toString()]
90
90
  );
91
+ const pythApiKey = process.env.PYTH_API_KEY;
92
+
93
+ // Hermes requires authentication as of August 26, 2026. Jupiter keeps price
94
+ // fetching available for browser clients and services without a Pyth API key.
95
+ if (!pythApiKey) {
96
+ consoleLog("PYTH_API_KEY is not configured; using Jupiter prices");
97
+ return await getJupTokenPrices(mints);
98
+ }
91
99
 
92
100
  const getReq = async () =>
93
101
  await fetch(
94
- `https://hermes.pyth.network/v2/updates/price/latest?${priceFeedIds
102
+ `https://pyth.dourolabs.app/hermes/v2/updates/price/latest?${priceFeedIds
95
103
  .map((x) => `ids%5B%5D=${x}`)
96
- .join("&")}`
104
+ .join("&")}`,
105
+ { headers: { Authorization: `Bearer ${pythApiKey}` } }
97
106
  );
98
107
 
99
108
  const deriveValue = (price: number, exponent: number) => {
@@ -106,30 +115,40 @@ export async function getPythPrices(
106
115
  }
107
116
  };
108
117
 
109
- const prices: PriceResult[] = await retryWithExponentialBackoff(
110
- async () => {
111
- let resp = await getReq();
112
- let status = resp.status;
113
- if (status !== 200) {
114
- throw new Error(JSON.stringify(resp));
115
- }
118
+ let prices: PriceResult[];
119
+ try {
120
+ prices = await retryWithExponentialBackoff(
121
+ async () => {
122
+ const resp = await getReq();
123
+ if (!resp.ok) {
124
+ const responseBody = await resp.text();
125
+ throw new Error(
126
+ `Pyth Hermes request failed (${resp.status} ${resp.statusText}): ${responseBody}`
127
+ );
128
+ }
116
129
 
117
- const json = await resp.json();
118
- const prices = json.parsed.map((x: any) => {
119
- return {
120
- realtimePrice: deriveValue(x.price.price, x.price.expo),
121
- confInterval: deriveValue(x.price.conf, x.price.expo) * 2.12,
122
- emaPrice: deriveValue(x.ema_price.price, x.ema_price.expo),
123
- emaConfInterval:
124
- deriveValue(x.ema_price.conf, x.ema_price.expo) * 2.12,
125
- };
126
- });
127
-
128
- return prices;
129
- },
130
- 5,
131
- 250
132
- );
130
+ const json = await resp.json();
131
+ const parsedPrices = json.parsed.map((x: any) => {
132
+ return {
133
+ realtimePrice: deriveValue(x.price.price, x.price.expo),
134
+ confInterval: deriveValue(x.price.conf, x.price.expo) * 2.12,
135
+ emaPrice: deriveValue(x.ema_price.price, x.ema_price.expo),
136
+ emaConfInterval:
137
+ deriveValue(x.ema_price.conf, x.ema_price.expo) * 2.12,
138
+ };
139
+ });
140
+
141
+ return parsedPrices;
142
+ },
143
+ 5,
144
+ 250
145
+ );
146
+ } catch (error: any) {
147
+ consoleLog(
148
+ `Failed to fetch Pyth prices; using Jupiter prices: ${error?.message ?? error}`
149
+ );
150
+ return await getJupTokenPrices(mints);
151
+ }
133
152
 
134
153
  return createRecord(
135
154
  mints.map((x) => x.toString()),
@@ -0,0 +1,64 @@
1
+ import { expect } from "chai";
2
+ import { describe, it } from "mocha";
3
+ import { PublicKey } from "@solana/web3.js";
4
+ import { getPythPrices, USDC } from "../../src";
5
+
6
+ describe("Pyth price fetching", () => {
7
+ const originalFetch = global.fetch;
8
+ const originalPythApiKey = process.env.PYTH_API_KEY;
9
+
10
+ afterEach(() => {
11
+ global.fetch = originalFetch;
12
+ if (originalPythApiKey === undefined) {
13
+ delete process.env.PYTH_API_KEY;
14
+ } else {
15
+ process.env.PYTH_API_KEY = originalPythApiKey;
16
+ }
17
+ });
18
+
19
+ it("uses Jupiter when a Pyth API key is not configured", async () => {
20
+ delete process.env.PYTH_API_KEY;
21
+ let requestUrl = "";
22
+ global.fetch = (async (input: string | URL | Request) => {
23
+ requestUrl = input.toString();
24
+ return {
25
+ json: async () => ({ [USDC]: { usdPrice: 1 } }),
26
+ } as Response;
27
+ }) as typeof fetch;
28
+
29
+ const prices = await getPythPrices([new PublicKey(USDC)]);
30
+
31
+ expect(requestUrl).to.contain("lite-api.jup.ag/price/v3");
32
+ expect(prices[USDC].realtimePrice).to.equal(1);
33
+ });
34
+
35
+ it("uses authenticated upgraded Hermes when a Pyth API key is configured", async () => {
36
+ process.env.PYTH_API_KEY = "test-api-key";
37
+ let requestUrl = "";
38
+ let authorization = "";
39
+ global.fetch = (async (
40
+ input: string | URL | Request,
41
+ init?: RequestInit
42
+ ) => {
43
+ requestUrl = input.toString();
44
+ authorization = (init?.headers as Record<string, string>).Authorization;
45
+ return {
46
+ ok: true,
47
+ json: async () => ({
48
+ parsed: [
49
+ {
50
+ price: { price: "100000000", conf: "1000", expo: -8 },
51
+ ema_price: { price: "100000000", conf: "1000", expo: -8 },
52
+ },
53
+ ],
54
+ }),
55
+ } as Response;
56
+ }) as typeof fetch;
57
+
58
+ const prices = await getPythPrices([new PublicKey(USDC)]);
59
+
60
+ expect(requestUrl).to.contain("pyth.dourolabs.app/hermes");
61
+ expect(authorization).to.equal("Bearer test-api-key");
62
+ expect(prices[USDC].realtimePrice).to.equal(1);
63
+ });
64
+ });