@evaafi/sdk 0.9.2 → 0.9.4

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,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { HexString } from '@pythnetwork/hermes-client';
4
3
  import { Dictionary } from '@ton/ton';
5
4
  import { Buffer } from 'buffer';
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { HexString } from '@pythnetwork/hermes-client';
4
3
  import { Cell } from '@ton/core';
5
4
  import { Buffer } from 'buffer';
@@ -79,7 +79,7 @@ exports.FEES = {
79
79
  // Supply
80
80
  SUPPLY: (0, core_1.toNano)('0.25'),
81
81
  // Supply & Withdraw
82
- SUPPLY_WITHDRAW: (0, core_1.toNano)('0.45'),
82
+ SUPPLY_WITHDRAW: (0, core_1.toNano)('0.48'),
83
83
  // Liquidation
84
84
  LIQUIDATION: (0, core_1.toNano)('0.8'),
85
85
  LIQUIDATION_JETTON: (0, core_1.toNano)('1'),
@@ -3,11 +3,11 @@ import { ExtendedEvaaOracle } from '../../types/Master';
3
3
  export declare const EVAA_MASTER_MAINNET: Address;
4
4
  export declare const MAINNET_VERSION = 9;
5
5
  export declare const EVAA_LP_MAINNET: Address;
6
- export declare const EVAA_LP_MAINNET_VERSION = 5;
6
+ export declare const EVAA_LP_MAINNET_VERSION = 6;
7
7
  export declare const EVAA_ALTS_MAINNET: Address;
8
- export declare const EVAA_ALTS_MAINNET_VERSION = 3;
8
+ export declare const EVAA_ALTS_MAINNET_VERSION = 4;
9
9
  export declare const EVAA_STABLE_MAINNET: Address;
10
- export declare const STABLE_VERSION = 1;
10
+ export declare const STABLE_VERSION = 3;
11
11
  export declare const EVAA_TOB_MAINNET: Address;
12
12
  export declare const EVAA_TOB_VERSION = 0;
13
13
  export declare const EVAA_PYTH_TOB_MAINNET: Address;
@@ -7,13 +7,13 @@ exports.EVAA_MASTER_MAINNET = core_1.Address.parse('EQC8rUZqR_pWV1BylWUlPNBzyiTY
7
7
  exports.MAINNET_VERSION = 9;
8
8
  /* LP POOL */
9
9
  exports.EVAA_LP_MAINNET = core_1.Address.parse('EQBIlZX2URWkXCSg3QF2MJZU-wC5XkBoLww-hdWk2G37Jc6N');
10
- exports.EVAA_LP_MAINNET_VERSION = 5;
10
+ exports.EVAA_LP_MAINNET_VERSION = 6;
11
11
  /* ALTS POOL */
12
12
  exports.EVAA_ALTS_MAINNET = core_1.Address.parse('EQANURVS3fhBO9bivig34iyJQi97FhMbpivo1aUEAS2GYSu-');
13
- exports.EVAA_ALTS_MAINNET_VERSION = 3;
13
+ exports.EVAA_ALTS_MAINNET_VERSION = 4;
14
14
  /* STABLE POOL */
15
15
  exports.EVAA_STABLE_MAINNET = core_1.Address.parse('EQCdIdXf1kA_2Hd9mbGzSFDEPA-Px-et8qTWHEXgRGo0K3zd');
16
- exports.STABLE_VERSION = 1;
16
+ exports.STABLE_VERSION = 3;
17
17
  /* EVAA V8 Trail of Bits Audited Pool */
18
18
  exports.EVAA_TOB_MAINNET = core_1.Address.parse('EQDrSGBBunrWNmIdHgdaA7Weu4yUr8Ckw3C9wNWobq7osn3H');
19
19
  exports.EVAA_TOB_VERSION = 0;
@@ -45,7 +45,7 @@ exports.ORACLES_MAINNET = [
45
45
  pubkey: Buffer.from('9cbf8374cf1f2cf17110134871d580198416e101683f4a61f54cf2a3e4e32070', 'hex'),
46
46
  },
47
47
  ];
48
- exports.PYTH_ORACLE_MAINNET = core_1.Address.parse('EQBU6k8HH6yX4Jf3d18swWbnYr31D3PJI7PgjXT-flsKHqql');
48
+ exports.PYTH_ORACLE_MAINNET = core_1.Address.parse('EQA5NPyjfZztDm8jcTBwTAU9NGsgJEkw19z61yecX0TlseSB');
49
49
  exports.ORACLES_LP = exports.ORACLES_MAINNET;
50
50
  exports.ORACLES_ALTS = exports.ORACLES_MAINNET;
51
51
  exports.LENDING_CODE = core_1.Cell.fromBoc(Buffer.from('b5ee9c72c1010e0100fd000d12182a555a6065717691969efd0114ff00f4a413f4bcf2c80b010202c8050202039f740403001ff2f8276a2687d2018fd201800f883b840051d38642c678b64e4400780e58fc10802faf07f80e59fa801e78b096664c02078067c07c100627a7978402014807060007a0ddb0c60201c709080013a0fd007a026900aa90400201200b0a0031b8e1002191960aa00b9e2ca007f4042796d225e8019203f6010201200d0c000bf7c147d2218400b9d10e86981fd201840b07f8138d809797976a2687d2029116382f970fd9178089910374daf81b619fd20182c7883b8701981684100627910eba56001797a6a6ba610fd8200e8768f76a9f6aa00cc2a32a8292878809bef2f1889f883bbcdeb86f01', 'hex'))[0];
@@ -42,17 +42,17 @@ exports.MAINNET_POOL_CONFIG = {
42
42
  poolAssetsConfig: exports.MAINNET_POOL_ASSETS_CONFIG,
43
43
  pythOracle: {
44
44
  feedsMap: new Map([
45
- [feeds_1.FEED_ID.TON, { assetId: assets_1.ASSET_ID.TON, feedId: '0x0' }],
46
- [feeds_1.FEED_ID.USDT, { assetId: assets_1.ASSET_ID.USDT, feedId: '0x0' }],
45
+ [feeds_1.FEED_ID.TON, { assetId: assets_1.ASSET_ID.TON, feedId: "0x0" }],
46
+ [feeds_1.FEED_ID.USDT, { assetId: assets_1.ASSET_ID.USDT, feedId: "0x0" }],
47
47
  [feeds_1.FEED_ID.tsTON, { assetId: assets_1.ASSET_ID.tsTON, feedId: feeds_1.FEED_ID.TON }],
48
- [feeds_1.FEED_ID.tsUSDe, { assetId: assets_1.ASSET_ID.tsUSDe, feedId: feeds_1.FEED_ID.USDT }],
48
+ [feeds_1.FEED_ID.stTON, { assetId: assets_1.ASSET_ID.stTON, feedId: feeds_1.FEED_ID.TON }],
49
+ [feeds_1.FEED_ID.USDe, { assetId: assets_1.ASSET_ID.USDe, feedId: "0x0" }],
50
+ [feeds_1.FEED_ID.tsUSDe, { assetId: assets_1.ASSET_ID.tsUSDe, feedId: feeds_1.FEED_ID.USDe }],
49
51
  ]),
50
52
  pythAddress: general_1.PYTH_ORACLE_MAINNET,
51
53
  allowedRefTokens: core_1.Dictionary.empty()
52
54
  .set(assets_1.ASSET_ID.jUSDT, assets_1.ASSET_ID.USDT)
53
55
  .set(assets_1.ASSET_ID.jUSDC, assets_1.ASSET_ID.USDT)
54
- .set(assets_1.ASSET_ID.USDe, assets_1.ASSET_ID.USDT)
55
- .set(assets_1.ASSET_ID.stTON, assets_1.ASSET_ID.tsTON),
56
56
  },
57
57
  }),
58
58
  poolAssetsConfig: exports.MAINNET_POOL_ASSETS_CONFIG,
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { HexString } from '@pythnetwork/hermes-client';
4
3
  import { Address, Cell, ContractProvider, Dictionary, Sender } from '@ton/core';
5
4
  import { PythOracleInfo } from '../api/parsers/PythOracleParser';
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { Address, Cell, Contract, ContractProvider } from '@ton/core';
4
3
  export declare class PythOracle implements Contract {
5
4
  readonly address: Address;
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import type { PriceUpdate } from '@pythnetwork/hermes-client';
4
3
  import type { Cell, Dictionary } from '@ton/core';
5
4
  /**
@@ -137,7 +137,7 @@ _ClassicCollector_prices = new WeakMap(), _ClassicCollector_poolAssetsConfig = n
137
137
  }, _ClassicCollector_collectPrices = async function _ClassicCollector_collectPrices(fetchConfig) {
138
138
  for (const priceSource of __classPrivateFieldGet(this, _ClassicCollector_priceSources, "f")) {
139
139
  try {
140
- __classPrivateFieldSet(this, _ClassicCollector_prices, await (0, utils_1.proxyFetchRetries)((0, utils_2.collectAndFilterPrices)(priceSource, __classPrivateFieldGet(this, _ClassicCollector_minimalOracles, "f"), fetchConfig), fetchConfig), "f");
140
+ __classPrivateFieldSet(this, _ClassicCollector_prices, await (0, utils_1.proxyFetchRetries)(() => (0, utils_2.collectAndFilterPrices)(priceSource, __classPrivateFieldGet(this, _ClassicCollector_minimalOracles, "f"), fetchConfig), fetchConfig), "f");
141
141
  return true;
142
142
  }
143
143
  catch (error) {
@@ -13,7 +13,6 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
13
13
  var _PythCollector_instances, _PythCollector_parsedFeedsMap, _PythCollector_pythConfig, _PythCollector_poolAssetsConfig, _PythCollector_allowedRefTokens, _PythCollector_assetToFeeds, _PythCollector_getPythFeedsUpdates, _PythCollector_fetchPythUpdatesWithRetry, _PythCollector_filterEmptyPrincipalsAndAssets;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.PythCollector = void 0;
16
- const hermes_client_1 = require("@pythnetwork/hermes-client");
17
16
  const core_1 = require("@ton/core");
18
17
  const math_1 = require("../../api/math");
19
18
  const prices_1 = require("../../api/prices");
@@ -238,12 +237,27 @@ _PythCollector_parsedFeedsMap = new WeakMap(), _PythCollector_pythConfig = new W
238
237
  * @returns binary - buffer of feeds update, parsed - json feeds data
239
238
  */
240
239
  async function _PythCollector_getPythFeedsUpdates(feedIds) {
241
- const latestPriceUpdates = await Promise.any(__classPrivateFieldGet(this, _PythCollector_pythConfig, "f").pythEndpoints.map((x) => new hermes_client_1.HermesClient(x).getLatestPriceUpdates(feedIds, { encoding: 'hex' })));
240
+ const pythUpdateResponse = await Promise.any(__classPrivateFieldGet(this, _PythCollector_pythConfig, "f").pythEndpoints.map((x) => {
241
+ const endpoint = new URL(`./v2/updates/price/latest?encoding=hex`, `${x}${x.endsWith('/') ? '' : '/'}`);
242
+ for (const feedId of feedIds) {
243
+ endpoint.searchParams.append('ids[]', feedId);
244
+ }
245
+ return fetch(endpoint.toString());
246
+ }));
247
+ if (!pythUpdateResponse.ok) {
248
+ const text = await pythUpdateResponse.text();
249
+ throw new Error(`Failed to fetch Pyth updates: ${text}`);
250
+ }
251
+ const latestPriceUpdates = (await pythUpdateResponse.json());
242
252
  const parsed = latestPriceUpdates['parsed'];
243
- const binary = Buffer.from(latestPriceUpdates.binary.data[0], 'hex');
253
+ const binaryHex = latestPriceUpdates?.binary?.data?.[0];
254
+ if (!binaryHex) {
255
+ throw new Error('Pyth update missing binary data');
256
+ }
257
+ const binary = Buffer.from(binaryHex, 'hex');
244
258
  return { binary, parsed };
245
259
  }, _PythCollector_fetchPythUpdatesWithRetry = async function _PythCollector_fetchPythUpdatesWithRetry(requiredFeeds, fetchConfig) {
246
- return (0, utils_1.proxyFetchRetries)(__classPrivateFieldGet(this, _PythCollector_instances, "m", _PythCollector_getPythFeedsUpdates).call(this, requiredFeeds), fetchConfig);
260
+ return (0, utils_1.proxyFetchRetries)(() => __classPrivateFieldGet(this, _PythCollector_instances, "m", _PythCollector_getPythFeedsUpdates).call(this, requiredFeeds), fetchConfig);
247
261
  }, _PythCollector_filterEmptyPrincipalsAndAssets = function _PythCollector_filterEmptyPrincipalsAndAssets(principals) {
248
262
  return principals
249
263
  .keys()
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { HexString } from '@pythnetwork/hermes-client';
4
3
  import { PoolAssetConfig } from '../../types/Master';
5
4
  import { AbstractPrices, PriceParameters } from './AbstractPrices';
@@ -14,19 +14,22 @@ class BackendPriceSource extends PriceSource_1.PriceSource {
14
14
  return data.map((outputData) => this.parsePrices(outputData));
15
15
  }
16
16
  async loadOracleData(fetchConfig = utils_1.DefaultFetchConfig) {
17
- const fetchPromise = fetch(`https://${this._endpoint}/api/prices`, {
18
- headers: { accept: 'application/json' },
19
- signal: AbortSignal.timeout(fetchConfig.timeout),
20
- }).then(async (response) => {
21
- const resp = await response.json();
22
- const data = resp;
17
+ return await (0, utils_1.proxyFetchRetries)(async () => {
18
+ const response = await fetch(`https://${this._endpoint}/api/prices`, {
19
+ headers: { accept: 'application/json' },
20
+ signal: AbortSignal.timeout(fetchConfig.timeout),
21
+ });
22
+ if (!response.ok) {
23
+ const body = await response.text().catch(() => '');
24
+ throw new Error(`HTTP error! status: ${response.status}${body ? `, body: ${body}` : ''}`);
25
+ }
26
+ const data = (await response.json());
23
27
  let outputData = [];
24
28
  for (const nft of this._nfts) {
25
29
  outputData.push({ oracleId: nft.id, data: data[nft.address] });
26
30
  }
27
31
  return outputData;
28
- });
29
- return await (0, utils_1.proxyFetchRetries)(fetchPromise, fetchConfig);
32
+ }, fetchConfig);
30
33
  }
31
34
  parsePrices(outputData) {
32
35
  try {
@@ -9,18 +9,22 @@ class IcpPriceSource extends _1.BackendPriceSource {
9
9
  this.priceSourceName = 'IcpPriceSource';
10
10
  }
11
11
  async loadOracleData(fetchConfig = utils_1.DefaultFetchConfig) {
12
- const fetchPromise = fetch(`https://${this._endpoint}/prices`, {
13
- headers: { accept: 'application/json' },
14
- signal: AbortSignal.timeout(fetchConfig.timeout)
15
- }).then(async (response) => {
12
+ return await (0, utils_1.proxyFetchRetries)(async () => {
13
+ const response = await fetch(`https://${this._endpoint}/prices`, {
14
+ headers: { accept: 'application/json' },
15
+ signal: AbortSignal.timeout(fetchConfig.timeout)
16
+ });
17
+ if (!response.ok) {
18
+ const body = await response.text().catch(() => '');
19
+ throw new Error(`HTTP error! status: ${response.status}${body ? `, body: ${body}` : ''}`);
20
+ }
16
21
  const data = (await response.json());
17
22
  let outputData = [];
18
23
  for (const nft of this._nfts) {
19
24
  outputData.push({ oracleId: nft.id, data: data[nft.address] });
20
25
  }
21
26
  return outputData;
22
- });
23
- return await (0, utils_1.proxyFetchRetries)(fetchPromise, fetchConfig);
27
+ }, fetchConfig);
24
28
  }
25
29
  }
26
30
  exports.IcpPriceSource = IcpPriceSource;
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { Cell, Dictionary, Slice } from '@ton/core';
4
3
  import { EvaaOracle, ExtendedEvaaOracle } from '../types/Master';
5
4
  import { FetchConfig } from '../utils/utils';
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { Address, Cell, Contract, ContractProvider, Sender, StateInit } from '@ton/ton';
4
3
  import { Maybe } from '@ton/ton/dist/utils/maybe';
5
4
  import { EvaaRewardsConfig } from '../types/MasterRewards';
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { Address, Cell, Contract, ContractProvider, Sender, StateInit } from '@ton/ton';
4
3
  import { Maybe } from '@ton/ton/dist/utils/maybe';
5
4
  import { EvaaUserRewardsConfig } from '../types/UserRewards';
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { Address, Cell, Dictionary } from '@ton/core';
4
3
  import { AbstractCollector } from '../oracles';
5
4
  export { FeedMapItem, parseFeedsMapDict } from '../api/feeds';
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { Address, Cell } from '@ton/ton';
4
3
  import { PoolAssetConfig } from './Master';
5
4
  export type EvaaRewardsConfig = {
@@ -1,5 +1,4 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  import { Address, Cell } from '@ton/ton';
4
3
  import { PoolAssetConfig } from './Master';
5
4
  export type EvaaUserRewardsConfig = {
@@ -1,4 +1,3 @@
1
1
  /// <reference types="node" />
2
- /// <reference types="node" />
3
2
  export declare function sha256Hash(input: string): bigint;
4
3
  export declare function bigIntToBuffer(value: bigint): Buffer;
@@ -7,5 +7,5 @@ export interface FetchConfig {
7
7
  timeout: number;
8
8
  }
9
9
  export declare const DefaultFetchConfig: FetchConfig;
10
- export declare function proxyFetchRetries<T>(fetch: Promise<T>, config?: FetchConfig): Promise<T>;
10
+ export declare function proxyFetchRetries<T>(factory: () => Promise<T>, config?: FetchConfig): Promise<T>;
11
11
  export declare function isValidSubaccountId(subaccountId: number): boolean;
@@ -18,14 +18,16 @@ exports.DefaultFetchConfig = {
18
18
  retries: 3,
19
19
  timeout: 1000,
20
20
  };
21
- async function proxyFetchRetries(fetch, config = exports.DefaultFetchConfig) {
21
+ async function proxyFetchRetries(factory, config = exports.DefaultFetchConfig) {
22
22
  let lastError = null;
23
23
  for (let attempt = 0; attempt <= config.retries; attempt++) {
24
24
  try {
25
25
  const timeoutPromise = new Promise((_, reject) => {
26
26
  setTimeout(() => reject(new Error('Request timeout')), config.timeout);
27
27
  });
28
- return await Promise.race([fetch, timeoutPromise]);
28
+ // Create a fresh promise per attempt
29
+ const result = await Promise.race([factory(), timeoutPromise]);
30
+ return result;
29
31
  }
30
32
  catch (error) {
31
33
  lastError = error instanceof Error ? error : new Error(String(error));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evaafi/sdk",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "The EVAA SDK is designed to easily integrate with the EVAA lending protocol on TON blockchain.",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -41,5 +41,6 @@
41
41
  "@pythnetwork/pyth-ton-js": "^0.1.2",
42
42
  "@ton/ton": "14.0.0",
43
43
  "dotenv": "16.4.5"
44
- }
44
+ },
45
+ "packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c"
45
46
  }
@@ -75,7 +75,7 @@ export const FEES = {
75
75
  SUPPLY: toNano('0.25'),
76
76
 
77
77
  // Supply & Withdraw
78
- SUPPLY_WITHDRAW: toNano('0.45'),
78
+ SUPPLY_WITHDRAW: toNano('0.48'),
79
79
 
80
80
  // Liquidation
81
81
  LIQUIDATION: toNano('0.8'),
@@ -7,15 +7,15 @@ export const MAINNET_VERSION = 9;
7
7
 
8
8
  /* LP POOL */
9
9
  export const EVAA_LP_MAINNET = Address.parse('EQBIlZX2URWkXCSg3QF2MJZU-wC5XkBoLww-hdWk2G37Jc6N');
10
- export const EVAA_LP_MAINNET_VERSION = 5;
10
+ export const EVAA_LP_MAINNET_VERSION = 6;
11
11
 
12
12
  /* ALTS POOL */
13
13
  export const EVAA_ALTS_MAINNET = Address.parse('EQANURVS3fhBO9bivig34iyJQi97FhMbpivo1aUEAS2GYSu-');
14
- export const EVAA_ALTS_MAINNET_VERSION = 3;
14
+ export const EVAA_ALTS_MAINNET_VERSION = 4;
15
15
 
16
16
  /* STABLE POOL */
17
17
  export const EVAA_STABLE_MAINNET = Address.parse('EQCdIdXf1kA_2Hd9mbGzSFDEPA-Px-et8qTWHEXgRGo0K3zd');
18
- export const STABLE_VERSION = 1;
18
+ export const STABLE_VERSION = 3;
19
19
 
20
20
  /* EVAA V8 Trail of Bits Audited Pool */
21
21
  export const EVAA_TOB_MAINNET = Address.parse('EQDrSGBBunrWNmIdHgdaA7Weu4yUr8Ckw3C9wNWobq7osn3H');
@@ -52,7 +52,7 @@ export const ORACLES_MAINNET: ExtendedEvaaOracle[] = [
52
52
  },
53
53
  ];
54
54
 
55
- export const PYTH_ORACLE_MAINNET: Address = Address.parse('EQBU6k8HH6yX4Jf3d18swWbnYr31D3PJI7PgjXT-flsKHqql');
55
+ export const PYTH_ORACLE_MAINNET: Address = Address.parse('EQA5NPyjfZztDm8jcTBwTAU9NGsgJEkw19z61yecX0TlseSB');
56
56
 
57
57
  export const ORACLES_LP: ExtendedEvaaOracle[] = ORACLES_MAINNET;
58
58
  export const ORACLES_ALTS: ExtendedEvaaOracle[] = ORACLES_MAINNET;
@@ -91,17 +91,17 @@ export const MAINNET_POOL_CONFIG: PoolConfig = {
91
91
  poolAssetsConfig: MAINNET_POOL_ASSETS_CONFIG,
92
92
  pythOracle: {
93
93
  feedsMap: new Map<HexString, FeedMapItem>([
94
- [FEED_ID.TON, { assetId: ASSET_ID.TON, feedId: '0x0' }],
95
- [FEED_ID.USDT, { assetId: ASSET_ID.USDT, feedId: '0x0' }],
94
+ [FEED_ID.TON, { assetId: ASSET_ID.TON, feedId: "0x0" }],
95
+ [FEED_ID.USDT, { assetId: ASSET_ID.USDT, feedId: "0x0" }],
96
96
  [FEED_ID.tsTON, { assetId: ASSET_ID.tsTON, feedId: FEED_ID.TON }],
97
- [FEED_ID.tsUSDe, { assetId: ASSET_ID.tsUSDe, feedId: FEED_ID.USDT }],
97
+ [FEED_ID.stTON, { assetId: ASSET_ID.stTON, feedId: FEED_ID.TON }],
98
+ [FEED_ID.USDe, { assetId: ASSET_ID.USDe, feedId: "0x0" }],
99
+ [FEED_ID.tsUSDe, { assetId: ASSET_ID.tsUSDe, feedId: FEED_ID.USDe }],
98
100
  ]),
99
101
  pythAddress: PYTH_ORACLE_MAINNET,
100
102
  allowedRefTokens: Dictionary.empty<bigint, bigint>()
101
103
  .set(ASSET_ID.jUSDT, ASSET_ID.USDT)
102
104
  .set(ASSET_ID.jUSDC, ASSET_ID.USDT)
103
- .set(ASSET_ID.USDe, ASSET_ID.USDT)
104
- .set(ASSET_ID.stTON, ASSET_ID.tsTON),
105
105
  },
106
106
  }),
107
107
  poolAssetsConfig: MAINNET_POOL_ASSETS_CONFIG,
@@ -184,7 +184,7 @@ export class ClassicCollector extends AbstractCollector {
184
184
  for (const priceSource of this.#priceSources) {
185
185
  try {
186
186
  this.#prices = await proxyFetchRetries(
187
- collectAndFilterPrices(priceSource, this.#minimalOracles, fetchConfig),
187
+ () => collectAndFilterPrices(priceSource, this.#minimalOracles, fetchConfig),
188
188
  fetchConfig,
189
189
  );
190
190
  return true;
@@ -1,4 +1,4 @@
1
- import { HermesClient, HexString, PriceUpdate } from '@pythnetwork/hermes-client';
1
+ import { HexString, PriceUpdate } from '@pythnetwork/hermes-client';
2
2
  import { Dictionary } from '@ton/core';
3
3
  import { checkNotInDebtAtAll } from '../../api/math';
4
4
  import { OracleConfig } from '../../api/parsers/PythOracleParser';
@@ -275,15 +275,29 @@ export class PythCollector extends AbstractCollector {
275
275
  * @returns binary - buffer of feeds update, parsed - json feeds data
276
276
  */
277
277
  async #getPythFeedsUpdates(feedIds: HexString[]): Promise<PythFeedUpdateType> {
278
- const latestPriceUpdates: PriceUpdate = await Promise.any(
279
- this.#pythConfig.pythEndpoints.map((x) =>
280
- new HermesClient(x).getLatestPriceUpdates(feedIds, { encoding: 'hex' }),
281
- ),
278
+ const pythUpdateResponse = await Promise.any(
279
+ this.#pythConfig.pythEndpoints.map((x) => {
280
+ const endpoint = new URL(`./v2/updates/price/latest?encoding=hex`, `${x}${x.endsWith('/') ? '' : '/'}`);
281
+
282
+ for (const feedId of feedIds) {
283
+ endpoint.searchParams.append('ids[]', feedId);
284
+ }
285
+
286
+ return fetch(endpoint.toString());
287
+ }),
282
288
  );
289
+ if (!pythUpdateResponse.ok) {
290
+ const text = await pythUpdateResponse.text();
291
+ throw new Error(`Failed to fetch Pyth updates: ${text}`);
292
+ }
283
293
 
294
+ const latestPriceUpdates = (await pythUpdateResponse.json()) as PriceUpdate;
284
295
  const parsed = latestPriceUpdates['parsed'];
285
- const binary = Buffer.from(latestPriceUpdates.binary.data[0], 'hex');
286
-
296
+ const binaryHex = latestPriceUpdates?.binary?.data?.[0];
297
+ if (!binaryHex) {
298
+ throw new Error('Pyth update missing binary data');
299
+ }
300
+ const binary = Buffer.from(binaryHex, 'hex');
287
301
  return { binary, parsed };
288
302
  }
289
303
 
@@ -291,7 +305,7 @@ export class PythCollector extends AbstractCollector {
291
305
  requiredFeeds: HexString[],
292
306
  fetchConfig?: FetchConfig,
293
307
  ): Promise<PythFeedUpdateType> {
294
- return proxyFetchRetries(this.#getPythFeedsUpdates(requiredFeeds), fetchConfig);
308
+ return proxyFetchRetries(() => this.#getPythFeedsUpdates(requiredFeeds), fetchConfig);
295
309
  }
296
310
 
297
311
  /**
@@ -12,22 +12,23 @@ export class BackendPriceSource extends PriceSource {
12
12
  }
13
13
 
14
14
  async loadOracleData(fetchConfig: FetchConfig = DefaultFetchConfig): Promise<OutputData[]> {
15
- const fetchPromise = fetch(`https://${this._endpoint}/api/prices`, {
16
- headers: { accept: 'application/json' },
17
- signal: AbortSignal.timeout(fetchConfig.timeout),
18
- }).then(async (response) => {
19
- const resp = await response.json();
20
- const data = resp as Record<string, string>;
21
- let outputData: OutputData[] = [];
15
+ return await proxyFetchRetries(async () => {
16
+ const response = await fetch(`https://${this._endpoint}/api/prices`, {
17
+ headers: { accept: 'application/json' },
18
+ signal: AbortSignal.timeout(fetchConfig.timeout),
19
+ });
20
+ if (!response.ok) {
21
+ const body = await response.text().catch(() => '');
22
+ throw new Error(`HTTP error! status: ${response.status}${body ? `, body: ${body}` : ''}`);
23
+ }
24
+ const data = (await response.json()) as Record<string, string>;
22
25
 
26
+ let outputData: OutputData[] = [];
23
27
  for (const nft of this._nfts) {
24
28
  outputData.push({ oracleId: nft.id, data: data[nft.address] });
25
29
  }
26
-
27
30
  return outputData;
28
- });
29
-
30
- return await proxyFetchRetries(fetchPromise, fetchConfig);
31
+ }, fetchConfig);
31
32
  }
32
33
 
33
34
  parsePrices(outputData: OutputData): RawPriceData {
@@ -5,22 +5,23 @@ export class IcpPriceSource extends BackendPriceSource {
5
5
  protected priceSourceName: string = 'IcpPriceSource';
6
6
 
7
7
  async loadOracleData(fetchConfig: FetchConfig = DefaultFetchConfig): Promise<OutputData[]> {
8
- const fetchPromise = fetch(`https://${this._endpoint}/prices`, {
9
- headers: { accept: 'application/json' },
10
- signal: AbortSignal.timeout(fetchConfig.timeout)
11
- }).then(async (response) => {
8
+ return await proxyFetchRetries(async () => {
9
+ const response = await fetch(`https://${this._endpoint}/prices`, {
10
+ headers: { accept: 'application/json' },
11
+ signal: AbortSignal.timeout(fetchConfig.timeout)
12
+ });
13
+ if (!response.ok) {
14
+ const body = await response.text().catch(() => '');
15
+ throw new Error(`HTTP error! status: ${response.status}${body ? `, body: ${body}` : ''}`);
16
+ }
12
17
  const data = (await response.json()) as Record<string, string>;
13
18
 
14
19
  let outputData: OutputData[] = [];
15
-
16
20
  for (const nft of this._nfts) {
17
21
  outputData.push({oracleId: nft.id, data: data[nft.address] })
18
22
  }
19
-
20
23
  return outputData;
21
- });
22
-
23
- return await proxyFetchRetries(fetchPromise, fetchConfig);
24
+ }, fetchConfig);
24
25
  }
25
26
  }
26
27
 
@@ -23,7 +23,7 @@ export const DefaultFetchConfig: FetchConfig = {
23
23
  timeout: 1000,
24
24
  };
25
25
 
26
- export async function proxyFetchRetries<T>(fetch: Promise<T>, config: FetchConfig = DefaultFetchConfig) {
26
+ export async function proxyFetchRetries<T>(factory: () => Promise<T>, config: FetchConfig = DefaultFetchConfig) {
27
27
  let lastError: Error | null = null;
28
28
  for (let attempt = 0; attempt <= config.retries; attempt++) {
29
29
  try {
@@ -31,7 +31,9 @@ export async function proxyFetchRetries<T>(fetch: Promise<T>, config: FetchConfi
31
31
  setTimeout(() => reject(new Error('Request timeout')), config.timeout);
32
32
  });
33
33
 
34
- return await Promise.race([fetch, timeoutPromise]);
34
+ // Create a fresh promise per attempt
35
+ const result = await Promise.race([factory(), timeoutPromise]);
36
+ return result;
35
37
  } catch (error) {
36
38
  lastError = error instanceof Error ? error : new Error(String(error));
37
39