@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.
- package/dist/esm/bytecodes/votemarket/batchCampaigns.js +6 -3
- package/dist/esm/bytecodes/votemarket/batchCampaigns.js.map +1 -1
- package/dist/esm/bytecodes/votemarket/batchClaimableData.js +21 -0
- package/dist/esm/bytecodes/votemarket/batchClaimableData.js.map +1 -0
- package/dist/esm/bytecodes/votemarket/batchGaugesWeight.js +13 -0
- package/dist/esm/bytecodes/votemarket/batchGaugesWeight.js.map +1 -0
- package/dist/esm/bytecodes/votemarket/batchVotes.js +13 -0
- package/dist/esm/bytecodes/votemarket/batchVotes.js.map +1 -0
- package/dist/esm/bytecodes/votemarket/curve/batchCurveGaugesMetadata.js +5 -3
- package/dist/esm/bytecodes/votemarket/curve/batchCurveGaugesMetadata.js.map +1 -1
- package/dist/esm/bytecodes/votemarket/curve/batchCurveOldLps.js +3 -2
- package/dist/esm/bytecodes/votemarket/curve/batchCurveOldLps.js.map +1 -1
- package/dist/esm/endpoints.js +0 -1
- package/dist/esm/endpoints.js.map +1 -1
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lockers/fetch/index.js +10 -0
- package/dist/esm/lockers/fetch/index.js.map +1 -1
- package/dist/esm/lockers/utils/getHttpCalls.js +8 -0
- package/dist/esm/lockers/utils/getHttpCalls.js.map +1 -1
- package/dist/esm/sdt/fetch.js +20 -26
- package/dist/esm/sdt/fetch.js.map +1 -1
- package/dist/esm/strategies/pancakeswap/endpoints.js +1 -1
- package/dist/esm/tsconfig.build.tsbuildinfo +1 -1
- package/dist/esm/utils.js +43 -19
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/votemarket/curve/config.js +4 -1
- package/dist/esm/votemarket/curve/config.js.map +1 -1
- package/dist/esm/votemarket/curve/fetchCurveGauges.js +6 -1
- package/dist/esm/votemarket/curve/fetchCurveGauges.js.map +1 -1
- package/dist/esm/votemarket/curve/fetchSnapshotUserData.js +14 -9
- package/dist/esm/votemarket/curve/fetchSnapshotUserData.js.map +1 -1
- package/dist/esm/votemarket/fetchCampaigns.js +115 -14
- package/dist/esm/votemarket/fetchCampaigns.js.map +1 -1
- package/dist/esm/votemarket/fetchClaimableData.js +36 -0
- package/dist/esm/votemarket/fetchClaimableData.js.map +1 -0
- package/dist/esm/votemarket/fetchEpochVotes.js +30 -0
- package/dist/esm/votemarket/fetchEpochVotes.js.map +1 -0
- package/dist/esm/votemarket/generateProofs/config.js +53 -0
- package/dist/esm/votemarket/generateProofs/config.js.map +1 -0
- package/dist/esm/votemarket/generateProofs/getBlockProof.js +33 -0
- package/dist/esm/votemarket/generateProofs/getBlockProof.js.map +1 -0
- package/dist/esm/votemarket/generateProofs/getGaugeProof.js +50 -0
- package/dist/esm/votemarket/generateProofs/getGaugeProof.js.map +1 -0
- package/dist/esm/votemarket/generateProofs/getUserProof.js +49 -0
- package/dist/esm/votemarket/generateProofs/getUserProof.js.map +1 -0
- package/dist/types/bytecodes/votemarket/batchCampaigns.d.ts.map +1 -1
- package/dist/types/bytecodes/votemarket/batchClaimableData.d.ts +4 -0
- package/dist/types/bytecodes/votemarket/batchClaimableData.d.ts.map +1 -0
- package/dist/types/bytecodes/votemarket/batchGaugesWeight.d.ts +4 -0
- package/dist/types/bytecodes/votemarket/batchGaugesWeight.d.ts.map +1 -0
- package/dist/types/bytecodes/votemarket/batchVotes.d.ts +4 -0
- package/dist/types/bytecodes/votemarket/batchVotes.d.ts.map +1 -0
- package/dist/types/bytecodes/votemarket/curve/batchCurveGaugesMetadata.d.ts.map +1 -1
- package/dist/types/bytecodes/votemarket/curve/batchCurveOldLps.d.ts.map +1 -1
- package/dist/types/endpoints.d.ts +0 -1
- package/dist/types/endpoints.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lockers/fetch/index.d.ts.map +1 -1
- package/dist/types/lockers/utils/getHttpCalls.d.ts +3 -0
- package/dist/types/lockers/utils/getHttpCalls.d.ts.map +1 -1
- package/dist/types/sdt/fetch.d.ts +2 -2
- package/dist/types/sdt/fetch.d.ts.map +1 -1
- package/dist/types/utils.d.ts +13 -1
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/types/votemarket/curve/config.d.ts +8 -1
- package/dist/types/votemarket/curve/config.d.ts.map +1 -1
- package/dist/types/votemarket/curve/fetchCurveGauges.d.ts.map +1 -1
- package/dist/types/votemarket/curve/fetchSnapshotUserData.d.ts +1 -0
- package/dist/types/votemarket/curve/fetchSnapshotUserData.d.ts.map +1 -1
- package/dist/types/votemarket/fetchCampaigns.d.ts.map +1 -1
- package/dist/types/votemarket/fetchClaimableData.d.ts +6 -0
- package/dist/types/votemarket/fetchClaimableData.d.ts.map +1 -0
- package/dist/types/votemarket/fetchEpochVotes.d.ts +9 -0
- package/dist/types/votemarket/fetchEpochVotes.d.ts.map +1 -0
- package/dist/types/votemarket/generateProofs/config.d.ts +32 -0
- package/dist/types/votemarket/generateProofs/config.d.ts.map +1 -0
- package/dist/types/votemarket/generateProofs/getBlockProof.d.ts +6 -0
- package/dist/types/votemarket/generateProofs/getBlockProof.d.ts.map +1 -0
- package/dist/types/votemarket/generateProofs/getGaugeProof.d.ts +6 -0
- package/dist/types/votemarket/generateProofs/getGaugeProof.d.ts.map +1 -0
- package/dist/types/votemarket/generateProofs/getUserProof.d.ts +6 -0
- package/dist/types/votemarket/generateProofs/getUserProof.d.ts.map +1 -0
- package/dist/types/votemarket/types.d.ts +31 -8
- package/dist/types/votemarket/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/bytecodes/votemarket/batchCampaigns.ts +6 -3
- package/src/bytecodes/votemarket/batchClaimableData.ts +24 -0
- package/src/bytecodes/votemarket/batchGaugesWeight.ts +15 -0
- package/src/bytecodes/votemarket/batchVotes.ts +15 -0
- package/src/bytecodes/votemarket/curve/batchCurveGaugesMetadata.ts +5 -3
- package/src/bytecodes/votemarket/curve/batchCurveOldLps.ts +3 -2
- package/src/endpoints.ts +0 -1
- package/src/index.ts +10 -0
- package/src/lockers/fetch/index.ts +13 -0
- package/src/lockers/utils/getHttpCalls.ts +8 -0
- package/src/sdt/fetch.ts +29 -27
- package/src/strategies/pancakeswap/endpoints.ts +1 -1
- package/src/utils.ts +66 -19
- package/src/votemarket/curve/config.ts +5 -1
- package/src/votemarket/curve/fetchCurveGauges.ts +15 -2
- package/src/votemarket/curve/fetchSnapshotUserData.ts +16 -9
- package/src/votemarket/fetchCampaigns.ts +148 -15
- package/src/votemarket/fetchClaimableData.ts +50 -0
- package/src/votemarket/fetchEpochVotes.ts +48 -0
- package/src/votemarket/generateProofs/config.ts +53 -0
- package/src/votemarket/generateProofs/getBlockProof.ts +48 -0
- package/src/votemarket/generateProofs/getGaugeProof.ts +75 -0
- package/src/votemarket/generateProofs/getUserProof.ts +74 -0
- 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 =
|
|
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 {
|
|
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
|
-
|
|
52
|
+
state
|
|
53
|
+
created
|
|
54
|
+
scores
|
|
52
55
|
space {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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 {
|
|
8
|
-
import
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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(
|
|
109
|
-
rewardPerVote: formatUnits(
|
|
110
|
-
leftover: formatUnits(
|
|
111
|
-
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
|
+
}
|