@snapshot-labs/snapshot.js 0.12.0-beta.0 → 0.12.0-beta.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.
Files changed (56) hide show
  1. package/README.md +2 -2
  2. package/dist/index.d.ts +20 -2
  3. package/dist/schemas/index.d.ts +19 -1
  4. package/dist/sign/types.d.ts +2 -0
  5. package/dist/snapshot.cjs.js +1851 -1929
  6. package/dist/snapshot.esm.js +1852 -1930
  7. package/dist/snapshot.min.js +16 -5
  8. package/dist/src/index.d.ts +725 -0
  9. package/dist/src/schemas/index.d.ts +671 -0
  10. package/dist/src/sign/index.d.ts +29 -0
  11. package/dist/src/sign/types.d.ts +227 -0
  12. package/dist/src/utils/blockfinder.d.ts +1 -0
  13. package/dist/src/utils/delegation.d.ts +18 -0
  14. package/dist/src/utils/multicaller.d.ts +12 -0
  15. package/dist/src/utils/provider.d.ts +5 -0
  16. package/dist/src/utils/web3.d.ts +2 -0
  17. package/dist/src/utils.d.ts +91 -0
  18. package/dist/src/verify/evm.d.ts +4 -0
  19. package/dist/src/verify/evm.spec.d.ts +1 -0
  20. package/dist/src/verify/index.d.ts +11 -0
  21. package/dist/src/verify/index.spec.d.ts +1 -0
  22. package/dist/src/verify/starknet.d.ts +6 -0
  23. package/dist/src/verify/starknet.spec.d.ts +1 -0
  24. package/dist/src/voting/approval.d.ts +22 -0
  25. package/dist/src/voting/index.d.ts +14 -0
  26. package/dist/src/voting/quadratic.d.ts +20 -0
  27. package/dist/src/voting/rankedChoice.d.ts +18 -0
  28. package/dist/src/voting/singleChoice.d.ts +18 -0
  29. package/dist/src/voting/types.d.ts +35 -0
  30. package/dist/src/voting/weighted.d.ts +26 -0
  31. package/dist/utils/delegation.d.ts +1 -1
  32. package/dist/utils.d.ts +3 -2
  33. package/package.json +6 -3
  34. package/src/delegationSubgraphs.json +8 -8
  35. package/src/gateways.json +1 -2
  36. package/src/networks.json +222 -60
  37. package/src/schemas/profile.json +30 -0
  38. package/src/schemas/proposal.json +1 -1
  39. package/src/schemas/space.json +8 -7
  40. package/src/schemas/statement.json +17 -2
  41. package/src/schemas/vote.json +1 -1
  42. package/src/sign/hashedTypes.json +8 -1
  43. package/src/sign/index.ts +8 -6
  44. package/src/sign/types.ts +11 -1
  45. package/src/utils/provider.ts +8 -2
  46. package/src/utils/web3.ts +1 -1
  47. package/src/utils.spec.js +86 -31
  48. package/src/utils.ts +111 -38
  49. package/src/verify/evm.spec.ts +32 -0
  50. package/src/verify/evm.ts +82 -0
  51. package/src/verify/index.spec.ts +84 -0
  52. package/src/verify/index.ts +41 -0
  53. package/src/verify/starknet.spec.ts +55 -0
  54. package/src/verify/starknet.ts +82 -0
  55. package/src/sign/eip1271.ts +0 -55
  56. package/src/sign/utils.ts +0 -27
@@ -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
- }
package/src/sign/utils.ts DELETED
@@ -1,27 +0,0 @@
1
- import { verifyTypedData } from '@ethersproject/wallet';
2
- import { _TypedDataEncoder } from '@ethersproject/hash';
3
- import { verify as verifyEIP1271 } from './eip1271';
4
-
5
- export function getHash(data) {
6
- const { domain, types, message } = data;
7
- return _TypedDataEncoder.hash(domain, types, message);
8
- }
9
-
10
- export async function verify(address, sig, data, network = '1', options = {}) {
11
- const { domain, types, message } = data;
12
-
13
- const hash = getHash(data);
14
- // console.log('Hash', hash);
15
- // console.log('Address', address);
16
-
17
- try {
18
- const recoverAddress = verifyTypedData(domain, types, message, sig);
19
- // console.log('Recover address', recoverAddress);
20
- if (address === recoverAddress) return true;
21
- } catch (e) {
22
- // console.log('Could not recoverAddress:' + e.message);
23
- }
24
-
25
- // console.log('Check EIP1271 signature');
26
- return await verifyEIP1271(address, sig, hash, network, options);
27
- }