@evaafi/sdk 0.9.0-a → 0.9.1

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.
Files changed (101) hide show
  1. package/dist/api/feeds.d.ts +15 -24
  2. package/dist/api/feeds.js +22 -49
  3. package/dist/api/math.d.ts +1 -1
  4. package/dist/api/math.js +55 -38
  5. package/dist/api/parser.d.ts +2 -2
  6. package/dist/api/parser.js +3 -3
  7. package/dist/api/parsers/PythOracleParser.d.ts +3 -3
  8. package/dist/api/parsers/PythOracleParser.js +2 -1
  9. package/dist/api/prices.js +2 -1
  10. package/dist/constants/general/index.d.ts +25 -11
  11. package/dist/constants/general/index.js +15 -1
  12. package/dist/constants/pools/mainnet.js +20 -18
  13. package/dist/constants/pools/testnet.js +14 -6
  14. package/dist/contracts/AbstractMaster.d.ts +239 -127
  15. package/dist/contracts/AbstractMaster.js +101 -16
  16. package/dist/contracts/ClassicMaster.d.ts +12 -12
  17. package/dist/contracts/PythMaster.d.ts +40 -24
  18. package/dist/contracts/PythMaster.js +61 -76
  19. package/dist/contracts/PythOracle.d.ts +16 -0
  20. package/dist/contracts/PythOracle.js +19 -0
  21. package/dist/contracts/index.d.ts +1 -0
  22. package/dist/contracts/index.js +1 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +1 -1
  25. package/dist/{prices → oracles}/Types.d.ts +0 -4
  26. package/dist/oracles/collectors/AbstractCollector.d.ts +10 -0
  27. package/dist/oracles/collectors/AbstractCollector.js +6 -0
  28. package/dist/oracles/collectors/ClassicCollector.d.ts +22 -0
  29. package/dist/oracles/collectors/ClassicCollector.js +192 -0
  30. package/dist/oracles/collectors/PythCollector.d.ts +27 -0
  31. package/dist/oracles/collectors/PythCollector.js +252 -0
  32. package/dist/oracles/collectors/index.d.ts +3 -0
  33. package/dist/oracles/collectors/index.js +19 -0
  34. package/dist/{prices → oracles}/index.d.ts +2 -3
  35. package/dist/{prices → oracles}/index.js +2 -3
  36. package/dist/oracles/prices/AbstractPrices.d.ts +33 -0
  37. package/dist/oracles/prices/AbstractPrices.js +40 -0
  38. package/dist/oracles/prices/ClassicPrices.d.ts +19 -0
  39. package/dist/oracles/prices/ClassicPrices.js +48 -0
  40. package/dist/oracles/prices/PythPrices.d.ts +18 -0
  41. package/dist/oracles/prices/PythPrices.js +32 -0
  42. package/dist/oracles/prices/index.d.ts +3 -0
  43. package/dist/oracles/prices/index.js +19 -0
  44. package/dist/{prices → oracles}/utils.d.ts +6 -1
  45. package/dist/{prices → oracles}/utils.js +10 -1
  46. package/dist/types/Master.d.ts +4 -5
  47. package/package.json +2 -3
  48. package/src/api/feeds.ts +24 -60
  49. package/src/api/math.ts +118 -90
  50. package/src/api/parser.ts +5 -5
  51. package/src/api/parsers/PythOracleParser.ts +6 -2
  52. package/src/api/prices.ts +2 -1
  53. package/src/constants/general/index.ts +16 -1
  54. package/src/constants/pools/mainnet.ts +20 -35
  55. package/src/constants/pools/testnet.ts +17 -8
  56. package/src/contracts/AbstractMaster.ts +272 -144
  57. package/src/contracts/ClassicMaster.ts +12 -12
  58. package/src/contracts/PythMaster.ts +130 -123
  59. package/src/contracts/PythOracle.ts +20 -0
  60. package/src/contracts/index.ts +2 -0
  61. package/src/index.ts +1 -1
  62. package/src/{prices → oracles}/Types.ts +0 -5
  63. package/src/oracles/collectors/AbstractCollector.ts +22 -0
  64. package/src/oracles/collectors/ClassicCollector.ts +263 -0
  65. package/src/oracles/collectors/PythCollector.ts +358 -0
  66. package/src/oracles/collectors/index.ts +3 -0
  67. package/src/{prices → oracles}/index.ts +2 -3
  68. package/src/oracles/prices/AbstractPrices.ts +59 -0
  69. package/src/oracles/prices/ClassicPrices.ts +52 -0
  70. package/src/oracles/prices/PythPrices.ts +40 -0
  71. package/src/oracles/prices/index.ts +3 -0
  72. package/src/{prices → oracles}/utils.ts +12 -1
  73. package/src/types/Master.ts +4 -6
  74. package/dist/prices/Oracle.interface.d.ts +0 -9
  75. package/dist/prices/Oracle.interface.js +0 -2
  76. package/dist/prices/Prices.d.ts +0 -11
  77. package/dist/prices/Prices.js +0 -53
  78. package/dist/prices/PricesCollector.d.ts +0 -22
  79. package/dist/prices/PricesCollector.js +0 -146
  80. package/dist/prices/PythCollector.d.ts +0 -22
  81. package/dist/prices/PythCollector.js +0 -217
  82. package/src/prices/Oracle.interface.ts +0 -18
  83. package/src/prices/Prices.ts +0 -45
  84. package/src/prices/PricesCollector.ts +0 -169
  85. package/src/prices/PythCollector.ts +0 -294
  86. /package/dist/{prices → oracles}/Types.js +0 -0
  87. /package/dist/{prices → oracles}/constants.d.ts +0 -0
  88. /package/dist/{prices → oracles}/constants.js +0 -0
  89. /package/dist/{prices → oracles}/sources/Backend.d.ts +0 -0
  90. /package/dist/{prices → oracles}/sources/Backend.js +0 -0
  91. /package/dist/{prices → oracles}/sources/Icp.d.ts +0 -0
  92. /package/dist/{prices → oracles}/sources/Icp.js +0 -0
  93. /package/dist/{prices → oracles}/sources/PriceSource.d.ts +0 -0
  94. /package/dist/{prices → oracles}/sources/PriceSource.js +0 -0
  95. /package/dist/{prices → oracles}/sources/index.d.ts +0 -0
  96. /package/dist/{prices → oracles}/sources/index.js +0 -0
  97. /package/src/{prices → oracles}/constants.ts +0 -0
  98. /package/src/{prices → oracles}/sources/Backend.ts +0 -0
  99. /package/src/{prices → oracles}/sources/Icp.ts +0 -0
  100. /package/src/{prices → oracles}/sources/PriceSource.ts +0 -0
  101. /package/src/{prices → oracles}/sources/index.ts +0 -0
@@ -1,22 +0,0 @@
1
- import { Dictionary } from "@ton/core";
2
- import { ExtendedEvaaOracle, PoolAssetConfig, PoolAssetsConfig } from "../types/Master";
3
- import { FetchConfig } from '../utils/utils';
4
- import { Oracle } from "./Oracle.interface";
5
- import { Prices } from "./Prices";
6
- import { PriceSource } from "./sources";
7
- import { PriceSourcesConfig } from "./Types";
8
- export type PricesCollectorConfig = {
9
- poolAssetsConfig: PoolAssetsConfig;
10
- minimalOracles: number;
11
- evaaOracles: ExtendedEvaaOracle[];
12
- sourcesConfig?: PriceSourcesConfig;
13
- additionalPriceSources?: PriceSource[];
14
- };
15
- export declare class PricesCollector implements Oracle {
16
- #private;
17
- constructor(config: PricesCollectorConfig);
18
- getPricesForLiquidate(realPrincipals: Dictionary<bigint, bigint>, fetchConfig?: FetchConfig): Promise<Prices>;
19
- getPricesForWithdraw(realPrincipals: Dictionary<bigint, bigint>, withdrawAsset: PoolAssetConfig, collateralToDebt?: boolean, fetchConfig?: FetchConfig): Promise<Prices>;
20
- getPricesForSupplyWithdraw(realPrincipals: Dictionary<bigint, bigint>, supplyAsset: PoolAssetConfig | undefined, withdrawAsset: PoolAssetConfig | undefined, collateralToDebt: boolean, fetchConfig?: FetchConfig): Promise<Prices>;
21
- getPrices(assets?: PoolAssetsConfig, fetchConfig?: FetchConfig): Promise<Prices>;
22
- }
@@ -1,146 +0,0 @@
1
- "use strict";
2
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
- if (kind === "m") throw new TypeError("Private method is not writable");
4
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
- };
8
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
- };
13
- var _PricesCollector_instances, _PricesCollector_prices, _PricesCollector_poolAssetsConfig, _PricesCollector_sourcesConfig, _PricesCollector_priceSources, _PricesCollector_minimalOracles, _PricesCollector_getPricesByAssetList, _PricesCollector_collectPrices, _PricesCollector_collectPricesWithValidation, _PricesCollector_filterPrices, _PricesCollector_filterEmptyPrincipalsAndAssets;
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.PricesCollector = void 0;
16
- const core_1 = require("@ton/core");
17
- const math_1 = require("../api/math");
18
- const utils_1 = require("../utils/utils");
19
- const Prices_1 = require("./Prices");
20
- const Types_1 = require("./Types");
21
- const utils_2 = require("./utils");
22
- class PricesCollector {
23
- constructor(config) {
24
- _PricesCollector_instances.add(this);
25
- _PricesCollector_prices.set(this, void 0);
26
- _PricesCollector_poolAssetsConfig.set(this, void 0);
27
- _PricesCollector_sourcesConfig.set(this, void 0);
28
- _PricesCollector_priceSources.set(this, void 0);
29
- _PricesCollector_minimalOracles.set(this, void 0);
30
- __classPrivateFieldSet(this, _PricesCollector_poolAssetsConfig, config.poolAssetsConfig, "f");
31
- __classPrivateFieldSet(this, _PricesCollector_sourcesConfig, config.sourcesConfig ?? Types_1.DefaultPriceSourcesConfig, "f");
32
- __classPrivateFieldSet(this, _PricesCollector_priceSources, (0, utils_2.generatePriceSources)(__classPrivateFieldGet(this, _PricesCollector_sourcesConfig, "f"), config.evaaOracles), "f");
33
- __classPrivateFieldSet(this, _PricesCollector_minimalOracles, config.minimalOracles, "f");
34
- if (config.additionalPriceSources) {
35
- __classPrivateFieldGet(this, _PricesCollector_priceSources, "f").push(...config.additionalPriceSources);
36
- }
37
- __classPrivateFieldSet(this, _PricesCollector_prices, [], "f");
38
- }
39
- async getPricesForLiquidate(realPrincipals, fetchConfig) {
40
- const assets = __classPrivateFieldGet(this, _PricesCollector_instances, "m", _PricesCollector_filterEmptyPrincipalsAndAssets).call(this, realPrincipals);
41
- if (assets.includes(undefined)) {
42
- throw new Error("User from another pool");
43
- }
44
- return await this.getPrices(assets.map(x => x), fetchConfig);
45
- }
46
- async getPricesForWithdraw(realPrincipals, withdrawAsset, collateralToDebt = false, fetchConfig) {
47
- let assets = __classPrivateFieldGet(this, _PricesCollector_instances, "m", _PricesCollector_filterEmptyPrincipalsAndAssets).call(this, realPrincipals);
48
- if ((0, math_1.checkNotInDebtAtAll)(realPrincipals) && (realPrincipals.get(withdrawAsset.assetId) ?? 0n) > 0n && !collateralToDebt) {
49
- return new Prices_1.Prices(core_1.Dictionary.empty(), core_1.Cell.EMPTY);
50
- }
51
- if (assets.includes(undefined)) {
52
- throw new Error("User from another pool");
53
- }
54
- if (!assets.includes(withdrawAsset)) {
55
- assets.push(withdrawAsset);
56
- }
57
- if (collateralToDebt && assets.length == 1) {
58
- throw new Error("Cannot debt only one supplied asset");
59
- }
60
- return await this.getPrices(assets.map(x => x), fetchConfig);
61
- }
62
- async getPricesForSupplyWithdraw(realPrincipals, supplyAsset, withdrawAsset, collateralToDebt, fetchConfig) {
63
- // Используем ту же логику, что и getPricesForWithdraw, но supplyAsset не используется
64
- let assets = __classPrivateFieldGet(this, _PricesCollector_instances, "m", _PricesCollector_filterEmptyPrincipalsAndAssets).call(this, realPrincipals);
65
- if ((0, math_1.checkNotInDebtAtAll)(realPrincipals) && withdrawAsset && (realPrincipals.get(withdrawAsset.assetId) ?? 0n) > 0n && !collateralToDebt) {
66
- return new Prices_1.Prices(core_1.Dictionary.empty(), core_1.Cell.EMPTY);
67
- }
68
- if (assets.includes(undefined)) {
69
- throw new Error("User from another pool");
70
- }
71
- if (withdrawAsset && !assets.includes(withdrawAsset)) {
72
- assets.push(withdrawAsset);
73
- }
74
- if (collateralToDebt && assets.length == 1) {
75
- throw new Error("Cannot debt only one supplied asset");
76
- }
77
- return await this.getPrices(assets.map(x => x), fetchConfig);
78
- }
79
- async getPrices(assets = __classPrivateFieldGet(this, _PricesCollector_poolAssetsConfig, "f"), fetchConfig) {
80
- if (assets.length == 0) {
81
- return new Prices_1.Prices(core_1.Dictionary.empty(), core_1.Cell.EMPTY);
82
- }
83
- await __classPrivateFieldGet(this, _PricesCollector_instances, "m", _PricesCollector_collectPricesWithValidation).call(this, fetchConfig);
84
- if (__classPrivateFieldGet(this, _PricesCollector_prices, "f").length < __classPrivateFieldGet(this, _PricesCollector_minimalOracles, "f")) {
85
- throw new Error(`Error per updating prices, valid ${__classPrivateFieldGet(this, _PricesCollector_prices, "f").length} of ${__classPrivateFieldGet(this, _PricesCollector_minimalOracles, "f")}`);
86
- }
87
- const prices = __classPrivateFieldGet(this, _PricesCollector_instances, "m", _PricesCollector_getPricesByAssetList).call(this, assets);
88
- return new Prices_1.Prices(prices.dict, prices.dataCell);
89
- }
90
- }
91
- exports.PricesCollector = PricesCollector;
92
- _PricesCollector_prices = new WeakMap(), _PricesCollector_poolAssetsConfig = new WeakMap(), _PricesCollector_sourcesConfig = new WeakMap(), _PricesCollector_priceSources = new WeakMap(), _PricesCollector_minimalOracles = new WeakMap(), _PricesCollector_instances = new WeakSet(), _PricesCollector_getPricesByAssetList = function _PricesCollector_getPricesByAssetList(assets) {
93
- let pricesFiltered = __classPrivateFieldGet(this, _PricesCollector_prices, "f");
94
- if (pricesFiltered.length < __classPrivateFieldGet(this, _PricesCollector_minimalOracles, "f")) {
95
- throw new Error("Not enough price data");
96
- }
97
- if (pricesFiltered.length > __classPrivateFieldGet(this, _PricesCollector_minimalOracles, "f")) {
98
- const sortedByTimestamp = pricesFiltered.slice().sort((a, b) => b.timestamp - a.timestamp);
99
- const newerPrices = sortedByTimestamp.slice(0, __classPrivateFieldGet(this, _PricesCollector_minimalOracles, "f"));
100
- pricesFiltered = newerPrices.sort((a, b) => a.oracleId - b.oracleId);
101
- }
102
- const medianData = assets.map((asset) => ({
103
- assetId: asset.assetId,
104
- medianPrice: (0, utils_2.getMedianPrice)(pricesFiltered, asset.assetId),
105
- }));
106
- const nonEmptymedianData = medianData.filter(x => x.medianPrice != null);
107
- const packedMedianData = (0, utils_2.packAssetsData)(nonEmptymedianData);
108
- const oraclesData = pricesFiltered.map((x) => ({
109
- oracle: { id: x.oracleId, pubkey: x.pubkey },
110
- data: { timestamp: x.timestamp, prices: x.dict },
111
- signature: x.signature,
112
- }));
113
- const packedOracleData = (0, utils_2.packOraclesData)(oraclesData, nonEmptymedianData.map(x => x.assetId));
114
- const dict = core_1.Dictionary.empty();
115
- for (const medianDataAsset of nonEmptymedianData) {
116
- dict.set(medianDataAsset.assetId, medianDataAsset.medianPrice);
117
- }
118
- return {
119
- dict: dict,
120
- dataCell: (0, utils_2.packPrices)(packedMedianData, packedOracleData)
121
- };
122
- }, _PricesCollector_collectPrices = async function _PricesCollector_collectPrices(fetchConfig) {
123
- for (const priceSource of __classPrivateFieldGet(this, _PricesCollector_priceSources, "f")) {
124
- try {
125
- __classPrivateFieldSet(this, _PricesCollector_prices, await (0, utils_1.proxyFetchRetries)((0, utils_2.collectAndFilterPrices)(priceSource, __classPrivateFieldGet(this, _PricesCollector_minimalOracles, "f"), fetchConfig), fetchConfig), "f");
126
- return true;
127
- }
128
- catch (error) {
129
- // Try next source
130
- continue;
131
- }
132
- }
133
- return false;
134
- }, _PricesCollector_collectPricesWithValidation = async function _PricesCollector_collectPricesWithValidation(fetchConfig) {
135
- if (!__classPrivateFieldGet(this, _PricesCollector_prices, "f") || __classPrivateFieldGet(this, _PricesCollector_instances, "m", _PricesCollector_filterPrices).call(this) < __classPrivateFieldGet(this, _PricesCollector_minimalOracles, "f")) {
136
- const success = await __classPrivateFieldGet(this, _PricesCollector_instances, "m", _PricesCollector_collectPrices).call(this, fetchConfig);
137
- if (!success || __classPrivateFieldGet(this, _PricesCollector_prices, "f").length < __classPrivateFieldGet(this, _PricesCollector_minimalOracles, "f")) {
138
- throw new Error(`Failed to collect sufficient prices: ${__classPrivateFieldGet(this, _PricesCollector_prices, "f")?.length || 0} of ${__classPrivateFieldGet(this, _PricesCollector_minimalOracles, "f")}`);
139
- }
140
- }
141
- }, _PricesCollector_filterPrices = function _PricesCollector_filterPrices() {
142
- __classPrivateFieldSet(this, _PricesCollector_prices, __classPrivateFieldGet(this, _PricesCollector_prices, "f").filter((0, utils_2.verifyPricesTimestamp)()), "f");
143
- return __classPrivateFieldGet(this, _PricesCollector_prices, "f").length;
144
- }, _PricesCollector_filterEmptyPrincipalsAndAssets = function _PricesCollector_filterEmptyPrincipalsAndAssets(principals) {
145
- return principals.keys().filter(x => principals.get(x) != 0n).map(x => __classPrivateFieldGet(this, _PricesCollector_poolAssetsConfig, "f").find(asset => asset.assetId == x));
146
- };
@@ -1,22 +0,0 @@
1
- import { HexString } from '@pythnetwork/hermes-client';
2
- import { Dictionary } from '@ton/core';
3
- import { OracleConfig } from '../api/parsers/PythOracleParser';
4
- import { PoolAssetConfig, PoolAssetsConfig } from '../types/Master';
5
- import { FetchConfig } from '../utils/utils';
6
- import { Oracle } from './Oracle.interface';
7
- import { Prices } from './Prices';
8
- import { PythPriceSourcesConfig } from './Types';
9
- export type PythCollectorConfig = {
10
- poolAssetsConfig: PoolAssetsConfig;
11
- pythOracle: OracleConfig;
12
- pythConfig: PythPriceSourcesConfig;
13
- };
14
- export declare class PythCollector implements Oracle {
15
- #private;
16
- constructor(config: PythCollectorConfig);
17
- getPricesForLiquidate(realPrincipals: Dictionary<bigint, bigint>, fetchConfig?: FetchConfig): Promise<Prices>;
18
- getPricesForSupplyWithdraw(realPrincipals: Dictionary<bigint, bigint>, supplyAsset: PoolAssetConfig | undefined, withdrawAsset: PoolAssetConfig | undefined, collateralToDebt: boolean, fetchConfig?: FetchConfig): Promise<Prices>;
19
- getPrices(assets?: PoolAssetsConfig, fetchConfig?: FetchConfig): Promise<Prices>;
20
- getPricesForWithdraw(realPrincipals: Dictionary<bigint, bigint>, withdrawAsset: PoolAssetConfig, collateralToDebt?: boolean, fetchConfig?: FetchConfig): Promise<Prices>;
21
- createRequiredFeedsList(evaaIds: bigint[]): HexString[];
22
- }
@@ -1,217 +0,0 @@
1
- "use strict";
2
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
- if (kind === "m") throw new TypeError("Private method is not writable");
4
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
- };
8
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
- };
13
- var _PythCollector_instances, _PythCollector_oracleInfo, _PythCollector_parsedFeedsMap, _PythCollector_pythConfig, _PythCollector_poolAssetsConfig, _PythCollector_pythToEvaaDirect, _PythCollector_pythToEvaaReferred, _PythCollector_evaaToPythDirect, _PythCollector_allowedRefEvaa, _PythCollector_getPythFeedsUpdates, _PythCollector_fetchPythUpdatesWithRetry, _PythCollector_filterEmptyPrincipalsAndAssets;
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.PythCollector = void 0;
16
- const hermes_client_1 = require("@pythnetwork/hermes-client");
17
- const core_1 = require("@ton/core");
18
- const math_1 = require("../api/math");
19
- const prices_1 = require("../api/prices");
20
- const constants_1 = require("../constants");
21
- const Master_1 = require("../types/Master");
22
- const utils_1 = require("../utils/utils");
23
- const Prices_1 = require("./Prices");
24
- const constants_2 = require("./constants");
25
- class PythCollector {
26
- constructor(config) {
27
- _PythCollector_instances.add(this);
28
- _PythCollector_oracleInfo.set(this, void 0);
29
- _PythCollector_parsedFeedsMap.set(this, void 0);
30
- _PythCollector_pythConfig.set(this, void 0);
31
- _PythCollector_poolAssetsConfig.set(this, void 0);
32
- _PythCollector_pythToEvaaDirect.set(this, new Map()); // pythId -> evaaId (native)
33
- _PythCollector_pythToEvaaReferred.set(this, new Map()); // pythId -> Set<evaaId>,
34
- _PythCollector_evaaToPythDirect.set(this, new Map()); // evaaId -> pythId (native)
35
- _PythCollector_allowedRefEvaa.set(this, new Map()); // evaaId -> baseEvaaId (allowedRefTokens)
36
- __classPrivateFieldSet(this, _PythCollector_oracleInfo, config.pythOracle, "f");
37
- __classPrivateFieldSet(this, _PythCollector_pythConfig, config.pythConfig, "f");
38
- __classPrivateFieldSet(this, _PythCollector_poolAssetsConfig, config.poolAssetsConfig, "f");
39
- __classPrivateFieldSet(this, _PythCollector_parsedFeedsMap, (0, Master_1.parseFeedsMapDict)(__classPrivateFieldGet(this, _PythCollector_oracleInfo, "f").feedsMap), "f");
40
- // 1) pythId -> evaaId, evaaId -> pythId
41
- for (const [pythId, feedInfo] of __classPrivateFieldGet(this, _PythCollector_parsedFeedsMap, "f").entries()) {
42
- __classPrivateFieldGet(this, _PythCollector_pythToEvaaDirect, "f").set(pythId, feedInfo.evaaId);
43
- __classPrivateFieldGet(this, _PythCollector_evaaToPythDirect, "f").set(feedInfo.evaaId, pythId);
44
- }
45
- // 2) pythId (native) -> Set<evaaId>
46
- for (const [pythId, feedInfo] of __classPrivateFieldGet(this, _PythCollector_parsedFeedsMap, "f").entries()) {
47
- const ref = feedInfo.referredPythFeed;
48
- if (ref && ref !== 0n) {
49
- if (!__classPrivateFieldGet(this, _PythCollector_pythToEvaaReferred, "f").has(ref))
50
- __classPrivateFieldGet(this, _PythCollector_pythToEvaaReferred, "f").set(ref, new Set());
51
- __classPrivateFieldGet(this, _PythCollector_pythToEvaaReferred, "f").get(ref).add(feedInfo.evaaId);
52
- }
53
- }
54
- // 3) evaaId -> baseEvaaId (allowedRefTokens)
55
- for (const evaaId of __classPrivateFieldGet(this, _PythCollector_oracleInfo, "f").allowedRefTokens.keys()) {
56
- const base = __classPrivateFieldGet(this, _PythCollector_oracleInfo, "f").allowedRefTokens.get(evaaId);
57
- __classPrivateFieldGet(this, _PythCollector_allowedRefEvaa, "f").set(evaaId, base);
58
- // If baseEvaaId have pythId. evaaId -> pyth(base)
59
- const basePyth = __classPrivateFieldGet(this, _PythCollector_evaaToPythDirect, "f").get(base);
60
- if (basePyth) {
61
- if (!__classPrivateFieldGet(this, _PythCollector_pythToEvaaReferred, "f").has(basePyth))
62
- __classPrivateFieldGet(this, _PythCollector_pythToEvaaReferred, "f").set(basePyth, new Set());
63
- __classPrivateFieldGet(this, _PythCollector_pythToEvaaReferred, "f").get(basePyth).add(evaaId);
64
- }
65
- }
66
- }
67
- async getPricesForLiquidate(realPrincipals, fetchConfig) {
68
- const assets = __classPrivateFieldGet(this, _PythCollector_instances, "m", _PythCollector_filterEmptyPrincipalsAndAssets).call(this, realPrincipals);
69
- if (assets.includes(undefined)) {
70
- throw new Error('User from another pool');
71
- }
72
- return await this.getPrices(assets.map((x) => x), fetchConfig);
73
- }
74
- async getPricesForSupplyWithdraw(realPrincipals, supplyAsset, withdrawAsset, collateralToDebt, fetchConfig) {
75
- let assets = __classPrivateFieldGet(this, _PythCollector_instances, "m", _PythCollector_filterEmptyPrincipalsAndAssets).call(this, realPrincipals);
76
- if ((0, math_1.checkNotInDebtAtAll)(realPrincipals) &&
77
- withdrawAsset &&
78
- (realPrincipals.get(withdrawAsset.assetId) ?? 0n) > 0n &&
79
- !collateralToDebt) {
80
- return new Prices_1.Prices(core_1.Dictionary.empty(), core_1.Cell.EMPTY, undefined, undefined);
81
- }
82
- if (assets.includes(undefined)) {
83
- throw new Error('User from another pool');
84
- }
85
- if (withdrawAsset && !assets.find((a) => a?.assetId === withdrawAsset.assetId)) {
86
- assets.push(withdrawAsset);
87
- }
88
- if (collateralToDebt && assets.length == 1) {
89
- throw new Error('Cannot debt only one supplied asset');
90
- }
91
- return await this.getPrices(assets.map((x) => x), fetchConfig);
92
- }
93
- async getPrices(assets = __classPrivateFieldGet(this, _PythCollector_poolAssetsConfig, "f"), fetchConfig) {
94
- // Declare variables at the beginning
95
- let minPublishTime;
96
- let maxPublishTime;
97
- if (assets.length === 0) {
98
- return new Prices_1.Prices(core_1.Dictionary.empty(), core_1.Cell.EMPTY, undefined, undefined);
99
- }
100
- const requiredFeeds = this.createRequiredFeedsList(assets.map((a) => a.assetId));
101
- const pythUpdates = await __classPrivateFieldGet(this, _PythCollector_instances, "m", _PythCollector_fetchPythUpdatesWithRetry).call(this, requiredFeeds, fetchConfig);
102
- // Calculate min and max publish times for validation
103
- if (pythUpdates.parsed && pythUpdates.parsed.length > 0) {
104
- const publishTimes = pythUpdates.parsed.map((u) => BigInt(u.price.publish_time));
105
- const tmin = publishTimes.reduce((a, b) => (a < b ? a : b));
106
- const tmax = publishTimes.reduce((a, b) => (a > b ? a : b));
107
- if (tmax - tmin > constants_2.TTL_ORACLE_DATA_SEC) {
108
- throw new Error(`Price feeds don't fit in a single 3-minute window. Time span: ${tmax - tmin} seconds (max allowed: ${constants_2.TTL_ORACLE_DATA_SEC})`);
109
- }
110
- // Set boundaries using "from oldest" approach: minPublishTime = tmin, maxPublishTime = tmin + 180
111
- minPublishTime = tmin;
112
- maxPublishTime = tmin + BigInt(constants_2.TTL_ORACLE_DATA_SEC);
113
- }
114
- const pricesDict = core_1.Dictionary.empty();
115
- const pythPriceUpdates = pythUpdates.parsed;
116
- if (pythPriceUpdates) {
117
- // Only set prices for requested assets, not all possible mapped assets
118
- const requestedAssetIds = new Set(assets.map((a) => a.assetId));
119
- for (const u of pythPriceUpdates) {
120
- const pythId = BigInt('0x' + u.id);
121
- const price = (BigInt(u.price.price) * BigInt(10 ** 9)) / BigInt(10 ** (u.price.expo * -1));
122
- // Set price for direct mapping if the evaaId is requested
123
- const directEvaa = __classPrivateFieldGet(this, _PythCollector_pythToEvaaDirect, "f").get(pythId);
124
- if (directEvaa && requestedAssetIds.has(directEvaa)) {
125
- pricesDict.set(directEvaa, price);
126
- }
127
- // Set price for referred assets only if they are requested
128
- const referredSet = __classPrivateFieldGet(this, _PythCollector_pythToEvaaReferred, "f").get(pythId);
129
- if (referredSet && referredSet.size) {
130
- for (const evaaId of referredSet) {
131
- if (requestedAssetIds.has(evaaId)) {
132
- pricesDict.set(evaaId, price);
133
- }
134
- }
135
- }
136
- }
137
- // TODO: fix it
138
- if (pricesDict.get(constants_1.TSTON_MAINNET.assetId) && pricesDict.get(constants_1.TON_MAINNET.assetId)) {
139
- pricesDict.set(constants_1.TSTON_MAINNET.assetId, (pricesDict.get(constants_1.TSTON_MAINNET.assetId) * pricesDict.get(constants_1.TON_MAINNET.assetId)) / BigInt(10 ** 9));
140
- }
141
- // TODO: fix it
142
- if (pricesDict.get(constants_1.STTON_MAINNET.assetId) && pricesDict.get(constants_1.TON_MAINNET.assetId)) {
143
- pricesDict.set(constants_1.STTON_MAINNET.assetId, (pricesDict.get(constants_1.STTON_MAINNET.assetId) * pricesDict.get(constants_1.TON_MAINNET.assetId)) / BigInt(10 ** 9));
144
- }
145
- // TODO: fix it
146
- if (pricesDict.get(constants_1.TSUSDE_MAINNET.assetId) && pricesDict.get(constants_1.USDE_MAINNET.assetId)) {
147
- pricesDict.set(constants_1.TSUSDE_MAINNET.assetId, (pricesDict.get(constants_1.TSUSDE_MAINNET.assetId) * pricesDict.get(constants_1.USDE_MAINNET.assetId)) / BigInt(10 ** 9));
148
- }
149
- // Check that all requested assets have prices
150
- const missing = assets.map((a) => a.assetId).filter((id) => pricesDict.get(id) === undefined);
151
- if (missing.length) {
152
- throw new Error(`Missing prices for ${missing.length} asset(s): ${missing.map((x) => x.toString()).join(', ')}`);
153
- }
154
- const dataCell = (0, prices_1.packPythUpdatesData)(pythUpdates.binary);
155
- return new Prices_1.Prices(pricesDict, dataCell, minPublishTime, maxPublishTime);
156
- }
157
- return new Prices_1.Prices(core_1.Dictionary.empty(), core_1.Cell.EMPTY, minPublishTime, maxPublishTime);
158
- }
159
- async getPricesForWithdraw(realPrincipals, withdrawAsset, collateralToDebt = false, fetchConfig) {
160
- let assets = __classPrivateFieldGet(this, _PythCollector_instances, "m", _PythCollector_filterEmptyPrincipalsAndAssets).call(this, realPrincipals);
161
- if ((0, math_1.checkNotInDebtAtAll)(realPrincipals) &&
162
- (realPrincipals.get(withdrawAsset.assetId) ?? 0n) > 0n &&
163
- !collateralToDebt) {
164
- return new Prices_1.Prices(core_1.Dictionary.empty(), core_1.Cell.EMPTY);
165
- }
166
- if (assets.includes(undefined)) {
167
- throw new Error('User from another pool');
168
- }
169
- if (!assets.includes(withdrawAsset)) {
170
- assets.push(withdrawAsset);
171
- }
172
- if (collateralToDebt && assets.length == 1) {
173
- throw new Error('Cannot debt only one supplied asset');
174
- }
175
- return await this.getPrices(assets.map((x) => x), fetchConfig);
176
- }
177
- createRequiredFeedsList(evaaIds) {
178
- const requiredFeeds = new Set();
179
- for (const evaaId of evaaIds) {
180
- let pythId = __classPrivateFieldGet(this, _PythCollector_evaaToPythDirect, "f").get(evaaId);
181
- // If evaaId no have native feed — try by allowedRefTokens (evAA->baseEvAA->pyth)
182
- if (!pythId) {
183
- const baseEvaa = __classPrivateFieldGet(this, _PythCollector_allowedRefEvaa, "f").get(evaaId);
184
- if (baseEvaa)
185
- pythId = __classPrivateFieldGet(this, _PythCollector_evaaToPythDirect, "f").get(baseEvaa) ?? null;
186
- }
187
- if (pythId) {
188
- requiredFeeds.add(pythId);
189
- const feedInfo = __classPrivateFieldGet(this, _PythCollector_parsedFeedsMap, "f").get(pythId);
190
- if (feedInfo?.referredPythFeed && feedInfo.referredPythFeed !== 0n) {
191
- requiredFeeds.add(feedInfo.referredPythFeed);
192
- }
193
- }
194
- }
195
- return Array.from(requiredFeeds).map((id) => '0x' + id.toString(16));
196
- }
197
- }
198
- exports.PythCollector = PythCollector;
199
- _PythCollector_oracleInfo = new WeakMap(), _PythCollector_parsedFeedsMap = new WeakMap(), _PythCollector_pythConfig = new WeakMap(), _PythCollector_poolAssetsConfig = new WeakMap(), _PythCollector_pythToEvaaDirect = new WeakMap(), _PythCollector_pythToEvaaReferred = new WeakMap(), _PythCollector_evaaToPythDirect = new WeakMap(), _PythCollector_allowedRefEvaa = new WeakMap(), _PythCollector_instances = new WeakSet(), _PythCollector_getPythFeedsUpdates =
200
- /**
201
- * Updates feeds data from specified endpoint
202
- * @param feedIds list of pyth feed ids to fetch
203
- * @returns binary - buffer of feeds update, parsed - json feeds data
204
- */
205
- async function _PythCollector_getPythFeedsUpdates(feedIds) {
206
- const latestPriceUpdates = await Promise.any(__classPrivateFieldGet(this, _PythCollector_pythConfig, "f").pythEndpoints.map((x) => new hermes_client_1.HermesClient(x).getLatestPriceUpdates(feedIds, { encoding: 'hex' })));
207
- const parsed = latestPriceUpdates['parsed'];
208
- const binary = Buffer.from(latestPriceUpdates.binary.data[0], 'hex');
209
- return { binary, parsed };
210
- }, _PythCollector_fetchPythUpdatesWithRetry = async function _PythCollector_fetchPythUpdatesWithRetry(requiredFeeds, fetchConfig) {
211
- return (0, utils_1.proxyFetchRetries)(__classPrivateFieldGet(this, _PythCollector_instances, "m", _PythCollector_getPythFeedsUpdates).call(this, requiredFeeds), fetchConfig);
212
- }, _PythCollector_filterEmptyPrincipalsAndAssets = function _PythCollector_filterEmptyPrincipalsAndAssets(principals) {
213
- return principals
214
- .keys()
215
- .filter((x) => principals.get(x) != 0n)
216
- .map((x) => __classPrivateFieldGet(this, _PythCollector_poolAssetsConfig, "f").find((asset) => asset.assetId == x));
217
- };
@@ -1,18 +0,0 @@
1
- import { Dictionary } from '@ton/core';
2
- import { Prices } from '.';
3
- import { PoolAssetConfig, PoolAssetsConfig } from '../types/Master';
4
- import { FetchConfig } from '../utils/utils';
5
-
6
- export interface Oracle {
7
- getPricesForLiquidate(realPrincipals: Dictionary<bigint, bigint>, fetchConfig?: FetchConfig): Promise<Prices>;
8
-
9
- getPricesForSupplyWithdraw(
10
- realPrincipals: Dictionary<bigint, bigint>,
11
- supplyAsset: PoolAssetConfig | undefined,
12
- withdrawAsset: PoolAssetConfig | undefined,
13
- collateralToDebt: boolean,
14
- fetchConfig?: FetchConfig,
15
- ): Promise<Prices>;
16
-
17
- getPrices(assets: PoolAssetsConfig, fetchConfig?: FetchConfig): Promise<Prices>;
18
- }
@@ -1,45 +0,0 @@
1
- import { Cell, Dictionary } from '@ton/core';
2
- import { PoolAssetConfig } from '../types/Master';
3
-
4
- export class Prices {
5
- #dict: Dictionary<bigint, bigint>;
6
- #dataCell: Cell;
7
- #minPublishTime?: bigint;
8
- #maxPublishTime?: bigint;
9
-
10
- constructor(dict: Dictionary<bigint, bigint>, dataCell: Cell, minPublishTime?: bigint, maxPublishTime?: bigint) {
11
- this.#dict = dict;
12
- this.#dataCell = dataCell;
13
- this.#minPublishTime = minPublishTime;
14
- this.#maxPublishTime = maxPublishTime;
15
- }
16
-
17
- get dict() {
18
- const dict = Dictionary.empty<bigint, bigint>();
19
- for (const [key, value] of this.#dict) {
20
- dict.set(key, value);
21
- }
22
- return dict;
23
- }
24
-
25
- get dataCell() {
26
- return new Cell(this.#dataCell);
27
- }
28
-
29
- get minPublishTime(): bigint | undefined {
30
- return this.#minPublishTime;
31
- }
32
-
33
- get maxPublishTime(): bigint | undefined {
34
- return this.#maxPublishTime;
35
- }
36
-
37
- getAssetPrice<T extends bigint | PoolAssetConfig>(asset: T): bigint | undefined {
38
- const assetId = this.#extractAssetId(asset);
39
- return this.#dict.get(assetId);
40
- }
41
-
42
- #extractAssetId(asset: bigint | PoolAssetConfig): bigint {
43
- return typeof asset === 'bigint' ? asset : asset.assetId;
44
- }
45
- }
@@ -1,169 +0,0 @@
1
- import { Cell, Dictionary } from "@ton/core"
2
- import { checkNotInDebtAtAll } from "../api/math"
3
- import { ExtendedEvaaOracle, PoolAssetConfig, PoolAssetsConfig } from "../types/Master"
4
- import { FetchConfig, proxyFetchRetries } from '../utils/utils'
5
- import { Oracle } from "./Oracle.interface"
6
- import { Prices } from "./Prices"
7
- import { PriceSource } from "./sources"
8
- import { DefaultPriceSourcesConfig, PriceSourcesConfig, RawPriceData } from "./Types"
9
- import { collectAndFilterPrices, generatePriceSources, getMedianPrice, packAssetsData, packOraclesData, packPrices, verifyPricesTimestamp } from "./utils"
10
-
11
-
12
- export type PricesCollectorConfig = {
13
- poolAssetsConfig: PoolAssetsConfig;
14
- minimalOracles: number;
15
- evaaOracles: ExtendedEvaaOracle[];
16
- sourcesConfig?: PriceSourcesConfig;
17
- additionalPriceSources?: PriceSource[];
18
- };
19
-
20
- export class PricesCollector implements Oracle {
21
- #prices: RawPriceData[];
22
- #poolAssetsConfig: PoolAssetsConfig;
23
- #sourcesConfig: PriceSourcesConfig;
24
- #priceSources: PriceSource[];
25
- #minimalOracles: number;
26
-
27
- constructor(config: PricesCollectorConfig) {
28
- this.#poolAssetsConfig = config.poolAssetsConfig;
29
- this.#sourcesConfig = config.sourcesConfig ?? DefaultPriceSourcesConfig;
30
- this.#priceSources = generatePriceSources(this.#sourcesConfig, config.evaaOracles);
31
- this.#minimalOracles = config.minimalOracles;
32
- if (config.additionalPriceSources) {
33
- this.#priceSources.push(...config.additionalPriceSources);
34
- }
35
- this.#prices = [];
36
- }
37
-
38
- async getPricesForLiquidate(realPrincipals: Dictionary<bigint, bigint>, fetchConfig?: FetchConfig): Promise<Prices> {
39
- const assets = this.#filterEmptyPrincipalsAndAssets(realPrincipals);
40
- if (assets.includes(undefined)) {
41
- throw new Error("User from another pool");
42
- }
43
- return await this.getPrices(assets.map(x => x!), fetchConfig);
44
- }
45
-
46
- async getPricesForWithdraw(realPrincipals: Dictionary<bigint, bigint>, withdrawAsset: PoolAssetConfig, collateralToDebt = false, fetchConfig?: FetchConfig): Promise<Prices> {
47
- let assets = this.#filterEmptyPrincipalsAndAssets(realPrincipals);
48
- if (checkNotInDebtAtAll(realPrincipals) && (realPrincipals.get(withdrawAsset.assetId) ?? 0n) > 0n && !collateralToDebt) {
49
- return new Prices(Dictionary.empty<bigint, bigint>(), Cell.EMPTY);
50
- }
51
- if (assets.includes(undefined)) {
52
- throw new Error("User from another pool");
53
- }
54
- if (!assets.includes(withdrawAsset)) {
55
- assets.push(withdrawAsset);
56
- }
57
- if (collateralToDebt && assets.length == 1) {
58
- throw new Error("Cannot debt only one supplied asset");
59
- }
60
- return await this.getPrices(assets.map(x => x!), fetchConfig);
61
- }
62
-
63
- async getPricesForSupplyWithdraw(
64
- realPrincipals: Dictionary<bigint, bigint>,
65
- supplyAsset: PoolAssetConfig | undefined,
66
- withdrawAsset: PoolAssetConfig | undefined,
67
- collateralToDebt: boolean,
68
- fetchConfig?: FetchConfig,
69
- ): Promise<Prices> {
70
- // Используем ту же логику, что и getPricesForWithdraw, но supplyAsset не используется
71
- let assets = this.#filterEmptyPrincipalsAndAssets(realPrincipals);
72
- if (checkNotInDebtAtAll(realPrincipals) && withdrawAsset && (realPrincipals.get(withdrawAsset.assetId) ?? 0n) > 0n && !collateralToDebt) {
73
- return new Prices(Dictionary.empty<bigint, bigint>(), Cell.EMPTY);
74
- }
75
- if (assets.includes(undefined)) {
76
- throw new Error("User from another pool");
77
- }
78
- if (withdrawAsset && !assets.includes(withdrawAsset)) {
79
- assets.push(withdrawAsset);
80
- }
81
- if (collateralToDebt && assets.length == 1) {
82
- throw new Error("Cannot debt only one supplied asset");
83
- }
84
- return await this.getPrices(assets.map(x => x!), fetchConfig);
85
- }
86
-
87
- async getPrices(assets: PoolAssetsConfig = this.#poolAssetsConfig, fetchConfig?: FetchConfig): Promise<Prices> {
88
- if (assets.length == 0) {
89
- return new Prices(Dictionary.empty<bigint, bigint>(), Cell.EMPTY);
90
- }
91
-
92
- await this.#collectPricesWithValidation(fetchConfig);
93
-
94
- if (this.#prices.length < this.#minimalOracles) {
95
- throw new Error(`Error per updating prices, valid ${this.#prices.length} of ${this.#minimalOracles}`);
96
- }
97
- const prices = this.#getPricesByAssetList(assets);
98
- return new Prices(prices.dict, prices.dataCell);
99
- }
100
-
101
- #getPricesByAssetList(assets: PoolAssetsConfig) {
102
- let pricesFiltered = this.#prices;
103
- if (pricesFiltered.length < this.#minimalOracles) {
104
- throw new Error("Not enough price data");
105
- }
106
- if (pricesFiltered.length > this.#minimalOracles) {
107
- const sortedByTimestamp = pricesFiltered.slice().sort((a, b) => b.timestamp - a.timestamp);
108
- const newerPrices = sortedByTimestamp.slice(0, this.#minimalOracles);
109
- pricesFiltered = newerPrices.sort((a, b) => a.oracleId - b.oracleId);
110
- }
111
- const medianData = assets.map((asset) => ({
112
- assetId: asset.assetId,
113
- medianPrice: getMedianPrice(pricesFiltered, asset.assetId),
114
- }));
115
- const nonEmptymedianData = medianData.filter(x => x.medianPrice != null) as { assetId: bigint, medianPrice: bigint }[];
116
- const packedMedianData = packAssetsData(nonEmptymedianData);
117
- const oraclesData = pricesFiltered.map((x) => ({
118
- oracle: { id: x.oracleId, pubkey: x.pubkey },
119
- data: { timestamp: x.timestamp, prices: x.dict },
120
- signature: x.signature,
121
- }));
122
- const packedOracleData = packOraclesData(oraclesData, nonEmptymedianData.map(x => x.assetId));
123
- const dict = Dictionary.empty<bigint, bigint>();
124
- for (const medianDataAsset of nonEmptymedianData) {
125
- dict.set(medianDataAsset.assetId, medianDataAsset.medianPrice);
126
- }
127
- return {
128
- dict: dict,
129
- dataCell: packPrices(packedMedianData, packedOracleData)
130
- }
131
- }
132
-
133
- async #collectPrices(fetchConfig?: FetchConfig): Promise<boolean> {
134
- for (const priceSource of this.#priceSources) {
135
- try {
136
- this.#prices = await proxyFetchRetries(
137
- collectAndFilterPrices(priceSource, this.#minimalOracles, fetchConfig),
138
- fetchConfig,
139
- );
140
- return true;
141
- } catch (error) {
142
- // Try next source
143
- continue;
144
- }
145
- }
146
-
147
- return false;
148
- }
149
-
150
- async #collectPricesWithValidation(fetchConfig?: FetchConfig): Promise<void> {
151
- if (!this.#prices || this.#filterPrices() < this.#minimalOracles) {
152
- const success = await this.#collectPrices(fetchConfig);
153
- if (!success || this.#prices.length < this.#minimalOracles) {
154
- throw new Error(
155
- `Failed to collect sufficient prices: ${this.#prices?.length || 0} of ${this.#minimalOracles}`,
156
- );
157
- }
158
- }
159
- }
160
-
161
- #filterPrices(): number {
162
- this.#prices = this.#prices.filter(verifyPricesTimestamp());
163
- return this.#prices.length;
164
- }
165
-
166
- #filterEmptyPrincipalsAndAssets(principals: Dictionary<bigint, bigint>) {
167
- return principals.keys().filter(x => principals.get(x)! != 0n).map(x => this.#poolAssetsConfig.find(asset => asset.assetId == x));
168
- }
169
- }