diff --git a/projects/ithaca/index.js b/projects/ithaca/index.js index cdb45d8182..443a333cdd 100644 --- a/projects/ithaca/index.js +++ b/projects/ithaca/index.js @@ -23,4 +23,4 @@ module.exports = { ], }), } -}; +}; \ No newline at end of file diff --git a/projects/loan-protocol/index.js b/projects/loan-protocol/index.js new file mode 100644 index 0000000000..7805fba467 --- /dev/null +++ b/projects/loan-protocol/index.js @@ -0,0 +1,104 @@ +const { post } = require('../helper/http') +const sdk = require('@defillama/sdk') + +const tokenMapping = { + 'xtokens:XBTC': 'bitcoin', + 'xtokens:XLTC': 'litecoin', + 'xtokens:XETH': 'ethereum', + 'xtokens:XXRP': 'ripple', + 'eosio.token:XPR': 'proton', + 'xtokens:XMT': 'metal', + 'xtokens:XUSDC': 'usd-coin', + 'xtokens:XDOGE': 'dogecoin', + 'xtokens:XUSDT': 'tether', + 'xtokens:XUST': 'terrausd-wormhole', + 'xtokens:XLUNA': 'terra-luna-2', +} + +const API_ENDPOINT = 'https://proton.eosusa.io' +const LENDING_CONTRACT = 'lending.loan' + +function parseAsset(assetString) { + if (!assetString) return { amount: 0, symbol: '' } + const [amount, symbol] = assetString.split(' ') + return { amount: parseFloat(amount), symbol } +} + +async function fetchMarkets() { + const res = await post(`${API_ENDPOINT}/v1/chain/get_table_rows`, { + code: LENDING_CONTRACT, + scope: LENDING_CONTRACT, + table: 'markets', + limit: 100, + json: true, + }) + return res.rows || [] +} + +async function fetchLiquidity(tokenContract, symbol) { + // available liquidity (cash) held by lending.loan for a given token + const res = await post(`${API_ENDPOINT}/v1/chain/get_table_rows`, { + code: tokenContract, + scope: LENDING_CONTRACT, + table: 'accounts', + limit: 100, + json: true, + }) + const rows = res.rows || [] + const tokenBalance = rows.find(b => parseAsset(b.balance).symbol === symbol) + return tokenBalance ? parseAsset(tokenBalance.balance).amount : 0 +} + +// ---------------------------- +// TVL = only available liquidity (cash) +// ---------------------------- +async function tvl() { + const balances = {} + const markets = await fetchMarkets() + + const promises = markets.map(async (market) => { + const [ , symbol ] = market.underlying_symbol.sym.split(',') + const tokenContract = market.underlying_symbol.contract + const internalId = `${tokenContract}:${symbol}` + const cgkId = tokenMapping[internalId] + if (!cgkId) return + + const cashAvailable = await fetchLiquidity(tokenContract, symbol) + sdk.util.sumSingleBalance(balances, `coingecko:${cgkId}`, cashAvailable) + }) + + await Promise.all(promises) + return balances +} + +// ---------------------------- +// Borrowed = total variable + stable borrows +// ---------------------------- +async function borrowed() { + const balances = {} + const markets = await fetchMarkets() + + markets.forEach(market => { + const totalVar = parseAsset(market.total_variable_borrows.quantity).amount + const totalStable = parseAsset(market.total_stable_borrows.quantity).amount + const totalBorrows = totalVar + totalStable + + const [ , symbol ] = market.underlying_symbol.sym.split(',') + const tokenContract = market.underlying_symbol.contract + const internalId = `${tokenContract}:${symbol}` + const cgkId = tokenMapping[internalId] + if (!cgkId) return + + sdk.util.sumSingleBalance(balances, `coingecko:${cgkId}`, totalBorrows) + }) + + return balances +} + +module.exports = { + methodology: 'TVL = only available liquidity (cash held by lending.loan). Borrowed = total variable + stable borrows (outstanding debt). Deposits = TVL + Borrowed, but we report liquidity as TVL per DefiLlama standards.', + proton: { + tvl, + borrowed, + } +} \ No newline at end of file