@@ -2,10 +2,8 @@ import fetch from 'cross-fetch';
22import _strategies from './strategies' ;
33import snapshot from '@snapshot-labs/snapshot.js' ;
44import { getDelegations } from './utils/delegation' ;
5- import { getVp , getDelegations as getCoreDelegations } from './utils/vp' ;
65import { 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
108export 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
4949export 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+
78119export 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 ( / ^ 0 x [ a - f A - F 0 - 9 ] { 40 } $ / . test ( address ) ) return 'evm' ;
146+ if ( / ^ 0 x [ a - f A - F 0 - 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
165199export 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