@subsquid/evm-typegen 4.6.0 → 5.0.0

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/src/multicall.ts CHANGED
@@ -1,174 +1,186 @@
1
1
  import * as p from '@subsquid/evm-codec'
2
- import {fun, ContractBase, type AbiFunction, type FunctionReturn, type FunctionArguments} from '@subsquid/evm-abi'
3
-
4
- const aggregate = fun('0x252dba42', "aggregate((address,bytes)[])", {
5
- calls: p.array(p.struct({
6
- target: p.address,
7
- callData: p.bytes
8
- }))
9
- }, {blockNumber: p.uint256, returnData: p.array(p.bytes)})
10
-
11
- const tryAggregate = fun('0xbce38bd7', "tryAggregate(bool,(address,bytes)[])", {
12
- requireSuccess: p.bool,
13
- calls: p.array(p.struct({target: p.address, callData: p.bytes}))
14
- }, p.array(p.struct({success: p.bool, returnData: p.bytes})))
15
-
16
- export type MulticallResult<T extends AbiFunction<any, any>> = {
17
- success: true
18
- value: FunctionReturn<T>
19
- } | {
20
- success: false
21
- returnData?: string
22
- value?: undefined
23
- }
2
+ import {func, ContractBase, type AbiFunction, type FunctionReturn, type FunctionArguments} from '@subsquid/evm-abi'
3
+
4
+ // aggregate((address,bytes)[])
5
+ const aggregate = func(
6
+ '0x252dba42',
7
+ {
8
+ calls: p.array(
9
+ p.struct({
10
+ target: p.address,
11
+ callData: p.bytes,
12
+ }),
13
+ ),
14
+ },
15
+ p.struct({blockNumber: p.uint256, returnData: p.array(p.bytes)}),
16
+ )
17
+
18
+ // tryAggregate(bool,(address,bytes)[])
19
+ const tryAggregate = func(
20
+ '0xbce38bd7',
21
+ {
22
+ requireSuccess: p.bool,
23
+ calls: p.array(p.struct({target: p.address, callData: p.bytes})),
24
+ },
25
+ p.array(p.struct({success: p.bool, returnData: p.bytes})),
26
+ )
27
+
28
+ export type MulticallResult<T extends AbiFunction<any, any>> =
29
+ | {
30
+ success: true
31
+ value: FunctionReturn<T>
32
+ }
33
+ | {
34
+ success: false
35
+ returnData?: string
36
+ value?: undefined
37
+ }
24
38
 
25
39
  type AnyFunc = AbiFunction<any, any>
26
- type AggregateTuple<T extends AnyFunc = AnyFunc> = [func: T, address: string, args: T extends AnyFunc ? FunctionArguments<T> : never]
27
- type Call = {target: string, func: AnyFunc, callData: string}
40
+ type AggregateTuple<T extends AnyFunc = AnyFunc> = [
41
+ func: T,
42
+ address: string,
43
+ args: T extends AnyFunc ? FunctionArguments<T> : never,
44
+ ]
45
+ type Call = {target: string; func: AnyFunc; callData: string}
28
46
 
29
47
  export class Multicall extends ContractBase {
30
- static aggregate = aggregate
31
- static tryAggregate = tryAggregate
48
+ static aggregate = aggregate
49
+ static tryAggregate = tryAggregate
50
+
51
+ aggregate<TF extends AnyFunc>(
52
+ func: TF,
53
+ address: string,
54
+ calls: FunctionArguments<TF>[],
55
+ pageSize?: number,
56
+ ): Promise<FunctionReturn<TF>[]>
57
+
58
+ aggregate<TF extends AnyFunc>(
59
+ func: TF,
60
+ calls: (readonly [address: string, args: FunctionArguments<TF>])[],
61
+ pageSize?: number,
62
+ ): Promise<FunctionReturn<TF>[]>
63
+
64
+ aggregate(calls: AggregateTuple[], pageSize?: number): Promise<any[]>
65
+
66
+ async aggregate(...args: any[]): Promise<any[]> {
67
+ let [calls, pageSize] = this.makeCalls(args)
68
+ if (calls.length === 0) return []
69
+
70
+ const pages = Array.from(splitArray(pageSize, calls))
71
+ const results = await Promise.all(
72
+ pages.map(async (page) => {
73
+ const {returnData} = await this.eth_call(aggregate, {calls: page})
74
+ return returnData.map((data, i) => page[i].func.decodeResult(data))
75
+ }),
76
+ )
77
+
78
+ return results.flat()
79
+ }
32
80
 
33
- aggregate<TF extends AnyFunc>(
34
- func: TF,
35
- address: string,
36
- calls: FunctionArguments<TF>[],
37
- pageSize?: number
38
- ): Promise<FunctionReturn<TF>[]>
39
-
40
- aggregate<TF extends AnyFunc>(
41
- func: TF,
42
- calls: (readonly [address: string, args: FunctionArguments<TF>])[],
43
- pageSize?: number
44
- ): Promise<FunctionReturn<TF>[]>
45
-
46
- aggregate(
47
- calls: AggregateTuple[],
48
- pageSize?: number
49
- ): Promise<any[]>
50
-
51
- async aggregate(...args: any[]): Promise<any[]> {
52
- let [calls, pageSize] = this.makeCalls(args)
53
- if (calls.length === 0) return []
54
-
55
- const pages = Array.from(splitArray(pageSize, calls))
56
- const results = await Promise.all(
57
- pages.map(async (page) => {
58
- const {returnData} = await this.eth_call(aggregate, {calls: page})
59
- return returnData.map((data, i) => page[i].func.decodeResult(data))
60
- })
61
- )
62
-
63
- return results.flat()
64
- }
65
-
66
- tryAggregate<TF extends AnyFunc>(
67
- func: TF,
68
- address: string,
69
- calls: FunctionArguments<TF>[],
70
- pageSize?: number
71
- ): Promise<MulticallResult<TF>[]>
72
-
73
- tryAggregate<TF extends AnyFunc>(
74
- func: TF,
75
- calls: (readonly [address: string, args: FunctionArguments<TF>])[],
76
- pageSize?: number
77
- ): Promise<MulticallResult<TF>[]>
78
-
79
- tryAggregate(
80
- calls: AggregateTuple[],
81
- pageSize?: number
82
- ): Promise<MulticallResult<any>[]>
83
-
84
- async tryAggregate(...args: any[]): Promise<any[]> {
85
- let [calls, pageSize] = this.makeCalls(args)
86
- if (calls.length === 0) return []
87
-
88
- const pages = Array.from(splitArray(pageSize, calls))
89
- const results = await Promise.all(
90
- pages.map(async (page) => {
91
- const response = await this.eth_call(tryAggregate, {
92
- requireSuccess: false,
93
- calls: page,
94
- })
95
- return response.map((res, i) => {
96
- if (res.success) {
97
- try {
98
- return {
99
- success: true,
100
- value: page[i].func.decodeResult(res.returnData)
101
- }
102
- } catch (err: any) {
103
- return {success: false, returnData: res.returnData}
81
+ tryAggregate<TF extends AnyFunc>(
82
+ func: TF,
83
+ address: string,
84
+ calls: FunctionArguments<TF>[],
85
+ pageSize?: number,
86
+ ): Promise<MulticallResult<TF>[]>
87
+
88
+ tryAggregate<TF extends AnyFunc>(
89
+ func: TF,
90
+ calls: (readonly [address: string, args: FunctionArguments<TF>])[],
91
+ pageSize?: number,
92
+ ): Promise<MulticallResult<TF>[]>
93
+
94
+ tryAggregate(calls: AggregateTuple[], pageSize?: number): Promise<MulticallResult<any>[]>
95
+
96
+ async tryAggregate(...args: any[]): Promise<any[]> {
97
+ let [calls, pageSize] = this.makeCalls(args)
98
+ if (calls.length === 0) return []
99
+
100
+ const pages = Array.from(splitArray(pageSize, calls))
101
+ const results = await Promise.all(
102
+ pages.map(async (page) => {
103
+ const response = await this.eth_call(tryAggregate, {
104
+ requireSuccess: false,
105
+ calls: page,
106
+ })
107
+ return response.map((res, i) => {
108
+ if (res.success) {
109
+ try {
110
+ return {
111
+ success: true,
112
+ value: page[i].func.decodeResult(res.returnData),
113
+ }
114
+ } catch (err: any) {
115
+ return {success: false, returnData: res.returnData}
116
+ }
117
+ } else {
118
+ return {success: false}
119
+ }
120
+ })
121
+ }),
122
+ )
123
+
124
+ return results.flat()
125
+ }
126
+
127
+ private makeCalls(args: any[]): [calls: Call[], page: number] {
128
+ let page = typeof args[args.length - 1] == 'number' ? args.pop()! : Number.MAX_SAFE_INTEGER
129
+ switch (args.length) {
130
+ case 1: {
131
+ let list: AggregateTuple[] = args[0]
132
+ let calls: Call[] = new Array(list.length)
133
+ for (let i = 0; i < list.length; i++) {
134
+ let [func, address, args] = list[i]
135
+ calls[i] = {target: address, callData: func.encode(args), func}
104
136
  }
105
- } else {
106
- return {success: false}
107
- }
108
- })
109
- })
110
- )
111
-
112
- return results.flat()
113
- }
114
-
115
- private makeCalls(args: any[]): [calls: Call[], page: number] {
116
- let page = typeof args[args.length - 1] == 'number' ? args.pop()! : Number.MAX_SAFE_INTEGER
117
- switch (args.length) {
118
- case 1: {
119
- let list: AggregateTuple[] = args[0]
120
- let calls: Call[] = new Array(list.length)
121
- for (let i = 0; i < list.length; i++) {
122
- let [func, address, args] = list[i]
123
- calls[i] = {target: address, callData: func.encode(args), func}
124
- }
125
- return [calls, page]
126
- }
127
- case 2: {
128
- let func: AnyFunc = args[0]
129
- let list: [address: string, args: any][] = args[1]
130
- let calls: Call[] = new Array(list.length)
131
- for (let i = 0; i < list.length; i++) {
132
- let [address, args] = list[i]
133
- calls[i] = {target: address, callData: func.encode(args), func}
134
- }
135
- return [calls, page]
136
- }
137
- case 3: {
138
- let func: AnyFunc = args[0]
139
- let address: string = args[1]
140
- let list: any = args[2]
141
- let calls: Call[] = new Array(list.length)
142
- for (let i = 0; i < list.length; i++) {
143
- let args = list[i]
144
- calls[i] = {target: address, callData: func.encode(args), func}
137
+ return [calls, page]
138
+ }
139
+ case 2: {
140
+ let func: AnyFunc = args[0]
141
+ let list: [address: string, args: any][] = args[1]
142
+ let calls: Call[] = new Array(list.length)
143
+ for (let i = 0; i < list.length; i++) {
144
+ let [address, args] = list[i]
145
+ calls[i] = {target: address, callData: func.encode(args), func}
146
+ }
147
+ return [calls, page]
148
+ }
149
+ case 3: {
150
+ let func: AnyFunc = args[0]
151
+ let address: string = args[1]
152
+ let list: any = args[2]
153
+ let calls: Call[] = new Array(list.length)
154
+ for (let i = 0; i < list.length; i++) {
155
+ let args = list[i]
156
+ calls[i] = {target: address, callData: func.encode(args), func}
157
+ }
158
+ return [calls, page]
159
+ }
160
+ default:
161
+ throw new Error(`Unexpected number of arguments: ${args.length}`)
145
162
  }
146
- return [calls, page]
147
- }
148
- default:
149
- throw new Error(`Unexpected number of arguments: ${args.length}`)
150
163
  }
151
- }
152
164
  }
153
165
 
154
166
  function* splitSlice(maxSize: number, beg: number, end?: number): Iterable<[beg: number, end: number]> {
155
- maxSize = Math.max(1, maxSize)
156
- end = end ?? Number.MAX_SAFE_INTEGER
157
- while (beg < end) {
158
- let left = end - beg
159
- let splits = Math.ceil(left / maxSize)
160
- let step = Math.round(left / splits)
161
- yield [beg, beg + step]
162
- beg += step
163
- }
167
+ maxSize = Math.max(1, maxSize)
168
+ end = end ?? Number.MAX_SAFE_INTEGER
169
+ while (beg < end) {
170
+ let left = end - beg
171
+ let splits = Math.ceil(left / maxSize)
172
+ let step = Math.round(left / splits)
173
+ yield [beg, beg + step]
174
+ beg += step
175
+ }
164
176
  }
165
177
 
166
178
  function* splitArray<T>(maxSize: number, arr: T[]): Iterable<T[]> {
167
- if (arr.length <= maxSize) {
168
- yield arr
169
- } else {
170
- for (let [beg, end] of splitSlice(maxSize, 0, arr.length)) {
171
- yield arr.slice(beg, end)
172
- }
173
- }
174
- }
179
+ if (arr.length <= maxSize) {
180
+ yield arr
181
+ } else {
182
+ for (let [beg, end] of splitSlice(maxSize, 0, arr.length)) {
183
+ yield arr.slice(beg, end)
184
+ }
185
+ }
186
+ }