@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.
- package/dist/snapshot.cjs.js +499 -324
- package/dist/snapshot.esm.js +500 -325
- package/dist/snapshot.min.js +21 -21
- package/dist/src/index.d.ts +4 -20
- package/dist/src/multicall/evm.d.ts +4 -0
- package/dist/src/multicall/index.d.ts +3 -0
- package/dist/src/multicall/multicaller.d.ts +15 -0
- package/dist/src/multicall/starknet.d.ts +2 -0
- package/dist/src/schemas/index.d.ts +2 -18
- package/dist/src/utils/provider.d.ts +5 -6
- package/dist/src/utils.d.ts +1 -2
- package/package.json +1 -1
- package/src/multicall/evm.ts +48 -0
- package/src/multicall/index.ts +52 -0
- package/src/{utils → multicall}/multicaller.ts +13 -8
- package/src/multicall/starknet.ts +154 -0
- package/src/networks.json +30 -1
- package/src/schemas/space.json +3 -13
- package/src/sign/hashedTypes.json +2 -1
- package/src/sign/types.ts +1 -1
- package/src/utils/provider.ts +91 -44
- package/src/utils.ts +3 -74
- package/dist/src/utils/multicaller.d.ts +0 -12
package/src/utils/provider.ts
CHANGED
|
@@ -1,52 +1,99 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '@ethersproject/providers';
|
|
1
|
+
import { StaticJsonRpcProvider } from '@ethersproject/providers';
|
|
2
|
+
import { RpcProvider } from 'starknet';
|
|
3
|
+
import networks from '../networks.json';
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
export interface ProviderOptions {
|
|
6
|
+
readonly broviderUrl?: string;
|
|
7
|
+
readonly timeout?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type ProviderInstance = StaticJsonRpcProvider | RpcProvider;
|
|
11
|
+
|
|
12
|
+
type ProviderType = 'evm' | 'starknet';
|
|
13
|
+
|
|
14
|
+
const DEFAULT_BROVIDER_URL = 'https://rpc.snapshot.org' as const;
|
|
15
|
+
const DEFAULT_TIMEOUT = 25000 as const;
|
|
16
|
+
const STARKNET_BROVIDER_KEYS: string[] = ['sn', 'sn-sep'] as const;
|
|
17
|
+
|
|
18
|
+
const providerMemo = new Map<string, ProviderInstance>();
|
|
8
19
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
20
|
+
const providerFnMap: Record<
|
|
21
|
+
ProviderType,
|
|
22
|
+
(networkId: string, options: Required<ProviderOptions>) => ProviderInstance
|
|
23
|
+
> = {
|
|
24
|
+
evm: getEvmProvider,
|
|
25
|
+
starknet: getStarknetProvider
|
|
12
26
|
};
|
|
13
27
|
|
|
14
|
-
|
|
15
|
-
|
|
28
|
+
function normalizeOptions(
|
|
29
|
+
options: ProviderOptions = {}
|
|
30
|
+
): Required<ProviderOptions> {
|
|
31
|
+
return {
|
|
32
|
+
broviderUrl: options.broviderUrl || DEFAULT_BROVIDER_URL,
|
|
33
|
+
timeout: options.timeout ?? DEFAULT_TIMEOUT
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getBroviderNetworkId(network: string | number): string {
|
|
38
|
+
const config = networks[network];
|
|
39
|
+
if (!config) {
|
|
40
|
+
throw new Error(`Network '${network}' is not supported`);
|
|
41
|
+
}
|
|
42
|
+
return String(config.key);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getProviderType(networkId: string): ProviderType {
|
|
46
|
+
const isStarknet = STARKNET_BROVIDER_KEYS.includes(networkId);
|
|
47
|
+
return isStarknet ? 'starknet' : 'evm';
|
|
48
|
+
}
|
|
16
49
|
|
|
50
|
+
function createMemoKey(
|
|
51
|
+
networkId: string,
|
|
52
|
+
options: Required<ProviderOptions>
|
|
53
|
+
): string {
|
|
54
|
+
return `${networkId}:${options.broviderUrl}:${options.timeout}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// return loose `any` type to avoid typecheck issues on package consumers
|
|
17
58
|
export default function getProvider(
|
|
18
|
-
network,
|
|
19
|
-
{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (!batchedProviders[network])
|
|
46
|
-
batchedProviders[network] = new JsonRpcBatchProvider({
|
|
47
|
-
url,
|
|
48
|
-
timeout,
|
|
59
|
+
network: string | number,
|
|
60
|
+
options: ProviderOptions = {}
|
|
61
|
+
): any {
|
|
62
|
+
const networkId = getBroviderNetworkId(network);
|
|
63
|
+
const normalizedOptions = normalizeOptions(options);
|
|
64
|
+
const memoKey = createMemoKey(networkId, normalizedOptions);
|
|
65
|
+
|
|
66
|
+
const memoized = providerMemo.get(memoKey);
|
|
67
|
+
if (memoized) {
|
|
68
|
+
return memoized;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const providerType = getProviderType(networkId);
|
|
72
|
+
const provider = providerFnMap[providerType](networkId, normalizedOptions);
|
|
73
|
+
|
|
74
|
+
providerMemo.set(memoKey, provider);
|
|
75
|
+
return provider;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getEvmProvider(
|
|
79
|
+
networkId: string,
|
|
80
|
+
options: Required<ProviderOptions>
|
|
81
|
+
): StaticJsonRpcProvider {
|
|
82
|
+
return new StaticJsonRpcProvider(
|
|
83
|
+
{
|
|
84
|
+
url: `${options.broviderUrl}/${networkId}`,
|
|
85
|
+
timeout: options.timeout,
|
|
49
86
|
allowGzip: true
|
|
50
|
-
}
|
|
51
|
-
|
|
87
|
+
},
|
|
88
|
+
Number(networkId)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getStarknetProvider(
|
|
93
|
+
networkKey: string,
|
|
94
|
+
options: Required<ProviderOptions>
|
|
95
|
+
): RpcProvider {
|
|
96
|
+
return new RpcProvider({
|
|
97
|
+
nodeUrl: `${options.broviderUrl}/${networkKey}`
|
|
98
|
+
});
|
|
52
99
|
}
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import fetch from 'cross-fetch';
|
|
2
|
-
import { Interface } from '@ethersproject/abi';
|
|
3
2
|
import { Contract } from '@ethersproject/contracts';
|
|
4
3
|
import { getAddress, isAddress } from '@ethersproject/address';
|
|
5
4
|
import { parseUnits } from '@ethersproject/units';
|
|
@@ -8,7 +7,6 @@ import { jsonToGraphQLQuery } from 'json-to-graphql-query';
|
|
|
8
7
|
import Ajv from 'ajv';
|
|
9
8
|
import addFormats from 'ajv-formats';
|
|
10
9
|
import addErrors from 'ajv-errors';
|
|
11
|
-
import Multicaller from './utils/multicaller';
|
|
12
10
|
import { getSnapshots } from './utils/blockfinder';
|
|
13
11
|
import getProvider from './utils/provider';
|
|
14
12
|
import { signMessage, getBlockNumber } from './utils/web3';
|
|
@@ -18,6 +16,7 @@ import networks from './networks.json';
|
|
|
18
16
|
import voting from './voting';
|
|
19
17
|
import getDelegatesBySpace, { SNAPSHOT_SUBGRAPH_URL } from './utils/delegation';
|
|
20
18
|
import { validateAndParseAddress } from 'starknet';
|
|
19
|
+
import { multicall, Multicaller } from './multicall';
|
|
21
20
|
|
|
22
21
|
interface Options {
|
|
23
22
|
url?: string;
|
|
@@ -45,16 +44,6 @@ const ENS_ABI = [
|
|
|
45
44
|
'function resolver(bytes32 node) view returns (address)' // ENS registry ABI
|
|
46
45
|
];
|
|
47
46
|
const EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
48
|
-
const STARKNET_NETWORKS = {
|
|
49
|
-
'0x534e5f4d41494e': {
|
|
50
|
-
name: 'Starknet',
|
|
51
|
-
testnet: false
|
|
52
|
-
},
|
|
53
|
-
'0x534e5f5345504f4c4941': {
|
|
54
|
-
name: 'Starknet Sepolia',
|
|
55
|
-
testnet: true
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
47
|
|
|
59
48
|
const scoreApiHeaders = {
|
|
60
49
|
Accept: 'application/json',
|
|
@@ -189,24 +178,6 @@ ajv.addKeyword({
|
|
|
189
178
|
}
|
|
190
179
|
});
|
|
191
180
|
|
|
192
|
-
ajv.addKeyword({
|
|
193
|
-
keyword: 'starknetNetwork',
|
|
194
|
-
validate: function (schema, data) {
|
|
195
|
-
// @ts-ignore
|
|
196
|
-
const snapshotEnv = this.snapshotEnv || 'default';
|
|
197
|
-
if (snapshotEnv === 'mainnet') {
|
|
198
|
-
return Object.keys(STARKNET_NETWORKS)
|
|
199
|
-
.filter((id) => !STARKNET_NETWORKS[id].testnet)
|
|
200
|
-
.includes(data);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return Object.keys(STARKNET_NETWORKS).includes(data);
|
|
204
|
-
},
|
|
205
|
-
error: {
|
|
206
|
-
message: 'network not allowed'
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
|
|
210
181
|
// Custom URL format to allow empty string values
|
|
211
182
|
// https://github.com/snapshot-labs/snapshot.js/pull/541/files
|
|
212
183
|
ajv.addFormat('customUrl', {
|
|
@@ -280,48 +251,6 @@ export async function call(provider, abi: any[], call: any[], options?) {
|
|
|
280
251
|
return Promise.reject(e);
|
|
281
252
|
}
|
|
282
253
|
}
|
|
283
|
-
|
|
284
|
-
export async function multicall(
|
|
285
|
-
network: string,
|
|
286
|
-
provider,
|
|
287
|
-
abi: any[],
|
|
288
|
-
calls: any[],
|
|
289
|
-
options?
|
|
290
|
-
) {
|
|
291
|
-
const multicallAbi = [
|
|
292
|
-
'function aggregate(tuple(address target, bytes callData)[] calls) view returns (uint256 blockNumber, bytes[] returnData)'
|
|
293
|
-
];
|
|
294
|
-
const multicallAddress =
|
|
295
|
-
options?.multicallAddress || networks[network].multicall;
|
|
296
|
-
const multi = new Contract(multicallAddress, multicallAbi, provider);
|
|
297
|
-
const itf = new Interface(abi);
|
|
298
|
-
try {
|
|
299
|
-
const max = options?.limit || 500;
|
|
300
|
-
if (options?.limit) delete options.limit;
|
|
301
|
-
const pages = Math.ceil(calls.length / max);
|
|
302
|
-
const promises: any = [];
|
|
303
|
-
Array.from(Array(pages)).forEach((x, i) => {
|
|
304
|
-
const callsInPage = calls.slice(max * i, max * (i + 1));
|
|
305
|
-
promises.push(
|
|
306
|
-
multi.aggregate(
|
|
307
|
-
callsInPage.map((call) => [
|
|
308
|
-
call[0].toLowerCase(),
|
|
309
|
-
itf.encodeFunctionData(call[1], call[2])
|
|
310
|
-
]),
|
|
311
|
-
options || {}
|
|
312
|
-
)
|
|
313
|
-
);
|
|
314
|
-
});
|
|
315
|
-
let results: any = await Promise.all(promises);
|
|
316
|
-
results = results.reduce((prev: any, [, res]: any) => prev.concat(res), []);
|
|
317
|
-
return results.map((call, i) =>
|
|
318
|
-
itf.decodeFunctionResult(calls[i][1], call)
|
|
319
|
-
);
|
|
320
|
-
} catch (e: any) {
|
|
321
|
-
return Promise.reject(e);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
254
|
export async function subgraphRequest(url: string, query, options: any = {}) {
|
|
326
255
|
const body: Record<string, any> = { query: jsonToGraphQLQuery({ query }) };
|
|
327
256
|
if (options.variables) body.variables = options.variables;
|
|
@@ -634,13 +563,13 @@ export async function getEnsTextRecord(
|
|
|
634
563
|
]) // Query for text record from each resolver
|
|
635
564
|
];
|
|
636
565
|
|
|
637
|
-
const [[resolverAddress], ...textRecords]
|
|
566
|
+
const [[resolverAddress], ...textRecords] = (await multicall(
|
|
638
567
|
network,
|
|
639
568
|
provider,
|
|
640
569
|
ENS_ABI,
|
|
641
570
|
calls,
|
|
642
571
|
multicallOptions
|
|
643
|
-
);
|
|
572
|
+
)) as string[][];
|
|
644
573
|
|
|
645
574
|
const resolverIndex = ensResolvers.indexOf(resolverAddress);
|
|
646
575
|
return resolverIndex !== -1 ? textRecords[resolverIndex]?.[0] : null;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { StaticJsonRpcProvider } from '@ethersproject/providers';
|
|
2
|
-
export default class Multicaller {
|
|
3
|
-
network: string;
|
|
4
|
-
provider: StaticJsonRpcProvider;
|
|
5
|
-
abi: any[];
|
|
6
|
-
options: any;
|
|
7
|
-
calls: any[];
|
|
8
|
-
paths: any[];
|
|
9
|
-
constructor(network: string, provider: StaticJsonRpcProvider, abi: any[], options?: any);
|
|
10
|
-
call(path: any, address: any, fn: any, params?: any): Multicaller;
|
|
11
|
-
execute(from?: any): Promise<any>;
|
|
12
|
-
}
|