@snapshot-labs/snapshot.js 0.13.1 → 0.14.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.
@@ -93,7 +93,6 @@ declare const _default: {
93
93
  snapshotNetwork: boolean;
94
94
  title: string;
95
95
  minLength: number;
96
- maxLength: number;
97
96
  };
98
97
  symbol: {
99
98
  type: string;
@@ -136,7 +135,6 @@ declare const _default: {
136
135
  };
137
136
  network: {
138
137
  type: string;
139
- maxLength: number;
140
138
  title: string;
141
139
  snapshotNetwork: boolean;
142
140
  };
@@ -291,14 +289,7 @@ declare const _default: {
291
289
  type: string;
292
290
  title: string;
293
291
  description: string;
294
- anyOf: ({
295
- snapshotNetwork: boolean;
296
- starknetNetwork?: undefined;
297
- } | {
298
- starknetNetwork: boolean;
299
- snapshotNetwork?: undefined;
300
- })[];
301
- errorMessage: string;
292
+ snapshotNetwork: boolean;
302
293
  };
303
294
  delegationApi: {
304
295
  type: string;
@@ -399,14 +390,7 @@ declare const _default: {
399
390
  network: {
400
391
  type: string;
401
392
  title: string;
402
- anyOf: ({
403
- snapshotNetwork: boolean;
404
- starknetNetwork?: undefined;
405
- } | {
406
- starknetNetwork: boolean;
407
- snapshotNetwork?: undefined;
408
- })[];
409
- errorMessage: string;
393
+ snapshotNetwork: boolean;
410
394
  };
411
395
  };
412
396
  required: string[];
@@ -827,7 +811,7 @@ declare const _default: {
827
811
  };
828
812
  utils: {
829
813
  call: typeof import("./utils").call;
830
- multicall: typeof import("./utils").multicall;
814
+ multicall: typeof import("./multicall").multicall;
831
815
  subgraphRequest: typeof import("./utils").subgraphRequest;
832
816
  ipfsGet: typeof import("./utils").ipfsGet;
833
817
  getUrl: typeof import("./utils").getUrl;
@@ -856,7 +840,7 @@ declare const _default: {
856
840
  getProvider: typeof import("./utils/provider").default;
857
841
  signMessage: typeof import("./utils/web3").signMessage;
858
842
  getBlockNumber: typeof import("./utils/web3").getBlockNumber;
859
- Multicaller: typeof import("./utils/multicaller").default;
843
+ Multicaller: typeof import("./multicall").Multicaller;
860
844
  getSnapshots: typeof import("./utils/blockfinder").getSnapshots;
861
845
  getHash: typeof import("./verify").getHash;
862
846
  verify: typeof import("./verify").verify;
@@ -0,0 +1,4 @@
1
+ import { Fragment, FunctionFragment, JsonFragment } from '@ethersproject/abi';
2
+ import { Signer } from '@ethersproject/abstract-signer';
3
+ import { Provider } from '@ethersproject/providers';
4
+ export default function multicall(address: string, provider: Signer | Provider, abi: string | (Fragment | JsonFragment | string)[], calls: [string, FunctionFragment | string, any[]][], limit: number, options?: {}): Promise<any>;
@@ -0,0 +1,3 @@
1
+ import Multicaller from './multicaller';
2
+ declare function multicall(network: string, provider: any, abi: any[], calls: any[], options?: Record<string, any>): Promise<any>;
3
+ export { multicall, Multicaller };
@@ -0,0 +1,15 @@
1
+ import { StaticJsonRpcProvider } from '@ethersproject/providers';
2
+ import { RpcProvider } from 'starknet';
3
+ type Path = string | number | number[] | string[];
4
+ export default class Multicaller {
5
+ network: string;
6
+ provider: StaticJsonRpcProvider | RpcProvider;
7
+ abi: any[];
8
+ options: any;
9
+ calls: any[];
10
+ paths: Path[];
11
+ constructor(network: string, provider: StaticJsonRpcProvider | RpcProvider, abi: any[], options?: any);
12
+ call(path: Path, address: string, fn: string, params?: any[]): Multicaller;
13
+ execute(from?: any): Promise<any>;
14
+ }
15
+ export {};
@@ -0,0 +1,2 @@
1
+ import { RpcProvider } from 'starknet';
2
+ export default function multicall(address: string, provider: RpcProvider, abi: any[], calls: any[], limit: number, options?: Record<string, any>): Promise<any[][]>;
@@ -89,7 +89,6 @@ declare const _default: {
89
89
  snapshotNetwork: boolean;
90
90
  title: string;
91
91
  minLength: number;
92
- maxLength: number;
93
92
  };
94
93
  symbol: {
95
94
  type: string;
@@ -132,7 +131,6 @@ declare const _default: {
132
131
  };
133
132
  network: {
134
133
  type: string;
135
- maxLength: number;
136
134
  title: string;
137
135
  snapshotNetwork: boolean;
138
136
  };
@@ -287,14 +285,7 @@ declare const _default: {
287
285
  type: string;
288
286
  title: string;
289
287
  description: string;
290
- anyOf: ({
291
- snapshotNetwork: boolean;
292
- starknetNetwork?: undefined;
293
- } | {
294
- starknetNetwork: boolean;
295
- snapshotNetwork?: undefined;
296
- })[];
297
- errorMessage: string;
288
+ snapshotNetwork: boolean;
298
289
  };
299
290
  delegationApi: {
300
291
  type: string;
@@ -395,14 +386,7 @@ declare const _default: {
395
386
  network: {
396
387
  type: string;
397
388
  title: string;
398
- anyOf: ({
399
- snapshotNetwork: boolean;
400
- starknetNetwork?: undefined;
401
- } | {
402
- starknetNetwork: boolean;
403
- snapshotNetwork?: undefined;
404
- })[];
405
- errorMessage: string;
389
+ snapshotNetwork: boolean;
406
390
  };
407
391
  };
408
392
  required: string[];
@@ -1,6 +1,5 @@
1
- export type ProviderOptions = {
2
- broviderUrl?: string;
3
- timeout?: number;
4
- };
5
- export default function getProvider(network: any, { broviderUrl, timeout }?: ProviderOptions): any;
6
- export declare function getBatchedProvider(network: any, { broviderUrl, timeout }?: ProviderOptions): any;
1
+ export interface ProviderOptions {
2
+ readonly broviderUrl?: string;
3
+ readonly timeout?: number;
4
+ }
5
+ export default function getProvider(network: string | number, options?: ProviderOptions): any;
@@ -1,9 +1,9 @@
1
- import Multicaller from './utils/multicaller';
2
1
  import { getSnapshots } from './utils/blockfinder';
3
2
  import getProvider from './utils/provider';
4
3
  import { signMessage, getBlockNumber } from './utils/web3';
5
4
  import { getHash, verify } from './verify';
6
5
  import getDelegatesBySpace, { SNAPSHOT_SUBGRAPH_URL } from './utils/delegation';
6
+ import { multicall, Multicaller } from './multicall';
7
7
  interface Options {
8
8
  url?: string;
9
9
  headers?: any;
@@ -14,7 +14,6 @@ interface Strategy {
14
14
  params: any;
15
15
  }
16
16
  export declare function call(provider: any, abi: any[], call: any[], options?: any): Promise<any>;
17
- export declare function multicall(network: string, provider: any, abi: any[], calls: any[], options?: any): Promise<any>;
18
17
  export declare function subgraphRequest(url: string, query: any, options?: any): Promise<any>;
19
18
  export declare function getUrl(uri: any, gateway?: string): any;
20
19
  export declare function getJSON(uri: any, options?: any): Promise<any>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapshot-labs/snapshot.js",
3
- "version": "0.13.1",
3
+ "version": "0.14.1",
4
4
  "repository": "snapshot-labs/snapshot.js",
5
5
  "license": "MIT",
6
6
  "main": "dist/snapshot.cjs.js",
@@ -0,0 +1,48 @@
1
+ import {
2
+ Fragment,
3
+ FunctionFragment,
4
+ Interface,
5
+ JsonFragment
6
+ } from '@ethersproject/abi';
7
+ import { Signer } from '@ethersproject/abstract-signer';
8
+ import { Contract } from '@ethersproject/contracts';
9
+ import { Provider } from '@ethersproject/providers';
10
+
11
+ const multicallAbi = [
12
+ 'function aggregate(tuple(address target, bytes callData)[] calls) view returns (uint256 blockNumber, bytes[] returnData)'
13
+ ];
14
+
15
+ export default async function multicall(
16
+ address: string,
17
+ provider: Signer | Provider,
18
+ abi: string | (Fragment | JsonFragment | string)[],
19
+ calls: [string, FunctionFragment | string, any[]][],
20
+ limit: number,
21
+ options = {}
22
+ ): Promise<any> {
23
+ const multi = new Contract(address, multicallAbi, provider);
24
+ const itf = new Interface(abi);
25
+ try {
26
+ const pages = Math.ceil(calls.length / limit);
27
+ const promises: any = [];
28
+ Array.from(Array(pages)).forEach((x, i) => {
29
+ const callsInPage = calls.slice(limit * i, limit * (i + 1));
30
+ promises.push(
31
+ multi.aggregate(
32
+ callsInPage.map((call) => [
33
+ call[0].toLowerCase(),
34
+ itf.encodeFunctionData(call[1], call[2])
35
+ ]),
36
+ options
37
+ )
38
+ );
39
+ });
40
+ let results: any[] = await Promise.all(promises);
41
+ results = results.reduce((prev: any, [, res]: any) => prev.concat(res), []);
42
+ return results.map((call, i) =>
43
+ itf.decodeFunctionResult(calls[i][1], call)
44
+ );
45
+ } catch (e: any) {
46
+ return Promise.reject(e);
47
+ }
48
+ }
@@ -0,0 +1,52 @@
1
+ import { constants } from 'starknet';
2
+ import multicallEvm from './evm';
3
+ import multicallStarknet from './starknet';
4
+ import Multicaller from './multicaller';
5
+ import networks from '../networks.json';
6
+
7
+ type NetworkId = keyof typeof networks;
8
+
9
+ const STARKNET_CHAIN_IDS: NetworkId[] = [
10
+ constants.StarknetChainId.SN_MAIN,
11
+ constants.StarknetChainId.SN_SEPOLIA
12
+ ] as const;
13
+
14
+ const MULTICALLS_FN = {
15
+ evm: multicallEvm,
16
+ starknet: multicallStarknet
17
+ } as const;
18
+
19
+ async function multicall(
20
+ network: string,
21
+ provider,
22
+ abi: any[],
23
+ calls: any[],
24
+ options: Record<string, any> = {}
25
+ ) {
26
+ const address = options?.multicallAddress || networks[network].multicall;
27
+
28
+ if (!address) {
29
+ throw new Error('missing multicall address');
30
+ }
31
+
32
+ const multicallOptions = { ...options };
33
+ const limit = multicallOptions?.limit || 500;
34
+
35
+ delete multicallOptions.limit;
36
+ delete multicallOptions.multicallAddress;
37
+
38
+ const protocol = STARKNET_CHAIN_IDS.includes(network as NetworkId)
39
+ ? 'starknet'
40
+ : 'evm';
41
+
42
+ return MULTICALLS_FN[protocol](
43
+ address,
44
+ provider,
45
+ abi,
46
+ calls,
47
+ limit,
48
+ multicallOptions
49
+ );
50
+ }
51
+
52
+ export { multicall, Multicaller };
@@ -1,28 +1,31 @@
1
1
  import { StaticJsonRpcProvider } from '@ethersproject/providers';
2
2
  import set from 'lodash.set';
3
- import { multicall } from '../utils';
3
+ import { RpcProvider } from 'starknet';
4
+ import { multicall } from './';
5
+
6
+ type Path = string | number | number[] | string[];
4
7
 
5
8
  export default class Multicaller {
6
9
  public network: string;
7
- public provider: StaticJsonRpcProvider;
10
+ public provider: StaticJsonRpcProvider | RpcProvider;
8
11
  public abi: any[];
9
12
  public options: any = {};
10
13
  public calls: any[] = [];
11
- public paths: any[] = [];
14
+ public paths: Path[] = [];
12
15
 
13
16
  constructor(
14
17
  network: string,
15
- provider: StaticJsonRpcProvider,
18
+ provider: StaticJsonRpcProvider | RpcProvider,
16
19
  abi: any[],
17
- options?
20
+ options: any = {}
18
21
  ) {
19
22
  this.network = network;
20
23
  this.provider = provider;
21
24
  this.abi = abi;
22
- this.options = options || {};
25
+ this.options = options;
23
26
  }
24
27
 
25
- call(path, address, fn, params?): Multicaller {
28
+ call(path: Path, address: string, fn: string, params?: any[]): Multicaller {
26
29
  this.calls.push([address, fn, params]);
27
30
  this.paths.push(path);
28
31
  return this;
@@ -37,7 +40,9 @@ export default class Multicaller {
37
40
  this.calls,
38
41
  this.options
39
42
  );
40
- result.forEach((r, i) => set(obj, this.paths[i], r.length > 1 ? r : r[0]));
43
+ result.forEach((r: any, i: number) =>
44
+ set(obj, this.paths[i], r.length > 1 ? r : r[0])
45
+ );
41
46
  this.calls = [];
42
47
  this.paths = [];
43
48
  return obj;
@@ -0,0 +1,154 @@
1
+ import { num, RpcProvider, shortString, transaction, uint256 } from 'starknet';
2
+
3
+ /**
4
+ * Parses the raw result from a Starknet function call based on its ABI.
5
+ * It handles different types like felt252, u8, u256, etc., and decodes them accordingly.
6
+ * @param rawResult - The raw result from the Starknet function call.
7
+ * @param functionAbi - The ABI of the function that was called.
8
+ * @returns The parsed result in a more usable format.
9
+ */
10
+ function parseStarknetResult(rawResult: string[], functionAbi: any): any {
11
+ if (
12
+ !functionAbi ||
13
+ !functionAbi.outputs ||
14
+ !Array.isArray(rawResult) ||
15
+ rawResult.length === 0
16
+ ) {
17
+ return rawResult;
18
+ }
19
+
20
+ const output = functionAbi.outputs[0];
21
+ const rawValue = rawResult[0];
22
+
23
+ try {
24
+ switch (output.type) {
25
+ case 'core::felt252':
26
+ // Try to decode as shortString (for name, symbol)
27
+ try {
28
+ return shortString.decodeShortString(rawValue);
29
+ } catch {
30
+ // If shortString decode fails, return as hex
31
+ return rawValue;
32
+ }
33
+
34
+ // Unsigned integers
35
+ case 'core::integer::u8':
36
+ case 'core::integer::u16':
37
+ case 'core::integer::u32':
38
+ case 'core::integer::u64':
39
+ return parseInt(rawValue, 16);
40
+
41
+ case 'core::integer::u128':
42
+ case 'core::integer::usize':
43
+ return BigInt(rawValue).toString();
44
+
45
+ case 'core::integer::u256':
46
+ return uint256.uint256ToBN({
47
+ low: rawValue,
48
+ high: rawResult[1] || '0x0'
49
+ });
50
+
51
+ // Signed integers
52
+ case 'core::integer::i8':
53
+ case 'core::integer::i16':
54
+ case 'core::integer::i32':
55
+ case 'core::integer::i64':
56
+ return parseInt(rawValue, 16);
57
+
58
+ case 'core::integer::i128':
59
+ return BigInt(rawValue).toString();
60
+
61
+ // Boolean type
62
+ case 'core::bool':
63
+ return rawValue === '0x1' || rawValue === '0x01';
64
+
65
+ // Address types
66
+ case 'core::starknet::contract_address::ContractAddress':
67
+ case 'core::starknet::class_hash::ClassHash':
68
+ case 'core::starknet::storage_access::StorageAddress':
69
+ return rawValue;
70
+
71
+ // Byte array
72
+ case 'core::bytes_31::bytes31':
73
+ return rawValue;
74
+
75
+ default:
76
+ // Return raw value for unknown types
77
+ return rawValue;
78
+ }
79
+ } catch {
80
+ // Fallback to raw result if parsing fails
81
+ return rawResult;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Partitions the responses from a Starknet multicall into individual call results.
87
+ * Each response starts with its length, followed by the actual response data.
88
+ * @param responses - The array of responses from the Starknet multicall.
89
+ * @returns An array of arrays, where each inner array contains the response data for a single call.
90
+ */
91
+ const partitionResponses = (responses: string[]): string[][] => {
92
+ if (responses.length === 0) {
93
+ return [];
94
+ }
95
+
96
+ const [responseLength, ...restResponses] = responses;
97
+ const responseLengthInt = Number(num.toBigInt(responseLength));
98
+ const response = restResponses.slice(0, responseLengthInt);
99
+ const remainingResponses = restResponses.slice(responseLengthInt);
100
+
101
+ return [response, ...partitionResponses(remainingResponses)];
102
+ };
103
+
104
+ export default async function multicall(
105
+ address: string,
106
+ provider: RpcProvider,
107
+ abi: any[],
108
+ calls: any[],
109
+ limit: number,
110
+ options: Record<string, any> = {}
111
+ ) {
112
+ const callData = calls.map((call) => {
113
+ return {
114
+ contractAddress: call[0],
115
+ entrypoint: call[1],
116
+ calldata: call[2] || []
117
+ };
118
+ });
119
+
120
+ // Chunk calls into batches based on limit
121
+ const chunks: any[][] = [];
122
+ for (let i = 0; i < callData.length; i += limit) {
123
+ chunks.push(callData.slice(i, i + limit));
124
+ }
125
+
126
+ // Process each chunk
127
+ const paginatedResults = await Promise.all(
128
+ chunks.map((chunk) =>
129
+ provider.callContract(
130
+ {
131
+ contractAddress: address,
132
+ entrypoint: 'aggregate',
133
+ calldata: transaction.fromCallsToExecuteCalldata(chunk)
134
+ },
135
+ options.blockTag ?? 'latest'
136
+ )
137
+ )
138
+ );
139
+
140
+ const callResults = paginatedResults
141
+ .map((callContractResult) => {
142
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
143
+ const [_blockNumber, _totalLength, ...results] = callContractResult;
144
+ return partitionResponses(results);
145
+ })
146
+ .flat();
147
+
148
+ return callResults.map((result, index) => {
149
+ const [, functionName] = calls[index];
150
+ const functionAbi = abi.find((item) => item.name === functionName);
151
+
152
+ return [parseStarknetResult(result, functionAbi)];
153
+ });
154
+ }
package/src/networks.json CHANGED
@@ -1970,5 +1970,34 @@
1970
1970
  },
1971
1971
  "start": 7521509,
1972
1972
  "logo": "ipfs://QmNnGPr1CNvj12SSGzKARtUHv9FyEfE5nES73U4vBWQSJL"
1973
+ },
1974
+ "0x534e5f4d41494e": {
1975
+ "key": "sn",
1976
+ "name": "Starknet",
1977
+ "shortName": "Starknet",
1978
+ "chainId": "0x534e5f4d41494e",
1979
+ "network": "mainnet",
1980
+ "multicall": "0x05754af3760f3356da99aea5c3ec39ccac7783d925a19666ebbeca58ff0087f4",
1981
+ "explorer": {
1982
+ "url": "https://starkscan.co"
1983
+ },
1984
+ "rpc": [],
1985
+ "start": 8446,
1986
+ "logo": "ipfs://bafkreihbjafyh7eud7r6e5743esaamifcttsvbspfwcrfoc5ykodjdi67m"
1987
+ },
1988
+ "0x534e5f5345504f4c4941": {
1989
+ "key": "sn-sep",
1990
+ "name": "Starknet Testnet",
1991
+ "shortName": "testnet",
1992
+ "chainId": "0x534e5f5345504f4c4941",
1993
+ "network": "testnet",
1994
+ "testnet": true,
1995
+ "multicall": "0x05754af3760f3356da99aea5c3ec39ccac7783d925a19666ebbeca58ff0087f4",
1996
+ "explorer": {
1997
+ "url": "https://sepolia.starkscan.co"
1998
+ },
1999
+ "rpc": [],
2000
+ "start": 7,
2001
+ "logo": "ipfs://bafkreihbjafyh7eud7r6e5743esaamifcttsvbspfwcrfoc5ykodjdi67m"
1973
2002
  }
1974
- }
2003
+ }
@@ -91,8 +91,7 @@
91
91
  "type": "string",
92
92
  "snapshotNetwork": true,
93
93
  "title": "network",
94
- "minLength": 1,
95
- "maxLength": 32
94
+ "minLength": 1
96
95
  },
97
96
  "symbol": {
98
97
  "type": "string",
@@ -135,7 +134,6 @@
135
134
  },
136
135
  "network": {
137
136
  "type": "string",
138
- "maxLength": 12,
139
137
  "title": "network",
140
138
  "snapshotNetwork": true
141
139
  },
@@ -291,11 +289,7 @@
291
289
  "type": "string",
292
290
  "title": "Delegation network",
293
291
  "description": "The network of your delegation contract",
294
- "anyOf": [
295
- { "snapshotNetwork": true },
296
- { "starknetNetwork": true }
297
- ],
298
- "errorMessage": "Must be a valid network"
292
+ "snapshotNetwork": true
299
293
  },
300
294
  "delegationApi": {
301
295
  "type": "string",
@@ -417,11 +411,7 @@
417
411
  "network": {
418
412
  "type": "string",
419
413
  "title": "Network",
420
- "anyOf": [
421
- { "snapshotNetwork": true },
422
- { "starknetNetwork": true }
423
- ],
424
- "errorMessage": "Must be a valid network"
414
+ "snapshotNetwork": true
425
415
  }
426
416
  },
427
417
  "required": ["name", "address", "network"],
@@ -74,5 +74,6 @@
74
74
  "afc5911fd9722b3dc5e8b16a552997510644a52d2b229c3868fb1910b112416e": "vote-string",
75
75
  "e4b768874f191321f9a6460e15f87e98800c78c1e3104f9d4e443f2ed9b1c45e": "vote",
76
76
  "86c81555a3dda45aa7b151c574dc27f7402c23d9b1432156f1daa6c2e15f9891": "vote-array",
77
- "c0f418890817b3e6deb58fa3182bf8ed7619242666a9087eab28f27a6876e1da": "vote-string"
77
+ "c0f418890817b3e6deb58fa3182bf8ed7619242666a9087eab28f27a6876e1da": "vote-string",
78
+ "6107486ff2c58778b92eac3f9c92b26ab59f30959a12307ae868c197f382cdf6": "flag-proposal"
78
79
  }
package/src/sign/types.ts CHANGED
@@ -176,7 +176,7 @@ export const updateProposalTypes = {
176
176
 
177
177
  export const flagProposalTypes = {
178
178
  FlagProposal: [
179
- { name: 'from', type: 'address' },
179
+ { name: 'from', type: 'string' },
180
180
  { name: 'space', type: 'string' },
181
181
  { name: 'proposal', type: 'string' },
182
182
  { name: 'timestamp', type: 'uint64' }