@stake-dao/reader 0.4.31 → 0.4.33

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 (73) hide show
  1. package/dist/esm/bytecodes/index.js +1 -0
  2. package/dist/esm/bytecodes/index.js.map +1 -1
  3. package/dist/esm/bytecodes/votemarket/getNeededCampaignUpdate.js +17 -0
  4. package/dist/esm/bytecodes/votemarket/getNeededCampaignUpdate.js.map +1 -0
  5. package/dist/esm/endpoints.js +1 -0
  6. package/dist/esm/endpoints.js.map +1 -1
  7. package/dist/esm/index.js +1 -0
  8. package/dist/esm/index.js.map +1 -1
  9. package/dist/esm/lockers/fetch/index.js +5 -2
  10. package/dist/esm/lockers/fetch/index.js.map +1 -1
  11. package/dist/esm/lockers/fetch/parseBoost.js +13 -0
  12. package/dist/esm/lockers/fetch/parseBoost.js.map +1 -0
  13. package/dist/esm/lockers/utils/callsForLockers/index.js +3 -1
  14. package/dist/esm/lockers/utils/callsForLockers/index.js.map +1 -1
  15. package/dist/esm/lockers/utils/callsForLockers/veBoosts.js +9 -0
  16. package/dist/esm/lockers/utils/callsForLockers/veBoosts.js.map +1 -0
  17. package/dist/esm/strategies/pancakeswap/fetch/getPancakeV3Vaults.js +1 -1
  18. package/dist/esm/strategies/pancakeswap/fetch/getPancakeV3Vaults.js.map +1 -1
  19. package/dist/esm/strategies/pancakeswap/fetch/pancakePositions.js +2 -2
  20. package/dist/esm/strategies/pancakeswap/fetch/pancakePositions.js.map +1 -1
  21. package/dist/esm/strategies/pancakeswap/fetch/pancakeswapMath.js +1 -1
  22. package/dist/esm/strategies/pancakeswap/fetch/pancakeswapMath.js.map +1 -1
  23. package/dist/esm/tsconfig.build.tsbuildinfo +1 -1
  24. package/dist/esm/votemarket/curve/fetchCurveGauges.js +4 -1
  25. package/dist/esm/votemarket/curve/fetchCurveGauges.js.map +1 -1
  26. package/dist/esm/votemarket/curve/fetchSnapshotUserData.js +34 -1
  27. package/dist/esm/votemarket/curve/fetchSnapshotUserData.js.map +1 -1
  28. package/dist/esm/votemarket/curve/fetchUserVlCvxClaimable.js +37 -5
  29. package/dist/esm/votemarket/curve/fetchUserVlCvxClaimable.js.map +1 -1
  30. package/dist/esm/votemarket/fetchCampaigns.js +4 -4
  31. package/dist/esm/votemarket/fetchCampaigns.js.map +1 -1
  32. package/dist/esm/votemarket/fetchNeededCampaignUpdate.js +17 -0
  33. package/dist/esm/votemarket/fetchNeededCampaignUpdate.js.map +1 -0
  34. package/dist/types/bytecodes/index.d.ts +1 -0
  35. package/dist/types/bytecodes/index.d.ts.map +1 -1
  36. package/dist/types/bytecodes/votemarket/getNeededCampaignUpdate.d.ts +4 -0
  37. package/dist/types/bytecodes/votemarket/getNeededCampaignUpdate.d.ts.map +1 -0
  38. package/dist/types/endpoints.d.ts +1 -0
  39. package/dist/types/endpoints.d.ts.map +1 -1
  40. package/dist/types/index.d.ts +1 -0
  41. package/dist/types/index.d.ts.map +1 -1
  42. package/dist/types/lockers/fetch/index.d.ts +1 -0
  43. package/dist/types/lockers/fetch/index.d.ts.map +1 -1
  44. package/dist/types/lockers/fetch/parseBoost.d.ts +3 -0
  45. package/dist/types/lockers/fetch/parseBoost.d.ts.map +1 -0
  46. package/dist/types/lockers/utils/callsForLockers/index.d.ts +5 -1
  47. package/dist/types/lockers/utils/callsForLockers/index.d.ts.map +1 -1
  48. package/dist/types/lockers/utils/callsForLockers/veBoosts.d.ts +7 -0
  49. package/dist/types/lockers/utils/callsForLockers/veBoosts.d.ts.map +1 -0
  50. package/dist/types/strategies/pancakeswap/fetch/pancakeswapMath.d.ts.map +1 -1
  51. package/dist/types/votemarket/curve/fetchCurveGauges.d.ts.map +1 -1
  52. package/dist/types/votemarket/curve/fetchSnapshotUserData.d.ts +6 -0
  53. package/dist/types/votemarket/curve/fetchSnapshotUserData.d.ts.map +1 -1
  54. package/dist/types/votemarket/curve/fetchUserVlCvxClaimable.d.ts.map +1 -1
  55. package/dist/types/votemarket/fetchNeededCampaignUpdate.d.ts +2 -0
  56. package/dist/types/votemarket/fetchNeededCampaignUpdate.d.ts.map +1 -0
  57. package/package.json +2 -2
  58. package/src/bytecodes/index.ts +1 -0
  59. package/src/bytecodes/votemarket/getNeededCampaignUpdate.ts +19 -0
  60. package/src/endpoints.ts +2 -0
  61. package/src/index.ts +1 -0
  62. package/src/lockers/fetch/index.ts +6 -0
  63. package/src/lockers/fetch/parseBoost.ts +13 -0
  64. package/src/lockers/utils/callsForLockers/index.ts +6 -0
  65. package/src/lockers/utils/callsForLockers/veBoosts.ts +15 -0
  66. package/src/strategies/pancakeswap/fetch/getPancakeV3Vaults.ts +1 -1
  67. package/src/strategies/pancakeswap/fetch/pancakePositions.ts +2 -2
  68. package/src/strategies/pancakeswap/fetch/pancakeswapMath.ts +1 -1
  69. package/src/votemarket/curve/fetchCurveGauges.ts +5 -1
  70. package/src/votemarket/curve/fetchSnapshotUserData.ts +43 -3
  71. package/src/votemarket/curve/fetchUserVlCvxClaimable.ts +51 -6
  72. package/src/votemarket/fetchCampaigns.ts +4 -4
  73. package/src/votemarket/fetchNeededCampaignUpdate.ts +28 -0
@@ -14,6 +14,7 @@ import getFees from '../utils/getFees.js'
14
14
  import getHttpCalls from '../utils/getHttpCalls.js'
15
15
  import { lockedAmountCallSideChain } from '../utils/index.js'
16
16
  import substractFees from '../utils/substractFees.js'
17
+ import { parseBoost } from './parseBoost.js'
17
18
  import { parsePeg } from './parsePeg.js'
18
19
  import { parseVoteBoosterData } from './parseVoteBoosterData.js'
19
20
 
@@ -59,6 +60,7 @@ export const fetchLockers = async ({ provider, chainId, prices, withAirdropGraph
59
60
  locker.modules.locker,
60
61
  locker.secondaryMarket,
61
62
  locker.voteBooster,
63
+ locker.veBoost,
62
64
  ),
63
65
  )
64
66
  lockersRewardsCalls.push(...callsForRewards(locker.modules.gauge, locker.rewards))
@@ -101,6 +103,7 @@ export const fetchLockers = async ({ provider, chainId, prices, withAirdropGraph
101
103
  locker.modules.locker,
102
104
  locker.secondaryMarket,
103
105
  locker.voteBooster,
106
+ locker.veBoost,
104
107
  ).length
105
108
  const chunkedRawLockersData = rawLockersData.splice(0, lockerCallsLength)
106
109
 
@@ -140,6 +143,8 @@ export const fetchLockers = async ({ provider, chainId, prices, withAirdropGraph
140
143
  const veTokenRate =
141
144
  locker.id === 'mav' ? formatUnits(chunkedRawLockersData[11].result[0], locker.token.decimals) : '4'
142
145
 
146
+ const veBoostTotalSupply = parseBoost(locker, chunkedRawLockersData)
147
+
143
148
  const sideChains = locker.extensions.sideChains?.map((sideChain) => {
144
149
  const rewards =
145
150
  sideChain.rewards?.map((reward) => {
@@ -322,6 +327,7 @@ export const fetchLockers = async ({ provider, chainId, prices, withAirdropGraph
322
327
  sideChains,
323
328
  ...(locker.id === 'cake' ? { airdropGraph } : {}),
324
329
  },
330
+ veBoostTotalSupply,
325
331
  }
326
332
  })
327
333
 
@@ -0,0 +1,13 @@
1
+ import { type Locker } from '@stake-dao/constants'
2
+ import { formatUnits } from 'viem'
3
+
4
+ export const parseBoost = (locker: Locker, chunkedRawLockersData: any) => {
5
+ let boost = '0'
6
+ if (locker.veBoost && locker.id === 'crv') {
7
+ boost = formatUnits(chunkedRawLockersData[15].result, locker.token.decimals)
8
+ } else if (locker.veBoost) {
9
+ boost = formatUnits(chunkedRawLockersData[11].result, locker.token.decimals)
10
+ }
11
+
12
+ return boost
13
+ }
@@ -4,6 +4,7 @@ import type { Address } from 'viem'
4
4
  import depositorCalls from './depositorCalls.js'
5
5
  import gaugeCalls from './gaugeCalls.js'
6
6
  import pegCalls from './pegCalls.js'
7
+ import veBoosts from './veBoosts.js'
7
8
  import veCalls from './veCalls.js'
8
9
  import veSdtCalls from './veSdtCalls.js'
9
10
 
@@ -17,6 +18,10 @@ const callsForLockers = (
17
18
  locker: string,
18
19
  secondaryMarket: LockerSecondaryMarket | undefined,
19
20
  voteBooster: any,
21
+ veBoost?: {
22
+ address: Address
23
+ delegator: Address
24
+ },
20
25
  ) => [
21
26
  ...veCalls(lockerId, votingEscrow, locker as Address, token),
22
27
  ...depositorCalls(depositor, locker as Address),
@@ -34,6 +39,7 @@ const callsForLockers = (
34
39
  },
35
40
  ]
36
41
  : []),
42
+ ...veBoosts(veBoost),
37
43
  ]
38
44
 
39
45
  export default callsForLockers
@@ -0,0 +1,15 @@
1
+ import type { Address } from 'viem'
2
+
3
+ const veBoosts = (veBoost?: {
4
+ address: Address
5
+ delegator: Address
6
+ }) => {
7
+ const calls: any[] = []
8
+ if (veBoost) {
9
+ calls.push({ address: veBoost.delegator, name: 'totalSupply', params: [] })
10
+ }
11
+
12
+ return calls
13
+ }
14
+
15
+ export default veBoosts
@@ -150,7 +150,7 @@ export const getPancakeV3Vaults = async (
150
150
  },
151
151
  ]
152
152
 
153
- const tradingApy = Number(feesApr[strat.pool.toLowerCase()]?.apr7d || 0)
153
+ const tradingApy = Number(feesApr[strat.pool.toLowerCase()]?.apr7d || 0) * 100
154
154
  const apr = getAprBreakdown({ ...strat, tradingApy }, rewards, boost, undefined)
155
155
 
156
156
  const parsedData = {
@@ -44,8 +44,8 @@ export const computePositionData = (
44
44
  const token1AmountUsd = Number(token1Amount) * token1PriceInUsd
45
45
  const tvlToAdd = token0AmountUsd + token1AmountUsd
46
46
 
47
- const priceLower = tickToPrice(p.tickLower)
48
- const priceUpper = tickToPrice(p.tickUpper)
47
+ const priceLower = tickToPrice(p.tickLower) * 10 ** (s.coins[0].decimals - s.coins[1].decimals)
48
+ const priceUpper = tickToPrice(p.tickUpper) * 10 ** (s.coins[0].decimals - s.coins[1].decimals)
49
49
 
50
50
  const boost = Number(formatUnits(p.boostMultiplier.toString(), 12))
51
51
 
@@ -295,7 +295,7 @@ export function getPositionTradingFee({
295
295
  return (positionUSD + fees24HPosition * 365) / positionUSD - 1
296
296
  }
297
297
 
298
- export function tickToPrice(tick: number, precision = 6): number {
298
+ export function tickToPrice(tick: number, precision = 18): number {
299
299
  const sqrtRatioX96 = getSqrtRatioAtTick(tick)
300
300
 
301
301
  const ratioX192 = sqrtRatioX96 * sqrtRatioX96
@@ -111,7 +111,7 @@ export const fetchCurveGauges = async (rpc: Rpcs): Promise<GaugesData> => {
111
111
  const gaugeMetadata = gaugesMetadata.find((gm) => equalTlc(gm.gauge, g.childGauge))
112
112
  const filteredCoins = gaugeMetadata.coins.filter((c) => c._address !== zeroAddress)
113
113
 
114
- const name = gaugeMetadata.lp.name
114
+ let name = gaugeMetadata.lp.name
115
115
  .replace('Curve.fi Factory Crypto Pool:', '')
116
116
  .replace('Curve.fi Factory Pool:', '')
117
117
  .replace('Curve.fi Factory USD Metapool:', '')
@@ -119,6 +119,10 @@ export const fetchCurveGauges = async (rpc: Rpcs): Promise<GaugesData> => {
119
119
  .replace('Curve.fi', '')
120
120
  .trim()
121
121
 
122
+ if (name.startsWith('Curve Vault for')) {
123
+ name = `Lending ${gaugeMetadata.coins[0].symbol} (${gaugeMetadata.coins[1].symbol})`
124
+ }
125
+
122
126
  if (filteredCoins.length === 0 && !g.isKilled && g.weight !== '0')
123
127
  gaugesLpWithMissingCoins.push(gaugeMetadata.lp._address)
124
128
 
@@ -1,6 +1,7 @@
1
1
  import snapshot from '@snapshot-labs/snapshot.js'
2
2
  import { gql } from 'graphql-request'
3
3
  import { isAddress } from 'viem'
4
+ import { SNAPSHOT_HUB } from '../../endpoints.js'
4
5
  import { graphql } from '../../utils.js'
5
6
 
6
7
  interface SnapshotProposal {
@@ -21,6 +22,7 @@ interface ISnapshotUserData {
21
22
  proposal: SnapshotProposal
22
23
  vp: number
23
24
  space: string
25
+ userVotes: { [gaugeId: string]: { weight: number; relativeWeight: number } }
24
26
  }
25
27
 
26
28
  interface SnapshotStrategy {
@@ -66,6 +68,24 @@ query Proposals {
66
68
  }
67
69
  `
68
70
 
71
+ const SNAPSHOT_USER_VOTE_QUERY = (space: string, proposal: string, user: string) => gql`
72
+ query UserVote {
73
+ votes (
74
+ first: 1
75
+ where: {
76
+ space: "${space}"
77
+ proposal: "${proposal}"
78
+ voter: "${user}"
79
+ }
80
+ ) {
81
+ id
82
+ created
83
+ choice
84
+ vp
85
+ }
86
+ }
87
+ `
88
+
69
89
  export const fetchSnapshotUserData = async (
70
90
  userAddress: string,
71
91
  spaces: string[],
@@ -74,9 +94,7 @@ export const fetchSnapshotUserData = async (
74
94
  return undefined
75
95
  }
76
96
 
77
- const responses = await Promise.all(
78
- spaces.map((space) => graphql('https://hub.snapshot.org/graphql', SNAPSHOT_PROPOSAL_QUERY(space))),
79
- )
97
+ const responses = await Promise.all(spaces.map((space) => graphql(SNAPSHOT_HUB, SNAPSHOT_PROPOSAL_QUERY(space))))
80
98
 
81
99
  const results: ISnapshotUserData[] = []
82
100
  for (let i = 0; i < responses.length; i++) {
@@ -94,6 +112,7 @@ export const fetchSnapshotUserData = async (
94
112
 
95
113
  // Fetch user voting power
96
114
  let vp = 0
115
+ const userVotes = {}
97
116
  if (isAddress(userAddress)) {
98
117
  const fetchedVp = await (snapshot as any).utils.getScores(
99
118
  spaces[i],
@@ -108,12 +127,33 @@ export const fetchSnapshotUserData = async (
108
127
  vp += v?.[userAddress] || 0
109
128
  }
110
129
  }
130
+
131
+ const rawUserVotes = await graphql(SNAPSHOT_HUB, SNAPSHOT_USER_VOTE_QUERY(spaces[i]!, proposal.id, userAddress))
132
+
133
+ if (rawUserVotes?.votes && rawUserVotes.votes.length > 0) {
134
+ const rawUserVote = rawUserVotes.votes[0]
135
+ const totalSnapshotVote = Object.keys(rawUserVote.choice).reduce(
136
+ (total, i) => (total += rawUserVote.choice[i]),
137
+ 0,
138
+ )
139
+
140
+ for (const i of Object.keys(rawUserVote.choice)) {
141
+ const relativeWeight = rawUserVote.choice[i] / totalSnapshotVote
142
+ const proposalChoiceIndex = Number(i) - 1
143
+
144
+ userVotes[proposal.choices[proposalChoiceIndex]!] = {
145
+ weight: rawUserVote.vp * relativeWeight,
146
+ relativeWeight: relativeWeight * 100,
147
+ }
148
+ }
149
+ }
111
150
  }
112
151
 
113
152
  results.push({
114
153
  proposal,
115
154
  space: spaces[i] as string,
116
155
  vp,
156
+ userVotes,
117
157
  })
118
158
  }
119
159
 
@@ -19,9 +19,39 @@ type UnknownToken = {
19
19
  }
20
20
 
21
21
  const getMerkleData = async (epoch: number) => {
22
- return await fetch(
22
+ const response = await fetch(
23
23
  `${GH_STAKE_DAO_BOUNTIES_REPORT}/refs/heads/main/bounties-reports/${epoch}/vlCVX/merkle_data.json`,
24
- ).then((res) => res.json())
24
+ )
25
+ if (!response.ok) throw new Error('Failed to fetch merkle data')
26
+ return response.json()
27
+ }
28
+
29
+ const isRootMatch = (contractRoot: string, fileRoot: string) =>
30
+ (contractRoot === ZERO_ROOT && fileRoot === ZERO_ROOT) || contractRoot.toLowerCase() === fileRoot.toLowerCase()
31
+
32
+ const validateMerkleRoots = (delegatorRoot: string, nonDelegatorRoot: string, merkleData: any) => {
33
+ const delegatorMatches = isRootMatch(delegatorRoot, merkleData.delegators.merkleRoot)
34
+ const nonDelegatorMatches = isRootMatch(nonDelegatorRoot, merkleData.nonDelegators.merkleRoot)
35
+ return delegatorMatches || nonDelegatorMatches
36
+ }
37
+
38
+ const getMerkleRoots = async (provider: any) => {
39
+ const contracts = [contract('vlCvxDelegatorMerkle', mainnet.id), contract('vlCvxNotDelegatorMerkle', mainnet.id)]
40
+
41
+ const result = await multicall(
42
+ provider,
43
+ contracts.map((address) => ({
44
+ address,
45
+ name: 'root',
46
+ params: [],
47
+ })),
48
+ parseAbi(['function root() external view returns (bytes32)']),
49
+ )
50
+
51
+ return {
52
+ delegatorRoot: result[0].result,
53
+ nonDelegatorRoot: result[1].result,
54
+ }
25
55
  }
26
56
 
27
57
  const getClaimedData = async (provider: any, tokens: string[], user: string, contract: string) => {
@@ -74,14 +104,29 @@ const getParsedData = (
74
104
  : undefined
75
105
  }
76
106
 
107
+ const ZERO_ROOT = '0x0000000000000000000000000000000000000000000000000000000000000000'
108
+
77
109
  export const fetchUserVlCvxClaimable = async (provider: any, rpc: Rpcs, user: string) => {
78
110
  const nowEpoch = Math.floor(Date.now() / (1000 * ONE_WEEK)) * ONE_WEEK
111
+ const prevEpoch = nowEpoch - ONE_WEEK
112
+
113
+ const { delegatorRoot, nonDelegatorRoot } = await getMerkleRoots(provider)
79
114
 
80
115
  let vlCvxClaimable: any
81
- try {
82
- vlCvxClaimable = await getMerkleData(nowEpoch)
83
- } catch {
84
- vlCvxClaimable = await getMerkleData(nowEpoch - ONE_WEEK)
116
+
117
+ // Try current week, then previous week if needed
118
+ for (const epoch of [nowEpoch, prevEpoch]) {
119
+ try {
120
+ const merkleData = await getMerkleData(epoch)
121
+ if (validateMerkleRoots(delegatorRoot, nonDelegatorRoot, merkleData)) {
122
+ vlCvxClaimable = merkleData
123
+ break
124
+ }
125
+ } catch {}
126
+ }
127
+
128
+ if (!vlCvxClaimable) {
129
+ throw new Error('Could not find valid merkle data')
85
130
  }
86
131
 
87
132
  const vlCvxUserDelegated = vlCvxClaimable?.delegators.claims[user]
@@ -161,10 +161,10 @@ export const fetchCampaigns = async ({ platform, chainId, rpcs }: FetchCampaigns
161
161
  const prices = [
162
162
  ...(fetchedTokens.length > 0 ? await getPrices(fetchedTokens as any, chainId) : []),
163
163
  ...(nativeFetchedTokens.length > 0 ? await getPrices(nativeFetchedTokens as any, mainnet.id) : []),
164
- { address: '0x9E49A0314AE61e9C9E34e4Af62a73FBFfB6DE95A', symbol: 'ARR', usdPrice: 10 }, // For test purpose
165
- { address: '0x0Cc20784f790805537D4eE33B41f1aC4eC55446B', symbol: 'USDC', usdPrice: 1 }, // For test purpose
166
- { address: '0x59c9a01163e685719FAbE5De34779F563f49D4a3', symbol: 'CRV', usdPrice: 30 }, // For test purpose
167
- { address: '0x3c7b193aa39a85FDE911465d35CE3A74499F0A7B', symbol: 'ARR', usdPrice: 10 }, // For test purpose
164
+ // { address: '0x9E49A0314AE61e9C9E34e4Af62a73FBFfB6DE95A', symbol: 'ARR', usdPrice: 10 }, // For test purpose
165
+ // { address: '0x0Cc20784f790805537D4eE33B41f1aC4eC55446B', symbol: 'USDC', usdPrice: 1 }, // For test purpose
166
+ // { address: '0x59c9a01163e685719FAbE5De34779F563f49D4a3', symbol: 'CRV', usdPrice: 30 }, // For test purpose
167
+ // { address: '0x3c7b193aa39a85FDE911465d35CE3A74499F0A7B', symbol: 'ARR', usdPrice: 10 }, // For test purpose
168
168
  ]
169
169
 
170
170
  const allFetchedTokens = [...fetchedTokens, ...nativeFetchedTokens]
@@ -0,0 +1,28 @@
1
+ import { encodeAbiParameters, parseAbiParameters } from 'viem'
2
+ import { mainnet } from 'viem/chains'
3
+ import { getNeededCampaignUpdate } from '../bytecodes/index.js'
4
+ import { concatBytecode } from '../index.js'
5
+ import { batchJsonRpc } from '../utils.js'
6
+
7
+ export const fetchNeededCampaignUpdate = async (rpc: string, platform: string, campaign: any) => {
8
+ const inputData = encodeAbiParameters(
9
+ parseAbiParameters(
10
+ getNeededCampaignUpdate.inputType[campaign.chainId] || getNeededCampaignUpdate.inputType[mainnet.id]!,
11
+ ) as any,
12
+ [platform, campaign.id],
13
+ )
14
+ const call = concatBytecode(
15
+ getNeededCampaignUpdate.bytecode[campaign.chainId] || getNeededCampaignUpdate.bytecode[mainnet.id]!,
16
+ inputData,
17
+ )
18
+
19
+ const neededForUpdate = await batchJsonRpc({
20
+ rpc,
21
+ calls: [call],
22
+ outputTypeAbi:
23
+ getNeededCampaignUpdate.outputTypeHr[campaign.chainId] || getNeededCampaignUpdate.outputTypeHr[mainnet.id]!,
24
+ callsKey: `Get needed update - chainId ${campaign.chainId} - rpc ${rpc}`,
25
+ })
26
+
27
+ return neededForUpdate
28
+ }