From 7ef3a181f71b1b20f9b02602e420c608738bb408 Mon Sep 17 00:00:00 2001 From: Thomas Sileghem Date: Tue, 22 Apr 2025 15:51:08 +0100 Subject: [PATCH] add Merkl rewards to existing pools --- src/adaptors/merkl/config.js | 13 ++++ src/adaptors/merkl/index.js | 11 +-- src/adaptors/merkl/merkl-additional-reward.js | 68 +++++++++++++++++++ src/adaptors/uniswap-v2/index.js | 6 +- src/adaptors/uniswap-v3/index.js | 8 ++- 5 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 src/adaptors/merkl/config.js create mode 100644 src/adaptors/merkl/merkl-additional-reward.js diff --git a/src/adaptors/merkl/config.js b/src/adaptors/merkl/config.js new file mode 100644 index 0000000000..286c41f94b --- /dev/null +++ b/src/adaptors/merkl/config.js @@ -0,0 +1,13 @@ +exports.networks = { + 1: 'ethereum', + 137: 'polygon', + 10: 'optimism', + 42161: 'arbitrum', + 1101: 'polygon_zkevm', + 8453: 'base', + 60808: 'bob', + 146: 'sonic', + 43114: 'avax', + 80094: 'berachain', + 56: 'bsc', +}; diff --git a/src/adaptors/merkl/index.js b/src/adaptors/merkl/index.js index 2b241125f1..7e04e5b56e 100644 --- a/src/adaptors/merkl/index.js +++ b/src/adaptors/merkl/index.js @@ -1,15 +1,7 @@ const sdk = require('@defillama/sdk'); const utils = require('../utils'); -const networks = { - 1: 'ethereum', - 137: 'polygon', - 10: 'optimism', - 42161: 'arbitrum', - 1101: 'polygon_zkevm', - 8453: 'base', - 60808: 'bob', -}; +const { networks } = require('./config'); // Protocols that should not be listed under Merkl // as they already have their own adapters. @@ -47,6 +39,7 @@ const main = async () => { ); } catch (err) { console.log('failed to fetch Merkl data on chain ' + chain); + break; } if (data.length === 0) { diff --git a/src/adaptors/merkl/merkl-additional-reward.js b/src/adaptors/merkl/merkl-additional-reward.js new file mode 100644 index 0000000000..f6f11ee8b2 --- /dev/null +++ b/src/adaptors/merkl/merkl-additional-reward.js @@ -0,0 +1,68 @@ +const { networks } = require('./config'); +const { getData } = require('../utils'); + +exports.addMerklRewardApy = async ( + pools, + protocolId, + poolAddressGetter, +) => { + try { + let merklPools = []; + let pageI = 0; + + while(true) { + let data; + try { + data = await getData(`https://api.merkl.xyz/v4/opportunities?mainProtocolId=${protocolId}&status=LIVE&items=100&page=${pageI}`); + } catch (err) { + console.log(`failed to fetch Merkl data for ${protocolId}: ${err}`); + break; + } + + if (data.length === 0) { + break; + } + + merklPools.push(...data); + pageI++; + } + + + const merklPoolsMap = Object.fromEntries(Object.keys(networks).map(id => [networks[id], {}])); + merklPools.forEach(pool => { + if (!networks[pool.chainId]) { + return; + } + + merklPoolsMap[networks[pool.chainId]][pool.identifier.toLowerCase()] = { + apyReward: pool.apr, + rewardTokens: [...new Set(pool.rewardsRecord?.breakdowns.map(x => x.token.address) || [])] + } + }); + + return pools.map(pool => { + const poolAddress = poolAddressGetter ? poolAddressGetter(pool) : pool.pool; + const merklRewards = merklPoolsMap[pool.chain.toLowerCase()][poolAddress.toLowerCase()]; + + if (!merklRewards) { + return pool; + } + + // if the data is already present, don't overwrite it + if (pool.apyReward || (pool.rewardTokens && pool.rewardTokens.length !== 0)) { + console.log('pool already has apyReward or rewardTokens', pool.pool); + return pool; + } + + return { + ...pool, + ...merklRewards, + } + }); + } catch (err) { + console.log(`Failed to add Merkl reward apy to ${protocolId}: ${err}`); + + // If we fail to fetch Merkl data, just return the original pools + return pools; + } +}; \ No newline at end of file diff --git a/src/adaptors/uniswap-v2/index.js b/src/adaptors/uniswap-v2/index.js index b6b3f7f798..9d3f48ed0f 100755 --- a/src/adaptors/uniswap-v2/index.js +++ b/src/adaptors/uniswap-v2/index.js @@ -2,6 +2,7 @@ const sdk = require('@defillama/sdk'); const { request, gql } = require('graphql-request'); const utils = require('../utils'); +const { addMerklRewardApy } = require('../merkl/merkl-additional-reward'); const chains = { ethereum: sdk.graph.modifyEndpoint( @@ -126,7 +127,10 @@ const main = async (timestamp = null) => { } } - return data.filter((p) => utils.keepFinite(p)); + return addMerklRewardApy( + data.filter((p) => utils.keepFinite(p)), + 'uniswap' + ); }; module.exports = { diff --git a/src/adaptors/uniswap-v3/index.js b/src/adaptors/uniswap-v3/index.js index 73c8d19928..37c3ba3c2a 100644 --- a/src/adaptors/uniswap-v3/index.js +++ b/src/adaptors/uniswap-v3/index.js @@ -7,6 +7,7 @@ const { EstimatedFees } = require('./estimateFee.ts'); const { checkStablecoin } = require('../../handlers/triggerEnrichment'); const { boundaries } = require('../../utils/exclude'); const getOnchainPools = require('./onchain'); +const { addMerklRewardApy } = require('../merkl/merkl-additional-reward'); const chains = { ethereum: sdk.graph.modifyEndpoint( @@ -352,7 +353,8 @@ const main = async (timestamp = null) => { ); } data.push(...(await getOnchainPools())) - return data + return addMerklRewardApy( + data .flat() .filter( (p) => @@ -361,7 +363,9 @@ const main = async (timestamp = null) => { '0x0c6d9d0f82ed2e0b86c4d3e9a9febf95415d1b76', '0xc809d13e9ea08f296d3b32d4c69d46ff90f73fd8', ].includes(p.pool) - ); + ), + 'uniswap' + ); }; module.exports = {