@gibs/common 1.12.0 → 1.13.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.
@@ -1,263 +0,0 @@
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 DELETED
@@ -1,68 +0,0 @@
1
- import _ from 'lodash';
2
- import {
3
- type Abi,
4
- type Chain,
5
- decodeFunctionResult,
6
- encodeFunctionData,
7
- getContract,
8
- type Hex,
9
- multicall3Abi,
10
- type PublicClient,
11
- } from 'viem';
12
- import { ensureResult } from './assert.js';
13
- import type { Call } from './types.js';
14
-
15
- /** The multicall for the given chain, client, abi, calls, and target */
16
- export const multicallRead = async <T>({
17
- chain,
18
- client,
19
- abi,
20
- calls,
21
- target,
22
- }: {
23
- chain: Chain;
24
- client: PublicClient;
25
- abi: Abi;
26
- calls: Call[];
27
- target?: Hex;
28
- }) => {
29
- const multicall = getContract({
30
- abi: multicall3Abi,
31
- address: chain.contracts!.multicall3!.address!,
32
- // this seems like a bug in viem, it should be able to infer the type of the client
33
- client: client as any,
34
- }) as any;
35
- const arg = calls.map((call) => ({
36
- callData: encodeFunctionData({
37
- abi: call.abi || abi,
38
- functionName: call.functionName,
39
- // this seems like a bug in viem, it should be able to infer the type of the args
40
- args: (call.args || []) as any,
41
- }),
42
- allowFailure: call.allowFailure || false,
43
- target: (call.target || target) as Hex,
44
- }));
45
- let reads: null | Awaited<ReturnType<typeof multicall.read.aggregate3>> = null;
46
- try {
47
- reads = await multicall.read.aggregate3([arg]);
48
- const r = ensureResult(reads, 'Multicall aggregate3');
49
- return calls.map((call, i) =>
50
- call.allowFailure
51
- ? r[i].success
52
- ? decodeFunctionResult({
53
- abi: call.abi || abi,
54
- functionName: call.functionName,
55
- 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;
64
- } catch (err) {
65
- console.error(client.chain?.id, target, calls, reads);
66
- throw err;
67
- }
68
- };
@@ -1,128 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { isSerializedBigInt, jsonAnyParse, jsonAnyStringify } from './serialize';
3
-
4
- describe('serialize', () => {
5
- describe('jsonAnyStringify', () => {
6
- it('should serialize bigint values', () => {
7
- const result = jsonAnyStringify('test', 12345n);
8
- expect(result).toEqual({
9
- __type__: 'bigint',
10
- value: '12345',
11
- });
12
- });
13
-
14
- it('should serialize large bigint values', () => {
15
- const largeBigInt = BigInt('999999999999999999999999999999');
16
- const result = jsonAnyStringify('test', largeBigInt);
17
- expect(result).toEqual({
18
- __type__: 'bigint',
19
- value: '999999999999999999999999999999',
20
- });
21
- });
22
-
23
- it('should pass through non-bigint values unchanged', () => {
24
- expect(jsonAnyStringify('test', 'string')).toBe('string');
25
- expect(jsonAnyStringify('test', 123)).toBe(123);
26
- expect(jsonAnyStringify('test', true)).toBe(true);
27
- expect(jsonAnyStringify('test', null)).toBe(null);
28
- expect(jsonAnyStringify('test', undefined)).toBe(undefined);
29
- });
30
-
31
- it('should handle objects', () => {
32
- const obj = { foo: 'bar' };
33
- expect(jsonAnyStringify('test', obj)).toBe(obj);
34
- });
35
-
36
- it('should handle arrays', () => {
37
- const arr = [1, 2, 3];
38
- expect(jsonAnyStringify('test', arr)).toBe(arr);
39
- });
40
- });
41
-
42
- describe('isSerializedBigInt', () => {
43
- it('should identify serialized bigint objects', () => {
44
- const serialized = {
45
- __type__: 'bigint',
46
- value: '12345',
47
- };
48
- expect(isSerializedBigInt(serialized)).toBe(true);
49
- });
50
-
51
- it('should reject non-serialized bigint objects', () => {
52
- expect(isSerializedBigInt({})).toBe(false);
53
- expect(isSerializedBigInt({ __type__: 'string' })).toBe(false);
54
- expect(isSerializedBigInt({ value: '123' })).toBe(false);
55
- expect(isSerializedBigInt('string')).toBe(false);
56
- expect(isSerializedBigInt(123)).toBe(false);
57
- expect(isSerializedBigInt(null)).toBe(false);
58
- expect(isSerializedBigInt(undefined)).toBe(false);
59
- });
60
- });
61
-
62
- describe('jsonAnyParse', () => {
63
- it('should deserialize bigint values', () => {
64
- const serialized = {
65
- __type__: 'bigint',
66
- value: '12345',
67
- };
68
- const result = jsonAnyParse('test', serialized);
69
- expect(result).toBe(12345n);
70
- expect(typeof result).toBe('bigint');
71
- });
72
-
73
- it('should deserialize large bigint values', () => {
74
- const serialized = {
75
- __type__: 'bigint',
76
- value: '999999999999999999999999999999',
77
- };
78
- const result = jsonAnyParse('test', serialized);
79
- expect(result).toBe(BigInt('999999999999999999999999999999'));
80
- });
81
-
82
- it('should pass through non-serialized-bigint values unchanged', () => {
83
- expect(jsonAnyParse('test', 'string')).toBe('string');
84
- expect(jsonAnyParse('test', 123)).toBe(123);
85
- expect(jsonAnyParse('test', true)).toBe(true);
86
- expect(jsonAnyParse('test', null)).toBe(null);
87
- expect(jsonAnyParse('test', undefined)).toBe(undefined);
88
- });
89
- });
90
-
91
- describe('round-trip serialization', () => {
92
- it('should correctly round-trip bigint values through JSON', () => {
93
- const original = { amount: 12345n, balance: 999999999999999999n };
94
- const serialized = JSON.stringify(original, jsonAnyStringify);
95
- const deserialized = JSON.parse(serialized, jsonAnyParse);
96
-
97
- expect(deserialized.amount).toBe(12345n);
98
- expect(deserialized.balance).toBe(999999999999999999n);
99
- });
100
-
101
- it('should handle nested objects with bigints', () => {
102
- const original = {
103
- user: {
104
- address: '0x123',
105
- balance: 1000000000000000000n,
106
- },
107
- transaction: {
108
- value: 5000000000000000n,
109
- gasLimit: 21000n,
110
- },
111
- };
112
- const serialized = JSON.stringify(original, jsonAnyStringify);
113
- const deserialized = JSON.parse(serialized, jsonAnyParse);
114
-
115
- expect(deserialized.user.balance).toBe(1000000000000000000n);
116
- expect(deserialized.transaction.value).toBe(5000000000000000n);
117
- expect(deserialized.transaction.gasLimit).toBe(21000n);
118
- });
119
-
120
- it('should handle arrays with bigints', () => {
121
- const original = [1n, 2n, 3n, 999999999999999999n];
122
- const serialized = JSON.stringify(original, jsonAnyStringify);
123
- const deserialized = JSON.parse(serialized, jsonAnyParse);
124
-
125
- expect(deserialized).toEqual([1n, 2n, 3n, 999999999999999999n]);
126
- });
127
- });
128
- });
package/src/serialize.ts DELETED
@@ -1,30 +0,0 @@
1
- const BIGINT_TYPE = 'bigint' as const;
2
- type SerializedBigInt = {
3
- __type__: typeof BIGINT_TYPE;
4
- value: string;
5
- };
6
- /** The json any stringify for the given key and value */
7
- export const jsonAnyStringify = (key: string, value: unknown) => {
8
- if (typeof value === BIGINT_TYPE) {
9
- return {
10
- __type__: BIGINT_TYPE,
11
- value: (value as bigint).toString(),
12
- };
13
- }
14
- return value;
15
- };
16
-
17
- /** The is serialized big int for the given value */
18
- export const isSerializedBigInt = (value: unknown): value is SerializedBigInt => {
19
- return (
20
- !!value && typeof value === 'object' && (value as SerializedBigInt).__type__ === BIGINT_TYPE
21
- );
22
- };
23
-
24
- /** The json any parse for the given key and value */
25
- export const jsonAnyParse = (_key: string, value: unknown) => {
26
- if (isSerializedBigInt(value)) {
27
- return BigInt(value.value);
28
- }
29
- return value;
30
- };
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "useDefineForClassFields": true,
5
- "module": "NodeNext",
6
- "composite": true,
7
- "allowJs": true,
8
- "outDir": "./dist",
9
- "strict": true,
10
- "isolatedModules": true,
11
- "skipLibCheck": true,
12
- "moduleDetection": "force",
13
- "moduleResolution": "nodenext"
14
- },
15
- "include": ["src/**/*.ts", "src/**/*.js"],
16
- "exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"]
17
- }
package/vitest.config.ts DELETED
@@ -1,13 +0,0 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- environment: 'node',
7
- coverage: {
8
- provider: 'v8',
9
- reporter: ['text', 'json', 'html'],
10
- exclude: ['node_modules/**', 'dist/**', '**/*.config.ts', '**/*.d.ts'],
11
- },
12
- },
13
- });
File without changes