Skip to content

Commit b88995c

Browse files
wa0x6eChaituVR
andauthored
fix: remove delegation support from getVp (snapshot-labs#1770)
* fix: remove delegation support from getVp * refactor: use `getScoresDirect` to get scores in `getVp` (snapshot-labs#1771) * refactor: use getScoresDirect to fech getVp score * fix: add validations to `getVp` and `getScoresDirect` (snapshot-labs#1772) * fix: add address validation to getvp * fix: add addresses validations when getting scores * perf: --------- Co-authored-by: Chaitanya <yourchaitu@gmail.com> --------- Co-authored-by: Chaitanya <yourchaitu@gmail.com> * fix: add back optional parameter for backward compatibility --------- Co-authored-by: Chaitanya <yourchaitu@gmail.com>
1 parent 4415fd0 commit b88995c

File tree

11 files changed

+355
-597
lines changed

11 files changed

+355
-597
lines changed

src/utils.ts

Lines changed: 105 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ import fetch from 'cross-fetch';
22
import _strategies from './strategies';
33
import snapshot from '@snapshot-labs/snapshot.js';
44
import { getDelegations } from './utils/delegation';
5-
import { getVp, getDelegations as getCoreDelegations } from './utils/vp';
65
import { createHash } from 'crypto';
7-
import { Protocol, Score, Snapshot } from './types';
8-
import { VALID_PROTOCOLS } from './constants';
6+
import { Protocol, Score, Snapshot, VotingPower } from './types';
97

108
export function sha256(str) {
119
return createHash('sha256').update(str).digest('hex');
@@ -25,11 +23,6 @@ async function callStrategy(
2523
) {
2624
return {};
2725
}
28-
29-
if (!_strategies.hasOwnProperty(strategy.name)) {
30-
throw new Error(`Invalid strategy: ${strategy.name}`);
31-
}
32-
3326
const score: Score = await _strategies[strategy.name].strategy(
3427
space,
3528
network,
@@ -38,12 +31,19 @@ async function callStrategy(
3831
strategy.params,
3932
snapshot
4033
);
41-
const addressesLc = addresses.map((address) => address.toLowerCase());
42-
return Object.fromEntries(
43-
Object.entries(score).filter(
44-
([address, vp]) => vp > 0 && addressesLc.includes(address.toLowerCase())
45-
)
34+
35+
const normalizedAddresses = new Set(
36+
addresses.map((address) => address.toLowerCase())
4637
);
38+
const filteredScore: Score = {};
39+
40+
for (const [address, vp] of Object.entries(score)) {
41+
if (vp > 0 && normalizedAddresses.has(address.toLowerCase())) {
42+
filteredScore[address] = vp;
43+
}
44+
}
45+
46+
return filteredScore;
4747
}
4848

4949
export async function getScoresDirect(
@@ -55,16 +55,20 @@ export async function getScoresDirect(
5555
snapshot: Snapshot
5656
): Promise<Score[]> {
5757
try {
58-
const networks = strategies.map((s) => s.network || network);
59-
const snapshots = await getSnapshots(network, snapshot, provider, networks);
60-
// @ts-ignore
6158
if (addresses.length === 0) return strategies.map(() => ({}));
59+
60+
const addressesByProtocol = categorizeAddressesByProtocol(addresses);
61+
validateStrategies(strategies);
62+
63+
const networks = [...new Set(strategies.map((s) => s.network || network))];
64+
const snapshots = await getSnapshots(network, snapshot, provider, networks);
65+
6266
return await Promise.all(
6367
strategies.map((strategy) =>
6468
callStrategy(
6569
space,
6670
strategy.network || network,
67-
addresses,
71+
filterAddressesForStrategy(addressesByProtocol, strategy.name),
6872
strategy,
6973
snapshots[strategy.network || network]
7074
)
@@ -75,6 +79,43 @@ export async function getScoresDirect(
7579
}
7680
}
7781

82+
export async function getVp(
83+
address: string,
84+
network: string,
85+
strategies: any[],
86+
snapshot: Snapshot,
87+
space: string,
88+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
89+
_ = false // kept for backward compatibility
90+
): Promise<VotingPower> {
91+
if (!strategies.length) {
92+
throw new Error('no strategies provided');
93+
}
94+
95+
const scores = await getScoresDirect(
96+
space,
97+
strategies,
98+
network,
99+
getProvider(network),
100+
[address],
101+
snapshot
102+
);
103+
104+
const normalizedAddress = address.toLowerCase();
105+
const vpByStrategy = scores.map((score) => {
106+
const matchingKey = Object.keys(score).find(
107+
(key) => key.toLowerCase() === normalizedAddress
108+
);
109+
return matchingKey ? score[matchingKey] : 0;
110+
});
111+
112+
return {
113+
vp: vpByStrategy.reduce((a, b) => a + b, 0),
114+
vp_by_strategy: vpByStrategy,
115+
vp_state: snapshot === 'latest' ? 'pending' : 'final'
116+
};
117+
}
118+
78119
export function customFetch(
79120
url: RequestInfo | URL,
80121
options: RequestInit = {},
@@ -100,66 +141,59 @@ export function customFetch(
100141
]);
101142
}
102143

103-
/**
104-
* Validates that protocols are non-empty and contain only valid protocol names.
105-
*
106-
* @param protocols - Array of protocol names to validate
107-
*/
108-
function validateProtocols(protocols: Protocol[]): void {
109-
if (!protocols.length) {
110-
throw new Error('At least one protocol must be specified');
111-
}
112-
113-
const invalidProtocols = protocols.filter(
114-
(p) => !VALID_PROTOCOLS.includes(p)
115-
);
116-
if (invalidProtocols.length > 0) {
117-
throw new Error(`Invalid protocol(s): ${invalidProtocols.join(', ')}`);
118-
}
144+
function detectProtocol(address: string): Protocol | null {
145+
if (/^0x[a-fA-F0-9]{40}$/.test(address)) return 'evm';
146+
if (/^0x[a-fA-F0-9]{64}$/.test(address)) return 'starknet';
147+
return null;
119148
}
120149

121-
/**
122-
* Formats addresses according to the specified blockchain protocols.
123-
*
124-
* This function takes a list of addresses and formats them according to the provided
125-
* protocols. It prioritizes EVM formatting when multiple protocols are specified and
126-
* an address is valid for both. If EVM formatting fails but Starknet is supported,
127-
* it falls back to Starknet formatting. Throws an error if any address cannot be
128-
* formatted according to the specified protocols.
129-
*
130-
* @param addresses - Array of blockchain addresses to format
131-
* @param protocols - Array of protocol names to validate against. Defaults to ['evm'].
132-
* Valid protocols are 'evm' and 'starknet'.
133-
*
134-
* @returns Array of formatted addresses in the same order as input
135-
*/
136-
export function getFormattedAddressesByProtocol(
137-
addresses: string[],
138-
protocols: Protocol[] = ['evm']
139-
): string[] {
140-
validateProtocols(protocols);
141-
142-
return addresses.map((address) => {
143-
if (protocols.includes('evm')) {
144-
try {
145-
return snapshot.utils.getFormattedAddress(address, 'evm');
146-
} catch (e) {
147-
// Continue to starknet if evm formatting fails and starknet is supported
148-
}
150+
function categorizeAddressesByProtocol(
151+
addresses: string[]
152+
): Record<Protocol, string[]> {
153+
const results: Record<Protocol, string[]> = {
154+
evm: [],
155+
starknet: []
156+
};
157+
158+
for (const address of addresses) {
159+
const addressType = detectProtocol(address);
160+
if (!addressType) {
161+
throw new Error(`Invalid address format: ${address}`);
149162
}
150163

151-
if (protocols.includes('starknet')) {
152-
try {
153-
return snapshot.utils.getFormattedAddress(address, 'starknet');
154-
} catch (e) {
155-
// Address format not supported by any protocol
164+
try {
165+
const formattedAddress = snapshot.utils.getFormattedAddress(
166+
address,
167+
addressType
168+
);
169+
if (!results[addressType].includes(formattedAddress)) {
170+
results[addressType].push(formattedAddress);
156171
}
172+
} catch {
173+
throw new Error(`Invalid ${addressType} address: ${address}`);
157174
}
175+
}
158176

159-
throw new Error(
160-
`Address "${address}" is not a valid ${protocols.join(' or ')} address`
161-
);
162-
});
177+
return results;
178+
}
179+
180+
function filterAddressesForStrategy(
181+
addressesByProtocol: Record<Protocol, string[]>,
182+
strategyName: string
183+
): string[] {
184+
return _strategies[strategyName].supportedProtocols.flatMap(
185+
(protocol: Protocol) => addressesByProtocol[protocol] || []
186+
);
187+
}
188+
189+
function validateStrategies(strategies: any[]): void {
190+
const invalidStrategies = strategies
191+
.filter((strategy) => !_strategies[strategy.name])
192+
.map((strategy) => strategy.name);
193+
194+
if (invalidStrategies.length > 0) {
195+
throw new Error(`Invalid strategies: ${invalidStrategies.join(', ')}`);
196+
}
163197
}
164198

165199
export const {
@@ -180,7 +214,6 @@ export default {
180214
sha256,
181215
getScoresDirect,
182216
customFetch,
183-
getFormattedAddressesByProtocol,
184217
multicall,
185218
Multicaller,
186219
subgraphRequest,
@@ -193,6 +226,5 @@ export default {
193226
getSnapshots,
194227
getFormattedAddress,
195228
SNAPSHOT_SUBGRAPH_URL,
196-
getVp,
197-
getCoreDelegations
229+
getVp
198230
};

0 commit comments

Comments
 (0)