@suilend/sdk 11.0.4 → 11.1.0-next.g73e1e84

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.
package/lib/pyth.d.ts CHANGED
@@ -81,20 +81,46 @@ export declare const DEFAULT_HERMES_TIMEOUT_MS: number;
81
81
  * silently inheriting hermes-client's.
82
82
  */
83
83
  export declare const hermesConnectionConfig: (hermesConfig?: HermesClientConfig) => HermesClientConfig;
84
+ /**
85
+ * Public Hermes has required a Pyth API key since 2026-08-26 16:00 UTC, and
86
+ * nothing in this module can supply one — so an unconfigured caller gets a 401
87
+ * on every price read. Said once, at construction, because the alternative is
88
+ * discovering it per-request from inside whatever swallows the rejection.
89
+ *
90
+ * Two ways to be authenticated, and both are honoured: `accessToken`, which
91
+ * hermes-client turns into a Bearer header itself, or an `Authorization`
92
+ * header passed as a plain object. A header in either other `HeadersInit` form
93
+ * gets its own warning, because it is silently dropped in transit rather than
94
+ * rejected — the caller believes they configured auth and did not.
95
+ *
96
+ * Exported for tests only.
97
+ */
98
+ export declare const warnIfPublicHermesIsUnauthenticated: (endpoint: string, hermesConfig?: HermesClientConfig) => void;
99
+ /** Tests only: forget that the unauthenticated warning was already emitted. */
100
+ export declare const resetPublicHermesAuthWarning: () => void;
84
101
  /**
85
102
  * Pick the price source `initializeSuilend` will use.
86
103
  *
87
104
  * Lives here rather than in initialize.ts because it belongs beside
88
105
  * getWorkingPythEndpoint, the thing it decides whether to call.
89
106
  *
90
- * Skipping the probe for an injected source is deliberate, not an
91
- * optimisation. getWorkingPythEndpoint fetches hermes.pyth.network/live, which
92
- * answers for whether Hermes is up — not for whether it still serves the
93
- * caller's feeds, and not for whether the caller is even pointed at Hermes.
107
+ * The `/live` probe now runs ONLY when a `fallbackPythEndpoint` was supplied,
108
+ * which is the only case where its answer can change anything: with no
109
+ * fallback, `getWorkingPythEndpoint` returns the primary whether the probe
110
+ * passes or fails, so it was a request and up to 5s spent on a decision already
111
+ * made. Post-cutover it was worse than wasted — it reports Hermes healthy
112
+ * (`/live` needs no key) for an endpoint that 401s every real read, so it
113
+ * certified the broken path. Skipping it for an injected source is the same
114
+ * argument taken further: that probe answers for whether Hermes is up, not for
115
+ * whether it still serves the caller's feeds, nor whether the caller is even
116
+ * pointed at Hermes.
94
117
  *
95
118
  * `hermesConfig` exists so the commonest reason to reach for an injected source
96
- * — an access token, since public Hermes is expected to require one does not
97
- * cost the caller a hand-rolled `PriceFeedSource`. It configures only the
119
+ * — an API key, which public Hermes now requires does not cost the caller a
120
+ * hand-rolled `PriceFeedSource`. Pass it as `accessToken`, which hermes-client
121
+ * turns into a Bearer header on every request; a plain-object
122
+ * `headers: { Authorization: ... }` also works, but the other two `HeadersInit`
123
+ * forms do not survive its object-spread merge. It configures only the
98
124
  * connection built here; an injected source is returned untouched, config or
99
125
  * not, because there is nothing left to build.
100
126
  *
package/lib/pyth.js CHANGED
@@ -149,20 +149,97 @@ export const hermesConnectionConfig = (hermesConfig) => ({
149
149
  // quietly hand back hermes-client's 5s.
150
150
  timeout: hermesConfig?.timeout ?? DEFAULT_HERMES_TIMEOUT_MS,
151
151
  });
152
+ let _warnedPublicHermesIsUnauthenticated = false;
153
+ /**
154
+ * Whether `headers` will actually reach Hermes carrying an Authorization.
155
+ *
156
+ * `HeadersInit` allows three forms, but hermes-client merges with an OBJECT
157
+ * SPREAD (`{ ...authHeaders, ...this.headers }`), not `new Headers()`, so only
158
+ * a plain object survives:
159
+ *
160
+ * [["Authorization","k"]] -> { "0": ["Authorization","k"] } header named "0"
161
+ * new Headers({Authorization}) -> {} dropped
162
+ * { Authorization: "Bearer k" } -> { Authorization: "Bearer k" } sent
163
+ *
164
+ * So this deliberately does NOT use `new Headers(init).has(...)`, which answers
165
+ * true for all three and would go quiet for the two that cannot authenticate.
166
+ * Constructing one would also throw on an invalid header name, which is not a
167
+ * thing a diagnostic should do to its caller.
168
+ */
169
+ const headersCarryAuthorization = (headers) => {
170
+ if (headers === undefined)
171
+ return false;
172
+ if (Array.isArray(headers))
173
+ return false;
174
+ if (typeof Headers !== "undefined" && headers instanceof Headers)
175
+ return false;
176
+ return Object.keys(headers).some((name) => name.toLowerCase() === "authorization");
177
+ };
178
+ /**
179
+ * Public Hermes has required a Pyth API key since 2026-08-26 16:00 UTC, and
180
+ * nothing in this module can supply one — so an unconfigured caller gets a 401
181
+ * on every price read. Said once, at construction, because the alternative is
182
+ * discovering it per-request from inside whatever swallows the rejection.
183
+ *
184
+ * Two ways to be authenticated, and both are honoured: `accessToken`, which
185
+ * hermes-client turns into a Bearer header itself, or an `Authorization`
186
+ * header passed as a plain object. A header in either other `HeadersInit` form
187
+ * gets its own warning, because it is silently dropped in transit rather than
188
+ * rejected — the caller believes they configured auth and did not.
189
+ *
190
+ * Exported for tests only.
191
+ */
192
+ export const warnIfPublicHermesIsUnauthenticated = (endpoint, hermesConfig) => {
193
+ if (_warnedPublicHermesIsUnauthenticated)
194
+ return;
195
+ if (endpoint !== PRIMARY_PYTH_ENDPOINT)
196
+ return;
197
+ if (hermesConfig?.accessToken !== undefined)
198
+ return;
199
+ if (headersCarryAuthorization(hermesConfig?.headers))
200
+ return;
201
+ const headers = hermesConfig?.headers;
202
+ const droppedForm = headers !== undefined &&
203
+ (Array.isArray(headers) ||
204
+ (typeof Headers !== "undefined" && headers instanceof Headers));
205
+ _warnedPublicHermesIsUnauthenticated = true;
206
+ console.warn(droppedForm
207
+ ? `[resolvePythConnection] hermesConfig.headers was passed as ${Array.isArray(headers) ? "an array of tuples" : "a Headers instance"}, which hermes-client drops: it merges headers with an object spread, ` +
208
+ `so only a plain object is sent. ${PRIMARY_PYTH_ENDPOINT} has required ` +
209
+ `a Pyth API key since 2026-08-26 16:00 UTC, so every price read will ` +
210
+ `fail with 401. Pass hermesConfig.accessToken instead.`
211
+ : `[resolvePythConnection] ${PRIMARY_PYTH_ENDPOINT} has required a Pyth ` +
212
+ `API key since 2026-08-26 16:00 UTC and none was configured, so every ` +
213
+ `price read will fail with 401. Pass hermesConfig.accessToken, or ` +
214
+ `inject your own pythConnection.`);
215
+ };
216
+ /** Tests only: forget that the unauthenticated warning was already emitted. */
217
+ export const resetPublicHermesAuthWarning = () => {
218
+ _warnedPublicHermesIsUnauthenticated = false;
219
+ };
152
220
  /**
153
221
  * Pick the price source `initializeSuilend` will use.
154
222
  *
155
223
  * Lives here rather than in initialize.ts because it belongs beside
156
224
  * getWorkingPythEndpoint, the thing it decides whether to call.
157
225
  *
158
- * Skipping the probe for an injected source is deliberate, not an
159
- * optimisation. getWorkingPythEndpoint fetches hermes.pyth.network/live, which
160
- * answers for whether Hermes is up — not for whether it still serves the
161
- * caller's feeds, and not for whether the caller is even pointed at Hermes.
226
+ * The `/live` probe now runs ONLY when a `fallbackPythEndpoint` was supplied,
227
+ * which is the only case where its answer can change anything: with no
228
+ * fallback, `getWorkingPythEndpoint` returns the primary whether the probe
229
+ * passes or fails, so it was a request and up to 5s spent on a decision already
230
+ * made. Post-cutover it was worse than wasted — it reports Hermes healthy
231
+ * (`/live` needs no key) for an endpoint that 401s every real read, so it
232
+ * certified the broken path. Skipping it for an injected source is the same
233
+ * argument taken further: that probe answers for whether Hermes is up, not for
234
+ * whether it still serves the caller's feeds, nor whether the caller is even
235
+ * pointed at Hermes.
162
236
  *
163
237
  * `hermesConfig` exists so the commonest reason to reach for an injected source
164
- * — an access token, since public Hermes is expected to require one does not
165
- * cost the caller a hand-rolled `PriceFeedSource`. It configures only the
238
+ * — an API key, which public Hermes now requires does not cost the caller a
239
+ * hand-rolled `PriceFeedSource`. Pass it as `accessToken`, which hermes-client
240
+ * turns into a Bearer header on every request; a plain-object
241
+ * `headers: { Authorization: ... }` also works, but the other two `HeadersInit`
242
+ * forms do not survive its object-spread merge. It configures only the
166
243
  * connection built here; an injected source is returned untouched, config or
167
244
  * not, because there is nothing left to build.
168
245
  *
@@ -176,8 +253,10 @@ export const hermesConnectionConfig = (hermesConfig) => ({
176
253
  export const resolvePythConnection = async (pythConnectionOverride, fallbackPythEndpoint, hermesConfig) => {
177
254
  if (pythConnectionOverride)
178
255
  return pythConnectionOverride;
179
- // Get a working Pyth endpoint (try primary, fallback to fallbackPythEndpoint if provided)
180
- const pythEndpoint = await getWorkingPythEndpoint(fallbackPythEndpoint);
256
+ const pythEndpoint = fallbackPythEndpoint
257
+ ? await getWorkingPythEndpoint(fallbackPythEndpoint)
258
+ : PRIMARY_PYTH_ENDPOINT;
259
+ warnIfPublicHermesIsUnauthenticated(pythEndpoint, hermesConfig);
181
260
  return createHermesPriceFeedSource(new HermesClient(pythEndpoint, hermesConnectionConfig(hermesConfig)));
182
261
  };
183
262
  /**
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@suilend/sdk","version":"11.0.4","private":false,"description":"A TypeScript SDK for interacting with the Suilend program","author":"Suilend","license":"MIT","main":"./index.js","exports":{".":"./index.js","./client":"./client.js","./mmt":"./mmt.js","./strategies":"./strategies.js","./api/events":"./api/events.js","./api":"./api/index.js","./lib/constants":"./lib/constants.js","./lib":"./lib/index.js","./lib/initialize":"./lib/initialize.js","./lib/liquidityMining":"./lib/liquidityMining.js","./lib/proCompatible":"./lib/proCompatible.js","./lib/pyth":"./lib/pyth.js","./lib/strategyOwnerCap":"./lib/strategyOwnerCap.js","./lib/transactions":"./lib/transactions.js","./lib/types":"./lib/types.js","./margin":"./margin/index.js","./parsers/apiReserveAssetDataEvent":"./parsers/apiReserveAssetDataEvent.js","./parsers":"./parsers/index.js","./parsers/lendingMarket":"./parsers/lendingMarket.js","./parsers/obligation":"./parsers/obligation.js","./parsers/rateLimiter":"./parsers/rateLimiter.js","./parsers/reserve":"./parsers/reserve.js","./swap":"./swap/index.js","./swap/quote":"./swap/quote.js","./swap/transaction":"./swap/transaction.js","./utils/events":"./utils/events.js","./utils/feedId":"./utils/feedId.js","./utils":"./utils/index.js","./utils/obligation":"./utils/obligation.js","./utils/simulate":"./utils/simulate.js","./_generated/_framework/reified":"./_generated/_framework/reified.js","./_generated/_framework/util":"./_generated/_framework/util.js","./_generated/_framework/vector":"./_generated/_framework/vector.js","./_generated/suilend":"./_generated/suilend/index.js","./margin/margin/admin_cap":"./margin/margin/admin_cap.js","./margin/margin/market":"./margin/margin/market.js","./margin/margin/permissions":"./margin/margin/permissions.js","./margin/margin/position":"./margin/margin/position.js","./margin/margin/router":"./margin/margin/router.js","./margin/margin/version":"./margin/margin/version.js","./margin/utils":"./margin/utils/index.js","./_generated/suilend/cell/structs":"./_generated/suilend/cell/structs.js","./_generated/suilend/decimal/structs":"./_generated/suilend/decimal/structs.js","./_generated/suilend/lending-market/functions":"./_generated/suilend/lending-market/functions.js","./_generated/suilend/lending-market/structs":"./_generated/suilend/lending-market/structs.js","./_generated/suilend/lending-market-registry/functions":"./_generated/suilend/lending-market-registry/functions.js","./_generated/suilend/liquidity-mining/structs":"./_generated/suilend/liquidity-mining/structs.js","./_generated/suilend/obligation/structs":"./_generated/suilend/obligation/structs.js","./_generated/suilend/rate-limiter/functions":"./_generated/suilend/rate-limiter/functions.js","./_generated/suilend/rate-limiter/structs":"./_generated/suilend/rate-limiter/structs.js","./_generated/suilend/reserve/structs":"./_generated/suilend/reserve/structs.js","./_generated/suilend/reserve-config/functions":"./_generated/suilend/reserve-config/functions.js","./_generated/suilend/reserve-config/structs":"./_generated/suilend/reserve-config/structs.js","./_generated/_dependencies/source/0x1":"./_generated/_dependencies/source/0x1/index.js","./_generated/_dependencies/source/0x2":"./_generated/_dependencies/source/0x2/index.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/index.js","./margin/margin/deps/std/type_name":"./margin/margin/deps/std/type_name.js","./margin/margin/deps/sui/vec_set":"./margin/margin/deps/sui/vec_set.js","./margin/margin/deps/suilend/lending_market":"./margin/margin/deps/suilend/lending_market.js","./_generated/_dependencies/source/0x1/ascii/structs":"./_generated/_dependencies/source/0x1/ascii/structs.js","./_generated/_dependencies/source/0x1/option/structs":"./_generated/_dependencies/source/0x1/option/structs.js","./_generated/_dependencies/source/0x1/type-name/structs":"./_generated/_dependencies/source/0x1/type-name/structs.js","./_generated/_dependencies/source/0x2/bag/structs":"./_generated/_dependencies/source/0x2/bag/structs.js","./_generated/_dependencies/source/0x2/balance/structs":"./_generated/_dependencies/source/0x2/balance/structs.js","./_generated/_dependencies/source/0x2/object/structs":"./_generated/_dependencies/source/0x2/object/structs.js","./_generated/_dependencies/source/0x2/object-table/structs":"./_generated/_dependencies/source/0x2/object-table/structs.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.js"},"types":"./index.d.ts","scripts":{"build":"rm -rf ./dist && tsc && node ./fix-esm-imports.js","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","lint:ci":"yarn run typecheck","prettier":"prettier --write src/ tests/","release":"yarn run build && node ./release.js && cd ./dist && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/fireflyprotocol/lending-mono.git","directory":"ts/sdks/sdk"},"dependencies":{"@bluefin-exchange/bluefin7k-aggregator-sdk":"^7.5.0","@cetusprotocol/aggregator-sdk":"^1.5.7","@flowx-finance/sdk":"^2.1.0","@pythnetwork/hermes-client":"3.1.0","@pythnetwork/pyth-sui-js":"4.0.0","@suilend/springsui-sdk":"^4.0.0","bignumber.js":"^11.1.5","bn.js":"^5.2.2","crypto-js":"^4.2.0","lodash":"^4.17.21","p-limit":"7.3.1","uuid":"^14.0.1"},"devDependencies":{"@mysten/bcs":"^2.0.5","@mysten/sui":"2.23.2","@suilend/sui-core":"^1.0.0","@tsconfig/recommended":"^1.0.8","@types/bn.js":"^5.2.0","@types/lodash":"^4.17.25","@types/node":"^26.2.0","fast-check":"^4.9.0","prettier":"^3.3.3","ts-node":"^10.9.2","typescript":"^6.0.3","vitest":"4.1.10"},"peerDependencies":{"@mysten/bcs":"^2.0.5","@mysten/sui":">=2.22.1 <3","@suilend/sui-core":"^1.0.0"},"type":"module"}
1
+ {"name":"@suilend/sdk","version":"11.1.0-next.g73e1e84","private":false,"description":"A TypeScript SDK for interacting with the Suilend program","author":"Suilend","license":"MIT","main":"./index.js","exports":{".":"./index.js","./client":"./client.js","./mmt":"./mmt.js","./strategies":"./strategies.js","./api/events":"./api/events.js","./api":"./api/index.js","./lib/constants":"./lib/constants.js","./lib":"./lib/index.js","./lib/initialize":"./lib/initialize.js","./lib/liquidityMining":"./lib/liquidityMining.js","./lib/proCompatible":"./lib/proCompatible.js","./lib/pyth":"./lib/pyth.js","./lib/strategyOwnerCap":"./lib/strategyOwnerCap.js","./lib/transactions":"./lib/transactions.js","./lib/types":"./lib/types.js","./margin":"./margin/index.js","./parsers/apiReserveAssetDataEvent":"./parsers/apiReserveAssetDataEvent.js","./parsers":"./parsers/index.js","./parsers/lendingMarket":"./parsers/lendingMarket.js","./parsers/obligation":"./parsers/obligation.js","./parsers/rateLimiter":"./parsers/rateLimiter.js","./parsers/reserve":"./parsers/reserve.js","./swap":"./swap/index.js","./swap/quote":"./swap/quote.js","./swap/transaction":"./swap/transaction.js","./utils/events":"./utils/events.js","./utils/feedId":"./utils/feedId.js","./utils":"./utils/index.js","./utils/obligation":"./utils/obligation.js","./utils/simulate":"./utils/simulate.js","./_generated/_framework/reified":"./_generated/_framework/reified.js","./_generated/_framework/util":"./_generated/_framework/util.js","./_generated/_framework/vector":"./_generated/_framework/vector.js","./_generated/suilend":"./_generated/suilend/index.js","./margin/margin/admin_cap":"./margin/margin/admin_cap.js","./margin/margin/market":"./margin/margin/market.js","./margin/margin/permissions":"./margin/margin/permissions.js","./margin/margin/position":"./margin/margin/position.js","./margin/margin/router":"./margin/margin/router.js","./margin/margin/version":"./margin/margin/version.js","./margin/utils":"./margin/utils/index.js","./_generated/suilend/cell/structs":"./_generated/suilend/cell/structs.js","./_generated/suilend/decimal/structs":"./_generated/suilend/decimal/structs.js","./_generated/suilend/lending-market/functions":"./_generated/suilend/lending-market/functions.js","./_generated/suilend/lending-market/structs":"./_generated/suilend/lending-market/structs.js","./_generated/suilend/lending-market-registry/functions":"./_generated/suilend/lending-market-registry/functions.js","./_generated/suilend/liquidity-mining/structs":"./_generated/suilend/liquidity-mining/structs.js","./_generated/suilend/obligation/structs":"./_generated/suilend/obligation/structs.js","./_generated/suilend/rate-limiter/functions":"./_generated/suilend/rate-limiter/functions.js","./_generated/suilend/rate-limiter/structs":"./_generated/suilend/rate-limiter/structs.js","./_generated/suilend/reserve/structs":"./_generated/suilend/reserve/structs.js","./_generated/suilend/reserve-config/functions":"./_generated/suilend/reserve-config/functions.js","./_generated/suilend/reserve-config/structs":"./_generated/suilend/reserve-config/structs.js","./_generated/_dependencies/source/0x1":"./_generated/_dependencies/source/0x1/index.js","./_generated/_dependencies/source/0x2":"./_generated/_dependencies/source/0x2/index.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/index.js","./margin/margin/deps/std/type_name":"./margin/margin/deps/std/type_name.js","./margin/margin/deps/sui/vec_set":"./margin/margin/deps/sui/vec_set.js","./margin/margin/deps/suilend/lending_market":"./margin/margin/deps/suilend/lending_market.js","./_generated/_dependencies/source/0x1/ascii/structs":"./_generated/_dependencies/source/0x1/ascii/structs.js","./_generated/_dependencies/source/0x1/option/structs":"./_generated/_dependencies/source/0x1/option/structs.js","./_generated/_dependencies/source/0x1/type-name/structs":"./_generated/_dependencies/source/0x1/type-name/structs.js","./_generated/_dependencies/source/0x2/bag/structs":"./_generated/_dependencies/source/0x2/bag/structs.js","./_generated/_dependencies/source/0x2/balance/structs":"./_generated/_dependencies/source/0x2/balance/structs.js","./_generated/_dependencies/source/0x2/object/structs":"./_generated/_dependencies/source/0x2/object/structs.js","./_generated/_dependencies/source/0x2/object-table/structs":"./_generated/_dependencies/source/0x2/object-table/structs.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.js"},"types":"./index.d.ts","scripts":{"build":"rm -rf ./dist && tsc && node ./fix-esm-imports.js","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","lint:ci":"yarn run typecheck","prettier":"prettier --write src/ tests/","release":"yarn run build && node ./release.js && cd ./dist && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/fireflyprotocol/lending-mono.git","directory":"ts/sdks/sdk"},"dependencies":{"@bluefin-exchange/bluefin7k-aggregator-sdk":"^7.5.0","@cetusprotocol/aggregator-sdk":"^1.5.7","@flowx-finance/sdk":"^2.1.0","@pythnetwork/hermes-client":"3.1.0","@pythnetwork/pyth-sui-js":"4.0.0","@suilend/springsui-sdk":"^4.0.0","bignumber.js":"^11.1.5","bn.js":"^5.2.2","crypto-js":"^4.2.0","lodash":"^4.17.21","p-limit":"7.3.1","uuid":"^14.0.1"},"devDependencies":{"@mysten/bcs":"^2.0.5","@mysten/sui":"2.23.2","@suilend/sui-core":"^1.0.0","@tsconfig/recommended":"^1.0.8","@types/bn.js":"^5.2.0","@types/lodash":"^4.17.25","@types/node":"^26.2.0","fast-check":"^4.9.0","prettier":"^3.3.3","ts-node":"^10.9.2","typescript":"^6.0.3","vitest":"4.1.10"},"peerDependencies":{"@mysten/bcs":"^2.0.5","@mysten/sui":">=2.22.1 <3","@suilend/sui-core":"^1.0.0"},"type":"module"}