@stake-dao/reader 0.4.27 → 0.4.29

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 (111) hide show
  1. package/dist/esm/bytecodes/votemarket/batchCampaigns.js +6 -3
  2. package/dist/esm/bytecodes/votemarket/batchCampaigns.js.map +1 -1
  3. package/dist/esm/bytecodes/votemarket/batchClaimableData.js +21 -0
  4. package/dist/esm/bytecodes/votemarket/batchClaimableData.js.map +1 -0
  5. package/dist/esm/bytecodes/votemarket/batchGaugesWeight.js +13 -0
  6. package/dist/esm/bytecodes/votemarket/batchGaugesWeight.js.map +1 -0
  7. package/dist/esm/bytecodes/votemarket/batchVotes.js +13 -0
  8. package/dist/esm/bytecodes/votemarket/batchVotes.js.map +1 -0
  9. package/dist/esm/bytecodes/votemarket/curve/batchCurveGaugesMetadata.js +5 -3
  10. package/dist/esm/bytecodes/votemarket/curve/batchCurveGaugesMetadata.js.map +1 -1
  11. package/dist/esm/bytecodes/votemarket/curve/batchCurveOldLps.js +3 -2
  12. package/dist/esm/bytecodes/votemarket/curve/batchCurveOldLps.js.map +1 -1
  13. package/dist/esm/endpoints.js +0 -1
  14. package/dist/esm/endpoints.js.map +1 -1
  15. package/dist/esm/index.js +7 -0
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/lockers/fetch/index.js +10 -0
  18. package/dist/esm/lockers/fetch/index.js.map +1 -1
  19. package/dist/esm/lockers/utils/getHttpCalls.js +8 -0
  20. package/dist/esm/lockers/utils/getHttpCalls.js.map +1 -1
  21. package/dist/esm/sdt/fetch.js +20 -26
  22. package/dist/esm/sdt/fetch.js.map +1 -1
  23. package/dist/esm/strategies/pancakeswap/endpoints.js +1 -1
  24. package/dist/esm/tsconfig.build.tsbuildinfo +1 -1
  25. package/dist/esm/utils.js +43 -19
  26. package/dist/esm/utils.js.map +1 -1
  27. package/dist/esm/votemarket/curve/config.js +4 -1
  28. package/dist/esm/votemarket/curve/config.js.map +1 -1
  29. package/dist/esm/votemarket/curve/fetchCurveGauges.js +6 -1
  30. package/dist/esm/votemarket/curve/fetchCurveGauges.js.map +1 -1
  31. package/dist/esm/votemarket/curve/fetchSnapshotUserData.js +14 -9
  32. package/dist/esm/votemarket/curve/fetchSnapshotUserData.js.map +1 -1
  33. package/dist/esm/votemarket/fetchCampaigns.js +115 -14
  34. package/dist/esm/votemarket/fetchCampaigns.js.map +1 -1
  35. package/dist/esm/votemarket/fetchClaimableData.js +36 -0
  36. package/dist/esm/votemarket/fetchClaimableData.js.map +1 -0
  37. package/dist/esm/votemarket/fetchEpochVotes.js +30 -0
  38. package/dist/esm/votemarket/fetchEpochVotes.js.map +1 -0
  39. package/dist/esm/votemarket/generateProofs/config.js +53 -0
  40. package/dist/esm/votemarket/generateProofs/config.js.map +1 -0
  41. package/dist/esm/votemarket/generateProofs/getBlockProof.js +33 -0
  42. package/dist/esm/votemarket/generateProofs/getBlockProof.js.map +1 -0
  43. package/dist/esm/votemarket/generateProofs/getGaugeProof.js +50 -0
  44. package/dist/esm/votemarket/generateProofs/getGaugeProof.js.map +1 -0
  45. package/dist/esm/votemarket/generateProofs/getUserProof.js +49 -0
  46. package/dist/esm/votemarket/generateProofs/getUserProof.js.map +1 -0
  47. package/dist/types/bytecodes/votemarket/batchCampaigns.d.ts.map +1 -1
  48. package/dist/types/bytecodes/votemarket/batchClaimableData.d.ts +4 -0
  49. package/dist/types/bytecodes/votemarket/batchClaimableData.d.ts.map +1 -0
  50. package/dist/types/bytecodes/votemarket/batchGaugesWeight.d.ts +4 -0
  51. package/dist/types/bytecodes/votemarket/batchGaugesWeight.d.ts.map +1 -0
  52. package/dist/types/bytecodes/votemarket/batchVotes.d.ts +4 -0
  53. package/dist/types/bytecodes/votemarket/batchVotes.d.ts.map +1 -0
  54. package/dist/types/bytecodes/votemarket/curve/batchCurveGaugesMetadata.d.ts.map +1 -1
  55. package/dist/types/bytecodes/votemarket/curve/batchCurveOldLps.d.ts.map +1 -1
  56. package/dist/types/endpoints.d.ts +0 -1
  57. package/dist/types/endpoints.d.ts.map +1 -1
  58. package/dist/types/index.d.ts +5 -0
  59. package/dist/types/index.d.ts.map +1 -1
  60. package/dist/types/lockers/fetch/index.d.ts.map +1 -1
  61. package/dist/types/lockers/utils/getHttpCalls.d.ts +3 -0
  62. package/dist/types/lockers/utils/getHttpCalls.d.ts.map +1 -1
  63. package/dist/types/sdt/fetch.d.ts +2 -2
  64. package/dist/types/sdt/fetch.d.ts.map +1 -1
  65. package/dist/types/utils.d.ts +13 -1
  66. package/dist/types/utils.d.ts.map +1 -1
  67. package/dist/types/votemarket/curve/config.d.ts +8 -1
  68. package/dist/types/votemarket/curve/config.d.ts.map +1 -1
  69. package/dist/types/votemarket/curve/fetchCurveGauges.d.ts.map +1 -1
  70. package/dist/types/votemarket/curve/fetchSnapshotUserData.d.ts +1 -0
  71. package/dist/types/votemarket/curve/fetchSnapshotUserData.d.ts.map +1 -1
  72. package/dist/types/votemarket/fetchCampaigns.d.ts.map +1 -1
  73. package/dist/types/votemarket/fetchClaimableData.d.ts +6 -0
  74. package/dist/types/votemarket/fetchClaimableData.d.ts.map +1 -0
  75. package/dist/types/votemarket/fetchEpochVotes.d.ts +9 -0
  76. package/dist/types/votemarket/fetchEpochVotes.d.ts.map +1 -0
  77. package/dist/types/votemarket/generateProofs/config.d.ts +32 -0
  78. package/dist/types/votemarket/generateProofs/config.d.ts.map +1 -0
  79. package/dist/types/votemarket/generateProofs/getBlockProof.d.ts +6 -0
  80. package/dist/types/votemarket/generateProofs/getBlockProof.d.ts.map +1 -0
  81. package/dist/types/votemarket/generateProofs/getGaugeProof.d.ts +6 -0
  82. package/dist/types/votemarket/generateProofs/getGaugeProof.d.ts.map +1 -0
  83. package/dist/types/votemarket/generateProofs/getUserProof.d.ts +6 -0
  84. package/dist/types/votemarket/generateProofs/getUserProof.d.ts.map +1 -0
  85. package/dist/types/votemarket/types.d.ts +31 -8
  86. package/dist/types/votemarket/types.d.ts.map +1 -1
  87. package/package.json +1 -1
  88. package/src/bytecodes/votemarket/batchCampaigns.ts +6 -3
  89. package/src/bytecodes/votemarket/batchClaimableData.ts +24 -0
  90. package/src/bytecodes/votemarket/batchGaugesWeight.ts +15 -0
  91. package/src/bytecodes/votemarket/batchVotes.ts +15 -0
  92. package/src/bytecodes/votemarket/curve/batchCurveGaugesMetadata.ts +5 -3
  93. package/src/bytecodes/votemarket/curve/batchCurveOldLps.ts +3 -2
  94. package/src/endpoints.ts +0 -1
  95. package/src/index.ts +10 -0
  96. package/src/lockers/fetch/index.ts +13 -0
  97. package/src/lockers/utils/getHttpCalls.ts +8 -0
  98. package/src/sdt/fetch.ts +29 -27
  99. package/src/strategies/pancakeswap/endpoints.ts +1 -1
  100. package/src/utils.ts +66 -19
  101. package/src/votemarket/curve/config.ts +5 -1
  102. package/src/votemarket/curve/fetchCurveGauges.ts +15 -2
  103. package/src/votemarket/curve/fetchSnapshotUserData.ts +16 -9
  104. package/src/votemarket/fetchCampaigns.ts +148 -15
  105. package/src/votemarket/fetchClaimableData.ts +50 -0
  106. package/src/votemarket/fetchEpochVotes.ts +48 -0
  107. package/src/votemarket/generateProofs/config.ts +53 -0
  108. package/src/votemarket/generateProofs/getBlockProof.ts +48 -0
  109. package/src/votemarket/generateProofs/getGaugeProof.ts +75 -0
  110. package/src/votemarket/generateProofs/getUserProof.ts +74 -0
  111. package/src/votemarket/types.ts +36 -8
package/src/utils.ts CHANGED
@@ -2,9 +2,8 @@ import { tokens } from '@stake-dao/constants'
2
2
  import type { Token } from '@stake-dao/constants'
3
3
  import { request } from 'graphql-request'
4
4
  import { groupBy } from 'lodash-es'
5
- import { http, createPublicClient, decodeAbiParameters, extractChain, parseAbiParameters } from 'viem'
5
+ import { http, createPublicClient, decodeAbiParameters, extractChain, parseAbiParameters, toHex } from 'viem'
6
6
  import * as viemChains from 'viem/chains'
7
- import { AGNOSTIC_QUERY_URL } from './endpoints.js'
8
7
  import { withTwoDec } from './number.js'
9
8
 
10
9
  export const isProtocolToken = (protocol: string, tokenSymbol: string) =>
@@ -70,6 +69,26 @@ export const projectWorkingBalance = (veBalance: bigint, veTotal: bigint, balanc
70
69
  return [balance, derived + adjusted].reduce((m, e) => (e < m ? e : m))
71
70
  }
72
71
 
72
+ export const rpcGetLastBlockTimetstamp = async (rpc: string) => {
73
+ const request = await (
74
+ await fetch(rpc, {
75
+ method: 'POST',
76
+ headers: { 'Content-Type': 'application/json' },
77
+ body: JSON.stringify({
78
+ id: 0,
79
+ jsonrpc: '2.0',
80
+ method: 'eth_getBlockByNumber',
81
+ params: ['latest', false],
82
+ }),
83
+ })
84
+ ).json()
85
+
86
+ return {
87
+ number: Number(request.result.number),
88
+ timestamp: Number(request.result.timestamp),
89
+ }
90
+ }
91
+
73
92
  interface RpcCallArgs {
74
93
  to: string
75
94
  data: string
@@ -138,6 +157,51 @@ export const batchJsonRpc = async (args: BatchJsonRpcArgs) => {
138
157
  return rpcResults.flatMap((r) => (r.status === 'fulfilled' ? r.value[0] : []))
139
158
  }
140
159
 
160
+ interface BatchJsonRpcWithBlocksArgs {
161
+ rpc: string
162
+ call: string
163
+ outputTypeAbi: any | any[]
164
+ parse?: boolean
165
+ callsKey: string
166
+ blocks: number[]
167
+ }
168
+
169
+ export const batchJsonRpcWithBlocks = async (args: BatchJsonRpcWithBlocksArgs) => {
170
+ const { rpc, call, outputTypeAbi, parse = true, callsKey, blocks } = args
171
+
172
+ const rpcRequests = await (
173
+ await fetch(rpc, {
174
+ method: 'POST',
175
+ headers: { 'Content-Type': 'application/json' },
176
+ body: JSON.stringify(
177
+ blocks.map((block, index) => ({
178
+ id: index,
179
+ jsonrpc: '2.0',
180
+ method: 'eth_call',
181
+ params: [{ data: call }, toHex(block)],
182
+ })),
183
+ ),
184
+ })
185
+ ).json()
186
+
187
+ const rpcResults = rpcRequests.map((res: any, index: number) => {
188
+ const isError = Object.keys(res).includes('error')
189
+ const abi = Array.isArray(outputTypeAbi[0]) ? outputTypeAbi[index] : outputTypeAbi
190
+ const status = isError ? 'error' : 'fulfilled'
191
+ const value = isError
192
+ ? `${callsKey} call index ${index}: code ${res.error.code}\n${res.error.message}`
193
+ : decodeAbiParameters(parse ? (parseAbiParameters(abi) as any) : abi, res.result)
194
+
195
+ if (isError) {
196
+ console.error(value)
197
+ }
198
+
199
+ return { status, value }
200
+ })
201
+
202
+ return rpcResults.flatMap((r) => (r.status === 'fulfilled' ? r.value : []))
203
+ }
204
+
141
205
  export interface Call {
142
206
  address: string // Address of the contract
143
207
  name: string // Function name on the contract (example: balanceOf)
@@ -157,23 +221,6 @@ export const multicall = async (provider: any, calls: Call[], abi: readonly any[
157
221
  return results
158
222
  }
159
223
 
160
- export const agnostic = async (query: string, apiKey: string, defaultErrorValue?: any): Promise<any> => {
161
- try {
162
- const response = await fetch(AGNOSTIC_QUERY_URL, {
163
- body: query,
164
- method: 'POST',
165
- headers: {
166
- Authorization: apiKey,
167
- },
168
- })
169
- const data = await response.json()
170
- return data.rows
171
- } catch (e) {
172
- console.error(e)
173
- return defaultErrorValue
174
- }
175
- }
176
-
177
224
  export const graphql = async (url: string, query: string, params?: any, defaultErrorValue?: any): Promise<any> => {
178
225
  try {
179
226
  return await request(url, query, params)
@@ -1,6 +1,6 @@
1
1
  import { lc } from '../../utils.js'
2
2
 
3
- export const GAUGES_CHUNK_SIZE = 35
3
+ export const GAUGES_CHUNK_SIZE = 30
4
4
  export const GAUGES_METADATA_CHUNK_SIZE = 10
5
5
 
6
6
  export const NOT_LP_GAUGES = {
@@ -41,3 +41,7 @@ export const BROKEN_GAUGES = [
41
41
  lc('0x4dC4A289a8E33600D8bD4cf5F6313E43a37adec7'), // chainId 1 - Curve.fi RSV/3Crv not in registry
42
42
  lc('0x82049b520cAc8b05E703bb35d1691B5005A92848'), // chainId 100 - Curve.fi x3CRV RewardGauge Deposit
43
43
  ]
44
+
45
+ export const CHAIN_NATIVE_COIN = {
46
+ 1: { name: 'Ether', symbol: 'ETH', decimals: 18 },
47
+ }
@@ -5,8 +5,15 @@ import { mainnet } from 'viem/chains'
5
5
  import { batchCurveGauges, batchCurveGaugesMetadata, batchCurveOldLps } from '../../bytecodes/index.js'
6
6
  import { concatBytecode } from '../../index.js'
7
7
  import { batchJsonRpc, equalTlc, rpcCall } from '../../utils.js'
8
- import { BROKEN_GAUGES, GAUGES_CHUNK_SIZE, GAUGES_METADATA_CHUNK_SIZE, NOT_LP_GAUGES_INDEX } from './config.js'
9
-
8
+ import {
9
+ BROKEN_GAUGES,
10
+ CHAIN_NATIVE_COIN,
11
+ GAUGES_CHUNK_SIZE,
12
+ GAUGES_METADATA_CHUNK_SIZE,
13
+ NOT_LP_GAUGES_INDEX,
14
+ } from './config.js'
15
+
16
+ // TODO
10
17
  export type Gauge = {
11
18
  gauge: string
12
19
  inflationRate: string
@@ -116,12 +123,14 @@ export const fetchCurveGauges = async (rpc: Rpcs): Promise<GaugesData> => {
116
123
 
117
124
  return {
118
125
  gauge: g.gauge,
126
+ manager: gaugeMetadata.manager,
119
127
  name,
120
128
  weight: g.weight,
121
129
  relativeWeight: g.relativeWeight,
122
130
  futureRelativeWeight: g.futureRelativeWeight,
123
131
  isKilled: g.isKilled,
124
132
  chainId: g.chainId,
133
+ pool: gaugeMetadata.pool,
125
134
  lp: {
126
135
  name: gaugeMetadata.lp.name,
127
136
  symbol: gaugeMetadata.lp.symbol,
@@ -156,6 +165,8 @@ export const fetchCurveGauges = async (rpc: Rpcs): Promise<GaugesData> => {
156
165
  symbol: c.symbol,
157
166
  address: c._address,
158
167
  decimals: Number(c.decimals),
168
+
169
+ ...(equalTlc(c._address, '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') ? CHAIN_NATIVE_COIN[g.chainId] : {}),
159
170
  }))
160
171
  .filter((c) => c.address !== zeroAddress)
161
172
 
@@ -163,6 +174,8 @@ export const fetchCurveGauges = async (rpc: Rpcs): Promise<GaugesData> => {
163
174
  ...g,
164
175
  name: missingData ? missingData.name : g.name,
165
176
  coins,
177
+ pool: missingData ? missingData.pool : g.pool,
178
+ inController: true,
166
179
  }
167
180
  })
168
181
 
@@ -10,6 +10,7 @@ interface SnapshotProposal {
10
10
  end: number
11
11
  state: string
12
12
  choices: string[]
13
+ scores: number[]
13
14
  snapshot: string
14
15
  space: {
15
16
  strategies: SnapshotStrategy[]
@@ -41,20 +42,25 @@ query Proposals {
41
42
  }
42
43
  orderBy: "created"
43
44
  orderDirection: desc
44
- ){
45
- id
45
+ ) {
46
+ id
46
47
  title
48
+ choices
47
49
  start
48
50
  end
49
- state
50
51
  snapshot
51
- choices
52
+ state
53
+ created
54
+ scores
52
55
  space {
53
- strategies {
54
- name
55
- network
56
- params
57
- }
56
+ id
57
+ name
58
+ symbol
59
+ strategies {
60
+ name
61
+ network
62
+ params
63
+ }
58
64
  }
59
65
  }
60
66
  }
@@ -67,6 +73,7 @@ export const fetchSnapshotUserData = async (
67
73
  if (spaces.length === 0) {
68
74
  return undefined
69
75
  }
76
+
70
77
  const responses = await Promise.all(
71
78
  spaces.map((space) => graphql('https://hub.snapshot.org/graphql', SNAPSHOT_PROPOSAL_QUERY(space))),
72
79
  )
@@ -1,11 +1,14 @@
1
- import { ONE_WEEK, tokenWithAddress, tokens } from '@stake-dao/constants'
1
+ import { ONE_WEEK, RPC, contracts, tokenWithAddress, tokens } from '@stake-dao/constants'
2
2
  import { chunk, filter, range, remove, uniq } from 'lodash-es'
3
- import { decodeAbiParameters, encodeAbiParameters, formatUnits, parseAbiParameters } from 'viem'
3
+ import { decodeAbiParameters, encodeAbiParameters, formatUnits, parseAbiParameters, zeroAddress } from 'viem'
4
+ import { mainnet } from 'viem/chains'
4
5
  import { batchTokenData } from '../bytecodes/index.js'
5
6
  import batchCampaigns from '../bytecodes/votemarket/batchCampaigns.js'
7
+ import batchGaugesWeight from '../bytecodes/votemarket/batchGaugesWeight.js'
6
8
  import { getPrices } from '../prices.js'
7
- import { batchJsonRpc, concatBytecode, equalTlc, rpcCall } from '../utils.js'
8
- import type { Campaign } from './types.js'
9
+ import { Zero } from '../strategies/utils/index.js'
10
+ import { batchJsonRpc, concatBytecode, equalTlc, rpcCall, rpcGetLastBlockTimetstamp } from '../utils.js'
11
+ import type { Campaign, RawCampaign, RawPeriod } from './types.js'
9
12
 
10
13
  export const CAMPAIGNS_CHUNK_SIZE = 10
11
14
  export const CLAIM_WINDOW_LENGTH = 24 // weeks
@@ -17,7 +20,68 @@ interface FetchCampaignsProps {
17
20
  rpc: string
18
21
  }
19
22
 
23
+ const updateEpoch = ({
24
+ period,
25
+ previousPeriod,
26
+ campaign,
27
+ totalVotes,
28
+ remainingPeriods,
29
+ }: {
30
+ period: RawPeriod
31
+ previousPeriod?: RawPeriod
32
+ campaign: RawCampaign
33
+ totalVotes: bigint
34
+ remainingPeriods: number
35
+ }) => {
36
+ // -- _checkForUpgrade is handled in solidity BatchCampaigns.sol
37
+ // -- If not first period, check if previousPeriod is updated
38
+ if (!period.updated && (previousPeriod?.updated || typeof previousPeriod === 'undefined')) {
39
+ // Calculate remaining periods and total reward.
40
+ const totalRewardForRemainingPeriods: bigint = campaign.totalRewardAmount - campaign.totalDistributed
41
+
42
+ // -- Update the period data.
43
+ const newRewardPerPeriod =
44
+ remainingPeriods > 0 ? totalRewardForRemainingPeriods / BigInt(remainingPeriods) : totalRewardForRemainingPeriods
45
+ period.rewardPerPeriod = newRewardPerPeriod
46
+
47
+ // -- Update the reward per vote
48
+ // If no votes, rollover the leftover to the next epoch.
49
+ if (totalVotes === Zero) {
50
+ period.leftover = period.rewardPerPeriod
51
+ } else {
52
+ // Calculate reward per vote
53
+ let rewardPerVote = (newRewardPerPeriod * BigInt(1e18)) / totalVotes
54
+
55
+ // Cap reward per vote at max reward per vote
56
+ if (rewardPerVote > campaign.maxRewardPerVote) {
57
+ rewardPerVote = campaign.maxRewardPerVote
58
+
59
+ // Calculate leftover rewards
60
+ const leftOver = newRewardPerPeriod - (rewardPerVote * totalVotes) / BigInt(1e18)
61
+
62
+ // Handle leftover rewards
63
+ if (campaign.hook === zeroAddress) {
64
+ // Store leftover in the period if there is no hook.
65
+ period.leftover = period.leftover + leftOver
66
+ }
67
+ }
68
+
69
+ // Save the calculated reward per vote.
70
+ period.rewardPerVote = rewardPerVote
71
+ }
72
+
73
+ // -- Update the total distributed amount
74
+ campaign.totalDistributed = campaign.totalDistributed + newRewardPerPeriod - period.leftover
75
+
76
+ // -- Mark the period as updated
77
+ period.updated = true
78
+ }
79
+
80
+ return campaign
81
+ }
82
+
20
83
  export const fetchCampaigns = async ({ platform, chainId, rpc }: FetchCampaignsProps): Promise<Campaign[]> => {
84
+ const block = await rpcGetLastBlockTimetstamp(rpc)
21
85
  const nCampaignsRequest = await rpcCall(rpc, [{ to: platform, data: '0x7274e30d' }]) // campaignCount
22
86
  const nCampaigns = Number(
23
87
  decodeAbiParameters([{ type: 'uint256', name: 'campaignCount' }], nCampaignsRequest[0].result),
@@ -42,6 +106,22 @@ export const fetchCampaigns = async ({ platform, chainId, rpc }: FetchCampaignsP
42
106
  callsKey: `votemarket/fetchCampaigns.ts: rawCampaigns - chainId ${chainId} - rpc ${rpc}`,
43
107
  })
44
108
 
109
+ const campaignsGauges: string[] = uniq(rawCampaigns.map((rc) => rc.campaign.gauge))
110
+ const rawGaugesWeights = await batchJsonRpc({
111
+ rpc: RPC[mainnet.id],
112
+ calls: [
113
+ concatBytecode(
114
+ batchGaugesWeight.bytecode[mainnet.id]!,
115
+ encodeAbiParameters(parseAbiParameters(batchGaugesWeight.inputType[mainnet.id]!) as any, [
116
+ contracts.curveGaugeController![1]!,
117
+ campaignsGauges,
118
+ ]),
119
+ ),
120
+ ],
121
+ outputTypeAbi: batchGaugesWeight.outputTypeHr[mainnet.id]!,
122
+ callsKey: `votemarket/fetchCampaigns.ts: rawGaugesWeights - chainId ${mainnet.id} - rpc ${RPC[mainnet.id]}`,
123
+ })
124
+
45
125
  const campaignsTokens: string[] = uniq(rawCampaigns.map((rc) => rc.campaign.rewardToken))
46
126
  const tokensToFetch: string[] = remove(campaignsTokens, (t) => !tokenWithAddress(t, chainId))
47
127
 
@@ -73,11 +153,17 @@ export const fetchCampaigns = async ({ platform, chainId, rpc }: FetchCampaignsP
73
153
  })),
74
154
  ]
75
155
 
76
- const prices = await getPrices(fetchedTokens as any, chainId)
156
+ // const prices = await getPrices(fetchedTokens as any, chainId)
157
+ // TMP FOR TEST PURPOSE REMOVE ONCE IN PROD
158
+ const prices = [
159
+ ...(await getPrices(fetchedTokens as any, chainId)),
160
+ { address: '0x9E49A0314AE61e9C9E34e4Af62a73FBFfB6DE95A', symbol: 'ARR', usdPrice: 10 },
161
+ ]
77
162
 
78
- const currentTimestamp = Math.floor(Date.now() / 1000)
163
+ const currentTimestamp = block.timestamp
79
164
 
80
165
  const campaigns = rawCampaigns.map((rc) => {
166
+ const gaugeWeightData = rawGaugesWeights.find((g) => equalTlc(g.gauge, rc.campaign.gauge))
81
167
  const rewardToken = fetchedTokens.find((t) => equalTlc(t.address, rc.campaign.rewardToken) && t.chainId === chainId)
82
168
  const decimals = rewardToken ? rewardToken.decimals : 18
83
169
 
@@ -85,33 +171,80 @@ export const fetchCampaigns = async ({ platform, chainId, rpc }: FetchCampaignsP
85
171
 
86
172
  const startTimestamp = Number(rc.campaign.startTimestamp)
87
173
  const endTimestamp = Number(rc.campaign.endTimestamp)
174
+ const numberOfPeriods = Number(rc.campaign.numberOfPeriods)
175
+
176
+ const totalVotes = gaugeWeightData.weight
177
+
178
+ rc.periods.forEach((_, index) => {
179
+ if (!rc.periods[index].updated) {
180
+ updateEpoch({
181
+ period: rc.periods[index],
182
+ previousPeriod: index > 0 ? rc.periods[index - 1] : undefined,
183
+ campaign: rc.campaign,
184
+ remainingPeriods: Number(rc.campaign.numberOfPeriods) - index,
185
+ totalVotes,
186
+ })
187
+ }
188
+ })
189
+
190
+ const indexOfCurrentPeriod =
191
+ rc.periods.length > numberOfPeriods - Number(rc.periodLeft)
192
+ ? numberOfPeriods - Number(rc.periodLeft)
193
+ : rc.periods.length - 1
194
+ const rawCurrentPeriod = rc.periods[indexOfCurrentPeriod]
195
+ const rawPreviousPeriod = rc.periods[indexOfCurrentPeriod - 1]
196
+
197
+ // // Simulate previous period update if not updated
198
+ // if (rc.previousPeriod && !rc.previousPeriod.updated && Number(numberOfPeriods) > Number(rc.periodLeft)) {
199
+ // updateEpoch({
200
+ // period: rc.previousPeriod,
201
+ // campaign: rc.campaign,
202
+ // remainingPeriods: Number(rc.periodLeft) + 1,
203
+ // totalVotes,
204
+ // })
205
+ // }
206
+
207
+ // // Simulate current period update to simulate reward and apr
208
+ // updateEpoch({
209
+ // period: rc.currentPeriod,
210
+ // previousPeriod: rc.previousPeriod,
211
+ // campaign: rc.campaign,
212
+ // remainingPeriods: Number(rc.periodLeft),
213
+ // totalVotes,
214
+ // })
88
215
 
89
216
  return {
90
217
  id: Number(rc.id),
91
- chainId: Number(rc.campaign.chainId),
218
+ chainId,
92
219
  gauge: rc.campaign.gauge,
220
+ gaugeChainId: Number(rc.campaign.chainId),
93
221
  manager: rc.campaign.manager,
94
222
  rewardToken,
95
223
  rewardTokenPrice,
96
- numberOfPeriods: Number(rc.campaign.numberOfPeriods),
224
+ numberOfPeriods,
225
+ periodLeft: Number(rc.periodLeft),
97
226
  maxRewardPerVote: formatUnits(rc.campaign.maxRewardPerVote, decimals),
98
227
  totalRewardAmount: formatUnits(rc.campaign.totalRewardAmount, decimals),
99
228
  totalDistributed: formatUnits(rc.campaign.totalDistributed, decimals),
100
229
  startTimestamp,
101
230
  endTimestamp,
102
231
  hook: rc.campaign.hook,
103
- isKilled: rc.isKilled,
232
+ isClosed: rc.isClosed,
104
233
  addresses: rc.addresses,
105
234
  isWhitelist: rc.isWhitelistOnly,
106
235
  isBlacklist: !rc.isWhitelistOnly && rc.addresses.length > 0,
236
+ previousPeriod: {
237
+ rewardPerPeriod: formatUnits(rawPreviousPeriod.rewardPerPeriod, decimals),
238
+ rewardPerVote: formatUnits(rawPreviousPeriod.rewardPerVote, decimals),
239
+ leftover: formatUnits(rawPreviousPeriod.leftover, decimals),
240
+ updated: rawPreviousPeriod.updated,
241
+ },
107
242
  currentPeriod: {
108
- rewardPerPeriod: formatUnits(rc.currentPeriod.rewardPerPeriod, decimals),
109
- rewardPerVote: formatUnits(rc.currentPeriod.rewardPerVote, decimals),
110
- leftover: formatUnits(rc.currentPeriod.leftover, decimals),
111
- updated: rc.currentPeriod.updated,
243
+ rewardPerPeriod: formatUnits(rawCurrentPeriod.rewardPerPeriod, decimals),
244
+ rewardPerVote: formatUnits(rawCurrentPeriod.rewardPerVote, decimals),
245
+ leftover: formatUnits(rawCurrentPeriod.leftover, decimals),
246
+ updated: rawCurrentPeriod.updated,
112
247
  },
113
- totalPeriods: (endTimestamp - startTimestamp) / ONE_WEEK,
114
- periodLeft: Number(rc.periodLeft),
115
248
  status: {
116
249
  voteOpen: endTimestamp - ONE_WEEK > currentTimestamp,
117
250
  voteClosed: currentTimestamp > endTimestamp - ONE_WEEK,
@@ -0,0 +1,50 @@
1
+ import { arbitrum } from '@stake-dao/constants'
2
+ import { chunk } from 'lodash-es'
3
+ import { encodeAbiParameters, formatUnits, parseAbiParameters } from 'viem'
4
+ import batchClaimableData from '../bytecodes/votemarket/batchClaimableData.js'
5
+ import { concatBytecode } from '../index.js'
6
+ import { batchJsonRpc } from '../utils.js'
7
+
8
+ type Rpcs = {
9
+ [chainId: number]: string
10
+ }
11
+
12
+ const CLAIMABLE_CHUNK_SIZE = 10
13
+
14
+ export const fetchClaimableData = async (
15
+ rpc: Rpcs,
16
+ platform: string,
17
+ user: string,
18
+ campaigns: number[],
19
+ userVotes: any[][],
20
+ ) => {
21
+ const inputCampaignsChunks = chunk(campaigns, CLAIMABLE_CHUNK_SIZE)
22
+ const inputVotesChunks = chunk(userVotes, CLAIMABLE_CHUNK_SIZE)
23
+
24
+ const claimableCalls = inputCampaignsChunks.map((campaignsArgs, index) => {
25
+ const inputData = encodeAbiParameters(parseAbiParameters(batchClaimableData.inputType[arbitrum.id]!) as any, [
26
+ platform,
27
+ user,
28
+ campaignsArgs,
29
+ inputVotesChunks[index],
30
+ ])
31
+ return concatBytecode(batchClaimableData.bytecode[arbitrum.id]!, inputData)
32
+ })
33
+
34
+ const claimable = await batchJsonRpc({
35
+ rpc: rpc[arbitrum.id]!,
36
+ calls: claimableCalls,
37
+ outputTypeAbi: batchClaimableData.outputTypeHr[arbitrum.id],
38
+ callsKey: `Batch claimable data - chainId ${arbitrum.id} - rpc ${rpc[arbitrum.id]}`,
39
+ })
40
+
41
+ return claimable.map((c) => ({
42
+ campaignId: Number(c.campaignId),
43
+ claimInfo: c.claimInfo.map((ci) => ({
44
+ ...ci,
45
+ epoch: Number(ci.epoch),
46
+ blockNumber: Number(ci.blockNumber),
47
+ claimable: formatUnits(ci.claimable, 0),
48
+ })),
49
+ }))
50
+ }
@@ -0,0 +1,48 @@
1
+ import { chunk } from 'lodash-es'
2
+ import { encodeAbiParameters, parseAbiParameters } from 'viem'
3
+ import { mainnet } from 'viem/chains'
4
+ import batchVotes from '../bytecodes/votemarket/batchVotes.js'
5
+ import { concatBytecode } from '../index.js'
6
+ import { batchJsonRpcWithBlocks } from '../utils.js'
7
+
8
+ type Rpcs = {
9
+ [chainId: number]: string
10
+ }
11
+
12
+ export const fetchEpochsVotes = async (rpc: Rpcs, gaugeController: string, user: string, epochs: number[]) => {
13
+ const blocksForEpoch = (
14
+ await Promise.allSettled(
15
+ epochs.map((epoch) => fetch(`https://coins.llama.fi/block/ethereum/${epoch}`).then((res) => res.json())),
16
+ )
17
+ ).map((r) => (r.status === 'fulfilled' ? Number(r.value.height) : 0))
18
+
19
+ if (blocksForEpoch.includes(0)) {
20
+ throw Error(`Block not found for epoch at timestamp ${epochs[blocksForEpoch.indexOf(0)]}`)
21
+ }
22
+
23
+ const inputData = encodeAbiParameters(parseAbiParameters(batchVotes.inputType[mainnet.id]!) as any, [
24
+ gaugeController,
25
+ user,
26
+ ])
27
+ const call = concatBytecode(batchVotes.bytecode[mainnet.id]!, inputData)
28
+
29
+ const blocksChunks = chunk(blocksForEpoch, 10)
30
+ const votes = (
31
+ await Promise.all(
32
+ blocksChunks.map((b) =>
33
+ batchJsonRpcWithBlocks({
34
+ rpc: rpc[1]!,
35
+ call,
36
+ outputTypeAbi: batchVotes.outputTypeHr[mainnet.id],
37
+ callsKey: `Batch block votes data - chainId ${mainnet.id} - rpc ${rpc[mainnet.id]}`,
38
+ blocks: b,
39
+ }),
40
+ ),
41
+ )
42
+ ).flat()
43
+
44
+ return votes.map((v, index) => ({
45
+ votes: v,
46
+ epoch: epochs[index],
47
+ }))
48
+ }
@@ -0,0 +1,53 @@
1
+ export const GaugeControllerConstants = {
2
+ GAUGES_SLOTS: {
3
+ curve: {
4
+ pointWeights: 12,
5
+ lastUserVote: 11,
6
+ voteUserSlope: 9,
7
+ },
8
+ balancer: {
9
+ pointWeights: 1000000008,
10
+ lastUserVote: 1000000007,
11
+ voteUserSlope: 1000000005,
12
+ },
13
+ frax: {
14
+ pointWeights: 10000000011,
15
+ lastUserVote: 1000000010,
16
+ voteUserSlope: 1000000008,
17
+ },
18
+ fxn: {
19
+ pointWeights: 10000000011,
20
+ lastUserVote: 1000000010,
21
+ voteUserSlope: 1000000008,
22
+ },
23
+ },
24
+ GAUGE_CONTROLLER: {
25
+ curve: '0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB',
26
+ balancer: '0xC128468b7Ce63eA702C1f104D55A2566b13D3ABD',
27
+ frax: '0x3669C421b77340B2979d1A00a792CC2ee0FcE737',
28
+ fxn: '0xe60eB8098B34eD775ac44B1ddE864e098C6d7f37',
29
+ },
30
+ }
31
+
32
+ export const BLOCK_HEADER = [
33
+ 'parentHash',
34
+ 'sha3Uncles',
35
+ 'miner',
36
+ 'stateRoot',
37
+ 'transactionsRoot',
38
+ 'receiptsRoot',
39
+ 'logsBloom',
40
+ 'difficulty',
41
+ 'number',
42
+ 'gasLimit',
43
+ 'gasUsed',
44
+ 'timestamp',
45
+ 'extraData',
46
+ 'mixHash',
47
+ 'nonce',
48
+ 'baseFeePerGas',
49
+ 'withdrawalsRoot',
50
+ 'blobGasUsed',
51
+ 'excessBlobGas',
52
+ 'parentBeaconBlockRoot',
53
+ ]
@@ -0,0 +1,48 @@
1
+ import { http, createPublicClient, toHex, toRlp } from 'viem'
2
+ import { mainnet } from 'viem/chains'
3
+ import { BLOCK_HEADER } from './config.js'
4
+ import { generateGaugeProof } from './getGaugeProof.js'
5
+
6
+ type Rpcs = {
7
+ [chainId: number]: string
8
+ }
9
+
10
+ function encodeRlpBlock(block: Record<string, any>): string {
11
+ const blockHeader = BLOCK_HEADER.map((key) => {
12
+ if (key in block) {
13
+ if (typeof block[key] === 'bigint' && block[key] === BigInt(0)) {
14
+ return '0x'
15
+ }
16
+ if (typeof block[key] === 'string') {
17
+ return block[key]
18
+ }
19
+ return toHex(block[key])
20
+ }
21
+ return undefined
22
+ }).filter((value): value is `0x${string}` => value !== undefined)
23
+
24
+ return toRlp(blockHeader)
25
+ }
26
+
27
+ // Main function to generate block proof
28
+ export async function generateBlockProof(
29
+ rpc: Rpcs,
30
+ protocol: string,
31
+ gaugeAddress: `0x${string}`,
32
+ currentPeriod: bigint,
33
+ blockNumber: bigint,
34
+ ): Promise<[string, string]> {
35
+ const publicClient = createPublicClient({
36
+ chain: mainnet,
37
+ transport: http(rpc[mainnet.id]),
38
+ })
39
+
40
+ const block = await publicClient.getBlock({
41
+ blockNumber,
42
+ })
43
+
44
+ const blockHeaderRlp = encodeRlpBlock(block)
45
+
46
+ const [proof, _] = await generateGaugeProof(rpc, protocol, gaugeAddress, currentPeriod, blockNumber)
47
+ return [blockHeaderRlp, proof]
48
+ }