Skip to content
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

Add factor-v2 #1041

Closed
wants to merge 20 commits into from
Closed
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
39 changes: 39 additions & 0 deletions src/adaptors/factor-v2/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const vaults = require('./vaults');
const { getTvl, getApr } = require('./shared');

async function getSingleYieldVaultAPY() {
const poolData = await Promise.all(
vaults.map(async (vault) => {
const project = 'factor-v2';
const chain = 'arbitrum';
const pool = `${vault.poolAddress}-${chain}`.toLowerCase();
const url = `https://app.factor.fi/vault/${vault.poolAddress}`;
const symbol = vault.symbol;

const [tvlUsd, apyBase] = await Promise.all([
getTvl(vault.poolAddress, vault.underlyingToken, vault.strategy),
getApr(vault.poolAddress, vault.underlyingToken, vault.strategy),
]);

const data = {
pool,
chain,
project,
symbol,
tvlUsd,
apyBase,
underlyingTokens: [vault.underlyingToken],
url,
};

return data;
})
);

return poolData;
}

module.exports = {
timetravel: false,
apy: getSingleYieldVaultAPY,
};
101 changes: 101 additions & 0 deletions src/adaptors/factor-v2/shared.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const sdk = require('@defillama/sdk3');
const utils = require('../utils');

const {
getMuxLpApr,
getGlpApr,
getVlpApr,
getLodestarApr,
getLodestarTokenPriceInUSD,
getPendleApr,
getSJoeApr,
getSiloApr,
getTenderApr,
getOliveApr,
getPxGMXApr,
getPenpieApr
} = require('./strategy-adapter');
const { getCoinDataFromDefillamaAPI } = require('./strategy-adapter/utils');

async function getApr(poolAddress, underlyingTokenAddress, strategy) {
let apr = 0;
switch (strategy) {
case 'GLPStrategy':
apr = await getGlpApr();
break;
case 'MuxStrategy':
apr = await getMuxLpApr();
break;
case 'VelaStrategy':
apr = await getVlpApr();
break;
case 'LodestarStrategy':
apr = await getLodestarApr(underlyingTokenAddress);
break;
case 'PenpieStrategy':
apr = await getPenpieApr(underlyingTokenAddress);
break;
case 'PendleStrategy':
apr = await getPendleApr(underlyingTokenAddress);
break;
case 'TraderJoeStrategy':
apr = await getSJoeApr(underlyingTokenAddress);
break;
case 'SiloStrategy':
apr = await getSiloApr(underlyingTokenAddress);
break;
case 'TenderStrategy':
apr = await getTenderApr(underlyingTokenAddress);
break;
case 'OliveStrategy':
apr = await getOliveApr();
break;
case 'RedactedStrategy':
apr = await getPxGMXApr();
break;
default:
apr = 0;
}

const harvestCountPerDay = 3;
const apyBase = utils.aprToApy(apr, harvestCountPerDay * 365);

return apyBase;
}

async function getTvl(poolAddress, underlyingTokenAddress, strategy) {
let underlyingTokenPrice = 0;

if (strategy == 'LodestarStrategy') {
underlyingTokenPrice = await getLodestarTokenPriceInUSD(
underlyingTokenAddress
);
} else if (strategy == "RedactedStrategy") {
const gmxCoin = await getCoinDataFromDefillamaAPI('arbitrum','0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a')
underlyingTokenPrice = gmxCoin.price;
} else {
underlyingTokenPrice = (
await utils.getPrices([underlyingTokenAddress], 'arbitrum')
).pricesByAddress[underlyingTokenAddress.toLowerCase()];
}

const [{ output: assetBalance }, { output: assetDecimals }] =
await Promise.all([
sdk.api.abi.call({
target: poolAddress,
abi: 'uint256:assetBalance',
chain: 'arbitrum',
}),
sdk.api.abi.call({
target: underlyingTokenAddress,
abi: 'erc20:decimals',
chain: 'arbitrum',
}),
]);

const tvlUsd = (assetBalance / 10 ** assetDecimals) * underlyingTokenPrice;

return tvlUsd;
}

module.exports = { getTvl, getApr };
17 changes: 17 additions & 0 deletions src/adaptors/factor-v2/strategy-adapter/glp-adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const { apy } = require('../../gmx-v1');
const { getAprFromDefillamaPool } = require('./utils');

/*//////////////////////////////////////////////////////////////////////////////
GLP APR
//////////////////////////////////////////////////////////////////////////////*/

async function getGlpApr() {
const apr = await getAprFromDefillamaPool(
apy,
'0x1aDDD80E6039594eE970E5872D247bf0414C8903'
);

return apr;
}

module.exports = { getGlpApr };
13 changes: 13 additions & 0 deletions src/adaptors/factor-v2/strategy-adapter/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module.exports = {
...require('./lodestar-adapter'),
...require('./mux-adapter'),
...require('./glp-adapter'),
...require('./vela-adapter'),
...require('./pendle-adapter'),
...require('./sjoe-adapter'),
...require('./silo-adapter'),
...require('./tender-adapter'),
...require('./olive-adapter'),
...require('./redacted-adapter'),
...require('./penpie-adapter'),
};
59 changes: 59 additions & 0 deletions src/adaptors/factor-v2/strategy-adapter/lodestar-adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const sdk = require('@defillama/sdk3');
const utils = require('../../utils');
const { makeReadable } = require('./utils');

async function getSupplyRatePerBlock(assetAddress) {
const { output } = await sdk.api.abi.call({
target: assetAddress,
abi: 'uint256:supplyRatePerBlock',
chain: 'arbitrum',
});
return {
supplyRatePerBlock: makeReadable(output),
};
}

async function getLodestarApr(assetAddress) {
const { supplyRatePerBlock } = await getSupplyRatePerBlock(assetAddress);
const blocksPerYear = 7200 * 365;
const apr = (1 + supplyRatePerBlock) ** blocksPerYear - 1;
return apr * 100;
}

async function getExchangeRateStored(assetAddress) {
const { output } = await sdk.api.abi.call({
target: assetAddress,
abi: 'uint256:exchangeRateStored',
chain: 'arbitrum',
});
return {
exchangeRateStored: makeReadable(output, 16),
};
}

async function getLodestarUnderlyingTokenPriceInUSD(lToken) {
const { output: underlyingTokenAddress } = await sdk.api.abi.call({
target: lToken,
abi: 'address:underlying',
chain: 'arbitrum',
});
const underlyingLodestarTokenPriceInUSD = (
await utils.getPrices([underlyingTokenAddress], 'arbitrum')
).pricesByAddress[underlyingTokenAddress.toLowerCase()];

return { underlyingLodestarTokenPriceInUSD };
}

async function getLodestarTokenPriceInUSD(lToken) {
const [{ exchangeRateStored }, { underlyingLodestarTokenPriceInUSD }] =
await Promise.all([
getExchangeRateStored(lToken),
getLodestarUnderlyingTokenPriceInUSD(lToken),
]);

const lTokenPriceInUSD =
underlyingLodestarTokenPriceInUSD * exchangeRateStored;
return lTokenPriceInUSD;
}

module.exports = { getLodestarApr, getLodestarTokenPriceInUSD };
108 changes: 108 additions & 0 deletions src/adaptors/factor-v2/strategy-adapter/mux-adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const sdk = require('@defillama/sdk3');
const utils = require('../../utils');
const { makeReadable } = require('./utils');

/*//////////////////////////////////////////////////////////////////////////////
Mux Reward Router
//////////////////////////////////////////////////////////////////////////////*/

async function getDataFromRewardRouter() {
const REWARD_ROUTER_ADDRESS = '0xaf9C4F6A0ceB02d4217Ff73f3C95BbC8c7320ceE';

const rewardRouterABIs = [
'uint256:feeRewardRate',
'uint256:muxRewardRate',
'uint256:poolOwnedRate',
'uint256:votingEscrowedRate',
];

const [feeRewardRate, muxRewardRate, poolOwnedRate, votingEscrowedRate] =
await Promise.all(
rewardRouterABIs.map(async (abi) => {
const { output } = await sdk.api.abi.call({
target: REWARD_ROUTER_ADDRESS,
abi: abi,
chain: 'arbitrum',
});
return output;
})
);
return {
feeRewardRate: makeReadable(feeRewardRate),
muxRewardRate: makeReadable(muxRewardRate),
poolOwnedRate: makeReadable(poolOwnedRate),
votingEscrowedRate: makeReadable(votingEscrowedRate),
};
}

/*//////////////////////////////////////////////////////////////////////////////
Mux Pool Owned Liquidity
//////////////////////////////////////////////////////////////////////////////*/

async function getDataFromPOL() {
const MLP_ADDRESS = '0x7CbaF5a14D953fF896E5B3312031515c858737C8';
const POL_ADDRESS = '0x18891480b9dd2aC5eF03220C45713d780b5CFdeF';

const { output: mlpPolBalance } = await sdk.api.abi.call({
target: MLP_ADDRESS,
abi: 'erc20:balanceOf',
params: [POL_ADDRESS],
chain: 'arbitrum',
});

return {
mlpPolBalance: makeReadable(mlpPolBalance),
};
}

/*//////////////////////////////////////////////////////////////////////////////
Token Prices
//////////////////////////////////////////////////////////////////////////////*/

async function getTokenPrices() {
const tokenAddresses = [
'0x7CbaF5a14D953fF896E5B3312031515c858737C8', // MuxLP
'0x4e352cf164e64adcbad318c3a1e222e9eba4ce42', // MCB
'0x82af49447d8a07e3bd95bd0d56f35241523fbab1', // WETH
];

const tokenPrices = await utils.getPrices(tokenAddresses, 'arbitrum');

const mlpPrice = tokenPrices.pricesByAddress[tokenAddresses[0].toLowerCase()];
const mcbPrice = tokenPrices.pricesByAddress[tokenAddresses[1].toLowerCase()];
const ethPrice = tokenPrices.pricesByAddress[tokenAddresses[2].toLowerCase()];

return { mlpPrice, mcbPrice, ethPrice };
}

/*//////////////////////////////////////////////////////////////////////////////
Mux LP APR
//////////////////////////////////////////////////////////////////////////////*/

async function getMuxLpApr() {
const [
{ mlpPolBalance },
{ feeRewardRate, muxRewardRate, poolOwnedRate, votingEscrowedRate },
{ mlpPrice, mcbPrice, ethPrice },
] = await Promise.all([
getDataFromPOL(),
getDataFromRewardRouter(),
getTokenPrices(),
]);

const mlpCirculatingSupply = mlpPolBalance / poolOwnedRate;

const muxAPR =
(muxRewardRate * mcbPrice * 86400 * 365 * (1 - votingEscrowedRate)) /
(mlpCirculatingSupply * mlpPrice);

const ethAPR =
(feeRewardRate * ethPrice * 86400 * 365 * 0.7) /
(mlpCirculatingSupply * mlpPrice);

const totalAPR = (muxAPR + ethAPR) * 100;

return totalAPR;
}

module.exports = { getMuxLpApr };
22 changes: 22 additions & 0 deletions src/adaptors/factor-v2/strategy-adapter/olive-adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const { getAprFromDefillamaPool } = require('./utils');
const { apy } = require('../../beefy');

async function getOliveApr() {
const beefyApr = await getAprFromDefillamaPool(
apy,
'0x9dbbbaecacedf53d5caa295b8293c1def2055adc'
);

const ampFactor = 1.33;
const weeksPerYear = 365 / 7;
const oliveBoost = 0.05;
const apr =
(1 + (beefyApr * ampFactor) / weeksPerYear) ** weeksPerYear -
1 +
oliveBoost;

return apr * 100;
}

module.exports = { getOliveApr };

10 changes: 10 additions & 0 deletions src/adaptors/factor-v2/strategy-adapter/pendle-adapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { apy } = require('../../pendle');
const { getAprFromDefillamaPool } = require('./utils');

async function getPendleApr(poolId) {
const apr = await getAprFromDefillamaPool(apy, poolId);

return apr;
}

module.exports = { getPendleApr };
Loading
Loading