Skip to content
This repository was archived by the owner on Aug 22, 2025. It is now read-only.

Commit c07d333

Browse files
committed
fix: throw error on invalid address when calling getVp
1 parent 6d97b0a commit c07d333

3 files changed

Lines changed: 182 additions & 80 deletions

File tree

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import { Protocol } from './types';
22

33
export const DEFAULT_SUPPORTED_PROTOCOLS: Protocol[] = ['evm'];
4+
export const VALID_PROTOCOLS: Protocol[] = ['evm', 'starknet'];

src/utils.ts

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { getDelegations } from './utils/delegation';
55
import { getVp, getDelegations as getCoreDelegations } from './utils/vp';
66
import { createHash } from 'crypto';
77
import { Protocol, Score, Snapshot } from './types';
8+
import { VALID_PROTOCOLS } from './constants';
89

910
export function sha256(str) {
1011
return createHash('sha256').update(str).digest('hex');
@@ -99,33 +100,66 @@ export function customFetch(
99100
]);
100101
}
101102

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+
}
119+
}
120+
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+
*/
102136
export function getFormattedAddressesByProtocol(
103137
addresses: string[],
104138
protocols: Protocol[] = ['evm']
105139
): string[] {
106-
if (!protocols.length) {
107-
throw new Error('At least one protocol must be specified');
108-
}
140+
validateProtocols(protocols);
109141

110-
return addresses
111-
.map((address) => {
112-
if (protocols.includes('evm')) {
113-
try {
114-
return snapshot.utils.getFormattedAddress(address, 'evm');
115-
} catch (e) {
116-
// Continue to starknet if evm formatting fails and starknet is supported
117-
}
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
118148
}
149+
}
119150

120-
if (protocols.includes('starknet')) {
121-
try {
122-
return snapshot.utils.getFormattedAddress(address, 'starknet');
123-
} catch (e) {
124-
// Address format not supported by any protocol
125-
}
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
126156
}
127-
})
128-
.filter(Boolean) as string[];
157+
}
158+
159+
throw new Error(
160+
`Address "${address}" is not a valid ${protocols.join(' or ')} address`
161+
);
162+
});
129163
}
130164

131165
export const {

test/unit/utils.test.ts

Lines changed: 128 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -10,80 +10,147 @@ describe('utils', () => {
1010
'0x07f71118e351c02f6ec7099c8cdf93aed66ced8406e94631cc91637f7d7f203a';
1111

1212
describe('getFormattedAddressesByProtocol()', () => {
13-
const input = [
14-
VALID_EVM_ADDRESS,
15-
'invalidEVMAddress',
16-
VALID_STARKNET_ADDRESS,
17-
''
18-
];
19-
20-
it('should return an empty array when no addresses are provided', () => {
21-
const result = getFormattedAddressesByProtocol([]);
22-
expect(result).toEqual([]);
23-
});
13+
// Test data constants
14+
const INVALID_ADDRESS = 'invalidAddress';
15+
const EMPTY_ADDRESS = '';
16+
const STARKNET_ONLY_ADDRESS = VALID_STARKNET_ADDRESS;
17+
const EVM_ONLY_ADDRESS = VALID_EVM_ADDRESS;
2418

25-
it('should return an empty array when protocol is not valid', () => {
26-
const result = getFormattedAddressesByProtocol(input, [
27-
// @ts-ignore
28-
'invalidProtocol'
29-
]);
30-
expect(result).toEqual([]);
31-
});
19+
describe('Basic functionality', () => {
20+
it('should return an empty array when no addresses are provided', () => {
21+
const result = getFormattedAddressesByProtocol([]);
22+
expect(result).toEqual([]);
23+
});
3224

33-
it('should throw an error when no protocol is provided', () => {
34-
expect(() => {
35-
getFormattedAddressesByProtocol([], []);
36-
}).toThrow();
25+
it('should use evm as default protocol when no protocols provided', () => {
26+
const result = getFormattedAddressesByProtocol([EVM_ONLY_ADDRESS]);
27+
expect(result).toEqual([VALID_FORMATTED_EVM_ADDRESS]);
28+
});
3729
});
3830

39-
it('should use evm as default protocol when no protocols provided', () => {
40-
const result = getFormattedAddressesByProtocol(input);
41-
expect(result).toEqual([VALID_FORMATTED_EVM_ADDRESS]);
42-
});
31+
describe('Protocol validation', () => {
32+
it('should throw an error when no protocols are provided', () => {
33+
expect(() => {
34+
getFormattedAddressesByProtocol([], []);
35+
}).toThrow('At least one protocol must be specified');
36+
});
4337

44-
it('should prioritize evm when address is valid for both protocols and both are specified', () => {
45-
const result = getFormattedAddressesByProtocol(
46-
[VALID_EVM_ADDRESS],
47-
['evm', 'starknet']
48-
);
49-
expect(result).toEqual([VALID_FORMATTED_EVM_ADDRESS]);
50-
});
38+
it('should throw an error for single invalid protocol', () => {
39+
expect(() => {
40+
getFormattedAddressesByProtocol(
41+
[EVM_ONLY_ADDRESS],
42+
[
43+
// @ts-ignore
44+
'invalidProtocol'
45+
]
46+
);
47+
}).toThrow('Invalid protocol(s): invalidProtocol');
48+
});
5149

52-
it('should return only formatted EVM addresses on evm protocol', () => {
53-
const result = getFormattedAddressesByProtocol(input, ['evm']);
54-
expect(result).toEqual([VALID_FORMATTED_EVM_ADDRESS]);
50+
it('should throw an error for multiple invalid protocols', () => {
51+
expect(() => {
52+
getFormattedAddressesByProtocol(
53+
[EVM_ONLY_ADDRESS],
54+
[
55+
// @ts-ignore
56+
'invalidProtocol1',
57+
// @ts-ignore
58+
'invalidProtocol2'
59+
]
60+
);
61+
}).toThrow('Invalid protocol(s): invalidProtocol1, invalidProtocol2');
62+
});
5563
});
5664

57-
it('should return only formatted starknet addresses on starknet protocol', () => {
58-
const result = getFormattedAddressesByProtocol(input, ['starknet']);
59-
expect(result).toEqual([VALID_FORMATTED_STARKNET_ADDRESS]);
60-
});
65+
describe('Single protocol formatting', () => {
66+
it('should format EVM addresses correctly', () => {
67+
const result = getFormattedAddressesByProtocol(
68+
[EVM_ONLY_ADDRESS],
69+
['evm']
70+
);
71+
expect(result).toEqual([VALID_FORMATTED_EVM_ADDRESS]);
72+
});
6173

62-
it('should return both formatted starknet and evm addresses on starknet and evm protocol', () => {
63-
const result = getFormattedAddressesByProtocol(input, [
64-
'starknet',
65-
'evm'
66-
]);
67-
expect(result).toEqual([
68-
VALID_FORMATTED_EVM_ADDRESS,
69-
VALID_FORMATTED_STARKNET_ADDRESS
70-
]);
74+
it('should format Starknet addresses correctly', () => {
75+
const result = getFormattedAddressesByProtocol(
76+
[STARKNET_ONLY_ADDRESS],
77+
['starknet']
78+
);
79+
expect(result).toEqual([VALID_FORMATTED_STARKNET_ADDRESS]);
80+
});
7181
});
7282

73-
it('should return empty array when all addresses are invalid for specified protocol', () => {
74-
const result = getFormattedAddressesByProtocol(
75-
['invalidAddress'],
76-
['evm']
77-
);
78-
expect(result).toEqual([]);
83+
describe('Multiple protocol formatting', () => {
84+
it('should prioritize EVM when address is valid for both protocols', () => {
85+
const result = getFormattedAddressesByProtocol(
86+
[EVM_ONLY_ADDRESS],
87+
['evm', 'starknet']
88+
);
89+
expect(result).toEqual([VALID_FORMATTED_EVM_ADDRESS]);
90+
});
91+
92+
it('should fall back to Starknet when EVM formatting fails', () => {
93+
const result = getFormattedAddressesByProtocol(
94+
[STARKNET_ONLY_ADDRESS],
95+
['evm', 'starknet']
96+
);
97+
expect(result).toEqual([VALID_FORMATTED_STARKNET_ADDRESS]);
98+
});
99+
100+
it('should format addresses from different protocols correctly', () => {
101+
const result = getFormattedAddressesByProtocol(
102+
[EVM_ONLY_ADDRESS, STARKNET_ONLY_ADDRESS],
103+
['evm', 'starknet']
104+
);
105+
expect(result).toEqual([
106+
VALID_FORMATTED_EVM_ADDRESS,
107+
VALID_FORMATTED_STARKNET_ADDRESS
108+
]);
109+
});
110+
111+
it('should maintain protocol order independence for multiple valid protocols', () => {
112+
const result1 = getFormattedAddressesByProtocol(
113+
[EVM_ONLY_ADDRESS],
114+
['evm', 'starknet']
115+
);
116+
const result2 = getFormattedAddressesByProtocol(
117+
[EVM_ONLY_ADDRESS],
118+
['starknet', 'evm']
119+
);
120+
expect(result1).toEqual(result2);
121+
});
79122
});
80123

81-
it('should return empty array when all addresses are invalid for specified protocol', () => {
82-
const result = getFormattedAddressesByProtocol(
83-
['invalidAddress'],
84-
['starknet']
85-
);
86-
expect(result).toEqual([]);
124+
describe('Error handling', () => {
125+
it('should throw an error for completely invalid addresses', () => {
126+
expect(() => {
127+
getFormattedAddressesByProtocol([INVALID_ADDRESS], ['evm']);
128+
}).toThrow('is not a valid evm address');
129+
});
130+
131+
it('should throw an error for empty string addresses', () => {
132+
expect(() => {
133+
getFormattedAddressesByProtocol([EMPTY_ADDRESS], ['evm']);
134+
}).toThrow('is not a valid evm address');
135+
});
136+
137+
it('should throw an error when address is invalid for all specified protocols', () => {
138+
expect(() => {
139+
getFormattedAddressesByProtocol(
140+
[INVALID_ADDRESS],
141+
['evm', 'starknet']
142+
);
143+
}).toThrow('is not a valid evm or starknet address');
144+
});
145+
146+
it('should throw an error on first invalid address in mixed array', () => {
147+
expect(() => {
148+
getFormattedAddressesByProtocol(
149+
[EVM_ONLY_ADDRESS, INVALID_ADDRESS, STARKNET_ONLY_ADDRESS],
150+
['evm', 'starknet']
151+
);
152+
}).toThrow('is not a valid evm or starknet address');
153+
});
87154
});
88155
});
89156
});

0 commit comments

Comments
 (0)