Skip to content

Commit

Permalink
fix: add GHO rewards
Browse files Browse the repository at this point in the history
  • Loading branch information
doomsower committed Apr 3, 2024
1 parent 28ca12f commit 4d58852
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 50 deletions.
9 changes: 8 additions & 1 deletion src/yield-server/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import type { Address } from "./types";

export const ADDRESS_PROVIDER_V3 = "0x9ea7b04da02a5373317d745c1571c84aad03321d";
export const GEAR_TOKEN = "0xBa3335588D9403515223F109EdC4eB7269a9Ab5D";
export const GEAR_TOKEN =
"0xBa3335588D9403515223F109EdC4eB7269a9Ab5D".toLowerCase() as Address;
export const GHO_TOKEN: Address =
"0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f".toLowerCase() as Address;
export const POOL_USDT_V3_BROKEN =
"0x1dc0f3359a254f876b37906cfc1000a35ce2d717".toLowerCase();
export const POOL_GHO_V3 =
"0x4d56c9cBa373AD39dF69Eb18F076b7348000AE09".toLowerCase();
37 changes: 37 additions & 0 deletions src/yield-server/extraRewards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { GHO_TOKEN, POOL_GHO_V3 } from "./constants";
import type { Address, FarmInfo } from "./types";

interface ExtraRewardsInfo {
getFarmInfo: (timestamp: bigint) => FarmInfo<bigint>;
token: Address;
}

export const EXTRA_REWARDS: Record<Address, ExtraRewardsInfo[]> = {
[POOL_GHO_V3]: [
{
token: GHO_TOKEN,
getFarmInfo: (timestamp: bigint) => {
const GHO_DECIMALS = 10n ** 18n;
const REWARD_PERIOD = 14n * 24n * 60n * 60n;
const REWARDS_FIRST_START = 1711448651n;
const REWARDS_FIRST_END = REWARDS_FIRST_START + REWARD_PERIOD;
const REWARDS_SECOND_END = REWARDS_FIRST_END + REWARD_PERIOD;
const REWARD_FIRST_PART = 15000n * GHO_DECIMALS;
const REWARD_SECOND_PART = 10000n * GHO_DECIMALS;
const reward =
timestamp >= REWARDS_FIRST_END
? REWARD_SECOND_PART
: REWARD_FIRST_PART;
return {
balance: 0n,
duration: REWARD_PERIOD,
finished:
timestamp >= REWARDS_FIRST_END
? REWARDS_SECOND_END
: REWARDS_FIRST_END,
reward: reward,
};
},
},
],
};
102 changes: 54 additions & 48 deletions src/yield-server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
GEAR_TOKEN,
POOL_USDT_V3_BROKEN,
} from "./constants";
import { EXTRA_REWARDS } from "./extraRewards";
import type {
Address,
BigNumberish,
Expand All @@ -19,7 +20,7 @@ import type {
PoolDataV3,
PoolInfoV3,
SupplyInfo,
UnderlyingData,
TokenData,
} from "./types";

const SECONDS_PER_YEAR = 365n * 24n * 60n * 60n;
Expand All @@ -44,7 +45,7 @@ async function multiCall<T>(
* Price is converted into bigint with 10^18 precision
* See https://defillama.com/docs/api
*/
async function getPrices(
async function fetchLLamaPrices(
chain: Chain,
addresses: Address[],
): Promise<Record<Address, bigint>> {
Expand All @@ -59,17 +60,6 @@ async function getPrices(
return prices;
}

/**
* Returns floating-point price of token
* @param chain
* @param address
* @returns
*/
async function getPrice(chain: Chain, address: Address): Promise<bigint> {
const prices = await getPrices(chain, [address]);
return Object.values(prices)[0];
}

/**
* Returns mapping pool.address -> daoFee, where daoFee is a feeInterest from first credit manager that uses this pool
* @param chain
Expand Down Expand Up @@ -210,33 +200,39 @@ async function getPoolsV3(chain: Chain): Promise<PoolInfoV3[]> {
}

/**
* Returns mapping between underlying tokens of pools (in lower case) and their symbols, decimals and prices
* Returns mapping between tokens (in lower case) and their symbols, decimals and prices
*/
async function getUnderlyingTokensData(
async function getTokensData(
chain: Chain,
pools: PoolInfoV3[],
): Promise<Record<Address, UnderlyingData>> {
const underlyings = Array.from(
new Set(pools.map(p => p.underlying.toLowerCase() as Address)),
): Promise<Record<Address, TokenData>> {
let tokens = pools.map(p => p.underlying);
tokens.push(GEAR_TOKEN);
tokens.push(
...Object.values(EXTRA_REWARDS).flatMap(poolExtras =>
poolExtras.map(({ token }) => token),
),
);
tokens = Array.from(new Set(tokens.map(t => t.toLowerCase() as Address)));
const prices = await fetchLLamaPrices(chain, tokens);

const symbols = await multiCall<string>({
abi: abis.symbol,
calls: underlyings.map(target => ({ target })),
calls: tokens.map(target => ({ target })),
chain,
});
const decimals = await multiCall<BigNumberish>({
abi: abis.decimals,
calls: underlyings.map(target => ({ target })),
calls: tokens.map(target => ({ target })),
chain,
});
const prices = await getPrices(chain, underlyings);
const result: Record<Address, UnderlyingData> = {};
for (let i = 0; i < underlyings.length; i++) {
const underlying = underlyings[i];
result[underlying] = {
const result: Record<Address, TokenData> = {};
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
result[token] = {
symbol: symbols[i],
decimals: 10n ** BigInt(decimals[i]),
price: prices[underlying],
price: prices[token],
};
}
return result;
Expand All @@ -245,7 +241,7 @@ async function getUnderlyingTokensData(
function calcApyV3(
info: FarmInfo<bigint>,
supply: SupplyInfo,
gearPrice: bigint,
rewardPrice: bigint,
): number {
const now = BigInt(Math.floor(Date.now() / 1000));
if (info.finished <= now) {
Expand All @@ -254,15 +250,15 @@ function calcApyV3(
if (supply.amount <= 0n) {
return 0;
}
if (supply.price === 0n || gearPrice === 0n) {
if (supply.price === 0n || rewardPrice === 0n) {
return 0;
}
if (info.duration === 0n) {
return 0;
}

const supplyUsd = (supply.price * supply.amount) / supply.decimals;
const rewardUsd = (gearPrice * info.reward) / WAD;
const rewardUsd = (rewardPrice * info.reward) / WAD;

return (
Number(
Expand All @@ -284,14 +280,14 @@ function calculateTvl(

function getApyV3(
pools: PoolInfoV3[],
underlyings: Record<Address, UnderlyingData>,
gearPrice: bigint,
tokens: Record<Address, TokenData>,
daoFees: Record<Address, bigint>,
) {
return pools.map(pool => {
const underlyingPrice =
underlyings[pool.underlying.toLowerCase() as Address].price;
const daoFee = daoFees[pool.pool.toLowerCase() as Address] ?? 0;
const underlying = pool.underlying.toLowerCase() as Address;
const poolAddr = pool.pool.toLowerCase() as Address;
const underlyingPrice = tokens[underlying].price;
const daoFee = daoFees[poolAddr] ?? 0;
const totalSupplyUsd = calculateTvl(
pool.availableLiquidity,
pool.totalBorrowed,
Expand All @@ -306,26 +302,37 @@ function getApyV3(
);
const tvlUsd = totalSupplyUsd - totalBorrowUsd;
const dieselPrice = (underlyingPrice * pool.dieselRate) / RAY;
const apyReward = calcApyV3(

const supplyInfo: SupplyInfo = {
amount: pool.stakedDieselTokenSupply,
decimals: pool.decimals,
price: dieselPrice,
};
let apyRewardTotal = calcApyV3(
pool.farmInfo,
{
amount: pool.stakedDieselTokenSupply,
decimals: pool.decimals,
price: dieselPrice,
},
gearPrice,
supplyInfo,
tokens[GEAR_TOKEN].price,
);
const extraRewardTokens: Address[] = [];
for (const { token, getFarmInfo } of EXTRA_REWARDS[poolAddr] ?? []) {
extraRewardTokens.push(token);
const farmInfo = getFarmInfo(
BigInt(Math.floor(new Date().getTime() / 1000)),
);
const apyReward = calcApyV3(farmInfo, supplyInfo, tokens[token].price);
apyRewardTotal += apyReward;
}

return {
pool: pool.pool,
pool: poolAddr,
chain: "Ethereum",
project: "gearbox",
symbol: underlyings[pool.underlying.toLowerCase() as Address].symbol,
symbol: tokens[underlying].symbol,
tvlUsd: Number(tvlUsd),
apyBase: (Number(pool.supplyRate) / 1e27) * 100,
apyReward,
apyReward: apyRewardTotal,
underlyingTokens: [pool.underlying],
rewardTokens: [GEAR_TOKEN],
rewardTokens: [GEAR_TOKEN, ...extraRewardTokens],
url: `https://app.gearbox.fi/pools/${pool.pool}`,
// daoFee here is taken from last cm connected to this pool. in theory, it can be different for different CMs
// in practice, it's 25% for v3 cms and 50% for v2 cms
Expand All @@ -342,11 +349,10 @@ function getApyV3(
}

async function getApy() {
const gearPrice = await getPrice("ethereum", GEAR_TOKEN);
const daoFees = await getPoolsDaoFees("ethereum");
const v3Pools = await getPoolsV3("ethereum");
const underlyings = await getUnderlyingTokensData("ethereum", v3Pools);
const pools = getApyV3(v3Pools, underlyings, gearPrice, daoFees);
const tokens = await getTokensData("ethereum", v3Pools);
const pools = getApyV3(v3Pools, tokens, daoFees);
return pools.filter(i => keepFinite(i));
}

Expand Down
2 changes: 1 addition & 1 deletion src/yield-server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export interface PoolInfoV3 extends FarmingPoolData {
decimals: bigint;
}

export interface UnderlyingData {
export interface TokenData {
symbol: string;
decimals: bigint;
price: bigint;
Expand Down

0 comments on commit 4d58852

Please sign in to comment.