@snapshot-labs/snapshot.js 0.12.0 → 0.12.2

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.
@@ -2,7 +2,7 @@ import type { StarkNetType } from 'starknet';
2
2
  import type { TypedDataField } from '@ethersproject/abstract-signer';
3
3
  import type { ProviderOptions } from '../utils/provider';
4
4
  export type SignaturePayload = {
5
- domain: Record<string, string>;
5
+ domain: Record<string, string | number>;
6
6
  types: Record<string, StarkNetType[] | TypedDataField[]>;
7
7
  primaryType?: string;
8
8
  message: Record<string, any>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapshot-labs/snapshot.js",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
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",
@@ -1,5 +1,5 @@
1
1
  {
2
- "livenet": {
2
+ "mainnet": {
3
3
  "hub": "https://hub.snapshot.org",
4
4
  "sequencer": "https://seq.snapshot.org"
5
5
  },
package/src/sign/index.ts CHANGED
@@ -57,10 +57,10 @@ export default class Client {
57
57
  readonly address: string;
58
58
  readonly options: any;
59
59
 
60
- constructor(address: string = constants.livenet.sequencer, options = {}) {
60
+ constructor(address: string = constants.mainnet.sequencer, options = {}) {
61
61
  address = address.replace(
62
- constants.livenet.hub,
63
- constants.livenet.sequencer
62
+ constants.mainnet.hub,
63
+ constants.mainnet.sequencer
64
64
  );
65
65
  address = address.replace(
66
66
  constants.testnet.hub,
@@ -6,7 +6,7 @@ import type { TypedDataField } from '@ethersproject/abstract-signer';
6
6
  import type { ProviderOptions } from '../utils/provider';
7
7
 
8
8
  export type SignaturePayload = {
9
- domain: Record<string, string>;
9
+ domain: Record<string, string | number>;
10
10
  types: Record<string, StarkNetType[] | TypedDataField[]>;
11
11
  primaryType?: string;
12
12
  message: Record<string, any>;
@@ -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
  }