diff --git a/src/gearboxRewards/api.ts b/src/gearboxRewards/api.ts index b8afbc9e..5e785499 100644 --- a/src/gearboxRewards/api.ts +++ b/src/gearboxRewards/api.ts @@ -18,17 +18,13 @@ import { import { GearboxBackendApi } from "../core/endpoint"; import { PoolData } from "../core/pool"; import { TokenData } from "../tokens/tokenData"; -import { - iAirdropDistributorAbi, - iFarmingPoolAbi, - iMulticall3Abi, -} from "../types"; -import { toBN } from "../utils/formatter"; +import { iAirdropDistributorAbi, iFarmingPoolAbi } from "../types"; import { BigIntMath } from "../utils/math"; +import { ExtraRewardApy } from "./apy"; import { MerkleXYZApi, - MerkleXYZRewardsCampaignsResponse, MerkleXYZUserRewardsResponse, + MerkleXYZV4CampaignsResponse, } from "./merklAPI"; export interface GearboxExtraMerkleLmReward { @@ -97,29 +93,17 @@ export interface FarmInfo { symbol: SupportedToken; } -type PoolsWithExtraRewardsList = Record>; - -const DEFAULT_POOLS_WITH_EXTRA_REWARDS: PoolsWithExtraRewardsList = { - Mainnet: [ - "0x7354EC6E852108411e681D13E11185c3a2567981", // dtBTCV3 - ], - Arbitrum: [], - Optimism: [], - Base: [], -}; - type ReportHandler = (e: unknown, description?: string) => void; export interface GetLmRewardsInfoProps { pools: Record; tokensList: Record; provider: PublicClient; +} - multicallAddress: Address; - - poolsWithExtraRewards?: PoolsWithExtraRewardsList; - network: NetworkType; - reportError?: ReportHandler; +export interface GetExtraRewardsProps { + chainId: number; + tokensList: Record; } export interface GetLmRewardsProps { @@ -150,15 +134,53 @@ export interface ClaimLmRewardsV3Props { } export class GearboxRewardsApi { + static async getExtraRewards({ chainId, tokensList }: GetExtraRewardsProps) { + const res = await axios.get( + MerkleXYZApi.getGearboxCampaignsUrl(), + ); + const currentActiveCampaigns = res.data.filter( + c => c.status === "LIVE" && c.chainId === chainId, + ); + + const r = currentActiveCampaigns.reduce< + Record> + >((acc, campaign) => { + const rewardSource = ( + campaign.tokens[0]?.address || campaign.identifier + ).toLowerCase() as Address; + + const allRewards = campaign.rewardsRecord.breakdowns + .map((r, i) => { + const tokenLc = r.token.address.toLowerCase() as Address; + const { symbol = r.token.symbol } = tokensList[tokenLc] || {}; + + const apy = campaign.aprRecord.breakdowns[i]?.value || 0; + + const apyObject: ExtraRewardApy = { + token: rewardSource, + balance: null, + + apy: apy, + rewardToken: tokenLc, + rewardTokenSymbol: symbol, + }; + + return apyObject; + }) + .filter(r => r.apy > 0); + + acc[rewardSource] = [...(acc[rewardSource] || []), ...allRewards]; + + return acc; + }, {}); + + return r; + } + static async getLmRewardsInfo({ pools, provider, - multicallAddress, tokensList, - - poolsWithExtraRewards = DEFAULT_POOLS_WITH_EXTRA_REWARDS, - network, - reportError, }: GetLmRewardsInfoProps) { const poolByStakedDiesel = Object.values(pools).reduce< Record @@ -187,19 +209,6 @@ export class GearboxRewardsApi { const poolStakedTokens = TypedObjectUtils.keys(poolByStakedDiesel); const allPoolTokens = TypedObjectUtils.keys(poolByItsToken); - const chainId = CHAINS[network]; - const poolTokensWithExtraReward = ( - poolsWithExtraRewards[network] || [] - ).filter(p => { - const token = tokensList[p.toLowerCase() as Address]; - - if (!token) { - console.error(`Pool token not found ${p}`); - return false; - } - return true; - }); - const farmInfoCalls = poolStakedTokens.map(address => ({ address, abi: iFarmingPoolAbi, @@ -221,40 +230,14 @@ export class GearboxRewardsApi { args: [], })); - const [mc, ...extra] = await Promise.allSettled([ - provider.multicall({ - allowFailure: false, - multicallAddress: MULTICALL_ADDRESS, - contracts: [ - { - address: multicallAddress, - abi: iMulticall3Abi as any, - functionName: "getCurrentBlockTimestamp", - args: [], - }, - - ...farmInfoCalls, - ...farmSupplyCalls, - ...rewardTokenCalls, - ], - }), - - ...poolTokensWithExtraReward.map(t => { - return axios.get( - MerkleXYZApi.getRewardsCampaignsUrl({ - params: { - chainId, - mainParameter: getAddress(t), - }, - }), - ); - }), - ]); + const mc = await provider.multicall({ + allowFailure: false, + multicallAddress: MULTICALL_ADDRESS, + contracts: [...farmInfoCalls, ...farmSupplyCalls, ...rewardTokenCalls], + }); - const mcResponse = - this.extractFulfilled(mc, reportError, "rewardsInfoMulticall") || []; - const [ts = 0n, ...restMCResponse] = mcResponse; - const blockTimestamp = (ts as bigint) || 0n; + const mcResponse = mc; + const [...restMCResponse] = mcResponse; const farmInfoCallsEnd = farmInfoCalls.length; const farmInfo = restMCResponse.slice( @@ -298,57 +281,8 @@ export class GearboxRewardsApi { {}, ); - const extraRewards = extra.reduce>>( - (acc, r, index) => { - const p = poolTokensWithExtraReward[index].toLowerCase() as Address; - - const safeResp = this.extractFulfilled( - r, - reportError, - `merkleCampaign: ${p}`, - ); - - const l = safeResp?.data.reduce>((infos, d) => { - const started = toBigInt(d.startTimestamp || 0); - const finished = toBigInt(d.endTimestamp || 0); - - if (blockTimestamp >= started && blockTimestamp <= finished) { - const rewardTokenLc = ( - d.rewardToken || "" - ).toLowerCase() as Address; - const rewardTokenData = tokensList[rewardTokenLc]; - const reward = toBN( - d.amountDecimal, - rewardTokenData?.decimals || 18, - ); - - if (rewardTokenData && reward > 0) { - infos.push({ - pool: poolByItsToken[p], - duration: toBigInt(d.endTimestamp - d.startTimestamp), - finished, - reward, - balance: 0n, - symbol: rewardTokenData.symbol, - }); - } - } - - return infos; - }, []); - - if (l) { - acc[p] = l; - } - - return acc; - }, - {}, - ); - const stakedTokenRewards = allPoolTokens.reduce<{ base: Record; - extra: Record>; all: Record>; }>( (acc, pool) => { @@ -368,15 +302,12 @@ export class GearboxRewardsApi { } : undefined; - const extra = extraRewards[pool] || []; - if (baseReward) acc.base[pool] = baseReward; - acc.extra[pool] = extra; - acc.all[pool] = [...(baseReward ? [baseReward] : []), ...extra]; + acc.all[pool] = [...(baseReward ? [baseReward] : [])]; return acc; }, - { base: {}, extra: {}, all: {} }, + { base: {}, all: {} }, ); const rewardPoolsSupply = allPoolTokens.reduce>( @@ -391,7 +322,6 @@ export class GearboxRewardsApi { return { rewardPoolsInfo: stakedTokenRewards.all, baseRewardPoolsInfo: stakedTokenRewards.base, - extraRewardPoolsInfo: stakedTokenRewards.extra, rewardPoolsSupply, }; } diff --git a/src/gearboxRewards/apy.ts b/src/gearboxRewards/apy.ts index 7fae53e9..0df96586 100644 --- a/src/gearboxRewards/apy.ts +++ b/src/gearboxRewards/apy.ts @@ -37,9 +37,10 @@ const ONE = PERCENTAGE_FACTOR_1KK * 10n; export interface ExtraRewardApy { token: Address; balance: bigint | null; - apy: number; - rewardInfo: FarmInfo; + apy: number; + rewardToken: Address; + rewardTokenSymbol: string; } interface GetPoolExtraAPY_V3Props { @@ -150,8 +151,10 @@ export class GearboxRewardsApy { return { token: stakedDieselToken, balance: null, + apy: r, - rewardInfo: rewardPoolsInfo, + rewardToken: rewardAddress, + rewardTokenSymbol: rewardPoolsInfo.symbol, }; } @@ -252,6 +255,13 @@ export class GearboxRewardsApy { }, }) / Number(PERCENTAGE_FACTOR); - return { token, balance, rewardInfo, apy: r }; + return { + token, + balance, + + apy: r, + rewardToken: rewardAddress, + rewardTokenSymbol: rewardInfo.symbol, + }; } } diff --git a/src/gearboxRewards/merklAPI.ts b/src/gearboxRewards/merklAPI.ts index ee22d872..728195ce 100644 --- a/src/gearboxRewards/merklAPI.ts +++ b/src/gearboxRewards/merklAPI.ts @@ -1,3 +1,5 @@ +import { Address } from "viem"; + import { URLApi } from "../core/endpoint"; import { BigNumberish } from "../utils/formatter"; @@ -27,13 +29,79 @@ export interface MerkleXYZUserRewards { export type MerkleXYZUserRewardsResponse = Record; -interface CampaignsOptions { - params: { +export interface MerklXYZV4Campaign { + chainId: number; + type: string; + identifier: Address; + name: string; + status: "LIVE" | "PAST"; + action: "LEND" | "BORROW"; + tvl: number; + apr: number; + dailyRewards: number; + tags: Array; + id: string; + tokens: Array<{ + id: string; + name: string; chainId: number; - mainParameter?: string; - rewardToken?: string; + address: Address; + decimals: number; + icon: string; + verified: boolean; + isTest: boolean; + price: number | null; + symbol: string; + }>; + chain: { + id: number; + name: string; + icon: string; + }; + aprRecord: { + cumulated: number; + timestamp: string; + breakdowns: Array<{ + id: number; + identifier: Address; + type: "CAMPAIGN"; + value: number; + aprRecordId: string; + }>; + }; + tvlRecord: { + id: string; + total: number; + timestamp: string; + breakdowns: []; + }; + rewardsRecord: { + id: string; + total: number; + timestamp: string; + breakdowns: Array<{ + token: { + id: string; + name: string; + chainId: number; + address: Address; + decimals: 18; + symbol: string; + displaySymbol: string; + icon: string; + verified: boolean; + isTest: boolean; + price: number; + }; + amount: string; + id: number; + value: number; + campaignId: string; + dailyRewardsRecordid: string; + }>; }; } +export type MerkleXYZV4CampaignsResponse = Array; interface Campaign { amountDecimal: string; @@ -54,9 +122,7 @@ export class MerkleXYZApi { static getUserRewardsUrl = (options: UserOptions) => URLApi.getRelativeUrl([this.domain, "userRewards"].join("/"), options); - static getRewardsCampaignsUrl = (options: CampaignsOptions) => - URLApi.getRelativeUrl( - [this.domain, "campaignsForMainParameter"].join("/"), - options, - ); + + static getGearboxCampaignsUrl = () => + "https://api.merkl.xyz/v4/opportunities?name=gearbox"; }