@stake-dao/reader 0.4.50 → 0.4.52

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 (65) hide show
  1. package/dist/esm/abis/passiveVaultsMulticall.js +56 -0
  2. package/dist/esm/abis/passiveVaultsMulticall.js.map +1 -0
  3. package/dist/esm/index.js +2 -0
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/esm/lockers/utils/getBribesRewardsData.js +1 -0
  6. package/dist/esm/lockers/utils/getBribesRewardsData.js.map +1 -1
  7. package/dist/esm/prices.js +7 -7
  8. package/dist/esm/prices.js.map +1 -1
  9. package/dist/esm/sdt/fetch.js +40 -40
  10. package/dist/esm/sdt/fetch.js.map +1 -1
  11. package/dist/esm/strategies/pancakeswap/fetch/pancakeswapMath.js +0 -3
  12. package/dist/esm/strategies/pancakeswap/fetch/pancakeswapMath.js.map +1 -1
  13. package/dist/esm/strategies/passive/fetch.js +73 -0
  14. package/dist/esm/strategies/passive/fetch.js.map +1 -0
  15. package/dist/esm/strategies/passive/httpCalls.js +35 -0
  16. package/dist/esm/strategies/passive/httpCalls.js.map +1 -0
  17. package/dist/esm/tsconfig.build.tsbuildinfo +1 -1
  18. package/dist/esm/utils/etherscan.js +7 -0
  19. package/dist/esm/utils/etherscan.js.map +1 -0
  20. package/dist/esm/utils/getAverageYearlyRewards.js +38 -0
  21. package/dist/esm/utils/getAverageYearlyRewards.js.map +1 -0
  22. package/dist/esm/utils.js +7 -7
  23. package/dist/esm/utils.js.map +1 -1
  24. package/dist/esm/votemarket/curve/config.js +1 -0
  25. package/dist/esm/votemarket/curve/config.js.map +1 -1
  26. package/dist/esm/votemarket/curve/fetchUserVlCvxClaimable.js +10 -9
  27. package/dist/esm/votemarket/curve/fetchUserVlCvxClaimable.js.map +1 -1
  28. package/dist/esm/votemarket/fetchPendingRemoteCampaigns.js +1 -1
  29. package/dist/esm/votemarket/fetchPendingRemoteCampaigns.js.map +1 -1
  30. package/dist/types/abis/passiveVaultsMulticall.d.ts +33 -0
  31. package/dist/types/abis/passiveVaultsMulticall.d.ts.map +1 -0
  32. package/dist/types/index.d.ts +1 -0
  33. package/dist/types/index.d.ts.map +1 -1
  34. package/dist/types/lockers/utils/getBribesRewardsData.d.ts.map +1 -1
  35. package/dist/types/sdt/fetch.d.ts +4 -4
  36. package/dist/types/sdt/fetch.d.ts.map +1 -1
  37. package/dist/types/strategies/pancakeswap/fetch/pancakeswapMath.d.ts.map +1 -1
  38. package/dist/types/strategies/passive/fetch.d.ts +161 -0
  39. package/dist/types/strategies/passive/fetch.d.ts.map +1 -0
  40. package/dist/types/strategies/passive/httpCalls.d.ts +8 -0
  41. package/dist/types/strategies/passive/httpCalls.d.ts.map +1 -0
  42. package/dist/types/utils/etherscan.d.ts +14 -0
  43. package/dist/types/utils/etherscan.d.ts.map +1 -0
  44. package/dist/types/utils/getAverageYearlyRewards.d.ts +3 -0
  45. package/dist/types/utils/getAverageYearlyRewards.d.ts.map +1 -0
  46. package/dist/types/utils.d.ts +1 -1
  47. package/dist/types/utils.d.ts.map +1 -1
  48. package/dist/types/votemarket/curve/config.d.ts +1 -0
  49. package/dist/types/votemarket/curve/config.d.ts.map +1 -1
  50. package/dist/types/votemarket/curve/fetchUserVlCvxClaimable.d.ts.map +1 -1
  51. package/package.json +2 -2
  52. package/src/abis/passiveVaultsMulticall.ts +56 -0
  53. package/src/index.ts +3 -0
  54. package/src/lockers/utils/getBribesRewardsData.ts +1 -0
  55. package/src/prices.ts +7 -7
  56. package/src/sdt/fetch.ts +40 -51
  57. package/src/strategies/pancakeswap/fetch/pancakeswapMath.ts +0 -3
  58. package/src/strategies/passive/fetch.ts +97 -0
  59. package/src/strategies/passive/httpCalls.ts +42 -0
  60. package/src/utils/etherscan.ts +21 -0
  61. package/src/utils/getAverageYearlyRewards.ts +53 -0
  62. package/src/utils.ts +22 -18
  63. package/src/votemarket/curve/config.ts +1 -0
  64. package/src/votemarket/curve/fetchUserVlCvxClaimable.ts +15 -10
  65. package/src/votemarket/fetchPendingRemoteCampaigns.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"fetchUserVlCvxClaimable.d.ts","sourceRoot":"","sources":["../../../../src/votemarket/curve/fetchUserVlCvxClaimable.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAyB,MAAM,MAAM,CAAA;AAO1D,KAAK,IAAI,GAAG;IACV,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;CAC1B,CAAA;AAkGD,eAAO,MAAM,uBAAuB,aAAoB,GAAG,OAAO,IAAI,QAAQ,MAAM;;;;;;;mBAT1C,OAAO;;iBA6EhD,CAAA"}
1
+ {"version":3,"file":"fetchUserVlCvxClaimable.d.ts","sourceRoot":"","sources":["../../../../src/votemarket/curve/fetchUserVlCvxClaimable.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAyB,MAAM,MAAM,CAAA;AAO1D,KAAK,IAAI,GAAG;IACV,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;CAC1B,CAAA;AAuGD,eAAO,MAAM,uBAAuB,aAAoB,GAAG,OAAO,IAAI,QAAQ,MAAM;;;;;;;mBAT1C,OAAO;;iBA6EhD,CAAA"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stake-dao/reader",
3
3
  "description": "",
4
- "version": "0.4.50",
4
+ "version": "0.4.52",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
7
7
  "type": "git",
@@ -36,7 +36,7 @@
36
36
  "graphql-request": "^6.0.0",
37
37
  "lodash-es": "^4.17.21",
38
38
  "viem": "^2.9.31",
39
- "@stake-dao/constants": "0.4.22"
39
+ "@stake-dao/constants": "0.4.23"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/lodash-es": "^4.17.12"
@@ -0,0 +1,56 @@
1
+ const passiveVaultsMulticall = [
2
+ {
3
+ constant: true,
4
+ inputs: [],
5
+ name: 'totalSupply',
6
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
7
+ payable: false,
8
+ stateMutability: 'view',
9
+ type: 'function',
10
+ },
11
+ {
12
+ constant: true,
13
+ inputs: [],
14
+ name: 'getPricePerFullShare',
15
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
16
+ payable: false,
17
+ stateMutability: 'view',
18
+ type: 'function',
19
+ },
20
+ {
21
+ constant: true,
22
+ inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
23
+ name: 'balanceOf',
24
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
25
+ payable: false,
26
+ stateMutability: 'view',
27
+ type: 'function',
28
+ },
29
+ {
30
+ constant: true,
31
+ inputs: [],
32
+ name: 'withdrawalFee',
33
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
34
+ payable: false,
35
+ stateMutability: 'view',
36
+ type: 'function',
37
+ },
38
+ {
39
+ constant: true,
40
+ inputs: [],
41
+ name: 'performanceFee',
42
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
43
+ payable: false,
44
+ stateMutability: 'view',
45
+ type: 'function',
46
+ },
47
+ {
48
+ name: 'working_balances',
49
+ outputs: [{ type: 'uint256', name: '' }],
50
+ inputs: [{ type: 'address', name: 'arg0' }],
51
+ stateMutability: 'view',
52
+ type: 'function',
53
+ },
54
+ ]
55
+
56
+ export default passiveVaultsMulticall
package/src/index.ts CHANGED
@@ -48,6 +48,9 @@ export { buildPancakeSwapErc20Strategies } from './strategies/pancakeswap/buildE
48
48
  export { computePositionData } from './strategies/pancakeswap/fetch/pancakePositions.js'
49
49
  export { getPancakeV3Positions } from './strategies/pancakeswap/fetch/getPancakeV3Positions.js'
50
50
 
51
+ // Passive
52
+ export { fetchPassive } from './strategies/passive/fetch.js'
53
+
51
54
  // Pendle
52
55
  export { fetchPendle } from './strategies/pendle/fetch.js'
53
56
  export { buildPendleStrategies } from './strategies/pendle/build.js'
@@ -30,6 +30,7 @@ const getBribesRewardsData = (
30
30
  { id: 'cake', value: bribesData?.[delegationWithId('cake')!] / 100 || 0 },
31
31
  { id: 'pendle', value: bribesData?.[delegationWithId('pendle')!] / 100 || 0 },
32
32
  { id: 'fxn', value: bribesData?.[delegationWithId('fxn')!] / 100 || 0 },
33
+ { id: 'apw', value: bribesData?.[delegationWithId('apw')!] / 100 || 0 },
33
34
  ]
34
35
 
35
36
  // Vote incentives APR
package/src/prices.ts CHANGED
@@ -109,21 +109,21 @@ interface TokenToFetch {
109
109
  }
110
110
 
111
111
  export const getPrices = async (tokens: TokenToFetch[], chainId = 1): Promise<Price[]> => {
112
- let pricesFromLlama: Price[] = []
112
+ let pricesFromGecko: Price[] = []
113
113
  if (tokens.length > 0) {
114
- pricesFromLlama = pricesFromLlama.concat(await getPricesFromLlama(tokens, chainId))
114
+ pricesFromGecko = pricesFromGecko.concat(await getPricesFromGeckoTerminal(tokens, chainId))
115
115
  }
116
116
 
117
- const pricesToFetchFromGecko = tokens
117
+ const pricesToFetchFromLlama = tokens
118
118
  .map((t) => {
119
- const coinPrice = pricesFromLlama.find((pl) => equalTlc(pl.address, t.address))?.usdPrice || 0
119
+ const coinPrice = pricesFromGecko.find((pl) => equalTlc(pl.address, t.address))?.usdPrice || 0
120
120
  return coinPrice === 0 ? t : undefined
121
121
  })
122
122
  .filter(Boolean)
123
123
 
124
- let pricesFromGecko: Price[] = []
125
- if (pricesToFetchFromGecko.length > 0) {
126
- pricesFromGecko = pricesFromGecko.concat(await getPricesFromGeckoTerminal(pricesToFetchFromGecko, chainId))
124
+ let pricesFromLlama: Price[] = []
125
+ if (tokens.length > 0) {
126
+ pricesFromLlama = pricesFromLlama.concat(await getPricesFromLlama(pricesToFetchFromLlama, chainId))
127
127
  }
128
128
 
129
129
  return [pricesFromLlama, pricesFromGecko].flat().filter((p) => p.usdPrice > 0)
package/src/sdt/fetch.ts CHANGED
@@ -1,14 +1,15 @@
1
- import { decodeAbiParameters, formatUnits, parseAbi, parseUnits } from 'viem'
1
+ import { formatUnits, parseAbi, parseUnits } from 'viem'
2
2
 
3
- import { ONE_DAY, type Token, Zero, contracts, explorers, tokenWithId } from '@stake-dao/constants'
3
+ import { ONE_DAY, type Token, contract, tokenWithId } from '@stake-dao/constants'
4
+ import { mainnet } from 'viem/chains'
4
5
  import { type Price } from '../prices.js'
5
- import { multicall } from '../utils.js'
6
+ import { equalTlc, multicall } from '../utils.js'
7
+ import { getLogsFromEtherscan } from '../utils/etherscan.js'
8
+ import getAverageYearlyRewards from '../utils/getAverageYearlyRewards.js'
6
9
  import veSdtAlternativeYields from './veSdtAlternativeYields.js'
7
10
 
8
- const CHECKPOINT_EVENTS_ENDPOINT = (fromBlock: number, apiKey: string) =>
9
- `https://api.${
10
- explorers[1].url
11
- }/api?module=logs&action=getLogs&fromBlock=${fromBlock}&address=${contracts.veSdtFeeDistributor![1]!}&topic0=0xce749457b74e10f393f2c6b1ce4261b78791376db5a3f501477a809f03f500d6&apikey=${apiKey}`
11
+ const NB_EVENTS_TO_USE = 5
12
+ const EVENT_TOPIC = '0xce749457b74e10f393f2c6b1ce4261b78791376db5a3f501477a809f03f500d6'
12
13
 
13
14
  interface TFetchSdt {
14
15
  provider: any
@@ -22,26 +23,41 @@ export const fetchSdt = async ({ provider, prices, explorerApiKey }: TFetchSdt)
22
23
  const frax3CrvToken = tokenWithId('frax3crv') as Token
23
24
 
24
25
  const now = Math.ceil(new Date().getTime() / 1000)
25
- const lastCheck = now - ONE_DAY * 28
26
+ const lastCheck = now - ONE_DAY * 60
26
27
  const lastCheckBlock = await (await fetch(`https://coins.llama.fi/block/ethereum/${lastCheck}`)).json()
27
28
 
28
- const checkpoints = await (await fetch(CHECKPOINT_EVENTS_ENDPOINT(lastCheckBlock.height, explorerApiKey))).json()
29
+ let amountPerYear = 0
30
+
31
+ try {
32
+ const rawEventsData = await getLogsFromEtherscan({
33
+ chainId: mainnet.id,
34
+ address: contract('veSdtFeeDistributor'),
35
+ apikey: explorerApiKey,
36
+ extraQueryParams: {
37
+ fromBlock: `${lastCheckBlock.height}`,
38
+ topic0: EVENT_TOPIC,
39
+ },
40
+ })
41
+ amountPerYear = await getAverageYearlyRewards(rawEventsData, NB_EVENTS_TO_USE)
42
+ } catch {
43
+ amountPerYear = 0
44
+ }
29
45
 
30
46
  const rawSdtData: any = await multicall(
31
47
  provider,
32
48
  [
33
- { address: contracts.sdFrx3CrvVault![1]!, name: 'getPricePerFullShare' },
34
- { address: sdtToken.address, name: 'balanceOf', params: [contracts.veSdt![1]!] },
35
- { address: sdtToken.address, name: 'balanceOf', params: [contracts.vestedSdt![1]!] },
49
+ { address: contract('sdFrx3CrvVault'), name: 'getPricePerFullShare' },
50
+ { address: sdtToken.address, name: 'balanceOf', params: [contract('veSdt')] },
51
+ { address: sdtToken.address, name: 'balanceOf', params: [contract('vestedSdt')] },
36
52
  { address: sdtToken.address, name: 'totalSupply' },
37
- { address: contracts.veSdt![1]!, name: 'totalSupply' },
53
+ { address: contract('veSdt'), name: 'totalSupply' },
38
54
  {
39
- address: contracts.veSdtBoostDelegation![1]!,
55
+ address: contract('veSdtBoostDelegation'),
40
56
  name: 'received_balance',
41
- params: [contracts.voteBoosterStrategy![1]!],
57
+ params: [contract('voteBoosterStrategy')],
42
58
  },
43
- { address: contracts.liquidLockersGaugeController![1]!, name: 'get_total_weight' },
44
- { address: contracts.sdGaugeController![1]!, name: 'get_total_weight' },
59
+ { address: contract('liquidLockersGaugeController'), name: 'get_total_weight' },
60
+ { address: contract('sdGaugeController'), name: 'get_total_weight' },
45
61
  ],
46
62
  parseAbi([
47
63
  'function getPricePerFullShare() external view returns (uint256)',
@@ -59,55 +75,28 @@ export const fetchSdt = async ({ provider, prices, explorerApiKey }: TFetchSdt)
59
75
  const veSdtTotalSupply = rawSdtData[4].result
60
76
  const voteBoosterVeSdt = rawSdtData[5].result
61
77
 
62
- const sdtPrice = prices?.find((p) => p.address.toLowerCase() === sdtToken.address.toLowerCase())?.usdPrice || 0
63
- const frax3CrvPrice =
64
- prices?.find((p) => p.address.toLowerCase() === frax3CrvToken.address.toLowerCase())?.usdPrice || 0
78
+ const sdtPrice = prices.find((p) => equalTlc(p.address, sdtToken.address))?.usdPrice || 0
79
+ const frax3CrvPrice = prices.find((p) => equalTlc(p.address, frax3CrvToken.address))?.usdPrice || 0
65
80
 
66
81
  const lockedInUsd = parseUnits(`${sdtPrice}`, sdtToken.decimals) * lockedSdtBalance
67
82
  const vestedInUsd = parseUnits(`${sdtPrice}`, sdtToken.decimals) * vestedSdtBalance
68
83
  const percentLocked = parseUnits(formatUnits(lockedSdtBalance, 0), sdtToken.decimals) / sdtTotalSupply
69
84
  const averageLock = (parseUnits(formatUnits(veSdtTotalSupply, 0), sdtToken.decimals) / lockedSdtBalance) * BigInt(4)
70
85
 
71
- let rewardsAmount = Zero
72
- let checkedTimestamp = now - ONE_DAY * 7
73
-
74
- let apr = 0
75
- if (checkpoints && checkpoints.message === 'OK') {
76
- const checkpointsData = checkpoints.result.map((r) => {
77
- const [eventTimestamp, eventAmount] = decodeAbiParameters(
78
- [
79
- { type: 'uint256', name: 'timestamp' },
80
- { type: 'uint256', name: 'amount' },
81
- ],
82
- r.data,
83
- )
84
-
85
- return { ts: Number(eventTimestamp), amount: eventAmount }
86
- })
87
-
88
- while (lastCheck <= checkedTimestamp && rewardsAmount === Zero) {
89
- const checkedWeek = checkpointsData ? checkpointsData.filter((d: any) => checkedTimestamp < d.ts) : null
90
- rewardsAmount = checkedWeek ? checkedWeek.reduce((total, d) => total + d.amount, Zero) : Zero
91
- checkedTimestamp = checkedTimestamp - ONE_DAY * 7
92
- }
93
-
94
- apr =
95
- ((frax3CrvPrice * Number(formatUnits(rewardsAmount, 18)) * 52) / Number(formatUnits(lockedInUsd, 18))) *
96
- Number(vaultRatio)
97
- }
86
+ const apr = ((frax3CrvPrice * amountPerYear) / Number(formatUnits(lockedInUsd, 18))) * Number(vaultRatio)
98
87
 
99
88
  const parsedData = {
100
89
  alternativeYields: veSdtAlternativeYields,
101
- veSdtTotalSupply: formatUnits(veSdtTotalSupply, 0),
102
- locked: formatUnits(lockedSdtBalance, 0),
90
+ veSdtTotalSupply: Number(formatUnits(veSdtTotalSupply, 18)),
91
+ locked: Number(formatUnits(lockedSdtBalance, sdtToken.decimals)),
103
92
  lockedInUsd: Number(formatUnits(lockedInUsd, sdtToken.decimals * 2)),
104
93
  vestedInUsd: Number(formatUnits(vestedInUsd, sdtToken.decimals * 2)),
105
94
  percentLocked: Number(formatUnits(percentLocked, 18)) * 100,
106
95
  averageLockInYears: Number(formatUnits(averageLock, 18)),
107
96
  apr: apr * 100,
108
97
  voteBoosterVeSdt: Number(formatUnits(voteBoosterVeSdt, sdtToken.decimals)),
109
- lockersTotalWeight: formatUnits(rawSdtData[6].result, 0),
110
- stratsTotalWeight: formatUnits(rawSdtData[7].result, 0),
98
+ lockersTotalWeight: Number(formatUnits(rawSdtData[6].result, sdtToken.decimals * 2)),
99
+ stratsTotalWeight: Number(formatUnits(rawSdtData[7].result, sdtToken.decimals * 2)),
111
100
  }
112
101
 
113
102
  return parsedData
@@ -215,7 +215,6 @@ export function mostSignificantBit(x: bigint): number {
215
215
  let msb = 0
216
216
  for (const [power, min] of POWERS_OF_2) {
217
217
  if (x >= min) {
218
- // eslint-disable-next-line operator-assignment
219
218
  x = x >> BigInt(power)
220
219
  msb += power
221
220
  }
@@ -240,9 +239,7 @@ function getTickAtSqrtRatio(sqrtRatioX96: bigint): number {
240
239
  for (let i = 0; i < 14; i++) {
241
240
  r = (r * r) >> BigInt(127)
242
241
  const f = r >> BigInt(128)
243
- // eslint-disable-next-line operator-assignment
244
242
  log_2 = log_2 | (f << BigInt(63 - i))
245
- // eslint-disable-next-line operator-assignment
246
243
  r = r >> f
247
244
  }
248
245
 
@@ -0,0 +1,97 @@
1
+ import { formatUnits } from 'viem'
2
+
3
+ import multicallPassiveVaultsAbi from '../../abis/passiveVaultsMulticall.js'
4
+ import { contract, passiveStrats } from '@stake-dao/constants'
5
+ import { multicall } from '../../utils.js'
6
+ import { mainnet } from 'viem/chains'
7
+ import { getPassiveHttpCalls } from './httpCalls.js'
8
+ import { getBoost } from '../utils/boost.js'
9
+
10
+ interface TFetchPassive {
11
+ provider: any
12
+ chainId: number
13
+ }
14
+
15
+ const vaultCalls = (vaultAddress: string, strategyAddress: string, gaugeAddress: string, chainId: number) =>
16
+ [
17
+ { address: vaultAddress, name: 'totalSupply' },
18
+ { address: vaultAddress, name: 'getPricePerFullShare' },
19
+ { address: strategyAddress, name: 'performanceFee' },
20
+ { address: strategyAddress, name: 'withdrawalFee' },
21
+ chainId === mainnet.id && { address: gaugeAddress, name: 'balanceOf', params: [contract('convexCrvLocker')] },
22
+ chainId === mainnet.id && {
23
+ address: gaugeAddress,
24
+ name: 'working_balances',
25
+ params: [contract('convexCrvLocker')],
26
+ },
27
+ ].filter((c) => c && typeof c !== 'undefined')
28
+
29
+ export const fetchPassive = async ({ provider, chainId }: TFetchPassive) => {
30
+ const passive = passiveStrats.strats.filter((s) => s.chainId === chainId)
31
+ const apisData = await getPassiveHttpCalls(chainId)
32
+
33
+ const rawData: any = await multicall(
34
+ provider,
35
+ passive.flatMap((s) => vaultCalls(s.vault.address, s.vault.strategy, s.gaugeAddress, chainId)),
36
+ multicallPassiveVaultsAbi,
37
+ )
38
+
39
+ let currentRawIndex = 0
40
+ const parsedStratsData = passive.map((s) => {
41
+ const curveApiRaw = apisData.curveApiData.map((apiResult) =>
42
+ apiResult.value.find((pool) => pool.lpTokenAddress.toLowerCase() === s.lpToken.address.toLowerCase()),
43
+ )
44
+ const indexOfData = curveApiRaw.findIndex((e) => e)
45
+ const curveApiData = curveApiRaw[indexOfData]
46
+
47
+ const subgraphData = apisData.curveSubgraphData.find((el) => el.address === curveApiData.address)
48
+
49
+ const lpPriceInUsd = curveApiData.usdTotal / Number(formatUnits(BigInt(curveApiData.totalSupply), 18))
50
+
51
+ const totalSupply = Number(formatUnits(rawData[currentRawIndex].result, s.lpToken.decimals))
52
+ const conversionRate = Number(formatUnits(rawData[currentRawIndex + 1].result, s.lpToken.decimals))
53
+ const totalSupplyInLpToken = totalSupply * conversionRate
54
+ const tvl = totalSupplyInLpToken * lpPriceInUsd
55
+ const perfFee = rawData[currentRawIndex + 2].result
56
+ const withdrawalFee = rawData[currentRawIndex + 3].result
57
+ const boost =
58
+ chainId === 1 ? getBoost('curve', rawData[currentRawIndex + 4].result, rawData[currentRawIndex + 5].result) : 1
59
+ const aprWithBoost = curveApiData.gaugeCrvApy?.[0] && boost ? curveApiData.gaugeCrvApy[0] * boost : 0
60
+
61
+ currentRawIndex += vaultCalls('', '', '', chainId).length
62
+
63
+ const apy = subgraphData.latestWeeklyApy ? Number(subgraphData.latestWeeklyApy) + aprWithBoost : 0
64
+
65
+ return {
66
+ ...s,
67
+ tradingApy: subgraphData.latestWeeklyApy,
68
+ minApy: curveApiData.gaugeCrvApy ? curveApiData.gaugeCrvApy[0] : 0,
69
+ maxApy: curveApiData.gaugeCrvApy ? curveApiData.gaugeCrvApy[1] : 0,
70
+ lpPriceInUsd,
71
+ tvl,
72
+ apr: {
73
+ boost,
74
+ current: {
75
+ total: apy,
76
+ details: [{ label: 'Trading Fees + Rewards (APY)', value: [apy] }],
77
+ },
78
+ },
79
+ stratsData: {
80
+ boost,
81
+ accumulatedFees: '0',
82
+ keeperFee: '0',
83
+ veSDTFee: '0',
84
+ accumulatorFee: '0',
85
+ claimerFee: '0',
86
+ perfFee: formatUnits(perfFee, 0),
87
+ withdrawalFee: formatUnits(withdrawalFee, 0),
88
+ },
89
+ }
90
+ })
91
+
92
+ return {
93
+ deployed: parsedStratsData,
94
+ notDeployed: [],
95
+ fetched: true,
96
+ }
97
+ }
@@ -0,0 +1,42 @@
1
+ import { CURVE_API_URL } from '../curve/endpoints.js'
2
+
3
+ export const getPassiveHttpCalls = async (chainId: number) => {
4
+ switch (chainId) {
5
+ case 1: {
6
+ const ethHttpCalls = [
7
+ // Curve Apis
8
+ (await fetch(`${CURVE_API_URL}/getPools/ethereum/main`)).json(),
9
+ (await fetch(`${CURVE_API_URL}/getSubgraphData/ethereum`)).json(),
10
+ ]
11
+
12
+ const ethHttpResponses = await Promise.all(ethHttpCalls)
13
+
14
+ const ethCurveMain = ethHttpResponses[0].data.poolData
15
+ const ethCurveSubgraph = ethHttpResponses[1].data.poolList
16
+
17
+ return {
18
+ curveSubgraphData: ethCurveSubgraph,
19
+ curveApiData: [{ type: 'main', value: ethCurveMain }],
20
+ }
21
+ }
22
+ case 137: {
23
+ const polygonHttpCalls = [
24
+ // Curve Apis
25
+ (await fetch(`${CURVE_API_URL}/getPools/polygon/main`)).json(),
26
+ (await fetch(`${CURVE_API_URL}/getSubgraphData/polygon`)).json(),
27
+ ]
28
+
29
+ const polygonHttpResponses = await Promise.all(polygonHttpCalls)
30
+
31
+ const polygonCurveMain = polygonHttpResponses[0].data.poolData
32
+ const polygonCurveSubgraph = polygonHttpResponses[1].data.poolList
33
+
34
+ return {
35
+ curveSubgraphData: polygonCurveSubgraph,
36
+ curveApiData: [{ type: 'main', value: polygonCurveMain }],
37
+ }
38
+ }
39
+ }
40
+
41
+ return { curveSubgraphData: undefined, curveApiData: [] }
42
+ }
@@ -0,0 +1,21 @@
1
+ interface GetLogsFromEtherscanArgs {
2
+ chainId: number
3
+ address: string
4
+ apikey: string
5
+ rawQueryParams?: { [key: string]: string }
6
+ extraQueryParams?: { [key: string]: string }
7
+ }
8
+
9
+ export const getLogsFromEtherscan = async ({
10
+ chainId,
11
+ address,
12
+ apikey,
13
+ rawQueryParams,
14
+ extraQueryParams = {},
15
+ }: GetLogsFromEtherscanArgs) => {
16
+ const queryParams = rawQueryParams
17
+ ? rawQueryParams
18
+ : { chainid: `${chainId}`, module: 'logs', action: 'getLogs', address, apikey, ...extraQueryParams }
19
+
20
+ return fetch(`https://api.etherscan.io/v2/api?${new URLSearchParams(queryParams)}`).then((res) => res.json())
21
+ }
@@ -0,0 +1,53 @@
1
+ import { ONE_YEAR, Zero } from '@stake-dao/constants'
2
+ import { decodeAbiParameters, formatUnits } from 'viem'
3
+
4
+ const decodeEvent = (rawEventsData: any) => {
5
+ return rawEventsData.result?.map((event) => {
6
+ const [eventTimestamp, eventAmount] = decodeAbiParameters(
7
+ [
8
+ { type: 'uint256', name: 'timestamp' },
9
+ { type: 'uint256', name: 'amount' },
10
+ ],
11
+ event.data,
12
+ )
13
+
14
+ return { eventTimestamp: Number(eventTimestamp), eventAmount }
15
+ })
16
+ }
17
+
18
+ const decodeEventWithRewardToken = (rawEventsData: any) => {
19
+ return rawEventsData.result?.map((event) => {
20
+ const [eventTimestamp, eventRewardToken, eventAmount] = decodeAbiParameters(
21
+ [
22
+ { type: 'uint256', name: 'timestamp' },
23
+ { type: 'address', name: 'rewardToken' },
24
+ { type: 'uint256', name: 'amount' },
25
+ ],
26
+ event.data,
27
+ )
28
+
29
+ return { eventTimestamp: Number(eventTimestamp), eventAmount, eventRewardToken }
30
+ })
31
+ }
32
+
33
+ const getAverageYearlyRewards = async (rawEventsData: any, nbEvents: number, withRewardToken?: boolean) => {
34
+ const eventsData = (withRewardToken ? decodeEventWithRewardToken(rawEventsData) : decodeEvent(rawEventsData))
35
+ .filter((event) => event.eventAmount > Zero)
36
+ .sort((a, b) => b.eventTimestamp - a.eventTimestamp)
37
+
38
+ const amountPerYear =
39
+ eventsData
40
+ .slice(0, nbEvents)
41
+ .map((_, index) => {
42
+ const duration = eventsData[index].eventTimestamp - eventsData[index + 1].eventTimestamp
43
+ const amount = Number(formatUnits(eventsData[index].eventAmount, 18))
44
+
45
+ // Annualized Reward
46
+ return (amount / duration) * ONE_YEAR
47
+ })
48
+ .reduce((total, x) => (total += x), 0) / nbEvents
49
+
50
+ return amountPerYear
51
+ }
52
+
53
+ export default getAverageYearlyRewards
package/src/utils.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { RPC, tokens } from '@stake-dao/constants'
2
2
  import type { Token } from '@stake-dao/constants'
3
3
  import { request } from 'graphql-request'
4
- import { groupBy } from 'lodash-es'
4
+ import { chunk, groupBy } from 'lodash-es'
5
5
  import {
6
6
  http,
7
7
  type Address,
@@ -121,23 +121,27 @@ export const rpcCall = async (rpc: string, params: RpcCallArgs[]) => {
121
121
  return request
122
122
  }
123
123
 
124
- export const ethGetTxsReceipts = async (rpc: string, txHashes: string[]) => {
125
- const request = (
126
- await fetch(rpc, {
127
- method: 'POST',
128
- headers: { 'Content-Type': 'application/json' },
129
- body: JSON.stringify(
130
- txHashes.map((txHash, index) => ({
131
- id: index,
132
- jsonrpc: '2.0',
133
- method: 'eth_getTransactionReceipt',
134
- params: [txHash],
135
- })),
136
- ),
137
- })
138
- ).json()
124
+ export const ethGetTxsReceipts = async (rpc: string, txHashes: string[], maxSize: number = 100) => {
125
+ const chunkedTxHashes = chunk(txHashes, maxSize)
139
126
 
140
- return request
127
+ const receiptsRequests = await Promise.all(
128
+ chunkedTxHashes.map((txs) =>
129
+ fetch(rpc, {
130
+ method: 'POST',
131
+ headers: { 'Content-Type': 'application/json' },
132
+ body: JSON.stringify(
133
+ txs.map((txHash, index) => ({
134
+ id: index,
135
+ jsonrpc: '2.0',
136
+ method: 'eth_getTransactionReceipt',
137
+ params: [txHash],
138
+ })),
139
+ ),
140
+ }).then((res) => res.json()),
141
+ ),
142
+ )
143
+
144
+ return receiptsRequests.flat()
141
145
  }
142
146
 
143
147
  export const ethEstimateGas = async (rpc: string, from: string, to: string, data: string) => {
@@ -289,7 +293,7 @@ export const multichainMulticall = async (calls: MultiChainCall[], abi: readonly
289
293
  const callsPerChain = groupBy(calls, (call) => call.chainId)
290
294
 
291
295
  const chains = Object.keys(callsPerChain)
292
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
296
+
293
297
  const parsedCallsPerChain = chains.map((chainId) => callsPerChain[chainId]!.map(({ chainId, ...call }) => call))
294
298
 
295
299
  const promises = await Promise.allSettled(
@@ -40,6 +40,7 @@ export const NOT_LP_GAUGES = {
40
40
  427: '0xC91113B4Dd89dd20FDEECDAC82477Bc99A840355',
41
41
  428: '0x7ce8aF75A9180B602445bE230860DDcb4cAc3E42',
42
42
  473: '0x66EFd8E255B8B7Cf32961E90A5820f289402629e',
43
+ 670: '0x5A537a46D780B1C70138aB98eDce69e7a53177ba',
43
44
  }
44
45
 
45
46
  export const NOT_LP_GAUGES_INDEX = Object.keys(NOT_LP_GAUGES).map((x) => Number(x))
@@ -19,15 +19,20 @@ type UnknownToken = {
19
19
  }
20
20
 
21
21
  const getMerkleData = async (epoch: number) => {
22
- const response = await fetch(
23
- `${GH_STAKE_DAO_BOUNTIES_REPORT}/refs/heads/main/bounties-reports/${epoch}/vlCVX/merkle_data.json`,
24
- )
25
- if (!response.ok) throw new Error('Failed to fetch merkle data')
26
- return response.json()
22
+ const [delegators, nonDelegators] = await Promise.all([
23
+ fetch(
24
+ `${GH_STAKE_DAO_BOUNTIES_REPORT}/refs/heads/main/bounties-reports/${epoch}/vlCVX/merkle_data_delegators.json`,
25
+ ).then((res) => res.json()),
26
+ fetch(
27
+ `${GH_STAKE_DAO_BOUNTIES_REPORT}/refs/heads/main/bounties-reports/${epoch}/vlCVX/merkle_data_non_delegators.json`,
28
+ ).then((res) => res.json()),
29
+ ])
30
+
31
+ return { delegators, nonDelegators }
27
32
  }
28
33
 
29
34
  const isRootMatch = (contractRoot: string, fileRoot: string) =>
30
- (contractRoot === ZERO_ROOT && fileRoot === ZERO_ROOT) || contractRoot.toLowerCase() === fileRoot.toLowerCase()
35
+ (contractRoot === ZERO_ROOT && fileRoot === ZERO_ROOT) || equalTlc(contractRoot, fileRoot)
31
36
 
32
37
  const validateMerkleRoots = (delegatorRoot: string, nonDelegatorRoot: string, merkleData: any) => {
33
38
  const delegatorMatches = isRootMatch(delegatorRoot, merkleData.delegators.merkleRoot)
@@ -107,8 +112,8 @@ const getParsedData = (
107
112
  const ZERO_ROOT = '0x0000000000000000000000000000000000000000000000000000000000000000'
108
113
 
109
114
  export const fetchUserVlCvxClaimable = async (provider: any, rpc: Rpcs, user: string) => {
110
- const nowEpoch = Math.floor(Date.now() / (1000 * 2 * ONE_WEEK)) * 2 * ONE_WEEK
111
- const prevEpoch = nowEpoch - 2 * ONE_WEEK
115
+ const nowEpoch = Math.floor(Date.now() / 1000 / ONE_WEEK) * ONE_WEEK
116
+ const prevEpoch = nowEpoch - ONE_WEEK
112
117
 
113
118
  const { delegatorRoot, nonDelegatorRoot } = await getMerkleRoots(provider)
114
119
 
@@ -140,8 +145,8 @@ export const fetchUserVlCvxClaimable = async (provider: any, rpc: Rpcs, user: st
140
145
  if (!tokens.length) return []
141
146
 
142
147
  const [delegatedClaimed, notDelegatedClaimed] = await Promise.all([
143
- getClaimedData(provider, tokensDelegated, user, contract('vlCvxDelegatorMerkle', mainnet.id)),
144
- getClaimedData(provider, tokensNotDelegated, user, contract('vlCvxNotDelegatorMerkle', mainnet.id)),
148
+ getClaimedData(provider, tokensDelegated, user, contract('vlCvxDelegatorMerkle')),
149
+ getClaimedData(provider, tokensNotDelegated, user, contract('vlCvxNotDelegatorMerkle')),
145
150
  ])
146
151
 
147
152
  const unknownTokens = tokens.filter((t) => !tokenWithAddress(t))
@@ -54,7 +54,7 @@ export const fetchPendingRemoteCampaigns = async ({
54
54
  const payload = decodeAbiParameters(
55
55
  parseAbiParameters([
56
56
  'Payload payload',
57
- 'struct Payload { uint256 actionType; address sender; bytes parameters }',
57
+ 'struct Payload { uint256 actionType; address sender; address votemarket; bytes parameters }',
58
58
  ]),
59
59
  decodedEvent.args.message.payload,
60
60
  )