@1delta/margin-fetcher 0.0.8 → 0.0.10

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 (146) hide show
  1. package/dist/abis/aave/AaveOracle.d.ts +10 -0
  2. package/dist/abis/aave/AaveOracle.d.ts.map +1 -1
  3. package/dist/abis/aave/AaveOracle.js +13 -0
  4. package/dist/abis/morpho/blue.d.ts +76 -0
  5. package/dist/abis/morpho/blue.d.ts.map +1 -0
  6. package/dist/abis/morpho/blue.js +99 -0
  7. package/dist/abis/morpho/lens.d.ts +16 -0
  8. package/dist/abis/morpho/lens.d.ts.map +1 -0
  9. package/dist/abis/morpho/lens.js +31 -0
  10. package/dist/abis/oracle/AaveOracle.d.ts +10 -0
  11. package/dist/abis/oracle/AaveOracle.d.ts.map +1 -1
  12. package/dist/abis/oracle/AaveOracle.js +13 -0
  13. package/dist/assets/index.d.ts +4 -4
  14. package/dist/assets/index.d.ts.map +1 -1
  15. package/dist/assets/index.js +7 -7
  16. package/dist/assets/liquidityThresholds.d.ts +29 -0
  17. package/dist/assets/liquidityThresholds.d.ts.map +1 -0
  18. package/dist/assets/liquidityThresholds.js +51 -0
  19. package/dist/flash-liquidity/index.d.ts +1 -0
  20. package/dist/flash-liquidity/index.d.ts.map +1 -1
  21. package/dist/flash-liquidity/index.js +1 -0
  22. package/dist/flash-liquidity/utils.d.ts +11 -0
  23. package/dist/flash-liquidity/utils.d.ts.map +1 -0
  24. package/dist/flash-liquidity/utils.js +17 -0
  25. package/dist/lending/aave-v2-type/publicCallParse.d.ts +2 -1
  26. package/dist/lending/aave-v2-type/publicCallParse.d.ts.map +1 -1
  27. package/dist/lending/aave-v2-type/publicCallParse.js +10 -7
  28. package/dist/lending/aave-v3-type/publicCallParse.d.ts +3 -2
  29. package/dist/lending/aave-v3-type/publicCallParse.d.ts.map +1 -1
  30. package/dist/lending/aave-v3-type/publicCallParse.js +11 -8
  31. package/dist/lending/aave-v3-type/types.d.ts +2 -0
  32. package/dist/lending/aave-v3-type/types.d.ts.map +1 -1
  33. package/dist/lending/addresses/compoundV3.d.ts +1 -1
  34. package/dist/lending/addresses/compoundV3.d.ts.map +1 -1
  35. package/dist/lending/addresses/compoundV3.js +8 -7
  36. package/dist/lending/compound-v3/publicCallParse.d.ts +2 -1
  37. package/dist/lending/compound-v3/publicCallParse.d.ts.map +1 -1
  38. package/dist/lending/compound-v3/publicCallParse.js +3 -2
  39. package/dist/lending/compound-v3/types.d.ts +2 -0
  40. package/dist/lending/compound-v3/types.d.ts.map +1 -1
  41. package/dist/lending/fetchLender.d.ts +2 -1
  42. package/dist/lending/fetchLender.d.ts.map +1 -1
  43. package/dist/lending/fetchLender.js +12 -8
  44. package/dist/lending/fetchLenderAll.d.ts +8 -0
  45. package/dist/lending/fetchLenderAll.d.ts.map +1 -0
  46. package/dist/lending/fetchLenderAll.js +12 -0
  47. package/dist/lending/fetchLenderExt.d.ts +8 -0
  48. package/dist/lending/fetchLenderExt.d.ts.map +1 -0
  49. package/dist/lending/fetchLenderExt.js +28 -0
  50. package/dist/lending/index.d.ts +1 -0
  51. package/dist/lending/index.d.ts.map +1 -1
  52. package/dist/lending/index.js +1 -0
  53. package/dist/lending/init/publicCallParse.d.ts +2 -1
  54. package/dist/lending/init/publicCallParse.d.ts.map +1 -1
  55. package/dist/lending/init/publicCallParse.js +3 -2
  56. package/dist/lending/init/types.d.ts +2 -0
  57. package/dist/lending/init/types.d.ts.map +1 -1
  58. package/dist/lending/morpho/convertPublic.d.ts +9 -0
  59. package/dist/lending/morpho/convertPublic.d.ts.map +1 -0
  60. package/dist/lending/morpho/convertPublic.js +115 -0
  61. package/dist/lending/morpho/fetchPublic.d.ts +3 -0
  62. package/dist/lending/morpho/fetchPublic.d.ts.map +1 -0
  63. package/dist/lending/morpho/fetchPublic.js +72 -0
  64. package/dist/lending/morpho/publicCallBuild.d.ts +8 -0
  65. package/dist/lending/morpho/publicCallBuild.d.ts.map +1 -0
  66. package/dist/lending/morpho/publicCallBuild.js +6 -0
  67. package/dist/lending/morpho/types.d.ts +106 -0
  68. package/dist/lending/morpho/types.d.ts.map +1 -0
  69. package/dist/lending/morpho/types.js +1 -0
  70. package/dist/lending/types/index.d.ts +18 -0
  71. package/dist/lending/types/index.d.ts.map +1 -0
  72. package/dist/lending/types/index.js +1 -0
  73. package/dist/lending/user-data/aave-v2-type/userCallBuild.d.ts.map +1 -1
  74. package/dist/lending/user-data/aave-v2-type/userCallBuild.js +4 -23
  75. package/dist/lending/user-data/aave-v2-type/userCallParse.d.ts.map +1 -1
  76. package/dist/lending/user-data/aave-v2-type/userCallParse.js +24 -11
  77. package/dist/lending/user-data/aave-v3-type/userCallBuild.d.ts.map +1 -1
  78. package/dist/lending/user-data/aave-v3-type/userCallBuild.js +1 -16
  79. package/dist/lending/user-data/aave-v3-type/userCallParse.d.ts.map +1 -1
  80. package/dist/lending/user-data/aave-v3-type/userCallParse.js +13 -6
  81. package/dist/lending/user-data/aave-v3-type/userCallParseYldr.d.ts.map +1 -1
  82. package/dist/lending/user-data/aave-v3-type/userCallParseYldr.js +12 -6
  83. package/dist/lending/user-data/compound-v3/addresses.d.ts.map +1 -1
  84. package/dist/lending/user-data/compound-v3/addresses.js +8 -7
  85. package/dist/lending/user-data/compound-v3/userCallParse.d.ts.map +1 -1
  86. package/dist/lending/user-data/compound-v3/userCallParse.js +2 -0
  87. package/dist/lending/user-data/morpho/decoder.d.ts +21 -0
  88. package/dist/lending/user-data/morpho/decoder.d.ts.map +1 -0
  89. package/dist/lending/user-data/morpho/decoder.js +52 -0
  90. package/dist/lending/user-data/morpho/morphoLib.d.ts +5 -0
  91. package/dist/lending/user-data/morpho/morphoLib.d.ts.map +1 -0
  92. package/dist/lending/user-data/morpho/morphoLib.js +21 -0
  93. package/dist/lending/user-data/morpho/types.d.ts +12 -0
  94. package/dist/lending/user-data/morpho/types.d.ts.map +1 -0
  95. package/dist/lending/user-data/morpho/types.js +13 -0
  96. package/dist/lending/user-data/morpho/userCallBuild.d.ts +6 -0
  97. package/dist/lending/user-data/morpho/userCallBuild.d.ts.map +1 -0
  98. package/dist/lending/user-data/morpho/userCallBuild.js +36 -0
  99. package/dist/lending/user-data/morpho/userCallParse.d.ts +11 -0
  100. package/dist/lending/user-data/morpho/userCallParse.d.ts.map +1 -0
  101. package/dist/lending/user-data/morpho/userCallParse.js +99 -0
  102. package/dist/lending/user-data/morpho/userCallParseNoDeploy.d.ts +11 -0
  103. package/dist/lending/user-data/morpho/userCallParseNoDeploy.d.ts.map +1 -0
  104. package/dist/lending/user-data/morpho/userCallParseNoDeploy.js +112 -0
  105. package/dist/lending-pairs/computeLendingPairs.d.ts +13 -0
  106. package/dist/lending-pairs/computeLendingPairs.d.ts.map +1 -1
  107. package/dist/lending-pairs/computeLendingPairs.js +129 -23
  108. package/dist/prices/main-prices/fetchOracleData.d.ts +1 -2
  109. package/dist/prices/main-prices/fetchOracleData.d.ts.map +1 -1
  110. package/dist/prices/main-prices/fetchOracleData.js +95 -55
  111. package/dist/utils/index.d.ts +2 -0
  112. package/dist/utils/index.d.ts.map +1 -1
  113. package/dist/utils/index.js +41 -16
  114. package/dist/utils/parsing.d.ts.map +1 -1
  115. package/dist/utils/parsing.js +4 -2
  116. package/package.json +2 -2
  117. package/src/abis/aave/AaveOracle.ts +13 -0
  118. package/src/abis/oracle/AaveOracle.ts +13 -0
  119. package/src/assets/index.ts +70 -51
  120. package/src/assets/liquidityThresholds.ts +83 -0
  121. package/src/flash-liquidity/index.ts +2 -1
  122. package/src/flash-liquidity/utils.ts +22 -0
  123. package/src/lending/aave-v2-type/publicCallParse.ts +12 -7
  124. package/src/lending/aave-v3-type/publicCallParse.ts +13 -8
  125. package/src/lending/aave-v3-type/types.ts +2 -0
  126. package/src/lending/addresses/compoundV3.ts +29 -15
  127. package/src/lending/compound-v3/publicCallParse.ts +5 -2
  128. package/src/lending/compound-v3/types.ts +2 -0
  129. package/src/lending/fetchLender.ts +23 -17
  130. package/src/lending/index.ts +2 -1
  131. package/src/lending/init/publicCallParse.ts +5 -2
  132. package/src/lending/init/types.ts +3 -0
  133. package/src/lending/types/index.ts +16 -0
  134. package/src/lending/user-data/aave-v2-type/userCallBuild.ts +1 -25
  135. package/src/lending/user-data/aave-v2-type/userCallParse.ts +24 -11
  136. package/src/lending/user-data/aave-v3-type/userCallBuild.ts +1 -16
  137. package/src/lending/user-data/aave-v3-type/userCallParse.ts +15 -6
  138. package/src/lending/user-data/aave-v3-type/userCallParseYldr.ts +14 -6
  139. package/src/lending/user-data/compound-v3/addresses.ts +58 -39
  140. package/src/lending/user-data/compound-v3/userCallParse.ts +2 -2
  141. package/src/lending-pairs/computeLendingPairs.ts +421 -189
  142. package/src/prices/main-prices/fetchOracleData.ts +197 -107
  143. package/src/utils/index.ts +90 -58
  144. package/src/utils/parsing.ts +51 -51
  145. package/test/index.test.ts +15 -7
  146. package/test/userdata.test.ts +5 -5
@@ -1,213 +1,445 @@
1
- import { getAssetMeta, toGenericPriceKey, toOracleKey } from "../assets"
2
- import { ConfigEntry, LenderData, PoolData } from "../types"
3
- import { isAaveV3Type, isInit } from "../utils"
1
+ import { getAssetMeta, toGenericPriceKey, toOracleKey } from "../assets";
2
+ import {
3
+ getLiquidityThresholds,
4
+ meetsLiquidityThresholds,
5
+ } from "../assets/liquidityThresholds";
6
+ import { ConfigEntry, LenderData, PoolData } from "../types";
7
+ import { isAaveV3Type, isInit } from "../utils";
4
8
 
5
9
  export interface LendingPair {
6
- // lender info
7
- chainId: string
8
- lender: string
9
- // asset addresses
10
- assetLong: string
11
- assetShort: string
12
-
13
- // asset group for long asset
14
- assetGroupLong: string
15
- // asset group for short asset
16
- assetGroupShort: string
17
- // pair prices
18
- price: number
19
- price24h: number
20
- priceChange: number
21
-
22
- // pair props
23
- maxLeverage: number
24
- apr: number
25
- tags?: string[]
26
- eMode?: number
10
+ // lender info
11
+ chainId: string;
12
+ lender: string;
13
+ // asset addresses
14
+ assetLong: string;
15
+ assetShort: string;
16
+
17
+ // asset group for long asset
18
+ assetGroupLong: string;
19
+ // asset group for short asset
20
+ assetGroupShort: string;
21
+ // pair prices
22
+ price: number;
23
+ price24h: number;
24
+ priceChange: number;
25
+
26
+ // pair props
27
+ maxLeverage: number;
28
+ apr: number;
29
+ baseApr: number;
30
+ rewardApr: number;
31
+ tags?: string[];
32
+ eMode?: number;
33
+
34
+ // liquidity data
35
+ longTotalDepositsUSD: number;
36
+ longTotalDebtUSD: number;
37
+ longTotalLiquidityUSD: number;
38
+ shortTotalDepositsUSD: number;
39
+ shortTotalDebtUSD: number;
40
+ shortTotalLiquidityUSD: number;
41
+
42
+ // status flags
43
+ longIsActive: boolean;
44
+ longIsFrozen: boolean;
45
+ shortIsActive: boolean;
46
+ shortIsFrozen: boolean;
47
+ shortBorrowingEnabled: boolean;
27
48
  }
28
49
 
29
- const isEMode = (longData: PoolData, shortData: PoolData) => Boolean(longData.eMode) ?
30
- Number(longData.eMode?.category) > 0 && Number(longData.eMode?.category) === shortData.eMode?.category : false
50
+ const isEMode = (longData: PoolData, shortData: PoolData) =>
51
+ Boolean(longData.eMode)
52
+ ? Number(longData.eMode?.category) > 0 &&
53
+ Number(longData.eMode?.category) === shortData.eMode?.category
54
+ : false;
31
55
 
32
56
  const intersection = <T>(a0: T[], a1: T[]): T[] => {
33
- return a0.filter(a => a1.includes(a))
34
- }
35
- const LENDER_MODE_NO_MODE = 0
57
+ return a0.filter((a) => a1.includes(a));
58
+ };
59
+ const LENDER_MODE_NO_MODE = 0;
36
60
 
37
61
  // calculate the maximum leverage for a given pair
38
- const getMaxLeverage = (lender: string, longData: PoolData, shortData: PoolData) => {
39
- // for Aave V3 we check for eModes
40
- if (isAaveV3Type(lender)) {
41
- const cf = isEMode(longData, shortData) ?
42
- longData.config[longData.eMode?.category ?? 0]?.borrowCollateralFactor :
43
- longData.config[LENDER_MODE_NO_MODE]?.borrowCollateralFactor
44
- return 1 / (1 - cf)
45
- }
46
- // for init, we match the modes and find the one with the highest collatearal factor
47
- if (isInit(lender)) {
48
- const modeIntersect = intersection(
49
- Object.values(longData.config).map((c: ConfigEntry) => c.category),
50
- Object.values(shortData.config).map((c: ConfigEntry) => c.category))
51
-
52
- if (modeIntersect.length === 0) return 1
53
- const configId = Object.values(longData.config).filter(
54
- (c: ConfigEntry) => modeIntersect.includes(c.category)
55
- ).sort((c: ConfigEntry, d: ConfigEntry) => c.borrowCollateralFactor < d.borrowCollateralFactor ? 1 : -1)[0].category
56
-
57
- // we have to use the adjusted formula with the borrowFactor here
58
- const cf = longData.config[configId]?.borrowCollateralFactor
59
- const bf = shortData.config[configId]?.borrowFactor
60
- return 1 / (1 - (cf / bf))
61
- }
62
- // all other cases use the default mode no_mode without borrowFactor
63
- const cf = longData?.config?.[LENDER_MODE_NO_MODE]?.borrowCollateralFactor ?? 0.8
64
- return 1 / (1 - cf)
65
- }
62
+ const getMaxLeverage = (
63
+ lender: string,
64
+ longData: PoolData,
65
+ shortData: PoolData
66
+ ) => {
67
+ // for Aave V3 we check for eModes
68
+ if (isAaveV3Type(lender)) {
69
+ const cf = isEMode(longData, shortData)
70
+ ? longData.config[longData.eMode?.category ?? 0]?.borrowCollateralFactor
71
+ : longData.config[LENDER_MODE_NO_MODE]?.borrowCollateralFactor;
72
+ return 1 / (1 - cf);
73
+ }
74
+ // for init, we match the modes and find the one with the highest collatearal factor
75
+ if (isInit(lender)) {
76
+ const modeIntersect = intersection(
77
+ Object.values(longData.config).map((c: ConfigEntry) => c.category),
78
+ Object.values(shortData.config).map((c: ConfigEntry) => c.category)
79
+ );
80
+
81
+ if (modeIntersect.length === 0) return 1;
82
+ const configId = Object.values(longData.config)
83
+ .filter((c: ConfigEntry) => modeIntersect.includes(c.category))
84
+ .sort((c: ConfigEntry, d: ConfigEntry) =>
85
+ c.borrowCollateralFactor < d.borrowCollateralFactor ? 1 : -1
86
+ )[0].category;
87
+
88
+ // we have to use the adjusted formula with the borrowFactor here
89
+ const cf = longData.config[configId]?.borrowCollateralFactor;
90
+ const bf = shortData.config[configId]?.borrowFactor;
91
+ return 1 / (1 - cf / bf);
92
+ }
93
+ // all other cases use the default mode no_mode without borrowFactor
94
+ const cf =
95
+ longData?.config?.[LENDER_MODE_NO_MODE]?.borrowCollateralFactor ?? 0.8;
96
+ return 1 / (1 - cf);
97
+ };
66
98
 
67
99
  /** create array of pairs for given lender data */
68
- export function generateLendingPairs(lenderData: LenderData, prices: { [k: string]: number }, histPrices: { [k: string]: number }): LendingPair[] {
69
- const pairs: LendingPair[] = [];
70
-
71
- for (const chainId in lenderData) {
72
- const chainEntry = lenderData[chainId];
73
- const lenderToData = (chainEntry.data ?? chainEntry)
74
-
75
- for (const lender in lenderToData) {
76
- const lenderEntry = lenderToData[lender];
77
-
78
- // protect agaist error data
79
- // @ts-ignore
80
- let pools: PoolData[] = Object.values(lenderEntry?.data ?? lenderEntry ?? {}).map(a => a?.data ? a.data : a);
81
-
82
- // sometimes error messages are included
83
- // @ts-ignore
84
- if (pools.includes("No lenders available") || pools.includes("Failed to fetch")) continue;
85
- const poolArray = pools.filter(p => !p?.isFrozen);
86
-
87
- // Loop through all ordered pairs of pools
88
- for (let i = 0; i < poolArray.length; i++) {
89
- for (let j = 0; j < poolArray.length; j++) {
90
- if (i === j) continue;
91
-
92
- const long = poolArray[i];
93
- const short = poolArray[j];
94
- if (!short.borrowingEnabled) continue;
95
-
96
- // Skip same asset pairs
97
- if (long.underlying === short.underlying) continue;
98
-
99
- const maxDepositLeverage = getMaxLeverage(lender, long, short)
100
- const borrowLeverage = (maxDepositLeverage - 1)
101
- const depositLeverage = borrowLeverage + 1
102
- let collateralRewards = 0
103
- let borrowRewards = 0
104
- const rewards: any = {}
105
- // get the rewards from the collateral
106
- for (const [rewardId, baseYields] of Object.entries(long.rewards ?? {})) {
107
- collateralRewards += baseYields.depositRate
108
- if (baseYields.depositRate > 0)
109
- rewards[rewardId] = baseYields.depositRate * depositLeverage
110
- }
111
- // get the rewards from debt
112
- for (const [rewardId, baseYields] of Object.entries(short.rewards ?? {})) {
113
- const borrowRate = baseYields.stableBorrowRate >= baseYields.variableBorrowRate
114
- ? baseYields.stableBorrowRate
115
- : baseYields.variableBorrowRate
116
- borrowRewards += borrowRate
117
- if (borrowRate != 0)
118
- rewards[rewardId] = (rewards[rewardId] ?? 0) + borrowRate * borrowLeverage
119
- }
120
- const depositApr = long.depositRate + long.stakingYield
121
- const borrowApr = short.variableBorrowRate + short.stakingYield
122
-
123
-
124
- const [assetGroupLong, keyLong] = getPriceKey(long.underlying, chainId)
125
- const [assetGroupShort, keyShort] = getPriceKey(short.underlying, chainId)
126
- const priceLong = prices[keyLong] ?? 0
127
- const priceShort = prices[keyShort] ?? 0
128
-
129
- const price24hLong = histPrices[keyLong] ?? priceLong
130
- const price24hShort = histPrices[keyShort] ?? priceShort
131
-
132
- const price = priceShort !== 0 ? priceLong / priceShort : 0
133
- const price24h = price24hShort !== 0 ? price24hLong / price24hShort : 0
134
-
135
-
136
- pairs.push({
137
- chainId,
138
- lender,
139
- assetLong: long.underlying,
140
- assetShort: short.underlying,
141
- price,
142
- price24h,
143
- assetGroupLong,
144
- assetGroupShort,
145
- priceChange: price24h === 0 ? 0 : (price - price24h) / price24h,
146
- maxLeverage: maxDepositLeverage,
147
- apr: (depositApr + collateralRewards) * depositLeverage + (borrowRewards - borrowApr) * borrowLeverage,
148
- eMode: long.eMode?.category ?? 0
149
- });
150
- }
151
- }
152
- }
153
- }
154
-
155
- return pairs;
100
+ export function generateLendingPairs(
101
+ lenderData: LenderData,
102
+ prices: { [k: string]: number },
103
+ histPrices: { [k: string]: number }
104
+ ): LendingPair[] {
105
+ const pairs: LendingPair[] = [];
106
+
107
+ for (const chainId in lenderData) {
108
+ const chainEntry = lenderData[chainId];
109
+ const lenderToData = chainEntry.data ?? chainEntry;
110
+
111
+ for (const lender in lenderToData) {
112
+ const lenderEntry = lenderToData[lender];
113
+
114
+ // protect agaist error data
115
+ let pools: any[] = Object.values(
116
+ lenderEntry?.data ?? lenderEntry ?? {}
117
+ ).map((a: any) => (a?.data ? a.data : a));
118
+
119
+ // sometimes error messages are included
120
+ const hasErrorMessages = pools.some(
121
+ (p: any) =>
122
+ typeof p === "string" &&
123
+ (p.includes("No lenders available") || p.includes("Failed to fetch"))
124
+ );
125
+
126
+ if (hasErrorMessages) continue;
127
+
128
+ // cast to poolData
129
+ const validPools: PoolData[] = pools.filter(
130
+ (p: any) => typeof p === "object" && p !== null
131
+ );
132
+ const poolArray = validPools.filter((p) => !p?.isFrozen);
133
+
134
+ // Loop through all ordered pairs of pools
135
+ for (let i = 0; i < poolArray.length; i++) {
136
+ for (let j = 0; j < poolArray.length; j++) {
137
+ if (i === j) continue;
138
+
139
+ const long = poolArray[i];
140
+ const short = poolArray[j];
141
+ if (!short.borrowingEnabled) continue;
142
+
143
+ // Skip same asset pairs
144
+ if (long.underlying === short.underlying) continue;
145
+
146
+ const maxDepositLeverage = getMaxLeverage(lender, long, short);
147
+ const borrowLeverage = maxDepositLeverage - 1;
148
+ const depositLeverage = borrowLeverage + 1;
149
+ let collateralRewards = 0;
150
+ let borrowRewards = 0;
151
+ const rewards: any = {};
152
+ // get the rewards from the collateral
153
+ for (const [rewardId, baseYields] of Object.entries(
154
+ long.rewards ?? {}
155
+ )) {
156
+ collateralRewards += baseYields.depositRate;
157
+ if (baseYields.depositRate > 0)
158
+ rewards[rewardId] = baseYields.depositRate * depositLeverage;
159
+ }
160
+ // get the rewards from debt
161
+ for (const [rewardId, baseYields] of Object.entries(
162
+ short.rewards ?? {}
163
+ )) {
164
+ const borrowRate =
165
+ baseYields.stableBorrowRate >= baseYields.variableBorrowRate
166
+ ? baseYields.stableBorrowRate
167
+ : baseYields.variableBorrowRate;
168
+ borrowRewards += borrowRate;
169
+ if (borrowRate != 0)
170
+ rewards[rewardId] =
171
+ (rewards[rewardId] ?? 0) + borrowRate * borrowLeverage;
172
+ }
173
+ const depositApr = long.depositRate + long.stakingYield;
174
+ const borrowApr = short.variableBorrowRate + short.stakingYield;
175
+
176
+ const [assetGroupLong, keyLong] = getPriceKey(
177
+ long.underlying,
178
+ chainId
179
+ );
180
+ const [assetGroupShort, keyShort] = getPriceKey(
181
+ short.underlying,
182
+ chainId
183
+ );
184
+ const priceLong = prices[keyLong] ?? 0;
185
+ const priceShort = prices[keyShort] ?? 0;
186
+
187
+ const price24hLong = histPrices[keyLong] ?? priceLong;
188
+ const price24hShort = histPrices[keyShort] ?? priceShort;
189
+
190
+ const price = priceShort !== 0 ? priceLong / priceShort : 0;
191
+ const price24h =
192
+ price24hShort !== 0 ? price24hLong / price24hShort : 0;
193
+
194
+ // only base apr
195
+ const baseApr =
196
+ depositApr * depositLeverage - borrowApr * borrowLeverage;
197
+
198
+ // only rewards apr
199
+ const rewardApr =
200
+ collateralRewards * depositLeverage +
201
+ borrowRewards * borrowLeverage;
202
+
203
+ // total apr
204
+ const totalApr = baseApr + rewardApr;
205
+
206
+ pairs.push({
207
+ chainId,
208
+ lender,
209
+ assetLong: long.underlying,
210
+ assetShort: short.underlying,
211
+ price,
212
+ price24h,
213
+ assetGroupLong,
214
+ assetGroupShort,
215
+ priceChange: price24h === 0 ? 0 : (price - price24h) / price24h,
216
+ maxLeverage: maxDepositLeverage,
217
+ apr: totalApr,
218
+ baseApr,
219
+ rewardApr,
220
+ eMode: long.eMode?.category ?? 0,
221
+ longTotalDepositsUSD: long.totalDepositsUSD,
222
+ longTotalDebtUSD: long.totalDebtUSD,
223
+ longTotalLiquidityUSD: long.totalLiquidityUSD,
224
+ shortTotalDepositsUSD: short.totalDepositsUSD,
225
+ shortTotalDebtUSD: short.totalDebtUSD,
226
+ shortTotalLiquidityUSD: short.totalLiquidityUSD,
227
+ longIsActive: long.isActive,
228
+ longIsFrozen: long.isFrozen,
229
+ shortIsActive: short.isActive,
230
+ shortIsFrozen: short.isFrozen,
231
+ shortBorrowingEnabled: short.borrowingEnabled,
232
+ });
233
+ }
234
+ }
235
+ }
236
+ }
237
+
238
+ return pairs;
156
239
  }
157
240
 
158
241
  function getPriceKey(asset: string, chainId: string): [string, string] {
159
- const assetGroup = getAssetMeta(chainId, asset)?.assetGroup
160
- return [assetGroup, toOracleKey(assetGroup) ?? toGenericPriceKey(asset, chainId)]
242
+ const assetGroup = getAssetMeta(chainId, asset)?.assetGroup;
243
+ return [
244
+ assetGroup,
245
+ toOracleKey(assetGroup) ?? toGenericPriceKey(asset, chainId),
246
+ ];
161
247
  }
162
248
 
163
- const MAX_ENTRIES = 10
249
+ const MAX_ENTRIES = 10;
250
+
251
+ function filterPairsByCategory(
252
+ pairs: LendingPair[],
253
+ longFilter: (group: string) => boolean,
254
+ shortFilter: (group: string) => boolean
255
+ ): LendingPair[] {
256
+ return pairs
257
+ .filter((p) => {
258
+ if (!longFilter(p.assetGroupLong) || !shortFilter(p.assetGroupShort)) {
259
+ return false;
260
+ }
261
+
262
+ if (
263
+ p.assetGroupShort?.includes("PT-") ||
264
+ p.assetGroupLong?.includes("PT-")
265
+ ) {
266
+ return false;
267
+ }
268
+
269
+ if (
270
+ !p.longIsActive ||
271
+ p.longIsFrozen ||
272
+ !p.shortIsActive ||
273
+ p.shortIsFrozen
274
+ ) {
275
+ return false;
276
+ }
277
+
278
+ if (!p.shortBorrowingEnabled) {
279
+ return false;
280
+ }
281
+
282
+ // apply liquidity thresholds
283
+ const longThresholds = getLiquidityThresholds(
284
+ p.chainId,
285
+ p.assetLong,
286
+ p.assetGroupLong
287
+ );
288
+ const shortThresholds = getLiquidityThresholds(
289
+ p.chainId,
290
+ p.assetShort,
291
+ p.assetGroupShort
292
+ );
293
+
294
+ const longMeetsThresholds = meetsLiquidityThresholds(
295
+ {
296
+ totalDepositsUSD: p.longTotalDepositsUSD,
297
+ totalDebtUSD: p.longTotalDebtUSD,
298
+ totalLiquidityUSD: p.longTotalLiquidityUSD,
299
+ },
300
+ longThresholds
301
+ );
302
+
303
+ const shortMeetsThresholds = meetsLiquidityThresholds(
304
+ {
305
+ totalDepositsUSD: p.shortTotalDepositsUSD,
306
+ totalDebtUSD: p.shortTotalDebtUSD,
307
+ totalLiquidityUSD: p.shortTotalLiquidityUSD,
308
+ },
309
+ shortThresholds
310
+ );
311
+
312
+ if (!longMeetsThresholds || !shortMeetsThresholds) {
313
+ return false;
314
+ }
315
+
316
+ return true;
317
+ })
318
+ .sort((a, b) => b.apr - a.apr)
319
+ .slice(0, MAX_ENTRIES);
320
+ }
164
321
 
165
322
  export function getTopPairs(pairs: LendingPair[]) {
166
- const stables = pairs.filter(p => p.assetGroupLong?.includes("USD") && p.assetGroupShort?.includes("USD") && !p.assetGroupShort?.includes("PT-")).sort((a, b) => a.apr > b.apr ? -1 : 1).slice(0, MAX_ENTRIES)
167
- const btcLst = pairs.filter(p => p.assetGroupLong?.includes("BTC") && p.assetGroupShort?.includes("BTC") && !p.assetGroupShort?.includes("PT-")).sort((a, b) => a.apr > b.apr ? -1 : 1).slice(0, MAX_ENTRIES)
168
- const ethLst = pairs.filter(p => p.assetGroupLong?.includes("ETH") && p.assetGroupShort?.includes("ETH") && !p.assetGroupShort?.includes("PT-")).sort((a, b) => a.apr > b.apr ? -1 : 1).slice(0, MAX_ENTRIES)
169
-
170
- return {
171
- stables,
172
- btcLst,
173
- ethLst,
174
- gainers: getDistinctPairsByShortAsset(pairs).slice(0, MAX_ENTRIES),
175
- }
323
+ const stables = filterPairsByCategory(
324
+ pairs,
325
+ (group) => group?.includes("USD") ?? false,
326
+ (group) => group?.includes("USD") ?? false
327
+ );
328
+
329
+ const btcLst = filterPairsByCategory(
330
+ pairs,
331
+ (group) => group?.includes("BTC") ?? false,
332
+ (group) => group?.includes("BTC") ?? false
333
+ );
334
+
335
+ const ethLst = filterPairsByCategory(
336
+ pairs,
337
+ (group) => group?.includes("ETH") ?? false,
338
+ (group) => group?.includes("ETH") ?? false
339
+ );
340
+
341
+ return {
342
+ stables,
343
+ btcLst,
344
+ ethLst,
345
+ gainers: getDistinctPairsByShortAsset(pairs).slice(0, MAX_ENTRIES),
346
+ };
176
347
  }
177
348
 
178
349
  function isStable(assetGroup: string): boolean {
179
- return assetGroup.includes("USD");
350
+ return assetGroup.includes("USD");
180
351
  }
181
352
 
182
353
  export function getDistinctPairsByShortAsset(
183
- pairs: LendingPair[],
354
+ pairs: LendingPair[]
184
355
  ): LendingPair[] {
185
- const sorted = [...pairs].sort((a, b) =>
186
- b.priceChange - a.priceChange
187
- );
188
-
189
- const blacklist = new Set<string>();
190
- const blacklistLong = new Set<string>();
191
- const result: LendingPair[] = [];
192
-
193
- for (const pair of sorted) {
194
- const shortGroup = pair.assetGroupShort;
195
- const longGroup = pair.assetGroupLong;
196
-
197
- if (blacklist.has(shortGroup)|| blacklistLong.has(longGroup)) continue;
198
-
199
- result.push(pair);
200
-
201
- // Only blacklist if not a stablecoin
202
- if (!isStable(shortGroup)) {
203
- blacklist.add(shortGroup);
204
- }
205
- if (!isStable(longGroup)) {
206
- blacklistLong.add(longGroup);
207
- }
208
- if (result.length >= 10) return result
209
- }
210
-
211
-
212
- return result;
213
- }
356
+ const qualityPairs = pairs.filter((p) => {
357
+ if (!isFinite(p.apr) || !isFinite(p.priceChange)) {
358
+ return false;
359
+ }
360
+
361
+ if (
362
+ !p.longIsActive ||
363
+ p.longIsFrozen ||
364
+ !p.shortIsActive ||
365
+ p.shortIsFrozen
366
+ ) {
367
+ return false;
368
+ }
369
+
370
+ if (!p.shortBorrowingEnabled) {
371
+ return false;
372
+ }
373
+
374
+ // Apply liquidity thresholds
375
+ const longThresholds = getLiquidityThresholds(
376
+ p.chainId,
377
+ p.assetLong,
378
+ p.assetGroupLong
379
+ );
380
+ const shortThresholds = getLiquidityThresholds(
381
+ p.chainId,
382
+ p.assetShort,
383
+ p.assetGroupShort
384
+ );
385
+
386
+ const longMeetsThresholds = meetsLiquidityThresholds(
387
+ {
388
+ totalDepositsUSD: p.longTotalDepositsUSD,
389
+ totalDebtUSD: p.longTotalDebtUSD,
390
+ totalLiquidityUSD: p.longTotalLiquidityUSD,
391
+ },
392
+ longThresholds
393
+ );
394
+
395
+ const shortMeetsThresholds = meetsLiquidityThresholds(
396
+ {
397
+ totalDepositsUSD: p.shortTotalDepositsUSD,
398
+ totalDebtUSD: p.shortTotalDebtUSD,
399
+ totalLiquidityUSD: p.shortTotalLiquidityUSD,
400
+ },
401
+ shortThresholds
402
+ );
403
+
404
+ if (!longMeetsThresholds || !shortMeetsThresholds) {
405
+ return false;
406
+ }
407
+
408
+ if (
409
+ p.assetGroupShort?.includes("PT-") ||
410
+ p.assetGroupLong?.includes("PT-")
411
+ ) {
412
+ return false;
413
+ }
414
+
415
+ return true;
416
+ });
417
+
418
+ const sorted = [...qualityPairs].sort(
419
+ (a, b) => b.priceChange - a.priceChange
420
+ );
421
+
422
+ const blacklist = new Set<string>();
423
+ const blacklistLong = new Set<string>();
424
+ const result: LendingPair[] = [];
425
+
426
+ for (const pair of sorted) {
427
+ const shortGroup = pair.assetGroupShort;
428
+ const longGroup = pair.assetGroupLong;
429
+
430
+ if (blacklist.has(shortGroup) || blacklistLong.has(longGroup)) continue;
431
+
432
+ result.push(pair);
433
+
434
+ // Only blacklist if not a stablecoin
435
+ if (!isStable(shortGroup)) {
436
+ blacklist.add(shortGroup);
437
+ }
438
+ if (!isStable(longGroup)) {
439
+ blacklistLong.add(longGroup);
440
+ }
441
+ if (result.length >= 10) return result;
442
+ }
443
+
444
+ return result;
445
+ }