-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Changes from 7 commits
274b7ea
c9dbad5
44f1fd3
78d031d
9e0321d
a40a81e
6b6af62
4112e73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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) => { | ||
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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: shall this variable be called There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; |
There was a problem hiding this comment.
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)