Skip to content
Merged
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
111 changes: 69 additions & 42 deletions projects/templarfi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,31 @@ const CALL_TIMEOUT_MS = 30000
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms))

function detectCrossChainToken(tokenId) {
// Stellar tokens via HOT omnichain bridge (chain ID 1100)
if (tokenId.includes('v2_1.omni.hot.tg:1100_')) {
const stellarMappings = {
'111bzQBB5v7AhLyPMDwS8uJgQV24KaAPXtwyVWu2KXbbfQU6NXRCz': 'coingecko:stellar',
'111bzQBB65GxAPAVoxqmMcgYo5oS3txhqs1Uh1cgahKQUeTUq1TJu': 'coingecko:usd-coin',
}
const match = tokenId.match(/1100_([a-zA-Z0-9]+)$/)
if (match && stellarMappings[match[1]]) {
return stellarMappings[match[1]]
return { chain: 'stellar', token: stellarMappings[match[1]] }
}
}

if (tokenId === 'btc.omft.near') return 'coingecko:bitcoin'
// Bitcoin via omnichain bridge
if (tokenId === 'btc.omft.near') {
return { chain: 'bitcoin', token: 'coingecko:bitcoin' }
}

// Ethereum tokens via omnichain bridge
if (tokenId.match(/^eth-0x([a-fA-F0-9]{40})\.omft\.near$/)) {
const match = tokenId.match(/^eth-0x([a-fA-F0-9]{40})\.omft\.near$/)
return `ethereum:0x${match[1].toLowerCase()}`
return { chain: 'ethereum', token: `ethereum:0x${match[1].toLowerCase()}` }
}

return tokenId
// Native NEAR tokens
return { chain: 'near', token: tokenId }
}

async function withRetry(fn, maxAttempts = MAX_RETRY_ATTEMPTS, delayMs = RETRY_DELAY_MS) {
Expand Down Expand Up @@ -82,7 +89,7 @@ function extractTokenAddress(assetConfig, assetType) {
}

const crossChainResult = detectCrossChainToken(tokenId)
if (crossChainResult !== tokenId) {
if (crossChainResult.chain !== 'near') {
return crossChainResult
}

Expand All @@ -94,7 +101,7 @@ function extractTokenAddress(assetConfig, assetType) {
return detectCrossChainToken(parts[1])
}

return tokenId
return { chain: 'near', token: tokenId }
}

throw new Error(`Unsupported ${assetType} asset format: missing both Nep141 and valid Nep245`)
Expand All @@ -106,8 +113,8 @@ function validateConfiguration(configuration) {
if (!configuration.collateral_asset) throw new Error('Missing collateral_asset in configuration')
}

function scaleTokenAmount(amount, tokenAddress, decimals) {
if (tokenAddress.startsWith('coingecko')) {
function scaleTokenAmount(amount, tokenInfo, decimals) {
if (tokenInfo.token.startsWith('coingecko')) {
return amount.div(Math.pow(10, decimals)).toFixed();
}

Expand Down Expand Up @@ -256,61 +263,81 @@ async function processMarket(marketContract) {
}
}

async function tvl() {
const balances = {}

async function getMarketData() {
const deployments = await fetchAllDeployments(TEMPLAR_REGISTRY_CONTRACTS)
if (deployments.length === 0) {
console.log('No Templar deployments found for TVL calculation')
return balances
console.log('No Templar deployments found')
return []
}

const results = await Promise.allSettled(deployments.map(processMarket))
const marketData = []

results.forEach((result, index) => {
if (result.status === 'fulfilled') {
if (result.value === null) {
return
if (result.value !== null) {
marketData.push(result.value)
}
const { borrowAssetToken, collateralAssetToken, availableLiquidity, totalCollateral, borrowDecimals, collateralDecimals } = result.value
sumSingleBalance(balances, borrowAssetToken, scaleTokenAmount(availableLiquidity, borrowAssetToken, borrowDecimals))
sumSingleBalance(balances, collateralAssetToken, scaleTokenAmount(totalCollateral, collateralAssetToken, collateralDecimals))
} else {
throw new Error(`Market ${deployments[index]} failed: ${result.reason?.message || result.reason}`)
}
})

return balances
return marketData
}

async function borrowed() {
const balances = {}

const deployments = await fetchAllDeployments(TEMPLAR_REGISTRY_CONTRACTS)
if (deployments.length === 0) {
console.log('No Templar deployments found for borrowed calculation')
return balances
}

const results = await Promise.allSettled(deployments.map(processMarket))

results.forEach((result, index) => {
if (result.status === 'fulfilled') {
if (result.value === null) {
return
}
const { borrowAssetToken, totalBorrowed, borrowDecimals } = result.value
sumSingleBalance(balances, borrowAssetToken, scaleTokenAmount(totalBorrowed, borrowAssetToken, borrowDecimals))
} else {
throw new Error(`Market ${deployments[index]} failed: ${result.reason?.message || result.reason}`)
function aggregateByChain(marketData, type) {
const chainBalances = {}

marketData.forEach(market => {
const { borrowAssetToken, collateralAssetToken, availableLiquidity, totalBorrowed, totalCollateral, borrowDecimals, collateralDecimals } = market

if (type === 'tvl') {
// Add borrow asset liquidity
if (!chainBalances[borrowAssetToken.chain]) chainBalances[borrowAssetToken.chain] = {}
sumSingleBalance(chainBalances[borrowAssetToken.chain], borrowAssetToken.token, scaleTokenAmount(availableLiquidity, borrowAssetToken, borrowDecimals))

// Add collateral
if (!chainBalances[collateralAssetToken.chain]) chainBalances[collateralAssetToken.chain] = {}
sumSingleBalance(chainBalances[collateralAssetToken.chain], collateralAssetToken.token, scaleTokenAmount(totalCollateral, collateralAssetToken, collateralDecimals))
} else if (type === 'borrowed') {
if (!chainBalances[borrowAssetToken.chain]) chainBalances[borrowAssetToken.chain] = {}
sumSingleBalance(chainBalances[borrowAssetToken.chain], borrowAssetToken.token, scaleTokenAmount(totalBorrowed, borrowAssetToken, borrowDecimals))
}
})

return chainBalances
}

let cachedMarketData = null

return balances
async function getChainTvl(chain) {
if (!cachedMarketData) {
cachedMarketData = await getMarketData()
}
const chainBalances = aggregateByChain(cachedMarketData, 'tvl')
return chainBalances[chain] || {}
}

async function getChainBorrowed(chain) {
if (!cachedMarketData) {
cachedMarketData = await getMarketData()
}
const chainBalances = aggregateByChain(cachedMarketData, 'borrowed')
return chainBalances[chain] || {}
}

// Supported chains for cross-chain assets
const SUPPORTED_CHAINS = ['near', 'stellar', 'ethereum', 'bitcoin']

module.exports = {
methodology: 'TVL is calculated by summing the net borrow asset liquidity (deposits minus outstanding loans) and full collateral deposits for each market deployment.',
methodology: 'TVL is calculated by summing the net borrow asset liquidity (deposits minus outstanding loans) and full collateral deposits for each market deployment. Assets are attributed to their origin chain (Stellar, Ethereum, Bitcoin).',
start: 1754902109,
near: { tvl, borrowed },
}

SUPPORTED_CHAINS.forEach(chain => {
module.exports[chain] = {
tvl: () => getChainTvl(chain),
borrowed: () => getChainBorrowed(chain),
}
})
Loading