Skip to content
Open
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
47 changes: 38 additions & 9 deletions projects/TurtleClub/assets.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ const tokens = {
WBTC: '0x3aAB2285ddcDdaD8edf438C1bAB47e1a9D05a9b4',
FOXY: '0x5FBDF89403270a1846F5ae7D113A989F850d1566',
CROAK: '0xaCb54d07cA167934F57F829BeE2cC665e1A5ebEF',
REX33: '0xe4eeb461ad1e4ef8b8ef71a33694ccd84af051c4',
xREX: '0xc93b315971a4f260875103f5da84cb1e30f366cc',
z0weETH: '0x77E305B4D4D3b9DA4e82Cefd564F5b948366A44b', // TODO all ZeroLend not priced properly
z0WETH: '0xB4FFEf15daf4C02787bC5332580b838cE39805f5',
z0ezETH: '0x0684FC172a0B8e6A65cF4684eDb2082272fe9050',
Expand Down Expand Up @@ -137,7 +139,16 @@ const tokens = {
}
};

const exceptions = {
const tokenMapping = {
linea: {
[tokens.linea.xREX]: {
coingeckoId: 'etherex',
decimals: 18,
},
},
}

const tokenMappingERC20 = {
ethereum: [
{ token: tokens.ethereum.rEUL, use: tokens.ethereum.EUL },
{ token: tokens.ethereum.ezREZ, use: tokens.ethereum.REZ }, // TODO ezREZ not priced properly
Expand All @@ -146,6 +157,8 @@ const exceptions = {
],
linea: [
{ token: tokens.linea.oLYNX, use: tokens.linea.LYNX },
{ token: tokens.linea.xREX, coingeckoId: "etherex", decimals: 18 },
{ token: tokens.linea.REX33, coingeckoId: "etherex", decimals: 18 },
Copy link
Collaborator

Choose a reason for hiding this comment

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

// { token: tokens.linea.z0WETH, use: tokens.linea.ETH },
// { token: tokens.linea.z0ezETH, use: tokens.linea.ETH },
// { token: tokens.linea.z0rsETH, use: tokens.linea.ETH },
Expand Down Expand Up @@ -184,19 +197,35 @@ const treasuryNFTs = {
mode: [
// { name: 'MODE', veNft: '0x06ab1Dc3c330E9CeA4fDF0C7C6F6Fb6442A4273C', baseToken: tokens.mode.MODE, owner: '0x41FC0479A3E67Ac6d26760D1205dC523abee8b94', useLocked: false },
]
};
};

const turtleVaults = {
linea: [
{address: '0x1b316fA2D6C44b65C1ed6D29b37743Cd362F0f71', strategy: "erc4626" }, // Turtle Linea ETH
{address: '0x7df7e45ab573ace8f872b5d5a1689af7ff1a07f7', strategy: "erc4626" }, // Turtle Linea USDC
],
ethereum: [
{address: '0x6Bf340dB729d82af1F6443A0Ea0d79647b1c3DDf', strategy: "erc20" }, // tacBTC
{address: '0x294eecec65A0142e84AEdfD8eB2FBEA8c9a9fbad', strategy: "erc20" }, // tacETH
{address: '0x699e04F98dE2Fc395a7dcBf36B48EC837A976490', strategy: "erc20" }, // tacUSD
{address: '0xe0dfbe4748ed96350754f1328679bd9647bf9621', strategy: "erc20" }, // Lagoon USDT
{address: '0xbca723C30d55F0915e32019a95AA29ea21fd555C', strategy: "erc20" }, // Lagoon WETH
{address: '0x423b469268b15821107C38d1E1f702877219bc52', strategy: "erc20" }, // Lagoon WBTC
{address: '0xd56031b6E6860Bd41dCe2729D1beD21c387B26ce', strategy: "erc20" }, // Lagoon USDC
{address: '0x1E2aAaDcF528b9cC08F43d4fd7db488cE89F5741', strategy: "erc4626" }, // Morpho USDC
{address: '0xb5e4576C2FAA16b0cC59D1A2f3366164844Ef9E0', strategy: "erc4626" }, // Morpho cbBTC
{address: '0x0bB2751a90fFF62e844b1521637DeD28F3f5046A', strategy: "erc4626" }, // Morpho WETH
],
}


const vaultContracts = [
'0x294eecec65A0142e84AEdfD8eB2FBEA8c9a9fbad', // tacETH
'0x6Bf340dB729d82af1F6443A0Ea0d79647b1c3DDf', // tacBTC
'0x699e04F98dE2Fc395a7dcBf36B48EC837A976490', // tacUSD
];

module.exports = {
defaultTokens,
tokens,
treasuryMultisigs,
exceptions,
tokenMapping,
tokenMappingERC20,
treasuryNFTs,
vaultContracts,
turtleVaults,
};
52 changes: 52 additions & 0 deletions projects/TurtleClub/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const { log } = require("../helper/utils");


const getVaultsERC20Tvl = async (api, vaults) => {
for (const vault of vaults) {
try {
// Get total supply of the boring vault token
const totalSupply = await api.call({
Copy link
Collaborator

Choose a reason for hiding this comment

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

can we count the underlying collateral rather than the receipt token supply?

abi: 'erc20:totalSupply',
target: vault,
});

// Add the vault token to balances with its total supply
// The vault token itself represents the TVL
api.add(vault, totalSupply);
} catch (error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

please no try/catch

log(`Error fetching data for boring vault:`, error.message);
}
}
}

const getERC4626VaultsTvl = async (api, vaults) => {
for (const vault of vaults) {
try {
// Get the underlying asset from the vault
const asset = await api.call({
abi: 'function asset() view returns (address)',
target: vault,
});

// Get total assets from the morpho vault
const totalAssets = await api.call({
abi: 'function totalAssets() view returns (uint256)',
target: vault,
});


// Add the underlying asset with the total assets amount
// This represents the actual underlying assets held by the vault
api.add(asset, totalAssets);

} catch (error) {
log(`Error fetching data for morpho vault ${vault}:`, error.message);
// Continue with other vaults if one fails
}
}
}

module.exports = {
getVaultsERC20Tvl,
getERC4626VaultsTvl,
}
40 changes: 27 additions & 13 deletions projects/TurtleClub/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
const { sumTokensExport } = require("../helper/unwrapLPs");
const { tokens, vaultContracts } = require("./assets");

const plainTokens = Object.values(tokens).map(chain => Object.values(chain)).flat(1);

const { getVaultsERC20Tvl, getERC4626VaultsTvl } = require("./helpers");
const { turtleVaults } = require("./assets");

module.exports = {
// All vaults currently ethereum
doublecounted: true,
ethereum: {
tvl: sumTokensExport({
owners: vaultContracts,
tokens: plainTokens,
permitFailure: true,
tokenConfig: { onlyWhitelisted: false },
}),
tvl: async (api) => {
const erc20Vaults = turtleVaults.ethereum.filter(vault => vault.strategy === "erc20").map(vault => vault.address);

// Handle Boring Vaults
await getVaultsERC20Tvl(api, erc20Vaults);
const erc4626Vaults = turtleVaults.ethereum.filter(vault => vault.strategy === "erc4626").map(vault => vault.address);

// Handle Morpho Vaults
await getERC4626VaultsTvl(api, erc4626Vaults);

return api.getBalances();
},
},
linea: {
tvl: async (api) => {
const erc4626Vaults = turtleVaults.linea.filter(vault => vault.strategy === "erc4626").map(vault => vault.address);

// Handle ERC4626 Vaults
await getERC4626VaultsTvl(api, erc4626Vaults);

return api.getBalances();
},
},
};
};

30 changes: 24 additions & 6 deletions projects/treasury/turtleclub.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { tokens, treasuryMultisigs, treasuryNFTs, defaultTokens, exceptions } = require('../TurtleClub/assets');
const { tokens, treasuryMultisigs, treasuryNFTs, defaultTokens, tokenMappingERC20, tokenMapping } = require('../TurtleClub/assets');
const { ankrChainMapping } = require('../helper/token');
const { sumTokens2, unwrapSolidlyVeNft } = require('../helper/unwrapLPs');
const SOLIDLY_VE_NFT_ABI = require('../helper/abis/solidlyVeNft.json');
Expand All @@ -13,7 +13,7 @@ function formatForTreasuryExport(tokens = {}) {
return treasuryExportsFormat;
}

async function sumPositions(api, NFTs) {
async function sumNFTs(api, NFTs) {
const waitNFTs = [];
for (const treasuryNFT of NFTs) {
const { veNft, owner, baseToken, useLocked = true } = treasuryNFT;
Expand Down Expand Up @@ -50,9 +50,12 @@ function turtleTreasuryExports(config, treasuryNFTs) {
}

const tvl = async (api) => {
if (exceptions[chain]?.length > 0) {
if (tokenMappingERC20[chain]?.length > 0) {
const es = [];
exceptions[chain].forEach(async ({ token, use }) => {
tokenMappingERC20[chain].forEach(async ({ token, use, coingeckoId, decimals }) => {
const balanceLogic = coingeckoId ?
bal => api.add(coingeckoId, bal / (10 ** decimals), { skipChain: true }) :
bal => api.add(use, bal);
es.push((async () => {
const balances = await api.multiCall({
abi: 'erc20:balanceOf',
Expand All @@ -62,14 +65,29 @@ function turtleTreasuryExports(config, treasuryNFTs) {
})),
permitFailure: true
});
balances.filter(b => b !== '0' && !!b).forEach(bal => api.add(use, bal));
balances.filter(b => b !== '0' && !!b).map(bal => Number(bal)).forEach(balanceLogic);
})());
});
await Promise.allSettled(es);
}

const xRexAddr = tokens.linea.xREX;
const xRexStakedBalances = await api.multiCall({
abi: 'function balanceOf(address) view returns (uint256)',
calls: treasuryMultisigs.map(owner => ({ params: [owner] })),
target: '0xedd7cbc9c47547d0b552d5bc2be76135f49c15b1', // VoteModule staking contract
permitFailure: true,
});
xRexStakedBalances.filter(b => b && b !== '0').map(bal => Number(bal)).forEach(balance => {
const xRexMapping = tokenMapping.linea[xRexAddr];
if (xRexMapping)
api.add(xRexMapping.coingeckoId, balance / (10 ** xRexMapping.decimals), { skipChain: true });
else
api.add(xRexAddr, balance);
});

await sumTokens2({ ...api, api, ...tvlConfig });
if (treasuryNFTs[chain]?.length > 0) await sumPositions(api, treasuryNFTs[chain]);
if (treasuryNFTs[chain]?.length > 0) await sumNFTs(api, treasuryNFTs[chain]);
};
exportObj[chain] = { tvl };
}
Expand Down
Loading