@gibs/common 1.9.1 → 1.12.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/client.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type Chain, type PublicClient, createPublicClient, fallback, http, webSocket } from "viem"
1
+ import { type Chain, createPublicClient, fallback, http, type PublicClient, webSocket } from 'viem';
2
2
 
3
3
  /** The default batch config for the client */
4
4
  export const defaultBatchConfig = {
@@ -6,7 +6,7 @@ export const defaultBatchConfig = {
6
6
  wait: 10,
7
7
  batchSize: 32,
8
8
  },
9
- }
9
+ };
10
10
 
11
11
  /**
12
12
  * Returns the chain key for the given chain id and urls
@@ -15,14 +15,17 @@ export const defaultBatchConfig = {
15
15
  * @returns the chain key
16
16
  */
17
17
  export const chainKey = (chainId: number, urls: string[]) => {
18
- return `${chainId},${urls.join(',')}`.toLowerCase()
19
- }
18
+ return `${chainId},${urls.join(',')}`.toLowerCase();
19
+ };
20
20
 
21
21
  /** The client cache for the given chain id */
22
- export const clientCache = new Map<number, {
23
- key: string
24
- client: PublicClient
25
- }>()
22
+ export const clientCache = new Map<
23
+ number,
24
+ {
25
+ key: string;
26
+ client: PublicClient;
27
+ }
28
+ >();
26
29
 
27
30
  /**
28
31
  * Returns the client for the given chain and urls
@@ -34,40 +37,40 @@ export const clientFromChain = ({
34
37
  chain,
35
38
  urls,
36
39
  }: {
37
- chain: Chain
38
- urls: string[]
40
+ chain: Chain;
41
+ urls: string[];
39
42
  }): PublicClient => {
40
- const key = chainKey(chain.id, urls)
41
- const existing = clientCache.get(chain.id)
43
+ const key = chainKey(chain.id, urls);
44
+ const existing = clientCache.get(chain.id);
42
45
  if (existing && existing.key === key) {
43
- return existing.client
46
+ return existing.client;
44
47
  }
45
48
  const transport = !urls?.length
46
49
  ? http()
47
50
  : fallback(
48
- urls.map((rpc) =>
49
- rpc.startsWith('http')
50
- ? http(rpc, {
51
- ...defaultBatchConfig,
52
- })
53
- : webSocket(rpc, {
54
- keepAlive: true,
55
- reconnect: true,
56
- retryDelay: 250,
57
- retryCount: 10,
58
- timeout: 4_000,
59
- ...defaultBatchConfig,
60
- }),
61
- ),
62
- { rank: true },
63
- )
51
+ urls.map((rpc) =>
52
+ rpc.startsWith('http')
53
+ ? http(rpc, {
54
+ ...defaultBatchConfig,
55
+ })
56
+ : webSocket(rpc, {
57
+ keepAlive: true,
58
+ reconnect: true,
59
+ retryDelay: 250,
60
+ retryCount: 10,
61
+ timeout: 4_000,
62
+ ...defaultBatchConfig,
63
+ }),
64
+ ),
65
+ { rank: true },
66
+ );
64
67
  const client = createPublicClient({
65
68
  chain,
66
69
  transport,
67
- })
70
+ });
68
71
  clientCache.set(chain.id, {
69
72
  key,
70
73
  client,
71
- })
72
- return client
73
- }
74
+ });
75
+ return client;
76
+ };
@@ -0,0 +1,140 @@
1
+ import type { Chain, Hex, PublicClient } from 'viem';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import { erc20MetadataCalls, multicallErc20 } from './erc20';
4
+
5
+ // Mock the multicall module
6
+ vi.mock('./multicall', () => ({
7
+ multicallRead: vi.fn(),
8
+ }));
9
+
10
+ describe('erc20', () => {
11
+ describe('erc20MetadataCalls', () => {
12
+ it('should have 3 metadata calls', () => {
13
+ expect(erc20MetadataCalls).toHaveLength(3);
14
+ });
15
+
16
+ it('should include name call', () => {
17
+ const nameCall = erc20MetadataCalls.find((c) => c.functionName === 'name');
18
+ expect(nameCall).toBeDefined();
19
+ expect(nameCall?.functionName).toBe('name');
20
+ });
21
+
22
+ it('should include symbol call', () => {
23
+ const symbolCall = erc20MetadataCalls.find((c) => c.functionName === 'symbol');
24
+ expect(symbolCall).toBeDefined();
25
+ expect(symbolCall?.functionName).toBe('symbol');
26
+ });
27
+
28
+ it('should include decimals call', () => {
29
+ const decimalsCall = erc20MetadataCalls.find((c) => c.functionName === 'decimals');
30
+ expect(decimalsCall).toBeDefined();
31
+ expect(decimalsCall?.functionName).toBe('decimals');
32
+ });
33
+
34
+ it('should have calls in correct order (name, symbol, decimals)', () => {
35
+ expect(erc20MetadataCalls[0].functionName).toBe('name');
36
+ expect(erc20MetadataCalls[1].functionName).toBe('symbol');
37
+ expect(erc20MetadataCalls[2].functionName).toBe('decimals');
38
+ });
39
+
40
+ it('should have only functionName property in each call', () => {
41
+ erc20MetadataCalls.forEach((call) => {
42
+ const keys = Object.keys(call);
43
+ expect(keys).toHaveLength(1);
44
+ expect(keys[0]).toBe('functionName');
45
+ });
46
+ });
47
+ });
48
+
49
+ describe('multicallErc20', () => {
50
+ let mockClient: PublicClient;
51
+ let mockChain: Chain;
52
+
53
+ beforeEach(() => {
54
+ vi.clearAllMocks();
55
+ // Clear memoization cache by deleting and recreating it
56
+ (multicallErc20 as any).cache = new Map();
57
+
58
+ mockChain = {
59
+ id: 1,
60
+ name: 'Ethereum',
61
+ nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
62
+ rpcUrls: {
63
+ default: { http: ['https://eth.llamarpc.com'] },
64
+ },
65
+ } as Chain;
66
+
67
+ mockClient = {
68
+ chain: mockChain,
69
+ } as PublicClient;
70
+ });
71
+
72
+ it('should successfully fetch erc20 metadata', async () => {
73
+ const { multicallRead } = await import('./multicall');
74
+ (multicallRead as any).mockResolvedValue(['Test Token', 'TST', 18]);
75
+
76
+ const result = await multicallErc20({
77
+ client: mockClient,
78
+ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex,
79
+ chain: mockChain,
80
+ });
81
+
82
+ expect(result).toEqual(['Test Token', 'TST', 18]);
83
+ expect(multicallRead).toHaveBeenCalledWith({
84
+ chain: mockChain,
85
+ client: mockClient,
86
+ abi: expect.anything(),
87
+ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
88
+ calls: erc20MetadataCalls,
89
+ });
90
+ });
91
+
92
+ it('should fallback to bytes32 ABI on failure', async () => {
93
+ const { multicallRead } = await import('./multicall');
94
+ const mockError = new Error('Failed with standard ABI');
95
+ (multicallRead as any)
96
+ .mockRejectedValueOnce(mockError)
97
+ .mockResolvedValueOnce(['Test Token', 'TST', 18]);
98
+
99
+ const result = await multicallErc20({
100
+ client: mockClient,
101
+ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex,
102
+ chain: mockChain,
103
+ });
104
+
105
+ expect(result).toEqual(['Test Token', 'TST', 18]);
106
+ expect(multicallRead).toHaveBeenCalledTimes(2);
107
+ // First call with standard ABI
108
+ expect((multicallRead as any).mock.calls[0][0].abi).toBeDefined();
109
+ // Second call with bytes32 ABI (fallback)
110
+ expect((multicallRead as any).mock.calls[1][0].abi).toBeDefined();
111
+ });
112
+
113
+ it('should use memoization with target address', async () => {
114
+ const { multicallRead } = await import('./multicall');
115
+ (multicallRead as any).mockResolvedValue(['Test Token', 'TST', 18]);
116
+
117
+ const target = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex;
118
+
119
+ // First call
120
+ const result1 = await multicallErc20({ client: mockClient, target, chain: mockChain });
121
+ // Second call with same params - should use memoized result
122
+ const result2 = await multicallErc20({ client: mockClient, target, chain: mockChain });
123
+
124
+ expect(result1).toEqual(result2);
125
+ // Should only call multicallRead once due to memoization
126
+ expect(multicallRead).toHaveBeenCalledTimes(1);
127
+ });
128
+
129
+ it('should use chain id as memoization key when target is falsy', async () => {
130
+ const { multicallRead } = await import('./multicall');
131
+ (multicallRead as any).mockResolvedValue(['Test Token', 'TST', 18]);
132
+
133
+ // Call with null target (uses chain.id as key)
134
+ await multicallErc20({ client: mockClient, target: null as any, chain: mockChain });
135
+
136
+ // The memoization resolver should handle null target gracefully
137
+ expect(multicallRead).toHaveBeenCalled();
138
+ });
139
+ });
140
+ });
package/src/erc20.ts CHANGED
@@ -1,22 +1,22 @@
1
+ import _ from 'lodash';
1
2
  import {
2
- type Chain,
3
- type PublicClient,
4
- type Hex,
5
3
  type Abi,
4
+ type Chain,
6
5
  erc20Abi,
7
6
  erc20Abi_bytes32,
8
7
  getAddress,
9
- } from 'viem'
10
- import _ from 'lodash'
11
- import type { Erc20Metadata } from './types.js'
12
- import { multicallRead } from './multicall.js'
8
+ type Hex,
9
+ type PublicClient,
10
+ } from 'viem';
11
+ import { multicallRead } from './multicall.js';
12
+ import type { Erc20Metadata } from './types.js';
13
13
 
14
14
  /** The erc20 metadata calls */
15
15
  export const erc20MetadataCalls = [
16
16
  { functionName: 'name' },
17
17
  { functionName: 'symbol' },
18
18
  { functionName: 'decimals' },
19
- ]
19
+ ];
20
20
 
21
21
  /** The multicall for the erc20 metadata */
22
22
  export const multicallErc20 = _.memoize(
@@ -27,17 +27,17 @@ export const multicallErc20 = _.memoize(
27
27
  abi: erc20Abi,
28
28
  target,
29
29
  calls: erc20MetadataCalls,
30
- }
30
+ };
31
31
  try {
32
- return await multicallRead<Erc20Metadata>(options)
32
+ return await multicallRead<Erc20Metadata>(options);
33
33
  } catch {
34
34
  return await multicallRead<Erc20Metadata>({
35
35
  ...options,
36
36
  abi: erc20Abi_bytes32,
37
- })
37
+ });
38
38
  }
39
39
  },
40
40
  ({ target, chain }) => {
41
- return !target ? chain.id : getAddress(target, chain.id)
41
+ return !target ? chain.id : getAddress(target, chain.id);
42
42
  },
43
- )
43
+ );
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
- export * from './types.js'
2
- export * from './serialize.js'
3
- export * from './cache.js'
4
- export * from './multicall.js'
5
- export * from './erc20.js'
6
- export * from './client.js'
1
+ export * from './assert.js';
2
+ export * from './cache.js';
3
+ export * from './client.js';
4
+ export * from './erc20.js';
5
+ export * from './multicall.js';
6
+ export * from './serialize.js';
7
+ export * from './types.js';
@@ -0,0 +1,263 @@
1
+ import type { Chain, Hex, PublicClient } from 'viem';
2
+ import { erc20Abi } from 'viem';
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import { multicallRead } from './multicall';
5
+
6
+ // Mock viem functions
7
+ vi.mock('viem', async () => {
8
+ const actual = await vi.importActual<typeof import('viem')>('viem');
9
+ return {
10
+ ...actual,
11
+ getContract: vi.fn(() => ({
12
+ read: {
13
+ aggregate3: vi.fn(),
14
+ },
15
+ })),
16
+ encodeFunctionData: vi.fn(() => '0xENCODED_DATA' as Hex),
17
+ decodeFunctionResult: vi.fn((params: any) => {
18
+ // Return different mock data based on function name
19
+ if (params.functionName === 'name') return 'Test Token';
20
+ if (params.functionName === 'symbol') return 'TST';
21
+ if (params.functionName === 'decimals') return 18;
22
+ if (params.functionName === 'balanceOf') return 1000000000000000000n;
23
+ return 'MOCK_RESULT';
24
+ }),
25
+ };
26
+ });
27
+
28
+ describe('multicall', () => {
29
+ let mockClient: PublicClient;
30
+ let mockChain: Chain;
31
+
32
+ beforeEach(() => {
33
+ mockChain = {
34
+ id: 1,
35
+ name: 'Ethereum',
36
+ nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
37
+ rpcUrls: {
38
+ default: { http: ['https://eth.llamarpc.com'] },
39
+ },
40
+ contracts: {
41
+ multicall3: {
42
+ address: '0xcA11bde05977b3631167028862bE2a173976CA11',
43
+ },
44
+ },
45
+ } as Chain;
46
+
47
+ mockClient = {
48
+ chain: mockChain,
49
+ } as PublicClient;
50
+ });
51
+
52
+ describe('multicallRead', () => {
53
+ it('should successfully read multiple calls', async () => {
54
+ const mockAggregate3 = vi.fn().mockResolvedValue([
55
+ { success: true, returnData: '0xRESULT1' as Hex },
56
+ { success: true, returnData: '0xRESULT2' as Hex },
57
+ { success: true, returnData: '0xRESULT3' as Hex },
58
+ ]);
59
+
60
+ const { getContract } = await import('viem');
61
+ (getContract as any).mockReturnValue({
62
+ read: {
63
+ aggregate3: mockAggregate3,
64
+ },
65
+ });
66
+
67
+ const result = await multicallRead({
68
+ chain: mockChain,
69
+ client: mockClient,
70
+ abi: erc20Abi,
71
+ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex,
72
+ calls: [{ functionName: 'name' }, { functionName: 'symbol' }, { functionName: 'decimals' }],
73
+ });
74
+
75
+ expect(result).toBeDefined();
76
+ expect(Array.isArray(result)).toBe(true);
77
+ expect(result).toHaveLength(3);
78
+ });
79
+
80
+ it('should handle calls with arguments', async () => {
81
+ const mockAggregate3 = vi
82
+ .fn()
83
+ .mockResolvedValue([{ success: true, returnData: '0xRESULT1' as Hex }]);
84
+
85
+ const { getContract } = await import('viem');
86
+ (getContract as any).mockReturnValue({
87
+ read: {
88
+ aggregate3: mockAggregate3,
89
+ },
90
+ });
91
+
92
+ const result = await multicallRead({
93
+ chain: mockChain,
94
+ client: mockClient,
95
+ abi: erc20Abi,
96
+ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex,
97
+ calls: [
98
+ {
99
+ functionName: 'balanceOf',
100
+ args: ['0x1111111111111111111111111111111111111111' as Hex],
101
+ },
102
+ ],
103
+ });
104
+
105
+ expect(result).toBeDefined();
106
+ expect(Array.isArray(result)).toBe(true);
107
+ });
108
+
109
+ it('should handle calls with allowFailure and failure', async () => {
110
+ const mockAggregate3 = vi.fn().mockResolvedValue([
111
+ { success: false, returnData: '0xERROR' as Hex },
112
+ { success: true, returnData: '0xRESULT2' as Hex },
113
+ ]);
114
+
115
+ const { getContract } = await import('viem');
116
+ (getContract as any).mockReturnValue({
117
+ read: {
118
+ aggregate3: mockAggregate3,
119
+ },
120
+ });
121
+
122
+ const result = await multicallRead({
123
+ chain: mockChain,
124
+ client: mockClient,
125
+ abi: erc20Abi,
126
+ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex,
127
+ calls: [
128
+ { functionName: 'name', allowFailure: true },
129
+ { functionName: 'symbol', allowFailure: false },
130
+ ],
131
+ });
132
+
133
+ expect(result).toBeDefined();
134
+ expect(result[0]).toBe('0xERROR'); // Failed call returns returnData
135
+ expect(result[1]).toBe('TST'); // Successful call returns decoded result (symbol mock)
136
+ });
137
+
138
+ it('should handle calls with allowFailure and success', async () => {
139
+ const mockAggregate3 = vi.fn().mockResolvedValue([
140
+ { success: true, returnData: '0xRESULT1' as Hex },
141
+ { success: true, returnData: '0xRESULT2' as Hex },
142
+ ]);
143
+
144
+ const { getContract } = await import('viem');
145
+ (getContract as any).mockReturnValue({
146
+ read: {
147
+ aggregate3: mockAggregate3,
148
+ },
149
+ });
150
+
151
+ const result = await multicallRead({
152
+ chain: mockChain,
153
+ client: mockClient,
154
+ abi: erc20Abi,
155
+ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex,
156
+ calls: [
157
+ { functionName: 'name', allowFailure: true }, // success=true, allowFailure=true -> decode
158
+ { functionName: 'symbol', allowFailure: false },
159
+ ],
160
+ });
161
+
162
+ expect(result).toBeDefined();
163
+ expect(result[0]).toBe('Test Token'); // Successful call with allowFailure=true returns decoded result
164
+ expect(result[1]).toBe('TST'); // Successful call returns decoded result
165
+ });
166
+
167
+ it('should handle calls with custom target addresses', async () => {
168
+ const mockAggregate3 = vi.fn().mockResolvedValue([
169
+ { success: true, returnData: '0xRESULT1' as Hex },
170
+ { success: true, returnData: '0xRESULT2' as Hex },
171
+ ]);
172
+
173
+ const { getContract } = await import('viem');
174
+ (getContract as any).mockReturnValue({
175
+ read: {
176
+ aggregate3: mockAggregate3,
177
+ },
178
+ });
179
+
180
+ const result = await multicallRead({
181
+ chain: mockChain,
182
+ client: mockClient,
183
+ abi: erc20Abi,
184
+ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex,
185
+ calls: [
186
+ {
187
+ functionName: 'name',
188
+ target: '0x2222222222222222222222222222222222222222' as Hex,
189
+ },
190
+ { functionName: 'symbol' }, // Uses default target
191
+ ],
192
+ });
193
+
194
+ expect(result).toBeDefined();
195
+ expect(Array.isArray(result)).toBe(true);
196
+ });
197
+
198
+ it('should handle calls with custom ABIs', async () => {
199
+ const customAbi = [
200
+ {
201
+ name: 'customFunction',
202
+ type: 'function',
203
+ inputs: [],
204
+ outputs: [{ type: 'string' }],
205
+ stateMutability: 'view',
206
+ },
207
+ ] as const;
208
+
209
+ const mockAggregate3 = vi
210
+ .fn()
211
+ .mockResolvedValue([{ success: true, returnData: '0xRESULT1' as Hex }]);
212
+
213
+ const { getContract } = await import('viem');
214
+ (getContract as any).mockReturnValue({
215
+ read: {
216
+ aggregate3: mockAggregate3,
217
+ },
218
+ });
219
+
220
+ const result = await multicallRead({
221
+ chain: mockChain,
222
+ client: mockClient,
223
+ abi: erc20Abi,
224
+ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex,
225
+ calls: [
226
+ {
227
+ functionName: 'customFunction',
228
+ abi: customAbi as any,
229
+ },
230
+ ],
231
+ });
232
+
233
+ expect(result).toBeDefined();
234
+ });
235
+
236
+ it('should throw error and log when aggregate3 fails', async () => {
237
+ const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
238
+ const mockError = new Error('Multicall failed');
239
+
240
+ const mockAggregate3 = vi.fn().mockRejectedValue(mockError);
241
+
242
+ const { getContract } = await import('viem');
243
+ (getContract as any).mockReturnValue({
244
+ read: {
245
+ aggregate3: mockAggregate3,
246
+ },
247
+ });
248
+
249
+ await expect(
250
+ multicallRead({
251
+ chain: mockChain,
252
+ client: mockClient,
253
+ abi: erc20Abi,
254
+ target: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Hex,
255
+ calls: [{ functionName: 'name' }],
256
+ }),
257
+ ).rejects.toThrow('Multicall failed');
258
+
259
+ expect(consoleErrorSpy).toHaveBeenCalled();
260
+ consoleErrorSpy.mockRestore();
261
+ });
262
+ });
263
+ });
package/src/multicall.ts CHANGED
@@ -1,15 +1,16 @@
1
+ import _ from 'lodash';
1
2
  import {
2
- type Chain,
3
- type PublicClient,
4
- type Hex,
5
3
  type Abi,
6
- getContract,
7
- encodeFunctionData,
4
+ type Chain,
8
5
  decodeFunctionResult,
6
+ encodeFunctionData,
7
+ getContract,
8
+ type Hex,
9
9
  multicall3Abi,
10
- } from 'viem'
11
- import _ from 'lodash'
12
- import type { Call } from './types.js'
10
+ type PublicClient,
11
+ } from 'viem';
12
+ import { ensureResult } from './assert.js';
13
+ import type { Call } from './types.js';
13
14
 
14
15
  /** The multicall for the given chain, client, abi, calls, and target */
15
16
  export const multicallRead = async <T>({
@@ -19,18 +20,18 @@ export const multicallRead = async <T>({
19
20
  calls,
20
21
  target,
21
22
  }: {
22
- chain: Chain
23
- client: PublicClient
24
- abi: Abi
25
- calls: Call[]
26
- target?: Hex
23
+ chain: Chain;
24
+ client: PublicClient;
25
+ abi: Abi;
26
+ calls: Call[];
27
+ target?: Hex;
27
28
  }) => {
28
29
  const multicall = getContract({
29
30
  abi: multicall3Abi,
30
31
  address: chain.contracts!.multicall3!.address!,
31
32
  // this seems like a bug in viem, it should be able to infer the type of the client
32
33
  client: client as any,
33
- }) as any
34
+ }) as any;
34
35
  const arg = calls.map((call) => ({
35
36
  callData: encodeFunctionData({
36
37
  abi: call.abi || abi,
@@ -40,29 +41,28 @@ export const multicallRead = async <T>({
40
41
  }),
41
42
  allowFailure: call.allowFailure || false,
42
43
  target: (call.target || target) as Hex,
43
- }))
44
- let reads: null | Awaited<ReturnType<typeof multicall.read.aggregate3>> = null
44
+ }));
45
+ let reads: null | Awaited<ReturnType<typeof multicall.read.aggregate3>> = null;
45
46
  try {
46
- reads = await multicall.read.aggregate3([arg])
47
- if (!reads) throw reads
48
- const r = reads
47
+ reads = await multicall.read.aggregate3([arg]);
48
+ const r = ensureResult(reads, 'Multicall aggregate3');
49
49
  return calls.map((call, i) =>
50
50
  call.allowFailure
51
51
  ? r[i].success
52
52
  ? decodeFunctionResult({
53
+ abi: call.abi || abi,
54
+ functionName: call.functionName,
55
+ data: r[i].returnData,
56
+ })
57
+ : r[i].returnData
58
+ : decodeFunctionResult({
53
59
  abi: call.abi || abi,
54
60
  functionName: call.functionName,
55
61
  data: r[i].returnData,
56
- })
57
- : r[i].returnData
58
- : decodeFunctionResult({
59
- abi: call.abi || abi,
60
- functionName: call.functionName,
61
- data: r[i].returnData,
62
- }),
63
- ) as T
62
+ }),
63
+ ) as T;
64
64
  } catch (err) {
65
- console.error(client.chain?.id, target, calls, reads)
66
- throw err
65
+ console.error(client.chain?.id, target, calls, reads);
66
+ throw err;
67
67
  }
68
- }
68
+ };