Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prototype for defillama IST tvl calculation v0.0.1 🚀 #1

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions projects/helper/chains.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
"icon",
"icp",
"imx",
"ist",
"inevm",
"injective",
"interlay",
Expand Down
84 changes: 84 additions & 0 deletions projects/inter-protocol/README.md
Original file line number Diff line number Diff line change
@@ -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
```
276 changes: 276 additions & 0 deletions projects/inter-protocol/index.js
Original file line number Diff line number Diff line change
@@ -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,
},
};
Loading