@evaafi/sdk 0.9.2 → 0.9.3
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/dist/api/feeds.d.ts +0 -1
- package/dist/api/prices.d.ts +0 -1
- package/dist/constants/general/index.js +1 -1
- package/dist/constants/general/mainnet.d.ts +3 -3
- package/dist/constants/general/mainnet.js +4 -4
- package/dist/constants/pools/mainnet.js +5 -5
- package/dist/contracts/PythMaster.d.ts +0 -1
- package/dist/contracts/PythOracle.d.ts +0 -1
- package/dist/oracles/Types.d.ts +0 -1
- package/dist/oracles/collectors/ClassicCollector.js +1 -1
- package/dist/oracles/collectors/PythCollector.js +18 -4
- package/dist/oracles/prices/PythPrices.d.ts +0 -1
- package/dist/oracles/sources/Backend.js +11 -8
- package/dist/oracles/sources/Icp.js +10 -6
- package/dist/oracles/utils.d.ts +0 -1
- package/dist/rewards/RewardMaster.d.ts +0 -1
- package/dist/rewards/RewardUser.d.ts +0 -1
- package/dist/types/Master.d.ts +0 -1
- package/dist/types/MasterRewards.d.ts +0 -1
- package/dist/types/UserRewards.d.ts +0 -1
- package/dist/utils/sha256BigInt.d.ts +0 -1
- package/dist/utils/utils.d.ts +1 -1
- package/dist/utils/utils.js +4 -2
- package/package.json +3 -2
- package/src/constants/general/index.ts +1 -1
- package/src/constants/general/mainnet.ts +4 -4
- package/src/constants/pools/mainnet.ts +5 -5
- package/src/oracles/collectors/ClassicCollector.ts +1 -1
- package/src/oracles/collectors/PythCollector.ts +22 -8
- package/src/oracles/sources/Backend.ts +12 -11
- package/src/oracles/sources/Icp.ts +10 -9
- package/src/utils/utils.ts +4 -2
package/dist/api/feeds.d.ts
CHANGED
package/dist/api/prices.d.ts
CHANGED
|
@@ -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.
|
|
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 =
|
|
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 =
|
|
8
|
+
export declare const EVAA_ALTS_MAINNET_VERSION = 4;
|
|
9
9
|
export declare const EVAA_STABLE_MAINNET: Address;
|
|
10
|
-
export declare const STABLE_VERSION =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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('
|
|
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:
|
|
46
|
-
[feeds_1.FEED_ID.USDT, { assetId: assets_1.ASSET_ID.USDT, feedId:
|
|
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.
|
|
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';
|
package/dist/oracles/Types.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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()
|
|
@@ -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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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;
|
package/dist/oracles/utils.d.ts
CHANGED
package/dist/types/Master.d.ts
CHANGED
package/dist/utils/utils.d.ts
CHANGED
|
@@ -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>(
|
|
10
|
+
export declare function proxyFetchRetries<T>(factory: () => Promise<T>, config?: FetchConfig): Promise<T>;
|
|
11
11
|
export declare function isValidSubaccountId(subaccountId: number): boolean;
|
package/dist/utils/utils.js
CHANGED
|
@@ -18,14 +18,16 @@ exports.DefaultFetchConfig = {
|
|
|
18
18
|
retries: 3,
|
|
19
19
|
timeout: 1000,
|
|
20
20
|
};
|
|
21
|
-
async function proxyFetchRetries(
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.9.3",
|
|
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
|
}
|
|
@@ -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 =
|
|
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 =
|
|
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 =
|
|
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('
|
|
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:
|
|
95
|
-
[FEED_ID.USDT, { assetId: ASSET_ID.USDT, feedId:
|
|
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.
|
|
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 {
|
|
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
|
|
279
|
-
this.#pythConfig.pythEndpoints.map((x) =>
|
|
280
|
-
new
|
|
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
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
package/src/utils/utils.ts
CHANGED
|
@@ -23,7 +23,7 @@ export const DefaultFetchConfig: FetchConfig = {
|
|
|
23
23
|
timeout: 1000,
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
export async function proxyFetchRetries<T>(
|
|
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
|
-
|
|
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
|
|