@stake-dao/reader 0.4.32 → 0.4.34

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 (82) hide show
  1. package/dist/esm/bytecodes/index.js +1 -0
  2. package/dist/esm/bytecodes/index.js.map +1 -1
  3. package/dist/esm/bytecodes/votemarket/batchCampaigns.js +1 -1
  4. package/dist/esm/bytecodes/votemarket/batchCampaigns.js.map +1 -1
  5. package/dist/esm/bytecodes/votemarket/batchCampaignsRestrictedVotes.js +33 -0
  6. package/dist/esm/bytecodes/votemarket/batchCampaignsRestrictedVotes.js.map +1 -0
  7. package/dist/esm/bytecodes/votemarket/batchClaimableData.js +1 -1
  8. package/dist/esm/bytecodes/votemarket/batchClaimableData.js.map +1 -1
  9. package/dist/esm/bytecodes/votemarket/getNeededCampaignUpdate.js +17 -0
  10. package/dist/esm/bytecodes/votemarket/getNeededCampaignUpdate.js.map +1 -0
  11. package/dist/esm/endpoints.js +1 -0
  12. package/dist/esm/endpoints.js.map +1 -1
  13. package/dist/esm/index.js +3 -1
  14. package/dist/esm/index.js.map +1 -1
  15. package/dist/esm/strategies/curve/fetch/curveApiData/getGaugesWeights.js +2 -2
  16. package/dist/esm/strategies/curve/fetch/curveApiData/getGaugesWeights.js.map +1 -1
  17. package/dist/esm/strategies/pancakeswap/fetch/getPancakeV3Vaults.js +1 -1
  18. package/dist/esm/strategies/pancakeswap/fetch/getPancakeV3Vaults.js.map +1 -1
  19. package/dist/esm/strategies/pancakeswap/fetch/pancakePositions.js +2 -2
  20. package/dist/esm/strategies/pancakeswap/fetch/pancakePositions.js.map +1 -1
  21. package/dist/esm/strategies/pancakeswap/fetch/pancakeswapMath.js +1 -1
  22. package/dist/esm/strategies/pancakeswap/fetch/pancakeswapMath.js.map +1 -1
  23. package/dist/esm/tsconfig.build.tsbuildinfo +1 -1
  24. package/dist/esm/utils.js +13 -0
  25. package/dist/esm/utils.js.map +1 -1
  26. package/dist/esm/votemarket/curve/fetchCurveGauges.js +4 -1
  27. package/dist/esm/votemarket/curve/fetchCurveGauges.js.map +1 -1
  28. package/dist/esm/votemarket/curve/fetchSnapshotUserData.js +34 -1
  29. package/dist/esm/votemarket/curve/fetchSnapshotUserData.js.map +1 -1
  30. package/dist/esm/votemarket/curve/fetchUserVlCvxClaimable.js +37 -5
  31. package/dist/esm/votemarket/curve/fetchUserVlCvxClaimable.js.map +1 -1
  32. package/dist/esm/votemarket/fetchCampaigns.js +75 -14
  33. package/dist/esm/votemarket/fetchCampaigns.js.map +1 -1
  34. package/dist/esm/votemarket/fetchNeededCampaignUpdate.js +17 -0
  35. package/dist/esm/votemarket/fetchNeededCampaignUpdate.js.map +1 -0
  36. package/dist/esm/votemarket/fetchPendingRemoteCampaigns.js +102 -0
  37. package/dist/esm/votemarket/fetchPendingRemoteCampaigns.js.map +1 -0
  38. package/dist/types/bytecodes/index.d.ts +1 -0
  39. package/dist/types/bytecodes/index.d.ts.map +1 -1
  40. package/dist/types/bytecodes/votemarket/batchCampaignsRestrictedVotes.d.ts +4 -0
  41. package/dist/types/bytecodes/votemarket/batchCampaignsRestrictedVotes.d.ts.map +1 -0
  42. package/dist/types/bytecodes/votemarket/getNeededCampaignUpdate.d.ts +4 -0
  43. package/dist/types/bytecodes/votemarket/getNeededCampaignUpdate.d.ts.map +1 -0
  44. package/dist/types/endpoints.d.ts +1 -0
  45. package/dist/types/endpoints.d.ts.map +1 -1
  46. package/dist/types/index.d.ts +3 -1
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/types/strategies/pancakeswap/fetch/pancakeswapMath.d.ts.map +1 -1
  49. package/dist/types/utils.d.ts +1 -0
  50. package/dist/types/utils.d.ts.map +1 -1
  51. package/dist/types/votemarket/curve/fetchCurveGauges.d.ts.map +1 -1
  52. package/dist/types/votemarket/curve/fetchSnapshotUserData.d.ts +6 -0
  53. package/dist/types/votemarket/curve/fetchSnapshotUserData.d.ts.map +1 -1
  54. package/dist/types/votemarket/curve/fetchUserVlCvxClaimable.d.ts.map +1 -1
  55. package/dist/types/votemarket/fetchCampaigns.d.ts +6 -1
  56. package/dist/types/votemarket/fetchCampaigns.d.ts.map +1 -1
  57. package/dist/types/votemarket/fetchNeededCampaignUpdate.d.ts +2 -0
  58. package/dist/types/votemarket/fetchNeededCampaignUpdate.d.ts.map +1 -0
  59. package/dist/types/votemarket/fetchPendingRemoteCampaigns.d.ts +46 -0
  60. package/dist/types/votemarket/fetchPendingRemoteCampaigns.d.ts.map +1 -0
  61. package/dist/types/votemarket/types.d.ts +1 -0
  62. package/dist/types/votemarket/types.d.ts.map +1 -1
  63. package/package.json +2 -2
  64. package/src/bytecodes/index.ts +1 -0
  65. package/src/bytecodes/votemarket/batchCampaigns.ts +1 -1
  66. package/src/bytecodes/votemarket/batchCampaignsRestrictedVotes.ts +35 -0
  67. package/src/bytecodes/votemarket/batchClaimableData.ts +1 -1
  68. package/src/bytecodes/votemarket/getNeededCampaignUpdate.ts +19 -0
  69. package/src/endpoints.ts +2 -0
  70. package/src/index.ts +3 -1
  71. package/src/strategies/curve/fetch/curveApiData/getGaugesWeights.ts +2 -2
  72. package/src/strategies/pancakeswap/fetch/getPancakeV3Vaults.ts +1 -1
  73. package/src/strategies/pancakeswap/fetch/pancakePositions.ts +2 -2
  74. package/src/strategies/pancakeswap/fetch/pancakeswapMath.ts +1 -1
  75. package/src/utils.ts +19 -0
  76. package/src/votemarket/curve/fetchCurveGauges.ts +5 -1
  77. package/src/votemarket/curve/fetchSnapshotUserData.ts +43 -3
  78. package/src/votemarket/curve/fetchUserVlCvxClaimable.ts +51 -6
  79. package/src/votemarket/fetchCampaigns.ts +113 -16
  80. package/src/votemarket/fetchNeededCampaignUpdate.ts +28 -0
  81. package/src/votemarket/fetchPendingRemoteCampaigns.ts +141 -0
  82. package/src/votemarket/types.ts +1 -0
@@ -0,0 +1,19 @@
1
+ import type { BatchMetaData } from '../types.js'
2
+
3
+ const getNeededCampaignUpdate: BatchMetaData = {
4
+ bytecode: {
5
+ 1: '0x608060405234801561000f575f80fd5b5060405161115d38038061115d83398181016040528101906100319190610737565b5f8290505f8173ffffffffffffffffffffffffffffffffffffffff166338013f026040518163ffffffff1660e01b8152600401602060405180830381865afa15801561007f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100a39190610775565b73ffffffffffffffffffffffffffffffffffffffff16637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100eb573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061010f9190610775565b90505f8273ffffffffffffffffffffffffffffffffffffffff16635598f8cc856040518263ffffffff1660e01b815260040161014b91906107af565b61016060405180830381865afa158015610167573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061018b9190610993565b90505f8373ffffffffffffffffffffffffffffffffffffffff16636fb08df2866040518263ffffffff1660e01b81526004016101c791906107af565b5f60405180830381865afa1580156101e1573d5f803e3d5ffd5b505050506040513d5f823e3d601f19601f820116820180604052508101906102099190610a87565b90505f826080015160ff1667ffffffffffffffff81111561022d5761022c6107dc565b5b60405190808252806020026020018201604052801561026657816020015b610253610632565b81526020019060019003908161024b5790505b5090505f5b836080015160ff16811015610603575f62093a808261028a9190610afb565b85610100015161029a9190610b3c565b90505f8773ffffffffffffffffffffffffffffffffffffffff166352aed5788a846040518363ffffffff1660e01b81526004016102d8929190610b6f565b608060405180830381865afa1580156102f3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103179190610bcb565b93505050505f855167ffffffffffffffff811115610338576103376107dc565b5b60405190808252806020026020018201604052801561037157816020015b61035e610669565b8152602001906001900390816103565790505b5090505f5b865181111561049657604051806040016040528088838151811061039d5761039c610c2f565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1681526020015f8b73ffffffffffffffffffffffffffffffffffffffff1663bef3ba998b86815181106103f0576103ef610c2f565b5b60200260200101518d602001518a6040518463ffffffff1660e01b815260040161041c93929190610c6b565b608060405180830381865afa158015610437573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061045b9190610d15565b6060015111151581525082828151811061047857610477610c2f565b5b6020026020010181905250808061048e90610d40565b915050610376565b505f8873ffffffffffffffffffffffffffffffffffffffff166329597009856040518263ffffffff1660e01b81526004016104d191906107af565b608060405180830381865afa1580156104ec573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105109190610e2f565b90506040518060c0016040528085815260200184151581526020015f801b83602001511415151581526020015f8b73ffffffffffffffffffffffffffffffffffffffff1663e31c6a638c60200151896040518363ffffffff1660e01b815260040161057c929190610e5a565b6040805180830381865afa158015610596573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105ba9190610ece565b60200151111515815260200182604001518152602001838152508686815181106105e7576105e6610c2f565b5b602002602001018190525050505050808060010191505061026b565b505f81604051602001610616919061113c565b6040516020818303038152906040529050602081018059038082f35b6040518060c001604052805f81526020015f151581526020015f151581526020015f151581526020015f8152602001606081525090565b60405180604001604052805f73ffffffffffffffffffffffffffffffffffffffff1681526020015f151581525090565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6106d3826106aa565b9050919050565b6106e3816106c9565b81146106ed575f80fd5b50565b5f815190506106fe816106da565b92915050565b5f819050919050565b61071681610704565b8114610720575f80fd5b50565b5f815190506107318161070d565b92915050565b5f806040838503121561074d5761074c6106a2565b5b5f61075a858286016106f0565b925050602061076b85828601610723565b9150509250929050565b5f6020828403121561078a576107896106a2565b5b5f610797848285016106f0565b91505092915050565b6107a981610704565b82525050565b5f6020820190506107c25f8301846107a0565b92915050565b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610812826107cc565b810181811067ffffffffffffffff82111715610831576108306107dc565b5b80604052505050565b5f610843610699565b905061084f8282610809565b919050565b5f60ff82169050919050565b61086981610854565b8114610873575f80fd5b50565b5f8151905061088481610860565b92915050565b5f61016082840312156108a05761089f6107c8565b5b6108ab61016061083a565b90505f6108ba84828501610723565b5f8301525060206108cd848285016106f0565b60208301525060406108e1848285016106f0565b60408301525060606108f5848285016106f0565b606083015250608061090984828501610876565b60808301525060a061091d84828501610723565b60a08301525060c061093184828501610723565b60c08301525060e061094584828501610723565b60e08301525061010061095a84828501610723565b6101008301525061012061097084828501610723565b61012083015250610140610986848285016106f0565b6101408301525092915050565b5f61016082840312156109a9576109a86106a2565b5b5f6109b68482850161088a565b91505092915050565b5f80fd5b5f67ffffffffffffffff8211156109dd576109dc6107dc565b5b602082029050602081019050919050565b5f80fd5b5f610a046109ff846109c3565b61083a565b90508083825260208201905060208402830185811115610a2757610a266109ee565b5b835b81811015610a505780610a3c88826106f0565b845260208401935050602081019050610a29565b5050509392505050565b5f82601f830112610a6e57610a6d6109bf565b5b8151610a7e8482602086016109f2565b91505092915050565b5f60208284031215610a9c57610a9b6106a2565b5b5f82015167ffffffffffffffff811115610ab957610ab86106a6565b5b610ac584828501610a5a565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610b0582610704565b9150610b1083610704565b9250828202610b1e81610704565b91508282048414831517610b3557610b34610ace565b5b5092915050565b5f610b4682610704565b9150610b5183610704565b9250828201905080821115610b6957610b68610ace565b5b92915050565b5f604082019050610b825f8301856107a0565b610b8f60208301846107a0565b9392505050565b5f8115159050919050565b610baa81610b96565b8114610bb4575f80fd5b50565b5f81519050610bc581610ba1565b92915050565b5f805f8060808587031215610be357610be26106a2565b5b5f610bf087828801610723565b9450506020610c0187828801610723565b9350506040610c1287828801610723565b9250506060610c2387828801610bb7565b91505092959194509250565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b610c65816106c9565b82525050565b5f606082019050610c7e5f830186610c5c565b610c8b6020830185610c5c565b610c9860408301846107a0565b949350505050565b5f60808284031215610cb557610cb46107c8565b5b610cbf608061083a565b90505f610cce84828501610723565b5f830152506020610ce184828501610723565b6020830152506040610cf584828501610723565b6040830152506060610d0984828501610723565b60608301525092915050565b5f60808284031215610d2a57610d296106a2565b5b5f610d3784828501610ca0565b91505092915050565b5f610d4a82610704565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610d7c57610d7b610ace565b5b600182019050919050565b5f819050919050565b610d9981610d87565b8114610da3575f80fd5b50565b5f81519050610db481610d90565b92915050565b5f60808284031215610dcf57610dce6107c8565b5b610dd9608061083a565b90505f610de884828501610da6565b5f830152506020610dfb84828501610da6565b6020830152506040610e0f84828501610723565b6040830152506060610e2384828501610723565b60608301525092915050565b5f60808284031215610e4457610e436106a2565b5b5f610e5184828501610dba565b91505092915050565b5f604082019050610e6d5f830185610c5c565b610e7a60208301846107a0565b9392505050565b5f60408284031215610e9657610e956107c8565b5b610ea0604061083a565b90505f610eaf84828501610723565b5f830152506020610ec284828501610723565b60208301525092915050565b5f60408284031215610ee357610ee26106a2565b5b5f610ef084828501610e81565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b610f2b81610704565b82525050565b610f3a81610b96565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b610f72816106c9565b82525050565b604082015f820151610f8c5f850182610f69565b506020820151610f9f6020850182610f31565b50505050565b5f610fb08383610f78565b60408301905092915050565b5f602082019050919050565b5f610fd282610f40565b610fdc8185610f4a565b9350610fe783610f5a565b805f5b83811015611017578151610ffe8882610fa5565b975061100983610fbc565b925050600181019050610fea565b5085935050505092915050565b5f60c083015f8301516110395f860182610f22565b50602083015161104c6020860182610f31565b50604083015161105f6040860182610f31565b5060608301516110726060860182610f31565b5060808301516110856080860182610f22565b5060a083015184820360a086015261109d8282610fc8565b9150508091505092915050565b5f6110b58383611024565b905092915050565b5f602082019050919050565b5f6110d382610ef9565b6110dd8185610f03565b9350836020820285016110ef85610f13565b805f5b8581101561112a578484038952815161110b85826110aa565b9450611116836110bd565b925060208a019950506001810190506110f2565b50829750879550505050505092915050565b5f6020820190508181035f83015261115481846110c9565b90509291505056fe',
6
+ },
7
+ inputType: {
8
+ 1: ['address platform, uint256 campaignId'],
9
+ },
10
+ outputTypeHr: {
11
+ 1: [
12
+ 'UpdateInfo[] returnData',
13
+ 'struct UpdateInfo { uint256 epoch; bool updated; bool hasBlockData; bool hasGaugeData; uint256 blockNumber; AddressesData[] addressesData;}',
14
+ 'struct AddressesData { address listed; bool hasData;}',
15
+ ],
16
+ },
17
+ }
18
+
19
+ export default getNeededCampaignUpdate
package/src/endpoints.ts CHANGED
@@ -5,3 +5,5 @@ export const GH_STAKE_DAO_VM_DATA = `${GH_STAKE_DAO_RAW_ORG}/votemarket-data`
5
5
  export const LLAMA_API_URL = 'https://coins.llama.fi'
6
6
  export const THE_GRAPH_BASE_URL =
7
7
  'https://gateway-arbitrum.network.thegraph.com/api/e3c2ef8231c88703a5b450c188ce2db6/subgraphs/id'
8
+
9
+ export const SNAPSHOT_HUB = 'https://hub.snapshot.org/graphql'
package/src/index.ts CHANGED
@@ -3,7 +3,8 @@
3
3
  ///////////////////////////////////////////////////////////////
4
4
  export { getPricesFromLlama, getPricesFromGeckoTerminal } from './prices.js'
5
5
  export * from './sdt.js'
6
- export { rpcCall, batchJsonRpc } from './utils.js'
6
+ export * from './tokens.js'
7
+ export { rpcCall, batchJsonRpc, ethGetTxsReceipts } from './utils.js'
7
8
  export type { BatchJsonRpcArgs } from './utils.js'
8
9
 
9
10
  ////////////////////////////////////////////////////////////////
@@ -70,6 +71,7 @@ export { fetchSnapshotUserData } from './votemarket/curve/fetchSnapshotUserData.
70
71
  // Campaign data
71
72
  export { fetchEpochsVotes } from './votemarket/fetchEpochVotes.js'
72
73
  export { fetchClaimableData } from './votemarket/fetchClaimableData.js'
74
+ export { fetchNeededCampaignUpdate } from './votemarket/fetchNeededCampaignUpdate.js'
73
75
 
74
76
  // Proofs
75
77
 
@@ -99,8 +99,8 @@ export const getGaugesWeights = async (provider: any, rpc: string): Promise<Gaug
99
99
  return {
100
100
  gauge,
101
101
  inflationRate: formatUnits(inflationRate || 0, 0),
102
- relativeWeight: formatUnits(gaugeWeight[2]!.result, 0),
103
- futureRelativeWeight: formatUnits(gaugeWeight[3]!.result, 0),
102
+ relativeWeight: formatUnits(gaugeWeight[2]!.result || 0, 0),
103
+ futureRelativeWeight: formatUnits(gaugeWeight[3]!.result || 0, 0),
104
104
  }
105
105
  }),
106
106
  }
@@ -150,7 +150,7 @@ export const getPancakeV3Vaults = async (
150
150
  },
151
151
  ]
152
152
 
153
- const tradingApy = Number(feesApr[strat.pool.toLowerCase()]?.apr7d || 0)
153
+ const tradingApy = Number(feesApr[strat.pool.toLowerCase()]?.apr7d || 0) * 100
154
154
  const apr = getAprBreakdown({ ...strat, tradingApy }, rewards, boost, undefined)
155
155
 
156
156
  const parsedData = {
@@ -44,8 +44,8 @@ export const computePositionData = (
44
44
  const token1AmountUsd = Number(token1Amount) * token1PriceInUsd
45
45
  const tvlToAdd = token0AmountUsd + token1AmountUsd
46
46
 
47
- const priceLower = tickToPrice(p.tickLower)
48
- const priceUpper = tickToPrice(p.tickUpper)
47
+ const priceLower = tickToPrice(p.tickLower) * 10 ** (s.coins[0].decimals - s.coins[1].decimals)
48
+ const priceUpper = tickToPrice(p.tickUpper) * 10 ** (s.coins[0].decimals - s.coins[1].decimals)
49
49
 
50
50
  const boost = Number(formatUnits(p.boostMultiplier.toString(), 12))
51
51
 
@@ -295,7 +295,7 @@ export function getPositionTradingFee({
295
295
  return (positionUSD + fees24HPosition * 365) / positionUSD - 1
296
296
  }
297
297
 
298
- export function tickToPrice(tick: number, precision = 6): number {
298
+ export function tickToPrice(tick: number, precision = 18): number {
299
299
  const sqrtRatioX96 = getSqrtRatioAtTick(tick)
300
300
 
301
301
  const ratioX192 = sqrtRatioX96 * sqrtRatioX96
package/src/utils.ts CHANGED
@@ -121,6 +121,25 @@ export const rpcCall = async (rpc: string, params: RpcCallArgs[]) => {
121
121
  return request
122
122
  }
123
123
 
124
+ export const ethGetTxsReceipts = async (rpc: string, txHashes: string[]) => {
125
+ const request = (
126
+ await fetch(rpc, {
127
+ method: 'POST',
128
+ headers: { 'Content-Type': 'application/json' },
129
+ body: JSON.stringify(
130
+ txHashes.map((txHash, index) => ({
131
+ id: index,
132
+ jsonrpc: '2.0',
133
+ method: 'eth_getTransactionReceipt',
134
+ params: [txHash],
135
+ })),
136
+ ),
137
+ })
138
+ ).json()
139
+
140
+ return request
141
+ }
142
+
124
143
  export interface BatchJsonRpcArgs {
125
144
  rpc: string
126
145
  calls: string[]
@@ -111,7 +111,7 @@ export const fetchCurveGauges = async (rpc: Rpcs): Promise<GaugesData> => {
111
111
  const gaugeMetadata = gaugesMetadata.find((gm) => equalTlc(gm.gauge, g.childGauge))
112
112
  const filteredCoins = gaugeMetadata.coins.filter((c) => c._address !== zeroAddress)
113
113
 
114
- const name = gaugeMetadata.lp.name
114
+ let name = gaugeMetadata.lp.name
115
115
  .replace('Curve.fi Factory Crypto Pool:', '')
116
116
  .replace('Curve.fi Factory Pool:', '')
117
117
  .replace('Curve.fi Factory USD Metapool:', '')
@@ -119,6 +119,10 @@ export const fetchCurveGauges = async (rpc: Rpcs): Promise<GaugesData> => {
119
119
  .replace('Curve.fi', '')
120
120
  .trim()
121
121
 
122
+ if (name.startsWith('Curve Vault for')) {
123
+ name = `Lending ${gaugeMetadata.coins[0].symbol} (${gaugeMetadata.coins[1].symbol})`
124
+ }
125
+
122
126
  if (filteredCoins.length === 0 && !g.isKilled && g.weight !== '0')
123
127
  gaugesLpWithMissingCoins.push(gaugeMetadata.lp._address)
124
128
 
@@ -1,6 +1,7 @@
1
1
  import snapshot from '@snapshot-labs/snapshot.js'
2
2
  import { gql } from 'graphql-request'
3
3
  import { isAddress } from 'viem'
4
+ import { SNAPSHOT_HUB } from '../../endpoints.js'
4
5
  import { graphql } from '../../utils.js'
5
6
 
6
7
  interface SnapshotProposal {
@@ -21,6 +22,7 @@ interface ISnapshotUserData {
21
22
  proposal: SnapshotProposal
22
23
  vp: number
23
24
  space: string
25
+ userVotes: { [gaugeId: string]: { weight: number; relativeWeight: number } }
24
26
  }
25
27
 
26
28
  interface SnapshotStrategy {
@@ -66,6 +68,24 @@ query Proposals {
66
68
  }
67
69
  `
68
70
 
71
+ const SNAPSHOT_USER_VOTE_QUERY = (space: string, proposal: string, user: string) => gql`
72
+ query UserVote {
73
+ votes (
74
+ first: 1
75
+ where: {
76
+ space: "${space}"
77
+ proposal: "${proposal}"
78
+ voter: "${user}"
79
+ }
80
+ ) {
81
+ id
82
+ created
83
+ choice
84
+ vp
85
+ }
86
+ }
87
+ `
88
+
69
89
  export const fetchSnapshotUserData = async (
70
90
  userAddress: string,
71
91
  spaces: string[],
@@ -74,9 +94,7 @@ export const fetchSnapshotUserData = async (
74
94
  return undefined
75
95
  }
76
96
 
77
- const responses = await Promise.all(
78
- spaces.map((space) => graphql('https://hub.snapshot.org/graphql', SNAPSHOT_PROPOSAL_QUERY(space))),
79
- )
97
+ const responses = await Promise.all(spaces.map((space) => graphql(SNAPSHOT_HUB, SNAPSHOT_PROPOSAL_QUERY(space))))
80
98
 
81
99
  const results: ISnapshotUserData[] = []
82
100
  for (let i = 0; i < responses.length; i++) {
@@ -94,6 +112,7 @@ export const fetchSnapshotUserData = async (
94
112
 
95
113
  // Fetch user voting power
96
114
  let vp = 0
115
+ const userVotes = {}
97
116
  if (isAddress(userAddress)) {
98
117
  const fetchedVp = await (snapshot as any).utils.getScores(
99
118
  spaces[i],
@@ -108,12 +127,33 @@ export const fetchSnapshotUserData = async (
108
127
  vp += v?.[userAddress] || 0
109
128
  }
110
129
  }
130
+
131
+ const rawUserVotes = await graphql(SNAPSHOT_HUB, SNAPSHOT_USER_VOTE_QUERY(spaces[i]!, proposal.id, userAddress))
132
+
133
+ if (rawUserVotes?.votes && rawUserVotes.votes.length > 0) {
134
+ const rawUserVote = rawUserVotes.votes[0]
135
+ const totalSnapshotVote = Object.keys(rawUserVote.choice).reduce(
136
+ (total, i) => (total += rawUserVote.choice[i]),
137
+ 0,
138
+ )
139
+
140
+ for (const i of Object.keys(rawUserVote.choice)) {
141
+ const relativeWeight = rawUserVote.choice[i] / totalSnapshotVote
142
+ const proposalChoiceIndex = Number(i) - 1
143
+
144
+ userVotes[proposal.choices[proposalChoiceIndex]!] = {
145
+ weight: rawUserVote.vp * relativeWeight,
146
+ relativeWeight: relativeWeight * 100,
147
+ }
148
+ }
149
+ }
111
150
  }
112
151
 
113
152
  results.push({
114
153
  proposal,
115
154
  space: spaces[i] as string,
116
155
  vp,
156
+ userVotes,
117
157
  })
118
158
  }
119
159
 
@@ -19,9 +19,39 @@ type UnknownToken = {
19
19
  }
20
20
 
21
21
  const getMerkleData = async (epoch: number) => {
22
- return await fetch(
22
+ const response = await fetch(
23
23
  `${GH_STAKE_DAO_BOUNTIES_REPORT}/refs/heads/main/bounties-reports/${epoch}/vlCVX/merkle_data.json`,
24
- ).then((res) => res.json())
24
+ )
25
+ if (!response.ok) throw new Error('Failed to fetch merkle data')
26
+ return response.json()
27
+ }
28
+
29
+ const isRootMatch = (contractRoot: string, fileRoot: string) =>
30
+ (contractRoot === ZERO_ROOT && fileRoot === ZERO_ROOT) || contractRoot.toLowerCase() === fileRoot.toLowerCase()
31
+
32
+ const validateMerkleRoots = (delegatorRoot: string, nonDelegatorRoot: string, merkleData: any) => {
33
+ const delegatorMatches = isRootMatch(delegatorRoot, merkleData.delegators.merkleRoot)
34
+ const nonDelegatorMatches = isRootMatch(nonDelegatorRoot, merkleData.nonDelegators.merkleRoot)
35
+ return delegatorMatches || nonDelegatorMatches
36
+ }
37
+
38
+ const getMerkleRoots = async (provider: any) => {
39
+ const contracts = [contract('vlCvxDelegatorMerkle', mainnet.id), contract('vlCvxNotDelegatorMerkle', mainnet.id)]
40
+
41
+ const result = await multicall(
42
+ provider,
43
+ contracts.map((address) => ({
44
+ address,
45
+ name: 'root',
46
+ params: [],
47
+ })),
48
+ parseAbi(['function root() external view returns (bytes32)']),
49
+ )
50
+
51
+ return {
52
+ delegatorRoot: result[0].result,
53
+ nonDelegatorRoot: result[1].result,
54
+ }
25
55
  }
26
56
 
27
57
  const getClaimedData = async (provider: any, tokens: string[], user: string, contract: string) => {
@@ -74,14 +104,29 @@ const getParsedData = (
74
104
  : undefined
75
105
  }
76
106
 
107
+ const ZERO_ROOT = '0x0000000000000000000000000000000000000000000000000000000000000000'
108
+
77
109
  export const fetchUserVlCvxClaimable = async (provider: any, rpc: Rpcs, user: string) => {
78
110
  const nowEpoch = Math.floor(Date.now() / (1000 * ONE_WEEK)) * ONE_WEEK
111
+ const prevEpoch = nowEpoch - ONE_WEEK
112
+
113
+ const { delegatorRoot, nonDelegatorRoot } = await getMerkleRoots(provider)
79
114
 
80
115
  let vlCvxClaimable: any
81
- try {
82
- vlCvxClaimable = await getMerkleData(nowEpoch)
83
- } catch {
84
- vlCvxClaimable = await getMerkleData(nowEpoch - ONE_WEEK)
116
+
117
+ // Try current week, then previous week if needed
118
+ for (const epoch of [nowEpoch, prevEpoch]) {
119
+ try {
120
+ const merkleData = await getMerkleData(epoch)
121
+ if (validateMerkleRoots(delegatorRoot, nonDelegatorRoot, merkleData)) {
122
+ vlCvxClaimable = merkleData
123
+ break
124
+ }
125
+ } catch {}
126
+ }
127
+
128
+ if (!vlCvxClaimable) {
129
+ throw new Error('Could not find valid merkle data')
85
130
  }
86
131
 
87
132
  const vlCvxUserDelegated = vlCvxClaimable?.delegators.claims[user]
@@ -1,12 +1,21 @@
1
- import { ONE_WEEK, RPC, Zero, contract, contracts, tokenWithAddress, tokens } from '@stake-dao/constants'
2
- import { filter, range, remove, uniq } from 'lodash-es'
3
- import { decodeAbiParameters, encodeAbiParameters, formatUnits, parseAbiParameters, zeroAddress } from 'viem'
4
- import { mainnet } from 'viem/chains'
1
+ import { ONE_WEEK, Zero, contract, contracts, tokenWithAddress, tokens } from '@stake-dao/constants'
2
+ import { chunk, filter, range, remove, uniq } from 'lodash-es'
3
+ import {
4
+ decodeAbiParameters,
5
+ encodeAbiParameters,
6
+ formatEther,
7
+ formatUnits,
8
+ parseAbiParameters,
9
+ zeroAddress,
10
+ } from 'viem'
11
+ import { arbitrum, mainnet } from 'viem/chains'
5
12
  import batchCampaigns from '../bytecodes/votemarket/batchCampaigns.js'
13
+ import batchCampaignsRestrictedVotes from '../bytecodes/votemarket/batchCampaignsRestrictedVotes.js'
6
14
  import batchGaugesWeight from '../bytecodes/votemarket/batchGaugesWeight.js'
7
15
  import { getPrices } from '../prices.js'
8
16
  import { rpcFetchTokensData } from '../tokens.js'
9
17
  import { batchJsonRpc, concatBytecode, equalTlc, rpcCall, rpcGetLastBlockTimetstamp } from '../utils.js'
18
+ import { fetchPendingRemoteCampaigns } from './fetchPendingRemoteCampaigns.js'
10
19
  import type { Campaign, RawCampaign, RawPeriod } from './types.js'
11
20
 
12
21
  export const CAMPAIGNS_CHUNK_SIZE = 10
@@ -15,8 +24,13 @@ export const CLOSE_WINDOW_LENGTH = 4 // weeks
15
24
 
16
25
  interface FetchCampaignsProps {
17
26
  platform: string
27
+ gaugeController: string
28
+ veToken: string
18
29
  chainId: number
19
30
  rpcs: { [chainId: number]: string }
31
+ etherscanApiKey?: string
32
+ fromBlock?: number
33
+ mainnetVmRemote?: string
20
34
  }
21
35
 
22
36
  const updateEpoch = ({
@@ -79,7 +93,16 @@ const updateEpoch = ({
79
93
  return campaign
80
94
  }
81
95
 
82
- export const fetchCampaigns = async ({ platform, chainId, rpcs }: FetchCampaignsProps): Promise<Campaign[]> => {
96
+ export const fetchCampaigns = async ({
97
+ platform,
98
+ gaugeController,
99
+ veToken,
100
+ chainId,
101
+ rpcs,
102
+ etherscanApiKey,
103
+ fromBlock,
104
+ mainnetVmRemote,
105
+ }: FetchCampaignsProps): Promise<Campaign[]> => {
83
106
  const rpc = rpcs[chainId]!
84
107
 
85
108
  const block = await rpcGetLastBlockTimetstamp(rpc)
@@ -101,7 +124,7 @@ export const fetchCampaigns = async ({ platform, chainId, rpcs }: FetchCampaigns
101
124
  return concatBytecode(batchCampaigns.bytecode[1]!, inputData)
102
125
  })
103
126
 
104
- const rawCampaigns = await batchJsonRpc({
127
+ let rawCampaigns = await batchJsonRpc({
105
128
  rpc,
106
129
  calls,
107
130
  outputTypeAbi: batchCampaigns.outputType![1]!,
@@ -109,9 +132,65 @@ export const fetchCampaigns = async ({ platform, chainId, rpcs }: FetchCampaigns
109
132
  callsKey: `votemarket/fetchCampaigns.ts: rawCampaigns - chainId ${chainId} - rpc ${rpc}`,
110
133
  })
111
134
 
135
+ if (chainId === arbitrum.id && etherscanApiKey && fromBlock && mainnetVmRemote) {
136
+ const nextIndex = rawCampaigns.length > 0 ? Math.max(...rawCampaigns.map((rc) => Number(rc.id))) + 1 : 0
137
+ const { remoteCampaigns: pendingRemoteCampaigns } = await fetchPendingRemoteCampaigns({
138
+ mainnetVmRemote,
139
+ chainId,
140
+ fromBlock,
141
+ etherscanApiKey,
142
+ nextIndex,
143
+ })
144
+
145
+ rawCampaigns = [
146
+ ...rawCampaigns,
147
+ ...pendingRemoteCampaigns.filter((rmtC) => {
148
+ const existingCampaign = rawCampaigns.find(
149
+ (rc) =>
150
+ rc.addresses.length === rmtC.addresses.length &&
151
+ rc.addresses.every((rca, index) => equalTlc(rca, rmtC.addresses[index])) &&
152
+ equalTlc(rc.rewardAddress, rmtC.rewardAddress) &&
153
+ rc.rewardChainId === rmtC.rewardChainId &&
154
+ rc.campaign.startTimestamp === rmtC.campaign.startTimestamp &&
155
+ rc.campaign.endTimestamp === rmtC.campaign.endTimestamp &&
156
+ rc.campaign.numberOfPeriods === rmtC.campaign.numberOfPeriods &&
157
+ rc.campaign.maxRewardPerVote === rmtC.campaign.maxRewardPerVote &&
158
+ rc.campaign.totalRewardAmount === rmtC.campaign.totalRewardAmount &&
159
+ equalTlc(rc.campaign.gauge, rmtC.campaign.gauge) &&
160
+ equalTlc(rc.campaign.hook, rmtC.campaign.hook) &&
161
+ equalTlc(rc.campaign.manager, rmtC.campaign.manager),
162
+ )
163
+
164
+ return typeof existingCampaign === 'undefined'
165
+ }),
166
+ ]
167
+ }
168
+
169
+ const callsRestrictedVotes = rawCampaigns
170
+ .filter((rc) => rc.addresses.length > 0)
171
+ .map((rc) => [rc.campaign.chainId, rc.id, rc.campaign.gauge, rc.addresses])
172
+ const callsRestrictedVotesChunks = chunk(callsRestrictedVotes, CAMPAIGNS_CHUNK_SIZE).map((restrictedVoteChunk) => {
173
+ const inputData = encodeAbiParameters(parseAbiParameters(batchCampaignsRestrictedVotes.inputType[1]!) as any, [
174
+ veToken,
175
+ gaugeController,
176
+ restrictedVoteChunk,
177
+ ])
178
+ return concatBytecode(batchCampaignsRestrictedVotes.bytecode[1]!, inputData)
179
+ })
180
+
181
+ const rawCampaignsRestrictedVotes = await batchJsonRpc({
182
+ rpc: rpcs[mainnet.id]!,
183
+ calls: callsRestrictedVotesChunks,
184
+ outputTypeAbi: batchCampaignsRestrictedVotes.outputType![1]!,
185
+ parse: false,
186
+ callsKey: `votemarket/fetchCampaigns.ts: rawCampaignsRestrictedVotes - chainId ${mainnet.id} - rpc ${rpcs[
187
+ mainnet.id
188
+ ]!}`,
189
+ })
190
+
112
191
  const campaignsGauges: string[] = uniq(rawCampaigns.map((rc) => rc.campaign.gauge))
113
192
  const rawGaugesWeights = await batchJsonRpc({
114
- rpc: RPC[mainnet.id],
193
+ rpc: rpcs[mainnet.id]!,
115
194
  calls: [
116
195
  concatBytecode(
117
196
  batchGaugesWeight.bytecode[mainnet.id]!,
@@ -122,10 +201,12 @@ export const fetchCampaigns = async ({ platform, chainId, rpcs }: FetchCampaigns
122
201
  ),
123
202
  ],
124
203
  outputTypeAbi: batchGaugesWeight.outputTypeHr[mainnet.id]!,
125
- callsKey: `votemarket/fetchCampaigns.ts: rawGaugesWeights - chainId ${mainnet.id} - rpc ${RPC[mainnet.id]}`,
204
+ callsKey: `votemarket/fetchCampaigns.ts: rawGaugesWeights - chainId ${mainnet.id} - rpc ${rpcs[mainnet.id]!}`,
126
205
  })
127
206
 
128
- const campaignsTokens: string[] = uniq(rawCampaigns.map((rc) => rc.campaign.rewardToken))
207
+ const campaignsTokens: string[] = uniq(
208
+ rawCampaigns.map((rc) => (!rc.isPendingRemote ? rc.campaign.rewardToken : undefined)).filter(Boolean),
209
+ )
129
210
  const tokensToFetch: string[] = remove(campaignsTokens, (t) => !tokenWithAddress(t, chainId))
130
211
  const rawTokensData: any[] = tokensToFetch.length > 0 ? await rpcFetchTokensData(chainId, rpc, tokensToFetch) : []
131
212
 
@@ -161,10 +242,11 @@ export const fetchCampaigns = async ({ platform, chainId, rpcs }: FetchCampaigns
161
242
  const prices = [
162
243
  ...(fetchedTokens.length > 0 ? await getPrices(fetchedTokens as any, chainId) : []),
163
244
  ...(nativeFetchedTokens.length > 0 ? await getPrices(nativeFetchedTokens as any, mainnet.id) : []),
164
- { address: '0x9E49A0314AE61e9C9E34e4Af62a73FBFfB6DE95A', symbol: 'ARR', usdPrice: 10 }, // For test purpose
165
- { address: '0x0Cc20784f790805537D4eE33B41f1aC4eC55446B', symbol: 'USDC', usdPrice: 1 }, // For test purpose
166
- { address: '0x59c9a01163e685719FAbE5De34779F563f49D4a3', symbol: 'CRV', usdPrice: 30 }, // For test purpose
167
- { address: '0x3c7b193aa39a85FDE911465d35CE3A74499F0A7B', symbol: 'ARR', usdPrice: 10 }, // For test purpose
245
+ // { address: '0x9E49A0314AE61e9C9E34e4Af62a73FBFfB6DE95A', symbol: 'ARR', usdPrice: 10 }, // For test purpose
246
+ // { address: '0x0Cc20784f790805537D4eE33B41f1aC4eC55446B', symbol: 'USDC', usdPrice: 1 }, // For test purpose
247
+ // { address: '0x59c9a01163e685719FAbE5De34779F563f49D4a3', symbol: 'CRV', usdPrice: 30 }, // For test purpose
248
+ // { address: '0x3c7b193aa39a85FDE911465d35CE3A74499F0A7B', symbol: 'ARR', usdPrice: 10 }, // For test purpose
249
+ //{ address: '0xd9879d9dbdc5042d8f1c2710be293909b985dc90', symbol: 'rEYWA', usdPrice: 0.14 }, // tmp rEYWA price
168
250
  ]
169
251
 
170
252
  const allFetchedTokens = [...fetchedTokens, ...nativeFetchedTokens]
@@ -173,17 +255,26 @@ export const fetchCampaigns = async ({ platform, chainId, rpcs }: FetchCampaigns
173
255
 
174
256
  const campaigns = rawCampaigns.map((rc) => {
175
257
  const gaugeWeightData = rawGaugesWeights.find((g) => equalTlc(g.gauge, rc.campaign.gauge))
176
-
177
- let receiptRewardToken = undefined
258
+ let receiptRewardToken: any = undefined
178
259
  let rewardToken = allFetchedTokens.find(
179
260
  (t) => equalTlc(t.address, rc.campaign.rewardToken) && t.chainId === chainId,
180
261
  )
181
262
 
182
- if (!equalTlc(rewardToken.address, rc.rewardAddress)) {
263
+ if (rc.isPendingRemote || !equalTlc(rewardToken.address, rc.rewardAddress)) {
183
264
  receiptRewardToken = rewardToken
184
265
  rewardToken = allFetchedTokens.find(
185
266
  (t) => equalTlc(t.address, rc.rewardAddress) && t.chainId === Number(rc.rewardChainId),
186
267
  )
268
+
269
+ if (rc.isPendingRemote) {
270
+ receiptRewardToken = {
271
+ name: `LaPoste ${rewardToken.name}`,
272
+ symbol: `p${rewardToken.symbol}`,
273
+ address: undefined,
274
+ decimals: rewardToken.decimals,
275
+ chainId,
276
+ }
277
+ }
187
278
  }
188
279
 
189
280
  const decimals = rewardToken ? rewardToken.decimals : 18
@@ -215,6 +306,10 @@ export const fetchCampaigns = async ({ platform, chainId, rpcs }: FetchCampaigns
215
306
  const rawCurrentPeriod = rc.periods[indexOfCurrentPeriod]
216
307
  const rawPreviousPeriod = rc.periods[indexOfCurrentPeriod - 1]
217
308
 
309
+ const restrictedVotesData = rawCampaignsRestrictedVotes.find(
310
+ (rvc) => rvc.chainId === rc.campaign.chainId && rvc.campaignId === rc.id && rvc.gauge === rc.campaign.gauge,
311
+ )
312
+
218
313
  return {
219
314
  id: Number(rc.id),
220
315
  chainId,
@@ -234,6 +329,7 @@ export const fetchCampaigns = async ({ platform, chainId, rpcs }: FetchCampaigns
234
329
  endTimestamp,
235
330
  hook: rc.campaign.hook,
236
331
  isClosed: rc.isClosed,
332
+ restrictedVotes: formatEther(restrictedVotesData?.restrictedVotes || Zero),
237
333
  addresses: rc.addresses,
238
334
  isWhitelist: rc.isWhitelistOnly,
239
335
  isBlacklist: !rc.isWhitelistOnly && rc.addresses.length > 0,
@@ -251,6 +347,7 @@ export const fetchCampaigns = async ({ platform, chainId, rpcs }: FetchCampaigns
251
347
  leftover: formatUnits(rawCurrentPeriod.leftover, decimals),
252
348
  updated: rawCurrentPeriod.updated,
253
349
  },
350
+ periods: rc.periods,
254
351
  status: {
255
352
  voteOpen: endTimestamp - ONE_WEEK > currentTimestamp,
256
353
  voteClosed: currentTimestamp > endTimestamp - ONE_WEEK,
@@ -0,0 +1,28 @@
1
+ import { encodeAbiParameters, parseAbiParameters } from 'viem'
2
+ import { mainnet } from 'viem/chains'
3
+ import { getNeededCampaignUpdate } from '../bytecodes/index.js'
4
+ import { concatBytecode } from '../index.js'
5
+ import { batchJsonRpc } from '../utils.js'
6
+
7
+ export const fetchNeededCampaignUpdate = async (rpc: string, platform: string, campaign: any) => {
8
+ const inputData = encodeAbiParameters(
9
+ parseAbiParameters(
10
+ getNeededCampaignUpdate.inputType[campaign.chainId] || getNeededCampaignUpdate.inputType[mainnet.id]!,
11
+ ) as any,
12
+ [platform, campaign.id],
13
+ )
14
+ const call = concatBytecode(
15
+ getNeededCampaignUpdate.bytecode[campaign.chainId] || getNeededCampaignUpdate.bytecode[mainnet.id]!,
16
+ inputData,
17
+ )
18
+
19
+ const neededForUpdate = await batchJsonRpc({
20
+ rpc,
21
+ calls: [call],
22
+ outputTypeAbi:
23
+ getNeededCampaignUpdate.outputTypeHr[campaign.chainId] || getNeededCampaignUpdate.outputTypeHr[mainnet.id]!,
24
+ callsKey: `Get needed update - chainId ${campaign.chainId} - rpc ${rpc}`,
25
+ })
26
+
27
+ return neededForUpdate
28
+ }