@gibs/common 1.10.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.
@@ -0,0 +1,128 @@
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 CHANGED
@@ -1,31 +1,30 @@
1
-
2
- const BIGINT_TYPE = 'bigint' as const
1
+ const BIGINT_TYPE = 'bigint' as const;
3
2
  type SerializedBigInt = {
4
- __type__: typeof BIGINT_TYPE
5
- value: string
6
- }
3
+ __type__: typeof BIGINT_TYPE;
4
+ value: string;
5
+ };
7
6
  /** The json any stringify for the given key and value */
8
7
  export const jsonAnyStringify = (key: string, value: unknown) => {
9
8
  if (typeof value === BIGINT_TYPE) {
10
9
  return {
11
10
  __type__: BIGINT_TYPE,
12
11
  value: (value as bigint).toString(),
13
- }
12
+ };
14
13
  }
15
- return value
16
- }
14
+ return value;
15
+ };
17
16
 
18
17
  /** The is serialized big int for the given value */
19
18
  export const isSerializedBigInt = (value: unknown): value is SerializedBigInt => {
20
19
  return (
21
20
  !!value && typeof value === 'object' && (value as SerializedBigInt).__type__ === BIGINT_TYPE
22
- )
23
- }
21
+ );
22
+ };
24
23
 
25
24
  /** The json any parse for the given key and value */
26
25
  export const jsonAnyParse = (_key: string, value: unknown) => {
27
26
  if (isSerializedBigInt(value)) {
28
- return BigInt(value.value)
27
+ return BigInt(value.value);
29
28
  }
30
- return value
31
- }
29
+ return value;
30
+ };
package/src/types.ts CHANGED
@@ -1,13 +1,13 @@
1
- import type { Abi, Hex } from "viem"
1
+ import type { Abi, Hex } from 'viem';
2
2
 
3
3
  /** The inputs for a multicall read */
4
4
  export type Call = {
5
- allowFailure?: boolean
6
- functionName: string
7
- target?: Hex
8
- abi?: Abi
9
- args?: (bigint | Hex | Hex[] | bigint[] | string | string[])[]
10
- }
5
+ allowFailure?: boolean;
6
+ functionName: string;
7
+ target?: Hex;
8
+ abi?: Abi;
9
+ args?: (bigint | Hex | Hex[] | bigint[] | string | string[])[];
10
+ };
11
11
 
12
12
  /** The erc20 metadata */
13
- export type Erc20Metadata = [string, string, number]
13
+ export type Erc20Metadata = [string, string, number];
package/tsconfig.json CHANGED
@@ -12,5 +12,6 @@
12
12
  "moduleDetection": "force",
13
13
  "moduleResolution": "nodenext"
14
14
  },
15
- "include": ["src/**/*.ts", "src/**/*.js"]
15
+ "include": ["src/**/*.ts", "src/**/*.js"],
16
+ "exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"]
16
17
  }
@@ -0,0 +1,13 @@
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
+ });
@@ -1,14 +0,0 @@
1
- /**
2
- * Memoize a function result and return a single value
3
- * @param fn the function to memoize
4
- * @param max the maximum number of values to cache
5
- * @returns the memoized function
6
- */
7
- export declare const maxMemoize: <A extends unknown[], B>(fn: (...a: A) => B, max?: number) => (...a: A) => B | undefined;
8
- /**
9
- * Memoize a function result and return a single value
10
- * @param fn the function to memoize
11
- * @param ttl the time to live for the cache
12
- * @returns the memoized function
13
- */
14
- export declare const ttlMemoizeSingle: <A extends unknown[], B>(fn: (...a: A) => B, ttl?: number) => (...a: A) => B;
package/dist/src/cache.js DELETED
@@ -1,44 +0,0 @@
1
- import { jsonAnyStringify } from "./serialize.js";
2
- /**
3
- * Memoize a function result and return a single value
4
- * @param fn the function to memoize
5
- * @param max the maximum number of values to cache
6
- * @returns the memoized function
7
- */
8
- export const maxMemoize = (fn, max = 1024) => {
9
- const cache = new Map();
10
- return (...a) => {
11
- const key = JSON.stringify(a, jsonAnyStringify);
12
- const existing = cache.get(key);
13
- if (cache.has(key)) {
14
- return existing;
15
- }
16
- for (const k of cache.keys()) {
17
- if (cache.size < max)
18
- break;
19
- cache.delete(k);
20
- }
21
- const result = fn(...a);
22
- cache.set(key, result);
23
- return result;
24
- };
25
- };
26
- const hour1 = 1000 * 60 * 60;
27
- /**
28
- * Memoize a function result and return a single value
29
- * @param fn the function to memoize
30
- * @param ttl the time to live for the cache
31
- * @returns the memoized function
32
- */
33
- export const ttlMemoizeSingle = (fn, ttl = hour1) => {
34
- let cache = null;
35
- return (...a) => {
36
- const now = Date.now();
37
- if (cache && cache.timestamp + ttl > now) {
38
- return cache.result;
39
- }
40
- const result = fn(...a);
41
- cache = { timestamp: now, result };
42
- return result;
43
- };
44
- };
@@ -1,30 +0,0 @@
1
- import { type Chain, type PublicClient } from "viem";
2
- /** The default batch config for the client */
3
- export declare const defaultBatchConfig: {
4
- batch: {
5
- wait: number;
6
- batchSize: number;
7
- };
8
- };
9
- /**
10
- * Returns the chain key for the given chain id and urls
11
- * @param chainId - the chain id
12
- * @param urls - the urls
13
- * @returns the chain key
14
- */
15
- export declare const chainKey: (chainId: number, urls: string[]) => string;
16
- /** The client cache for the given chain id */
17
- export declare const clientCache: Map<number, {
18
- key: string;
19
- client: PublicClient;
20
- }>;
21
- /**
22
- * Returns the client for the given chain and urls
23
- * @param chain - the chain
24
- * @param urls - the urls
25
- * @returns the client
26
- */
27
- export declare const clientFromChain: ({ chain, urls, }: {
28
- chain: Chain;
29
- urls: string[];
30
- }) => PublicClient;
@@ -1,55 +0,0 @@
1
- import { createPublicClient, fallback, http, webSocket } from "viem";
2
- /** The default batch config for the client */
3
- export const defaultBatchConfig = {
4
- batch: {
5
- wait: 10,
6
- batchSize: 32,
7
- },
8
- };
9
- /**
10
- * Returns the chain key for the given chain id and urls
11
- * @param chainId - the chain id
12
- * @param urls - the urls
13
- * @returns the chain key
14
- */
15
- export const chainKey = (chainId, urls) => {
16
- return `${chainId},${urls.join(',')}`.toLowerCase();
17
- };
18
- /** The client cache for the given chain id */
19
- export const clientCache = new Map();
20
- /**
21
- * Returns the client for the given chain and urls
22
- * @param chain - the chain
23
- * @param urls - the urls
24
- * @returns the client
25
- */
26
- export const clientFromChain = ({ chain, urls, }) => {
27
- const key = chainKey(chain.id, urls);
28
- const existing = clientCache.get(chain.id);
29
- if (existing && existing.key === key) {
30
- return existing.client;
31
- }
32
- const transport = !urls?.length
33
- ? http()
34
- : fallback(urls.map((rpc) => rpc.startsWith('http')
35
- ? http(rpc, {
36
- ...defaultBatchConfig,
37
- })
38
- : webSocket(rpc, {
39
- keepAlive: true,
40
- reconnect: true,
41
- retryDelay: 250,
42
- retryCount: 10,
43
- timeout: 4_000,
44
- ...defaultBatchConfig,
45
- })), { rank: true });
46
- const client = createPublicClient({
47
- chain,
48
- transport,
49
- });
50
- clientCache.set(chain.id, {
51
- key,
52
- client,
53
- });
54
- return client;
55
- };
@@ -1,13 +0,0 @@
1
- import { type Chain, type PublicClient, type Hex } from 'viem';
2
- import _ from 'lodash';
3
- import type { Erc20Metadata } from './types.js';
4
- /** The erc20 metadata calls */
5
- export declare const erc20MetadataCalls: {
6
- functionName: string;
7
- }[];
8
- /** The multicall for the erc20 metadata */
9
- export declare const multicallErc20: (({ client, target, chain }: {
10
- client: PublicClient;
11
- target: Hex;
12
- chain: Chain;
13
- }) => Promise<Erc20Metadata>) & _.MemoizedFunction;
package/dist/src/erc20.js DELETED
@@ -1,30 +0,0 @@
1
- import { erc20Abi, erc20Abi_bytes32, getAddress, } from 'viem';
2
- import _ from 'lodash';
3
- import { multicallRead } from './multicall.js';
4
- /** The erc20 metadata calls */
5
- export const erc20MetadataCalls = [
6
- { functionName: 'name' },
7
- { functionName: 'symbol' },
8
- { functionName: 'decimals' },
9
- ];
10
- /** The multicall for the erc20 metadata */
11
- export const multicallErc20 = _.memoize(async ({ client, target, chain }) => {
12
- const options = {
13
- chain: chain,
14
- client: client,
15
- abi: erc20Abi,
16
- target,
17
- calls: erc20MetadataCalls,
18
- };
19
- try {
20
- return await multicallRead(options);
21
- }
22
- catch {
23
- return await multicallRead({
24
- ...options,
25
- abi: erc20Abi_bytes32,
26
- });
27
- }
28
- }, ({ target, chain }) => {
29
- return !target ? chain.id : getAddress(target, chain.id);
30
- });
@@ -1,6 +0,0 @@
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';
package/dist/src/index.js DELETED
@@ -1,6 +0,0 @@
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,10 +0,0 @@
1
- import { type Chain, type PublicClient, type Hex, type Abi } from 'viem';
2
- import type { Call } from './types.js';
3
- /** The multicall for the given chain, client, abi, calls, and target */
4
- export declare const multicallRead: <T>({ chain, client, abi, calls, target, }: {
5
- chain: Chain;
6
- client: PublicClient;
7
- abi: Abi;
8
- calls: Call[];
9
- target?: Hex;
10
- }) => Promise<T>;
@@ -1,44 +0,0 @@
1
- import { getContract, encodeFunctionData, decodeFunctionResult, multicall3Abi, } from 'viem';
2
- /** The multicall for the given chain, client, abi, calls, and target */
3
- export const multicallRead = async ({ chain, client, abi, calls, target, }) => {
4
- const multicall = getContract({
5
- abi: multicall3Abi,
6
- address: chain.contracts.multicall3.address,
7
- // this seems like a bug in viem, it should be able to infer the type of the client
8
- client: client,
9
- });
10
- const arg = calls.map((call) => ({
11
- callData: encodeFunctionData({
12
- abi: call.abi || abi,
13
- functionName: call.functionName,
14
- // this seems like a bug in viem, it should be able to infer the type of the args
15
- args: (call.args || []),
16
- }),
17
- allowFailure: call.allowFailure || false,
18
- target: (call.target || target),
19
- }));
20
- let reads = null;
21
- try {
22
- reads = await multicall.read.aggregate3([arg]);
23
- if (!reads)
24
- throw reads;
25
- const r = reads;
26
- return calls.map((call, i) => call.allowFailure
27
- ? r[i].success
28
- ? decodeFunctionResult({
29
- abi: call.abi || abi,
30
- functionName: call.functionName,
31
- data: r[i].returnData,
32
- })
33
- : r[i].returnData
34
- : decodeFunctionResult({
35
- abi: call.abi || abi,
36
- functionName: call.functionName,
37
- data: r[i].returnData,
38
- }));
39
- }
40
- catch (err) {
41
- console.error(client.chain?.id, target, calls, reads);
42
- throw err;
43
- }
44
- };
@@ -1,12 +0,0 @@
1
- declare const BIGINT_TYPE: "bigint";
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 declare const jsonAnyStringify: (key: string, value: unknown) => unknown;
8
- /** The is serialized big int for the given value */
9
- export declare const isSerializedBigInt: (value: unknown) => value is SerializedBigInt;
10
- /** The json any parse for the given key and value */
11
- export declare const jsonAnyParse: (_key: string, value: unknown) => unknown;
12
- export {};
@@ -1,22 +0,0 @@
1
- const BIGINT_TYPE = 'bigint';
2
- /** The json any stringify for the given key and value */
3
- export const jsonAnyStringify = (key, value) => {
4
- if (typeof value === BIGINT_TYPE) {
5
- return {
6
- __type__: BIGINT_TYPE,
7
- value: value.toString(),
8
- };
9
- }
10
- return value;
11
- };
12
- /** The is serialized big int for the given value */
13
- export const isSerializedBigInt = (value) => {
14
- return (!!value && typeof value === 'object' && value.__type__ === BIGINT_TYPE);
15
- };
16
- /** The json any parse for the given key and value */
17
- export const jsonAnyParse = (_key, value) => {
18
- if (isSerializedBigInt(value)) {
19
- return BigInt(value.value);
20
- }
21
- return value;
22
- };
@@ -1,11 +0,0 @@
1
- import type { Abi, Hex } from "viem";
2
- /** The inputs for a multicall read */
3
- export type Call = {
4
- allowFailure?: boolean;
5
- functionName: string;
6
- target?: Hex;
7
- abi?: Abi;
8
- args?: (bigint | Hex | Hex[] | bigint[] | string | string[])[];
9
- };
10
- /** The erc20 metadata */
11
- export type Erc20Metadata = [string, string, number];
package/dist/src/types.js DELETED
@@ -1 +0,0 @@
1
- export {};