@snapshot-labs/snapshot.js 0.11.22 → 0.11.24

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/dist/utils.d.ts CHANGED
@@ -6,6 +6,7 @@ import { getHash, verify } from './sign/utils';
6
6
  import getDelegatesBySpace, { SNAPSHOT_SUBGRAPH_URL } from './utils/delegation';
7
7
  interface Options {
8
8
  url?: string;
9
+ headers?: any;
9
10
  }
10
11
  interface Strategy {
11
12
  name: string;
@@ -21,7 +22,7 @@ export declare function ipfsGet(gateway: string, ipfsHash: string, protocolType?
21
22
  export declare function sendTransaction(web3: any, contractAddress: string, abi: any[], action: string, params: any[], overrides?: {}): Promise<any>;
22
23
  export declare function getScores(space: string, strategies: Strategy[], network: string, addresses: string[], snapshot?: number | string, scoreApiUrl?: string, options?: any): Promise<any>;
23
24
  export declare function getVp(address: string, network: string, strategies: Strategy[], snapshot: number | 'latest', space: string, delegation: boolean, options?: Options): Promise<any>;
24
- export declare function validate(validation: string, author: string, space: string, network: string, snapshot: number | 'latest', params: any, options: any): Promise<any>;
25
+ export declare function validate(validation: string, author: string, space: string, network: string, snapshot: number | 'latest', params: any, options?: Options): Promise<any>;
25
26
  interface validateSchemaOptions {
26
27
  snapshotEnv?: string;
27
28
  spaceType?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapshot-labs/snapshot.js",
3
- "version": "0.11.22",
3
+ "version": "0.11.24",
4
4
  "repository": "snapshot-labs/snapshot.js",
5
5
  "license": "MIT",
6
6
  "main": "dist/snapshot.cjs.js",
package/src/networks.json CHANGED
@@ -1125,6 +1125,35 @@
1125
1125
  "logo": "ipfs://QmaKRLxXPdeTsLx7MFLS3CJbhpSbResgoeL4fCgHB1mTsF",
1126
1126
  "testnet": true
1127
1127
  },
1128
+ "13371": {
1129
+ "key": "13371",
1130
+ "name": "Immutable zkEVM",
1131
+ "shortName": "mainnet",
1132
+ "chainId": 13371,
1133
+ "network": "mainnet",
1134
+ "multicall": "0xcA11bde05977b3631167028862bE2a173976CA11",
1135
+ "rpc": [],
1136
+ "explorer": {
1137
+ "url": "https://explorer.immutable.com"
1138
+ },
1139
+ "start": 3680945,
1140
+ "logo": "ipfs://bafkreiepnhfv3hgexddjpyaeemxo3byhtxxit6t4zsponyczee6ddjqxwi"
1141
+ },
1142
+ "13473": {
1143
+ "key": "13473",
1144
+ "name": "Immutable zkEVM Testnet",
1145
+ "shortName": "testnet",
1146
+ "chainId": 13473,
1147
+ "network": "testnet",
1148
+ "multicall": "0xcA11bde05977b3631167028862bE2a173976CA11",
1149
+ "rpc": [],
1150
+ "explorer": {
1151
+ "url": "https://explorer.testnet.immutable.com"
1152
+ },
1153
+ "start": 5307209,
1154
+ "logo": "ipfs://bafkreiepnhfv3hgexddjpyaeemxo3byhtxxit6t4zsponyczee6ddjqxwi",
1155
+ "testnet": true
1156
+ },
1128
1157
  "16718": {
1129
1158
  "key": "16718",
1130
1159
  "name": "AirDAO",
@@ -1460,4 +1489,4 @@
1460
1489
  "start": 7521509,
1461
1490
  "logo": "ipfs://QmNnGPr1CNvj12SSGzKARtUHv9FyEfE5nES73U4vBWQSJL"
1462
1491
  }
1463
- }
1492
+ }
package/src/utils.spec.js CHANGED
@@ -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
- json: () => new Promise((resolve) => resolve({ result: 'OK' }))
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
- json: () => new Promise((resolve) => resolve({ result: 'OK' }))
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
- json: () => new Promise((resolve) => resolve({ result: 'OK' }))
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/?apiKey=xxx',
126
- expect.anything()
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
- json: () => new Promise((resolve) => resolve(result))
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
- json: () => new Promise((resolve) => resolve(result))
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
- json: () => {
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
- json: () => new Promise((resolve) => resolve({ result: 'OK' }))
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
- json: () => new Promise((resolve) => resolve({ result: 'OK' }))
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
- json: () => new Promise((resolve) => resolve({ result: 'OK' }))
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?apiKey=xxx',
282
- expect.anything()
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
- json: () => new Promise((resolve) => resolve({ result }))
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
- json: () => new Promise((resolve) => resolve({ result }))
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
- json: () => new Promise((resolve) => resolve(result))
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
- json: () => {
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
- json: () => new Promise((resolve) => resolve({ result: 'OK' }))
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
- json: () => new Promise((resolve) => resolve({ result: 'OK' }))
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
- json: () => new Promise((resolve) => resolve({ result: 'OK' }))
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
- json: () => new Promise((resolve) => resolve({ result }))
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
- json: () => new Promise((resolve) => resolve(result))
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
- json: () => {
486
+ text: () => {
467
487
  throw result;
468
488
  }
469
489
  });
package/src/utils.ts CHANGED
@@ -20,6 +20,7 @@ import getDelegatesBySpace, { SNAPSHOT_SUBGRAPH_URL } from './utils/delegation';
20
20
 
21
21
  interface Options {
22
22
  url?: string;
23
+ headers?: any;
23
24
  }
24
25
 
25
26
  interface Strategy {
@@ -38,6 +39,43 @@ const scoreApiHeaders = {
38
39
  'Content-Type': 'application/json'
39
40
  };
40
41
 
42
+ const DEFAULT_SCORE_API_URL = 'https://score.snapshot.org';
43
+
44
+ function formatScoreAPIUrl(
45
+ url = DEFAULT_SCORE_API_URL,
46
+ options = {
47
+ path: ''
48
+ }
49
+ ) {
50
+ const scoreURL = new URL(url);
51
+ if (options.path) scoreURL.pathname = options.path;
52
+ const apiKey = scoreURL.searchParams.get('apiKey');
53
+ let headers: any = { ...scoreApiHeaders };
54
+ if (apiKey) {
55
+ scoreURL.searchParams.delete('apiKey');
56
+ headers = { ...scoreApiHeaders, 'X-API-KEY': apiKey };
57
+ }
58
+ return {
59
+ url: scoreURL.toString(),
60
+ headers
61
+ };
62
+ }
63
+
64
+ async function parseScoreAPIResponse(res: any) {
65
+ let data: any = await res.text();
66
+ try {
67
+ data = JSON.parse(data);
68
+ } catch (e) {
69
+ return Promise.reject({
70
+ code: res.status || 500,
71
+ message: 'Failed to parse response from score API',
72
+ data
73
+ });
74
+ }
75
+ if (data.error) return Promise.reject(data.error);
76
+ return data;
77
+ }
78
+
41
79
  const ajv = new Ajv({
42
80
  allErrors: true,
43
81
  allowUnionTypes: true,
@@ -135,7 +173,6 @@ ajv.addKeyword({
135
173
  errors: true
136
174
  });
137
175
 
138
-
139
176
  // Custom URL format to allow empty string values
140
177
  // https://github.com/snapshot-labs/snapshot.js/pull/541/files
141
178
  ajv.addFormat('customUrl', {
@@ -287,7 +324,7 @@ export async function getScores(
287
324
  network: string,
288
325
  addresses: string[],
289
326
  snapshot: number | string = 'latest',
290
- scoreApiUrl = 'https://score.snapshot.org',
327
+ scoreApiUrl = DEFAULT_SCORE_API_URL,
291
328
  options: any = {}
292
329
  ) {
293
330
  if (!Array.isArray(addresses)) {
@@ -317,9 +354,11 @@ export async function getScores(
317
354
  );
318
355
  }
319
356
 
320
- const url = new URL(scoreApiUrl);
321
- url.pathname = '/api/scores';
322
- scoreApiUrl = url.toString();
357
+ const urlObject = new URL(scoreApiUrl);
358
+ urlObject.pathname = '/api/scores';
359
+ const { url, headers } = formatScoreAPIUrl(scoreApiUrl, {
360
+ path: '/api/scores'
361
+ });
323
362
 
324
363
  try {
325
364
  const params = {
@@ -329,20 +368,16 @@ export async function getScores(
329
368
  strategies,
330
369
  addresses
331
370
  };
332
- const res = await fetch(scoreApiUrl, {
371
+ const res = await fetch(url, {
333
372
  method: 'POST',
334
- headers: scoreApiHeaders,
373
+ headers,
335
374
  body: JSON.stringify({ params })
336
375
  });
337
- const obj = await res.json();
338
-
339
- if (obj.error) {
340
- return Promise.reject(obj.error);
341
- }
376
+ const response = await parseScoreAPIResponse(res);
342
377
 
343
378
  return options.returnValue === 'all'
344
- ? obj.result
345
- : obj.result[options.returnValue || 'scores'];
379
+ ? response.result
380
+ : response.result[options.returnValue || 'scores'];
346
381
  } catch (e) {
347
382
  if (e.errno) {
348
383
  return Promise.reject({ code: e.errno, message: e.toString(), data: '' });
@@ -360,8 +395,7 @@ export async function getVp(
360
395
  delegation: boolean,
361
396
  options?: Options
362
397
  ) {
363
- if (!options) options = {};
364
- if (!options.url) options.url = 'https://score.snapshot.org';
398
+ const { url, headers } = formatScoreAPIUrl(options?.url);
365
399
  if (!isValidAddress(address)) {
366
400
  return inputError(`Invalid voter address: ${address}`);
367
401
  }
@@ -385,7 +419,7 @@ export async function getVp(
385
419
 
386
420
  const init = {
387
421
  method: 'POST',
388
- headers: scoreApiHeaders,
422
+ headers,
389
423
  body: JSON.stringify({
390
424
  jsonrpc: '2.0',
391
425
  method: 'get_vp',
@@ -401,10 +435,9 @@ export async function getVp(
401
435
  };
402
436
 
403
437
  try {
404
- const res = await fetch(options.url, init);
405
- const json = await res.json();
406
- if (json.error) return Promise.reject(json.error);
407
- if (json.result) return json.result;
438
+ const res = await fetch(url, init);
439
+ const response = await parseScoreAPIResponse(res);
440
+ return response.result;
408
441
  } catch (e) {
409
442
  if (e.errno) {
410
443
  return Promise.reject({ code: e.errno, message: e.toString(), data: '' });
@@ -420,7 +453,7 @@ export async function validate(
420
453
  network: string,
421
454
  snapshot: number | 'latest',
422
455
  params: any,
423
- options: any
456
+ options?: Options
424
457
  ) {
425
458
  if (!isValidAddress(author)) {
426
459
  return inputError(`Invalid author: ${author}`);
@@ -436,10 +469,11 @@ export async function validate(
436
469
  }
437
470
 
438
471
  if (!options) options = {};
439
- if (!options.url) options.url = 'https://score.snapshot.org';
472
+ const { url, headers } = formatScoreAPIUrl(options.url);
473
+
440
474
  const init = {
441
475
  method: 'POST',
442
- headers: scoreApiHeaders,
476
+ headers,
443
477
  body: JSON.stringify({
444
478
  jsonrpc: '2.0',
445
479
  method: 'validate',
@@ -455,10 +489,9 @@ export async function validate(
455
489
  };
456
490
 
457
491
  try {
458
- const res = await fetch(options.url, init);
459
- const json = await res.json();
460
- if (json.error) return Promise.reject(json.error);
461
- return json.result;
492
+ const res = await fetch(url, init);
493
+ const response = await parseScoreAPIResponse(res);
494
+ return response.result;
462
495
  } catch (e) {
463
496
  if (e.errno) {
464
497
  return Promise.reject({ code: e.errno, message: e.toString(), data: '' });