@@ -80,6 +80,16 @@ type ReserveIncentiveAdditionalData = {
8080export type ExtendedReserveIncentiveResponse = ReserveIncentiveResponse &
8181 ReserveIncentiveAdditionalData & {
8282 breakdown : MerklIncentivesBreakdown ;
83+ allOpportunities ?: {
84+ name : string ;
85+ apy : number ;
86+ rewardToken : {
87+ address : string ;
88+ symbol : string ;
89+ icon : string ;
90+ price : number ;
91+ } ;
92+ } [ ] ;
8393 } ;
8494
8595export type MerklIncentivesBreakdown = {
@@ -98,7 +108,80 @@ type WhitelistApiResponse = {
98108 whitelistedRewardTokens : string [ ] ;
99109 additionalIncentiveInfo : Record < string , ReserveIncentiveAdditionalData > ;
100110} ;
111+ // TODO: Remove mock before production
112+ const ethfiMockOpportunity : MerklOpportunity = {
113+ chainId : 9745 ,
114+ type : 'AAVE_SUPPLY' ,
115+ identifier : '0xaf1a7a488c8348b41d5860c04162af7d3d38a996' as Address ,
116+ name : 'Lend weETH on Aave (ETHFI Bonus)' ,
117+ status : OpportunityStatus . LIVE ,
118+ action : OpportunityAction . LEND ,
119+ tvl : 1000000 ,
120+ apr : 5.0 ,
121+ dailyRewards : 500 ,
122+ tags : [ ] ,
123+ id : 'mock-ethfi-campaign-2' ,
124+ explorerAddress : '0xaf1a7a488c8348b41d5860c04162af7d3d38a996' as Address ,
125+ tokens : [
126+ {
127+ id : '14178706307683891785' ,
128+ name : 'Aave Plasma weETH' ,
129+ chainId : 9745 ,
130+ address : '0xaf1a7a488c8348b41d5860c04162af7d3d38a996' as Address ,
131+ decimals : 18 ,
132+ icon : '' ,
133+ verified : false ,
134+ isTest : false ,
135+ price : 4156.526571271317 ,
136+ symbol : 'aPlaweETH' ,
137+ } ,
138+ {
139+ id : '3885658325202072166' ,
140+ name : 'Wrapped eETH' ,
141+ chainId : 9745 ,
142+ address : '0xa3d68b74bf0528fdd07263c60d6488749044914b' as Address ,
143+ decimals : 18 ,
144+ icon : 'https://storage.googleapis.com/merkl-static-assets/tokens/weETH.webp' ,
145+ verified : true ,
146+ isTest : false ,
147+ price : 4156.526571271317 ,
148+ symbol : 'weETH' ,
149+ } ,
150+ ] ,
151+ rewardsRecord : {
152+ id : 'mock-ethfi-rewards-record' ,
153+ total : 500 ,
154+ timestamp : '1761125029' ,
155+ breakdowns : [
156+ {
157+ token : {
158+ id : 'ethfi-token-id' ,
159+ name : 'Ether.fi Governance Token' ,
160+ chainId : 9745 ,
161+ address : '0xfe0c30065b384f05761f15d0cc899d4f9f9cc0eb' ,
162+ decimals : 18 ,
163+ symbol : 'ETHFI' ,
164+ displaySymbol : 'ETHFI' ,
165+ icon : 'https://assets.coingecko.com/coins/images/35958/standard/etherfi.png' ,
166+ verified : true ,
167+ isTest : false ,
168+ type : 'TOKEN' ,
169+ isNative : false ,
170+ price : 3.25 ,
171+ } ,
172+ amount : '153846153846153846153' ,
173+ value : 500 ,
174+ distributionType : 'DUTCH_AUCTION' ,
175+ id : 'mock-ethfi-breakdown-id' ,
176+ campaignId : 'mock-ethfi-campaign-id' ,
177+ dailyRewardsRecordId : 'mock-ethfi-daily-rewards-id' ,
178+ } ,
179+ ] ,
180+ } ,
181+ } ;
101182
183+ // TODO: Remove mock before production
184+ const mockaddressWeETH = '0xfe0c30065b384f05761f15d0cc899d4f9f9cc0eb' ;
102185const MERKL_ENDPOINT = 'https://api.merkl.xyz/v4/opportunities?mainProtocolId=aave' ; // Merkl API
103186const WHITELIST_ENDPOINT = 'https://apps.aavechan.com/api/aave/merkl/whitelist-token-list' ; // Endpoint to fetch whitelisted tokens
104187const checkOpportunityAction = (
@@ -121,7 +204,13 @@ const useWhitelistedTokens = () => {
121204 if ( ! response . ok ) {
122205 throw new Error ( 'Failed to fetch whitelisted tokens' ) ;
123206 }
124- return response . json ( ) ;
207+ const data = await response . json ( ) ;
208+
209+ // TODO: Remove mock before production
210+ if ( ! data . whitelistedRewardTokens . includes ( mockaddressWeETH . toLowerCase ( ) ) ) {
211+ data . whitelistedRewardTokens . push ( mockaddressWeETH . toLowerCase ( ) ) ;
212+ }
213+ return data ;
125214 } ,
126215 queryKey : [ 'whitelistedTokens' ] ,
127216 staleTime : 1000 * 60 * 5 , // 5 minutes
@@ -148,6 +237,8 @@ export const useMerklIncentives = ({
148237 queryFn : async ( ) => {
149238 const response = await fetch ( `${ MERKL_ENDPOINT } ` ) ;
150239 const merklOpportunities : MerklOpportunity [ ] = await response . json ( ) ;
240+ // TODO: Remove mock before production
241+ merklOpportunities . push ( ethfiMockOpportunity ) ;
151242 return merklOpportunities ;
152243 } ,
153244 queryKey : [ 'merklIncentives' , market ] ,
@@ -159,29 +250,22 @@ export const useMerklIncentives = ({
159250 opportunitiy . explorerAddress &&
160251 opportunitiy . explorerAddress . toLowerCase ( ) === rewardedAsset . toLowerCase ( ) &&
161252 protocolAction &&
162- checkOpportunityAction ( opportunitiy . action , protocolAction ) &&
163- opportunitiy . chainId === currentChainId
253+ checkOpportunityAction ( opportunitiy . action , protocolAction )
254+ // disabled to allow cross-chain incentives e.g. ethfi on weETH
255+ // opportunitiy.chainId === currentChainId
164256 ) ;
165257
166258 if ( opportunities . length === 0 ) {
167259 return null ;
168260 }
169261
170- const opportunity = opportunities [ 0 ] ;
171-
172- if ( opportunity . status !== OpportunityStatus . LIVE ) {
173- return null ;
174- }
175-
176- if ( opportunity . apr <= 0 ) {
262+ const validOpportunities = opportunities . filter (
263+ ( opp ) => opp . status === OpportunityStatus . LIVE && opp . apr > 0
264+ ) ;
265+ if ( validOpportunities . length === 0 ) {
177266 return null ;
178267 }
179268
180- const merklIncentivesAPR = opportunity . apr / 100 ;
181- const merklIncentivesAPY = convertAprToApy ( merklIncentivesAPR ) ;
182-
183- const rewardToken = opportunity . rewardsRecord . breakdowns [ 0 ] . token ;
184-
185269 if ( ! whitelistData ?. whitelistedRewardTokens ) {
186270 return null ;
187271 }
@@ -190,10 +274,24 @@ export const useMerklIncentives = ({
190274 whitelistData . whitelistedRewardTokens . map ( ( token ) => token . toLowerCase ( ) )
191275 ) ;
192276
193- if ( ! whitelistedTokensSet . has ( rewardToken . address . toLowerCase ( ) ) ) {
277+ const whitelistedOpportunities = validOpportunities . filter ( ( opp ) => {
278+ const rewardToken = opp . rewardsRecord . breakdowns [ 0 ] ?. token ;
279+ return rewardToken && whitelistedTokensSet . has ( rewardToken . address . toLowerCase ( ) ) ;
280+ } ) ;
281+
282+ if ( whitelistedOpportunities . length === 0 ) {
194283 return null ;
195284 }
196285
286+ const totalMerklAPR = whitelistedOpportunities . reduce ( ( sum , opp ) => {
287+ return sum + opp . apr / 100 ;
288+ } , 0 ) ;
289+
290+ const merklIncentivesAPY = convertAprToApy ( totalMerklAPR ) ;
291+
292+ const primaryOpportunity = whitelistedOpportunities [ 0 ] ;
293+ const rewardToken = primaryOpportunity . rewardsRecord . breakdowns [ 0 ] . token ;
294+
197295 const protocolIncentivesAPR = protocolIncentives . reduce ( ( sum , inc ) => {
198296 return sum + ( inc . incentiveAPR === 'Infinity' ? 0 : + inc . incentiveAPR ) ;
199297 } , 0 ) ;
@@ -211,6 +309,11 @@ export const useMerklIncentives = ({
211309 rewardTokenAddress : rewardToken . address ,
212310 rewardTokenSymbol : rewardToken . symbol ,
213311 ...incentiveAdditionalData ,
312+ allOpportunities : whitelistedOpportunities . map ( ( opp ) => ( {
313+ name : opp . name ,
314+ apy : convertAprToApy ( opp . apr / 100 ) ,
315+ rewardToken : opp . rewardsRecord . breakdowns [ 0 ] . token ,
316+ } ) ) ,
214317 breakdown : {
215318 protocolAPY,
216319 protocolIncentivesAPR,
0 commit comments