@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.
- package/dist/api/feeds.d.ts +15 -24
- package/dist/api/feeds.js +22 -49
- package/dist/api/math.d.ts +1 -1
- package/dist/api/math.js +55 -38
- package/dist/api/parser.d.ts +2 -2
- package/dist/api/parser.js +3 -3
- package/dist/api/parsers/PythOracleParser.d.ts +3 -3
- package/dist/api/parsers/PythOracleParser.js +2 -1
- package/dist/api/prices.js +2 -1
- package/dist/constants/general/index.d.ts +25 -11
- package/dist/constants/general/index.js +15 -1
- package/dist/constants/pools/mainnet.js +20 -18
- package/dist/constants/pools/testnet.js +14 -6
- package/dist/contracts/AbstractMaster.d.ts +239 -127
- package/dist/contracts/AbstractMaster.js +101 -16
- package/dist/contracts/ClassicMaster.d.ts +12 -12
- package/dist/contracts/PythMaster.d.ts +40 -24
- package/dist/contracts/PythMaster.js +61 -76
- package/dist/contracts/PythOracle.d.ts +16 -0
- package/dist/contracts/PythOracle.js +19 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{prices → oracles}/Types.d.ts +0 -4
- package/dist/oracles/collectors/AbstractCollector.d.ts +10 -0
- package/dist/oracles/collectors/AbstractCollector.js +6 -0
- package/dist/oracles/collectors/ClassicCollector.d.ts +22 -0
- package/dist/oracles/collectors/ClassicCollector.js +192 -0
- package/dist/oracles/collectors/PythCollector.d.ts +27 -0
- package/dist/oracles/collectors/PythCollector.js +252 -0
- package/dist/oracles/collectors/index.d.ts +3 -0
- package/dist/oracles/collectors/index.js +19 -0
- package/dist/{prices → oracles}/index.d.ts +2 -3
- package/dist/{prices → oracles}/index.js +2 -3
- package/dist/oracles/prices/AbstractPrices.d.ts +33 -0
- package/dist/oracles/prices/AbstractPrices.js +40 -0
- package/dist/oracles/prices/ClassicPrices.d.ts +19 -0
- package/dist/oracles/prices/ClassicPrices.js +48 -0
- package/dist/oracles/prices/PythPrices.d.ts +18 -0
- package/dist/oracles/prices/PythPrices.js +32 -0
- package/dist/oracles/prices/index.d.ts +3 -0
- package/dist/oracles/prices/index.js +19 -0
- package/dist/{prices → oracles}/utils.d.ts +6 -1
- package/dist/{prices → oracles}/utils.js +10 -1
- package/dist/types/Master.d.ts +4 -5
- package/package.json +2 -3
- package/src/api/feeds.ts +24 -60
- package/src/api/math.ts +118 -90
- package/src/api/parser.ts +5 -5
- package/src/api/parsers/PythOracleParser.ts +6 -2
- package/src/api/prices.ts +2 -1
- package/src/constants/general/index.ts +16 -1
- package/src/constants/pools/mainnet.ts +20 -35
- package/src/constants/pools/testnet.ts +17 -8
- package/src/contracts/AbstractMaster.ts +272 -144
- package/src/contracts/ClassicMaster.ts +12 -12
- package/src/contracts/PythMaster.ts +130 -123
- package/src/contracts/PythOracle.ts +20 -0
- package/src/contracts/index.ts +2 -0
- package/src/index.ts +1 -1
- package/src/{prices → oracles}/Types.ts +0 -5
- package/src/oracles/collectors/AbstractCollector.ts +22 -0
- package/src/oracles/collectors/ClassicCollector.ts +263 -0
- package/src/oracles/collectors/PythCollector.ts +358 -0
- package/src/oracles/collectors/index.ts +3 -0
- package/src/{prices → oracles}/index.ts +2 -3
- package/src/oracles/prices/AbstractPrices.ts +59 -0
- package/src/oracles/prices/ClassicPrices.ts +52 -0
- package/src/oracles/prices/PythPrices.ts +40 -0
- package/src/oracles/prices/index.ts +3 -0
- package/src/{prices → oracles}/utils.ts +12 -1
- package/src/types/Master.ts +4 -6
- package/dist/prices/Oracle.interface.d.ts +0 -9
- package/dist/prices/Oracle.interface.js +0 -2
- package/dist/prices/Prices.d.ts +0 -11
- package/dist/prices/Prices.js +0 -53
- package/dist/prices/PricesCollector.d.ts +0 -22
- package/dist/prices/PricesCollector.js +0 -146
- package/dist/prices/PythCollector.d.ts +0 -22
- package/dist/prices/PythCollector.js +0 -217
- package/src/prices/Oracle.interface.ts +0 -18
- package/src/prices/Prices.ts +0 -45
- package/src/prices/PricesCollector.ts +0 -169
- package/src/prices/PythCollector.ts +0 -294
- /package/dist/{prices → oracles}/Types.js +0 -0
- /package/dist/{prices → oracles}/constants.d.ts +0 -0
- /package/dist/{prices → oracles}/constants.js +0 -0
- /package/dist/{prices → oracles}/sources/Backend.d.ts +0 -0
- /package/dist/{prices → oracles}/sources/Backend.js +0 -0
- /package/dist/{prices → oracles}/sources/Icp.d.ts +0 -0
- /package/dist/{prices → oracles}/sources/Icp.js +0 -0
- /package/dist/{prices → oracles}/sources/PriceSource.d.ts +0 -0
- /package/dist/{prices → oracles}/sources/PriceSource.js +0 -0
- /package/dist/{prices → oracles}/sources/index.d.ts +0 -0
- /package/dist/{prices → oracles}/sources/index.js +0 -0
- /package/src/{prices → oracles}/constants.ts +0 -0
- /package/src/{prices → oracles}/sources/Backend.ts +0 -0
- /package/src/{prices → oracles}/sources/Icp.ts +0 -0
- /package/src/{prices → oracles}/sources/PriceSource.ts +0 -0
- /package/src/{prices → oracles}/sources/index.ts +0 -0
|
@@ -4,12 +4,14 @@ import { PythOracleInfo, PythOracleParser } from '../api/parsers/PythOracleParse
|
|
|
4
4
|
import { composeFeedsCell, packPythUpdatesData } from '../api/prices';
|
|
5
5
|
import { TON_MAINNET } from '../constants';
|
|
6
6
|
import { FEES, OPCODES } from '../constants/general';
|
|
7
|
+
import { PoolAssetConfig } from '../types/Master';
|
|
7
8
|
import { isTonAsset } from '../utils/utils';
|
|
8
9
|
import {
|
|
9
10
|
AbstractEvaaMaster,
|
|
10
11
|
BaseMasterConfig,
|
|
11
12
|
BaseMasterData,
|
|
12
13
|
EvaaParameters,
|
|
14
|
+
JettonParams,
|
|
13
15
|
LiquidationInnerParameters,
|
|
14
16
|
LiquidationOperationBuilderParameters,
|
|
15
17
|
LiquidationParameters,
|
|
@@ -20,26 +22,26 @@ import {
|
|
|
20
22
|
/**
|
|
21
23
|
* pyth specific parameters
|
|
22
24
|
*/
|
|
23
|
-
export
|
|
24
|
-
priceData: Buffer | Cell;
|
|
25
|
-
targetFeeds: HexString[];
|
|
26
|
-
|
|
27
|
-
}
|
|
25
|
+
export interface PythBaseData {
|
|
26
|
+
readonly priceData: Buffer | Cell;
|
|
27
|
+
readonly targetFeeds: HexString[];
|
|
28
|
+
readonly refAssets: PoolAssetConfig[];
|
|
29
|
+
}
|
|
28
30
|
|
|
29
|
-
export
|
|
30
|
-
pythAddress: Address;
|
|
31
|
-
minPublishTime: number | bigint;
|
|
32
|
-
maxPublishTime: number | bigint;
|
|
33
|
-
}
|
|
31
|
+
export interface ProxySpecificPythParams {
|
|
32
|
+
readonly pythAddress: Address;
|
|
33
|
+
readonly minPublishTime: number | bigint;
|
|
34
|
+
readonly maxPublishTime: number | bigint;
|
|
35
|
+
}
|
|
34
36
|
|
|
35
|
-
export
|
|
36
|
-
publishGap: number | bigint;
|
|
37
|
-
maxStaleness: number | bigint;
|
|
38
|
-
}
|
|
37
|
+
export interface OnchainSpecificPythParams {
|
|
38
|
+
readonly publishGap: number | bigint;
|
|
39
|
+
readonly maxStaleness: number | bigint;
|
|
40
|
+
}
|
|
39
41
|
|
|
40
|
-
export
|
|
42
|
+
export interface JettonPythParams extends PythBaseData, OnchainSpecificPythParams {}
|
|
41
43
|
|
|
42
|
-
export
|
|
44
|
+
export interface TonPythParams extends PythBaseData, ProxySpecificPythParams {}
|
|
43
45
|
|
|
44
46
|
export type PythProxyParams = {
|
|
45
47
|
pyth: PythBaseData & (ProxySpecificPythParams | OnchainSpecificPythParams) & { pythAddress: Address };
|
|
@@ -48,7 +50,7 @@ export type PythProxyParams = {
|
|
|
48
50
|
export type PythSupplyWithdrawParameters = SupplyWithdrawParameters & Partial<PythProxyParams>;
|
|
49
51
|
|
|
50
52
|
export type PythWithdrawParameters = WithdrawParameters & {
|
|
51
|
-
pyth
|
|
53
|
+
pyth?: TonPythParams;
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
export type PythLiquidationOperationParameters = LiquidationOperationBuilderParameters &
|
|
@@ -58,14 +60,14 @@ export type PythLiquidationOperationParameters = LiquidationOperationBuilderPara
|
|
|
58
60
|
export type PythLiquidationParameters = LiquidationParameters & PythLiquidationOperationParameters;
|
|
59
61
|
|
|
60
62
|
// Specific master configurations
|
|
61
|
-
export
|
|
62
|
-
oraclesInfo: PythOracleInfo;
|
|
63
|
-
}
|
|
63
|
+
export interface PythMasterConfig extends BaseMasterConfig {
|
|
64
|
+
readonly oraclesInfo: PythOracleInfo;
|
|
65
|
+
}
|
|
64
66
|
|
|
65
67
|
// Specific master data types
|
|
66
|
-
export
|
|
67
|
-
masterConfig: PythMasterConfig;
|
|
68
|
-
}
|
|
68
|
+
export interface PythMasterData extends BaseMasterData {
|
|
69
|
+
readonly masterConfig: PythMasterConfig;
|
|
70
|
+
}
|
|
69
71
|
|
|
70
72
|
export class EvaaMasterPyth extends AbstractEvaaMaster<PythMasterData> {
|
|
71
73
|
constructor(parameters: EvaaParameters) {
|
|
@@ -117,94 +119,120 @@ export class EvaaMasterPyth extends AbstractEvaaMaster<PythMasterData> {
|
|
|
117
119
|
.endCell();
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Creates a wrapped operation payload for TON assets
|
|
124
|
+
*/
|
|
125
|
+
private createWrappedOperationPayload(opCode: number, queryID: number | bigint, operationPayload: Cell): Cell {
|
|
126
|
+
return beginCell().storeUint(opCode, 32).storeUint(queryID, 64).storeRef(operationPayload).endCell();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Creates a jetton transfer message with pyth data
|
|
131
|
+
*/
|
|
132
|
+
private createJettonPythMessage(
|
|
133
|
+
parameters: JettonParams & { queryID: number | bigint },
|
|
134
|
+
operationPayload: Cell,
|
|
135
|
+
pythParams: JettonPythParams,
|
|
136
|
+
opCode: number,
|
|
137
|
+
): Cell {
|
|
138
|
+
const { priceData, targetFeeds, publishGap, maxStaleness } = pythParams;
|
|
139
|
+
const masterMessage = this.buildPythMasterMessage(
|
|
140
|
+
{
|
|
141
|
+
queryId: parameters.queryID,
|
|
142
|
+
opCode,
|
|
143
|
+
updateDataCell: packPythUpdatesData(priceData),
|
|
144
|
+
targetFeedsCell: composeFeedsCell(targetFeeds),
|
|
145
|
+
publishGap,
|
|
146
|
+
maxStaleness,
|
|
147
|
+
},
|
|
148
|
+
operationPayload,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return this.createJettonTransferMessage(parameters, FEES.SUPPLY_WITHDRAW, masterMessage);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Creates a TON pyth proxy message
|
|
156
|
+
*/
|
|
157
|
+
private createTonPythMessage(
|
|
158
|
+
parameters: { queryID: number | bigint },
|
|
159
|
+
operationPayload: Cell,
|
|
160
|
+
pythParams: TonPythParams,
|
|
161
|
+
opCode: number,
|
|
162
|
+
): Cell {
|
|
163
|
+
const { priceData, targetFeeds, minPublishTime, maxPublishTime } = pythParams;
|
|
164
|
+
const wrappedOperationPayload = this.createWrappedOperationPayload(
|
|
165
|
+
opCode,
|
|
166
|
+
parameters.queryID,
|
|
167
|
+
operationPayload,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
return this.buildPythProxyMessage(
|
|
171
|
+
this.address,
|
|
172
|
+
packPythUpdatesData(priceData),
|
|
173
|
+
composeFeedsCell(targetFeeds),
|
|
174
|
+
minPublishTime,
|
|
175
|
+
maxPublishTime,
|
|
176
|
+
wrappedOperationPayload,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
protected createSupplyWithdrawMessageNoPrices(parameters: SupplyWithdrawParameters, operationPayload: Cell): Cell {
|
|
181
|
+
const messageBody = beginCell()
|
|
182
|
+
.storeUint(OPCODES.SUPPLY_WITHDRAW_MASTER_WITHOUT_PRICES, 32)
|
|
183
|
+
.storeSlice(operationPayload.beginParse())
|
|
184
|
+
.endCell();
|
|
185
|
+
|
|
186
|
+
if (!isTonAsset(parameters.supplyAsset)) {
|
|
187
|
+
return this.createJettonTransferMessage(parameters, FEES.SUPPLY_WITHDRAW + FEES.JETTON_FWD, messageBody);
|
|
188
|
+
} else {
|
|
189
|
+
return beginCell()
|
|
190
|
+
.storeUint(OPCODES.SUPPLY_WITHDRAW_MASTER_WITHOUT_PRICES, 32)
|
|
191
|
+
.storeUint(parameters.queryID, 64)
|
|
192
|
+
.storeSlice(operationPayload.beginParse())
|
|
193
|
+
.endCell();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
121
197
|
createSupplyWithdrawMessage(parameters: PythSupplyWithdrawParameters): Cell {
|
|
122
198
|
const operationPayload = this.buildSupplyWithdrawOperationPayload(parameters);
|
|
123
199
|
|
|
200
|
+
// Handle case without pyth data
|
|
201
|
+
if (!parameters.pyth) {
|
|
202
|
+
return this.createSupplyWithdrawMessageNoPrices(parameters, operationPayload);
|
|
203
|
+
}
|
|
204
|
+
|
|
124
205
|
if (!isTonAsset(parameters.supplyAsset)) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
{
|
|
128
|
-
queryId: parameters.queryID,
|
|
129
|
-
opCode: OPCODES.SUPPLY_WITHDRAW_MASTER_JETTON,
|
|
130
|
-
updateDataCell: packPythUpdatesData(priceData),
|
|
131
|
-
targetFeedsCell: composeFeedsCell(targetFeeds),
|
|
132
|
-
publishGap,
|
|
133
|
-
maxStaleness,
|
|
134
|
-
},
|
|
206
|
+
return this.createJettonPythMessage(
|
|
207
|
+
parameters, // as JettonParams & { queryID: number | bigint },
|
|
135
208
|
operationPayload,
|
|
209
|
+
parameters.pyth as JettonPythParams,
|
|
210
|
+
OPCODES.SUPPLY_WITHDRAW_MASTER_JETTON,
|
|
136
211
|
);
|
|
137
|
-
|
|
138
|
-
return this.createJettonTransferMessage(parameters, FEES.SUPPLY_WITHDRAW, masterMessage);
|
|
139
212
|
} else {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
.
|
|
145
|
-
.endCell();
|
|
146
|
-
return this.buildPythProxyMessage(
|
|
147
|
-
this.address,
|
|
148
|
-
packPythUpdatesData(priceData),
|
|
149
|
-
composeFeedsCell(targetFeeds),
|
|
150
|
-
minPublishTime,
|
|
151
|
-
maxPublishTime,
|
|
152
|
-
wrappedOperationPayload,
|
|
213
|
+
return this.createTonPythMessage(
|
|
214
|
+
parameters,
|
|
215
|
+
operationPayload,
|
|
216
|
+
parameters.pyth as TonPythParams,
|
|
217
|
+
OPCODES.SUPPLY_WITHDRAW_MASTER,
|
|
153
218
|
);
|
|
154
219
|
}
|
|
155
220
|
}
|
|
156
221
|
|
|
157
|
-
|
|
158
|
-
// const extraTail =
|
|
159
|
-
// (parameters.subaccountId ?? 0) == 0
|
|
160
|
-
// ? beginCell().endCell()
|
|
161
|
-
// : beginCell()
|
|
162
|
-
// .storeInt(parameters.subaccountId ?? 0, 16)
|
|
163
|
-
// .storeUint(0, 2)
|
|
164
|
-
// .endCell();
|
|
165
|
-
|
|
166
|
-
// const wrappedOperationPayload = beginCell()
|
|
167
|
-
// .storeUint(OPCODES.SUPPLY_WITHDRAW_MASTER, 32) // op_code: 0x4
|
|
168
|
-
// .storeUint(parameters.queryID, 64)
|
|
169
|
-
// .storeRef(
|
|
170
|
-
// beginCell()
|
|
171
|
-
// .storeUint(parameters.asset.assetId, 256)
|
|
172
|
-
// .storeUint(parameters.amount, 64)
|
|
173
|
-
// .storeAddress(parameters.userAddress)
|
|
174
|
-
// .storeInt(parameters.includeUserCode ? -1 : 0, 2)
|
|
175
|
-
// .storeUint(parameters.amountToTransfer, 64)
|
|
176
|
-
// .storeRef(parameters.payload)
|
|
177
|
-
// .storeSlice(extraTail.beginParse())
|
|
178
|
-
// .endCell(),
|
|
179
|
-
// )
|
|
180
|
-
// .endCell();
|
|
181
|
-
|
|
182
|
-
// const { priceData, targetFeeds, minPublishTime, maxPublishTime } = parameters.pyth as TonPythParams;
|
|
183
|
-
|
|
184
|
-
// return makePythProxyMessage(
|
|
185
|
-
// this.address,
|
|
186
|
-
// packPythUpdatesData(priceData),
|
|
187
|
-
// composeFeedsCell(targetFeeds),
|
|
188
|
-
// minPublishTime,
|
|
189
|
-
// maxPublishTime,
|
|
190
|
-
// wrappedOperationPayload,
|
|
191
|
-
// );
|
|
192
|
-
// }
|
|
193
|
-
|
|
194
|
-
buildRefTokensDict(requestedRefTokens: bigint[]): Dictionary<bigint, Buffer> {
|
|
222
|
+
buildRefTokensDict(refAssets: PoolAssetConfig[]): Dictionary<bigint, Buffer> {
|
|
195
223
|
const refsDict: Dictionary<bigint, Buffer> = Dictionary.empty(
|
|
196
224
|
Dictionary.Keys.BigUint(256),
|
|
197
225
|
Dictionary.Values.Buffer(0),
|
|
198
226
|
);
|
|
199
|
-
for (const
|
|
200
|
-
refsDict.set(
|
|
227
|
+
for (const refAsset of refAssets) {
|
|
228
|
+
refsDict.set(refAsset.assetId, Buffer.alloc(0));
|
|
201
229
|
}
|
|
202
230
|
|
|
203
231
|
return refsDict;
|
|
204
232
|
}
|
|
205
233
|
|
|
206
234
|
buildGeneralDataPayload(parameters: PythSupplyWithdrawParameters): Cell {
|
|
207
|
-
const refTokensDict = this.buildRefTokensDict(parameters.pyth?.
|
|
235
|
+
const refTokensDict = this.buildRefTokensDict(parameters.pyth?.refAssets ?? []);
|
|
208
236
|
return beginCell()
|
|
209
237
|
.storeInt(parameters.includeUserCode ? -1 : 0, 2)
|
|
210
238
|
.storeDict(refTokensDict, Dictionary.Keys.BigUint(256), Dictionary.Values.Buffer(0))
|
|
@@ -240,7 +268,7 @@ export class EvaaMasterPyth extends AbstractEvaaMaster<PythMasterData> {
|
|
|
240
268
|
});
|
|
241
269
|
await via.send({
|
|
242
270
|
value,
|
|
243
|
-
to: parameters.pyth.
|
|
271
|
+
to: parameters.pyth?.pythAddress ?? this.address,
|
|
244
272
|
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
|
|
245
273
|
body: message,
|
|
246
274
|
});
|
|
@@ -250,48 +278,27 @@ export class EvaaMasterPyth extends AbstractEvaaMaster<PythMasterData> {
|
|
|
250
278
|
const operationPayloadBuilder = this.buildLiquidationOperationPayloadBuilder(parameters);
|
|
251
279
|
const innerBuilder = this.buildLiquidationInnerBuilder(parameters);
|
|
252
280
|
|
|
253
|
-
const refTokensDict = this.buildRefTokensDict(parameters.pyth.
|
|
281
|
+
const refTokensDict = this.buildRefTokensDict(parameters.pyth.refAssets);
|
|
254
282
|
|
|
255
283
|
return operationPayloadBuilder.storeDict(refTokensDict).storeRef(innerBuilder).endCell();
|
|
256
284
|
}
|
|
257
285
|
|
|
258
286
|
createLiquidationMessage(parameters: PythLiquidationParameters): Cell {
|
|
259
|
-
const isTon = isTonAsset(parameters.asset);
|
|
260
|
-
|
|
261
287
|
const operationPayload = this.buildLiquidationOperationPayload(parameters);
|
|
262
288
|
|
|
263
|
-
if (!
|
|
264
|
-
|
|
265
|
-
parameters
|
|
266
|
-
const masterMessage = this.buildPythMasterMessage(
|
|
267
|
-
{
|
|
268
|
-
queryId: parameters.queryID,
|
|
269
|
-
opCode: OPCODES.LIQUIDATE_MASTER,
|
|
270
|
-
updateDataCell: packPythUpdatesData(priceData as Buffer | Cell),
|
|
271
|
-
targetFeedsCell: composeFeedsCell(targetFeeds),
|
|
272
|
-
publishGap,
|
|
273
|
-
maxStaleness,
|
|
274
|
-
},
|
|
289
|
+
if (!isTonAsset(parameters.asset)) {
|
|
290
|
+
return this.createJettonPythMessage(
|
|
291
|
+
parameters,
|
|
275
292
|
operationPayload,
|
|
293
|
+
parameters.pyth as JettonPythParams,
|
|
294
|
+
OPCODES.LIQUIDATE_MASTER,
|
|
276
295
|
);
|
|
277
|
-
return this.createJettonTransferMessage(parameters, FEES.LIQUIDATION_JETTON_FWD, masterMessage);
|
|
278
296
|
} else {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
.storeUint(OPCODES.LIQUIDATE_MASTER, 32)
|
|
285
|
-
.storeUint(parameters.queryID, 64)
|
|
286
|
-
.storeRef(operationPayload)
|
|
287
|
-
.endCell();
|
|
288
|
-
return this.buildPythProxyMessage(
|
|
289
|
-
this.address,
|
|
290
|
-
packPythUpdatesData(priceData as Buffer | Cell),
|
|
291
|
-
composeFeedsCell(targetFeeds),
|
|
292
|
-
minPublishTime,
|
|
293
|
-
maxPublishTime,
|
|
294
|
-
wrappedOperationPayload,
|
|
297
|
+
return this.createTonPythMessage(
|
|
298
|
+
parameters,
|
|
299
|
+
operationPayload,
|
|
300
|
+
parameters.pyth as TonPythParams,
|
|
301
|
+
OPCODES.LIQUIDATE_MASTER,
|
|
295
302
|
);
|
|
296
303
|
}
|
|
297
304
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { calculateUpdatePriceFeedsFee, createCellChain } from '@pythnetwork/pyth-ton-js';
|
|
2
|
+
import { Address, Cell, Contract, ContractProvider } from '@ton/core';
|
|
3
|
+
|
|
4
|
+
export class PythOracle implements Contract {
|
|
5
|
+
constructor(
|
|
6
|
+
readonly address: Address,
|
|
7
|
+
readonly init?: { code: Cell; data: Cell },
|
|
8
|
+
) {}
|
|
9
|
+
|
|
10
|
+
static createFromAddress(address: Address) {
|
|
11
|
+
return new this(address);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async getUpdateFee(provider: ContractProvider, vm: Buffer) {
|
|
15
|
+
const result = await provider.get('get_update_fee', [{ type: 'slice', cell: createCellChain(vm) }]);
|
|
16
|
+
const numUpdates = result.stack.readNumber();
|
|
17
|
+
|
|
18
|
+
return calculateUpdatePriceFeedsFee(BigInt(numUpdates)) + BigInt(numUpdates);
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/contracts/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -51,11 +51,6 @@ export type PythFeedUpdateType = {
|
|
|
51
51
|
binary: Buffer;
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
export type PriceData = {
|
|
55
|
-
dict: Dictionary<bigint, bigint>;
|
|
56
|
-
dataCell: Cell;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
54
|
export type OraclePricesData = {
|
|
60
55
|
timestamp: number;
|
|
61
56
|
prices: Dictionary<bigint, bigint>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Dictionary } from '@ton/core';
|
|
2
|
+
import { PoolAssetConfig } from '../../types/Master';
|
|
3
|
+
import { FetchConfig } from '../../utils/utils';
|
|
4
|
+
import { ClassicPrices } from '../prices/ClassicPrices';
|
|
5
|
+
import { PythPrices } from '../prices/PythPrices';
|
|
6
|
+
|
|
7
|
+
export abstract class AbstractCollector {
|
|
8
|
+
abstract getPricesForLiquidate(
|
|
9
|
+
realPrincipals: Dictionary<bigint, bigint>,
|
|
10
|
+
fetchConfig?: FetchConfig,
|
|
11
|
+
): Promise<ClassicPrices | PythPrices>;
|
|
12
|
+
|
|
13
|
+
abstract getPricesForSupplyWithdraw(
|
|
14
|
+
realPrincipals: Dictionary<bigint, bigint>,
|
|
15
|
+
supplyAsset: PoolAssetConfig | undefined,
|
|
16
|
+
withdrawAsset: PoolAssetConfig | undefined,
|
|
17
|
+
collateralToDebt: boolean,
|
|
18
|
+
fetchConfig?: FetchConfig,
|
|
19
|
+
): Promise<ClassicPrices | PythPrices>;
|
|
20
|
+
|
|
21
|
+
abstract getPrices(assets: PoolAssetConfig[], fetchConfig?: FetchConfig): Promise<ClassicPrices | PythPrices>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { Dictionary } from '@ton/core';
|
|
2
|
+
import { checkNotInDebtAtAll } from '../../api/math';
|
|
3
|
+
import { ExtendedEvaaOracle, PoolAssetConfig } from '../../types/Master';
|
|
4
|
+
import { FetchConfig, proxyFetchRetries } from '../../utils/utils';
|
|
5
|
+
import { ClassicPrices, ClassicPricesMode, ClassicPricesOffset } from '../prices/ClassicPrices';
|
|
6
|
+
import { PriceSource } from '../sources';
|
|
7
|
+
import { DefaultPriceSourcesConfig, PriceSourcesConfig, RawPriceData } from '../Types';
|
|
8
|
+
import {
|
|
9
|
+
collectAndFilterPrices,
|
|
10
|
+
generatePriceSources,
|
|
11
|
+
getMedianPrice,
|
|
12
|
+
packAssetsData,
|
|
13
|
+
packOraclesData,
|
|
14
|
+
packPrices,
|
|
15
|
+
verifyPricesTimestamp,
|
|
16
|
+
} from '../utils';
|
|
17
|
+
import { AbstractCollector } from './AbstractCollector';
|
|
18
|
+
|
|
19
|
+
export type ClassicCollectorConfig = {
|
|
20
|
+
poolAssetsConfig: PoolAssetConfig[];
|
|
21
|
+
minimalOracles: number;
|
|
22
|
+
evaaOracles: ExtendedEvaaOracle[];
|
|
23
|
+
sourcesConfig?: PriceSourcesConfig;
|
|
24
|
+
additionalPriceSources?: PriceSource[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export class ClassicCollector extends AbstractCollector {
|
|
28
|
+
#prices: RawPriceData[];
|
|
29
|
+
#poolAssetsConfig: PoolAssetConfig[];
|
|
30
|
+
#sourcesConfig: PriceSourcesConfig;
|
|
31
|
+
#priceSources: PriceSource[];
|
|
32
|
+
#minimalOracles: number;
|
|
33
|
+
|
|
34
|
+
constructor(config: ClassicCollectorConfig) {
|
|
35
|
+
super();
|
|
36
|
+
|
|
37
|
+
this.#poolAssetsConfig = config.poolAssetsConfig;
|
|
38
|
+
this.#sourcesConfig = config.sourcesConfig ?? DefaultPriceSourcesConfig;
|
|
39
|
+
this.#priceSources = generatePriceSources(this.#sourcesConfig, config.evaaOracles);
|
|
40
|
+
this.#minimalOracles = config.minimalOracles;
|
|
41
|
+
if (config.additionalPriceSources) {
|
|
42
|
+
this.#priceSources.push(...config.additionalPriceSources);
|
|
43
|
+
}
|
|
44
|
+
this.#prices = [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async getPricesForLiquidate(
|
|
48
|
+
realPrincipals: Dictionary<bigint, bigint>,
|
|
49
|
+
fetchConfig?: FetchConfig,
|
|
50
|
+
): Promise<ClassicPrices> {
|
|
51
|
+
const assets = this.#filterEmptyPrincipalsAndAssets(realPrincipals);
|
|
52
|
+
if (assets.includes(undefined)) {
|
|
53
|
+
throw new Error('User from another pool');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const validAssets = assets.map((x) => x!);
|
|
57
|
+
const spotAssets = this.#convertToSpotAssets(validAssets);
|
|
58
|
+
|
|
59
|
+
return await this.#getPricesWithMode(spotAssets, ClassicPricesMode.SPOT, fetchConfig);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async getPricesForWithdraw(
|
|
63
|
+
realPrincipals: Dictionary<bigint, bigint>,
|
|
64
|
+
withdrawAsset: PoolAssetConfig,
|
|
65
|
+
collateralToDebt = false,
|
|
66
|
+
fetchConfig?: FetchConfig,
|
|
67
|
+
): Promise<ClassicPrices> {
|
|
68
|
+
let assets = this.#filterEmptyPrincipalsAndAssets(realPrincipals);
|
|
69
|
+
if (
|
|
70
|
+
checkNotInDebtAtAll(realPrincipals) &&
|
|
71
|
+
(realPrincipals.get(withdrawAsset.assetId) ?? 0n) > 0n &&
|
|
72
|
+
!collateralToDebt
|
|
73
|
+
) {
|
|
74
|
+
return ClassicPrices.createEmptyTwapPrices();
|
|
75
|
+
}
|
|
76
|
+
if (assets.includes(undefined)) {
|
|
77
|
+
throw new Error('User from another pool');
|
|
78
|
+
}
|
|
79
|
+
if (!assets.includes(withdrawAsset)) {
|
|
80
|
+
assets.push(withdrawAsset);
|
|
81
|
+
}
|
|
82
|
+
if (collateralToDebt && assets.length == 1) {
|
|
83
|
+
throw new Error('Cannot debt only one supplied asset');
|
|
84
|
+
}
|
|
85
|
+
return await this.getPrices(
|
|
86
|
+
assets.map((x) => x!),
|
|
87
|
+
fetchConfig,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async getPricesForSupplyWithdraw(
|
|
92
|
+
realPrincipals: Dictionary<bigint, bigint>,
|
|
93
|
+
supplyAsset: PoolAssetConfig | undefined,
|
|
94
|
+
withdrawAsset: PoolAssetConfig | undefined,
|
|
95
|
+
collateralToDebt: boolean,
|
|
96
|
+
fetchConfig?: FetchConfig,
|
|
97
|
+
): Promise<ClassicPrices> {
|
|
98
|
+
let assets = this.#filterEmptyPrincipalsAndAssets(realPrincipals);
|
|
99
|
+
if (
|
|
100
|
+
checkNotInDebtAtAll(realPrincipals) &&
|
|
101
|
+
withdrawAsset &&
|
|
102
|
+
(realPrincipals.get(withdrawAsset.assetId) ?? 0n) > 0n &&
|
|
103
|
+
!collateralToDebt
|
|
104
|
+
) {
|
|
105
|
+
return ClassicPrices.createEmptyTwapPrices();
|
|
106
|
+
}
|
|
107
|
+
if (assets.includes(undefined)) {
|
|
108
|
+
throw new Error('User from another pool');
|
|
109
|
+
}
|
|
110
|
+
if (withdrawAsset && !assets.includes(withdrawAsset)) {
|
|
111
|
+
assets.push(withdrawAsset);
|
|
112
|
+
}
|
|
113
|
+
if (collateralToDebt && assets.length == 1) {
|
|
114
|
+
throw new Error('Cannot debt only one supplied asset');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const validAssets = assets.map((x) => x!);
|
|
118
|
+
const twapAssets = this.#convertToTwapAssets(validAssets);
|
|
119
|
+
|
|
120
|
+
return await this.#getPricesWithMode(twapAssets, ClassicPricesMode.TWAP, fetchConfig);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async getPrices(
|
|
124
|
+
assets: PoolAssetConfig[] = this.#poolAssetsConfig,
|
|
125
|
+
fetchConfig?: FetchConfig,
|
|
126
|
+
): Promise<ClassicPrices> {
|
|
127
|
+
if (assets.length == 0) {
|
|
128
|
+
return ClassicPrices.createEmptyPrices();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await this.#collectPricesWithValidation(fetchConfig);
|
|
132
|
+
|
|
133
|
+
if (this.#prices.length < this.#minimalOracles) {
|
|
134
|
+
throw new Error(`Error per updating prices, valid ${this.#prices.length} of ${this.#minimalOracles}`);
|
|
135
|
+
}
|
|
136
|
+
const prices = this.#getPricesByAssetList(assets);
|
|
137
|
+
return new ClassicPrices({
|
|
138
|
+
dict: prices.dict,
|
|
139
|
+
dataCell: prices.dataCell,
|
|
140
|
+
minPublishTime: undefined,
|
|
141
|
+
maxPublishTime: undefined,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#getPricesByAssetList(assets: PoolAssetConfig[]) {
|
|
146
|
+
let pricesFiltered = this.#prices;
|
|
147
|
+
if (pricesFiltered.length < this.#minimalOracles) {
|
|
148
|
+
throw new Error('Not enough price data');
|
|
149
|
+
}
|
|
150
|
+
if (pricesFiltered.length > this.#minimalOracles) {
|
|
151
|
+
const sortedByTimestamp = pricesFiltered.slice().sort((a, b) => b.timestamp - a.timestamp);
|
|
152
|
+
const newerPrices = sortedByTimestamp.slice(0, this.#minimalOracles);
|
|
153
|
+
pricesFiltered = newerPrices.sort((a, b) => a.oracleId - b.oracleId);
|
|
154
|
+
}
|
|
155
|
+
const medianData = assets.map((asset) => ({
|
|
156
|
+
assetId: asset.assetId,
|
|
157
|
+
medianPrice: getMedianPrice(pricesFiltered, asset.assetId),
|
|
158
|
+
}));
|
|
159
|
+
const nonEmptymedianData = medianData.filter((x) => x.medianPrice != null) as {
|
|
160
|
+
assetId: bigint;
|
|
161
|
+
medianPrice: bigint;
|
|
162
|
+
}[];
|
|
163
|
+
const packedMedianData = packAssetsData(nonEmptymedianData);
|
|
164
|
+
const oraclesData = pricesFiltered.map((x) => ({
|
|
165
|
+
oracle: { id: x.oracleId, pubkey: x.pubkey },
|
|
166
|
+
data: { timestamp: x.timestamp, prices: x.dict },
|
|
167
|
+
signature: x.signature,
|
|
168
|
+
}));
|
|
169
|
+
const packedOracleData = packOraclesData(
|
|
170
|
+
oraclesData,
|
|
171
|
+
nonEmptymedianData.map((x) => x.assetId),
|
|
172
|
+
);
|
|
173
|
+
const dict = Dictionary.empty<bigint, bigint>();
|
|
174
|
+
for (const medianDataAsset of nonEmptymedianData) {
|
|
175
|
+
dict.set(medianDataAsset.assetId, medianDataAsset.medianPrice);
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
dict: dict,
|
|
179
|
+
dataCell: packPrices(packedMedianData, packedOracleData),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async #collectPrices(fetchConfig?: FetchConfig): Promise<boolean> {
|
|
184
|
+
for (const priceSource of this.#priceSources) {
|
|
185
|
+
try {
|
|
186
|
+
this.#prices = await proxyFetchRetries(
|
|
187
|
+
collectAndFilterPrices(priceSource, this.#minimalOracles, fetchConfig),
|
|
188
|
+
fetchConfig,
|
|
189
|
+
);
|
|
190
|
+
return true;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
// Try next source
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async #collectPricesWithValidation(fetchConfig?: FetchConfig): Promise<void> {
|
|
201
|
+
if (!this.#prices || this.#filterPrices() < this.#minimalOracles) {
|
|
202
|
+
const success = await this.#collectPrices(fetchConfig);
|
|
203
|
+
if (!success || this.#prices.length < this.#minimalOracles) {
|
|
204
|
+
throw new Error(
|
|
205
|
+
`Failed to collect sufficient prices: ${this.#prices?.length || 0} of ${this.#minimalOracles}`,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#filterPrices(): number {
|
|
212
|
+
this.#prices = this.#prices.filter(verifyPricesTimestamp());
|
|
213
|
+
return this.#prices.length;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#filterEmptyPrincipalsAndAssets(principals: Dictionary<bigint, bigint>) {
|
|
217
|
+
return principals
|
|
218
|
+
.keys()
|
|
219
|
+
.filter((x) => principals.get(x)! != 0n)
|
|
220
|
+
.map((x) => this.#poolAssetsConfig.find((asset) => asset.assetId == x));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
#convertToTwapAssets(assets: PoolAssetConfig[]): PoolAssetConfig[] {
|
|
224
|
+
return assets.map((asset) => ({
|
|
225
|
+
...asset,
|
|
226
|
+
assetId: asset.assetId - ClassicPricesOffset[ClassicPricesMode.TWAP],
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
#convertToSpotAssets(assets: PoolAssetConfig[]): PoolAssetConfig[] {
|
|
231
|
+
return assets.map((asset) => ({
|
|
232
|
+
...asset,
|
|
233
|
+
assetId: asset.assetId - ClassicPricesOffset[ClassicPricesMode.SPOT],
|
|
234
|
+
}));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async #getPricesWithMode(
|
|
238
|
+
assets: PoolAssetConfig[],
|
|
239
|
+
mode: ClassicPricesMode,
|
|
240
|
+
fetchConfig?: FetchConfig,
|
|
241
|
+
): Promise<ClassicPrices> {
|
|
242
|
+
if (assets.length == 0) {
|
|
243
|
+
return ClassicPricesMode.TWAP
|
|
244
|
+
? ClassicPrices.createEmptyTwapPrices()
|
|
245
|
+
: ClassicPrices.createEmptySpotPrices();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
await this.#collectPricesWithValidation(fetchConfig);
|
|
249
|
+
|
|
250
|
+
if (this.#prices.length < this.#minimalOracles) {
|
|
251
|
+
throw new Error(`Error per updating prices, valid ${this.#prices.length} of ${this.#minimalOracles}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const prices = this.#getPricesByAssetList(assets);
|
|
255
|
+
return new ClassicPrices({
|
|
256
|
+
mode,
|
|
257
|
+
dict: prices.dict,
|
|
258
|
+
dataCell: prices.dataCell,
|
|
259
|
+
minPublishTime: undefined,
|
|
260
|
+
maxPublishTime: undefined,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|