Skip to content
Open
Changes from 1 commit
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
88 changes: 87 additions & 1 deletion src/adaptors/fusion-by-ipor/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,77 @@
const axios = require('axios');
const sdk = require('@defillama/sdk');
const { addMerklRewardApy } = require('../merkl/merkl-additional-reward');

const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
const ONE_ETHER = 10n ** 18n;
const SECONDS_IN_YEAR = 31556952n;

const GET_REWARDS_CLAIM_MANAGER_ADDRESS_ABI =
'function getRewardsClaimManagerAddress() view returns (address)';
const TOTAL_ASSETS_ABI = 'function totalAssets() view returns (uint256)';
const GET_VESTING_DATA_ABI = {
type: 'function',
name: 'getVestingData',
inputs: [],
stateMutability: 'view',
outputs: [{
type: 'tuple',
components: [
{ name: 'vestingTime', type: 'uint32' },
{ name: 'updateBalanceTimestamp', type: 'uint32' },
{ name: 'transferredTokens', type: 'uint128' },
{ name: 'lastUpdateBalance', type: 'uint128' },
],
}],
};

async function getVestingRewardsApy(vaultAddress, chain) {
try {
const rcm = (await sdk.api.abi.call({
target: vaultAddress,
chain,
abi: GET_REWARDS_CLAIM_MANAGER_ADDRESS_ABI,
})).output;

if (!rcm || rcm.toLowerCase() === ZERO_ADDRESS) return 0;

const [vestingDataRes, totalAssetsRes] = await Promise.all([
sdk.api.abi.call({ target: rcm, chain, abi: GET_VESTING_DATA_ABI }),
sdk.api.abi.call({ target: vaultAddress, chain, abi: TOTAL_ASSETS_ABI }),
]);

const vestingTime = BigInt(vestingDataRes.output.vestingTime);
const lastUpdateBalance = BigInt(vestingDataRes.output.lastUpdateBalance);
const totalAssets = BigInt(totalAssetsRes.output);

if (vestingTime === 0n || lastUpdateBalance === 0n || totalAssets === 0n) {
return 0;
}

const apy_18 = ((lastUpdateBalance * ONE_ETHER) / totalAssets)
* (SECONDS_IN_YEAR / vestingTime)
* 100n;

return Number(apy_18) / 1e18;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
} catch (e) {
return 0;
}
}

const IPOR_GITHUB_ADDRESSES_URL = "https://raw.githubusercontent.com/IPOR-Labs/ipor-abi/refs/heads/main/mainnet/addresses.json";
const FUSION_API_URL = 'https://api.ipor.io/fusion/vaults';

const VESTING_APY_VAULTS = {
ethereum: ["0xb9e806e8f2d94c015ffefa90cd24ecce18f1663c"],
arbitrum: [],
base: ["0x5900C3b72458F12967DC1bef35b92d271F5cDBc1", "0x17d0f109ee895bad0b68aa104aa72bd0b003ad8e", "0xe883426B4fc84A7f5cc86415CAbBef43E73a4CC8"],
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
unichain: [],
tac: [],
ink: [],
plasma: [],
avax: [],
katana: [],
};
const CHAIN_CONFIG = {
ethereum: {
chainId: 1
Expand Down Expand Up @@ -76,6 +145,7 @@ async function buildPool(vault) {
symbol: `${vault.asset}`,
tvlUsd,
apyBase,
apyReward : 0,
underlyingTokens: [vault.assetAddress],
poolMeta: `${vault.name}`,
url
Expand All @@ -100,7 +170,23 @@ const apy = async() => {
)
);

return addMerklRewardApy(pools, 'ipor');
const poolsWithMerkl = await addMerklRewardApy(pools, 'ipor');

return Promise.all(poolsWithMerkl.map(async (pool) => {
const allowedVaults = VESTING_APY_VAULTS[pool.chain] || [];
if (!allowedVaults.includes(pool.pool.toLowerCase())) return pool;

const vestingApy = await getVestingRewardsApy(pool.pool, pool.chain);
if (vestingApy <= 0) return pool;
const rewardTokens = [
...new Set([...(pool.rewardTokens || []), ...pool.underlyingTokens]),
];
return {
...pool,
apyReward: (pool.apyReward || 0) + vestingApy,
rewardTokens,
};
}));
};

module.exports = {
Expand Down
Loading