Skip to content

track euler fees & revenue #2708

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 5, 2025
127 changes: 127 additions & 0 deletions fees/euler/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { Adapter, FetchOptions } from "../../adapters/types"
import { CHAIN } from "../../helpers/chains"
import * as sdk from "@defillama/sdk";

const UINT256_MAX = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";

const eVaultFactories = {
[CHAIN.ETHEREUM]: "0x29a56a1b8214D9Cf7c5561811750D5cBDb45CC8e",
[CHAIN.SONIC]: "0xF075cC8660B51D0b8a4474e3f47eDAC5fA034cFB",
[CHAIN.BASE]: "0x7F321498A801A191a93C840750ed637149dDf8D0"
}

const eulerFactoryABI = {
vaultLength: "function getProxyListLength() view returns (uint256)",
getProxyListSlice: "function getProxyListSlice(uint256 start, uint256 end) view returns (address[] list)",
}

const eulerVaultABI = {
asset: "function asset() view returns (address)",
accumulatedFees: "function accumulatedFees() view returns (uint256)",
convertToAssets: "function convertToAssets(uint256 shares) view returns (uint256)",
convertFees: "event ConvertFees(address indexed account, address indexed protocolReceiver, address indexed governorReceiver, uint256 protocolShares, uint256 governorShares)",
vaultStatus: "event VaultStatus(uint256 totalShares, uint256 totalBorrows, uint256 accumulatedFees, uint256 cash, uint256 interestAccumulator, uint256 interestRate, uint256 timestamp)",
interestAccumulated: "event InterestAccrued(address indexed account, uint256 borrowIndex)"
}

const getVaults = async ({createBalances, api, fromApi, toApi, getLogs, chain}: FetchOptions, {
dailyFees,
dailyRevenue,
}: {
dailyFees?: sdk.Balances,
dailyRevenue?: sdk.Balances,
}) => {

if (!dailyFees) dailyFees = createBalances()
if (!dailyRevenue) dailyRevenue = createBalances()
const vaults = await fromApi.call({target: eVaultFactories[chain], abi: eulerFactoryABI.getProxyListSlice, params: [0, UINT256_MAX]})
const underlyings = await fromApi.multiCall({calls: vaults, abi: eulerVaultABI.asset})
underlyings.forEach((underlying, index) => {
if (!underlying) underlyings[index] = '0x0000000000000000000000000000000000000000'
})

const accumulatedFeesStart = await fromApi.multiCall({calls: vaults, abi: eulerVaultABI.accumulatedFees})
const accumulatedFeesEnd = await toApi.multiCall({calls: vaults, abi: eulerVaultABI.accumulatedFees})

const interestAccrued = (await getLogs({targets: vaults, eventAbi: eulerVaultABI.interestAccumulated, flatten: false})).map((logs) => {

Choose a reason for hiding this comment

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

I thought about it a little bit more and I realized that this solution is sub-optimal. The reason for that is that this event is only emitted when the account is touched. It means that someone can create a borrow position and then simply don't interact with their account for a long time until repay. Only then the InterestAccrued will get emitted which would be obviously wrong as we want to track the interest accrued daily.

Different way we can approach it is by using VaultStatus logs:
https://github.com/euler-xyz/euler-vault-kit/blob/master/src/EVault/shared/Events.sol#L59-L67

The difficulty here is that the interest accrued would need to be calculated by looking at the current VaultStatus log and the previous one. It would look like this:
interest = prevTotalBorrows * (currentInterestAccumulator/prevInterestAccumulator - 1)

I guess that you can easily handle that by writing a reduce function. The only hard thing is to get the last log from the day preceding the one you're processing at the moment (or carry it over somehow)

if (!logs.length) return 0n;
let totalInterest = 0n;
for (const log of logs) {
totalInterest += BigInt(log.borrowIndex.toString());
}
return totalInterest;
})

const logs = (await getLogs({targets: vaults, eventAbi: eulerVaultABI.convertFees, flatten: false})).map((vaultLogs) => {
if (!vaultLogs.length) return 0n;
let totalShares = 0n;
for (const log of vaultLogs) {
totalShares += (log.protocolShares + log.governorShares);
}
return totalShares;
});

//calculate (accumulatedFeesEnd - accumulatedFeesStart) + totalShares from convertFees
const accumulatedFees = accumulatedFeesEnd.map((fees, i) => {
const feesEnd = BigInt(fees.toString());
const feesStart = BigInt(accumulatedFeesStart[i].toString());
return feesEnd - feesStart + logs[i];
});

//we then convert the accumulatedFees to asset by calling convertToAssets at the end therefore we won't have any problem with conversion rate changing
const totalAssets = await toApi.multiCall({
calls: accumulatedFees.map((fees, i) => ({
target: vaults[i],
params: [fees.toString()]
})),
abi: eulerVaultABI.convertToAssets,
});

totalAssets.forEach((assets, i) => {
dailyFees.add(underlyings[i], interestAccrued[i])
dailyRevenue.add(underlyings[i], assets)
})
const dailySupplySideRevenue = dailyFees.clone()

Choose a reason for hiding this comment

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

nit: shall this variable be called dailySupplySideRevenue? to be consistent I guess it should be called something like dailySupplySideFees?

Copy link
Member

Choose a reason for hiding this comment

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

you are right, but it is a bit late to fix now, though we could change this display label for this field in the website for all protocols

dailySupplySideRevenue.subtract(dailyRevenue)

return {dailyFees,dailyRevenue, dailySupplySideRevenue}
}

const fetch = async (options: FetchOptions) => {
return await getVaults(options, {})
}

const methodology = {
dailyFees: "Interest that is paid by the borrowers to the vaults",
dailyRevenue: "Protocol & Governor fees share"
}

const adapters: Adapter = {
adapter: {
[CHAIN.ETHEREUM]: {
fetch: fetch,
start: '2024-08-18',
meta: {
methodology
}
},
[CHAIN.SONIC]: {
fetch: fetch,
start: '2025-01-31',
meta: {
methodology
}
},
[CHAIN.BASE]: {
fetch: fetch,
start: '2024-11-27',
meta: {
methodology
}
}
},

version: 2
}

export default adapters;
Loading