@snapshot-labs/snapshot.js 0.12.0-beta.0 → 0.12.0-beta.1
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.
- package/README.md +2 -2
- package/dist/index.d.ts +20 -2
- package/dist/schemas/index.d.ts +19 -1
- package/dist/sign/types.d.ts +2 -0
- package/dist/snapshot.cjs.js +1851 -1929
- package/dist/snapshot.esm.js +1852 -1930
- package/dist/snapshot.min.js +16 -5
- package/dist/src/index.d.ts +725 -0
- package/dist/src/schemas/index.d.ts +671 -0
- package/dist/src/sign/index.d.ts +29 -0
- package/dist/src/sign/types.d.ts +227 -0
- package/dist/src/utils/blockfinder.d.ts +1 -0
- package/dist/src/utils/delegation.d.ts +18 -0
- package/dist/src/utils/multicaller.d.ts +12 -0
- package/dist/src/utils/provider.d.ts +5 -0
- package/dist/src/utils/web3.d.ts +2 -0
- package/dist/src/utils.d.ts +91 -0
- package/dist/src/verify/evm.d.ts +4 -0
- package/dist/src/verify/evm.spec.d.ts +1 -0
- package/dist/src/verify/index.d.ts +11 -0
- package/dist/src/verify/index.spec.d.ts +1 -0
- package/dist/src/verify/starknet.d.ts +6 -0
- package/dist/src/verify/starknet.spec.d.ts +1 -0
- package/dist/src/voting/approval.d.ts +22 -0
- package/dist/src/voting/index.d.ts +14 -0
- package/dist/src/voting/quadratic.d.ts +20 -0
- package/dist/src/voting/rankedChoice.d.ts +18 -0
- package/dist/src/voting/singleChoice.d.ts +18 -0
- package/dist/src/voting/types.d.ts +35 -0
- package/dist/src/voting/weighted.d.ts +26 -0
- package/dist/utils/delegation.d.ts +1 -1
- package/dist/utils.d.ts +3 -2
- package/package.json +6 -3
- package/src/delegationSubgraphs.json +8 -8
- package/src/gateways.json +1 -2
- package/src/networks.json +222 -60
- package/src/schemas/profile.json +30 -0
- package/src/schemas/proposal.json +1 -1
- package/src/schemas/space.json +8 -7
- package/src/schemas/statement.json +17 -2
- package/src/schemas/vote.json +1 -1
- package/src/sign/hashedTypes.json +8 -1
- package/src/sign/index.ts +8 -6
- package/src/sign/types.ts +11 -1
- package/src/utils/provider.ts +8 -2
- package/src/utils/web3.ts +1 -1
- package/src/utils.spec.js +86 -31
- package/src/utils.ts +111 -38
- package/src/verify/evm.spec.ts +32 -0
- package/src/verify/evm.ts +82 -0
- package/src/verify/index.spec.ts +84 -0
- package/src/verify/index.ts +41 -0
- package/src/verify/starknet.spec.ts +55 -0
- package/src/verify/starknet.ts +82 -0
- package/src/sign/eip1271.ts +0 -55
- package/src/sign/utils.ts +0 -27
package/src/utils.spec.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, test, expect, vi, afterEach } from 'vitest';
|
|
2
2
|
import * as crossFetch from 'cross-fetch';
|
|
3
|
-
import { validate, getScores, getVp } from './utils';
|
|
3
|
+
import { validate, getScores, getVp, getFormattedAddress } from './utils';
|
|
4
4
|
|
|
5
5
|
vi.mock('cross-fetch', async () => {
|
|
6
6
|
const actual = await vi.importActual('cross-fetch');
|
|
@@ -78,13 +78,14 @@ describe('utils', () => {
|
|
|
78
78
|
|
|
79
79
|
describe('when passing valid args', () => {
|
|
80
80
|
test('send a JSON-RPC payload to score-api', async () => {
|
|
81
|
+
const result = { result: 'OK' };
|
|
81
82
|
fetch.mockReturnValue({
|
|
82
|
-
|
|
83
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
83
84
|
});
|
|
84
85
|
|
|
85
86
|
expect(_validate({})).resolves;
|
|
86
87
|
expect(fetch).toHaveBeenCalledWith(
|
|
87
|
-
'https://score.snapshot.org',
|
|
88
|
+
'https://score.snapshot.org/',
|
|
88
89
|
expect.objectContaining({
|
|
89
90
|
body: JSON.stringify({
|
|
90
91
|
jsonrpc: '2.0',
|
|
@@ -96,13 +97,14 @@ describe('utils', () => {
|
|
|
96
97
|
});
|
|
97
98
|
|
|
98
99
|
test('send a POST request with JSON content-type', async () => {
|
|
100
|
+
const result = { result: 'OK' };
|
|
99
101
|
fetch.mockReturnValue({
|
|
100
|
-
|
|
102
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
101
103
|
});
|
|
102
104
|
|
|
103
105
|
expect(_validate({})).resolves;
|
|
104
106
|
expect(fetch).toHaveBeenCalledWith(
|
|
105
|
-
'https://score.snapshot.org',
|
|
107
|
+
'https://score.snapshot.org/',
|
|
106
108
|
expect.objectContaining({
|
|
107
109
|
method: 'POST',
|
|
108
110
|
headers: {
|
|
@@ -114,23 +116,28 @@ describe('utils', () => {
|
|
|
114
116
|
});
|
|
115
117
|
|
|
116
118
|
test('can customize the score-api url', () => {
|
|
119
|
+
const result = { result: 'OK' };
|
|
117
120
|
fetch.mockReturnValue({
|
|
118
|
-
|
|
121
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
119
122
|
});
|
|
120
123
|
|
|
121
124
|
expect(
|
|
122
125
|
_validate({ options: { url: 'https://snapshot.org/?apiKey=xxx' } })
|
|
123
126
|
).resolves;
|
|
124
127
|
expect(fetch).toHaveBeenCalledWith(
|
|
125
|
-
'https://snapshot.org
|
|
126
|
-
expect.
|
|
128
|
+
'https://snapshot.org/',
|
|
129
|
+
expect.objectContaining({
|
|
130
|
+
headers: expect.objectContaining({
|
|
131
|
+
'X-API-KEY': 'xxx'
|
|
132
|
+
})
|
|
133
|
+
})
|
|
127
134
|
);
|
|
128
135
|
});
|
|
129
136
|
|
|
130
137
|
test('returns the JSON-RPC result property', () => {
|
|
131
138
|
const result = { result: 'OK' };
|
|
132
139
|
fetch.mockReturnValue({
|
|
133
|
-
|
|
140
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
134
141
|
});
|
|
135
142
|
|
|
136
143
|
expect(_validate({})).resolves.toEqual('OK');
|
|
@@ -141,7 +148,7 @@ describe('utils', () => {
|
|
|
141
148
|
test('rejects with the JSON-RPC error object', () => {
|
|
142
149
|
const result = { error: { message: 'Oh no' } };
|
|
143
150
|
fetch.mockReturnValue({
|
|
144
|
-
|
|
151
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
145
152
|
});
|
|
146
153
|
|
|
147
154
|
expect(_validate({})).rejects.toEqual(result.error);
|
|
@@ -152,7 +159,7 @@ describe('utils', () => {
|
|
|
152
159
|
test('rejects with the error', () => {
|
|
153
160
|
const result = new Error('Oh no');
|
|
154
161
|
fetch.mockReturnValue({
|
|
155
|
-
|
|
162
|
+
text: () => {
|
|
156
163
|
throw result;
|
|
157
164
|
}
|
|
158
165
|
});
|
|
@@ -196,7 +203,7 @@ describe('utils', () => {
|
|
|
196
203
|
network ?? payload.network,
|
|
197
204
|
addresses ?? payload.addresses,
|
|
198
205
|
snapshot ?? payload.snapshot,
|
|
199
|
-
scoreApiUrl ?? 'https://score.snapshot.org',
|
|
206
|
+
scoreApiUrl ?? 'https://score.snapshot.org/',
|
|
200
207
|
options ?? {}
|
|
201
208
|
);
|
|
202
209
|
}
|
|
@@ -239,8 +246,9 @@ describe('utils', () => {
|
|
|
239
246
|
|
|
240
247
|
describe('when passing valid args', () => {
|
|
241
248
|
test('send a JSON-RPC payload to score-api', async () => {
|
|
249
|
+
const result = { result: 'OK' };
|
|
242
250
|
fetch.mockReturnValue({
|
|
243
|
-
|
|
251
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
244
252
|
});
|
|
245
253
|
|
|
246
254
|
expect(_getScores({})).resolves;
|
|
@@ -253,8 +261,9 @@ describe('utils', () => {
|
|
|
253
261
|
});
|
|
254
262
|
|
|
255
263
|
test('send a POST request with JSON content-type', async () => {
|
|
264
|
+
const result = { result: 'OK' };
|
|
256
265
|
fetch.mockReturnValue({
|
|
257
|
-
|
|
266
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
258
267
|
});
|
|
259
268
|
|
|
260
269
|
expect(_getScores({})).resolves;
|
|
@@ -270,23 +279,29 @@ describe('utils', () => {
|
|
|
270
279
|
);
|
|
271
280
|
});
|
|
272
281
|
|
|
273
|
-
test('can customize the score-api url', () => {
|
|
282
|
+
test('can customize the score-api url and if apiKey should be passed in headers', () => {
|
|
283
|
+
const result = { result: 'OK' };
|
|
274
284
|
fetch.mockReturnValue({
|
|
275
|
-
|
|
285
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
276
286
|
});
|
|
277
287
|
|
|
278
288
|
expect(_getScores({ scoreApiUrl: 'https://snapshot.org/?apiKey=xxx' }))
|
|
279
289
|
.resolves;
|
|
280
290
|
expect(fetch).toHaveBeenCalledWith(
|
|
281
|
-
'https://snapshot.org/api/scores
|
|
282
|
-
expect.
|
|
291
|
+
'https://snapshot.org/api/scores',
|
|
292
|
+
expect.objectContaining({
|
|
293
|
+
headers: expect.objectContaining({
|
|
294
|
+
'X-API-KEY': 'xxx'
|
|
295
|
+
})
|
|
296
|
+
})
|
|
283
297
|
);
|
|
284
298
|
});
|
|
285
299
|
|
|
286
300
|
test('returns the JSON-RPC result scores property', () => {
|
|
287
301
|
const result = { scores: 'SCORES', other: 'Other' };
|
|
288
302
|
fetch.mockReturnValue({
|
|
289
|
-
|
|
303
|
+
text: () =>
|
|
304
|
+
new Promise((resolve) => resolve(JSON.stringify({ result })))
|
|
290
305
|
});
|
|
291
306
|
|
|
292
307
|
expect(_getScores({})).resolves.toEqual('SCORES');
|
|
@@ -295,7 +310,8 @@ describe('utils', () => {
|
|
|
295
310
|
test('returns the JSON-RPC all properties', () => {
|
|
296
311
|
const result = { scores: 'SCORES', other: 'Other' };
|
|
297
312
|
fetch.mockReturnValue({
|
|
298
|
-
|
|
313
|
+
text: () =>
|
|
314
|
+
new Promise((resolve) => resolve(JSON.stringify({ result })))
|
|
299
315
|
});
|
|
300
316
|
|
|
301
317
|
expect(
|
|
@@ -308,7 +324,7 @@ describe('utils', () => {
|
|
|
308
324
|
test('rejects with the JSON-RPC error object', () => {
|
|
309
325
|
const result = { error: { message: 'Oh no' } };
|
|
310
326
|
fetch.mockReturnValue({
|
|
311
|
-
|
|
327
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
312
328
|
});
|
|
313
329
|
|
|
314
330
|
expect(_getScores({})).rejects.toEqual(result.error);
|
|
@@ -319,7 +335,7 @@ describe('utils', () => {
|
|
|
319
335
|
test('rejects with the error', () => {
|
|
320
336
|
const result = new Error('Oh no');
|
|
321
337
|
fetch.mockReturnValue({
|
|
322
|
-
|
|
338
|
+
text: () => {
|
|
323
339
|
throw result;
|
|
324
340
|
}
|
|
325
341
|
});
|
|
@@ -391,13 +407,14 @@ describe('utils', () => {
|
|
|
391
407
|
|
|
392
408
|
describe('when passing valid args', () => {
|
|
393
409
|
test('send a JSON-RPC payload to score-api', async () => {
|
|
410
|
+
const result = { result: 'OK' };
|
|
394
411
|
fetch.mockReturnValue({
|
|
395
|
-
|
|
412
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
396
413
|
});
|
|
397
414
|
|
|
398
415
|
expect(_getVp({})).resolves;
|
|
399
416
|
expect(fetch).toHaveBeenCalledWith(
|
|
400
|
-
'https://score.snapshot.org',
|
|
417
|
+
'https://score.snapshot.org/',
|
|
401
418
|
expect.objectContaining({
|
|
402
419
|
body: JSON.stringify({
|
|
403
420
|
jsonrpc: '2.0',
|
|
@@ -409,13 +426,14 @@ describe('utils', () => {
|
|
|
409
426
|
});
|
|
410
427
|
|
|
411
428
|
test('send a POST request with JSON content-type', async () => {
|
|
429
|
+
const result = { result: 'OK' };
|
|
412
430
|
fetch.mockReturnValue({
|
|
413
|
-
|
|
431
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
414
432
|
});
|
|
415
433
|
|
|
416
434
|
expect(_getVp({})).resolves;
|
|
417
435
|
expect(fetch).toHaveBeenCalledWith(
|
|
418
|
-
'https://score.snapshot.org',
|
|
436
|
+
'https://score.snapshot.org/',
|
|
419
437
|
expect.objectContaining({
|
|
420
438
|
method: 'POST',
|
|
421
439
|
headers: {
|
|
@@ -427,13 +445,14 @@ describe('utils', () => {
|
|
|
427
445
|
});
|
|
428
446
|
|
|
429
447
|
test('can customize the score-api url', () => {
|
|
448
|
+
const result = { result: 'OK' };
|
|
430
449
|
fetch.mockReturnValue({
|
|
431
|
-
|
|
450
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
432
451
|
});
|
|
433
452
|
|
|
434
453
|
expect(_getVp({ options: { url: 'https://snapshot.org' } })).resolves;
|
|
435
454
|
expect(fetch).toHaveBeenCalledWith(
|
|
436
|
-
'https://snapshot.org',
|
|
455
|
+
'https://snapshot.org/',
|
|
437
456
|
expect.anything()
|
|
438
457
|
);
|
|
439
458
|
});
|
|
@@ -441,7 +460,8 @@ describe('utils', () => {
|
|
|
441
460
|
test('returns the JSON-RPC result property', () => {
|
|
442
461
|
const result = { data: 'OK' };
|
|
443
462
|
fetch.mockReturnValue({
|
|
444
|
-
|
|
463
|
+
text: () =>
|
|
464
|
+
new Promise((resolve) => resolve(JSON.stringify({ result })))
|
|
445
465
|
});
|
|
446
466
|
|
|
447
467
|
expect(_getVp({})).resolves.toEqual(result);
|
|
@@ -452,7 +472,7 @@ describe('utils', () => {
|
|
|
452
472
|
test('rejects with the JSON-RPC error object', () => {
|
|
453
473
|
const result = { error: { message: 'Oh no' } };
|
|
454
474
|
fetch.mockReturnValue({
|
|
455
|
-
|
|
475
|
+
text: () => new Promise((resolve) => resolve(JSON.stringify(result)))
|
|
456
476
|
});
|
|
457
477
|
|
|
458
478
|
expect(_getVp({})).rejects.toEqual(result.error);
|
|
@@ -463,7 +483,7 @@ describe('utils', () => {
|
|
|
463
483
|
test('rejects with the error', () => {
|
|
464
484
|
const result = new Error('Oh no');
|
|
465
485
|
fetch.mockReturnValue({
|
|
466
|
-
|
|
486
|
+
text: () => {
|
|
467
487
|
throw result;
|
|
468
488
|
}
|
|
469
489
|
});
|
|
@@ -472,4 +492,39 @@ describe('utils', () => {
|
|
|
472
492
|
});
|
|
473
493
|
});
|
|
474
494
|
});
|
|
495
|
+
|
|
496
|
+
describe('getFormattedAddress', () => {
|
|
497
|
+
test('returns a checksummed EVM address', () => {
|
|
498
|
+
const address = '0x91fd2c8d24767db4ece7069aa27832ffaf8590f3';
|
|
499
|
+
expect(getFormattedAddress(address, ['evm'])).toEqual(
|
|
500
|
+
'0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3'
|
|
501
|
+
);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
test('returns a padded and lowercased starknet address', () => {
|
|
505
|
+
const address =
|
|
506
|
+
'0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0';
|
|
507
|
+
expect(getFormattedAddress(address, ['starknet'])).toEqual(
|
|
508
|
+
'0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0'
|
|
509
|
+
);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
test('returns an EVM address as starknet address', () => {
|
|
513
|
+
const address = '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3';
|
|
514
|
+
expect(getFormattedAddress(address, ['starknet'])).toEqual(
|
|
515
|
+
'0x00000000000000000000000091fd2c8d24767db4ece7069aa27832ffaf8590f3'
|
|
516
|
+
);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
test('throws an error when the address is not a starknet address', () => {
|
|
520
|
+
const address = 'hello';
|
|
521
|
+
expect(() => getFormattedAddress(address, ['starknet'])).toThrow();
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
test('throws an error when the address is not an EVM address', () => {
|
|
525
|
+
const address =
|
|
526
|
+
'0x2a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0';
|
|
527
|
+
expect(() => getFormattedAddress(address, ['evm'])).toThrow();
|
|
528
|
+
});
|
|
529
|
+
});
|
|
475
530
|
});
|
package/src/utils.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fetch from 'cross-fetch';
|
|
2
2
|
import { Interface } from '@ethersproject/abi';
|
|
3
3
|
import { Contract } from '@ethersproject/contracts';
|
|
4
|
-
import { isAddress } from '@ethersproject/address';
|
|
4
|
+
import { getAddress, isAddress } from '@ethersproject/address';
|
|
5
5
|
import { parseUnits } from '@ethersproject/units';
|
|
6
6
|
import { namehash, ensNormalize } from '@ethersproject/hash';
|
|
7
7
|
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
|
|
@@ -12,14 +12,16 @@ import Multicaller from './utils/multicaller';
|
|
|
12
12
|
import { getSnapshots } from './utils/blockfinder';
|
|
13
13
|
import getProvider from './utils/provider';
|
|
14
14
|
import { signMessage, getBlockNumber } from './utils/web3';
|
|
15
|
-
import { getHash, verify } from './
|
|
15
|
+
import { getHash, verify } from './verify';
|
|
16
16
|
import gateways from './gateways.json';
|
|
17
17
|
import networks from './networks.json';
|
|
18
18
|
import voting from './voting';
|
|
19
19
|
import getDelegatesBySpace, { SNAPSHOT_SUBGRAPH_URL } from './utils/delegation';
|
|
20
|
+
import { validateAndParseAddress } from 'starknet';
|
|
20
21
|
|
|
21
22
|
interface Options {
|
|
22
23
|
url?: string;
|
|
24
|
+
headers?: any;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
interface Strategy {
|
|
@@ -38,6 +40,43 @@ const scoreApiHeaders = {
|
|
|
38
40
|
'Content-Type': 'application/json'
|
|
39
41
|
};
|
|
40
42
|
|
|
43
|
+
const DEFAULT_SCORE_API_URL = 'https://score.snapshot.org';
|
|
44
|
+
|
|
45
|
+
function formatScoreAPIUrl(
|
|
46
|
+
url = DEFAULT_SCORE_API_URL,
|
|
47
|
+
options = {
|
|
48
|
+
path: ''
|
|
49
|
+
}
|
|
50
|
+
) {
|
|
51
|
+
const scoreURL = new URL(url);
|
|
52
|
+
if (options.path) scoreURL.pathname = options.path;
|
|
53
|
+
const apiKey = scoreURL.searchParams.get('apiKey');
|
|
54
|
+
let headers: any = { ...scoreApiHeaders };
|
|
55
|
+
if (apiKey) {
|
|
56
|
+
scoreURL.searchParams.delete('apiKey');
|
|
57
|
+
headers = { ...scoreApiHeaders, 'X-API-KEY': apiKey };
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
url: scoreURL.toString(),
|
|
61
|
+
headers
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function parseScoreAPIResponse(res: any) {
|
|
66
|
+
let data: any = await res.text();
|
|
67
|
+
try {
|
|
68
|
+
data = JSON.parse(data);
|
|
69
|
+
} catch (e: any) {
|
|
70
|
+
return Promise.reject({
|
|
71
|
+
code: res.status || 500,
|
|
72
|
+
message: 'Failed to parse response from score API',
|
|
73
|
+
data
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (data.error) return Promise.reject(data.error);
|
|
77
|
+
return data;
|
|
78
|
+
}
|
|
79
|
+
|
|
41
80
|
const ajv = new Ajv({
|
|
42
81
|
allErrors: true,
|
|
43
82
|
allowUnionTypes: true,
|
|
@@ -52,7 +91,17 @@ ajv.addFormat('address', {
|
|
|
52
91
|
validate: (value: string) => {
|
|
53
92
|
try {
|
|
54
93
|
return isAddress(value);
|
|
55
|
-
} catch (
|
|
94
|
+
} catch (e: any) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
ajv.addFormat('starknetAddress', {
|
|
101
|
+
validate: (value: string) => {
|
|
102
|
+
try {
|
|
103
|
+
return isStarknetAddress(value);
|
|
104
|
+
} catch (e: any) {
|
|
56
105
|
return false;
|
|
57
106
|
}
|
|
58
107
|
}
|
|
@@ -135,7 +184,6 @@ ajv.addKeyword({
|
|
|
135
184
|
errors: true
|
|
136
185
|
});
|
|
137
186
|
|
|
138
|
-
|
|
139
187
|
// Custom URL format to allow empty string values
|
|
140
188
|
// https://github.com/snapshot-labs/snapshot.js/pull/541/files
|
|
141
189
|
ajv.addFormat('customUrl', {
|
|
@@ -157,7 +205,7 @@ export async function call(provider, abi: any[], call: any[], options?) {
|
|
|
157
205
|
try {
|
|
158
206
|
const params = call[2] || [];
|
|
159
207
|
return await contract[call[1]](...params, options || {});
|
|
160
|
-
} catch (e) {
|
|
208
|
+
} catch (e: any) {
|
|
161
209
|
return Promise.reject(e);
|
|
162
210
|
}
|
|
163
211
|
}
|
|
@@ -198,7 +246,7 @@ export async function multicall(
|
|
|
198
246
|
return results.map((call, i) =>
|
|
199
247
|
itf.decodeFunctionResult(calls[i][1], call)
|
|
200
248
|
);
|
|
201
|
-
} catch (e) {
|
|
249
|
+
} catch (e: any) {
|
|
202
250
|
return Promise.reject(e);
|
|
203
251
|
}
|
|
204
252
|
}
|
|
@@ -216,7 +264,7 @@ export async function subgraphRequest(url: string, query, options: any = {}) {
|
|
|
216
264
|
let responseData: any = await res.text();
|
|
217
265
|
try {
|
|
218
266
|
responseData = JSON.parse(responseData);
|
|
219
|
-
} catch (e) {
|
|
267
|
+
} catch (e: any) {
|
|
220
268
|
throw new Error(
|
|
221
269
|
`Errors found in subgraphRequest: URL: ${url}, Status: ${
|
|
222
270
|
res.status
|
|
@@ -287,7 +335,7 @@ export async function getScores(
|
|
|
287
335
|
network: string,
|
|
288
336
|
addresses: string[],
|
|
289
337
|
snapshot: number | string = 'latest',
|
|
290
|
-
scoreApiUrl =
|
|
338
|
+
scoreApiUrl = DEFAULT_SCORE_API_URL,
|
|
291
339
|
options: any = {}
|
|
292
340
|
) {
|
|
293
341
|
if (!Array.isArray(addresses)) {
|
|
@@ -317,9 +365,11 @@ export async function getScores(
|
|
|
317
365
|
);
|
|
318
366
|
}
|
|
319
367
|
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
368
|
+
const urlObject = new URL(scoreApiUrl);
|
|
369
|
+
urlObject.pathname = '/api/scores';
|
|
370
|
+
const { url, headers } = formatScoreAPIUrl(scoreApiUrl, {
|
|
371
|
+
path: '/api/scores'
|
|
372
|
+
});
|
|
323
373
|
|
|
324
374
|
try {
|
|
325
375
|
const params = {
|
|
@@ -329,21 +379,17 @@ export async function getScores(
|
|
|
329
379
|
strategies,
|
|
330
380
|
addresses
|
|
331
381
|
};
|
|
332
|
-
const res = await fetch(
|
|
382
|
+
const res = await fetch(url, {
|
|
333
383
|
method: 'POST',
|
|
334
|
-
headers
|
|
384
|
+
headers,
|
|
335
385
|
body: JSON.stringify({ params })
|
|
336
386
|
});
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
if (obj.error) {
|
|
340
|
-
return Promise.reject(obj.error);
|
|
341
|
-
}
|
|
387
|
+
const response = await parseScoreAPIResponse(res);
|
|
342
388
|
|
|
343
389
|
return options.returnValue === 'all'
|
|
344
|
-
?
|
|
345
|
-
:
|
|
346
|
-
} catch (e) {
|
|
390
|
+
? response.result
|
|
391
|
+
: response.result[options.returnValue || 'scores'];
|
|
392
|
+
} catch (e: any) {
|
|
347
393
|
if (e.errno) {
|
|
348
394
|
return Promise.reject({ code: e.errno, message: e.toString(), data: '' });
|
|
349
395
|
}
|
|
@@ -360,8 +406,7 @@ export async function getVp(
|
|
|
360
406
|
delegation: boolean,
|
|
361
407
|
options?: Options
|
|
362
408
|
) {
|
|
363
|
-
|
|
364
|
-
if (!options.url) options.url = 'https://score.snapshot.org';
|
|
409
|
+
const { url, headers } = formatScoreAPIUrl(options?.url);
|
|
365
410
|
if (!isValidAddress(address)) {
|
|
366
411
|
return inputError(`Invalid voter address: ${address}`);
|
|
367
412
|
}
|
|
@@ -385,7 +430,7 @@ export async function getVp(
|
|
|
385
430
|
|
|
386
431
|
const init = {
|
|
387
432
|
method: 'POST',
|
|
388
|
-
headers
|
|
433
|
+
headers,
|
|
389
434
|
body: JSON.stringify({
|
|
390
435
|
jsonrpc: '2.0',
|
|
391
436
|
method: 'get_vp',
|
|
@@ -401,11 +446,10 @@ export async function getVp(
|
|
|
401
446
|
};
|
|
402
447
|
|
|
403
448
|
try {
|
|
404
|
-
const res = await fetch(
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
} catch (e) {
|
|
449
|
+
const res = await fetch(url, init);
|
|
450
|
+
const response = await parseScoreAPIResponse(res);
|
|
451
|
+
return response.result;
|
|
452
|
+
} catch (e: any) {
|
|
409
453
|
if (e.errno) {
|
|
410
454
|
return Promise.reject({ code: e.errno, message: e.toString(), data: '' });
|
|
411
455
|
}
|
|
@@ -420,7 +464,7 @@ export async function validate(
|
|
|
420
464
|
network: string,
|
|
421
465
|
snapshot: number | 'latest',
|
|
422
466
|
params: any,
|
|
423
|
-
options
|
|
467
|
+
options?: Options
|
|
424
468
|
) {
|
|
425
469
|
if (!isValidAddress(author)) {
|
|
426
470
|
return inputError(`Invalid author: ${author}`);
|
|
@@ -436,10 +480,11 @@ export async function validate(
|
|
|
436
480
|
}
|
|
437
481
|
|
|
438
482
|
if (!options) options = {};
|
|
439
|
-
|
|
483
|
+
const { url, headers } = formatScoreAPIUrl(options.url);
|
|
484
|
+
|
|
440
485
|
const init = {
|
|
441
486
|
method: 'POST',
|
|
442
|
-
headers
|
|
487
|
+
headers,
|
|
443
488
|
body: JSON.stringify({
|
|
444
489
|
jsonrpc: '2.0',
|
|
445
490
|
method: 'validate',
|
|
@@ -455,11 +500,10 @@ export async function validate(
|
|
|
455
500
|
};
|
|
456
501
|
|
|
457
502
|
try {
|
|
458
|
-
const res = await fetch(
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
} catch (e) {
|
|
503
|
+
const res = await fetch(url, init);
|
|
504
|
+
const response = await parseScoreAPIResponse(res);
|
|
505
|
+
return response.result;
|
|
506
|
+
} catch (e: any) {
|
|
463
507
|
if (e.errno) {
|
|
464
508
|
return Promise.reject({ code: e.errno, message: e.toString(), data: '' });
|
|
465
509
|
}
|
|
@@ -520,7 +564,7 @@ export async function getSpaceUri(
|
|
|
520
564
|
): Promise<string | null> {
|
|
521
565
|
try {
|
|
522
566
|
return await getEnsTextRecord(id, 'snapshot', network, options);
|
|
523
|
-
} catch (e) {
|
|
567
|
+
} catch (e: any) {
|
|
524
568
|
console.log(e);
|
|
525
569
|
return null;
|
|
526
570
|
}
|
|
@@ -604,6 +648,32 @@ function isValidSnapshot(snapshot: number | string, network: string) {
|
|
|
604
648
|
);
|
|
605
649
|
}
|
|
606
650
|
|
|
651
|
+
export function isStarknetAddress(address: string): boolean {
|
|
652
|
+
if (!address) return false;
|
|
653
|
+
|
|
654
|
+
try {
|
|
655
|
+
validateAndParseAddress(address);
|
|
656
|
+
return true;
|
|
657
|
+
} catch (e: any) {
|
|
658
|
+
return false;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export function isEvmAddress(address: string): boolean {
|
|
663
|
+
return isAddress(address);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
export function getFormattedAddress(
|
|
667
|
+
address: string,
|
|
668
|
+
format: string[] = ['evm', 'starknet']
|
|
669
|
+
): string {
|
|
670
|
+
if (format.includes('evm') && isAddress(address)) return getAddress(address);
|
|
671
|
+
if (format.includes('starknet') && isStarknetAddress(address))
|
|
672
|
+
return validateAndParseAddress(address);
|
|
673
|
+
|
|
674
|
+
throw new Error(`Invalid address: ${address}`);
|
|
675
|
+
}
|
|
676
|
+
|
|
607
677
|
function inputError(message: string) {
|
|
608
678
|
return Promise.reject(new Error(message));
|
|
609
679
|
}
|
|
@@ -638,5 +708,8 @@ export default {
|
|
|
638
708
|
getHash,
|
|
639
709
|
verify,
|
|
640
710
|
validate,
|
|
711
|
+
isStarknetAddress,
|
|
712
|
+
isEvmAddress,
|
|
713
|
+
getFormattedAddress,
|
|
641
714
|
SNAPSHOT_SUBGRAPH_URL
|
|
642
715
|
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { test, expect, describe } from 'vitest';
|
|
2
|
+
import evmMessage from '../../test/fixtures/evm/message-alias.json';
|
|
3
|
+
import verify, { getHash } from './evm';
|
|
4
|
+
|
|
5
|
+
describe('verify/evm', () => {
|
|
6
|
+
describe('getHash()', () => {
|
|
7
|
+
test('should return a hash string', () => {
|
|
8
|
+
const hash = getHash(evmMessage.data);
|
|
9
|
+
expect(hash).toBe(
|
|
10
|
+
'0x82ed8be33f43c86f9b83d14736e5762c89108fbc9b8b54f6e993818fc8a53525'
|
|
11
|
+
);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('verify()', () => {
|
|
16
|
+
test('should return true if the signature is valid', () => {
|
|
17
|
+
expect(
|
|
18
|
+
verify(evmMessage.address, evmMessage.sig, evmMessage.data)
|
|
19
|
+
).resolves.toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('should throw an error if the signature is invalid', () => {
|
|
23
|
+
expect(
|
|
24
|
+
verify(
|
|
25
|
+
'0xDD983E11Cf84746f3b7589ee1Dc2081c08c40Cb3',
|
|
26
|
+
evmMessage.sig,
|
|
27
|
+
evmMessage.data
|
|
28
|
+
)
|
|
29
|
+
).rejects.toThrowError(/isValidSignature/);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|