Skip to content

Commit

Permalink
feat(abracadabra): Add support for Multi Reward Staking farms (#1097)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xmDreamy authored Dec 1, 2023
1 parent 851377f commit a1e8e3c
Showing 3 changed files with 86 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/adaptors/abracadabra/abis/MultiRewardsStaking.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs":[{"internalType":"address","name":"_stakingToken","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ErrInvalidTokenAddress","type":"error"},{"inputs":[],"name":"ErrRewardAlreadyAdded","type":"error"},{"inputs":[],"name":"ErrRewardPeriodStillActive","type":"error"},{"inputs":[],"name":"ErrZeroAmount","type":"error"},{"inputs":[],"name":"ErrZeroDuration","type":"error"},{"inputs":[],"name":"NotAllowedOperator","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LogRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"LogRewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"rewardsToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"LogRewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"LogRewardsDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LogStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LogWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"},{"indexed":false,"internalType":"bool","name":"","type":"bool"}],"name":"OperatorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"addReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"getRewardForDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"operators","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"rewardData","outputs":[{"components":[{"internalType":"uint256","name":"rewardsDuration","type":"uint256"},{"internalType":"uint256","name":"periodFinish","type":"uint256"},{"internalType":"uint256","name":"rewardRate","type":"uint256"},{"internalType":"uint256","name":"lastUpdateTime","type":"uint256"},{"internalType":"uint256","name":"rewardPerTokenStored","type":"uint256"}],"internalType":"struct MultiRewardsStaking.Reward","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"}],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"setRewardsDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"userRewardPerTokenPaid","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
2 changes: 2 additions & 0 deletions src/adaptors/abracadabra/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const cauldrons = require('./cauldrons');
const multiRewardFarms = require('./multi-reward-farms');
const farms = require('./farms');
const magicGlp = require('./magic-glp');

const getApy = async () => [
...(await cauldrons()),
...(await farms()),
...(await magicGlp()),
...(await multiRewardFarms()),
];

module.exports = {
83 changes: 83 additions & 0 deletions src/adaptors/abracadabra/multi-reward-farms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const axios = require('axios');
const { utils: { formatUnits }, BigNumber } = require('ethers');
const sdk = require('@defillama/sdk');
const utils = require('../utils');
const MULTI_REWARDS_STAKING = require('./abis/MultiRewardsStaking.json');
const SECONDS_PER_YEAR = 31536000;

const FARMS = {
arbitrum: [{
address: "0x6d2070b13929Df15B13D96cFC509C574168988Cd",
stakingToken: "0x30dF229cefa463e991e29D42DB0bae2e122B2AC7",
stakingTokenPool: "0bf3cb38-1908-4d85-87c3-af62651d5a03",
rewardTokens: [
"0x912CE59144191C1204E64559FE8253a0e49E6548",
"0x3E6648C5a70A150A88bCE65F4aD4d506Fe15d2AF"
]
}]
}

const getApy = async () => {
const pools = await Promise.all(Object.entries(FARMS).map(async ([chain, chainFarms]) => {
const rewardDataCalls = chainFarms.flatMap(({ address, rewardTokens }) =>
rewardTokens.map((rewardToken) => ({
target: address,
params: [rewardToken]
})));
const [rewardDataResults, totalSupplyResults, prices, symbols, yieldPools] = await Promise.all([
sdk.api.abi.multiCall({
abi: MULTI_REWARDS_STAKING.find(({ name }) => name === "rewardData"),
calls: rewardDataCalls,
chain,
}).then((call => call.output)),
sdk.api.abi.multiCall({
abi: MULTI_REWARDS_STAKING.find(({ name }) => name === "totalSupply"),
calls: chainFarms.map(({ address }) => ({ target: address })),
chain
}).then((call => call.output.map((x) => [x.input.target.toLowerCase(), x])))
.then(Object.fromEntries),
utils.getPrices(chainFarms.flatMap(({ rewardTokens }) => rewardTokens), chain),
sdk.api.abi.multiCall({
abi: "erc20:symbol",
calls: chainFarms.map(({ stakingToken }) => ({
target: stakingToken
})),
chain
}).then((call => call.output.map((x) => [x.input.target.toLowerCase(), x])))
.then(Object.fromEntries),
axios.get('https://yields.llama.fi/pools').then((result) => result.data.data),
]);

const tvlUsdChainFarms = Object.fromEntries(chainFarms.map(({ address }) => {
const totalSupply = formatUnits(totalSupplyResults[address.toLowerCase()].output, 18);
const totalSupplyUsd = totalSupply; // Assume $1
return [address.toLowerCase(), totalSupplyUsd];
}))
const aprs = Object.fromEntries(chainFarms.map(({ address }) => [address.toLowerCase(), 0]));
rewardDataResults.forEach(({ input: { target: farm, params: [rewardToken] }, output: { rewardRate } }) => {
const rewardsPerYearRaw = BigNumber.from(rewardRate).mul(SECONDS_PER_YEAR);
const rewardsPerYear = formatUnits(rewardsPerYearRaw, 18);
const rewardsPerYearUsd = rewardsPerYear * prices.pricesByAddress[rewardToken.toLowerCase()];
const rewardApr = rewardsPerYearUsd / tvlUsdChainFarms[farm.toLowerCase()] * 100;

aprs[farm.toLowerCase()] = aprs[farm.toLowerCase()] + rewardApr;
});
return chainFarms.map(({ address, rewardTokens, stakingToken, stakingTokenPool }) => {
const stakingTokenYieldPool = yieldPools.find(({ pool }) => pool === stakingTokenPool);
return {
pool: `${address}-${chain}`,
chain: utils.formatChain(chain),
project: 'abracadabra',
tvlUsd: Number(tvlUsdChainFarms[address.toLowerCase()]),
symbol: utils.formatSymbol(symbols[stakingToken.toLowerCase()].output),
apyBase: stakingTokenYieldPool.apyBase,
apyReward: aprs[address.toLowerCase()],
rewardTokens,
underlyingTokens: stakingTokenYieldPool.underlyingTokens,
}
});
}));
return pools.flat();
}

module.exports = getApy;

0 comments on commit a1e8e3c

Please sign in to comment.