Skip to content

Commit 8bb09a1

Browse files
authored
feat: allow starknet address in schema and when signing (#1152)
1 parent 1d07362 commit 8bb09a1

File tree

8 files changed

+112
-52
lines changed

8 files changed

+112
-52
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@snapshot-labs/snapshot.js",
3-
"version": "0.12.65",
3+
"version": "0.13.0",
44
"repository": "snapshot-labs/snapshot.js",
55
"license": "MIT",
66
"main": "dist/snapshot.cjs.js",

src/schemas/space.json

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,11 @@
154154
"maxItems": 100,
155155
"items": {
156156
"type": "string",
157-
"format": "evmAddress"
157+
"anyOf": [
158+
{ "type": "string", "format": "evmAddress" },
159+
{ "type": "string", "format": "starknetAddress" }
160+
],
161+
"errorMessage": "Must be a valid address"
158162
},
159163
"title": "members",
160164
"uniqueItems": true
@@ -164,7 +168,11 @@
164168
"maxItems": 100,
165169
"items": {
166170
"type": "string",
167-
"format": "evmAddress"
171+
"anyOf": [
172+
{ "type": "string", "format": "evmAddress" },
173+
{ "type": "string", "format": "starknetAddress" }
174+
],
175+
"errorMessage": "Must be a valid address"
168176
},
169177
"title": "admins",
170178
"uniqueItems": true
@@ -174,7 +182,11 @@
174182
"maxItems": 100,
175183
"items": {
176184
"type": "string",
177-
"format": "evmAddress"
185+
"anyOf": [
186+
{ "type": "string", "format": "evmAddress" },
187+
{ "type": "string", "format": "starknetAddress" }
188+
],
189+
"errorMessage": "Must be a valid address"
178190
},
179191
"title": "moderators",
180192
"uniqueItems": true
@@ -270,8 +282,8 @@
270282
"description": "The address of your delegation contract",
271283
"examples": ["0x3901D0fDe202aF1427216b79f5243f8A022d68cf"],
272284
"anyOf": [
273-
{ "format": "evmAddress" },
274-
{ "format": "starknetAddress" }
285+
{ "type": "string", "format": "evmAddress" },
286+
{ "type": "string", "format": "starknetAddress" }
275287
],
276288
"errorMessage": "Must be a valid EVM of Starknet address"
277289
},
@@ -397,8 +409,8 @@
397409
"title": "Contract address",
398410
"examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"],
399411
"anyOf": [
400-
{ "format": "evmAddress" },
401-
{ "format": "starknetAddress" }
412+
{ "type": "string", "format": "evmAddress" },
413+
{ "type": "string", "format": "starknetAddress" }
402414
],
403415
"errorMessage": "Must be a valid EVM of Starknet address"
404416
},

src/sign/hashedTypes.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,15 @@
6464
"d56782e3b50ac86c25ae292923da8c367e3c9e8e7ea9d8baa435051fe2f430fa": "proposal",
6565
"df10a7eeabe19301d6018be8b6c5d13231320d7ece64d021043fa172b64f3796": "update-proposal",
6666
"beda1f464a6112f9ed6335c4614e32a97f0e18ef4ac10b4b1c8239c475f2d8e8": "proposal",
67-
"ff74674f39ca59b60056ecddaada0cb513c4729e634e99cb778f53ee404ac806": "update-proposal"
67+
"ff74674f39ca59b60056ecddaada0cb513c4729e634e99cb778f53ee404ac806": "update-proposal",
68+
"e037b37311c75c995f732b57cd456bdc685e5eac29668698be34388cafb14097": "proposal",
69+
"9c66485d2c9af4010e89b0352e3f78ce2aa96661c531b1bdbd7042388867e830": "update-proposal",
70+
"dd19ee4357e5bc813529e1b537b77ccb767135701f0223434f3b53f1ac03dcc6": "delete-proposal",
71+
"817265460009cfdbde0752fcc7ef629ae7e05a6e8b6cbffab8a7cbf6cd19e87e": "delete-proposal",
72+
"fb8fa9816cd42974e7f1af671aa548c8c458553364ed809e45042f141de8c0d5": "vote",
73+
"5f95ed849bafb034c37e340dc06ad6fa6985d674714cb602a6bf11119ffba2a1": "vote-array",
74+
"afc5911fd9722b3dc5e8b16a552997510644a52d2b229c3868fb1910b112416e": "vote-string",
75+
"e4b768874f191321f9a6460e15f87e98800c78c1e3104f9d4e443f2ed9b1c45e": "vote",
76+
"86c81555a3dda45aa7b151c574dc27f7402c23d9b1432156f1daa6c2e15f9891": "vote-array",
77+
"c0f418890817b3e6deb58fa3182bf8ed7619242666a9087eab28f27a6876e1da": "vote-string"
6878
}

src/sign/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import fetch from 'cross-fetch';
22
import { Web3Provider } from '@ethersproject/providers';
33
import { Wallet } from '@ethersproject/wallet';
4-
import { getAddress } from '@ethersproject/address';
54
import {
65
Space,
76
Proposal,
@@ -39,6 +38,7 @@ import {
3938
statementTypes
4039
} from './types';
4140
import constants from '../constants.json';
41+
import { getFormattedAddress } from '../utils';
4242

4343
const NAME = 'snapshot';
4444
const VERSION = '0.1.4';
@@ -74,8 +74,10 @@ export default class Client {
7474
async sign(web3: Web3Provider | Wallet, address: string, message, types) {
7575
// @ts-ignore
7676
const signer = web3?.getSigner ? web3.getSigner() : web3;
77-
const checksumAddress = getAddress(address);
78-
message.from = message.from ? getAddress(message.from) : checksumAddress;
77+
const checksumAddress = getFormattedAddress(address, 'evm');
78+
message.from = message.from
79+
? getFormattedAddress(message.from)
80+
: checksumAddress;
7981
if (!message.timestamp)
8082
message.timestamp = parseInt((Date.now() / 1e3).toFixed());
8183

src/sign/types.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export const spaceTypes = {
139139

140140
export const proposalTypes = {
141141
Proposal: [
142-
{ name: 'from', type: 'address' },
142+
{ name: 'from', type: 'string' },
143143
{ name: 'space', type: 'string' },
144144
{ name: 'timestamp', type: 'uint64' },
145145
{ name: 'type', type: 'string' },
@@ -160,7 +160,7 @@ export const proposalTypes = {
160160
export const updateProposalTypes = {
161161
UpdateProposal: [
162162
{ name: 'proposal', type: 'string' },
163-
{ name: 'from', type: 'address' },
163+
{ name: 'from', type: 'string' },
164164
{ name: 'space', type: 'string' },
165165
{ name: 'timestamp', type: 'uint64' },
166166
{ name: 'type', type: 'string' },
@@ -185,7 +185,7 @@ export const flagProposalTypes = {
185185

186186
export const cancelProposalTypes = {
187187
CancelProposal: [
188-
{ name: 'from', type: 'address' },
188+
{ name: 'from', type: 'string' },
189189
{ name: 'space', type: 'string' },
190190
{ name: 'timestamp', type: 'uint64' },
191191
{ name: 'proposal', type: 'string' }
@@ -194,7 +194,7 @@ export const cancelProposalTypes = {
194194

195195
export const cancelProposal2Types = {
196196
CancelProposal: [
197-
{ name: 'from', type: 'address' },
197+
{ name: 'from', type: 'string' },
198198
{ name: 'space', type: 'string' },
199199
{ name: 'timestamp', type: 'uint64' },
200200
{ name: 'proposal', type: 'bytes32' }
@@ -203,7 +203,7 @@ export const cancelProposal2Types = {
203203

204204
export const voteTypes = {
205205
Vote: [
206-
{ name: 'from', type: 'address' },
206+
{ name: 'from', type: 'string' },
207207
{ name: 'space', type: 'string' },
208208
{ name: 'timestamp', type: 'uint64' },
209209
{ name: 'proposal', type: 'string' },
@@ -216,7 +216,7 @@ export const voteTypes = {
216216

217217
export const voteArrayTypes = {
218218
Vote: [
219-
{ name: 'from', type: 'address' },
219+
{ name: 'from', type: 'string' },
220220
{ name: 'space', type: 'string' },
221221
{ name: 'timestamp', type: 'uint64' },
222222
{ name: 'proposal', type: 'string' },
@@ -229,7 +229,7 @@ export const voteArrayTypes = {
229229

230230
export const voteStringTypes = {
231231
Vote: [
232-
{ name: 'from', type: 'address' },
232+
{ name: 'from', type: 'string' },
233233
{ name: 'space', type: 'string' },
234234
{ name: 'timestamp', type: 'uint64' },
235235
{ name: 'proposal', type: 'string' },
@@ -242,7 +242,7 @@ export const voteStringTypes = {
242242

243243
export const vote2Types = {
244244
Vote: [
245-
{ name: 'from', type: 'address' },
245+
{ name: 'from', type: 'string' },
246246
{ name: 'space', type: 'string' },
247247
{ name: 'timestamp', type: 'uint64' },
248248
{ name: 'proposal', type: 'bytes32' },
@@ -255,7 +255,7 @@ export const vote2Types = {
255255

256256
export const voteArray2Types = {
257257
Vote: [
258-
{ name: 'from', type: 'address' },
258+
{ name: 'from', type: 'string' },
259259
{ name: 'space', type: 'string' },
260260
{ name: 'timestamp', type: 'uint64' },
261261
{ name: 'proposal', type: 'bytes32' },
@@ -268,7 +268,7 @@ export const voteArray2Types = {
268268

269269
export const voteString2Types = {
270270
Vote: [
271-
{ name: 'from', type: 'address' },
271+
{ name: 'from', type: 'string' },
272272
{ name: 'space', type: 'string' },
273273
{ name: 'timestamp', type: 'uint64' },
274274
{ name: 'proposal', type: 'bytes32' },

src/utils.spec.js

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -504,37 +504,68 @@ describe('utils', () => {
504504
});
505505

506506
describe('getFormattedAddress', () => {
507-
test('returns a checksummed EVM address', () => {
508-
const address = '0x91fd2c8d24767db4ece7069aa27832ffaf8590f3';
509-
expect(getFormattedAddress(address, 'evm')).toEqual(
510-
'0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3'
511-
);
512-
});
507+
describe('when explicitly passing an address type', () => {
508+
test('returns a checksummed EVM address', () => {
509+
const address = '0x91fd2c8d24767db4ece7069aa27832ffaf8590f3';
510+
expect(getFormattedAddress(address, 'evm')).toEqual(
511+
'0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3'
512+
);
513+
});
513514

514-
test('returns a padded and lowercased starknet address', () => {
515-
const address =
516-
'0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0';
517-
expect(getFormattedAddress(address, 'starknet')).toEqual(
518-
'0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'
519-
);
520-
});
515+
test('returns a padded and lowercased starknet address', () => {
516+
const address =
517+
'0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0';
518+
expect(getFormattedAddress(address, 'starknet')).toEqual(
519+
'0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'
520+
);
521+
});
521522

522-
test('returns an EVM address as starknet address', () => {
523-
const address = '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3';
524-
expect(getFormattedAddress(address, 'starknet')).toEqual(
525-
'0x00000000000000000000000091fd2c8d24767db4ece7069aa27832ffaf8590f3'
526-
);
527-
});
523+
test('returns an EVM address as starknet address', () => {
524+
const address = '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3';
525+
expect(getFormattedAddress(address, 'starknet')).toEqual(
526+
'0x00000000000000000000000091fd2c8d24767db4ece7069aa27832ffaf8590f3'
527+
);
528+
});
528529

529-
test('throws an error when the address is not a starknet address', () => {
530-
const address = 'hello';
531-
expect(() => getFormattedAddress(address, 'starknet')).toThrow();
530+
test('throws an error when the address is not a starknet address', () => {
531+
const address = 'hello';
532+
expect(() => getFormattedAddress(address, 'starknet')).toThrow();
533+
});
534+
535+
test('throws an error when the address is not an EVM address', () => {
536+
const address =
537+
'0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0';
538+
expect(() => getFormattedAddress(address, 'evm')).toThrow();
539+
});
532540
});
533541

534-
test('throws an error when the address is not an EVM address', () => {
535-
const address =
536-
'0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0';
537-
expect(() => getFormattedAddress(address, 'evm')).toThrow();
542+
describe('when not passing an address type', () => {
543+
test('returns a checksummed EVM address for an EVM input', () => {
544+
const address = '0x91fd2c8d24767db4ece7069aa27832ffaf8590f3';
545+
expect(getFormattedAddress(address)).toEqual(
546+
'0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3'
547+
);
548+
});
549+
550+
test('returns a padded and lowercased starknet address for a Starknet input', () => {
551+
const address =
552+
'0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0';
553+
expect(getFormattedAddress(address)).toEqual(
554+
'0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'
555+
);
556+
});
557+
558+
test('throws an error when the input is not address-like', () => {
559+
const address = 'hello';
560+
expect(() => getFormattedAddress(address)).toThrow();
561+
});
562+
563+
test('returns a padded and lowercased starknet address for any non-EVM like address input', () => {
564+
const address = '0x1';
565+
expect(getFormattedAddress(address)).toEqual(
566+
'0x0000000000000000000000000000000000000000000000000000000000000001'
567+
);
568+
});
538569
});
539570
});
540571

src/utils.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -828,10 +828,15 @@ export function isEvmAddress(address: string): boolean {
828828

829829
export function getFormattedAddress(
830830
address: string,
831-
format: 'evm' | 'starknet'
831+
format?: 'evm' | 'starknet'
832832
): string {
833-
if (format === 'evm' && isEvmAddress(address)) return getAddress(address);
834-
if (format === 'starknet' && isStarknetAddress(address))
833+
// Consider non-evm addresses as Starknet by default
834+
// as there's no other way to differentiate them
835+
const addressType = format ?? (isEvmAddress(address) ? 'evm' : 'starknet');
836+
837+
if (addressType === 'evm' && isEvmAddress(address))
838+
return getAddress(address);
839+
if (addressType === 'starknet' && isStarknetAddress(address))
835840
return validateAndParseAddress(address);
836841

837842
throw new Error(`Invalid address: ${address}`);

test/e2e/verify/index.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ describe('verify', () => {
1919
expect(verify(payload.address, payload.sig, payload.data)).resolves.toBe(
2020
true
2121
);
22-
});
22+
}, 15000);
2323
});

0 commit comments

Comments
 (0)