@snapshot-labs/snapshot.js 0.12.1 → 0.12.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapshot-labs/snapshot.js",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "repository": "snapshot-labs/snapshot.js",
5
5
  "license": "MIT",
6
6
  "main": "dist/snapshot.cjs.js",
@@ -11,6 +11,7 @@
11
11
  "@ensdomains/eth-ens-namehash": "^2.0.15",
12
12
  "@ethersproject/abi": "^5.6.4",
13
13
  "@ethersproject/address": "^5.6.1",
14
+ "@ethersproject/bignumber": "^5.7.0",
14
15
  "@ethersproject/bytes": "^5.6.1",
15
16
  "@ethersproject/contracts": "^5.6.2",
16
17
  "@ethersproject/hash": "^5.7.0",
package/src/utils.spec.js CHANGED
@@ -5,7 +5,9 @@ import {
5
5
  validateSchema,
6
6
  getScores,
7
7
  getVp,
8
- getFormattedAddress
8
+ getFormattedAddress,
9
+ getEnsOwner,
10
+ getEnsTextRecord
9
11
  } from './utils';
10
12
 
11
13
  vi.mock('cross-fetch', async () => {
@@ -611,4 +613,18 @@ describe('utils', () => {
611
613
  expect(result).not.toBe(true);
612
614
  });
613
615
  });
616
+
617
+ describe('getEnsOwner', () => {
618
+ test('should return null when the ENS is not valid', () => {
619
+ // special hidden characters after the k
620
+ expect(getEnsOwner('elonmusk‍‍.eth')).resolves.toBe(null);
621
+ });
622
+ });
623
+
624
+ describe('getEnsTextRecord', () => {
625
+ test('should return null when the ENS is not valid', () => {
626
+ // special hidden characters after the k
627
+ expect(getEnsTextRecord('elonmusk‍‍.eth')).resolves.toBe(null);
628
+ });
629
+ });
614
630
  });
package/src/utils.ts CHANGED
@@ -544,7 +544,14 @@ export async function getEnsTextRecord(
544
544
  ...multicallOptions
545
545
  } = options;
546
546
 
547
- const ensHash = namehash(ensNormalize(ens));
547
+ let ensHash: string;
548
+
549
+ try {
550
+ ensHash = namehash(ensNormalize(ens));
551
+ } catch (e: any) {
552
+ return null;
553
+ }
554
+
548
555
  const provider = getProvider(network, { broviderUrl });
549
556
 
550
557
  const calls = [
@@ -592,9 +599,17 @@ export async function getEnsOwner(
592
599
  ['function owner(bytes32) view returns (address)'],
593
600
  provider
594
601
  );
602
+
603
+ let ensHash: string;
604
+
605
+ try {
606
+ ensHash = namehash(ensNormalize(ens));
607
+ } catch (e: any) {
608
+ return null;
609
+ }
610
+
595
611
  const ensNameWrapper =
596
612
  options.ensNameWrapper || networks[network].ensNameWrapper;
597
- const ensHash = namehash(ensNormalize(ens));
598
613
  let owner = await ensRegistry.owner(ensHash);
599
614
  // If owner is the ENSNameWrapper contract, resolve the owner of the name
600
615
  if (owner === ensNameWrapper) {
@@ -1,7 +1,9 @@
1
1
  import { test, expect, describe } from 'vitest';
2
2
  import starknetMessage from '../../test/fixtures/starknet/message-alias.json';
3
+ import starknetMessageRsv from '../../test/fixtures/starknet/message-alias-rsv.json';
3
4
  import verify, { getHash } from './starknet';
4
5
  import { validateAndParseAddress } from 'starknet';
6
+ import { clone } from '../utils';
5
7
 
6
8
  describe('verify/starknet', () => {
7
9
  describe('getHash()', () => {
@@ -21,47 +23,79 @@ describe('verify/starknet', () => {
21
23
  });
22
24
 
23
25
  describe('verify()', () => {
24
- test('should return true if the signature is valid', () => {
25
- expect(
26
- verify(
27
- starknetMessage.address,
28
- starknetMessage.sig,
29
- starknetMessage.data,
30
- 'SN_SEPOLIA'
31
- )
32
- ).resolves.toBe(true);
26
+ describe.each([
27
+ ['2', starknetMessage],
28
+ ['3', starknetMessageRsv]
29
+ ])('with a %s items signature', (title, message) => {
30
+ test('should return true if the signature is valid', () => {
31
+ expect(
32
+ verify(message.address, message.sig, message.data, 'SN_MAIN')
33
+ ).resolves.toBe(true);
34
+ });
35
+
36
+ test('should return true if the signature is valid with a padded address', () => {
37
+ expect(
38
+ verify(
39
+ validateAndParseAddress(message.address),
40
+ message.sig,
41
+ message.data,
42
+ 'SN_MAIN'
43
+ )
44
+ ).resolves.toBe(true);
45
+ });
46
+
47
+ test('should return true when verifying on a different network', () => {
48
+ expect(
49
+ verify(message.address, message.sig, message.data, 'SN_SEPOLIA')
50
+ ).resolves.toBe(true);
51
+ });
52
+
53
+ test('should throw an error if the signature is invalid', () => {
54
+ expect(
55
+ verify(
56
+ '0x7667469b8e93faa642573078b6bf8c790d3a6184b2a1bb39c5c923a732862e1',
57
+ message.sig,
58
+ message.data
59
+ )
60
+ ).rejects.toThrow();
61
+ });
33
62
  });
34
63
 
35
- test('should return true if the signature is valid with a padded address', () => {
64
+ test('should throw an error when the contract is not deployed', () => {
36
65
  expect(
37
66
  verify(
38
- validateAndParseAddress(starknetMessage.address),
67
+ '0x07f71118e351c02f6EC7099C8CDf93AED66CEd8406E94631cC91637f7D7F203A',
39
68
  starknetMessage.sig,
40
69
  starknetMessage.data,
41
- 'SN_SEPOLIA'
70
+ 'SN_MAIN'
42
71
  )
43
- ).resolves.toBe(true);
72
+ ).rejects.toThrowError('Contract not deployed');
44
73
  });
45
74
 
46
- test('should throw an error if message is on wrong network', () => {
75
+ test('should return false when the signature is not valid', () => {
47
76
  expect(
48
77
  verify(
49
78
  starknetMessage.address,
50
- starknetMessage.sig,
79
+ ['1', '2'],
51
80
  starknetMessage.data,
52
81
  'SN_MAIN'
53
82
  )
54
- ).rejects.toThrowError();
83
+ ).resolves.toBe(false);
55
84
  });
56
85
 
57
- test('should throw an error if the signature is invalid', () => {
86
+ test('should return false when the signature is not valid', () => {
87
+ const data = clone(starknetMessage.data);
88
+ data.message.timestamp = 1234;
89
+
58
90
  expect(
59
- verify(
60
- '0x7667469b8e93faa642573078b6bf8c790d3a6184b2a1bb39c5c923a732862e1',
61
- starknetMessage.sig,
62
- starknetMessage.data
63
- )
64
- ).rejects.toThrow();
91
+ verify(starknetMessage.address, starknetMessage.sig, data, 'SN_MAIN')
92
+ ).resolves.toBe(false);
93
+ });
94
+
95
+ test('should throw an error on wrong signature length', () => {
96
+ expect(
97
+ verify(starknetMessage.address, ['1'], starknetMessage.data, 'SN_MAIN')
98
+ ).rejects.toThrowError('Invalid signature format');
65
99
  });
66
100
  });
67
101
  });
@@ -1,4 +1,5 @@
1
1
  import { Contract, RpcProvider, typedData } from 'starknet';
2
+ import { BigNumber } from '@ethersproject/bignumber';
2
3
  import type { SignaturePayload } from '.';
3
4
  import type { ProviderOptions } from '../utils/provider';
4
5
 
@@ -11,11 +12,11 @@ const RPC_URLS: Record<NetworkType, string> = {
11
12
 
12
13
  const ABI = [
13
14
  {
14
- name: 'argent::account::interface::IDeprecatedArgentAccount',
15
+ name: 'argent::common::account::IAccount',
15
16
  type: 'interface',
16
17
  items: [
17
18
  {
18
- name: 'isValidSignature',
19
+ name: 'is_valid_signature',
19
20
  type: 'function',
20
21
  inputs: [
21
22
  {
@@ -23,7 +24,7 @@ const ABI = [
23
24
  type: 'core::felt252'
24
25
  },
25
26
  {
26
- name: 'signatures',
27
+ name: 'signature',
27
28
  type: 'core::array::Array::<core::felt252>'
28
29
  }
29
30
  ],
@@ -67,16 +68,28 @@ export default async function verify(
67
68
  network: NetworkType = 'SN_MAIN',
68
69
  options: ProviderOptions = {}
69
70
  ): Promise<boolean> {
70
- const contractAccount = new Contract(
71
- ABI,
72
- address,
73
- getProvider(network, options)
74
- );
71
+ try {
72
+ const contractAccount = new Contract(
73
+ ABI,
74
+ address,
75
+ getProvider(network, options)
76
+ );
77
+
78
+ if (sig.length < 2) {
79
+ throw new Error('Invalid signature format');
80
+ }
75
81
 
76
- await contractAccount.isValidSignature(getHash(data, address), [
77
- sig[0],
78
- sig[1]
79
- ]);
82
+ const result = await contractAccount.is_valid_signature(
83
+ getHash(data, address),
84
+ sig.slice(-2)
85
+ );
80
86
 
81
- return true;
87
+ return BigNumber.from(result).eq(BigNumber.from('370462705988'));
88
+ } catch (e: any) {
89
+ if (e.message.includes('Contract not found')) {
90
+ throw new Error('Contract not deployed');
91
+ }
92
+
93
+ throw e;
94
+ }
82
95
  }