-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Track Ipor PlasmaVault fees #4347
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
base: master
Are you sure you want to change the base?
Conversation
The ipor-protocol adapter exports:
|
The ipor-protocol adapter exports:
|
fees/ipor-protocol/index.ts
Outdated
// Vaults & token mappings per chain | ||
const vaultsPerChain: Record<string, { vault: string, token: string }[]> = { | ||
[CHAIN.ETHEREUM]: [ | ||
{ vault: "0x20e934c725b6703f0aC696F1689008057dB9Ac44", token: ADDRESSES.ethereum.DAI }, // IPOR DAI Ethereum |
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.
please keep these vaults dynamic as we can't maintain the list manually
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.
got it sorry, I'll fix this asap
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 removed the list of token addresses and replaced it with a call to each vault's asset()
function, but I'm not sure how to make the vault addresses dynamic since their PlasmaVaultFactory doesn't seem to be deployed yet.
The ipor-protocol adapter exports:
|
@RohanNero can you replace hardcoded with how tvl adapter is fetching vaults, from github |
The ipor-protocol adapter exports:
|
Thank you, that works perfectly |
@RohanNero we have to also track the yield earned by vaults, which would be counted as supplysiderevenue |
@treeoflife2 I've added a function that attempts to track the yield earned by the vault, but I'm unsure if I've written it correctly (output screenshots below). const IPOR_GITHUB_ADDRESSES_URL = "https://raw.githubusercontent.com/IPOR-Labs/ipor-abi/refs/heads/main/mainnet/addresses.json";
const eventAbis = {
managementFee: "event ManagementFeeRealized(uint256 unrealizedFeeInUnderlying, uint256 unrealizedFeeInShares)",
}
const methodology = {
Fees: 'Management fees collected by the protocol are included in event logs emitted by each vault contract.',
Revenue: 'Total fees collected by the protocol.',
SupplySideRevenue: 'Revenue earned by PlasmaVault liquidity providers.'
}
const addManagementFees = async (options: FetchOptions, vaults: string[], vaultToToken: Record<string, string>, dailyFees: Balances, dailyRevenue: Balances) => {
const logs = await options.api.getLogs({
targets: vaults,
eventAbi: eventAbis.managementFee,
fromBlock: await options.getFromBlock(),
toBlock: await options.getToBlock(),
})
logs.forEach((log: any) => {
const token = vaultToToken[log.address.toLowerCase()]
if (token) {
dailyFees.add(token, log.args[0])
dailyRevenue.add(token, log.args[0])
}
})
}
const addSupplySideRevenue = async (options: FetchOptions, vaults: string[], vaultToToken: Record<string, string>, dailySupplySideRevenue: Balances) => {
const fromBlock = await options.getFromBlock();
const toBlock = await options.getToBlock();
const startAssets = await options.fromApi.multiCall({
abi: 'function totalAssets() view returns (uint256)',
calls: vaults.map(v => ({ target: v })),
block: fromBlock,
skipCache: true,
requery: true,
permitFailure: true
});
const endAssets = await options.toApi.multiCall({
abi: 'function totalAssets() view returns (uint256)',
calls: vaults.map(v => ({ target: v })),
skipCache: true,
requery: true,
permitFailure: true,
block: toBlock,
});
const depositLogs = await options.api.getLogs({
targets: vaults,
eventAbi: 'event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares)',
fromBlock: fromBlock,
toBlock: toBlock,
});
const withdrawLogs = await options.api.getLogs({
targets: vaults,
eventAbi: 'event Withdraw(address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares)',
fromBlock: fromBlock,
toBlock: toBlock,
});
const netDeposits: Record<string, bigint> = {};
depositLogs.forEach(log => {
const vault = log.address.toLowerCase();
netDeposits[vault] = (netDeposits[vault] || 0n) + BigInt(log.args.assets);
});
withdrawLogs.forEach(log => {
const vault = log.address.toLowerCase();
netDeposits[vault] = (netDeposits[vault] || 0n) - BigInt(log.args.assets);
});
vaults.forEach((vault, i) => {
if (!startAssets[i] || !endAssets[i]) return;
const assetsGrowth = BigInt(endAssets[i]) - BigInt(startAssets[i]);
const net = netDeposits[vault.toLowerCase()] || 0n;
const rev = assetsGrowth - net;
if (rev > 0) {
const token = vaultToToken[vault.toLowerCase()];
dailySupplySideRevenue.add(token, rev);
}
});
}
const fetch = () => {
return async (options: FetchOptions) => {
const dailyFees = options.createBalances()
const dailyRevenue = options.createBalances()
const dailySupplySideRevenue = options.createBalances()
const config = await getConfig('ipor/assets', IPOR_GITHUB_ADDRESSES_URL);
const chainConfig = config[options.chain];
if (!chainConfig || !chainConfig.vaults) {
return { dailyFees, dailyRevenue };
}
const vaults = chainConfig.vaults.map((vault: any) => vault.PlasmaVault);
const assetCalls = await options.api.multiCall({
abi: 'function asset() view returns (address)',
calls: vaults.map((v: string) => ({ target: v })),
permitFailure: true,
})
const vaultToToken: Record<string, string> = {}
vaults.forEach((v: string, i: number) => {
if (assetCalls[i]) {
vaultToToken[v.toLowerCase()] = assetCalls[i]
}
})
await addManagementFees(options, vaults, vaultToToken, dailyFees, dailyRevenue);
await addSupplySideRevenue(options, vaults, vaultToToken, dailySupplySideRevenue);
return { dailyFees, dailyRevenue, dailySupplySideRevenue }
}
}
const adapter: SimpleAdapter = {
version: 2,
adapter: {
[CHAIN.ETHEREUM]: {
fetch: fetch(),
start: "9-29-2024",
},
[CHAIN.ARBITRUM]: {
fetch: fetch(),
start: "9-3-2024",
},
[CHAIN.BASE]: {
fetch: fetch(),
start: "11-8-2024",
},
[CHAIN.UNICHAIN]: {
fetch: fetch(),
start: "6-18-2025",
},
[CHAIN.TAC]: {
fetch: fetch(),
start: "7-11-2025",
},
},
methodology
}
export default adapter; Test output examples: ![]() ![]() ![]() |
@RohanNero logic looks correct, small changes, dailyfees would include supplysiderevenue too, and can you check if management fees is included in the supplyside logic or no? to be sure there is no double counting, thanks |
NOTE
Please enable "Allow edits by maintainers" while putting up the PR.
poackage.json/package-lock.json
file as part of your changesAdd fee tracking for IPOR Protocol's PlasmaVaults. Their listed vaults can be found on their app and the documentation related to their management and performance fees can be found here.
Note: I only added tracking for Management fees based on
ManagementFeeRealized
event logs, the 2% performance fee charge still needs to be tracked.