diff --git a/projects/helper/chains.json b/projects/helper/chains.json index 5fe8d95cb3..3e8a425875 100644 --- a/projects/helper/chains.json +++ b/projects/helper/chains.json @@ -141,6 +141,7 @@ "icon", "icp", "imx", + "ist", "inevm", "injective", "interlay", diff --git a/projects/inter-protocol/README.md b/projects/inter-protocol/README.md new file mode 100644 index 0000000000..c2f12ec9ba --- /dev/null +++ b/projects/inter-protocol/README.md @@ -0,0 +1,84 @@ +# Inter Protocol Adapter + +Run with this command, from the root directory: +``` +node test.js projects/inter-protocol/index.js +``` + +# Approach 1: functions overview (deprecated) +- getCoinDecimals: returns the numbr of decimals for a givn denomination. IST uses 1e6 decimals. +- fetchISTData: fetches the total supply of IST and returns it in a format compatble with defillama. uses the cosmos directory api to get the supply data and processes it. +- fetchISTDataWithUSD: fetches the total suply of IST, converts it to usd value using coingecko api, and returns it in a format compatible with defillama. note: this function is not used by default. +- fetchVstorageData: fetches data from vstorage using rpc. sends a request to the local vstorage endpoint and processes the response. +- fetchReserveData: fetches reserve data from vstorage. parses the reserve metrics from the response to get the reserve allocation fee value. +- fetchPSMData: fetches psm data from vstorage for all asset types. iterates over each asset type, fetches their metrics, and sums up the psm values. +- fetchVaultData: fetches vault data from vstorage and calculates the total locked value in usd. collects unique collateral types, fetches their prices from coingecko, and calculates the total usd value of the locked collateral. +- fetchTotalTVL: calculates total tvl (total value locked) including reserves, psm, vaults, and IST supply. sums up the values fetched by the other functions to get the total tvl. + +## logic + +for ```fetchISTData```, we are pulling the total IST supply from the cosmos directory. the endpoint we’re hitting is https://rest.cosmos.directory/agoric/cosmos/bank/v1beta1/supply/by_denom?denom=uist. we process this to get the amount of IST in circulation. (May not need this as it might be redundant..?) + +for ```fetchReserveData```, this one’s for grabbing reserve metrics. we hit the path /custom/vstorage/data/published.reserve.metrics. the data we get back includes some marshaled bigints. we parse this to get the fee allocation value. + +for ```fetchPSMData```, we first get the different asset types from /custom/vstorage/children/published.psm.IST. then, for each asset type, we fetch its metrics from /custom/vstorage/data/published.psm.IST.${assetType}.metrics. we parse these to sum up the anchor pool balances. + +for ```fetchVaultData```, we start by fetching the vault managers from /custom/vstorage/children/published.vaultFactory.managers. then, for each manager, we get the vault data from + +## Updated Example Output +Taking into account reserve, vaults, and psm + +``` +IST Data: { 'inter-stable-token': 1334769.86126 } +Reserve Data: 155816555 +PSM Data: 30010011 +Vault Data: 31960.239999999998 +--- ist --- +IST 185.86 M +Total: 185.86 M + +--- tvl --- +IST 185.86 M +Total: 185.86 M + +------ TVL ------ +ist 185.86 M + +total 185.86 M +``` + +# Approach 2: Subquery approach + +This approach simply makes graphql queries to our aubquery indexer + +## logic + +for ```fetchReserveData```, we fetch reserve metrics data from the subquery endpoint. we get the reserve allocations and calculate the total reserve value. + +for ```fetchPSMData```, we fetch PSM metrics data from the subquery endpoint. we get the minted pool balances for all asset types and calculate the total PSM value. + +for ```fetchVaultData```, we fetch vault manager metrics data from the subquery endpoint. we get the total collateral locked in the vaults and calculate its value. + +for ```fetchTotalCollateral```, we fetch the total collateral and oracle prices from the subquery endpoint. for each collateral brand, we get its total collateral value and usd price from the oracle prices. we also get the decimal places for each collateral brand from board aux data. we calculate the usd value by multiplying the collateral amount by its price and dividing by its decimal places. finally, we sum up the usd values for all collateral types (need to sanity check this) + +## example output + +``` +IST Data: { 'inter-stable-token': 1324886.823845 } +Reserve Data: 88840.683426 +PSM Data: 5475.886243 +Vault Data: 1981257.5781 +Total Collat: 57157332.45712694 +--- ist --- +IST 56.99 M +Total: 56.99 M + +--- tvl --- +IST 56.99 M +Total: 56.99 M + +------ TVL ------ +ist 56.99 M + +total 56.99 M +``` \ No newline at end of file diff --git a/projects/inter-protocol/index.js b/projects/inter-protocol/index.js new file mode 100644 index 0000000000..631cd535ab --- /dev/null +++ b/projects/inter-protocol/index.js @@ -0,0 +1,276 @@ +const sdk = require("@defillama/sdk"); +const axios = require("axios"); + +// note: added agoric to projects/helper/chains.json +const agoric = { + chainId: "agoric-3", + denom: "uist", + coinGeckoId: "inter-stable-token", +}; + +/* +@name delay +@description throttle rpc calls +@param ms - milliseconds to delay +*/ +const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); + +/* +@name getCoinDecimals +@description returns the number of decimals for the given denomination +@param denom - the denomination to get decimals for +*/ +const getCoinDecimals = (denom) => 1e6; // IST uses 1e6 + +/* +@name fetchSubqueryData +@description fetches data from the Agoric Subquery Indexer +@param query - the GraphQL query to execute +*/ +const fetchSubqueryData = async (query) => { + const url = 'https://api.subquery.network/sq/agoric-labs/agoric-mainnet-v2'; + try { + const response = await axios.post(url, { query }, { + headers: { "Content-Type": "application/json" } + }); + + if (response.data.errors) { + throw new Error(response.data.errors.map(e => e.message).join(", ")); + } + + return response.data.data; + } catch (error) { + console.error('Error fetching data from Subquery:', error); + throw error; + } +} + +/* +@name fetchISTData +@description fetches the total supply of IST and returns it in a format compatble with defillama +*/ +const fetchISTData = async () => { + // from https://github.com/DefiLlama/peggedassets-server/pull/292 + const url = "https://rest.cosmos.directory/agoric/cosmos/bank/v1beta1/supply/by_denom?denom=uist"; + const response = await axios.get(url); + const assetBalance = response.data.amount.amount; + const coinDecimals = getCoinDecimals(agoric.denom); + const amount = assetBalance / coinDecimals; + + const balances = {}; + sdk.util.sumSingleBalance(balances, agoric.coinGeckoId, amount); + + return balances; +} + +/* +@name fetchISTDataWithUSD - NOT USED BY DEFAULT +@description fetches the total supply of IST, converts it to USD value and returns it in a format compatible with defillama +@note wanted to explore another calculation, although this is dependent on external cg call +*/ +const fetchISTDataWithUSD = async () => { + // fetch total supply of ist + const url = "https://rest.cosmos.directory/agoric/cosmos/bank/v1beta1/supply/by_denom?denom=uist"; + const response = await axios.get(url); + const assetBalance = response.data.amount.amount; + const coinDecimals = getCoinDecimals(agoric.denom); + const amount = assetBalance / coinDecimals; + + // fetch ist price in usd from coingecko + const priceUrl = "https://api.coingecko.com/api/v3/simple/price?ids=inter-stable-token&vs_currencies=usd"; + const priceResponse = await axios.get(priceUrl); + const price = priceResponse.data.agoric.usd; + + // calculate tvl in usd + const tvl = amount * price; + + const balances = {}; + sdk.util.sumSingleBalance(balances, agoric.coinGeckoId, tvl); + + return balances; +} + +/* +@name fetchReserveData +@description fetches reserve data from subquery +*/ +const fetchReserveData = async () => { + const query = ` + query { + reserveMetrics { + nodes { + shortfallBalance + allocations { + nodes { + id + denom + value + } + } + } + } + }`; + const data = await fetchSubqueryData(query); + const allocations = data.reserveMetrics.nodes[0].allocations.nodes; + let totalReserve = 0; + allocations.forEach(allocation => { + totalReserve += parseFloat(allocation.value); + }); + const reserve = totalReserve / getCoinDecimals('uist'); + return reserve; +} + +/* +@name fetchPSMData +@description fetches PSM data from subquery for all asset types +*/ +const fetchPSMData = async () => { + const query = ` + query { + psmMetrics { + nodes { + mintedPoolBalance + } + } + }`; + const data = await fetchSubqueryData(query); + let totalPsm = 0; + data.psmMetrics.nodes.forEach(psm => { + totalPsm += parseFloat(psm.mintedPoolBalance); + }); + return totalPsm / getCoinDecimals('uist'); +} + +/* +@name fetchVaultData +@description fetches vault data from subquery and calculates the total locked value in IST +*/ +const fetchVaultData = async () => { + const query = ` + query { + vaultManagerMetrics { + nodes { + totalCollateral + } + } + }`; + const data = await fetchSubqueryData(query); + let totalCollateral = 0; + data.vaultManagerMetrics.nodes.forEach(vault => { + totalCollateral += parseFloat(vault.totalCollateral); + }); + return totalCollateral / getCoinDecimals('uist'); +} + +/* +@name fetchTotalCollateral +@description fetches total collateral and calculates its USD value +*/ +const fetchTotalCollateral = async () => { + const query = ` + query { + vaultManagerMetrics { + nodes { + totalCollateral + liquidatingCollateralBrand + } + } + oraclePrices { + nodes { + priceFeedName + typeOutAmount + typeInAmount + } + } + boardAuxes { + nodes { + allegedName + decimalPlaces + } + } + }`; + + const data = await fetchSubqueryData(query); + console.log(data); + const collateralPrices = {}; + const collateralDecimals = {}; + const collateralMap = {}; + + data.vaultManagerMetrics.nodes.forEach(vault => { + console.log("vault"); + console.log(vault); + const collateralType = vault.liquidatingCollateralBrand; + const collateralValue = parseFloat(vault.totalCollateral); + if (!collateralMap[collateralType]) { + collateralMap[collateralType] = 0; + } + collateralMap[collateralType] += collateralValue; + }); + + data.oraclePrices.nodes.forEach(price => { + console.log("price"); + console.log(price); + collateralPrices[price.priceFeedName] = parseFloat(price.typeOutAmount) / parseFloat(price.typeInAmount); + }); + + data.boardAuxes.nodes.forEach(aux => { + console.log("aux") + console.log(aux) + collateralDecimals[aux.allegedName.toLowerCase()] = Math.pow(10, aux.decimalPlaces); + }); + + let totalCollateralUSD = 0; + Object.keys(collateralMap).forEach(collateral => { + const collatKey = `${collateral}-USD`; + const price = collateralPrices[collatKey]; + const decimals = collateralDecimals[collateral.toLowerCase()] || 1; + const collateralAmount = collateralMap[collateral] / decimals; + console.log("decimals: ", decimals); + if (price) { + console.log(`[${collatKey}]collat price: `, price); + console.log(`[${collatKey}]collat amount: `, collateralAmount); + console.log(`[${collatKey}]collat price USD: `, collateralAmount * price); + totalCollateralUSD += collateralAmount * price; + } else { + console.error(`Price not found for collateral: ${collateral}`); + } + }); + + return totalCollateralUSD / getCoinDecimals('uist'); +}; + +/* +@name fetchTotalTVL +@description calculates total TVL including reserves, PSM, vaultsl, (and IST supply?) +*/ +const fetchTotalTVL = async () => { + const istData = await fetchISTData(); + const reserveData = await fetchReserveData(); + const psmData = await fetchPSMData(); + const vaultData = await fetchVaultData(); + const totalCollateral = await fetchTotalCollateral(); + + console.log("IST Data:", istData); // do we need the supply? would it be redundant? + console.log("Reserve Data:", reserveData); + console.log("PSM Data:", psmData); + console.log("Vault Data:", vaultData); + console.log("Total Collat: ", totalCollateral) + + const totalIST = parseFloat(Object.values(istData)[0]); + + // TODO: decide on which one.... + // const totalTVL = totalIST + reserveData + psmData + vaultData; + const totalTVL = totalCollateral + const balances = {}; + sdk.util.sumSingleBalance(balances, agoric.coinGeckoId, totalTVL); + + return balances; +} + +module.exports = { + timetravel: false, + methodology: "sum of ist tvl on agoric", + ist: { + tvl: fetchTotalTVL, + }, +}; \ No newline at end of file diff --git a/projects/inter-protocol/index_old.js b/projects/inter-protocol/index_old.js new file mode 100644 index 0000000000..8bbb455a30 --- /dev/null +++ b/projects/inter-protocol/index_old.js @@ -0,0 +1,435 @@ +const sdk = require("@defillama/sdk"); +const axios = require("axios"); + +// note: added agoric to projects/helper/chains.json +const agoric = { + chainId: "agoric-3", + denom: "uist", + coinGeckoId: "inter-stable-token", +}; + +/* +@name delay +@description throttle rpc calls +@param ms - milliseconds to delay +*/ +const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); + +/* +@name getCoinDecimals +@description returns the number of decimals for the given denomination +@param denom - the denomination to get decimals for +*/ +const getCoinDecimals = (denom) => 1e6; // IST uses 1e6 + + +/* +@name fetchISTData +@description fetches the total supply of IST and returns it in a format compatble with defillama +*/ +const fetchISTData = async () => { + // from https://github.com/DefiLlama/peggedassets-server/pull/292 + const url = "https://rest.cosmos.directory/agoric/cosmos/bank/v1beta1/supply/by_denom?denom=uist"; + const response = await axios.get(url); + const assetBalance = response.data.amount.amount; + const coinDecimals = getCoinDecimals(agoric.denom); + const amount = assetBalance / coinDecimals; + + const balances = {}; + sdk.util.sumSingleBalance(balances, agoric.coinGeckoId, amount); + + return balances; +} + +/* +@name fetchISTDataWithUSD - NOT USED BY DEFAULT +@description fetches the total supply of IST, converts it to USD value and returns it in a format compatible with defillama +@note wanted to explore another calculation, although this is dependent on external cg call +*/ +const fetchISTDataWithUSD = async () => { + // fetch total supply of ist + const url = "https://rest.cosmos.directory/agoric/cosmos/bank/v1beta1/supply/by_denom?denom=uist"; + const response = await axios.get(url); + const assetBalance = response.data.amount.amount; + const coinDecimals = getCoinDecimals(agoric.denom); + const amount = assetBalance / coinDecimals; + + // fetch ist price in usd from coingecko + const priceUrl = "https://api.coingecko.com/api/v3/simple/price?ids=inter-stable-token&vs_currencies=usd"; + const priceResponse = await axios.get(priceUrl); + const price = priceResponse.data.agoric.usd; + + // calculate tvl in usd + const tvl = amount * price; + + const balances = {}; + sdk.util.sumSingleBalance(balances, agoric.coinGeckoId, tvl); + + return balances; +} + + +/* +@name fetchVstorageData +@description fetches data from vstorage +@param path - the vstorage path to query +*/ +let CALL_COUNTER = 0 +const fetchVstorageData = (() => { + const cache = new Map(); + + return async (path) => { + if (cache.has(path)) { + return cache.get(path); + } + + CALL_COUNTER += 1; + // const url = "http://localhost:26657/"; + // const url = "https://main.rpc.agoric.net:443"; + const url = "https://agoric-rpc.polkachu.com/" + // const url = "https://agoric-testnet-rpc.polkachu.com/" + const payload = { + jsonrpc: "2.0", + id: 1, + method: "abci_query", + params: { path }, + }; + + try { + await delay(5000); // 3-second delay + const response = await axios.post(url, payload, { + headers: { "Content-Type": "application/json" }, + }); + + if (response.data.result.response.value) { + const decodedValue = Buffer.from(response.data.result.response.value, 'base64').toString('utf8'); + const result = JSON.parse(decodedValue); + console.log("vstorage result:") + console.log(result) + cache.set(path, result); + return result; + } else { + throw new Error('No value found in response'); + } + } catch (error) { + throw error; + } + }; +})(); + + +/* +@name fetchReserveData +@description fetches reserve data from vstorage +*/ +const fetchReserveData = async () => { + const reserveData = await fetchVstorageData('/custom/vstorage/data/published.reserve.metrics'); // "+"" means is marshalled bigint + const parsedValue = JSON.parse(reserveData.value); + const parsedMetrics = JSON.parse(parsedValue.values[0]); + const cleanedMetricsBody = JSON.parse(parsedMetrics.body.substring(1)); + // TODO: look at marshaler, fromCapData UPDATE: cannot add dep to repo + const reserve = parseFloat(cleanedMetricsBody.allocations.Fee.value.replace('+', '')); + console.log("RESERVE"); + console.log(reserve); + return reserve; +} + +/* +@name fetchPSMData +@description fetches PSM data from vstorage for all asset types +*/ +const fetchPSMData = async () => { + const psmTypes = await fetchVstorageData('/custom/vstorage/children/published.psm.IST'); + let totalPsm = 0; + + await Promise.all(psmTypes.children.map(async (assetType) => { + console.log("assetType", assetType); + const psmData = await fetchVstorageData(`/custom/vstorage/data/published.psm.IST.${assetType}.metrics`); + console.log("fetchPSMData iteration"); + console.log(psmData); + const parsedPsmValue = JSON.parse(psmData.value); + console.log("parsedPsmValue"); + console.log(parsedPsmValue); + + parsedPsmValue.values.forEach((value) => { + const parsedMetrics = JSON.parse(value); + console.log("parsedMetrics"); + console.log(parsedMetrics); + const cleanedMetricsBody = parsedMetrics.body.substring(1); + const cleanedParsedMetrics = JSON.parse(cleanedMetricsBody); + console.log("cleanedParsedMetrics"); + console.log(cleanedParsedMetrics); + // const psm = parseFloat(cleanedParsedMetrics.anchorPoolBalance.value.replace('+', '')); + const psm = parseFloat(cleanedParsedMetrics.mintedPoolBalance.value.replace('+', '')); // anchor pool would be the coll, but we want the IST yes? + console.log("PSM") + console.log(psm) + totalPsm += psm; + }); + })); + + return totalPsm; +}; + + + +/* +@name fetchVaultData +@description fetches vault data from vstorage and calculates the total locked value in USD +CALL_COUNTER: 578 - first attempt +CALL_COUNTER: 573 - second attempt - with vault cache +CALL_COUNTER: 293 - third attempt - with price cache +*/ +// only calculating based on IST price +let VAULT_COUNT = 0 + +const fetchVaultData = async () => { + const managerData = await fetchVstorageData('/custom/vstorage/children/published.vaultFactory.managers'); + let totalLockedUSD = 0; + const collateralSet = new Set(); // no dups + const vaultDataCache = new Map(); // cache for vault data + const priceCache = new Map(); // cache for prices + + // collect unique collateral types... + await Promise.all(managerData.children.map(async (managerId) => { + if (!vaultDataCache.has(managerId)) { + const vaultDataList = await fetchVstorageData(`/custom/vstorage/children/published.vaultFactory.managers.${managerId}.vaults`); + vaultDataCache.set(managerId, vaultDataList); // cache the vault data list + } + const vaultDataList = vaultDataCache.get(managerId); + await Promise.all(vaultDataList.children.map(async (vaultId) => { + try { + if (!vaultDataCache.has(vaultId)) { + const vaultData = await fetchVstorageData(`/custom/vstorage/data/published.vaultFactory.managers.${managerId}.vaults.${vaultId}`); + vaultDataCache.set(vaultId, vaultData); + } + const vaultData = vaultDataCache.get(vaultId); + const parsedVaultData = JSON.parse(vaultData.value); + parsedVaultData.values.forEach((value) => { + const vaultMetrics = JSON.parse(value); + const cleanedMetricsBody = vaultMetrics.body.substring(1); + const cleanedParsedMetrics = JSON.parse(cleanedMetricsBody); + const lockedBrand = cleanedParsedMetrics.locked.brand.split(" ")[1].toLowerCase(); + collateralSet.add(lockedBrand); + }); + } catch (error) { + console.error("Error fetching vault data:", error); + } + })); + })); + + // fetch prices for unique collateral types... + const fetchPrice = async (collateral) => { + console.log("coll: ", collateral); + let collateralToFetch; + switch (collateral) { + case "atom": + collateralToFetch = "cosmos"; + break; + case "statom": + collateralToFetch = "stride-staked-atom"; + break; + case "stosmo": + collateralToFetch = "stride-staked-osmo"; + break; + case "sttia": + collateralToFetch = "stride-staked-tia"; + break; + // case "stkatom": + // collateralToFetch = "pstake-staked-atom-2"; + break; + default: + collateralToFetch = collateral; + } + + const priceUrl = `https://api.coingecko.com/api/v3/simple/price?ids=${collateralToFetch}&vs_currencies=usd`; + console.log("priceUrl:", priceUrl); + try { + await delay(3000); // 3-second delay + const priceResponse = await axios.get(priceUrl); + console.log("priceResponse.data") + // console.log(priceResponse.data) + if (priceResponse.data[collateralToFetch]) { + // console.log("Got Price: ", priceResponse); + return priceResponse.data[collateralToFetch].usd; + } else { + console.error(`Price data not found for: ${collateral}`); + return null; + } + } catch (error) { + console.error(`Error fetching price for ${collateral}:`, error); + return null; + } + }; + + const pricePromises = {}; + await Promise.all([...collateralSet].map(async (collateral) => { + // console.log("priceCache") + // console.log(priceCache) + if (!priceCache.has(collateral)) { + if (!pricePromises[collateral]) { + pricePromises[collateral] = fetchPrice(collateral); + } + const price = await pricePromises[collateral]; + if (price !== null) { + priceCache.set(collateral, price); + } + } + })); + + // calculate total locked value in USD... + await Promise.all(managerData.children.map(async (managerId) => { + const vaultDataList = vaultDataCache.get(managerId); // retrieve from cache + console.log("vaultDataList"); + // console.log(vaultDataList); + await Promise.all(vaultDataList.children.map(async (vaultId) => { + try { + const vaultData = vaultDataCache.get(vaultId); // retrieve from cache + const parsedVaultData = JSON.parse(vaultData.value); + parsedVaultData.values.forEach((value) => { + const vaultMetrics = JSON.parse(value); + const cleanedMetricsBody = vaultMetrics.body.substring(1); + const cleanedParsedMetrics = JSON.parse(cleanedMetricsBody); + const locked = parseFloat(cleanedParsedMetrics.locked.value.replace('+', '')); + const lockedBrand = cleanedParsedMetrics.locked.brand.split(" ")[1].toLowerCase(); + console.log("lockedBrand: ", lockedBrand); + // console.log(priceCache); + const price = priceCache.get(lockedBrand); + // console.log("coll price: ", price); + if (price) { + const lockedUSD = locked * price / getCoinDecimals(lockedBrand); + totalLockedUSD += lockedUSD; + } else { + console.error(`Price not available for collateral: ${lockedBrand}`); + } + }); + } catch (error) { + console.error("Error fetching vault data:", error); + } + })); + })); + + return totalLockedUSD; +}; + + + +// first attempt - VAULT_COUNT: 512 should not be as high: +// second attempt - VAULT_COUNT: 225 +/* +test: +Reserve Data: 69625972760 +PSM Data: 5475886243 +Vault Data: 1532786.3758959998 + + +example: +vstorage result: +{ + value: '{"blockHeight":"13092169","values":["{\\"body\\":\\"#{\\\\\\"debtSnapshot\\\\\\":{\\\\\\"debt\\\\\\":{\\\\\\"brand\\\\\\":\\\\\\"$0.Alleged: IST brand\\\\\\",\\\\\\"value\\\\\\":\\\\\\"+0\\\\\\"},\\\\\\"interest\\\\\\":{\\\\\\"denominator\\\\\\":{\\\\\\"brand\\\\\\":\\\\\\"$0\\\\\\",\\\\\\"value\\\\\\":\\\\\\"+100000000000000000000\\\\\\"},\\\\\\"numerator\\\\\\":{\\\\\\"brand\\\\\\":\\\\\\"$0\\\\\\",\\\\\\"value\\\\\\":\\\\\\"+100452870470097742390\\\\\\"}}},\\\\\\"locked\\\\\\":{\\\\\\"brand\\\\\\":\\\\\\"$1.Alleged: stATOM brand\\\\\\",\\\\\\"value\\\\\\":\\\\\\"+0\\\\\\"},\\\\\\"vaultState\\\\\\":\\\\\\"closed\\\\\\"}\\",\\"slots\\":[\\"board0257\\",\\"board037112\\"]}"]}' +} +IST brand amount: 100000000000000000000 ? this isn't 6 decimals... does it need to be... +statom brand amount: 100452870470097742390 + +from inter ui (seems wrong): + +Total Reserve Assets +$106.51K + +Total Locked Collateral Value +$4.14M + + +*/ +// const fetchVaultData = async () => { +// const managerData = await fetchVstorageData('/custom/vstorage/children/published.vaultFactory.managers'); +// let totalLockedIST = 0; +// const vaultDataCache = new Map(); // cache for vault data + +// // helper function to process vault data +// const processVaultData = async (managerId, vaultId) => { +// if (!vaultDataCache.has(vaultId)) { +// try { +// const vaultData = await fetchVstorageData(`/custom/vstorage/data/published.vaultFactory.managers.${managerId}.vaults.${vaultId}`); +// vaultDataCache.set(vaultId, vaultData); +// VAULT_COUNT += 1; // increment only when a new vault data is fetched +// } catch (error) { +// console.error("Error fetching vault data:", error); +// return; +// } +// } + +// const vaultData = vaultDataCache.get(vaultId); +// const parsedVaultData = JSON.parse(vaultData.value); +// parsedVaultData.values.forEach((value) => { +// const vaultMetrics = JSON.parse(value); +// const cleanedMetricsBody = vaultMetrics.body.substring(1); +// const cleanedParsedMetrics = JSON.parse(cleanedMetricsBody); +// const lockedIST = parseFloat(cleanedParsedMetrics.locked.value.replace('+', '')); +// totalLockedIST += lockedIST / getCoinDecimals('uist'); +// }); +// }; + +// // collect IST values from vaults... +// const vaultIdsToProcess = []; +// for (const managerId of managerData.children) { +// if (!vaultDataCache.has(managerId)) { +// try { +// const vaultDataList = await fetchVstorageData(`/custom/vstorage/children/published.vaultFactory.managers.${managerId}.vaults`); +// vaultDataCache.set(managerId, vaultDataList); // Cache the vault data list +// vaultIdsToProcess.push(...vaultDataList.children.map(vaultId => ({ managerId, vaultId }))); +// } catch (error) { +// console.error("Error fetching vault list:", error); +// continue; +// } +// } +// } + +// // process all collected vault IDs +// await Promise.all(vaultIdsToProcess.map(({ managerId, vaultId }) => processVaultData(managerId, vaultId))); + +// console.log("VAULT_COUNT: ", VAULT_COUNT); +// return totalLockedIST; +// }; + + + + +/* +@name fetchTotalTVL +@description calculates total TVL including reserves, PSM, vaultsl, (and IST supply?) +*/ +const fetchTotalTVL = async () => { + const istData = await fetchISTData(); + const reserveData = await fetchReserveData(); + const psmData = await fetchPSMData(); + const vaultData = await fetchVaultData(); + + + console.log("IST Data:", istData); // do we need the supply? would it be redundant? + console.log("Reserve Data:", reserveData); + console.log("PSM Data:", psmData); + console.log("Vault Data:", vaultData); + + + const totalIST = parseFloat(Object.values(istData)[0]); + // const totalTVL = totalIST + reserveData + psmData; + // const totalTVL = reserveData + psmData; // remove total supply from calc? + // const totalTVL = totalIST + reserveData + psmData + vaultData; //TODO: try vaut data + const totalTVL = reserveData + psmData + vaultData; + + const balances = {}; + sdk.util.sumSingleBalance(balances, agoric.coinGeckoId, totalTVL); + + console.log("CALL_COUNTER: ", CALL_COUNTER) + console.log("VAULT_COUNT: ", VAULT_COUNT) + return balances; +} + + +module.exports = { + timetravel: false, + methodology: "Sum of IST TVL on Agoric", + ist: { + tvl: fetchTotalTVL, //debug: total tvl + }, +};