@merkl/api 0.15.17 → 0.15.19
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.
- package/dist/src/constants.d.ts +4 -1
- package/dist/src/constants.js +9 -1
- package/dist/src/entities/opportunity.js +58 -0
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/helpers/tokenType.d.ts +3 -1
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/helpers/tokenType.js +4 -0
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/BeefyProcessor.js +0 -1
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/SpectraProcessor.d.ts +31 -0
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/SpectraProcessor.js +41 -0
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/SpectraYTProcessor.d.ts +31 -0
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/SpectraYTProcessor.js +39 -0
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/processor/processorMapping.js +4 -0
- package/dist/src/libs/campaigns/campaignTypes/ERC20SubTypes/subtypesRound1.js +5 -1
- package/dist/src/libs/campaigns/campaignTypes/UniswapV4DynamicData.d.ts +3 -0
- package/dist/src/libs/campaigns/campaignTypes/UniswapV4DynamicData.js +228 -0
- package/dist/src/libs/campaigns/campaignsDynamicData.js +4 -0
- package/dist/src/modules/v4/campaign/campaign.model.d.ts +1 -0
- package/dist/src/modules/v4/campaign/campaign.model.js +1 -0
- package/dist/src/modules/v4/reward/reward.service.js +2 -4
- package/dist/src/types/external/spectraAPI.d.ts +88 -0
- package/dist/src/types/external/spectraAPI.js +1 -0
- package/dist/src/utils/decodeCalls.js +9 -2
- package/dist/src/utils/encodeCalls.js +17 -2
- package/dist/src/utils/generateCardName.js +4 -0
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/src/constants.d.ts
CHANGED
@@ -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
|
+
};
|
package/dist/src/constants.js
CHANGED
@@ -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
|
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({
|
@@ -41,15 +41,13 @@ export class RewardService {
|
|
41
41
|
static format(rewards) {
|
42
42
|
return rewards.map(reward => {
|
43
43
|
const { Breakdown, RewardToken, id, rewardTokenId, ...rest } = reward;
|
44
|
-
const pending = Breakdown.reduce((sum, { pending }) => sum + BigInt(pending), 0n);
|
45
|
-
const claimed = Breakdown.reduce((sum, { claimed }) => sum + BigInt(claimed), 0n);
|
46
44
|
return {
|
47
45
|
...rest,
|
48
46
|
token: RewardToken,
|
49
47
|
breakdowns: Breakdown.map(RewardService.formatBreakdown),
|
50
|
-
claimed,
|
48
|
+
claimed: BigInt(rest.claimed),
|
51
49
|
amount: BigInt(rest.amount),
|
52
|
-
pending,
|
50
|
+
pending: BigInt(rest.pending),
|
53
51
|
};
|
54
52
|
});
|
55
53
|
}
|