From f77ef5563292e6cf52ec4e341f95535ecd1b9552 Mon Sep 17 00:00:00 2001 From: Geoff Hardy Date: Thu, 3 Jul 2025 11:17:43 -0700 Subject: [PATCH 1/8] simplify to using primary vault total supply for tvl --- projects/aera-v3/contracts.js | 96 +++++++++-------------------------- projects/aera-v3/index.js | 44 +++++++++++----- projects/aera-v3/tokens.js | 40 --------------- projects/aera-v3/utils.js | 86 +++++-------------------------- 4 files changed, 67 insertions(+), 199 deletions(-) delete mode 100644 projects/aera-v3/tokens.js diff --git a/projects/aera-v3/contracts.js b/projects/aera-v3/contracts.js index 72fb9099d9..1b99cbe525 100644 --- a/projects/aera-v3/contracts.js +++ b/projects/aera-v3/contracts.js @@ -2,92 +2,42 @@ const { ethers } = require("ethers"); const topics = { MultiDepositorVault_VaultCreated: ethers.id("VaultCreated(address,address,address,(string,string),(address,address,address),address,string)"), - BaseVault_VaultCreated: ethers.id("VaultCreated(address,address,address,string)"), SingleDepositorVault_VaultCreated: ethers.id("VaultCreated(address,address,address,(string,string),(address,address,address),address,string)"), - MetaMorpho_CreateMetaMorpho: ethers.id("CreateMetaMorpho(address indexed,address indexed,address,uint256,address indexed,string,string,bytes32)"), } const eventAbis = { MultiDepositorVault_VaultCreated: 'event VaultCreated(address indexed vault, address indexed owner, address hooks, (string name, string symbol) erc20Params, (address feeCalculator, address feeToken, address feeRecipient) feeVaultParams, address beforeTransferHook, string description)', - BaseVault_VaultCreated: 'event VaultCreated(address indexed vault, address indexed owner, address submitHooks, string description)', SingleDepositorVault_VaultCreated: 'event VaultCreated(address indexed vault, address indexed owner, address submitHooks, address feeToken, address feeCalculator, address feeRecipient, string description)', - MetaMorpho_CreateMetaMorpho: 'event CreateMetaMorpho(address indexed metaMorpho, address indexed caller, address initialOwner, uint256 initialTimelock, address indexed asset, string name, string symbol, bytes32 salt)', } module.exports = { ethereum: { fromBlock: 22583788, - morphoBlue: '0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb', - metaMorphoFactories: [ - { - address: '0x1897A8997241C1cD4bD0698647e4EB7213535c24', - fromBlock: 21439510, - eventAbi: eventAbis.MetaMorpho_CreateMetaMorpho, - topic: topics.MetaMorpho_CreateMetaMorpho - }, - { - address: '0xa9c3d3a366466fa809d1ae982fb2c46e5fc41101', - fromBlock: 18925584, - eventAbi: eventAbis.MetaMorpho_CreateMetaMorpho, - topic: topics.MetaMorpho_CreateMetaMorpho - } - ], - vaultFactories: { - multiDepositorVaultFactory: { - address: '0x29722cC9a1cACff4a15914F9bC274B46F3b90B4F', - fromBlock: 22583788, - eventAbi: eventAbis.MultiDepositorVault_VaultCreated, - topics: [topics.MultiDepositorVault_VaultCreated] - }, - baseVaultFactory: { - address: '0x1A8E10A9503e747Aeb81DA5941bCDa6C6a9741B9', - fromBlock: 22583788, - eventAbi: eventAbis.BaseVault_VaultCreated, - topics: [topics.BaseVault_VaultCreated] - }, - singleDepositorVaultFactory: { - address: '0x8f1FdB45160234d6E7e3653F5Af8e09A2Ce25AEb', - fromBlock: 22584116, - eventAbi: eventAbis.SingleDepositorVault_VaultCreated, - topics: [topics.SingleDepositorVault_VaultCreated] - }, - }, + multiDepositorVaultFactory: { + address: '0x29722cC9a1cACff4a15914F9bC274B46F3b90B4F', + fromBlock: 22583788, + eventAbi: eventAbis.MultiDepositorVault_VaultCreated, + topics: [topics.MultiDepositorVault_VaultCreated] + }, + singleDepositorVaultFactory: { + address: '0x8f1FdB45160234d6E7e3653F5Af8e09A2Ce25AEb', + fromBlock: 22584116, + eventAbi: eventAbis.SingleDepositorVault_VaultCreated, + topics: [topics.SingleDepositorVault_VaultCreated] + }, }, base: { fromBlock: 30834355, - morphoBlue: '0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb', - metaMorphoFactories: [ - { - address: '0xFf62A7c278C62eD665133147129245053Bbf5918', - fromBlock: 23928808, - eventAbi: eventAbis.MetaMorpho_CreateMetaMorpho, - topic: topics.MetaMorpho_CreateMetaMorpho - }, - { - address: '0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101', - fromBlock: 13978134, - eventAbi: eventAbis.MetaMorpho_CreateMetaMorpho, - topic: topics.MetaMorpho_CreateMetaMorpho - } - ], - vaultFactories: { - multiDepositorVaultFactory: { - address: '0x29722cC9a1cACff4a15914F9bC274B46F3b90B4F', - fromBlock: 30834355, - eventAbi: eventAbis.MultiDepositorVault_VaultCreated, - topics: [topics.MultiDepositorVault_VaultCreated] - }, - baseVaultFactory: { - address: '0x1A8E10A9503e747Aeb81DA5941bCDa6C6a9741B9', - fromBlock: 30834356, - eventAbi: eventAbis.BaseVault_VaultCreated, - topics: [topics.BaseVault_VaultCreated] - }, - singleDepositorVaultFactory: { - address: '0x8f1FdB45160234d6E7e3653F5Af8e09A2Ce25AEb', - fromBlock: 30834356, - eventAbi: eventAbis.SingleDepositorVault_VaultCreated, - topics: [topics.SingleDepositorVault_VaultCreated] - }, + multiDepositorVaultFactory: { + address: '0x29722cC9a1cACff4a15914F9bC274B46F3b90B4F', + fromBlock: 30834355, + eventAbi: eventAbis.MultiDepositorVault_VaultCreated, + topics: [topics.MultiDepositorVault_VaultCreated] + }, + singleDepositorVaultFactory: { + address: '0x8f1FdB45160234d6E7e3653F5Af8e09A2Ce25AEb', + fromBlock: 30834356, + eventAbi: eventAbis.SingleDepositorVault_VaultCreated, + topics: [topics.SingleDepositorVault_VaultCreated] }, }, }; \ No newline at end of file diff --git a/projects/aera-v3/index.js b/projects/aera-v3/index.js index 3a42ec3c2b..3b7ef679a8 100644 --- a/projects/aera-v3/index.js +++ b/projects/aera-v3/index.js @@ -1,17 +1,37 @@ -const { getAeraVaults, getMorphoVaults, sumErc4626Balances } = require('./utils'); -const { getTokens } = require('./tokens'); -const { sumTokens2 } = require('../helper/unwrapLPs'); +const { getMultiDepositorVaults } = require('./utils'); async function tvl(api) { - const aeraVaults = await getAeraVaults(api); - const tokens = await getTokens(api); - const morphoVaults = await getMorphoVaults(api); - const erc20 = tokens.filter(token => !morphoVaults.includes(token)); - const morphoFiltered = tokens.filter(token => morphoVaults.includes(token)); - await Promise.all([ - sumErc4626Balances({api, owners: aeraVaults, vaults: morphoFiltered}), - sumTokens2({owners: aeraVaults, tokens: erc20, api}), - ]); + const multiDepositorVaults = await getMultiDepositorVaults(api); + + await Promise.all(multiDepositorVaults.map(async (vault) => { + const [totalSupply, feeCalculator ] = await Promise.all([ + api.call({ + abi: 'function totalSupply() view returns (uint256)', + target: vault, + }), + api.call({ + abi: 'function feeCalculator() view returns (address)', + target: vault, + }), + ]) + + const [numeraireToken, vaultState] = await Promise.all([ + api.call({ + abi: 'function NUMERAIRE() view returns (address)', + target: feeCalculator, + }), + api.call({ + abi: 'function getVaultState(address vault) external view returns ((bool paused, uint8 maxPriceAge, uint16 minUpdateIntervalMinutes, uint16 maxPriceToleranceRatio, uint16 minPriceToleranceRatio, uint8 maxUpdateDelayDays, uint32 timestamp, uint24 accrualLag, uint128 unitPrice, uint128 highestPrice, uint128 lastTotalSupply))', + target: feeCalculator, + params: [vault], + }), + ]) + + const unitPrice = vaultState[8]; + const numeraireBalance = totalSupply * unitPrice / 1e18; + + api.add(numeraireToken, numeraireBalance); + })); } module.exports = { diff --git a/projects/aera-v3/tokens.js b/projects/aera-v3/tokens.js deleted file mode 100644 index 7730bfb281..0000000000 --- a/projects/aera-v3/tokens.js +++ /dev/null @@ -1,40 +0,0 @@ -const { ethers } = require('ethers'); - -// Ideally, these would be derived from on-chain data, but the transfer logs are too slow to process. -const tokens = [ - { chain: 'ethereum', address: '0x0404fd1a77756eb029f06b5cdea88b2b2ddc2fee', symbol: 'elixirUSDC' }, - { chain: 'ethereum', address: '0x132e6c9c33a62d7727cd359b1f51e5b566e485eb', symbol: 'resolvUSDC' }, - { chain: 'ethereum', address: '0x58d97b57bb95320f9a05dc918aef65434969c2b2', symbol: 'MORPHO' }, - { chain: 'ethereum', address: '0x7204b7dbf9412567835633b6f00c3edc3a8d6330', symbol: 'csUSDC' }, - { chain: 'ethereum', address: '0x8eb67a509616cd6a7c1b3c8c21d48ff57df3d458', symbol: 'gtUSDCcore' }, - { chain: 'ethereum', address: '0xa0804346780b4c2e3be118ac957d1db82f9d7484', symbol: 'bbqUSDT' }, - { chain: 'ethereum', address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', symbol: 'USDC' }, - { chain: 'ethereum', address: '0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f', symbol: 'eUSD' }, - { chain: 'ethereum', address: '0xa8875aaebc4f830524e35d57f9772ffacbdd6c45', symbol: 'midasUSDC' }, - { chain: 'ethereum', address: '0xbeefff209270748ddd194831b3fa287a5386f5bc', symbol: 'bbqUSDC' }, - { chain: 'ethereum', address: '0xc080f56504e0278828a403269db945f6c6d6e014', symbol: 'gteUSDc' }, - { chain: 'ethereum', address: '0xc582f04d8a82795aa2ff9c8bb4c1c889fe7b754e', symbol: 'gtusdcf' }, - { chain: 'ethereum', address: '0xc83e27f270cce0a3a3a29521173a83f402c1768b', symbol: 'USDQ' }, - { chain: 'ethereum', address: '0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf', symbol: 'cbBTC' }, - { chain: 'ethereum', address: '0xdac17f958d2ee523a2206206994597c13d831ec7', symbol: 'USDT' }, - { chain: 'base', address: '0x23479229e52ab6aad312d0b03df9f33b46753b5e', symbol: 'exmUSDC' }, - { chain: 'base', address: '0x57f5e098cad7a3d1eed53991d4d66c45c9af7812', symbol: 'wUSDM' }, - { chain: 'base', address: '0x616a4e1db48e22028f6bbf20444cd3b8e3273738', symbol: 'smUSDC' }, - { chain: 'base', address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', symbol: 'USDC' }, - { chain: 'base', address: '0xbeef010f9cb27031ad51e3333f9af9c6b1228183', symbol: 'steakUSDC' }, - { chain: 'base', address: '0xbeef03f0bf3cb2e348393008a826538aadd7d183', symbol: 'steakUSDM' }, - { chain: 'base', address: '0xbeefa74640a5f7c28966cba82466eed5609444e0', symbol: 'bbqUSDC' }, - { chain: 'base', address: '0xc0c5689e6f4d256e861f65465b691aeecc0deb12', symbol: 'gtUSDCc' }, - { chain: 'base', address: '0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf', symbol: 'cbBTC' }, - { chain: 'base', address: '0xee8f4ec5672f09119b96ab6fb59c27e1b7e44b61', symbol: 'gtUSDCp' }, -] - -async function getTokens(api) { - const chain = api.chain; - const tokensForChain = tokens.filter(token => token.chain === chain).map(token => ethers.getAddress(token.address)); - return tokensForChain; -} - -module.exports = { - getTokens, -} \ No newline at end of file diff --git a/projects/aera-v3/utils.js b/projects/aera-v3/utils.js index b7b1c5dadf..391fb607b1 100644 --- a/projects/aera-v3/utils.js +++ b/projects/aera-v3/utils.js @@ -1,84 +1,22 @@ const contracts = require('./contracts'); const { getLogs } = require('../helper/cache/getLogs') -async function getAeraVaults(api) { +async function getMultiDepositorVaults(api) { const vaults = []; - await Promise.all(Object.values(contracts[api.chain].vaultFactories).map(async (factory) => { - const logs = await getLogs({ - // skipCache: true, - api, - target: factory.address, - topic: factory.topic, - topics: factory.topics, - eventAbi: factory.eventAbi, - fromBlock: factory.fromBlock, - onlyArgs: true, - }); - vaults.push(...logs.map(x => x.vault)) - })); - return vaults; -} - -async function getMorphoVaults(api) { - const vaults = []; - await Promise.all(contracts[api.chain].metaMorphoFactories.map(async (factory) => { + const factory = contracts[api.chain].multiDepositorVaultFactory; const logs = await getLogs({ - // skipCache: true, - api, - target: factory.address, - topic: factory.topic, - topics: factory.topics, - eventAbi: factory.eventAbi, - fromBlock: factory.fromBlock, - onlyArgs: true, - }); - vaults.push(...logs.map(x => x.metaMorpho)); - })); - return vaults; -} - -async function sumErc4626Balances({api, owners, vaults}) { - // vaults = vaults.filter(vault => vault !== '0xc582F04d8a82795aa2Ff9c8bb4c1c889fe7b754e'); - const assets = await api.multiCall({ - calls: vaults.map(vault => ({ - target: vault, - params: [] - })), - abi: 'address:asset', - withMetadata: true, - }); - - const vaultsToAssets = vaults.reduce((acc, vault, index) => { - acc[vault] = assets[index].output; - return acc; - }, {}); - - const vaultsAndOwners = vaults.flatMap((vault, index) => - owners.map(owner => ({ - target: vault, - params: [owner], - }))); - - const shares = await api.multiCall({ - calls: vaultsAndOwners, - abi: 'function balanceOf(address) view returns (uint256)', + api, + target: factory.address, + topic: factory.topic, + topics: factory.topics, + eventAbi: factory.eventAbi, + fromBlock: factory.fromBlock, + onlyArgs: true, }); - const balances = await api.multiCall({ - calls: shares.map((share, index) => ({ - target: vaultsAndOwners[index].target, - params: [share], - })), - abi: 'function convertToAssets(uint256) view returns (uint256)', - }); - const assetsForBalances = vaultsAndOwners.map((vault) => (vaultsToAssets[vault.target])); - await api.addTokens( - assetsForBalances, - balances - ); + vaults.push(...logs.map(x => x.vault)) + return vaults; } module.exports = { - getAeraVaults, - getMorphoVaults, - sumErc4626Balances, + getMultiDepositorVaults, }; \ No newline at end of file From 3a7fd142fb8fba45c5b669f7b23eda86ceb99986 Mon Sep 17 00:00:00 2001 From: Geoff Hardy Date: Thu, 3 Jul 2025 11:27:46 -0700 Subject: [PATCH 2/8] add comment about single-depositor vaults --- projects/aera-v3/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects/aera-v3/index.js b/projects/aera-v3/index.js index 3b7ef679a8..413ad51114 100644 --- a/projects/aera-v3/index.js +++ b/projects/aera-v3/index.js @@ -3,6 +3,8 @@ const { getMultiDepositorVaults } = require('./utils'); async function tvl(api) { const multiDepositorVaults = await getMultiDepositorVaults(api); + // Compute TVL for multi depositor vaults + // TODO: Add single depositor vaults await Promise.all(multiDepositorVaults.map(async (vault) => { const [totalSupply, feeCalculator ] = await Promise.all([ api.call({ From fb6da5d928a989604c223ff18e5c77ecc09f3652 Mon Sep 17 00:00:00 2001 From: Geoff Hardy Date: Thu, 3 Jul 2025 16:43:35 -0700 Subject: [PATCH 3/8] use vault decimals instead of hard-coding --- projects/aera-v3/index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/projects/aera-v3/index.js b/projects/aera-v3/index.js index 413ad51114..ab8ac33315 100644 --- a/projects/aera-v3/index.js +++ b/projects/aera-v3/index.js @@ -6,7 +6,7 @@ async function tvl(api) { // Compute TVL for multi depositor vaults // TODO: Add single depositor vaults await Promise.all(multiDepositorVaults.map(async (vault) => { - const [totalSupply, feeCalculator ] = await Promise.all([ + const [totalSupply, feeCalculator, decimals ] = await Promise.all([ api.call({ abi: 'function totalSupply() view returns (uint256)', target: vault, @@ -15,6 +15,10 @@ async function tvl(api) { abi: 'function feeCalculator() view returns (address)', target: vault, }), + api.call({ + abi: 'function decimals() view returns (uint8)', + target: vault, + }), ]) const [numeraireToken, vaultState] = await Promise.all([ @@ -30,7 +34,8 @@ async function tvl(api) { ]) const unitPrice = vaultState[8]; - const numeraireBalance = totalSupply * unitPrice / 1e18; + const numeraireBalance = totalSupply * unitPrice / 10 ** decimals; + console.log(numeraireBalance.toString()); api.add(numeraireToken, numeraireBalance); })); From d4abeffd77cfa929e296ff438ab1387ee1336d4c Mon Sep 17 00:00:00 2001 From: Geoff Hardy Date: Wed, 16 Jul 2025 12:30:16 -0700 Subject: [PATCH 4/8] add morpho tvl computation to aera v2 --- projects/aera/index.js | 99 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/projects/aera/index.js b/projects/aera/index.js index 06d3e4592e..095aec8347 100644 --- a/projects/aera/index.js +++ b/projects/aera/index.js @@ -9,11 +9,13 @@ const GEARBOX_TOKEN_PREFIX = 'Farming of' const ARRAKIS_TOKEN_PREFIX = 'Arrakis Vault V2' const ESXAI_POSITION_ORACLE_NAME = 'EsXai Position Oracle' const SYMBIOTIC_TOKEN_PREFIX = 'Symbiotic Vault' +const MORPHO_BLUE_POSITION_ORACLE_NAME = 'MorphoBluePositionOracle' const config = { polygon: { aavePool: '0x794a61358D6845594F94dc1DB02A252b5b4814aD', aavePoolDataProvider: '0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654', + morphoBlue: '0x1bF0c2541F820E775182832f06c0B7Fc27A25f67', comets: [ { address: '0xF25212E676D1F7F89Cd72fFEe66158f541246445', @@ -44,6 +46,7 @@ const config = { ethereum: { aavePool: '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2', aavePoolDataProvider: '0x7B4EB56E7CD4b454BA8ff71E4518426369a138a3', + morphoBlue: '0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb', comets: [ { address: '0xA17581A9E3356d9A858b789D68B4d866e593aE94', @@ -82,6 +85,7 @@ const config = { arbitrum: { aavePool: '0x794a61358D6845594F94dc1DB02A252b5b4814aD', aavePoolDataProvider: '0x6b4E260b765B3cA1514e618C0215A6B7839fF93e', + morphoBlue: '0x6c247b1F6182318877311737BaC0844bAa518F5e', comets: [ { address: '0xA5EDBDD9646f8dFF606d7448e414884C7d905dCA', @@ -111,6 +115,7 @@ const config = { base: { aavePool: '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5', aavePoolDataProvider: '0x2d8A3C5677189723C4cB8873CfC9C8976FDF38Ac', + morphoBlue: '0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb', comets: [ { address: '0x46e6b214b524310239732D51387075E0e70970bf', @@ -148,6 +153,7 @@ Object.keys(config).forEach(chain => { tvl: async (api) => { const AAVE_POOL = config[chain].aavePool const AAVE_POOL_DATA_PROVIDER = config[chain].aavePoolDataProvider + const MORPHO_BLUE = config[chain].morphoBlue const COMETS = config[chain].comets const COMET_REWARD = config[chain].cometReward const ARRAKIS_HELPER = config[chain].arrakisHelper @@ -192,7 +198,7 @@ Object.keys(config).forEach(chain => { const xaiPositionVaults = [] const esXaiVaults = [] const symbioticVaults = [] - + const morphoBlueVaults = [] for (let i = 0; i < vaults.length; ++i) { const vault = vaults[i] for (let j = 0; j < assets[i].length; ++j) { @@ -228,6 +234,10 @@ Object.keys(config).forEach(chain => { symbioticVaults.push([vault, assetInfo.asset]) continue } + if (assetName === MORPHO_BLUE_POSITION_ORACLE_NAME) { + morphoBlueVaults.push([vault, assetInfo.asset]) + continue + } } @@ -424,10 +434,95 @@ async function processSymbioticTvl(symbioticVaults, api) { const balances = await api.multiCall({ abi: 'erc20:balanceOf', calls: symbioticVaults.map(x => ({target: x[1], params: [x[0]]}))}) collaterals.forEach((collateral, i) => { - api.addToken(collateral, balances[i]) + api.addToken(collateral, balances[i], symbioticVaults[i][0]) }) } +async function processMorphoBlueTvl(morphoBlueVaults, api, MORPHO_BLUE) { + if (morphoBlueVaults.length === 0) return; + + // Get the number of markets for each oracle + const marketLengths = await api.multiCall({ + abi: 'function getMarketsLength() view returns (uint256)', + calls: morphoBlueVaults.map(x => ({target: x[1], params: []})) + }); + + + // Build calls to get each market by index + const marketCalls = []; + morphoBlueVaults.forEach(([vault, oracle], oracleIndex) => { + const length = marketLengths[oracleIndex]; + for (let i = 0; i < length; i++) { + marketCalls.push({ + target: oracle, + params: [i], + vault: vault, + oracleIndex: oracleIndex + }); + } + }); + + if (marketCalls.length === 0) return; + + // Get all markets from the Morpho Blue position oracles + const markets = await api.multiCall({ + abi: 'function markets(uint256) view returns ((bytes32, address, uint80, uint8, bool))', + calls: marketCalls + }); + + const marketInfos = await api.multiCall({ + abi: 'function market(bytes32) view returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares)', + calls: markets.map(market => ({target: MORPHO_BLUE, params: [market[0]]})) + }); + + // Get market parameters for each market ID + const marketParams = await api.multiCall({ + abi: 'function idToMarketParams(bytes32) view returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv)', + calls: markets.map(market => ({ target: MORPHO_BLUE, params: [market[0]] })) + }); + + // Get positions for each vault across all markets + const positionCalls = []; + marketCalls.forEach((call, index) => { + const marketId = markets[index][0]; // Extract the market ID (bytes32) + positionCalls.push({ + target: MORPHO_BLUE, + params: [marketId, call.vault] + }); + }); + + const positions = await api.multiCall({ + abi: 'function position(bytes32, address) view returns (uint256 supplyShares, uint256 borrowShares, uint256 collateral)', + calls: positionCalls + }); + + // Process positions and add to TVL + await Promise.all(positionCalls.map(async (call, index) => { + const [marketId, vault] = call.params; + const position = positions[index]; + const marketParam = marketParams[index]; + const marketInfo = marketInfos[index]; + + // const loanToken = marketParam.loanToken; + // const loanDecimals = await api.call({ abi: 'uint8:decimals', target: loanToken }); + + console.log(position); + + if (position.collateral > 0) { + api.addToken(marketParam.collateralToken, position.collateral, vault); + } + + if (position.supplyShares > 0) { + const supplyAssets = Math.round(position.supplyShares * marketInfo.totalSupplyAssets / marketInfo.totalSupplyShares); + api.addToken(marketParam.loanToken, supplyAssets, vault); + } + if (position.borrowShares > 0) { + const borrowAssets = Math.round(position.borrowShares * marketInfo.totalBorrowAssets / marketInfo.totalBorrowShares); + api.addToken(marketParam.loanToken, -borrowAssets, vault); + } + })); +} + const abi = { "collateralBalanceOf": "function collateralBalanceOf(address account, address asset) view returns (uint128)", "getReserveData": "function getReserveData(address asset) view returns (((uint256 data) configuration, uint128 liquidityIndex, uint128 currentLiquidityRate, uint128 variableBorrowIndex, uint128 currentVariableBorrowRate, uint128 currentStableBorrowRate, uint40 lastUpdateTimestamp, uint16 id, address aTokenAddress, address stableDebtTokenAddress, address variableDebtTokenAddress, address interestRateStrategyAddress, uint128 accruedToTreasury, uint128 unbacked, uint128 isolationModeTotalDebt))", From 39e921d68b78ff9049b2fc1b87c5854590b57376 Mon Sep 17 00:00:00 2001 From: Geoff Hardy Date: Wed, 16 Jul 2025 13:25:50 -0700 Subject: [PATCH 5/8] cleanup --- projects/aera/index.js | 48 +++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/projects/aera/index.js b/projects/aera/index.js index 095aec8347..8d5e82e031 100644 --- a/projects/aera/index.js +++ b/projects/aera/index.js @@ -273,7 +273,8 @@ Object.keys(config).forEach(chain => { processGearboxTvl(gearboxFarmingPools, api), processArrakisTvl(arrakisVaults, api, ARRAKIS_HELPER), processXaiTvl(xaiPositionVaults, api, ESXAI_POOL_FACTORY, XAI), - processSymbioticTvl(symbioticVaults, api) + processSymbioticTvl(symbioticVaults, api), + processMorphoBlueTvl(morphoBlueVaults, api, MORPHO_BLUE) ]) Object.keys(erc4626UnderylingMap).forEach((erc4626Asset, i) => erc4626UnderylingMap[erc4626Asset] = underlyingTokens[i]) @@ -292,7 +293,7 @@ Object.keys(config).forEach(chain => { async function processXaiTvl(xaiPositionVaults, api, ESXAI_POOL_FACTORY, XAI) { if (xaiPositionVaults.length === 0) return - + const pools = await api.multiCall({ abi: abi.getPoolIndicesOfUser, calls: xaiPositionVaults.map(x => ({target: ESXAI_POOL_FACTORY, params: [x]}))}) const vaultPools = xaiPositionVaults.flatMap((vault, i) => pools[i].map(pool => ([vault, pool]))) @@ -320,7 +321,7 @@ async function processArrakisTvl(arrakisVaults, api, arrakisHelper) { api.multiCall({ abi: abi.token1, calls: arrakisVaults}), api.multiCall({ abi: abi.totalUnderlying, calls: arrakisVaults.map(x => ({target: arrakisHelper, params: [x]}))}) ]) - + totalUnderlyings.forEach((v, i) => { api.addToken(tokens0[i], v.totalAmount0) api.addToken(tokens1[i], v.totalAmount1) @@ -423,7 +424,7 @@ async function processAaveTvl(aaveVaults, api, AAVE_POOL, AAVE_POOL_DATA_PROVIDE api.addToken(aaveReserveDetails[reserveIdx].stableDebtTokenAddress, aavePosition.currentStableDebt); api.addToken(aaveReserveDetails[reserveIdx].variableDebtTokenAddress, aavePosition.currentVariableDebt); } - + } async function processSymbioticTvl(symbioticVaults, api) { @@ -434,7 +435,7 @@ async function processSymbioticTvl(symbioticVaults, api) { const balances = await api.multiCall({ abi: 'erc20:balanceOf', calls: symbioticVaults.map(x => ({target: x[1], params: [x[0]]}))}) collaterals.forEach((collateral, i) => { - api.addToken(collateral, balances[i], symbioticVaults[i][0]) + api.addToken(collateral, balances[i]) }) } @@ -443,7 +444,7 @@ async function processMorphoBlueTvl(morphoBlueVaults, api, MORPHO_BLUE) { // Get the number of markets for each oracle const marketLengths = await api.multiCall({ - abi: 'function getMarketsLength() view returns (uint256)', + abi: abi.getMarketsLength, calls: morphoBlueVaults.map(x => ({target: x[1], params: []})) }); @@ -465,19 +466,16 @@ async function processMorphoBlueTvl(morphoBlueVaults, api, MORPHO_BLUE) { if (marketCalls.length === 0) return; // Get all markets from the Morpho Blue position oracles - const markets = await api.multiCall({ - abi: 'function markets(uint256) view returns ((bytes32, address, uint80, uint8, bool))', - calls: marketCalls - }); + const markets = await api.multiCall({ abi: abi.markets, calls: marketCalls }); const marketInfos = await api.multiCall({ - abi: 'function market(bytes32) view returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares)', + abi: abi.market, calls: markets.map(market => ({target: MORPHO_BLUE, params: [market[0]]})) }); // Get market parameters for each market ID const marketParams = await api.multiCall({ - abi: 'function idToMarketParams(bytes32) view returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv)', + abi: abi.idToMarketParams, calls: markets.map(market => ({ target: MORPHO_BLUE, params: [market[0]] })) }); @@ -492,32 +490,33 @@ async function processMorphoBlueTvl(morphoBlueVaults, api, MORPHO_BLUE) { }); const positions = await api.multiCall({ - abi: 'function position(bytes32, address) view returns (uint256 supplyShares, uint256 borrowShares, uint256 collateral)', + abi: abi.position, calls: positionCalls }); // Process positions and add to TVL await Promise.all(positionCalls.map(async (call, index) => { - const [marketId, vault] = call.params; + const [_, vault] = call.params; const position = positions[index]; const marketParam = marketParams[index]; const marketInfo = marketInfos[index]; - // const loanToken = marketParam.loanToken; - // const loanDecimals = await api.call({ abi: 'uint8:decimals', target: loanToken }); - - console.log(position); - if (position.collateral > 0) { api.addToken(marketParam.collateralToken, position.collateral, vault); } if (position.supplyShares > 0) { - const supplyAssets = Math.round(position.supplyShares * marketInfo.totalSupplyAssets / marketInfo.totalSupplyShares); + const supplyAssets = ( + BigInt(position.supplyShares) * + BigInt(marketInfo.totalSupplyAssets) / BigInt(marketInfo.totalSupplyShares) + ); api.addToken(marketParam.loanToken, supplyAssets, vault); } if (position.borrowShares > 0) { - const borrowAssets = Math.round(position.borrowShares * marketInfo.totalBorrowAssets / marketInfo.totalBorrowShares); + const borrowAssets = ( + BigInt(position.borrowShares) * + BigInt(marketInfo.totalBorrowAssets) / BigInt(marketInfo.totalBorrowShares) + ); api.addToken(marketParam.loanToken, -borrowAssets, vault); } })); @@ -553,5 +552,10 @@ const abi = { "esXaiStakeBucket": "function esXaiStakeBucket() returns (address)", "getStakedAmounts": "function getStakedAmounts(address) returns (uint256)", "withdrawableDividendOf": "function withdrawableDividendOf(address) returns (uint256)", - "collateral": "function collateral() returns (address)" + "collateral": "function collateral() returns (address)", + "getMarketsLength": "function getMarketsLength() view returns (uint256)", + "markets": "function markets(uint256) view returns ((bytes32, address, uint80, uint8, bool))", + "idToMarketParams": "function idToMarketParams(bytes32) view returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv)", + "position": "function position(bytes32, address) view returns (uint256 supplyShares, uint256 borrowShares, uint256 collateral)", + "market": "function market(bytes32) view returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares)" } \ No newline at end of file From a241b3e57965e82218bc0fc26b868c66613610a5 Mon Sep 17 00:00:00 2001 From: Geoff Hardy Date: Wed, 16 Jul 2025 14:12:16 -0700 Subject: [PATCH 6/8] handle ERC4626 morpho collateral tokens --- projects/aera/index.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/projects/aera/index.js b/projects/aera/index.js index 8d5e82e031..babcd4439d 100644 --- a/projects/aera/index.js +++ b/projects/aera/index.js @@ -502,7 +502,16 @@ async function processMorphoBlueTvl(morphoBlueVaults, api, MORPHO_BLUE) { const marketInfo = marketInfos[index]; if (position.collateral > 0) { - api.addToken(marketParam.collateralToken, position.collateral, vault); + try { + // If the collateral token is an ERC4626, we need to convert it to the underlying asset + const [underlyingAsset, collateralAssets] = await Promise.all([ + api.call({ abi: 'address:asset', target: marketParam.collateralToken }), + api.call({ abi: abi.convertToAssets, target: marketParam.collateralToken, params: [position.collateral] }) + ]); + api.addToken(underlyingAsset, collateralAssets, vault); + } catch (e) { + api.addToken(marketParam.collateralToken, position.collateral, vault); + } } if (position.supplyShares > 0) { From c0353f1c8000d8835fbd80d25e97b44d6082beee Mon Sep 17 00:00:00 2001 From: Geoff Hardy Date: Wed, 16 Jul 2025 14:27:35 -0700 Subject: [PATCH 7/8] parallelize a couple of calls --- projects/aera/index.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/projects/aera/index.js b/projects/aera/index.js index babcd4439d..344b5b3721 100644 --- a/projects/aera/index.js +++ b/projects/aera/index.js @@ -468,16 +468,18 @@ async function processMorphoBlueTvl(morphoBlueVaults, api, MORPHO_BLUE) { // Get all markets from the Morpho Blue position oracles const markets = await api.multiCall({ abi: abi.markets, calls: marketCalls }); - const marketInfos = await api.multiCall({ - abi: abi.market, - calls: markets.map(market => ({target: MORPHO_BLUE, params: [market[0]]})) - }); - - // Get market parameters for each market ID - const marketParams = await api.multiCall({ - abi: abi.idToMarketParams, - calls: markets.map(market => ({ target: MORPHO_BLUE, params: [market[0]] })) - }); + const [marketInfos, marketParams] = await Promise.all( + [ + api.multiCall({ + abi: abi.market, + calls: markets.map(market => ({target: MORPHO_BLUE, params: [market[0]]})) + }), + api.multiCall({ + abi: abi.idToMarketParams, + calls: markets.map(market => ({ target: MORPHO_BLUE, params: [market[0]] })) + }) + ] + ); // Get positions for each vault across all markets const positionCalls = []; From 9dcdb68c422ae0ada7023327761a85868311e42b Mon Sep 17 00:00:00 2001 From: Geoff Hardy Date: Tue, 22 Jul 2025 10:08:38 -0700 Subject: [PATCH 8/8] hard-code the list of morpho erc4626 assets --- projects/aera/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/projects/aera/index.js b/projects/aera/index.js index 344b5b3721..558fc6c535 100644 --- a/projects/aera/index.js +++ b/projects/aera/index.js @@ -10,6 +10,10 @@ const ARRAKIS_TOKEN_PREFIX = 'Arrakis Vault V2' const ESXAI_POSITION_ORACLE_NAME = 'EsXai Position Oracle' const SYMBIOTIC_TOKEN_PREFIX = 'Symbiotic Vault' const MORPHO_BLUE_POSITION_ORACLE_NAME = 'MorphoBluePositionOracle' +const MORPHO_ERC4626_ASSETS = [ + '0xd9a442856c234a39a81a089c06451ebaa4306a72', + '0x9d60947d49911e3c262c108f97fe07cde209f9a7' +] const config = { polygon: { @@ -504,14 +508,14 @@ async function processMorphoBlueTvl(morphoBlueVaults, api, MORPHO_BLUE) { const marketInfo = marketInfos[index]; if (position.collateral > 0) { - try { + if (MORPHO_ERC4626_ASSETS.includes(marketParam.collateralToken.toLowerCase())) { // If the collateral token is an ERC4626, we need to convert it to the underlying asset const [underlyingAsset, collateralAssets] = await Promise.all([ api.call({ abi: 'address:asset', target: marketParam.collateralToken }), api.call({ abi: abi.convertToAssets, target: marketParam.collateralToken, params: [position.collateral] }) ]); api.addToken(underlyingAsset, collateralAssets, vault); - } catch (e) { + } else { api.addToken(marketParam.collateralToken, position.collateral, vault); } }