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

Add LibreCapital tvl calculation #13153

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from 2 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
108 changes: 108 additions & 0 deletions projects/libre-capital/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const sdk = require("@defillama/sdk");
const { queryContract: queryContractCosmos } = require("../helper/chain/cosmos");
const { fetchURL } = require("../helper/utils");

const NAV_API_URL = "https://nav.dev.librecapital.com/funds";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any on chain liquidity or third party oracles we could use instead? Your endpoint will be hard to debug and audit for us, so its hard to tell if the data is valid.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have already reached out to Chainlink. In the upcoming months, we expect to have our oracle ready. However, we need to list on DeFiLama as soon as possible. Please let me know what I can do to make this endpoint more transparent and easier for you to work with.

We have already fetched the number of shares from the blockchain. The challenge is that no one has ever developed an oracle for fund prices before. We are collaborating with Chainlink on this, but we need more time to finalize the solution, and we are under a tight deadline. The fund prices are not confidential, as they are published and audited in the stock market. We used our endpoints because they are the fastest and allow us to guarantee timely price updates. Of course, this is only a temporary solution until Chainlink completes the oracle.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our API pulls data from the fund administration platforms we don't create the values ourselves

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I right in thinking that this has been discussed further in our discord server?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@waynebruce0x yes we agreed to not use the API as it is offchain and instead we deploy a smart contract with the NAV value and we attach the hash of NAV document as proof with it


const RECEIPT_TOKENS = {
polygon: {
UMA: {
address: '0xcf2Ca1B21e6f5dA7A2744f89667dE4E450791C79',
decimals: 18,
underlying: 'security-token',
fundName:'USD I Money Market, a sub-fund of Libre SAF VCC'
},
BHMA: {
address: '0xcc777c52ee9Ee5A57965a8E56F06211Fad34Fb3B',
decimals: 18,
underlying: 'security-token',
fundName:'BH Master Fund Access, a sub-fund of Libre SAF VCC'
}
},
injective: {
UMA: {
address: 'inj1eh6h6vllrvtl6qyq77cv5uwy0hw6e6d8jy4pxy',
decimals: 18,
underlying: 'security-token',
fundName:'USD I Money Market, a sub-fund of Libre SAF VCC'
}
}
}

async function getFundPrices() {
const { data: navData } = await fetchURL(NAV_API_URL);
const priceMap = {};
navData.forEach(fund => {
priceMap[fund.fundName] = fund.nav;
});
return priceMap;
}

async function polygonTvl({ polygon: block137 }) {
const balances = {}
let totalTvl = 0;
const fundPrices = await getFundPrices();

// Get total supply of both receipt tokens
const supplies = await sdk.api.abi.multiCall({
abi: 'erc20:totalSupply',
calls: Object.values(RECEIPT_TOKENS.polygon).map(i => ({ target: i.address })),
chain: 'polygon',
block: block137,
})


// Map each token's total supply to represent RWA TVL
supplies.output.forEach((supply, i) => {
const token = Object.values(RECEIPT_TOKENS.polygon)[i]
const balance = supply.output;
const price = fundPrices[token.fundName] || 1;

// Convert balance to human readable and multiply by price
const adjustedBalance = Number(balance) / (10 ** token.decimals);
const valueUSD = adjustedBalance * price;

totalTvl += valueUSD;
})

// Return the total value in the format DeFiLlama expects
balances['usd-coin'] = totalTvl;
return balances;
}

async function injectiveTvl() {
const balances = {}
let totalTvl = 0;
const fundPrices = await getFundPrices();

// Query total supply from Injective contract
const supply = await queryContractCosmos({
contract: RECEIPT_TOKENS.injective.UMA.address,
chain: 'injective',
data: {
token_info: {}
}
})

if (supply?.total_supply) {
const token = RECEIPT_TOKENS.injective.UMA;
const balance = supply.total_supply;
const price = fundPrices[token.fundName] || 1;

// Convert balance to human readable and multiply by price
const adjustedBalance = Number(balance) / (10 ** token.decimals);
const valueUSD = adjustedBalance * price;

totalTvl += valueUSD;
}

// Return the total value in the format DeFiLlama expects
balances['usd-coin'] = totalTvl;
return balances;
}

module.exports = {
methodology: "TVL represents the total value of institutional funds represented by UMA, BHMA and UMA receipt tokens on Polygon and Injective. The value is calculated by multiplying the total supply of receipt tokens by their respective NAV prices, denominated in their underlying stablecoin value",
polygon: { tvl: polygonTvl },
injective: { tvl: injectiveTvl }
}
Loading