@merkl/api 0.15.17 → 0.15.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/dist/src/constants.d.ts +4 -1
  2. package/dist/src/constants.js +9 -1
  3. package/dist/src/entities/opportunity.js +58 -0
  4. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/helpers/tokenType.d.ts +3 -1
  5. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/helpers/tokenType.js +4 -0
  6. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/BeefyProcessor.js +0 -1
  7. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/SpectraProcessor.d.ts +31 -0
  8. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/SpectraProcessor.js +41 -0
  9. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/SpectraYTProcessor.d.ts +31 -0
  10. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/SpectraYTProcessor.js +39 -0
  11. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/processorMapping.js +4 -0
  12. package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/subtypesRound1.js +5 -1
  13. package/dist/src/libs/campaigns/campaignTypes/UniswapV4DynamicData.d.ts +3 -0
  14. package/dist/src/libs/campaigns/campaignTypes/UniswapV4DynamicData.js +228 -0
  15. package/dist/src/libs/campaigns/campaignsDynamicData.js +4 -0
  16. package/dist/src/modules/v4/campaign/campaign.model.d.ts +1 -0
  17. package/dist/src/modules/v4/campaign/campaign.model.js +1 -0
  18. package/dist/src/types/external/spectraAPI.d.ts +88 -0
  19. package/dist/src/types/external/spectraAPI.js +1 -0
  20. package/dist/src/utils/decodeCalls.js +9 -2
  21. package/dist/src/utils/encodeCalls.js +17 -2
  22. package/dist/src/utils/generateCardName.js +4 -0
  23. package/dist/tsconfig.package.tsbuildinfo +1 -1
  24. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ import { ChainId, type MerklChainId } from "@sdk";
1
2
  import Big from "big.js";
2
3
  export declare const zeroBN: Big.Big;
3
4
  export declare const baseTestAnalyticsGraphUrl = "https://api.thegraph.com/subgraphs/name/angleprotocol/test-analytics-";
@@ -29,7 +30,6 @@ export declare const RFX_DATASTORE = "0x895124783008C6c68eFcccac24c482Fdf30439B2
29
30
  export declare const DOPEX_OPTION_MARKET: {
30
31
  [market: string]: string;
31
32
  };
32
- import { ChainId } from "@sdk";
33
33
  export declare const constantChain: {
34
34
  [x: number]: {
35
35
  apw: string;
@@ -349,3 +349,6 @@ export declare const constantChain: {
349
349
  };
350
350
  };
351
351
  export declare const MAVERICK_ZKSYNC_BP_LENS_ADDRESS = "0xd32CE31CaC98CAC0631764B8286358c0606D87F9";
352
+ export declare const NETWORK_SLUGS: {
353
+ [chainId in MerklChainId]?: string;
354
+ };
@@ -1,3 +1,4 @@
1
+ import { ChainId } from "@sdk";
1
2
  import Big from "big.js";
2
3
  export const zeroBN = new Big(0);
3
4
  export const baseTestAnalyticsGraphUrl = "https://api.thegraph.com/subgraphs/name/angleprotocol/test-analytics-";
@@ -42,7 +43,6 @@ export const DOPEX_OPTION_MARKET = {
42
43
  ["0xd9e2a1a61b6e61b275cec326465d417e52c1b95c".toLowerCase()]: "0x501B03BdB431154b8Df17BF1c00756E3a8F21744", // wETH / USDC
43
44
  ["0x9fFCA51D23Ac7F7df82da414865Ef1055E5aFCc3".toLowerCase()]: "0x4eed3A2b797Bf5630517EcCe2e31C1438A76bb92", // ARB / USDC
44
45
  };
45
- import { ChainId } from "@sdk";
46
46
  const COMMON_PREFIX_STAKE = "https://lockers.stakedao.org/api/strategies/cache/curve";
47
47
  const COMMON_PREFIX_CONVEX = "https://www.convexfinance.com/api/curve-apys";
48
48
  export const constantChain = {
@@ -118,3 +118,11 @@ export const constantChain = {
118
118
  },
119
119
  };
120
120
  export const MAVERICK_ZKSYNC_BP_LENS_ADDRESS = "0xd32CE31CaC98CAC0631764B8286358c0606D87F9";
121
+ export const NETWORK_SLUGS = {
122
+ [ChainId.MAINNET]: "mainnet",
123
+ [ChainId.ARBITRUM]: "arbitrum",
124
+ [ChainId.OPTIMISM]: "optimism",
125
+ [ChainId.BASE]: "base",
126
+ [ChainId.POLYGON]: "polygon",
127
+ [ChainId.SONIC]: "sonic",
128
+ };
@@ -1122,6 +1122,64 @@ export const extractOpportunities = {
1122
1122
  };
1123
1123
  return opportunity;
1124
1124
  },
1125
+ [Campaign.UNISWAP_V4]: ({ chainId, mainParameter, tvl, campaignParameters: params, poolBalanceToken0, poolBalanceToken1, computeChainId }, campaigns, prices) => {
1126
+ const { active, all } = campaigns;
1127
+ const pair = `${params.symbolCurrency0}-${params.symbolCurrency1}`;
1128
+ // const tvlDetails = active.reduce(
1129
+ // (res, campaign) => {
1130
+ // for (const forwarder of campaign?.forwarders ?? []) {
1131
+ // const key = `${almName(forwarder.type)} ${forwarder.almAddress}`;
1132
+ // if (!res[key])
1133
+ // res[key] = {
1134
+ // value: forwarder.almTVL,
1135
+ // address: forwarder.almAddress,
1136
+ // type: EAprBreakdownType.FORWARDER,
1137
+ // label: forwarder.label.split(" ")[0],
1138
+ // };
1139
+ // }
1140
+ // return res;
1141
+ // },
1142
+ // {} as { [key: string]: tvlBreakdown }
1143
+ // );
1144
+ // // @Hugo wip: new way to structure aprBreakdowns
1145
+ // const aprBreakdownByType = sumAprBreakdownsByType(campaigns.active);
1146
+ // const campaignApr = getCampaignsApr<Campaign.CLAMM>(active);
1147
+ // const aprBreakdown2 = [...aprBreakdownByType, ...campaignApr];
1148
+ const aprBreakdown = active.reduce((res, campaign) => {
1149
+ if (!campaign.aprs)
1150
+ return res;
1151
+ return res;
1152
+ }, {});
1153
+ const holdings = [
1154
+ { symbol: params.symbolCurrency0, amount: poolBalanceToken0 ?? 0 },
1155
+ { symbol: params.symbolCurrency1, amount: poolBalanceToken1 ?? 0 },
1156
+ ];
1157
+ const platform = "UniswapV4";
1158
+ const opportunity = {
1159
+ id: `${Campaign.UNISWAP_V4}_${mainParameter}`,
1160
+ platform,
1161
+ name: [platform, pair, params.lpFee && `${params.lpFee} %`].join(" "),
1162
+ chainId: computeChainId ?? chainId,
1163
+ distributionChainId: chainId,
1164
+ action: "pool",
1165
+ status: getStatus(all),
1166
+ tvlBreakdown: {
1167
+ tokens: holdings,
1168
+ details: {},
1169
+ },
1170
+ aprBreakdown,
1171
+ aprBreakdown2: {},
1172
+ tvl: !!tvl ? tvl : holdings.reduce((prev, curr) => (prices[curr?.symbol] ?? 0) * curr?.amount + prev, 0),
1173
+ apr: getApr(active),
1174
+ tags: getTags(campaigns.all),
1175
+ dailyrewards: getDailyRewards(active, prices),
1176
+ tokenIcons: [params.symbolCurrency0, params.symbolCurrency1],
1177
+ rewardTokenIcons: getRewardTokenIcons(campaigns.active),
1178
+ dailyRewardTokens: getRewardTokens(campaigns.active),
1179
+ campaigns: { ...campaigns, type: Campaign.UNISWAP_V4, ids: campaigns.all.map(c => c.campaignId) },
1180
+ };
1181
+ return opportunity;
1182
+ },
1125
1183
  };
1126
1184
  /**
1127
1185
  * @returns the opportunities map with their corresponding campaign's data added
@@ -83,7 +83,9 @@ export declare enum tokenType {
83
83
  takotako_lending = "takotako_lending",
84
84
  equalizer_gauge = "equalizer_gauge",
85
85
  vicuna_lending = "vicuna_lending",
86
- vicuna_borrowing = "vicuna_borrowing"
86
+ vicuna_borrowing = "vicuna_borrowing",
87
+ spectra_lpt = "spectra_lpt",
88
+ spectra_yt = "spectra_yt"
87
89
  }
88
90
  export declare const tokenTypeToProtocol: {
89
91
  [key in tokenType]: {
@@ -85,6 +85,8 @@ export var tokenType;
85
85
  tokenType["equalizer_gauge"] = "equalizer_gauge";
86
86
  tokenType["vicuna_lending"] = "vicuna_lending";
87
87
  tokenType["vicuna_borrowing"] = "vicuna_borrowing";
88
+ tokenType["spectra_lpt"] = "spectra_lpt";
89
+ tokenType["spectra_yt"] = "spectra_yt";
88
90
  })(tokenType || (tokenType = {}));
89
91
  export const tokenTypeToProtocol = {
90
92
  [tokenType.aave_borrowing]: { protocol: "Aave", action: OpportunityAction.BORROW },
@@ -170,4 +172,6 @@ export const tokenTypeToProtocol = {
170
172
  [tokenType.equalizer_gauge]: { protocol: "Equalizer", action: OpportunityAction.HOLD },
171
173
  [tokenType.vicuna_lending]: { protocol: "Vicuna", action: OpportunityAction.LEND },
172
174
  [tokenType.vicuna_borrowing]: { protocol: "Vicuna", action: OpportunityAction.BORROW },
175
+ [tokenType.spectra_lpt]: { protocol: "Spectra", action: OpportunityAction.POOL },
176
+ [tokenType.spectra_yt]: { protocol: "Spectra", action: OpportunityAction.POOL },
173
177
  };
@@ -24,7 +24,6 @@ export class BeefyProcessor extends GenericProcessor {
24
24
  { key: "totalSupplyUnderlying", call: "totalSupply", target: "underlying" },
25
25
  ],
26
26
  };
27
- // override computeRound1(): void {}
28
27
  async processingRound5(_index, type, typeInfo, _calls, campaign, pricer) {
29
28
  const { whitelistedSupplyTargetToken, totalSupply, blacklistedSupply } = this.handleWhiteListBlacklistRound5(typeInfo, campaign);
30
29
  const priceToken0 = (await pricer.get({ symbol: typeInfo.symbolToken0 })) ?? 0;
@@ -0,0 +1,31 @@
1
+ import type { Pricer } from "../../../../../utils/pricer";
2
+ import type { Campaign, CampaignParameters } from "@sdk";
3
+ import type { tokenType } from "../helpers/tokenType";
4
+ import { GenericProcessor, type dataType, type mandatoryCallKeys } from "./GenericProcessor";
5
+ type callType = {
6
+ key: keyof dataRawSpectra;
7
+ call: string;
8
+ target: keyof callKeysSpectra;
9
+ metaData?: any;
10
+ };
11
+ type callKeysSpectra = mandatoryCallKeys & {
12
+ tokenPrice: string;
13
+ name: string;
14
+ minter: string;
15
+ };
16
+ type dataRawSpectra = callKeysSpectra & {
17
+ curvePool: string;
18
+ };
19
+ type dataTypeSpectra = dataType & {
20
+ tokenPrice: string;
21
+ };
22
+ export declare class SpectraProcessor extends GenericProcessor<callKeysSpectra, dataRawSpectra, dataTypeSpectra> {
23
+ rounds: {
24
+ round1: callType[];
25
+ round2: callType[];
26
+ round3: callType[];
27
+ round4: callType[];
28
+ };
29
+ processingRound5(_index: number, type: tokenType, typeInfo: dataRawSpectra, _calls: string[], campaign: CampaignParameters<Campaign.ERC20> | CampaignParameters<Campaign.EULER>, _pricer: Pricer): Promise<dataTypeSpectra>;
30
+ }
31
+ export {};
@@ -0,0 +1,41 @@
1
+ import { NETWORK_SLUGS } from "../../../../../constants";
2
+ import { generateCardName } from "../../../../../utils/generateCardName";
3
+ import { GenericProcessor } from "./GenericProcessor";
4
+ export class SpectraProcessor extends GenericProcessor {
5
+ rounds = {
6
+ round1: [{ key: "curvePool", call: "minter", target: "tokenAddress" }],
7
+ round2: [],
8
+ round3: [],
9
+ round4: [{ key: "totalSupply", call: "totalSupply", target: "tokenAddress" }],
10
+ };
11
+ async processingRound5(_index, type, typeInfo, _calls, campaign, _pricer) {
12
+ const { whitelistedSupplyTargetToken, totalSupply, blacklistedSupply } = this.handleWhiteListBlacklistRound5(typeInfo, campaign);
13
+ if (!NETWORK_SLUGS[campaign.computeChainId]) {
14
+ throw new Error("Network slug not found");
15
+ }
16
+ const curvePool = typeInfo.curvePool.toLowerCase();
17
+ const spectraData = (await (await fetch(`https://app.spectra.finance/api/v1/${NETWORK_SLUGS[campaign.computeChainId]}/pools`, {
18
+ headers: {
19
+ "x-client-id": "Merkl Studio",
20
+ source: "Merkl Studio",
21
+ },
22
+ })).json());
23
+ const poolData = spectraData
24
+ .find(d => d.pools.map(pool => pool.address.toLowerCase()).includes(curvePool))
25
+ ?.pools.find(pool => pool.address.toLowerCase() === curvePool);
26
+ if (!poolData) {
27
+ throw new Error("Pool not found in Spectra API");
28
+ }
29
+ const tvl = poolData.liquidity.usd; // in USD
30
+ const priceTargetToken = tvl / totalSupply;
31
+ return {
32
+ ...typeInfo,
33
+ totalSupply,
34
+ tvl,
35
+ whitelistedSupplyTargetToken,
36
+ blacklistedSupply,
37
+ priceTargetToken,
38
+ cardName: generateCardName(type, typeInfo, campaign),
39
+ };
40
+ }
41
+ }
@@ -0,0 +1,31 @@
1
+ import type { Pricer } from "../../../../../utils/pricer";
2
+ import type { Campaign, CampaignParameters } from "@sdk";
3
+ import type { tokenType } from "../helpers/tokenType";
4
+ import { GenericProcessor, type dataType, type mandatoryCallKeys } from "./GenericProcessor";
5
+ type callType = {
6
+ key: keyof dataRawSpectra;
7
+ call: string;
8
+ target: keyof callKeysSpectra;
9
+ metaData?: any;
10
+ };
11
+ type callKeysSpectra = mandatoryCallKeys & {
12
+ tokenPrice: string;
13
+ name: string;
14
+ principalToken: string;
15
+ };
16
+ type dataRawSpectra = callKeysSpectra & {
17
+ principalToken: string;
18
+ };
19
+ type dataTypeSpectra = dataType & {
20
+ tokenPrice: string;
21
+ };
22
+ export declare class SpectraYTProcessor extends GenericProcessor<callKeysSpectra, dataRawSpectra, dataTypeSpectra> {
23
+ rounds: {
24
+ round1: callType[];
25
+ round2: callType[];
26
+ round3: callType[];
27
+ round4: callType[];
28
+ };
29
+ processingRound5(_index: number, type: tokenType, typeInfo: dataRawSpectra, _calls: string[], campaign: CampaignParameters<Campaign.ERC20> | CampaignParameters<Campaign.EULER>, _pricer: Pricer): Promise<dataTypeSpectra>;
30
+ }
31
+ export {};
@@ -0,0 +1,39 @@
1
+ import { NETWORK_SLUGS } from "../../../../../constants";
2
+ import { generateCardName } from "../../../../../utils/generateCardName";
3
+ import { GenericProcessor } from "./GenericProcessor";
4
+ export class SpectraYTProcessor extends GenericProcessor {
5
+ rounds = {
6
+ round1: [{ key: "principalToken", call: "principalToken", target: "tokenAddress" }],
7
+ round2: [],
8
+ round3: [],
9
+ round4: [{ key: "totalSupply", call: "totalSupply", target: "tokenAddress" }],
10
+ };
11
+ async processingRound5(_index, type, typeInfo, _calls, campaign, _pricer) {
12
+ const { whitelistedSupplyTargetToken, totalSupply, blacklistedSupply } = this.handleWhiteListBlacklistRound5(typeInfo, campaign);
13
+ if (!NETWORK_SLUGS[campaign.computeChainId]) {
14
+ throw new Error("Network slug not found");
15
+ }
16
+ const principalToken = typeInfo.principalToken.toLowerCase();
17
+ const spectraData = (await (await fetch(`https://app.spectra.finance/api/v1/${NETWORK_SLUGS[campaign.computeChainId]}/pools`, {
18
+ headers: {
19
+ "x-client-id": "Merkl Studio",
20
+ source: "Merkl Studio",
21
+ },
22
+ })).json());
23
+ const poolData = spectraData.find(d => d.address.toLowerCase() === principalToken);
24
+ if (!poolData) {
25
+ throw new Error("Pool not found in Spectra API");
26
+ }
27
+ const tvl = poolData.pools[0].ytPrice.usd * totalSupply; // in USD
28
+ const priceTargetToken = tvl / totalSupply;
29
+ return {
30
+ ...typeInfo,
31
+ totalSupply,
32
+ tvl,
33
+ whitelistedSupplyTargetToken,
34
+ blacklistedSupply,
35
+ priceTargetToken,
36
+ cardName: generateCardName(type, typeInfo, campaign),
37
+ };
38
+ }
39
+ }
@@ -26,6 +26,8 @@ import { PendleYTProcessor } from "./PendleYTProcessor";
26
26
  import { RadiantProcessor } from "./RadiantProcessor";
27
27
  import { RfxProcessor } from "./RfxProcessor";
28
28
  import { SatlayerProcessor } from "./Satlayer";
29
+ import { SpectraProcessor } from "./SpectraProcessor";
30
+ import { SpectraYTProcessor } from "./SpectraYTProcessor";
29
31
  import { SpliceProcessor } from "./SpliceProcessor";
30
32
  import { StakingProcessor } from "./StakingProcessor";
31
33
  import { SturdySiloProcessor } from "./SturdySiloProcessor";
@@ -122,4 +124,6 @@ export const processorMapping = {
122
124
  [tokenType.vicuna_borrowing]: AaveProcessor,
123
125
  [tokenType.vicuna_lending]: AaveProcessor,
124
126
  [tokenType.anglesLiquid]: AnglesLiquidProcessor,
127
+ [tokenType.spectra_lpt]: SpectraProcessor,
128
+ [tokenType.spectra_yt]: SpectraYTProcessor,
125
129
  };
@@ -142,6 +142,10 @@ function satisfiesNameConditions(name, type) {
142
142
  return lowerCaseName.includes("concrete");
143
143
  case tokenType.equalizer_gauge:
144
144
  return lowerCaseName.includes("equalizer");
145
+ case tokenType.spectra_lpt:
146
+ return lowerCaseName.includes("spectra") && lowerCaseName.includes("curve");
147
+ case tokenType.spectra_yt:
148
+ return lowerCaseName.includes("yield") && lowerCaseName.includes("token");
145
149
  default:
146
150
  return false;
147
151
  }
@@ -301,7 +305,7 @@ export function getTokenTypeRound1(calls, targetToken, index, campaign) {
301
305
  try {
302
306
  name = decodeCall(returnValueOfCalls, index + 2, "name");
303
307
  }
304
- catch (e) {
308
+ catch {
305
309
  return generateResult(tokenType.unknown, "Unknown", targetToken, {});
306
310
  }
307
311
  result = parseForBalancer(returnValue, targetToken, name);
@@ -0,0 +1,3 @@
1
+ import { type Campaign, type CampaignDynamicData, type CampaignParameters, type MerklChainId } from "@sdk";
2
+ import type { UncachedResult } from "../../../utils/execute";
3
+ export declare function UniswapV4DynamicData(chainId: MerklChainId, campaigns: CampaignParameters<Campaign.UNISWAP_V4>[]): Promise<UncachedResult<Partial<CampaignDynamicData<Campaign.UNISWAP_V4>[]>>>;
@@ -0,0 +1,228 @@
1
+ import { BN2Number, EAprBreakdownType, NETWORK_LABELS, UniswapV4Addresses, UniswapV4StateViewInterface, getSqrtRatioAtTick, shortenAddress, } from "@sdk";
2
+ import moment from "moment";
3
+ import { log } from "../../../utils/logger";
4
+ import { Pricer } from "../../../utils/pricer";
5
+ const CALLS_LENGTH = 2;
6
+ export async function UniswapV4DynamicData(chainId, campaigns) {
7
+ const dynamicData = [];
8
+ const pricer = await Pricer.load();
9
+ const calls = [];
10
+ /** Dedupe pools from campaigns to build pool list */
11
+ const poolList = [];
12
+ for (const campaign of campaigns ?? []) {
13
+ /** Loop through all campaigns to add pools */
14
+ if (!poolList?.map(p => p.mainParameter.toLowerCase()).includes(campaign.mainParameter.toLowerCase())) {
15
+ poolList.push({
16
+ poolId: campaign.campaignParameters.poolId,
17
+ mainParameter: campaign.mainParameter, // FIXME
18
+ });
19
+ }
20
+ }
21
+ if (!!poolList) {
22
+ for (const pool of poolList) {
23
+ calls.push({
24
+ allowFailure: true,
25
+ callData: UniswapV4StateViewInterface.encodeFunctionData("getLiquidity", [pool.poolId]),
26
+ target: UniswapV4Addresses[chainId].StateView,
27
+ }, {
28
+ allowFailure: true,
29
+ callData: UniswapV4StateViewInterface.encodeFunctionData("getSlot0", [pool.poolId]),
30
+ target: UniswapV4Addresses[chainId].StateView,
31
+ }
32
+ // {
33
+ // allowFailure: true,
34
+ // callData: ERC20Interface.encodeFunctionData("balanceOf", [pool.address]),
35
+ // target: d.campaignParameters.token0,
36
+ // },
37
+ // {
38
+ // allowFailure: true,
39
+ // callData: ERC20Interface.encodeFunctionData("balanceOf", [pool.address]),
40
+ // target: d.campaignParameters.token1,
41
+ // }
42
+ );
43
+ }
44
+ return {
45
+ cached: false,
46
+ call: {
47
+ callData: calls,
48
+ handler: () => { },
49
+ reducer: async (result) => {
50
+ let i = 0;
51
+ if (!!poolList) {
52
+ for (const pool of poolList) {
53
+ // This liquidity call gives the active liquidity on the pool. To get the total liquidity we would need to loop over all positions
54
+ let poolTotalLiquidity;
55
+ let sqrtPrice;
56
+ let tick;
57
+ const poolBalanceToken0 = 0;
58
+ const poolBalanceToken1 = 0;
59
+ const campaignsForPool = campaigns?.filter(campaign => campaign.mainParameter.toLowerCase() === pool.mainParameter.toLowerCase());
60
+ // const decimalsToken0 = d.campaignParameters.decimalsCurrency0;
61
+ // const decimalsToken1 = d.campaignParameters.decimalsCurrency1;
62
+ const symbolToken0 = campaignsForPool[0].campaignParameters.symbolCurrency0;
63
+ const symbolToken1 = campaignsForPool[0].campaignParameters.symbolCurrency1;
64
+ const prevI = i;
65
+ try {
66
+ poolTotalLiquidity = UniswapV4StateViewInterface.decodeFunctionResult("getLiquidity", result[i++])[0].toString();
67
+ const poolData = UniswapV4StateViewInterface.decodeFunctionResult("getSlot0", result[i++]);
68
+ tick = poolData.tick;
69
+ sqrtPrice = getSqrtRatioAtTick(tick).toString();
70
+ // poolBalanceToken0 = BN2Number(
71
+ // ERC20Interface.decodeFunctionResult("balanceOf", result[i++])[0],
72
+ // decimalsToken0
73
+ // );
74
+ // poolBalanceToken1 = BN2Number(
75
+ // ERC20Interface.decodeFunctionResult("balanceOf", result[i++])[0],
76
+ // decimalsToken1
77
+ // );
78
+ }
79
+ catch {
80
+ log.warn(`merklDynamic data - failed to decode state of pool ${pool.poolId} on ${NETWORK_LABELS[chainId]}`);
81
+ i = prevI + CALLS_LENGTH;
82
+ continue;
83
+ }
84
+ const priceToken0 = (await pricer.get({
85
+ address: campaignsForPool[0].campaignParameters.currency0,
86
+ chainId: chainId,
87
+ symbol: symbolToken0,
88
+ }));
89
+ const priceToken1 = (await pricer.get({
90
+ address: campaignsForPool[0].campaignParameters.currency1,
91
+ chainId: chainId,
92
+ symbol: symbolToken1,
93
+ }));
94
+ /** Iterate over distributions to compute APRs */
95
+ for (const campaign of campaignsForPool) {
96
+ const c = campaign;
97
+ const amount = BN2Number(c.amount, c.campaignParameters.decimalsRewardToken);
98
+ const startTimestamp = BN2Number(c.startTimestamp, 0);
99
+ const endTimestamp = BN2Number(c.endTimestamp, 0);
100
+ const isLive = moment().unix() > startTimestamp && moment().unix() < endTimestamp;
101
+ const totalWeight = BN2Number(c.campaignParameters.weightFees, 4) +
102
+ BN2Number(c.campaignParameters.weightToken0, 4) +
103
+ BN2Number(c.campaignParameters.weightToken1, 4);
104
+ // Proportions in percentage
105
+ const propFees = (BN2Number(c.campaignParameters.weightFees, 4) / totalWeight) * 100;
106
+ const propToken0 = (BN2Number(c.campaignParameters.weightToken0, 4) / totalWeight) * 100;
107
+ const propToken1 = (BN2Number(c.campaignParameters.weightToken1, 4) / totalWeight) * 100;
108
+ let distributionMeanAPR = 0;
109
+ const blacklistedBalance0 = 0;
110
+ const blacklistedBalance1 = 0;
111
+ const blacklistedLiquidity = 0;
112
+ const aprs = {};
113
+ const aprBreakdowns = [];
114
+ let priceRewardToken = 0;
115
+ if (isLive && c.campaignParameters.symbolRewardToken !== "aglaMerkl") {
116
+ priceRewardToken = (await pricer.get({
117
+ address: c.rewardToken,
118
+ chainId: chainId,
119
+ symbol: c.campaignParameters.symbolRewardToken,
120
+ }));
121
+ /**
122
+ * Handle whitelisted/blacklisted addresses to compute APR
123
+ */
124
+ if (c.campaignParameters.whitelist.length > 0) {
125
+ // TODO
126
+ }
127
+ else if (c.campaignParameters.blacklist.length > 0) {
128
+ // TODO
129
+ }
130
+ /** Yearly rewards in $ */
131
+ const yearlyToken0Rewards = (propToken0 * priceRewardToken * amount * (365 * 24 * 3_600)) / (endTimestamp - startTimestamp);
132
+ const yearlyToken1Rewards = (propToken1 * priceRewardToken * amount * (365 * 24 * 3_600)) / (endTimestamp - startTimestamp);
133
+ const yearlyFeeRewards = (propFees * priceRewardToken * amount * (365 * 24 * 3_600)) / (endTimestamp - startTimestamp);
134
+ let poolAPRkey = "";
135
+ /**
136
+ * General APR (@notice potentially with a boost)
137
+ */
138
+ const poolBalanceToken0WithoutBlacklist = 0;
139
+ const poolBalanceToken1WithoutBlacklist = 0;
140
+ // const poolLiquidityWithoutBlacklist = poolTotalLiquidity - (blacklistedLiquidity ?? 0);
141
+ const tvl = poolBalanceToken0WithoutBlacklist * priceToken0 + poolBalanceToken1WithoutBlacklist * priceToken1;
142
+ distributionMeanAPR = (yearlyToken0Rewards + yearlyToken1Rewards + yearlyFeeRewards) / tvl;
143
+ distributionMeanAPR =
144
+ !distributionMeanAPR || Number.isNaN(distributionMeanAPR) ? 0 : distributionMeanAPR;
145
+ /**
146
+ * @dev We cannot include a whitelisted distrib apr into the mean APR
147
+ */
148
+ if (c.campaignParameters.whitelist.length === 0) {
149
+ poolAPRkey = "Average APR (rewards / pool TVL)";
150
+ if (!aprs[poolAPRkey])
151
+ aprs[poolAPRkey] = 0;
152
+ aprs[poolAPRkey] += distributionMeanAPR;
153
+ // @Hugo wip: new way to structure aprBreakdowns
154
+ aprBreakdowns.push({
155
+ address: pool.poolId,
156
+ value: distributionMeanAPR,
157
+ type: EAprBreakdownType.AVERAGE,
158
+ label: "Average APR (rewards / pool TVL)",
159
+ });
160
+ // APR per token
161
+ poolAPRkey = `APR for holding ${c.campaignParameters.symbolCurrency0} in pool`;
162
+ if (!aprs[poolAPRkey])
163
+ aprs[poolAPRkey] = 0;
164
+ aprs[poolAPRkey] += yearlyToken0Rewards / (poolBalanceToken0WithoutBlacklist * priceToken0);
165
+ // @Hugo wip: new way to structure aprBreakdowns
166
+ aprBreakdowns.push({
167
+ address: pool.poolId,
168
+ value: yearlyToken0Rewards / (poolBalanceToken0WithoutBlacklist * priceToken0),
169
+ type: EAprBreakdownType.TOKEN1,
170
+ label: c.campaignParameters.symbolCurrency0,
171
+ });
172
+ poolAPRkey = `APR for holding ${c.campaignParameters.symbolCurrency1} in pool`;
173
+ if (!aprs[poolAPRkey])
174
+ aprs[poolAPRkey] = 0;
175
+ aprs[poolAPRkey] += yearlyToken1Rewards / (poolBalanceToken1WithoutBlacklist * priceToken1);
176
+ // @Hugo wip: new way to structure aprBreakdowns
177
+ aprBreakdowns.push({
178
+ address: pool.poolId,
179
+ value: yearlyToken1Rewards / (poolBalanceToken1WithoutBlacklist * priceToken1),
180
+ type: EAprBreakdownType.TOKEN2,
181
+ label: c.campaignParameters.symbolCurrency1,
182
+ });
183
+ }
184
+ else {
185
+ for (const _ of c.campaignParameters.whitelist) {
186
+ const poolAPRkey = `Whitelisted campaign on UniswapV4 via address ${shortenAddress(c.campaignParameters.whitelist[0])} Average APR`;
187
+ if (!aprs[poolAPRkey])
188
+ aprs[poolAPRkey] = 0;
189
+ }
190
+ aprs[poolAPRkey] += distributionMeanAPR;
191
+ }
192
+ }
193
+ dynamicData.push({
194
+ ...campaign,
195
+ apr: distributionMeanAPR,
196
+ aprs,
197
+ aprBreakdowns,
198
+ blacklistedBalance0,
199
+ blacklistedBalance1,
200
+ blacklistedLiquidity,
201
+ poolBalanceToken0,
202
+ poolBalanceToken1,
203
+ poolTotalLiquidity,
204
+ sqrtPrice,
205
+ tick: tick,
206
+ priceRewardToken: priceRewardToken,
207
+ tvl: poolBalanceToken0 * priceToken0 + poolBalanceToken1 * priceToken1,
208
+ });
209
+ }
210
+ }
211
+ }
212
+ return dynamicData;
213
+ },
214
+ },
215
+ };
216
+ }
217
+ // Fallback in case something fails
218
+ return {
219
+ cached: false,
220
+ call: {
221
+ callData: [],
222
+ handler: () => { },
223
+ reducer: async (_result) => {
224
+ return campaigns;
225
+ },
226
+ },
227
+ };
228
+ }
@@ -14,6 +14,7 @@ import { JSON_AIRDROPDynamicData } from "./campaignTypes/JSON_AIRDROPDynamicData
14
14
  import { MORPHODynamicData } from "./campaignTypes/MORPHODynamicData";
15
15
  import { RadiantDynamicData } from "./campaignTypes/RadiantDynamicData";
16
16
  import { SILODynamicData } from "./campaignTypes/SILODynamicData";
17
+ import { UniswapV4DynamicData } from "./campaignTypes/UniswapV4DynamicData";
17
18
  import { VestDynamicData } from "./campaignTypes/VestDynamicData";
18
19
  export async function campaignsDynamicData(chainId, campaigns, type) {
19
20
  try {
@@ -82,6 +83,9 @@ export async function campaignsDynamicData(chainId, campaigns, type) {
82
83
  case Campaign.AMBIENTPROCESSOR: {
83
84
  return AmbientDynamicData(chainId, campaigns);
84
85
  }
86
+ case Campaign.UNISWAP_V4: {
87
+ return UniswapV4DynamicData(chainId, campaigns);
88
+ }
85
89
  }
86
90
  return {
87
91
  cached: false,
@@ -46,6 +46,7 @@ export declare const campaignTypeToEnumMap: {
46
46
  readonly M0: any;
47
47
  readonly MORPHOSUPPLY: any;
48
48
  readonly SYNCSWAP_VAULT: any;
49
+ readonly UNISWAP_V4_LP: any;
49
50
  };
50
51
  export type ConvertedCampaignType<C extends CampaignType> = (typeof campaignTypeToEnumMap)[C];
51
52
  export declare const CampaignUniqueDto: import("@sinclair/typebox").TObject<{
@@ -33,6 +33,7 @@ export const campaignTypeToEnumMap = {
33
33
  M0: CampaignTypeEnum.M0,
34
34
  MORPHOSUPPLY: CampaignTypeEnum.MORPHOSUPPLY,
35
35
  SYNCSWAP_VAULT: CampaignTypeEnum.SYNCSWAP_VAULT,
36
+ UNISWAP_V4_LP: CampaignTypeEnum.UNISWAP_V4,
36
37
  };
37
38
  // ─── DTOs ────────────────────────────────────────────────────────────────────
38
39
  export const CampaignUniqueDto = t.Object({