@snapshot-labs/snapshot.js 0.11.38 → 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.
Files changed (47) hide show
  1. package/LICENSE +0 -0
  2. package/dist/index.d.ts +2 -29
  3. package/dist/schemas/index.d.ts +2 -29
  4. package/dist/sign/types.d.ts +0 -3
  5. package/dist/snapshot.cjs.js +1549 -1906
  6. package/dist/snapshot.esm.js +1550 -1907
  7. package/dist/snapshot.js/src/index.d.ts +677 -0
  8. package/dist/snapshot.js/src/sign/index.d.ts +28 -0
  9. package/dist/snapshot.min.js +16 -5
  10. package/dist/src/index.d.ts +725 -0
  11. package/dist/src/schemas/index.d.ts +671 -0
  12. package/dist/src/sign/index.d.ts +29 -0
  13. package/dist/src/sign/types.d.ts +227 -0
  14. package/dist/src/utils/blockfinder.d.ts +1 -0
  15. package/dist/src/utils/delegation.d.ts +18 -0
  16. package/dist/src/utils/multicaller.d.ts +12 -0
  17. package/dist/src/utils/provider.d.ts +5 -0
  18. package/dist/src/utils/web3.d.ts +2 -0
  19. package/dist/src/utils.d.ts +91 -0
  20. package/dist/src/verify/evm.d.ts +4 -0
  21. package/dist/src/verify/evm.spec.d.ts +1 -0
  22. package/dist/src/verify/index.d.ts +11 -0
  23. package/dist/src/verify/index.spec.d.ts +1 -0
  24. package/dist/src/verify/starknet.d.ts +6 -0
  25. package/dist/src/verify/starknet.spec.d.ts +1 -0
  26. package/dist/src/voting/approval.d.ts +22 -0
  27. package/dist/src/voting/index.d.ts +14 -0
  28. package/dist/src/voting/quadratic.d.ts +20 -0
  29. package/dist/src/voting/rankedChoice.d.ts +18 -0
  30. package/dist/src/voting/singleChoice.d.ts +18 -0
  31. package/dist/src/voting/types.d.ts +35 -0
  32. package/dist/src/voting/weighted.d.ts +26 -0
  33. package/package.json +5 -3
  34. package/src/sign/hashedTypes.json +5 -1
  35. package/src/utils/dist/provider.js +47 -0
  36. package/src/utils/provider.ts +8 -2
  37. package/src/utils/web3.ts +1 -1
  38. package/src/utils.spec.js +36 -1
  39. package/src/utils.ts +44 -14
  40. package/src/verify/evm.spec.ts +32 -0
  41. package/src/verify/evm.ts +82 -0
  42. package/src/verify/index.spec.ts +84 -0
  43. package/src/verify/index.ts +41 -0
  44. package/src/verify/starknet.spec.ts +55 -0
  45. package/src/verify/starknet.ts +82 -0
  46. package/src/sign/eip1271.ts +0 -55
  47. package/src/sign/utils.ts +0 -27
@@ -6,9 +6,15 @@ import {
6
6
  const providers = {};
7
7
  const batchedProviders = {};
8
8
 
9
+ export type ProviderOptions = {
10
+ broviderUrl?: string;
11
+ };
12
+
13
+ const DEFAULT_BROVIDER_URL = 'https://rpc.snapshot.org';
14
+
9
15
  export default function getProvider(
10
16
  network,
11
- { broviderUrl = 'https://rpc.snapshot.org' } = {}
17
+ { broviderUrl = DEFAULT_BROVIDER_URL }: ProviderOptions = {}
12
18
  ) {
13
19
  const url = `${broviderUrl}/${network}`;
14
20
  if (!providers[network])
@@ -25,7 +31,7 @@ export default function getProvider(
25
31
 
26
32
  export function getBatchedProvider(
27
33
  network,
28
- { broviderUrl = 'https://rpc.snapshot.org' } = {}
34
+ { broviderUrl = DEFAULT_BROVIDER_URL }: ProviderOptions = {}
29
35
  ) {
30
36
  const url = `${broviderUrl}/${network}`;
31
37
  if (!batchedProviders[network])
package/src/utils/web3.ts CHANGED
@@ -9,7 +9,7 @@ export async function getBlockNumber(provider) {
9
9
  try {
10
10
  const blockNumber: any = await provider.getBlockNumber();
11
11
  return parseInt(blockNumber);
12
- } catch (e) {
12
+ } catch (e: any) {
13
13
  return Promise.reject();
14
14
  }
15
15
  }
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');
@@ -492,4 +492,39 @@ describe('utils', () => {
492
492
  });
493
493
  });
494
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
+ });
495
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,11 +12,12 @@ 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 './sign/utils';
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;
@@ -65,7 +66,7 @@ async function parseScoreAPIResponse(res: any) {
65
66
  let data: any = await res.text();
66
67
  try {
67
68
  data = JSON.parse(data);
68
- } catch (e) {
69
+ } catch (e: any) {
69
70
  return Promise.reject({
70
71
  code: res.status || 500,
71
72
  message: 'Failed to parse response from score API',
@@ -90,17 +91,17 @@ ajv.addFormat('address', {
90
91
  validate: (value: string) => {
91
92
  try {
92
93
  return isAddress(value);
93
- } catch (err) {
94
+ } catch (e: any) {
94
95
  return false;
95
96
  }
96
97
  }
97
98
  });
98
99
 
99
- ajv.addFormat('evmOrStarknetAddress', {
100
+ ajv.addFormat('starknetAddress', {
100
101
  validate: (value: string) => {
101
102
  try {
102
- return isAddress(value) || /^0x[0-9a-fA-F]{62,64}$/.test(value);
103
- } catch (err) {
103
+ return isStarknetAddress(value);
104
+ } catch (e: any) {
104
105
  return false;
105
106
  }
106
107
  }
@@ -204,7 +205,7 @@ export async function call(provider, abi: any[], call: any[], options?) {
204
205
  try {
205
206
  const params = call[2] || [];
206
207
  return await contract[call[1]](...params, options || {});
207
- } catch (e) {
208
+ } catch (e: any) {
208
209
  return Promise.reject(e);
209
210
  }
210
211
  }
@@ -245,7 +246,7 @@ export async function multicall(
245
246
  return results.map((call, i) =>
246
247
  itf.decodeFunctionResult(calls[i][1], call)
247
248
  );
248
- } catch (e) {
249
+ } catch (e: any) {
249
250
  return Promise.reject(e);
250
251
  }
251
252
  }
@@ -263,7 +264,7 @@ export async function subgraphRequest(url: string, query, options: any = {}) {
263
264
  let responseData: any = await res.text();
264
265
  try {
265
266
  responseData = JSON.parse(responseData);
266
- } catch (e) {
267
+ } catch (e: any) {
267
268
  throw new Error(
268
269
  `Errors found in subgraphRequest: URL: ${url}, Status: ${
269
270
  res.status
@@ -388,7 +389,7 @@ export async function getScores(
388
389
  return options.returnValue === 'all'
389
390
  ? response.result
390
391
  : response.result[options.returnValue || 'scores'];
391
- } catch (e) {
392
+ } catch (e: any) {
392
393
  if (e.errno) {
393
394
  return Promise.reject({ code: e.errno, message: e.toString(), data: '' });
394
395
  }
@@ -448,7 +449,7 @@ export async function getVp(
448
449
  const res = await fetch(url, init);
449
450
  const response = await parseScoreAPIResponse(res);
450
451
  return response.result;
451
- } catch (e) {
452
+ } catch (e: any) {
452
453
  if (e.errno) {
453
454
  return Promise.reject({ code: e.errno, message: e.toString(), data: '' });
454
455
  }
@@ -502,7 +503,7 @@ export async function validate(
502
503
  const res = await fetch(url, init);
503
504
  const response = await parseScoreAPIResponse(res);
504
505
  return response.result;
505
- } catch (e) {
506
+ } catch (e: any) {
506
507
  if (e.errno) {
507
508
  return Promise.reject({ code: e.errno, message: e.toString(), data: '' });
508
509
  }
@@ -563,7 +564,7 @@ export async function getSpaceUri(
563
564
  ): Promise<string | null> {
564
565
  try {
565
566
  return await getEnsTextRecord(id, 'snapshot', network, options);
566
- } catch (e) {
567
+ } catch (e: any) {
567
568
  console.log(e);
568
569
  return null;
569
570
  }
@@ -647,6 +648,32 @@ function isValidSnapshot(snapshot: number | string, network: string) {
647
648
  );
648
649
  }
649
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
+
650
677
  function inputError(message: string) {
651
678
  return Promise.reject(new Error(message));
652
679
  }
@@ -681,5 +708,8 @@ export default {
681
708
  getHash,
682
709
  verify,
683
710
  validate,
711
+ isStarknetAddress,
712
+ isEvmAddress,
713
+ getFormattedAddress,
684
714
  SNAPSHOT_SUBGRAPH_URL
685
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
+ });
@@ -0,0 +1,82 @@
1
+ import { verifyTypedData } from '@ethersproject/wallet';
2
+ import { _TypedDataEncoder } from '@ethersproject/hash';
3
+ import { arrayify } from '@ethersproject/bytes';
4
+ import getProvider, { type ProviderOptions } from '../utils/provider';
5
+ import { call } from '../utils';
6
+ import type { SignaturePayload } from '.';
7
+ import type { StaticJsonRpcProvider } from '@ethersproject/providers';
8
+
9
+ function isEqual(a: string, b: string): boolean {
10
+ return a.toLowerCase() === b.toLowerCase();
11
+ }
12
+
13
+ export function getHash(data: SignaturePayload): string {
14
+ const { domain, types, message } = data;
15
+ return _TypedDataEncoder.hash(domain, types, message);
16
+ }
17
+
18
+ export default async function verify(
19
+ address: string,
20
+ sig: string,
21
+ data: SignaturePayload,
22
+ network = '1',
23
+ options: ProviderOptions = {}
24
+ ): Promise<boolean> {
25
+ const { domain, types, message } = data;
26
+
27
+ try {
28
+ const recoverAddress = verifyTypedData(domain, types, message, sig);
29
+ if (isEqual(address, recoverAddress)) return true;
30
+ } catch (e: any) {}
31
+
32
+ const provider = getProvider(network, options);
33
+ const hash = getHash(data);
34
+
35
+ if (await verifyDefault(address, sig, hash, provider)) return true;
36
+
37
+ return await verifyOldVersion(address, sig, hash, provider);
38
+ }
39
+
40
+ async function verifyDefault(
41
+ address: string,
42
+ sig: string,
43
+ hash: string,
44
+ provider: StaticJsonRpcProvider
45
+ ): Promise<boolean> {
46
+ let returnValue: string;
47
+ const magicValue = '0x1626ba7e';
48
+ const abi =
49
+ 'function isValidSignature(bytes32 _hash, bytes memory _signature) public view returns (bytes4 magicValue)';
50
+ try {
51
+ returnValue = await call(
52
+ provider,
53
+ [abi],
54
+ [address, 'isValidSignature', [arrayify(hash), sig]]
55
+ );
56
+ } catch (e: any) {
57
+ if (e.message.startsWith('missing revert data in call exception')) {
58
+ return false;
59
+ }
60
+ throw e;
61
+ }
62
+
63
+ return isEqual(returnValue, magicValue);
64
+ }
65
+
66
+ async function verifyOldVersion(
67
+ address: string,
68
+ sig: string,
69
+ hash: string,
70
+ provider: StaticJsonRpcProvider
71
+ ): Promise<boolean> {
72
+ const magicValue = '0x20c13b0b';
73
+ const abi =
74
+ 'function isValidSignature(bytes _hash, bytes memory _signature) public view returns (bytes4 magicValue)';
75
+
76
+ const returnValue = await call(
77
+ provider,
78
+ [abi],
79
+ [address, 'isValidSignature', [arrayify(hash), sig]]
80
+ );
81
+ return isEqual(returnValue, magicValue);
82
+ }
@@ -0,0 +1,84 @@
1
+ import { test, expect, describe, vi } from 'vitest';
2
+ import { verify, getHash } from '.';
3
+ import evmMessage from '../../test/fixtures/evm/message-alias.json';
4
+ import starknetMessage from '../../test/fixtures/starknet/message-alias.json';
5
+ import * as evmVerification from './evm';
6
+ import * as starknetVerification from './starknet';
7
+
8
+ const evmVerificationMock = vi.spyOn(evmVerification, 'default');
9
+ const starknetVerificationMock = vi.spyOn(starknetVerification, 'default');
10
+ evmVerificationMock.mockImplementation(() => Promise.resolve(true));
11
+ starknetVerificationMock.mockImplementation(() => Promise.resolve(true));
12
+
13
+ const evmGetHashMock = vi.spyOn(evmVerification, 'getHash');
14
+ const starknetGetHashMock = vi.spyOn(starknetVerification, 'getHash');
15
+ evmGetHashMock.mockImplementation(() => '');
16
+ starknetGetHashMock.mockImplementation(() => '');
17
+
18
+ describe('sign/utils', () => {
19
+ describe('getHash', () => {
20
+ test('should return an EVM hash on EVM address', () => {
21
+ expect.assertions(1);
22
+ getHash(evmMessage.data, evmMessage.address);
23
+
24
+ expect(evmGetHashMock).toHaveBeenCalled();
25
+ });
26
+
27
+ test('should return a Starknet hash on starknet payload and starknet address', () => {
28
+ expect.assertions(1);
29
+ getHash(starknetMessage.data, starknetMessage.address);
30
+
31
+ expect(starknetGetHashMock).toHaveBeenCalled();
32
+ });
33
+
34
+ test('should return a Starknet hash on starknet payload and EVM address', () => {
35
+ expect.assertions(1);
36
+ getHash(starknetMessage.data, evmMessage.address);
37
+
38
+ expect(starknetGetHashMock).toHaveBeenCalled();
39
+ });
40
+ });
41
+
42
+ describe('verify', () => {
43
+ test('should call EVM verification on EVM address', async () => {
44
+ expect.assertions(1);
45
+ await verify(evmMessage.address, evmMessage.sig, evmMessage.data);
46
+
47
+ expect(evmVerificationMock).toHaveBeenCalled();
48
+ });
49
+
50
+ test('should call Starknet verification on Starknet payload', async () => {
51
+ expect.assertions(1);
52
+ await verify(
53
+ starknetMessage.address,
54
+ starknetMessage.sig,
55
+ starknetMessage.data
56
+ );
57
+
58
+ expect(starknetVerificationMock).toHaveBeenCalled();
59
+ });
60
+
61
+ test('should call Starknet verification on Starknet payload and EVM message', async () => {
62
+ expect.assertions(1);
63
+ await verify(
64
+ evmMessage.address,
65
+ starknetMessage.sig,
66
+ starknetMessage.data
67
+ );
68
+
69
+ expect(starknetVerificationMock).toHaveBeenCalled();
70
+ });
71
+
72
+ test('should throw an error on empty address', () => {
73
+ expect(
74
+ verify('', evmMessage.sig, evmMessage.data)
75
+ ).rejects.toThrowError();
76
+ });
77
+
78
+ test('should throw an error on invalid address', () => {
79
+ expect(
80
+ verify('hello-world', evmMessage.sig, evmMessage.data)
81
+ ).rejects.toThrowError();
82
+ });
83
+ });
84
+ });
@@ -0,0 +1,41 @@
1
+ import * as starknet from './starknet';
2
+ import * as evm from './evm';
3
+ import { isEvmAddress, isStarknetAddress } from '../utils';
4
+ import type { StarkNetType } from 'starknet';
5
+ import type { TypedDataField } from '@ethersproject/abstract-signer';
6
+ import type { ProviderOptions } from '../utils/provider';
7
+
8
+ export type SignaturePayload = {
9
+ domain: Record<string, string>;
10
+ types: Record<string, StarkNetType[] | TypedDataField[]>;
11
+ primaryType?: string;
12
+ message: Record<string, any>;
13
+ };
14
+
15
+ export function getHash(data: SignaturePayload, address?: string): string {
16
+ const networkType = starknet.isStarknetMessage(data) ? starknet : evm;
17
+
18
+ return networkType.getHash(data, address as string);
19
+ }
20
+
21
+ export async function verify(
22
+ address: string,
23
+ sig: string | string[],
24
+ data: SignaturePayload,
25
+ network = '1',
26
+ options: ProviderOptions = {}
27
+ ): Promise<boolean> {
28
+ if (!isStarknetAddress(address) && !isEvmAddress(address)) {
29
+ throw new Error('Invalid address');
30
+ }
31
+
32
+ const networkType = starknet.isStarknetMessage(data) ? starknet : evm;
33
+
34
+ return await networkType.default(
35
+ address,
36
+ sig as any,
37
+ data,
38
+ network as any,
39
+ options
40
+ );
41
+ }
@@ -0,0 +1,55 @@
1
+ import { test, expect, describe } from 'vitest';
2
+ import starknetMessage from '../../test/fixtures/starknet/message-alias.json';
3
+ import verify, { getHash } from './starknet';
4
+
5
+ describe('verify/starknet', () => {
6
+ describe('getHash()', () => {
7
+ test('should return a hash string', () => {
8
+ const hash = getHash(starknetMessage.data, starknetMessage.address);
9
+ expect(hash).toBe(
10
+ '0x2513fb25c2147469748ec099bf849a21dde6c9b7cfb6b86af431b5e898309bd'
11
+ );
12
+ });
13
+
14
+ test('should return a different by address', () => {
15
+ const hash = getHash(starknetMessage.data, '0x0');
16
+ expect(hash).not.toBe(
17
+ '0x2513fb25c2147469748ec099bf849a21dde6c9b7cfb6b86af431b5e898309bd'
18
+ );
19
+ });
20
+ });
21
+
22
+ describe('verify()', () => {
23
+ test('should return true if the signature is valid', () => {
24
+ expect(
25
+ verify(
26
+ starknetMessage.address,
27
+ starknetMessage.sig,
28
+ starknetMessage.data,
29
+ 'SN_SEPOLIA'
30
+ )
31
+ ).resolves.toBe(true);
32
+ });
33
+
34
+ test('should throw an error if message is on wrong network', () => {
35
+ expect(
36
+ verify(
37
+ starknetMessage.address,
38
+ starknetMessage.sig,
39
+ starknetMessage.data,
40
+ 'SN_MAIN'
41
+ )
42
+ ).rejects.toThrowError();
43
+ });
44
+
45
+ test('should throw an error if the signature is invalid', () => {
46
+ expect(
47
+ verify(
48
+ '0x7667469b8e93faa642573078b6bf8c790d3a6184b2a1bb39c5c923a732862e1',
49
+ starknetMessage.sig,
50
+ starknetMessage.data
51
+ )
52
+ ).rejects.toThrow();
53
+ });
54
+ });
55
+ });
@@ -0,0 +1,82 @@
1
+ import { Contract, RpcProvider, typedData } from 'starknet';
2
+ import type { SignaturePayload } from '.';
3
+ import type { ProviderOptions } from '../utils/provider';
4
+
5
+ export type NetworkType = 'SN_MAIN' | 'SN_SEPOLIA';
6
+
7
+ const RPC_URLS: Record<NetworkType, string> = {
8
+ SN_MAIN: 'https://starknet-mainnet.public.blastapi.io',
9
+ SN_SEPOLIA: 'https://starknet-sepolia.public.blastapi.io'
10
+ };
11
+
12
+ const ABI = [
13
+ {
14
+ name: 'argent::account::interface::IDeprecatedArgentAccount',
15
+ type: 'interface',
16
+ items: [
17
+ {
18
+ name: 'isValidSignature',
19
+ type: 'function',
20
+ inputs: [
21
+ {
22
+ name: 'hash',
23
+ type: 'core::felt252'
24
+ },
25
+ {
26
+ name: 'signatures',
27
+ type: 'core::array::Array::<core::felt252>'
28
+ }
29
+ ],
30
+ outputs: [
31
+ {
32
+ type: 'core::felt252'
33
+ }
34
+ ],
35
+ state_mutability: 'view'
36
+ }
37
+ ]
38
+ }
39
+ ];
40
+
41
+ function getProvider(network: NetworkType, options: ProviderOptions) {
42
+ if (!RPC_URLS[network]) throw new Error('Invalid network');
43
+
44
+ return new RpcProvider({
45
+ nodeUrl: options?.broviderUrl ?? RPC_URLS[network]
46
+ });
47
+ }
48
+
49
+ export function isStarknetMessage(data: SignaturePayload): boolean {
50
+ return !!data.primaryType && !!data.types.StarkNetDomain;
51
+ }
52
+
53
+ export function getHash(data: SignaturePayload, address: string): string {
54
+ const { domain, types, primaryType, message } =
55
+ data as Required<SignaturePayload>;
56
+
57
+ return typedData.getMessageHash(
58
+ { types, primaryType, domain, message },
59
+ address
60
+ );
61
+ }
62
+
63
+ export default async function verify(
64
+ address: string,
65
+ sig: string[],
66
+ data: SignaturePayload,
67
+ network: NetworkType = 'SN_MAIN',
68
+ options: ProviderOptions = {}
69
+ ): Promise<boolean> {
70
+ const contractAccount = new Contract(
71
+ ABI,
72
+ address,
73
+ getProvider(network, options)
74
+ );
75
+
76
+ await contractAccount.isValidSignature(getHash(data, address), [
77
+ sig[0],
78
+ sig[1]
79
+ ]);
80
+
81
+ return true;
82
+ }
@@ -1,55 +0,0 @@
1
- import { arrayify } from '@ethersproject/bytes';
2
- import { StaticJsonRpcProvider } from '@ethersproject/providers';
3
- import getProvider from '../utils/provider';
4
- import { call } from '../utils';
5
-
6
- export async function verifyDefault(
7
- address: string,
8
- sig: string,
9
- hash: string,
10
- provider: StaticJsonRpcProvider
11
- ) {
12
- let returnValue;
13
- const magicValue = '0x1626ba7e';
14
- const abi =
15
- 'function isValidSignature(bytes32 _hash, bytes memory _signature) public view returns (bytes4 magicValue)';
16
- try {
17
- returnValue = await call(
18
- provider,
19
- [abi],
20
- [address, 'isValidSignature', [arrayify(hash), sig]]
21
- );
22
- } catch (e) {
23
- // @ts-ignore
24
- if (e.message.startsWith('missing revert data in call exception')) {
25
- return false;
26
- }
27
- throw e;
28
- }
29
-
30
- return returnValue.toLowerCase() === magicValue.toLowerCase();
31
- }
32
-
33
- export async function verifyOldVersion(
34
- address: string,
35
- sig: string,
36
- hash: string,
37
- provider: StaticJsonRpcProvider
38
- ) {
39
- const magicValue = '0x20c13b0b';
40
- const abi =
41
- 'function isValidSignature(bytes _hash, bytes memory _signature) public view returns (bytes4 magicValue)';
42
-
43
- const returnValue = await call(
44
- provider,
45
- [abi],
46
- [address, 'isValidSignature', [arrayify(hash), sig]]
47
- );
48
- return returnValue.toLowerCase() === magicValue.toLowerCase();
49
- }
50
-
51
- export async function verify(address, sig, hash, network = '1', options = {}) {
52
- const provider = getProvider(network, options);
53
- if (await verifyDefault(address, sig, hash, provider)) return true;
54
- return await verifyOldVersion(address, sig, hash, provider);
55
- }