Skip to content
Draft
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
24 changes: 20 additions & 4 deletions fees/tulip-capital.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,46 @@
import { CHAIN } from "../helpers/chains";
import { CuratorConfig, getCuratorExport } from "../helpers/curators";

const curatorConfig: CuratorConfig = {
vaults: {
ethereum: {
[CHAIN.ETHEREUM]: {
morphoVaultOwners: [
'0x59e608E4842162480591032f3c8b0aE55C98d104',
],
eulerVaultOwners: [
'0x7c615e12D1163fc0DdDAA01B51922587034F5C93',
],
lagoon: [
"0x936facdf10c8c36294e7b9d28345255539d81bc7", // RockSolid rock.rETH
"0xb09f761cb13baca8ec087ac476647361b6314f98", // Flagship cbBTC
],
},
berachain: {
[CHAIN.BERACHAIN]: {
eulerVaultOwners: [
'0x18d23B961b11079EcD499c0EAD8E4F347e4d3A66',
],
},
bob: {
[CHAIN.BOB]: {
eulerVaultOwners: [
'0x7c615e12D1163fc0DdDAA01B51922587034F5C93',
],
},
bsc: {
[CHAIN.BSC]: {
eulerVaultOwners: [
'0x7c615e12D1163fc0DdDAA01B51922587034F5C93',
],
},
[CHAIN.AVAX]: {
lagoon: [
"0x3048925b3ea5a8c12eecccb8810f5f7544db54af", // Turtle Avalanche USDC
"0xb893c8d7000e0408eb7d168152ec7fefdd0d25e3", // Turtle Avalanche BTC.b
],
},
[CHAIN.TAC]: {
lagoon:[
'0x279385c180f5d01c4a4bdff040f17b8957304762' // Noon USN TAC
]
}
}
}

Expand Down
65 changes: 64 additions & 1 deletion helpers/curators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ export interface CuratorConfig {
methodology?: any;

vaults: {
// chain =>
// chain =>
[key: string]: {
start?: IStartTimestamp | number | string;
morpho?: Array<string>;
euler?: Array<string>;
lagoon?: Array<string>;

// initial owner of morpho vaults
morphoVaultOwners?: Array<string>;
Expand Down Expand Up @@ -227,6 +228,63 @@ export async function getEulerVaultFee(options: FetchOptions, balances: Balances
}
}

export async function getLagoonVaultFee(options: FetchOptions, balances: Balances, vaults: Array<string>) {
const feeRateAbi = "function feeRates() external view returns(tuple(uint16 managementRate, uint16 performanceRate))";
const ONE_YEAR = 365 * 24 * 60 * 60;
const { getERC4626VaultsYield } = await import("../erc4626");

// Get fee details from all vaults
const feeDetails = await options.api.multiCall({
abi: feeRateAbi,
calls: vaults,
permitFailure: true
});

const assets = await options.api.multiCall({
abi: 'address:asset',
calls: vaults,
permitFailure: true
});

const totalAssets = await options.api.multiCall({
abi: 'uint256:totalAssets',
calls: vaults,
permitFailure: true
});

for (let i = 0; i < vaults.length; i++) {
if (!feeDetails[i] || !assets[i] || !totalAssets[i]) continue;

const { performanceRate, managementRate } = feeDetails[i];

const dailyYield = await getERC4626VaultsYield({ options, vaults: [vaults[i]] });

const dailyManagementFee = (totalAssets[i]) * (managementRate / 10000) * ((options.toTimestamp - options.fromTimestamp) / ONE_YEAR);

const dailyYieldUSD = await dailyYield.getUSDValue();

if (dailyYieldUSD > 0) {
// Calculate total yield before performance fee
// If performance fee is 10%, and depositors get dailyYield, then dailyYield represents 90% of total
// So totalYieldBeforeFee = dailyYield / 0.9
const totalYieldBeforeFee = dailyYield.clone(1 / (1 - performanceRate / 10000), METRIC.ASSETS_YIELDS);

const vaultTotalFees = totalYieldBeforeFee.clone();
vaultTotalFees.add(assets[i], dailyManagementFee, METRIC.MANAGEMENT_FEES);

const performanceFee = totalYieldBeforeFee.clone();
performanceFee.subtract(dailyYield, METRIC.ASSETS_YIELDS);

balances.dailyFees.add(vaultTotalFees);
balances.dailyRevenue.add(performanceFee);
balances.dailyRevenue.add(assets[i], dailyManagementFee, METRIC.MANAGEMENT_FEES);
} else if (dailyManagementFee > 0) {
balances.dailyFees.add(assets[i], dailyManagementFee, METRIC.MANAGEMENT_FEES);
balances.dailyRevenue.add(assets[i], dailyManagementFee, METRIC.MANAGEMENT_FEES);
}
}
}

export function getCuratorExport(curatorConfig: CuratorConfig): SimpleAdapter {
const methodology = curatorConfig.methodology ? curatorConfig.methodology : {
Fees: 'Total yields from deposited assets in all curated vaults.',
Expand All @@ -244,12 +302,17 @@ export function getCuratorExport(curatorConfig: CuratorConfig): SimpleAdapter {

const morphoVaults = await getMorphoVaults(options, vaults.morpho, vaults.morphoVaultOwners);
const eulerVaults = await getEulerVaults(options, vaults.euler, vaults.eulerVaultOwners);
const lagoonVaults = vaults.lagoon || [];

if (morphoVaults.length > 0) {
await getMorphoVaultFee(options, { dailyFees, dailyRevenue }, morphoVaults)
}
if (eulerVaults.length > 0) {
await getEulerVaultFee(options, { dailyFees, dailyRevenue }, eulerVaults)
}
if (lagoonVaults.length > 0) {
await getLagoonVaultFee(options, { dailyFees, dailyRevenue }, lagoonVaults)
}

const dailySupplySideRevenue = dailyFees.clone(1, METRIC.ASSETS_YIELDS)
dailySupplySideRevenue.subtract(dailyRevenue, METRIC.ASSETS_YIELDS)
Expand Down
Loading