@evaafi/sdk 0.5.5 → 0.5.6
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/helpers.js +2 -3
- package/dist/api/math.d.ts +22 -3
- package/dist/api/math.js +146 -63
- package/dist/api/parser.d.ts +3 -3
- package/dist/api/parser.js +37 -21
- package/dist/api/prices.js +1 -2
- package/dist/config.d.ts +2 -0
- package/dist/config.js +5 -0
- package/dist/constants/assets.d.ts +2 -3
- package/dist/constants/assets.js +10 -2
- package/dist/constants/general.d.ts +3 -2
- package/dist/constants/general.js +6 -5
- package/dist/constants/pools.d.ts +0 -1
- package/dist/constants/pools.js +1 -14
- package/dist/constants.d.ts +5 -34
- package/dist/constants.js +90 -37
- package/dist/contracts/MasterContract.d.ts +13 -45
- package/dist/contracts/MasterContract.js +8 -10
- package/dist/contracts/UserContract.js +7 -7
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/types/Master.d.ts +8 -7
- package/dist/types/User.d.ts +5 -5
- package/dist/utils/merkleProof.d.ts +4 -0
- package/dist/utils/merkleProof.js +107 -0
- package/dist/utils/priceUtils.d.ts +55 -0
- package/dist/utils/priceUtils.js +117 -0
- package/dist/utils/sha256BigInt.js +1 -2
- package/dist/utils/tonConnectSender.js +2 -3
- package/dist/utils/userJettonWallet.d.ts +2 -2
- package/dist/utils/userJettonWallet.js +5 -2
- package/dist/utils/utils.d.ts +2 -0
- package/dist/utils/utils.js +6 -0
- package/package.json +2 -3
- package/src/api/math.ts +162 -49
- package/src/api/parser.ts +50 -25
- package/src/constants/assets.ts +11 -3
- package/src/constants/general.ts +6 -4
- package/src/constants/pools.ts +1 -15
- package/src/contracts/MasterContract.ts +23 -65
- package/src/contracts/UserContract.ts +8 -10
- package/src/index.ts +2 -6
- package/src/types/Master.ts +9 -8
- package/src/types/User.ts +5 -5
- package/src/utils/userJettonWallet.ts +6 -2
- package/src/utils/utils.ts +6 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertToMerkleProof = convertToMerkleProof;
|
|
4
|
+
exports.generateMerkleProofDirect = generateMerkleProofDirect;
|
|
5
|
+
exports.generateMerkleProof = generateMerkleProof;
|
|
6
|
+
const core_1 = require("@ton/core");
|
|
7
|
+
function readUnaryLength(slice) {
|
|
8
|
+
let res = 0;
|
|
9
|
+
while (slice.loadBit()) {
|
|
10
|
+
res++;
|
|
11
|
+
}
|
|
12
|
+
return res;
|
|
13
|
+
}
|
|
14
|
+
function endExoticCell(b) {
|
|
15
|
+
let c = b.endCell();
|
|
16
|
+
return new core_1.Cell({ exotic: true, bits: c.bits, refs: c.refs });
|
|
17
|
+
}
|
|
18
|
+
function convertToMerkleProof(c) {
|
|
19
|
+
return endExoticCell((0, core_1.beginCell)()
|
|
20
|
+
.storeUint(3, 8)
|
|
21
|
+
.storeBuffer(c.hash(0))
|
|
22
|
+
.storeUint(c.depth(0), 16)
|
|
23
|
+
.storeRef(c));
|
|
24
|
+
}
|
|
25
|
+
function convertToPrunedBranch(c) {
|
|
26
|
+
return endExoticCell((0, core_1.beginCell)()
|
|
27
|
+
.storeUint(1, 8)
|
|
28
|
+
.storeUint(1, 8)
|
|
29
|
+
.storeBuffer(c.hash(0))
|
|
30
|
+
.storeUint(c.depth(0), 16));
|
|
31
|
+
}
|
|
32
|
+
function doGenerateMerkleProof(prefix, slice, n, keys) {
|
|
33
|
+
// Reading label
|
|
34
|
+
const originalCell = slice.asCell();
|
|
35
|
+
if (keys.length == 0) {
|
|
36
|
+
// no keys to prove, prune the whole subdict
|
|
37
|
+
return convertToPrunedBranch(originalCell);
|
|
38
|
+
}
|
|
39
|
+
let lb0 = slice.loadBit() ? 1 : 0;
|
|
40
|
+
let prefixLength = 0;
|
|
41
|
+
let pp = prefix;
|
|
42
|
+
if (lb0 === 0) {
|
|
43
|
+
// Short label detected
|
|
44
|
+
// Read
|
|
45
|
+
prefixLength = readUnaryLength(slice);
|
|
46
|
+
// Read prefix
|
|
47
|
+
for (let i = 0; i < prefixLength; i++) {
|
|
48
|
+
pp += slice.loadBit() ? '1' : '0';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
let lb1 = slice.loadBit() ? 1 : 0;
|
|
53
|
+
if (lb1 === 0) {
|
|
54
|
+
// Long label detected
|
|
55
|
+
prefixLength = slice.loadUint(Math.ceil(Math.log2(n + 1)));
|
|
56
|
+
for (let i = 0; i < prefixLength; i++) {
|
|
57
|
+
pp += slice.loadBit() ? '1' : '0';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// Same label detected
|
|
62
|
+
let bit = slice.loadBit() ? '1' : '0';
|
|
63
|
+
prefixLength = slice.loadUint(Math.ceil(Math.log2(n + 1)));
|
|
64
|
+
for (let i = 0; i < prefixLength; i++) {
|
|
65
|
+
pp += bit;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (n - prefixLength === 0) {
|
|
70
|
+
return originalCell;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
let sl = originalCell.beginParse();
|
|
74
|
+
let left = sl.loadRef();
|
|
75
|
+
let right = sl.loadRef();
|
|
76
|
+
// NOTE: Left and right branches are implicitly contain prefixes '0' and '1'
|
|
77
|
+
if (!left.isExotic) {
|
|
78
|
+
const leftKeys = keys.filter((key) => {
|
|
79
|
+
return pp + '0' === key.slice(0, pp.length + 1);
|
|
80
|
+
});
|
|
81
|
+
left = doGenerateMerkleProof(pp + '0', left.beginParse(), n - prefixLength - 1, leftKeys);
|
|
82
|
+
}
|
|
83
|
+
if (!right.isExotic) {
|
|
84
|
+
const rightKeys = keys.filter((key) => {
|
|
85
|
+
return pp + '1' === key.slice(0, pp.length + 1);
|
|
86
|
+
});
|
|
87
|
+
right = doGenerateMerkleProof(pp + '1', right.beginParse(), n - prefixLength - 1, rightKeys);
|
|
88
|
+
}
|
|
89
|
+
return (0, core_1.beginCell)()
|
|
90
|
+
.storeSlice(sl)
|
|
91
|
+
.storeRef(left)
|
|
92
|
+
.storeRef(right)
|
|
93
|
+
.endCell();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function generateMerkleProofDirect(dict, keys, keyObject) {
|
|
97
|
+
keys.forEach((key) => {
|
|
98
|
+
if (!dict.has(key)) {
|
|
99
|
+
throw new Error(`Trying to generate merkle proof for a missing key "${key}"`);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
const s = (0, core_1.beginCell)().storeDictDirect(dict).asSlice();
|
|
103
|
+
return doGenerateMerkleProof('', s, keyObject.bits, keys.map((key) => keyObject.serialize(key).toString(2).padStart(keyObject.bits, '0')));
|
|
104
|
+
}
|
|
105
|
+
function generateMerkleProof(dict, keys, keyObject) {
|
|
106
|
+
return convertToMerkleProof(generateMerkleProofDirect(dict, keys, keyObject));
|
|
107
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Cell, Dictionary, Slice } from "@ton/core";
|
|
2
|
+
import { PriceData, RawPriceData } from "../types/Common";
|
|
3
|
+
import { Oracle, PoolAssetsConfig } from "../types/Master";
|
|
4
|
+
import 'promise.any';
|
|
5
|
+
type OutputData = {
|
|
6
|
+
metadata: {
|
|
7
|
+
blockId: string;
|
|
8
|
+
transactionId: string;
|
|
9
|
+
outputIndex: number;
|
|
10
|
+
isSpent: boolean;
|
|
11
|
+
milestoneIndexSpent: number;
|
|
12
|
+
milestoneTimestampSpent: number;
|
|
13
|
+
transactionIdSpent: string;
|
|
14
|
+
milestoneIndexBooked: number;
|
|
15
|
+
milestoneTimestampBooked: number;
|
|
16
|
+
ledgerIndex: number;
|
|
17
|
+
};
|
|
18
|
+
output: {
|
|
19
|
+
type: number;
|
|
20
|
+
amount: string;
|
|
21
|
+
nftId: string;
|
|
22
|
+
unlockConditions: {
|
|
23
|
+
type: number;
|
|
24
|
+
address: {
|
|
25
|
+
type: number;
|
|
26
|
+
pubKeyHash: string;
|
|
27
|
+
};
|
|
28
|
+
}[];
|
|
29
|
+
features: {
|
|
30
|
+
type: number;
|
|
31
|
+
data: string;
|
|
32
|
+
}[];
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
export type OraclePricesData = {
|
|
36
|
+
timestamp: number;
|
|
37
|
+
prices: Dictionary<bigint, bigint>;
|
|
38
|
+
};
|
|
39
|
+
export declare function loadPrices(oracleNftId: String, endpoints: String[], timeout: number): Promise<OutputData>;
|
|
40
|
+
export declare function parsePrices(outputData: OutputData, oracleId: number): Promise<RawPriceData>;
|
|
41
|
+
export declare function verifyPrices(assets: PoolAssetsConfig): (priceData: RawPriceData) => boolean;
|
|
42
|
+
export declare function getMedianPrice(pricesData: PriceData[], asset: bigint): bigint;
|
|
43
|
+
export declare function packAssetsData(assetsData: {
|
|
44
|
+
assetId: bigint;
|
|
45
|
+
medianPrice: bigint;
|
|
46
|
+
}[]): Cell;
|
|
47
|
+
export declare function packPrices(assetsDataCell: Cell, oraclesDataCell: Cell): Cell;
|
|
48
|
+
export declare function createOracleDataProof(oracle: Oracle, data: OraclePricesData, signature: Buffer, assets: Array<bigint>): Slice;
|
|
49
|
+
export declare function packOraclesData(oraclesData: {
|
|
50
|
+
oracle: Oracle;
|
|
51
|
+
data: OraclePricesData;
|
|
52
|
+
signature: Buffer;
|
|
53
|
+
}[], assets: Array<bigint>): Cell;
|
|
54
|
+
export declare function sumDicts(result: Dictionary<bigint, bigint>, addendum: Dictionary<bigint, bigint>): void;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadPrices = loadPrices;
|
|
4
|
+
exports.parsePrices = parsePrices;
|
|
5
|
+
exports.verifyPrices = verifyPrices;
|
|
6
|
+
exports.getMedianPrice = getMedianPrice;
|
|
7
|
+
exports.packAssetsData = packAssetsData;
|
|
8
|
+
exports.packPrices = packPrices;
|
|
9
|
+
exports.createOracleDataProof = createOracleDataProof;
|
|
10
|
+
exports.packOraclesData = packOraclesData;
|
|
11
|
+
exports.sumDicts = sumDicts;
|
|
12
|
+
const core_1 = require("@ton/core");
|
|
13
|
+
const config_1 = require("../config");
|
|
14
|
+
const merkleProof_1 = require("./merkleProof");
|
|
15
|
+
require("promise.any");
|
|
16
|
+
async function loadPrices(oracleNftId, endpoints, timeout) {
|
|
17
|
+
return await Promise.any(endpoints.map(x => loadOracleData(oracleNftId, x, timeout)));
|
|
18
|
+
}
|
|
19
|
+
async function loadOracleData(oracleNftId, endpoint, timeout) {
|
|
20
|
+
let result = await fetch(`https://${endpoint}/api/indexer/v1/outputs/nft/${oracleNftId}`, {
|
|
21
|
+
headers: { accept: 'application/json' },
|
|
22
|
+
signal: AbortSignal.timeout(timeout)
|
|
23
|
+
});
|
|
24
|
+
let outputId = (await result.json());
|
|
25
|
+
result = await fetch(`https://${endpoint}/api/core/v2/outputs/${outputId.items[0]}`, {
|
|
26
|
+
headers: { accept: 'application/json' },
|
|
27
|
+
signal: AbortSignal.timeout(timeout)
|
|
28
|
+
});
|
|
29
|
+
return await result.json();
|
|
30
|
+
}
|
|
31
|
+
async function parsePrices(outputData, oracleId) {
|
|
32
|
+
const data = JSON.parse(decodeURIComponent(outputData.output.features[0].data.replace('0x', '').replace(/[0-9a-f]{2}/g, '%$&')));
|
|
33
|
+
try {
|
|
34
|
+
const pricesCell = core_1.Cell.fromBoc(Buffer.from(data['packedPrices'], 'hex'))[0];
|
|
35
|
+
const signature = Buffer.from(data['signature'], 'hex');
|
|
36
|
+
const publicKey = Buffer.from(data['publicKey'], 'hex');
|
|
37
|
+
const timestamp = Number(data['timestamp']);
|
|
38
|
+
return {
|
|
39
|
+
dict: pricesCell.beginParse().loadRef().beginParse().loadDictDirect(core_1.Dictionary.Keys.BigUint(256), core_1.Dictionary.Values.BigVarUint(4)),
|
|
40
|
+
dataCell: (0, core_1.beginCell)().storeRef(pricesCell).storeBuffer(signature).endCell(),
|
|
41
|
+
oracleId: oracleId,
|
|
42
|
+
signature: signature,
|
|
43
|
+
pubkey: publicKey,
|
|
44
|
+
timestamp: timestamp,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.log(oracleId, data, error);
|
|
49
|
+
throw Error();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function verifyPrices(assets) {
|
|
53
|
+
return function (priceData) {
|
|
54
|
+
const timestamp = Date.now() / 1000;
|
|
55
|
+
const pricesTime = priceData.timestamp;
|
|
56
|
+
for (const [key, asset] of Object.entries(assets)) {
|
|
57
|
+
if (!priceData.dict.has(asset.assetId)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// console.log('timestamp', timestamp, 'pricestime', pricesTime, timestamp - pricesTime);
|
|
62
|
+
return timestamp - pricesTime < config_1.TTL_ORACLE_DATA_SEC;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function getMedianPrice(pricesData, asset) {
|
|
66
|
+
const sorted = pricesData.map(x => x.dict.get(asset)).sort((a, b) => Number(a) - Number(b));
|
|
67
|
+
const mid = Math.floor(sorted.length / 2);
|
|
68
|
+
if (sorted.length % 2 === 0) {
|
|
69
|
+
return (sorted[mid - 1] + sorted[mid]) / 2n;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
return sorted[mid];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function packAssetsData(assetsData) {
|
|
76
|
+
if (assetsData.length == 0) {
|
|
77
|
+
throw new Error("No assets data to pack");
|
|
78
|
+
}
|
|
79
|
+
return assetsData.reduceRight((acc, { assetId, medianPrice }) => (0, core_1.beginCell)()
|
|
80
|
+
.storeUint(assetId, 256)
|
|
81
|
+
.storeCoins(medianPrice)
|
|
82
|
+
.storeMaybeRef(acc)
|
|
83
|
+
.endCell(), null);
|
|
84
|
+
}
|
|
85
|
+
function packPrices(assetsDataCell, oraclesDataCell) {
|
|
86
|
+
let pricesCell = (0, core_1.beginCell)()
|
|
87
|
+
.storeRef(assetsDataCell)
|
|
88
|
+
.storeRef(oraclesDataCell)
|
|
89
|
+
.endCell();
|
|
90
|
+
return pricesCell;
|
|
91
|
+
}
|
|
92
|
+
function createOracleDataProof(oracle, data, signature, assets) {
|
|
93
|
+
let prunedDict = (0, merkleProof_1.generateMerkleProofDirect)(data.prices, assets, core_1.Dictionary.Keys.BigUint(256));
|
|
94
|
+
let prunedData = (0, core_1.beginCell)().storeUint(data.timestamp, 32).storeMaybeRef(prunedDict).endCell();
|
|
95
|
+
let merkleProof = (0, merkleProof_1.convertToMerkleProof)(prunedData);
|
|
96
|
+
let oracleDataProof = (0, core_1.beginCell)().storeUint(oracle.id, 32).storeRef(merkleProof).storeBuffer(signature).asSlice();
|
|
97
|
+
return oracleDataProof;
|
|
98
|
+
}
|
|
99
|
+
function packOraclesData(oraclesData, assets) {
|
|
100
|
+
if (oraclesData.length == 0) {
|
|
101
|
+
throw new Error("no oracles data to pack");
|
|
102
|
+
}
|
|
103
|
+
let proofs = oraclesData.sort((d1, d2) => d1.oracle.id - d2.oracle.id).map(({ oracle, data, signature }) => createOracleDataProof(oracle, data, signature, assets));
|
|
104
|
+
return proofs.reduceRight((acc, val) => (0, core_1.beginCell)().storeSlice(val).storeMaybeRef(acc).endCell(), null);
|
|
105
|
+
}
|
|
106
|
+
// : String = "api.stardust-mainnet.iotaledger.net"
|
|
107
|
+
function sumDicts(result, addendum) {
|
|
108
|
+
for (const key of addendum.keys()) {
|
|
109
|
+
const current = result.get(key);
|
|
110
|
+
const value = addendum.get(key);
|
|
111
|
+
if (current === undefined) {
|
|
112
|
+
result.set(key, value);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
result.set(key, current + value);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -3,11 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.sha256Hash =
|
|
6
|
+
exports.sha256Hash = sha256Hash;
|
|
7
7
|
const sha256_1 = __importDefault(require("crypto-js/sha256"));
|
|
8
8
|
function sha256Hash(input) {
|
|
9
9
|
const hash = (0, sha256_1.default)(input);
|
|
10
10
|
const hashHex = hash.toString();
|
|
11
11
|
return BigInt('0x' + hashHex);
|
|
12
12
|
}
|
|
13
|
-
exports.sha256Hash = sha256Hash;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getLastSentBoc = getLastSentBoc;
|
|
4
|
+
exports.getTonConnectSender = getTonConnectSender;
|
|
4
5
|
const core_1 = require("@ton/core");
|
|
5
6
|
/*
|
|
6
7
|
This is not the best solution to get the BOC of the sent external message, however the Sender
|
|
@@ -11,7 +12,6 @@ let lastSentBoc;
|
|
|
11
12
|
function getLastSentBoc() {
|
|
12
13
|
return lastSentBoc;
|
|
13
14
|
}
|
|
14
|
-
exports.getLastSentBoc = getLastSentBoc;
|
|
15
15
|
function getTonConnectSender(connector) {
|
|
16
16
|
return {
|
|
17
17
|
get address() {
|
|
@@ -34,4 +34,3 @@ function getTonConnectSender(connector) {
|
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
-
exports.getTonConnectSender = getTonConnectSender;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Address } from '@ton/core';
|
|
2
|
-
import { PoolAssetConfig
|
|
3
|
-
export declare function getUserJettonWallet(ownerAddress: Address, poolAssetConfig: PoolAssetConfig
|
|
2
|
+
import { PoolAssetConfig } from '../types/Master';
|
|
3
|
+
export declare function getUserJettonWallet(ownerAddress: Address, poolAssetConfig: PoolAssetConfig): Address;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getUserJettonWallet =
|
|
3
|
+
exports.getUserJettonWallet = getUserJettonWallet;
|
|
4
4
|
const core_1 = require("@ton/core");
|
|
5
|
+
const assets_1 = require("../constants/assets");
|
|
5
6
|
function getUserJettonWallet(ownerAddress, poolAssetConfig) {
|
|
7
|
+
if (poolAssetConfig.name == 'TON' || poolAssetConfig.assetId === assets_1.UNDEFINED_ASSET.assetId) {
|
|
8
|
+
throw new Error(`Cant getUserJettonWallet for ${poolAssetConfig.name} asset`);
|
|
9
|
+
}
|
|
6
10
|
const jettonMasterAddress = poolAssetConfig.jettonMasterAddress;
|
|
7
11
|
const jettonWalletCode = poolAssetConfig.jettonWalletCode;
|
|
8
12
|
if (poolAssetConfig.name === 'USDT') {
|
|
@@ -55,4 +59,3 @@ function getUserJettonWallet(ownerAddress, poolAssetConfig) {
|
|
|
55
59
|
.endCell();
|
|
56
60
|
return new core_1.Address(0, stateInit.hash());
|
|
57
61
|
}
|
|
58
|
-
exports.getUserJettonWallet = getUserJettonWallet;
|
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evaafi/sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "SDK for EVAA contracts",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
-
"types": "src/index.ts",
|
|
7
6
|
"files": [
|
|
8
7
|
"dist",
|
|
9
8
|
"src"
|
|
@@ -21,7 +20,7 @@
|
|
|
21
20
|
"devDependencies": {
|
|
22
21
|
"@orbs-network/ton-access": "^2.3.3",
|
|
23
22
|
"@ton/core": "0.56.0",
|
|
24
|
-
"@tonconnect/sdk": "^3.0.
|
|
23
|
+
"@tonconnect/sdk": "^3.0.5",
|
|
25
24
|
"@types/jest": "^29.5.12",
|
|
26
25
|
"@types/node": "^20.10.4",
|
|
27
26
|
"crypto-js": "^4.2.0",
|
package/src/api/math.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { AssetConfig, AssetData, AssetInterest, ExtendedAssetData, ExtendedAssetsConfig, ExtendedAssetsData, MasterConstants } from '../types/Master';
|
|
1
|
+
import { AgregatedBalances, AssetConfig, AssetData, AssetInterest, ExtendedAssetData, ExtendedAssetsConfig, ExtendedAssetsData, MasterConstants, PoolConfig } from '../types/Master';
|
|
2
2
|
import { Dictionary } from '@ton/core';
|
|
3
3
|
import { BalanceChangeType, BalanceType, LiquidationData, PredictHealthFactorArgs, UserBalance } from '../types/User';
|
|
4
4
|
import { sha256Hash } from '../utils/sha256BigInt';
|
|
5
|
+
import { TON_MAINNET, UNDEFINED_ASSET } from '../constants/assets';
|
|
6
|
+
import { MAINNET_POOL_CONFIG, TESTNET_POOL_CONFIG } from '..';
|
|
5
7
|
|
|
6
8
|
export function mulFactor(decimal: bigint, a: bigint, b: bigint): bigint {
|
|
7
9
|
return (a * b) / decimal;
|
|
@@ -125,13 +127,111 @@ export function checkNotInDebtAtAll(principals: Dictionary<bigint, bigint>): boo
|
|
|
125
127
|
return principals.values().every(x => x >= 0n);
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
export function
|
|
130
|
+
export function getAgregatedBalances (
|
|
131
|
+
assetsData: ExtendedAssetsData,
|
|
132
|
+
assetsConfig: ExtendedAssetsConfig,
|
|
133
|
+
principals: Dictionary<bigint, bigint>,
|
|
134
|
+
prices: Dictionary<bigint, bigint>,
|
|
135
|
+
masterConstants: MasterConstants,
|
|
136
|
+
): AgregatedBalances {
|
|
137
|
+
let user_total_supply = 0n;
|
|
138
|
+
let user_total_borrow = 0n;
|
|
139
|
+
|
|
140
|
+
for (const [assetId, principal] of principals) {
|
|
141
|
+
|
|
142
|
+
if (principal) {
|
|
143
|
+
|
|
144
|
+
if (!prices.has(assetId)) {
|
|
145
|
+
return {totalSupply: 0n, totalBorrow: 0n};
|
|
146
|
+
}
|
|
147
|
+
const price = prices.get(assetId)!;
|
|
148
|
+
const assetData = assetsData.get(assetId)!;
|
|
149
|
+
const assetConfig = assetsConfig.get(assetId)!;
|
|
150
|
+
|
|
151
|
+
if (principal < 0) {
|
|
152
|
+
user_total_borrow += presentValue(assetData.sRate, assetData.bRate, principal, masterConstants).amount * price / 10n ** assetConfig.decimals;
|
|
153
|
+
} else {
|
|
154
|
+
user_total_supply += presentValue(assetData.sRate, assetData.bRate, principal, masterConstants).amount * price / 10n ** assetConfig.decimals;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return {totalSupply: user_total_supply, totalBorrow: user_total_borrow};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @deprecated The method should be used only for main contract v5
|
|
164
|
+
*/
|
|
165
|
+
export function isV5MainPoolContract(poolConfig: PoolConfig): boolean {
|
|
166
|
+
if ((poolConfig.masterAddress == MAINNET_POOL_CONFIG.masterAddress && poolConfig.masterVersion == 5) ||
|
|
167
|
+
(poolConfig.masterAddress == TESTNET_POOL_CONFIG.masterAddress && poolConfig.masterVersion == 5)) {
|
|
168
|
+
return true;
|
|
169
|
+
} else {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @deprecated The method should be used only for main contract v5
|
|
176
|
+
*/
|
|
177
|
+
export function calculateMaximumWithdrawAmountOld(
|
|
129
178
|
assetsConfig: ExtendedAssetsConfig,
|
|
130
179
|
assetsData: ExtendedAssetsData,
|
|
131
180
|
principals: Dictionary<bigint, bigint>,
|
|
132
181
|
prices: Dictionary<bigint, bigint>,
|
|
133
182
|
masterConstants: MasterConstants,
|
|
134
|
-
assetId: bigint
|
|
183
|
+
assetId: bigint,
|
|
184
|
+
): bigint {
|
|
185
|
+
let withdrawAmountMax = 0n;
|
|
186
|
+
|
|
187
|
+
const assetConfig = assetsConfig.get(assetId) as AssetConfig;
|
|
188
|
+
const assetData = assetsData.get(assetId) as ExtendedAssetData;
|
|
189
|
+
const oldPrincipal = principals.get(assetId) as bigint;
|
|
190
|
+
const oldPresentValue = presentValue(assetData.sRate, assetData.bRate, oldPrincipal, masterConstants);
|
|
191
|
+
|
|
192
|
+
if (oldPresentValue.amount > assetConfig.dust) {
|
|
193
|
+
if(checkNotInDebtAtAll(principals)) {
|
|
194
|
+
withdrawAmountMax = oldPresentValue.amount;
|
|
195
|
+
} else {
|
|
196
|
+
if (!prices.has(assetId)) {
|
|
197
|
+
return 0n;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
//const borrowable = getAvailableToBorrow(assetsConfig, assetsData, principals, prices, masterConstants);
|
|
201
|
+
const price = prices.get(assetId) as bigint;
|
|
202
|
+
const agregatedBalances = getAgregatedBalances(assetsData, assetsConfig, principals, principals, masterConstants);
|
|
203
|
+
let maxAmountToReclaim =
|
|
204
|
+
mulDiv(
|
|
205
|
+
agregatedBalances.totalSupply - mulDivC(agregatedBalances.totalBorrow, masterConstants.ASSET_COEFFICIENT_SCALE, assetConfig.collateralFactor),
|
|
206
|
+
10n**assetConfig.decimals,
|
|
207
|
+
price
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
withdrawAmountMax = bigIntMin(
|
|
211
|
+
maxAmountToReclaim,
|
|
212
|
+
oldPresentValue.amount
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
if (!prices.has(assetId)) {
|
|
217
|
+
return 0n;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const price = prices.get(assetId) as bigint;
|
|
221
|
+
|
|
222
|
+
return getAvailableToBorrow(assetsConfig, assetsData, principals, prices, masterConstants) * (10n ** assetConfig.decimals) / price;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return withdrawAmountMax;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function calculateMaximumWithdrawAmount( // todo v6 ifelse dust not debt at all is fixed?
|
|
229
|
+
assetsConfig: ExtendedAssetsConfig,
|
|
230
|
+
assetsData: ExtendedAssetsData,
|
|
231
|
+
principals: Dictionary<bigint, bigint>,
|
|
232
|
+
prices: Dictionary<bigint, bigint>,
|
|
233
|
+
masterConstants: MasterConstants,
|
|
234
|
+
assetId: bigint,
|
|
135
235
|
): bigint {
|
|
136
236
|
let withdrawAmountMax = 0n;
|
|
137
237
|
|
|
@@ -152,6 +252,7 @@ export function calculateMaximumWithdrawAmount(
|
|
|
152
252
|
const price = prices.get(assetId) as bigint;
|
|
153
253
|
|
|
154
254
|
let maxAmountToReclaim = 0n;
|
|
255
|
+
|
|
155
256
|
if (assetConfig.collateralFactor == 0n) {
|
|
156
257
|
maxAmountToReclaim = oldPresentValue.amount;
|
|
157
258
|
}
|
|
@@ -230,74 +331,86 @@ export function presentValue(sRate: bigint, bRate: bigint, principalValue: bigin
|
|
|
230
331
|
}
|
|
231
332
|
}
|
|
232
333
|
|
|
334
|
+
/**
|
|
335
|
+
*
|
|
336
|
+
* @param assetsConfig
|
|
337
|
+
* @param assetsData
|
|
338
|
+
* @param principals
|
|
339
|
+
* @param prices
|
|
340
|
+
* @param poolConfig
|
|
341
|
+
* @returns can return UNDEFINED_ASSET if there are no assets
|
|
342
|
+
*/
|
|
233
343
|
export function calculateLiquidationData(
|
|
234
344
|
assetsConfig: ExtendedAssetsConfig,
|
|
235
345
|
assetsData: ExtendedAssetsData,
|
|
236
346
|
principals: Dictionary<bigint, bigint>,
|
|
237
347
|
prices: Dictionary<bigint, bigint>,
|
|
238
|
-
|
|
348
|
+
poolConfig: PoolConfig,
|
|
239
349
|
): LiquidationData {
|
|
240
|
-
let
|
|
241
|
-
let
|
|
242
|
-
let
|
|
243
|
-
let
|
|
350
|
+
let collateralValue = 0n;
|
|
351
|
+
let collateralAsset = UNDEFINED_ASSET;
|
|
352
|
+
let loanValue = 0n;
|
|
353
|
+
let loanAsset = UNDEFINED_ASSET;
|
|
244
354
|
let totalDebt = 0n;
|
|
245
355
|
let totalLimit = 0n;
|
|
246
356
|
|
|
247
|
-
for (const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
357
|
+
for (const asset of poolConfig.poolAssetsConfig) {
|
|
358
|
+
if (!principals.has(asset.assetId)) {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const principal = principals.get(asset.assetId)!;
|
|
362
|
+
const assetConfig = assetsConfig.get(asset.assetId)!;
|
|
363
|
+
const assetData = assetsData.get(asset.assetId)!;
|
|
251
364
|
const balance =
|
|
252
365
|
principal > 0 ? (principal * assetData.sRate) / BigInt(1e12) : (principal * assetData.bRate) / BigInt(1e12);
|
|
253
366
|
if (balance > 0) {
|
|
254
|
-
const assetWorth = (balance * prices.get(
|
|
255
|
-
totalLimit += (assetWorth * assetConfig.liquidationThreshold) / masterConstants.ASSET_COEFFICIENT_SCALE;
|
|
256
|
-
if (assetWorth >
|
|
257
|
-
|
|
258
|
-
|
|
367
|
+
const assetWorth = (balance * prices.get(asset.assetId)!) / 10n ** assetConfig.decimals;
|
|
368
|
+
totalLimit += (assetWorth * assetConfig.liquidationThreshold) / poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE;
|
|
369
|
+
if (assetWorth > collateralValue) {
|
|
370
|
+
collateralValue = assetWorth;
|
|
371
|
+
collateralAsset = asset;
|
|
259
372
|
}
|
|
260
373
|
} else if (balance < 0) {
|
|
261
|
-
const assetWorth = (-balance * prices.get(
|
|
374
|
+
const assetWorth = (-balance * prices.get(asset.assetId)!) / 10n ** assetConfig.decimals;
|
|
262
375
|
totalDebt += assetWorth;
|
|
263
|
-
if (assetWorth >
|
|
264
|
-
|
|
265
|
-
|
|
376
|
+
if (assetWorth > loanValue) {
|
|
377
|
+
loanValue = assetWorth;
|
|
378
|
+
loanAsset = asset;
|
|
266
379
|
}
|
|
267
380
|
}
|
|
268
381
|
}
|
|
269
382
|
|
|
270
|
-
if (totalLimit < totalDebt) {
|
|
271
|
-
const
|
|
383
|
+
if (collateralAsset.assetId !== UNDEFINED_ASSET.assetId && totalLimit < totalDebt) {
|
|
384
|
+
const loanAssetPrice = prices.get(loanAsset.assetId)!;
|
|
272
385
|
const values: bigint[] = [];
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
const liquidationBonus =
|
|
276
|
-
const
|
|
386
|
+
const collateralAssetConfig = assetsConfig.get(collateralAsset.assetId)!;
|
|
387
|
+
const loanAssetConfig = assetsConfig.get(loanAsset.assetId)!;
|
|
388
|
+
const liquidationBonus = collateralAssetConfig.liquidationBonus;
|
|
389
|
+
const loanScale = 10n ** loanAssetConfig.decimals;
|
|
277
390
|
values.push(
|
|
278
|
-
(bigIntMax(
|
|
279
|
-
|
|
280
|
-
masterConstants.ASSET_COEFFICIENT_SCALE) /
|
|
391
|
+
(bigIntMax(collateralValue / 2n, bigIntMin(collateralValue, 100_000_000_000n)) *
|
|
392
|
+
loanScale *
|
|
393
|
+
poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE) /
|
|
281
394
|
liquidationBonus /
|
|
282
|
-
|
|
395
|
+
loanAssetPrice,
|
|
283
396
|
);
|
|
284
|
-
values.push((
|
|
397
|
+
values.push((loanValue * loanScale) / loanAssetPrice);
|
|
285
398
|
|
|
286
399
|
const liquidationAmount = (bigIntMin(...values) as bigint) - 5n;
|
|
287
|
-
const
|
|
288
|
-
const collateralDecimal = 10n **
|
|
400
|
+
const collateralAssetPrice: bigint = prices.get(collateralAsset.assetId)!;
|
|
401
|
+
const collateralDecimal = 10n ** collateralAssetConfig.decimals;
|
|
289
402
|
let minCollateralAmount =
|
|
290
|
-
(((liquidationAmount *
|
|
291
|
-
|
|
292
|
-
|
|
403
|
+
(((liquidationAmount * loanAssetPrice * liquidationBonus) / poolConfig.masterConstants.ASSET_LIQUIDATION_BONUS_SCALE) * collateralDecimal) /
|
|
404
|
+
collateralAssetPrice /
|
|
405
|
+
loanScale -
|
|
293
406
|
10n;
|
|
294
407
|
minCollateralAmount = (minCollateralAmount * 97n) / 100n;
|
|
295
408
|
if (minCollateralAmount / collateralDecimal >= 1n) {
|
|
296
409
|
return {
|
|
297
|
-
greatestCollateralAsset:
|
|
298
|
-
greatestCollateralValue:
|
|
299
|
-
greatestLoanAsset:
|
|
300
|
-
greatestLoanValue:
|
|
410
|
+
greatestCollateralAsset: collateralAsset,
|
|
411
|
+
greatestCollateralValue: collateralValue,
|
|
412
|
+
greatestLoanAsset: loanAsset,
|
|
413
|
+
greatestLoanValue: loanValue,
|
|
301
414
|
totalDebt,
|
|
302
415
|
totalLimit,
|
|
303
416
|
liquidable: true,
|
|
@@ -308,10 +421,10 @@ export function calculateLiquidationData(
|
|
|
308
421
|
}
|
|
309
422
|
|
|
310
423
|
return {
|
|
311
|
-
greatestCollateralAsset:
|
|
312
|
-
greatestCollateralValue:
|
|
313
|
-
greatestLoanAsset:
|
|
314
|
-
greatestLoanValue:
|
|
424
|
+
greatestCollateralAsset: collateralAsset,
|
|
425
|
+
greatestCollateralValue: collateralValue,
|
|
426
|
+
greatestLoanAsset: loanAsset,
|
|
427
|
+
greatestLoanValue: loanValue,
|
|
315
428
|
totalDebt,
|
|
316
429
|
totalLimit,
|
|
317
430
|
liquidable: false,
|
|
@@ -319,7 +432,7 @@ export function calculateLiquidationData(
|
|
|
319
432
|
}
|
|
320
433
|
|
|
321
434
|
export function predictHealthFactor(args: PredictHealthFactorArgs): number {
|
|
322
|
-
const liquidationData = calculateLiquidationData(args.assetsConfig, args.assetsData, args.
|
|
435
|
+
const liquidationData = calculateLiquidationData(args.assetsConfig, args.assetsData, args.principals, args.prices, args.poolConfig);
|
|
323
436
|
const tokenHash = sha256Hash(args.tokenSymbol);
|
|
324
437
|
|
|
325
438
|
const assetConfig = args.assetsConfig.get(tokenHash)!;
|
|
@@ -337,13 +450,13 @@ export function predictHealthFactor(args: PredictHealthFactorArgs): number {
|
|
|
337
450
|
|
|
338
451
|
if (currentAmount != null && currentAmount != 0n) {
|
|
339
452
|
if (changeType == BalanceChangeType.Borrow) {
|
|
340
|
-
totalBorrow += currentBalance * (1 + Number(assetConfig.originationFee) / Number(args.masterConstants.ASSET_ORIGINATION_FEE_SCALE));
|
|
453
|
+
totalBorrow += currentBalance * (1 + Number(assetConfig.originationFee) / Number(args.poolConfig.masterConstants.ASSET_ORIGINATION_FEE_SCALE));
|
|
341
454
|
} else if (changeType == BalanceChangeType.Repay) {
|
|
342
455
|
totalBorrow -= currentBalance;
|
|
343
456
|
} else if (changeType == BalanceChangeType.Withdraw) {
|
|
344
|
-
totalLimit -= currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
457
|
+
totalLimit -= currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
345
458
|
} else if (changeType == BalanceChangeType.Supply) {
|
|
346
|
-
totalLimit += currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
459
|
+
totalLimit += currentBalance * Number(assetConfig.liquidationThreshold) / Number(args.poolConfig.masterConstants.ASSET_COEFFICIENT_SCALE);
|
|
347
460
|
}
|
|
348
461
|
}
|
|
349
462
|
if (Number(totalLimit) == 0) {
|