@stake-dao/reader 0.4.57 → 0.4.59
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/batchUserVotes.js +39 -0
- package/dist/esm/bytecodes/votemarket/batchUserVotes.js.map +1 -0
- package/dist/esm/endpoints.js +1 -0
- package/dist/esm/endpoints.js.map +1 -1
- package/dist/esm/index.js +7 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lockers/fetch/index.js +1 -1
- package/dist/esm/lockers/fetch/index.js.map +1 -1
- package/dist/esm/tsconfig.build.tsbuildinfo +1 -1
- package/dist/esm/votemarket/balancer/fetchBalancerUserVotes.js +42 -0
- package/dist/esm/votemarket/balancer/fetchBalancerUserVotes.js.map +1 -0
- package/dist/esm/votemarket/curve/fetchCurveUserVotes.js +8 -3
- package/dist/esm/votemarket/curve/fetchCurveUserVotes.js.map +1 -1
- package/dist/esm/votemarket/curve/fetchUserVlCvxClaimable.js +90 -40
- package/dist/esm/votemarket/curve/fetchUserVlCvxClaimable.js.map +1 -1
- package/dist/esm/votemarket/fetchCampaigns/index.js +65 -0
- package/dist/esm/votemarket/fetchCampaigns/index.js.map +1 -0
- package/dist/esm/votemarket/{fetchPendingRemoteCampaigns.js → fetchCampaigns/pendingRemote.js} +7 -2
- package/dist/esm/votemarket/fetchCampaigns/pendingRemote.js.map +1 -0
- package/dist/esm/votemarket/{fetchCampaigns.js → fetchCampaigns/processRawCampaigns.js} +16 -101
- package/dist/esm/votemarket/fetchCampaigns/processRawCampaigns.js.map +1 -0
- package/dist/esm/votemarket/fetchCampaigns/updateEpoch.js +41 -0
- package/dist/esm/votemarket/fetchCampaigns/updateEpoch.js.map +1 -0
- package/dist/esm/votemarket/fxn/fetchFxnUserVotes.js +42 -0
- package/dist/esm/votemarket/fxn/fetchFxnUserVotes.js.map +1 -0
- package/dist/types/bytecodes/votemarket/batchUserVotes.d.ts +4 -0
- package/dist/types/bytecodes/votemarket/batchUserVotes.d.ts.map +1 -0
- package/dist/types/endpoints.d.ts +1 -0
- package/dist/types/endpoints.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lockers/fetch/index.d.ts +1 -0
- package/dist/types/lockers/fetch/index.d.ts.map +1 -1
- package/dist/types/votemarket/balancer/fetchBalancerUserVotes.d.ts +16 -0
- package/dist/types/votemarket/balancer/fetchBalancerUserVotes.d.ts.map +1 -0
- package/dist/types/votemarket/curve/fetchCurveUserVotes.d.ts.map +1 -1
- package/dist/types/votemarket/curve/fetchUserVlCvxClaimable.d.ts +17 -1
- package/dist/types/votemarket/curve/fetchUserVlCvxClaimable.d.ts.map +1 -1
- package/dist/types/votemarket/{fetchCampaigns.d.ts → fetchCampaigns/index.d.ts} +2 -2
- package/dist/types/votemarket/fetchCampaigns/index.d.ts.map +1 -0
- package/dist/types/votemarket/{fetchPendingRemoteCampaigns.d.ts → fetchCampaigns/pendingRemote.d.ts} +4 -2
- package/dist/types/votemarket/fetchCampaigns/pendingRemote.d.ts.map +1 -0
- package/dist/types/votemarket/fetchCampaigns/processRawCampaigns.d.ts +16 -0
- package/dist/types/votemarket/fetchCampaigns/processRawCampaigns.d.ts.map +1 -0
- package/dist/types/votemarket/fetchCampaigns/updateEpoch.d.ts +9 -0
- package/dist/types/votemarket/fetchCampaigns/updateEpoch.d.ts.map +1 -0
- package/dist/types/votemarket/fxn/fetchFxnUserVotes.d.ts +16 -0
- package/dist/types/votemarket/fxn/fetchFxnUserVotes.d.ts.map +1 -0
- package/dist/types/votemarket/types.d.ts +3 -1
- package/dist/types/votemarket/types.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/bytecodes/votemarket/batchUserVotes.ts +41 -0
- package/src/endpoints.ts +2 -0
- package/src/index.ts +9 -2
- package/src/lockers/fetch/index.ts +1 -1
- package/src/votemarket/balancer/fetchBalancerUserVotes.ts +61 -0
- package/src/votemarket/curve/fetchCurveUserVotes.ts +9 -4
- package/src/votemarket/curve/fetchUserVlCvxClaimable.ts +114 -44
- package/src/votemarket/fetchCampaigns/index.ts +98 -0
- package/src/votemarket/{fetchPendingRemoteCampaigns.ts → fetchCampaigns/pendingRemote.ts} +7 -0
- package/src/votemarket/{fetchCampaigns.ts → fetchCampaigns/processRawCampaigns.ts} +20 -150
- package/src/votemarket/fetchCampaigns/updateEpoch.ts +63 -0
- package/src/votemarket/fxn/fetchFxnUserVotes.ts +61 -0
- package/src/votemarket/types.ts +3 -1
- package/dist/esm/bytecodes/votemarket/curve/batchCurveUserVotes.js +0 -39
- package/dist/esm/bytecodes/votemarket/curve/batchCurveUserVotes.js.map +0 -1
- package/dist/esm/votemarket/fetchCampaigns.js.map +0 -1
- package/dist/esm/votemarket/fetchPendingRemoteCampaigns.js.map +0 -1
- package/dist/types/bytecodes/votemarket/curve/batchCurveUserVotes.d.ts +0 -4
- package/dist/types/bytecodes/votemarket/curve/batchCurveUserVotes.d.ts.map +0 -1
- package/dist/types/votemarket/fetchCampaigns.d.ts.map +0 -1
- package/dist/types/votemarket/fetchPendingRemoteCampaigns.d.ts.map +0 -1
- package/src/bytecodes/votemarket/curve/batchCurveUserVotes.ts +0 -41
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Zero, contract, tokenWithAddress } from '@stake-dao/constants'
|
|
2
2
|
import { type Address, formatUnits, parseAbi } from 'viem'
|
|
3
3
|
import { mainnet } from 'viem/chains'
|
|
4
|
-
import { GH_STAKE_DAO_BOUNTIES_REPORT } from '../../endpoints.js'
|
|
4
|
+
import { GH_STAKE_DAO_BOUNTIES_REPORT, STAKE_DAO_API } from '../../endpoints.js'
|
|
5
5
|
import { type Price, getPrices } from '../../prices.js'
|
|
6
6
|
import { rpcFetchTokensData } from '../../tokens.js'
|
|
7
7
|
import { equalTlc, multicall } from '../../utils.js'
|
|
@@ -18,44 +18,52 @@ type UnknownToken = {
|
|
|
18
18
|
chainId: number
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const getMerkleData = async () => {
|
|
22
|
-
const [delegators, nonDelegators] = await Promise.all([
|
|
21
|
+
const getMerkleData = async (chainId: number) => {
|
|
22
|
+
const [delegators, nonDelegators, votiumMerkle] = await Promise.all([
|
|
23
|
+
chainId === mainnet.id
|
|
24
|
+
? fetch(
|
|
25
|
+
`${GH_STAKE_DAO_BOUNTIES_REPORT}/refs/heads/main/bounties-reports/latest/vlCVX/vlcvx_merkle_delegators.json`,
|
|
26
|
+
).then((res) => res.json())
|
|
27
|
+
: {},
|
|
23
28
|
fetch(
|
|
24
|
-
`${GH_STAKE_DAO_BOUNTIES_REPORT}/refs/heads/main/bounties-reports/latest/vlCVX/
|
|
29
|
+
`${GH_STAKE_DAO_BOUNTIES_REPORT}/refs/heads/main/bounties-reports/latest/vlCVX/vlcvx_merkle${
|
|
30
|
+
chainId === mainnet.id ? '' : `_${chainId}`
|
|
31
|
+
}.json`,
|
|
25
32
|
).then((res) => res.json()),
|
|
26
|
-
|
|
27
|
-
(res) => res.json()
|
|
28
|
-
|
|
33
|
+
chainId === mainnet.id
|
|
34
|
+
? fetch(`${STAKE_DAO_API}/votemarket/votium_merkles.json`).then((res) => res.json())
|
|
35
|
+
: { claims: {} },
|
|
29
36
|
])
|
|
30
37
|
|
|
31
|
-
return { delegators, nonDelegators }
|
|
38
|
+
return { delegators, nonDelegators, votiumMerkle }
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const validateMerkleRoots = (delegatorRoot: string, nonDelegatorRoot: string, merkleData: any) => {
|
|
38
|
-
const delegatorMatches = isRootMatch(delegatorRoot, merkleData.delegators.merkleRoot)
|
|
39
|
-
const nonDelegatorMatches = isRootMatch(nonDelegatorRoot, merkleData.nonDelegators.merkleRoot)
|
|
41
|
+
const validateMerkleRoots = (delegatorRoot: string, nonDelegatorRoot: string, merkleData: any, isMainnet: boolean) => {
|
|
42
|
+
const delegatorMatches = isMainnet ? equalTlc(delegatorRoot, merkleData.delegators.merkleRoot) : false
|
|
43
|
+
const nonDelegatorMatches = equalTlc(nonDelegatorRoot, merkleData.nonDelegators.merkleRoot)
|
|
40
44
|
return delegatorMatches || nonDelegatorMatches
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
const getMerkleRoots = async (provider: any) => {
|
|
47
|
+
const getMerkleRoots = async (provider: any, user: string) => {
|
|
44
48
|
const contracts = [contract('vlCvxDelegatorMerkle', mainnet.id), contract('vlCvxNotDelegatorMerkle', mainnet.id)]
|
|
45
49
|
|
|
46
50
|
const result = await multicall(
|
|
47
51
|
provider,
|
|
48
|
-
contracts.
|
|
49
|
-
address,
|
|
50
|
-
name: '
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
contracts.flatMap((address) => [
|
|
53
|
+
{ address, name: 'root', params: [] },
|
|
54
|
+
{ address, name: 'recipients', params: [user] },
|
|
55
|
+
]),
|
|
56
|
+
parseAbi([
|
|
57
|
+
'function root() external view returns (bytes32)',
|
|
58
|
+
'function recipients(address) external view returns (address)',
|
|
59
|
+
]),
|
|
54
60
|
)
|
|
55
61
|
|
|
56
62
|
return {
|
|
57
63
|
delegatorRoot: result[0].result,
|
|
58
|
-
|
|
64
|
+
delegatorRecipient: result[1].result,
|
|
65
|
+
nonDelegatorRoot: result[2].result,
|
|
66
|
+
nonDelegatorRecipient: result[3].result,
|
|
59
67
|
}
|
|
60
68
|
}
|
|
61
69
|
|
|
@@ -64,11 +72,7 @@ const getClaimedData = async (provider: any, tokens: string[], user: string, con
|
|
|
64
72
|
tokens.length > 0
|
|
65
73
|
? await multicall(
|
|
66
74
|
provider,
|
|
67
|
-
tokens.
|
|
68
|
-
address: contract,
|
|
69
|
-
name: 'claimed',
|
|
70
|
-
params: [user, t],
|
|
71
|
-
})),
|
|
75
|
+
tokens.flatMap((t) => [{ address: contract, name: 'claimed', params: [user, t] }]),
|
|
72
76
|
parseAbi(['function claimed(address,address) external view returns (uint256)']),
|
|
73
77
|
)
|
|
74
78
|
: []
|
|
@@ -79,12 +83,33 @@ const getClaimedData = async (provider: any, tokens: string[], user: string, con
|
|
|
79
83
|
}))
|
|
80
84
|
}
|
|
81
85
|
|
|
86
|
+
const getVotiumClaimedData = async (provider: any, merkle: any) => {
|
|
87
|
+
const rawClaimed = await multicall(
|
|
88
|
+
provider,
|
|
89
|
+
Object.keys(merkle).map((t) => ({
|
|
90
|
+
address: contract('votiumMerkle'),
|
|
91
|
+
name: 'isClaimed',
|
|
92
|
+
params: [t, merkle[t].index],
|
|
93
|
+
})),
|
|
94
|
+
parseAbi(['function isClaimed(address,uint256) external view returns(bool)']),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return Object.keys(merkle).map((t, index) => ({
|
|
98
|
+
...merkle[t],
|
|
99
|
+
token: t,
|
|
100
|
+
claimed: rawClaimed[index].result,
|
|
101
|
+
}))
|
|
102
|
+
}
|
|
103
|
+
|
|
82
104
|
const getParsedData = (
|
|
105
|
+
user: string,
|
|
106
|
+
recipient: string,
|
|
83
107
|
merkleAddress: string,
|
|
84
108
|
userData: any,
|
|
85
109
|
unknownTokensData: any[],
|
|
86
110
|
claimedData: any[],
|
|
87
111
|
prices: Price[],
|
|
112
|
+
chainId: number,
|
|
88
113
|
) => {
|
|
89
114
|
return userData
|
|
90
115
|
? Object.keys(userData.tokens).map((token) => {
|
|
@@ -95,6 +120,8 @@ const getParsedData = (
|
|
|
95
120
|
const claimable = BigInt(userData.tokens[token].amount) - claimed
|
|
96
121
|
|
|
97
122
|
return {
|
|
123
|
+
user,
|
|
124
|
+
recipient,
|
|
98
125
|
token: rewardToken,
|
|
99
126
|
claimable: formatUnits(claimable, 0),
|
|
100
127
|
claimableUsd: Number(formatUnits(claimable, rewardToken?.decimals || 18)) * tokenPrice,
|
|
@@ -104,71 +131,114 @@ const getParsedData = (
|
|
|
104
131
|
proof: userData.tokens[token].proof,
|
|
105
132
|
merkleAddress: merkleAddress as Address,
|
|
106
133
|
isClaimed: claimable === Zero,
|
|
134
|
+
chainId,
|
|
107
135
|
}
|
|
108
136
|
})
|
|
109
137
|
: undefined
|
|
110
138
|
}
|
|
111
139
|
|
|
112
|
-
const
|
|
140
|
+
const getVotiumParsedData = (user: string, userData: any[], unknownTokensData: any[], prices: Price[]) => {
|
|
141
|
+
return userData
|
|
142
|
+
? userData.map((ud) => {
|
|
143
|
+
const rewardToken = tokenWithAddress(ud.token) || unknownTokensData.find((t) => equalTlc(t.address, ud.token))
|
|
144
|
+
|
|
145
|
+
const tokenPrice = prices.find((p) => equalTlc(ud.token, p.address))?.usdPrice || 0
|
|
113
146
|
|
|
114
|
-
|
|
115
|
-
|
|
147
|
+
return {
|
|
148
|
+
user,
|
|
149
|
+
recipient: user,
|
|
150
|
+
token: rewardToken,
|
|
151
|
+
claimable: ud.claimed ? '0' : formatUnits(BigInt(ud.amount), 0),
|
|
152
|
+
claimableUsd: ud.claimed
|
|
153
|
+
? 0
|
|
154
|
+
: Number(formatUnits(BigInt(ud.amount), rewardToken?.decimals || 18)) * tokenPrice,
|
|
155
|
+
amount: formatUnits(BigInt(ud.amount), 0),
|
|
156
|
+
amountUsd: Number(formatUnits(BigInt(ud.amount), rewardToken?.decimals || 18)) * tokenPrice,
|
|
157
|
+
proof: ud.proof,
|
|
158
|
+
merkleAddress: contract('votiumMerkle'),
|
|
159
|
+
isClaimed: ud.claimed,
|
|
160
|
+
index: ud.index,
|
|
161
|
+
chainId: mainnet.id,
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
: undefined
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export const fetchUserVlCvxClaimable = async (provider: any, rpc: Rpcs, user: string, chainId: number) => {
|
|
168
|
+
const { delegatorRoot, delegatorRecipient, nonDelegatorRoot, nonDelegatorRecipient } = await getMerkleRoots(
|
|
169
|
+
provider,
|
|
170
|
+
user,
|
|
171
|
+
)
|
|
116
172
|
|
|
117
173
|
let vlCvxClaimable: any
|
|
174
|
+
let votiumMerkleData: any
|
|
118
175
|
|
|
119
176
|
try {
|
|
120
|
-
const merkleData = await getMerkleData()
|
|
121
|
-
if (validateMerkleRoots(delegatorRoot, nonDelegatorRoot, merkleData)) {
|
|
122
|
-
vlCvxClaimable = merkleData
|
|
177
|
+
const merkleData = await getMerkleData(chainId)
|
|
178
|
+
if (validateMerkleRoots(delegatorRoot, nonDelegatorRoot, merkleData, chainId === mainnet.id)) {
|
|
179
|
+
vlCvxClaimable = { delegators: merkleData.delegators, nonDelegators: merkleData.nonDelegators }
|
|
123
180
|
}
|
|
181
|
+
votiumMerkleData = merkleData.votiumMerkle.claims[user]
|
|
124
182
|
} catch {}
|
|
125
183
|
|
|
126
|
-
if (!vlCvxClaimable) {
|
|
127
|
-
|
|
184
|
+
if (!vlCvxClaimable && !votiumMerkleData) {
|
|
185
|
+
console.error('Could not find valid merkle data')
|
|
186
|
+
return []
|
|
128
187
|
}
|
|
129
188
|
|
|
130
|
-
const vlCvxUserDelegated = vlCvxClaimable?.
|
|
131
|
-
const vlCvxUserNotDelegated = vlCvxClaimable
|
|
189
|
+
const vlCvxUserDelegated = vlCvxClaimable.delegators?.claims?.[user]
|
|
190
|
+
const vlCvxUserNotDelegated = vlCvxClaimable.nonDelegators.claims[user]
|
|
132
191
|
|
|
133
192
|
const tokensDelegated = Object.keys(vlCvxUserDelegated?.tokens || {})
|
|
134
193
|
const tokensNotDelegated = Object.keys(vlCvxUserNotDelegated?.tokens || {})
|
|
194
|
+
const tokensVotium = Object.keys(votiumMerkleData?.tokens || {})
|
|
135
195
|
|
|
136
|
-
const tokens = [...tokensDelegated, ...tokensNotDelegated]
|
|
196
|
+
const tokens = [...tokensDelegated, ...tokensNotDelegated, ...tokensVotium]
|
|
137
197
|
|
|
138
198
|
if (!tokens.length) return []
|
|
139
199
|
|
|
140
|
-
const [delegatedClaimed, notDelegatedClaimed] = await Promise.all([
|
|
200
|
+
const [delegatedClaimed, notDelegatedClaimed, votiumClaimed] = await Promise.all([
|
|
141
201
|
getClaimedData(provider, tokensDelegated, user, contract('vlCvxDelegatorMerkle')),
|
|
142
202
|
getClaimedData(provider, tokensNotDelegated, user, contract('vlCvxNotDelegatorMerkle')),
|
|
203
|
+
votiumMerkleData?.tokens && Object.keys(votiumMerkleData?.tokens).length > 0
|
|
204
|
+
? getVotiumClaimedData(provider, votiumMerkleData?.tokens)
|
|
205
|
+
: [],
|
|
143
206
|
])
|
|
144
207
|
|
|
145
208
|
const unknownTokens = tokens.filter((t) => !tokenWithAddress(t))
|
|
146
|
-
const rawUnknownTokensData = await rpcFetchTokensData(
|
|
209
|
+
const rawUnknownTokensData = await rpcFetchTokensData(chainId, rpc[chainId]!, unknownTokens)
|
|
147
210
|
const unknownTokensData: UnknownToken[] = rawUnknownTokensData.map((t) => ({
|
|
148
211
|
name: t.name,
|
|
149
212
|
symbol: t.symbol,
|
|
150
213
|
address: t.tokenAddress,
|
|
151
214
|
decimals: Number(t.decimals),
|
|
152
|
-
chainId
|
|
215
|
+
chainId,
|
|
153
216
|
}))
|
|
154
217
|
|
|
155
|
-
const prices = await getPrices(tokens.map((t) => ({ address: t })) as any[],
|
|
218
|
+
const prices = await getPrices(tokens.map((t) => ({ address: t })) as any[], chainId)
|
|
156
219
|
|
|
157
220
|
return [
|
|
158
221
|
getParsedData(
|
|
159
|
-
|
|
222
|
+
user,
|
|
223
|
+
delegatorRecipient,
|
|
224
|
+
contract('vlCvxDelegatorMerkle'),
|
|
160
225
|
vlCvxUserDelegated,
|
|
161
226
|
unknownTokensData,
|
|
162
227
|
delegatedClaimed,
|
|
163
228
|
prices,
|
|
229
|
+
chainId,
|
|
164
230
|
),
|
|
165
231
|
getParsedData(
|
|
166
|
-
|
|
232
|
+
user,
|
|
233
|
+
nonDelegatorRecipient,
|
|
234
|
+
contract('vlCvxNotDelegatorMerkle', chainId),
|
|
167
235
|
vlCvxUserNotDelegated,
|
|
168
236
|
unknownTokensData,
|
|
169
237
|
notDelegatedClaimed,
|
|
170
238
|
prices,
|
|
239
|
+
chainId,
|
|
171
240
|
),
|
|
241
|
+
getVotiumParsedData(user, votiumClaimed, unknownTokensData, prices),
|
|
172
242
|
]
|
|
173
243
|
.filter(Boolean)
|
|
174
244
|
.flat()
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { contract } from '@stake-dao/constants'
|
|
2
|
+
import { range } from 'lodash-es'
|
|
3
|
+
import { decodeAbiParameters, encodeAbiParameters, parseAbiParameters } from 'viem'
|
|
4
|
+
import { arbitrum } from 'viem/chains'
|
|
5
|
+
import batchCampaigns from '../../bytecodes/votemarket/batchCampaigns.js'
|
|
6
|
+
import { batchJsonRpc, concatBytecode, equalTlc, rpcCall } from '../../utils.js'
|
|
7
|
+
import type { Campaign } from '../types.js'
|
|
8
|
+
import { fetchPendingRemoteCampaigns } from './pendingRemote.js'
|
|
9
|
+
import { processRawCampaigns } from './processRawCampaigns.js'
|
|
10
|
+
|
|
11
|
+
export const CAMPAIGNS_CHUNK_SIZE = 10
|
|
12
|
+
export const CLAIM_WINDOW_LENGTH = 24 // weeks
|
|
13
|
+
export const CLOSE_WINDOW_LENGTH = 4 // weeks
|
|
14
|
+
|
|
15
|
+
interface FetchCampaignsProps {
|
|
16
|
+
platform: string
|
|
17
|
+
gaugeController: string
|
|
18
|
+
chainId: number
|
|
19
|
+
rpcs: { [chainId: number]: string }
|
|
20
|
+
etherscanApiKey?: string
|
|
21
|
+
fromBlock?: number
|
|
22
|
+
mainnetVmRemote?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const fetchCampaigns = async ({
|
|
26
|
+
platform,
|
|
27
|
+
gaugeController,
|
|
28
|
+
chainId,
|
|
29
|
+
rpcs,
|
|
30
|
+
etherscanApiKey,
|
|
31
|
+
fromBlock,
|
|
32
|
+
mainnetVmRemote,
|
|
33
|
+
}: FetchCampaignsProps): Promise<Campaign[]> => {
|
|
34
|
+
const rpc = rpcs[chainId]!
|
|
35
|
+
|
|
36
|
+
const nCampaignsRequest = await rpcCall(rpc, [{ to: platform, data: '0x7274e30d' }]) // campaignCount
|
|
37
|
+
const nCampaigns = Number(
|
|
38
|
+
decodeAbiParameters([{ type: 'uint256', name: 'campaignCount' }], nCampaignsRequest[0].result),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
const calls = range(0, nCampaigns, CAMPAIGNS_CHUNK_SIZE).map((skip) => {
|
|
42
|
+
const inputParams = [
|
|
43
|
+
chainId,
|
|
44
|
+
platform,
|
|
45
|
+
contract('laPosteTokenFactory', chainId),
|
|
46
|
+
skip,
|
|
47
|
+
skip + CAMPAIGNS_CHUNK_SIZE > nCampaigns ? nCampaigns : skip + CAMPAIGNS_CHUNK_SIZE,
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
const inputData = encodeAbiParameters(parseAbiParameters(batchCampaigns.inputType[1]!) as any, inputParams)
|
|
51
|
+
return concatBytecode(batchCampaigns.bytecode[1]!, inputData)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
let rawCampaigns = await batchJsonRpc({
|
|
55
|
+
rpc,
|
|
56
|
+
calls,
|
|
57
|
+
outputTypeAbi: batchCampaigns.outputType![1]!,
|
|
58
|
+
parse: false,
|
|
59
|
+
callsKey: `votemarket/fetchCampaigns.ts: rawCampaigns - chainId ${chainId} - rpc ${rpc}`,
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
if (chainId === arbitrum.id && etherscanApiKey && fromBlock && mainnetVmRemote) {
|
|
63
|
+
const nextIndex = rawCampaigns.length > 0 ? Math.max(...rawCampaigns.map((rc) => Number(rc.id))) + 1 : 0
|
|
64
|
+
const { remoteCampaigns: pendingRemoteCampaigns } = await fetchPendingRemoteCampaigns({
|
|
65
|
+
mainnetVmRemote,
|
|
66
|
+
chainId,
|
|
67
|
+
fromBlock,
|
|
68
|
+
etherscanApiKey,
|
|
69
|
+
nextIndex,
|
|
70
|
+
votemarket: platform,
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
rawCampaigns = [
|
|
74
|
+
...rawCampaigns,
|
|
75
|
+
...pendingRemoteCampaigns.filter((rmtC) => {
|
|
76
|
+
const existingCampaign = rawCampaigns.find(
|
|
77
|
+
(rc) =>
|
|
78
|
+
rc.addresses.length === rmtC.addresses.length &&
|
|
79
|
+
rc.addresses.every((rca, index) => equalTlc(rca, rmtC.addresses[index])) &&
|
|
80
|
+
equalTlc(rc.rewardAddress, rmtC.rewardAddress) &&
|
|
81
|
+
rc.rewardChainId === rmtC.rewardChainId &&
|
|
82
|
+
rc.campaign.startTimestamp === rmtC.campaign.startTimestamp &&
|
|
83
|
+
rc.campaign.endTimestamp === rmtC.campaign.endTimestamp &&
|
|
84
|
+
rc.campaign.numberOfPeriods === rmtC.campaign.numberOfPeriods &&
|
|
85
|
+
rc.campaign.maxRewardPerVote === rmtC.campaign.maxRewardPerVote &&
|
|
86
|
+
rc.campaign.totalRewardAmount === rmtC.campaign.totalRewardAmount &&
|
|
87
|
+
equalTlc(rc.campaign.gauge, rmtC.campaign.gauge) &&
|
|
88
|
+
equalTlc(rc.campaign.hook, rmtC.campaign.hook) &&
|
|
89
|
+
equalTlc(rc.campaign.manager, rmtC.campaign.manager),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return typeof existingCampaign === 'undefined'
|
|
93
|
+
}),
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return await processRawCampaigns({ rawCampaigns, platform, chainId, gaugeController, rpcs })
|
|
98
|
+
}
|
|
@@ -2,6 +2,7 @@ import { LA_POSTE_MESSAGE_SENT_EVENT, ONE_WEEK, One, Zero, contract } from '@sta
|
|
|
2
2
|
import { range } from 'lodash-es'
|
|
3
3
|
import { decodeAbiParameters, decodeEventLog, parseAbiParameters, zeroAddress } from 'viem'
|
|
4
4
|
import { mainnet } from 'viem/chains'
|
|
5
|
+
import { equalTlc } from '../../utils.js'
|
|
5
6
|
|
|
6
7
|
export const CAMPAIGNS_CHUNK_SIZE = 10
|
|
7
8
|
export const CLAIM_WINDOW_LENGTH = 24 // weeks
|
|
@@ -13,6 +14,7 @@ interface FetchPendingRemoteCampaignsProps {
|
|
|
13
14
|
fromBlock: number
|
|
14
15
|
etherscanApiKey: string
|
|
15
16
|
nextIndex: number
|
|
17
|
+
votemarket: string
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export const fetchPendingRemoteCampaigns = async ({
|
|
@@ -21,6 +23,7 @@ export const fetchPendingRemoteCampaigns = async ({
|
|
|
21
23
|
fromBlock,
|
|
22
24
|
etherscanApiKey,
|
|
23
25
|
nextIndex,
|
|
26
|
+
votemarket,
|
|
24
27
|
}: FetchPendingRemoteCampaignsProps) => {
|
|
25
28
|
const queryParams = {
|
|
26
29
|
chainid: `${mainnet.id}`,
|
|
@@ -59,6 +62,9 @@ export const fetchPendingRemoteCampaigns = async ({
|
|
|
59
62
|
decodedEvent.args.message.payload,
|
|
60
63
|
)
|
|
61
64
|
|
|
65
|
+
// Skip if the target is not the protocol's votmarket
|
|
66
|
+
if (!equalTlc(payload[0].votemarket, votemarket)) continue
|
|
67
|
+
|
|
62
68
|
// Create Campaign (actionType === 0)
|
|
63
69
|
if (payload[0].actionType === Zero) {
|
|
64
70
|
const decodedParams = decodeAbiParameters(
|
|
@@ -131,6 +137,7 @@ export const fetchPendingRemoteCampaigns = async ({
|
|
|
131
137
|
})),
|
|
132
138
|
rewardAddress: campaignParams.rewardToken,
|
|
133
139
|
rewardChainId: BigInt(mainnet.id),
|
|
140
|
+
eventTimestamp: element.eventTimestamp,
|
|
134
141
|
}
|
|
135
142
|
|
|
136
143
|
nextIndex++
|
|
@@ -1,96 +1,26 @@
|
|
|
1
|
-
import { ONE_WEEK, Zero,
|
|
2
|
-
import { chunk, filter,
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import batchCampaignsRestrictedVotes from '../bytecodes/votemarket/batchCampaignsRestrictedVotes.js'
|
|
14
|
-
import batchGaugesWeight from '../bytecodes/votemarket/batchGaugesWeight.js'
|
|
15
|
-
import { getPrices } from '../prices.js'
|
|
16
|
-
import { rpcFetchTokensData } from '../tokens.js'
|
|
17
|
-
import { batchJsonRpc, concatBytecode, equalTlc, rpcCall, rpcGetLastBlockTimetstamp } from '../utils.js'
|
|
18
|
-
import campaignsUnderlyingTokens from './campaignsUnderlyingTokens.js'
|
|
19
|
-
import { fetchPendingRemoteCampaigns } from './fetchPendingRemoteCampaigns.js'
|
|
20
|
-
import type { Campaign, RawCampaign, RawPeriod } from './types.js'
|
|
1
|
+
import { ONE_WEEK, Zero, tokenWithAddress, tokens } from '@stake-dao/constants'
|
|
2
|
+
import { chunk, filter, remove, uniq } from 'lodash-es'
|
|
3
|
+
import { encodeAbiParameters, formatEther, formatUnits, parseAbiParameters } from 'viem'
|
|
4
|
+
import { mainnet } from 'viem/chains'
|
|
5
|
+
import batchCampaignsRestrictedVotes from '../../bytecodes/votemarket/batchCampaignsRestrictedVotes.js'
|
|
6
|
+
import batchGaugesWeight from '../../bytecodes/votemarket/batchGaugesWeight.js'
|
|
7
|
+
import { getPrices } from '../../prices.js'
|
|
8
|
+
import { rpcFetchTokensData } from '../../tokens.js'
|
|
9
|
+
import { batchJsonRpc, concatBytecode, equalTlc, lc, rpcGetLastBlockTimetstamp } from '../../utils.js'
|
|
10
|
+
import campaignsUnderlyingTokens from '../campaignsUnderlyingTokens.js'
|
|
11
|
+
import type { Campaign } from '../types.js'
|
|
12
|
+
import { updateEpoch } from './updateEpoch.js'
|
|
21
13
|
|
|
22
14
|
export const CAMPAIGNS_CHUNK_SIZE = 10
|
|
23
15
|
export const CLAIM_WINDOW_LENGTH = 24 // weeks
|
|
24
16
|
export const CLOSE_WINDOW_LENGTH = 4 // weeks
|
|
25
17
|
|
|
26
|
-
interface
|
|
18
|
+
interface ProcessRawCampaignsProps {
|
|
19
|
+
rawCampaigns: any[]
|
|
27
20
|
platform: string
|
|
28
21
|
gaugeController: string
|
|
29
22
|
chainId: number
|
|
30
23
|
rpcs: { [chainId: number]: string }
|
|
31
|
-
etherscanApiKey?: string
|
|
32
|
-
fromBlock?: number
|
|
33
|
-
mainnetVmRemote?: string
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const updateEpoch = ({
|
|
37
|
-
period,
|
|
38
|
-
previousPeriod,
|
|
39
|
-
campaign,
|
|
40
|
-
totalVotes,
|
|
41
|
-
remainingPeriods,
|
|
42
|
-
}: {
|
|
43
|
-
period: RawPeriod
|
|
44
|
-
previousPeriod?: RawPeriod
|
|
45
|
-
campaign: RawCampaign
|
|
46
|
-
totalVotes: bigint
|
|
47
|
-
remainingPeriods: number
|
|
48
|
-
}) => {
|
|
49
|
-
// -- _checkForUpgrade is handled in solidity BatchCampaigns.sol
|
|
50
|
-
// -- If not first period, check if previousPeriod is updated
|
|
51
|
-
if (!period.updated && (previousPeriod?.updated || typeof previousPeriod === 'undefined')) {
|
|
52
|
-
// Calculate remaining periods and total reward.
|
|
53
|
-
const totalRewardForRemainingPeriods: bigint = campaign.totalRewardAmount - campaign.totalDistributed
|
|
54
|
-
|
|
55
|
-
// -- Update the period data.
|
|
56
|
-
const newRewardPerPeriod =
|
|
57
|
-
remainingPeriods > 0 ? totalRewardForRemainingPeriods / BigInt(remainingPeriods) : totalRewardForRemainingPeriods
|
|
58
|
-
period.rewardPerPeriod = newRewardPerPeriod
|
|
59
|
-
|
|
60
|
-
// -- Update the reward per vote
|
|
61
|
-
// If no votes, rollover the leftover to the next epoch.
|
|
62
|
-
if (totalVotes === Zero) {
|
|
63
|
-
period.leftover = period.rewardPerPeriod
|
|
64
|
-
} else {
|
|
65
|
-
// Calculate reward per vote
|
|
66
|
-
let rewardPerVote = (newRewardPerPeriod * BigInt(1e18)) / totalVotes
|
|
67
|
-
|
|
68
|
-
// Cap reward per vote at max reward per vote
|
|
69
|
-
if (rewardPerVote > campaign.maxRewardPerVote) {
|
|
70
|
-
rewardPerVote = campaign.maxRewardPerVote
|
|
71
|
-
|
|
72
|
-
// Calculate leftover rewards
|
|
73
|
-
const leftOver = newRewardPerPeriod - (rewardPerVote * totalVotes) / BigInt(1e18)
|
|
74
|
-
|
|
75
|
-
// Handle leftover rewards
|
|
76
|
-
if (campaign.hook === zeroAddress) {
|
|
77
|
-
// Store leftover in the period if there is no hook.
|
|
78
|
-
period.leftover = period.leftover + leftOver
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Save the calculated reward per vote.
|
|
83
|
-
period.rewardPerVote = rewardPerVote
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// -- Update the total distributed amount
|
|
87
|
-
campaign.totalDistributed = campaign.totalDistributed + newRewardPerPeriod - period.leftover
|
|
88
|
-
|
|
89
|
-
// -- Mark the period as updated
|
|
90
|
-
period.updated = true
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return campaign
|
|
94
24
|
}
|
|
95
25
|
|
|
96
26
|
const formatPeriod = (rawPeriod: any, decimals: number) => ({
|
|
@@ -100,77 +30,15 @@ const formatPeriod = (rawPeriod: any, decimals: number) => ({
|
|
|
100
30
|
updated: rawPeriod.updated,
|
|
101
31
|
})
|
|
102
32
|
|
|
103
|
-
export const
|
|
33
|
+
export const processRawCampaigns = async ({
|
|
34
|
+
rawCampaigns,
|
|
104
35
|
platform,
|
|
105
36
|
gaugeController,
|
|
106
37
|
chainId,
|
|
107
38
|
rpcs,
|
|
108
|
-
|
|
109
|
-
fromBlock,
|
|
110
|
-
mainnetVmRemote,
|
|
111
|
-
}: FetchCampaignsProps): Promise<Campaign[]> => {
|
|
39
|
+
}: ProcessRawCampaignsProps): Promise<Campaign[]> => {
|
|
112
40
|
const rpc = rpcs[chainId]!
|
|
113
|
-
|
|
114
41
|
const block = await rpcGetLastBlockTimetstamp(rpc)
|
|
115
|
-
const nCampaignsRequest = await rpcCall(rpc, [{ to: platform, data: '0x7274e30d' }]) // campaignCount
|
|
116
|
-
const nCampaigns = Number(
|
|
117
|
-
decodeAbiParameters([{ type: 'uint256', name: 'campaignCount' }], nCampaignsRequest[0].result),
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
const calls = range(0, nCampaigns, CAMPAIGNS_CHUNK_SIZE).map((skip) => {
|
|
121
|
-
const inputParams = [
|
|
122
|
-
chainId,
|
|
123
|
-
platform,
|
|
124
|
-
contract('laPosteTokenFactory', chainId),
|
|
125
|
-
skip,
|
|
126
|
-
skip + CAMPAIGNS_CHUNK_SIZE > nCampaigns ? nCampaigns : skip + CAMPAIGNS_CHUNK_SIZE,
|
|
127
|
-
]
|
|
128
|
-
|
|
129
|
-
const inputData = encodeAbiParameters(parseAbiParameters(batchCampaigns.inputType[1]!) as any, inputParams)
|
|
130
|
-
return concatBytecode(batchCampaigns.bytecode[1]!, inputData)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
let rawCampaigns = await batchJsonRpc({
|
|
134
|
-
rpc,
|
|
135
|
-
calls,
|
|
136
|
-
outputTypeAbi: batchCampaigns.outputType![1]!,
|
|
137
|
-
parse: false,
|
|
138
|
-
callsKey: `votemarket/fetchCampaigns.ts: rawCampaigns - chainId ${chainId} - rpc ${rpc}`,
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
if (chainId === arbitrum.id && etherscanApiKey && fromBlock && mainnetVmRemote) {
|
|
142
|
-
const nextIndex = rawCampaigns.length > 0 ? Math.max(...rawCampaigns.map((rc) => Number(rc.id))) + 1 : 0
|
|
143
|
-
const { remoteCampaigns: pendingRemoteCampaigns } = await fetchPendingRemoteCampaigns({
|
|
144
|
-
mainnetVmRemote,
|
|
145
|
-
chainId,
|
|
146
|
-
fromBlock,
|
|
147
|
-
etherscanApiKey,
|
|
148
|
-
nextIndex,
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
rawCampaigns = [
|
|
152
|
-
...rawCampaigns,
|
|
153
|
-
...pendingRemoteCampaigns.filter((rmtC) => {
|
|
154
|
-
const existingCampaign = rawCampaigns.find(
|
|
155
|
-
(rc) =>
|
|
156
|
-
rc.addresses.length === rmtC.addresses.length &&
|
|
157
|
-
rc.addresses.every((rca, index) => equalTlc(rca, rmtC.addresses[index])) &&
|
|
158
|
-
equalTlc(rc.rewardAddress, rmtC.rewardAddress) &&
|
|
159
|
-
rc.rewardChainId === rmtC.rewardChainId &&
|
|
160
|
-
rc.campaign.startTimestamp === rmtC.campaign.startTimestamp &&
|
|
161
|
-
rc.campaign.endTimestamp === rmtC.campaign.endTimestamp &&
|
|
162
|
-
rc.campaign.numberOfPeriods === rmtC.campaign.numberOfPeriods &&
|
|
163
|
-
rc.campaign.maxRewardPerVote === rmtC.campaign.maxRewardPerVote &&
|
|
164
|
-
rc.campaign.totalRewardAmount === rmtC.campaign.totalRewardAmount &&
|
|
165
|
-
equalTlc(rc.campaign.gauge, rmtC.campaign.gauge) &&
|
|
166
|
-
equalTlc(rc.campaign.hook, rmtC.campaign.hook) &&
|
|
167
|
-
equalTlc(rc.campaign.manager, rmtC.campaign.manager),
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
return typeof existingCampaign === 'undefined'
|
|
171
|
-
}),
|
|
172
|
-
]
|
|
173
|
-
}
|
|
174
42
|
|
|
175
43
|
const callsRestrictedVotes = rawCampaigns
|
|
176
44
|
.filter((rc) => rc.addresses.length > 0)
|
|
@@ -200,7 +68,7 @@ export const fetchCampaigns = async ({
|
|
|
200
68
|
concatBytecode(
|
|
201
69
|
batchGaugesWeight.bytecode[mainnet.id]!,
|
|
202
70
|
encodeAbiParameters(parseAbiParameters(batchGaugesWeight.inputType[mainnet.id]!) as any, [
|
|
203
|
-
|
|
71
|
+
gaugeController,
|
|
204
72
|
campaignsGauges,
|
|
205
73
|
]),
|
|
206
74
|
),
|
|
@@ -324,7 +192,9 @@ export const fetchCampaigns = async ({
|
|
|
324
192
|
)
|
|
325
193
|
|
|
326
194
|
return {
|
|
195
|
+
key: `${chainId}-${lc(platform.slice(2, 6))}-${lc(platform.slice(-4))}-${Number(rc.id)}`,
|
|
327
196
|
id: Number(rc.id),
|
|
197
|
+
platform,
|
|
328
198
|
chainId,
|
|
329
199
|
rewardChainId: Number(rc.rewardChainId),
|
|
330
200
|
gaugeChainId: Number(rc.campaign.chainId),
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Zero } from '@stake-dao/constants'
|
|
2
|
+
import { zeroAddress } from 'viem'
|
|
3
|
+
import type { RawCampaign, RawPeriod } from '../types.js'
|
|
4
|
+
|
|
5
|
+
export const updateEpoch = ({
|
|
6
|
+
period,
|
|
7
|
+
previousPeriod,
|
|
8
|
+
campaign,
|
|
9
|
+
totalVotes,
|
|
10
|
+
remainingPeriods,
|
|
11
|
+
}: {
|
|
12
|
+
period: RawPeriod
|
|
13
|
+
previousPeriod?: RawPeriod
|
|
14
|
+
campaign: RawCampaign
|
|
15
|
+
totalVotes: bigint
|
|
16
|
+
remainingPeriods: number
|
|
17
|
+
}) => {
|
|
18
|
+
// -- _checkForUpgrade is handled in solidity BatchCampaigns.sol
|
|
19
|
+
// -- If not first period, check if previousPeriod is updated
|
|
20
|
+
if (!period.updated && (previousPeriod?.updated || typeof previousPeriod === 'undefined')) {
|
|
21
|
+
// Calculate remaining periods and total reward.
|
|
22
|
+
const totalRewardForRemainingPeriods: bigint = campaign.totalRewardAmount - campaign.totalDistributed
|
|
23
|
+
|
|
24
|
+
// -- Update the period data.
|
|
25
|
+
const newRewardPerPeriod =
|
|
26
|
+
remainingPeriods > 0 ? totalRewardForRemainingPeriods / BigInt(remainingPeriods) : totalRewardForRemainingPeriods
|
|
27
|
+
period.rewardPerPeriod = newRewardPerPeriod
|
|
28
|
+
|
|
29
|
+
// -- Update the reward per vote
|
|
30
|
+
// If no votes, rollover the leftover to the next epoch.
|
|
31
|
+
if (totalVotes === Zero) {
|
|
32
|
+
period.leftover = period.rewardPerPeriod
|
|
33
|
+
} else {
|
|
34
|
+
// Calculate reward per vote
|
|
35
|
+
let rewardPerVote = (newRewardPerPeriod * BigInt(1e18)) / totalVotes
|
|
36
|
+
|
|
37
|
+
// Cap reward per vote at max reward per vote
|
|
38
|
+
if (rewardPerVote > campaign.maxRewardPerVote) {
|
|
39
|
+
rewardPerVote = campaign.maxRewardPerVote
|
|
40
|
+
|
|
41
|
+
// Calculate leftover rewards
|
|
42
|
+
const leftOver = newRewardPerPeriod - (rewardPerVote * totalVotes) / BigInt(1e18)
|
|
43
|
+
|
|
44
|
+
// Handle leftover rewards
|
|
45
|
+
if (campaign.hook === zeroAddress) {
|
|
46
|
+
// Store leftover in the period if there is no hook.
|
|
47
|
+
period.leftover = period.leftover + leftOver
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Save the calculated reward per vote.
|
|
52
|
+
period.rewardPerVote = rewardPerVote
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// -- Update the total distributed amount
|
|
56
|
+
campaign.totalDistributed = campaign.totalDistributed + newRewardPerPeriod - period.leftover
|
|
57
|
+
|
|
58
|
+
// -- Mark the period as updated
|
|
59
|
+
period.updated = true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return campaign
|
|
63
|
+
}
|