Skip to content

Commit 189aa04

Browse files
authored
[virtue]: tvl stats (#15473)
1 parent b967999 commit 189aa04

File tree

3 files changed

+179
-0
lines changed

3 files changed

+179
-0
lines changed

projects/helper/chain/iota.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ const sdk = require('@defillama/sdk')
33

44
const http = require('../http')
55
const { getEnv } = require('../env')
6+
const { sliceIntoChunks } = require('../utils')
7+
68

79
const endpoint = () => getEnv('IOTA_RPC')
810

@@ -14,6 +16,25 @@ async function getObject(objectId) {
1416
}])).content
1517
}
1618

19+
async function getObjects(objectIds) {
20+
if (objectIds.length > 9) {
21+
const chunks = sliceIntoChunks(objectIds, 9)
22+
const res = []
23+
for (const chunk of chunks) res.push(...(await getObjects(chunk)))
24+
return res
25+
}
26+
const {
27+
result
28+
} = await http.post(endpoint(), {
29+
jsonrpc: "2.0", id: 1, method: 'iota_multiGetObjects', params: [objectIds, {
30+
"showType": true,
31+
"showOwner": true,
32+
"showContent": true,
33+
}],
34+
})
35+
return objectIds.map(i => result.find(j => j.data?.objectId === i)?.data?.content)
36+
}
37+
1738
async function call(method, params, { withMetadata = false } = {}) {
1839
if (!Array.isArray(params)) params = [params]
1940
const {
@@ -28,4 +49,5 @@ module.exports = {
2849
endpoint: endpoint(),
2950
call,
3051
getObject,
52+
getObjects
3153
};

projects/virtue/index.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const iota = require('../helper/chain/iota')
2+
const {getAllVaults} = require("./utils")
3+
4+
const COIN_TYPES= {
5+
IOTA: "0x2::iota::IOTA",
6+
stIOTA:
7+
"0x346778989a9f57480ec3fee15f2cd68409c73a62112d40a3efd13987997be68c::cert::CERT",
8+
VUSD: "0xd3b63e603a78786facf65ff22e79701f3e824881a12fa3268d62a75530fe904f::vusd::VUSD",
9+
};
10+
11+
async function getStIOTARatio() {
12+
const nativePool = await iota.getObject('0x02d641d7b021b1cd7a2c361ac35b415ae8263be0641f9475ec32af4b9d8a8056');
13+
const stIOTAMetadata = await iota.getObject('0x8c25ec843c12fbfddc7e25d66869f8639e20021758cac1a3db0f6de3c9fda2ed');
14+
15+
const stIOTATotalSupply = BigInt(stIOTAMetadata.fields.total_supply.fields.value) / BigInt(10 ** 9)
16+
const stIOTATotalStaked = BigInt(nativePool.fields.total_staked) / BigInt(10 ** 9)
17+
const stIOTATotalRewards = BigInt(nativePool.fields.total_rewards) / BigInt(10 ** 9)
18+
const tvl = stIOTATotalStaked + stIOTATotalRewards
19+
20+
return Number(stIOTATotalSupply) / Number(tvl)
21+
}
22+
23+
async function tvl(api) {
24+
const vaults = await getAllVaults()
25+
const stIOTARatio = await getStIOTARatio()
26+
Object.values(vaults).forEach(async (vault)=>{
27+
const balanceAmount = Number(vault.collateralBalance)
28+
const symbol = vault.token
29+
if(symbol === 'IOTA'){
30+
api.add(COIN_TYPES['IOTA'], balanceAmount)
31+
}
32+
if(symbol === 'stIOTA'){
33+
api.add(COIN_TYPES['IOTA'], balanceAmount/stIOTARatio)
34+
}
35+
36+
})
37+
}
38+
39+
40+
module.exports = {
41+
timetravel: false,
42+
iota: {
43+
tvl,
44+
}
45+
}

projects/virtue/utils.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
const iota = require('../helper/chain/iota');
2+
3+
const VAULT_MAP = {
4+
IOTA: {
5+
priceAggregater: {
6+
objectId: "0x052c40b4e8f16df5238457f3a7b3b0eeaa49c6bc8acc22f6a7790ab32495b2c6",
7+
mutable: false,
8+
initialSharedVersion: 22329880,
9+
},
10+
vault: {
11+
objectId: "0xaf306be8419cf059642acdba3b4e79a5ae893101ae62c8331cefede779ef48d5",
12+
mutable: true,
13+
initialSharedVersion: 22329895,
14+
},
15+
pythPriceId: "0xc7b72e5d860034288c9335d4d325da4272fe50c92ab72249d58f6cbba30e4c44",
16+
},
17+
stIOTA: {
18+
priceAggregater: {
19+
objectId: "0x8c730f64aa369eed69ddf7eea39c78bf0afd3f9fbb4ee0dfe457f6dea5a0f4ed",
20+
mutable: false,
21+
initialSharedVersion: 22329881,
22+
},
23+
vault: {
24+
objectId: "0xc9cb494657425f350af0948b8509efdd621626922e9337fd65eb161ec33de259",
25+
mutable: true,
26+
initialSharedVersion: 22329896,
27+
},
28+
},
29+
};
30+
31+
const formatUnits = (value, decimals) => {
32+
let display = value.toString();
33+
const negative = display.startsWith("-");
34+
if (negative) display = display.slice(1);
35+
display = display.padStart(decimals, "0");
36+
const integer = display.slice(0, display.length - decimals);
37+
let fraction = display.slice(display.length - decimals);
38+
fraction = fraction.replace(/(0+)$/, "");
39+
return `${negative ? "-" : ""}${integer || "0"}${fraction ? `.${fraction}` : ""}`;
40+
};
41+
42+
const formatBigInt = (value, decimals = 9) => {
43+
const formatted = formatUnits(BigInt(value), decimals);
44+
return Number(formatted);
45+
};
46+
47+
function getIotaObjectData(resp) {
48+
return resp.data;
49+
}
50+
51+
function isIotaObjectDataWithContent(
52+
data,
53+
) {
54+
return data.content !== undefined;
55+
}
56+
57+
function getMoveObject(data) {
58+
const obj = "data" in data ? getIotaObjectData(data) : data;
59+
if (!obj || !isIotaObjectDataWithContent(obj) || obj.content.dataType !== "moveObject") {
60+
return undefined;
61+
}
62+
return obj.content;
63+
}
64+
65+
function getObjectFields(resp) {
66+
if ("fields" in resp) {
67+
return resp.fields;
68+
}
69+
return getMoveObject(resp)?.fields;
70+
}
71+
72+
const parseVaultObject = (coinSymbol, fields) => {
73+
return {
74+
token: coinSymbol,
75+
positionTableSize: fields.position_table.fields.size,
76+
collateralDecimal: Number(fields.decimal),
77+
collateralBalance: fields.balance,
78+
supply: fields.limited_supply.fields.supply,
79+
maxSupply: fields.limited_supply.fields.limit,
80+
interestRate: formatBigInt(fields.interest_rate.fields.value, 18),
81+
minCollateralRatio: formatBigInt(fields.min_collateral_ratio.fields.value),
82+
};
83+
};
84+
85+
async function getAllVaults() {
86+
const vaultObjectIds = Object.values(VAULT_MAP).map((v) => v.vault.objectId);
87+
88+
const vaultResults = await iota.getObjects(vaultObjectIds);
89+
const vaults = vaultResults.reduce((acc, res) => {
90+
const fields = getObjectFields(res);
91+
const token = Object.keys(VAULT_MAP).find(
92+
(key) => VAULT_MAP[key].vault.objectId === res?.fields.id.id,
93+
);
94+
if (!token) return acc;
95+
const vault = parseVaultObject(token, fields);
96+
acc[vault.token] = vault;
97+
return acc;
98+
}, {});
99+
100+
return vaults;
101+
}
102+
103+
module.exports = {
104+
VAULT_MAP,
105+
formatUnits,
106+
formatBigInt,
107+
getIotaObjectData,
108+
getMoveObject,
109+
getObjectFields,
110+
parseVaultObject,
111+
getAllVaults,
112+
};

0 commit comments

Comments
 (0)