@merkl/api 0.21.28 → 0.21.30

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.
@@ -31,6 +31,7 @@ const factoryAddresses = {
31
31
  "0xB255D6A720BB7c39fee173cE22113397119cB930": Erc20SubType.katana, // Katana Ronin
32
32
  "0x29372c22459a4e373851798bFd6808e71EA34A71": Erc20SubType.punchswap, // Punchswap Flow EVM
33
33
  "0x724412C00059bf7d6ee7d4a1d0D5cd4de3ea1C48": Erc20SubType.hyperswap, // Hyperswap Factory HyperEVM
34
+ "0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6": Erc20SubType.uniswapv2, // Uniswap Factory Base
34
35
  };
35
36
  export function getTypeFromFactoryAddress(address) {
36
37
  if (factoryAddresses[address]) {
@@ -118,7 +118,7 @@ function satisfiesNameConditions(name, type) {
118
118
  case Erc20SubType.lnd_lending:
119
119
  return lowerCaseName.includes("lnd");
120
120
  case Erc20SubType.dlend_lending:
121
- return lowerCaseName.includes("dlend");
121
+ return lowerCaseName.startsWith("dlend") && !lowerCaseName.includes("debt");
122
122
  case Erc20SubType.compound:
123
123
  return lowerCaseName.includes("compound");
124
124
  case Erc20SubType.ironcladStaking:
@@ -1,14 +1,12 @@
1
1
  import { Erc20SubType } from ".";
2
- import { DlendMetadata } from "./implementations/dlend/metadata";
3
- import { DlendTVLBuilder } from "./implementations/dlend/tvl";
2
+ import { AaveMetadata } from "./implementations/aave/metadata";
3
+ import { AaveTVLBuilder } from "./implementations/aave/tvl";
4
4
  import { EulerMetadata } from "./implementations/euler/metadata";
5
5
  import { EulerTVLBuilder } from "./implementations/euler/tvl";
6
6
  import { GearboxMetadata } from "./implementations/gearbox/metadata";
7
7
  import { GearboxTVLBuilder } from "./implementations/gearbox/tvl";
8
8
  import { LendleMetadata } from "./implementations/lendleVaults/metadata";
9
9
  import { LendleTVLBuilder } from "./implementations/lendleVaults/tvl";
10
- import { SuperlendMetadata } from "./implementations/superlend/metadata";
11
- import { SuperlendTVLBuilder } from "./implementations/superlend/tvl";
12
10
  import { TermMaxMetadata } from "./implementations/termmax/metadata";
13
11
  import { TermMaxTVLBuilder } from "./implementations/termmax/tvl";
14
12
  /**
@@ -19,14 +17,14 @@ import { TermMaxTVLBuilder } from "./implementations/termmax/tvl";
19
17
  */
20
18
  const tvlMap = {
21
19
  [Erc20SubType.gearbox]: new GearboxTVLBuilder(),
22
- [Erc20SubType.superlend_borrowing]: new SuperlendTVLBuilder(),
23
- [Erc20SubType.superlend_lending]: new SuperlendTVLBuilder(),
20
+ [Erc20SubType.superlend_borrowing]: new AaveTVLBuilder(),
21
+ [Erc20SubType.superlend_lending]: new AaveTVLBuilder(),
24
22
  [Erc20SubType.euler_borrow]: new EulerTVLBuilder(),
25
23
  [Erc20SubType.euler_lend]: new EulerTVLBuilder(),
26
24
  [Erc20SubType.lendle_vaults]: new LendleTVLBuilder(),
27
25
  [Erc20SubType.termmax]: new TermMaxTVLBuilder(),
28
- [Erc20SubType.dlend_lending]: new DlendTVLBuilder(),
29
- [Erc20SubType.dlend_borrowing]: new DlendTVLBuilder(),
26
+ [Erc20SubType.dlend_lending]: new AaveTVLBuilder(),
27
+ [Erc20SubType.dlend_borrowing]: new AaveTVLBuilder(),
30
28
  };
31
29
  export const erc20SubTypeTVLBuilderFactory = (erc20Subtype) => {
32
30
  if (!tvlMap[erc20Subtype]) {
@@ -42,14 +40,14 @@ export const erc20SubTypeTVLBuilderFactory = (erc20Subtype) => {
42
40
  */
43
41
  const metadataMap = {
44
42
  [Erc20SubType.gearbox]: new GearboxMetadata(),
45
- [Erc20SubType.superlend_borrowing]: new SuperlendMetadata(),
46
- [Erc20SubType.superlend_lending]: new SuperlendMetadata(),
43
+ [Erc20SubType.superlend_borrowing]: new AaveMetadata("superlend", "Superlend", (underlyingToken) => `https://markets.superlend.xyz/reserve-overview/?underlyingAsset=${underlyingToken}`),
44
+ [Erc20SubType.superlend_lending]: new AaveMetadata("superlend", "Superlend", (underlyingToken) => `https://markets.superlend.xyz/reserve-overview/?underlyingAsset=${underlyingToken}`),
47
45
  [Erc20SubType.euler_borrow]: new EulerMetadata(),
48
46
  [Erc20SubType.euler_lend]: new EulerMetadata(),
49
47
  [Erc20SubType.lendle_vaults]: new LendleMetadata(),
50
48
  [Erc20SubType.termmax]: new TermMaxMetadata(),
51
- [Erc20SubType.dlend_lending]: new DlendMetadata(),
52
- [Erc20SubType.dlend_borrowing]: new DlendMetadata(),
49
+ [Erc20SubType.dlend_lending]: new AaveMetadata("dlend", "dTrinity dLend", (underlyingToken) => `https://app.dtrinity.org/dlend/reserve-overview/?underlyingAsset=${underlyingToken}`),
50
+ [Erc20SubType.dlend_borrowing]: new AaveMetadata("dlend", "dTrinity dLend", (underlyingToken) => `https://app.dtrinity.org/dlend/reserve-overview/?underlyingAsset=${underlyingToken}`),
53
51
  };
54
52
  export const erc20SubTypeMetadataBuilderFactory = (erc20Subtype) => {
55
53
  if (!metadataMap[erc20Subtype]) {
@@ -0,0 +1,30 @@
1
+ import type { MetadataBuilder } from "@/engine/metadata/interface";
2
+ import type { CampaignWithParams } from "@/modules/v4/campaign/campaign.model";
3
+ import type { ProtocolId } from "@/modules/v4/protocol/protocol.model";
4
+ import type { Erc20LikeCampaignEnum } from "../..";
5
+ export declare class AaveMetadata implements MetadataBuilder<Erc20LikeCampaignEnum> {
6
+ /**
7
+ * @notice Protocol entity ID - must be defined in the corresponding file and created in DB
8
+ */
9
+ mainProtocol: ProtocolId;
10
+ /**
11
+ * @notice Protocol name - used to create the opportunity name
12
+ */
13
+ protocolName: string;
14
+ /**
15
+ * @notice Function to generate the deposit URL for the opportunity
16
+ */
17
+ depositUrl: (underlyingToken: string) => string;
18
+ constructor(mainProtocol: ProtocolId, protocolName: string, depositUrl: (underlyingToken: string) => string);
19
+ build(campaign: Omit<CampaignWithParams<Erc20LikeCampaignEnum>, "manualOverrides">, _opportunityIdentifier: string): Promise<{
20
+ action: "LEND" | "BORROW";
21
+ mainProtocol: "splice" | "reserve" | "morpho" | "quickswap" | "euler" | "uniswap" | "ambient" | "arthswap" | "base-swap" | "camelot" | "crust" | "fenix" | "horiza" | "izumi" | "kim" | "pancake-swap" | "ramses" | "retro" | "stryke" | "sushi-swap" | "swapr" | "thruster" | "voltage" | "zero" | "koi" | "supswap" | "zk-swap" | "thirdtrade" | "swap-x" | "velodrome" | "aerodrome" | "balancer" | "curve" | "cross_curve" | "curveNPool" | "aura" | "akron" | "beefy" | "dragonswap" | "poolside" | "syncswap" | "neptune" | "zkSwapThreePool" | "rfx" | "ra" | "maverick" | "trader-joe" | "hanji" | "radiant" | "aave" | "fraxlend" | "ironclad" | "gearbox" | "compound" | "sturdy" | "frax" | "ionic" | "moonwell" | "fluid" | "silo" | "dolomite" | "badger" | "ajna" | "layerbank" | "ion" | "venus" | "woofi" | "reactor_fusion" | "eigenlayer" | "vest" | "zerolend" | "lnd" | "dlend" | "hyperdrive" | "gamma" | "oku" | "hourglass" | "veda" | "kyo" | "sonex" | "lendle" | "tako-tako" | "equalizer" | "spectra" | "beraborrow" | "superlend" | "avalon" | "iguana" | "xlend" | "sake" | "sonicmarket" | "stability" | "angles" | "enzyme" | "toros" | "vicuna" | "bunni" | "beratrax" | "concrete" | "cian" | "pendle" | "yei" | "termmax" | "filament" | "gammaswap" | "maha" | "tempest" | "uranium" | "holdstation" | "katana" | "punchswap" | "satlayer" | "puffer";
22
+ name: string;
23
+ tokens: {
24
+ chainId: number;
25
+ address: any;
26
+ }[];
27
+ depositUrl: string;
28
+ explorerAddress: any;
29
+ }>;
30
+ }
@@ -1,7 +1,24 @@
1
1
  import { TokenService } from "@/modules/v4/token/token.service";
2
2
  import { OpportunityAction } from "@db/api";
3
3
  import { Aave__factory, ChainInteractionService, TokenInteractionService } from "@sdk";
4
- export class SuperlendMetadata {
4
+ export class AaveMetadata {
5
+ /**
6
+ * @notice Protocol entity ID - must be defined in the corresponding file and created in DB
7
+ */
8
+ mainProtocol;
9
+ /**
10
+ * @notice Protocol name - used to create the opportunity name
11
+ */
12
+ protocolName;
13
+ /**
14
+ * @notice Function to generate the deposit URL for the opportunity
15
+ */
16
+ depositUrl;
17
+ constructor(mainProtocol, protocolName, depositUrl) {
18
+ this.mainProtocol = mainProtocol;
19
+ this.protocolName = protocolName;
20
+ this.depositUrl = depositUrl;
21
+ }
5
22
  async build(campaign, _opportunityIdentifier) {
6
23
  const { params, computeChainId } = campaign;
7
24
  const { targetToken } = params;
@@ -16,13 +33,13 @@ export class SuperlendMetadata {
16
33
  : OpportunityAction.LEND;
17
34
  return {
18
35
  action,
19
- mainProtocol: "superlend",
20
- name: `${action === OpportunityAction.BORROW ? "Borrow" : "Supply"} ${underlyingTokenSymbol} on Superlend`,
36
+ mainProtocol: this.mainProtocol,
37
+ name: `${action === OpportunityAction.BORROW ? "Borrow" : "Supply"} ${underlyingTokenSymbol} on ${this.protocolName}`,
21
38
  tokens: [
22
39
  { chainId: computeChainId, address: targetToken },
23
40
  { chainId: computeChainId, address: underlyingToken },
24
41
  ],
25
- depositUrl: `https://markets.superlend.xyz/reserve-overview/?underlyingAsset=${underlyingToken}`,
42
+ depositUrl: this.depositUrl(underlyingToken),
26
43
  explorerAddress: params.targetToken,
27
44
  };
28
45
  }
@@ -1,6 +1,6 @@
1
1
  import type { Erc20LikeCampaignEnum } from "@/engine/implementations/Erc20/subTypes";
2
2
  import type { TVLBuilder, TVLData } from "@/engine/tvl/interface";
3
3
  import { type CampaignParameters, type MerklChainId } from "@sdk";
4
- export declare class DlendTVLBuilder implements TVLBuilder<Erc20LikeCampaignEnum> {
4
+ export declare class AaveTVLBuilder implements TVLBuilder<Erc20LikeCampaignEnum> {
5
5
  build(computeChainId: MerklChainId, campaigns: CampaignParameters<Erc20LikeCampaignEnum>[]): Promise<TVLData<any>>;
6
6
  }
@@ -1,7 +1,7 @@
1
1
  import { TokenService } from "@/modules/v4/token/token.service";
2
2
  import { TvlType } from "@db/api";
3
3
  import { AaveInterface, ChainInteractionService, ERC20Interface, bigIntToNumber, } from "@sdk";
4
- export class DlendTVLBuilder {
4
+ export class AaveTVLBuilder {
5
5
  async build(computeChainId, campaigns) {
6
6
  const tvls = [];
7
7
  const firstRound = await ChainInteractionService(computeChainId).fetchAndDecodeObject(campaigns.flatMap(campaign => {
@@ -3,20 +3,20 @@ import { TokenService } from "@/modules/v4/token/token.service";
3
3
  import { log } from "@/utils/logger";
4
4
  import { engineDbClient } from "@db";
5
5
  import { TvlType } from "@db/api";
6
- import { BN2Number, ChainInteractionService, NETWORK_LABELS, } from "@sdk";
6
+ import { BN2Number, ChainInteractionService, NETWORK_LABELS, UniswapV4Addresses, UniswapV4StateViewInterface, getRawPriceFromSqrtRatioX96, getSqrtRatioAtTick, } from "@sdk";
7
7
  /**
8
8
  * Compute TVL
9
9
  * @dev important: using the most recent state save with current prices
10
10
  * it's only an estimate
11
11
  */
12
- async function computeUniV4PoolTVLFromMostRecentStateSave(computeChainId, poolID) {
12
+ async function computeUniV4PoolTVLFromMostRecentStateSave(computeChainId, poolID, campaign) {
13
13
  let stateSave;
14
14
  let blockNumber;
15
15
  let states = {};
16
16
  // TEMPORARY: handle Gamma TVL
17
17
  const nodesTVL = {};
18
+ const currentBlock = await ChainInteractionService(computeChainId).getBlockNumber();
18
19
  try {
19
- const currentBlock = await ChainInteractionService(computeChainId).getBlockNumber();
20
20
  const mostRecentStateSave = await engineDbClient.stateSave.findFirst({
21
21
  where: {
22
22
  id: `UniswapV4_${computeChainId}_${poolID}`,
@@ -37,25 +37,57 @@ async function computeUniV4PoolTVLFromMostRecentStateSave(computeChainId, poolID
37
37
  return { amount0: 0n, amount1: 0n, blockNumber: blockNumber ?? 0, nodesTVL };
38
38
  }
39
39
  const { fileName } = states;
40
- // Get nodes
40
+ // Root campaign info
41
+ const hasRulesOnPrice = !!campaign?.campaignParameters.lowerPriceBond || !!campaign?.campaignParameters.upperPriceBond;
42
+ let priceAtTick = 0n; // price in base 4
43
+ if (hasRulesOnPrice) {
44
+ const res = await ChainInteractionService(computeChainId).fetchArchiveState([
45
+ {
46
+ allowFailure: true,
47
+ callData: UniswapV4StateViewInterface.encodeFunctionData("getSlot0", [campaign.campaignParameters.poolId]),
48
+ target: UniswapV4Addresses[computeChainId].StateView,
49
+ },
50
+ ], blockNumber ?? currentBlock);
51
+ const poolData = UniswapV4StateViewInterface.decodeFunctionResult("getSlot0", res[0].returnData);
52
+ const tick = poolData.tick;
53
+ const sqrtRatioAtTick = BigInt(getSqrtRatioAtTick(tick).toString());
54
+ priceAtTick = getRawPriceFromSqrtRatioX96(1, sqrtRatioAtTick, campaign.campaignParameters.decimalsCurrency0, campaign.campaignParameters.decimalsCurrency1);
55
+ }
56
+ // SubCampaigns info
41
57
  const nodes = await engineDbClient.nodes.findMany({
42
58
  where: { chainId: computeChainId, nodeType: "GammaUniV4" },
43
59
  select: { nodeType: true, recipient: true },
44
60
  });
45
- // Bucket service
46
61
  let amount0 = 0n;
47
62
  let amount1 = 0n;
48
63
  try {
64
+ // Bucket service
49
65
  const bucket = new BucketService("merkl-production-states", "merkl-production");
50
66
  const storedStates = JSON.parse(await bucket.pull(fileName));
51
- for (const [_, { value, params: _params }] of Object.entries(storedStates)) {
52
- amount0 += BigInt(value.amount0);
53
- amount1 += BigInt(value.amount1);
54
- // TEMPORARY: handle Gamma TVL
55
- if (nodes.map(node => node.recipient).includes(_params.recipient)) {
56
- const node = nodes.find(node => node.recipient === _params.recipient);
67
+ for (const [_, { value, params }] of Object.entries(storedStates)) {
68
+ // _ Handle rules
69
+ if (!!hasRulesOnPrice && priceAtTick > 0n) {
70
+ if (!!campaign.campaignParameters.lowerPriceBond) {
71
+ const lowerPriceBond = BigInt(campaign.campaignParameters.lowerPriceBond);
72
+ const sqrtRatioAtLowerTick = BigInt(getSqrtRatioAtTick(params.additional.tickLower).toString());
73
+ const priceAtLowerTick = getRawPriceFromSqrtRatioX96(1, sqrtRatioAtLowerTick, campaign.campaignParameters.decimalsCurrency0, campaign.campaignParameters.decimalsCurrency1);
74
+ if (priceAtLowerTick < lowerPriceBond)
75
+ continue;
76
+ }
77
+ if (!!campaign.campaignParameters.upperPriceBond) {
78
+ const upperPriceBond = BigInt(campaign.campaignParameters.upperPriceBond);
79
+ const sqrtRatioAtUpperTick = BigInt(getSqrtRatioAtTick(params.additional.tickUpper).toString());
80
+ const priceAtUpperTick = getRawPriceFromSqrtRatioX96(1, sqrtRatioAtUpperTick, campaign.campaignParameters.decimalsCurrency0, campaign.campaignParameters.decimalsCurrency1);
81
+ if (priceAtUpperTick > upperPriceBond)
82
+ continue;
83
+ }
84
+ }
85
+ // _ Handle subcampaigns TVL
86
+ // ----------------- TEMPORARY: handle Gamma TVL
87
+ if (nodes.map(node => node.recipient).includes(params.recipient)) {
88
+ const node = nodes.find(node => node.recipient === params.recipient);
57
89
  if (!node) {
58
- log.warn(`merklDynamic data - failed to find node ${_params.recipient} for pool ${poolID}`);
90
+ log.warn(`merklDynamic data - failed to find node ${params.recipient} for pool ${poolID}`);
59
91
  continue;
60
92
  }
61
93
  if (!nodesTVL[node.nodeType]) {
@@ -63,7 +95,11 @@ async function computeUniV4PoolTVLFromMostRecentStateSave(computeChainId, poolID
63
95
  }
64
96
  nodesTVL[node.nodeType].amount0 += BigInt(value.amount0);
65
97
  nodesTVL[node.nodeType].amount1 += BigInt(value.amount1);
98
+ // -----------------
66
99
  }
100
+ // _ Add amounts for TVL
101
+ amount0 += BigInt(value.amount0);
102
+ amount1 += BigInt(value.amount1);
67
103
  }
68
104
  }
69
105
  catch {
@@ -75,7 +111,7 @@ export class UniswapV4TVLBuilder {
75
111
  async build(computeChainId, campaigns) {
76
112
  const tvls = [];
77
113
  for (const campaign of campaigns) {
78
- const { amount0: poolBalanceToken0, amount1: poolBalanceToken1, nodesTVL, } = await computeUniV4PoolTVLFromMostRecentStateSave(computeChainId, campaign.campaignParameters.poolId);
114
+ const { amount0: poolBalanceToken0, amount1: poolBalanceToken1, nodesTVL, } = await computeUniV4PoolTVLFromMostRecentStateSave(computeChainId, campaign.campaignParameters.poolId, campaign);
79
115
  const token0Id = TokenService.hashId({
80
116
  chainId: computeChainId,
81
117
  address: campaign.campaignParameters.currency0,
@@ -40,8 +40,9 @@ export function generateCardName(type, typeInfo, campaign, symbols = [""], displ
40
40
  case Erc20SubType.traderJoe:
41
41
  case Erc20SubType.katana:
42
42
  case Erc20SubType.punchswap:
43
- case Erc20SubType.hyperswap:
44
43
  return `Provide liquidity to ${capitalize(typeInfo.protocol)} ${typeInfo.symbolToken0}-${typeInfo.symbolToken1}`;
44
+ case Erc20SubType.hyperswap:
45
+ return `Provide liquidity to HyperSwap Standard AMM ${typeInfo.symbolToken0}-${typeInfo.symbolToken1}`;
45
46
  case Erc20SubType.pendleYT:
46
47
  return `Hold ${capitalize(typeInfo.protocol)} ${typeInfo.name}`;
47
48
  case Erc20SubType.pendle: