@cofhe/abi 0.2.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/utils.ts ADDED
@@ -0,0 +1,205 @@
1
+ import type {
2
+ Abi,
3
+ AbiConstructor,
4
+ AbiError,
5
+ AbiEvent,
6
+ AbiFallback,
7
+ AbiFunction,
8
+ AbiParameter,
9
+ AbiParametersToPrimitiveTypes,
10
+ AbiReceive,
11
+ AbiType,
12
+ Address,
13
+ ExtractAbiFunction,
14
+ ResolvedRegister,
15
+ SolidityArray,
16
+ SolidityFixedArrayRange,
17
+ SolidityTuple,
18
+ } from 'abitype';
19
+
20
+ export type ContractReturnType<
21
+ abi extends Abi | readonly unknown[] = Abi,
22
+ functionName extends string = string,
23
+ args extends readonly unknown[] | undefined = readonly unknown[] | undefined,
24
+ ///
25
+ abiFunction extends AbiFunction = (
26
+ abi extends Abi ? ExtractAbiFunction<abi, functionName> : AbiFunction
27
+ ) extends infer abiFunction_ extends AbiFunction
28
+ ? IsUnion<abiFunction_> extends true // narrow overloads by `args` by converting to tuple and filtering out overloads that don't match
29
+ ? UnionToTuple<abiFunction_> extends infer abiFunctions extends readonly AbiFunction[]
30
+ ? {
31
+ [K in keyof abiFunctions]: (
32
+ readonly unknown[] | undefined extends args // for functions that don't have inputs, `args` can be `undefined` so fallback to `readonly []`
33
+ ? readonly []
34
+ : args
35
+ ) extends AbiParametersToPrimitiveTypes<abiFunctions[K]['inputs'], 'inputs'>
36
+ ? abiFunctions[K]
37
+ : never;
38
+ }[number] // convert back to union (removes `never` tuple entries: `['foo', never, 'bar'][number]` => `'foo' | 'bar'`)
39
+ : never
40
+ : abiFunction_
41
+ : never,
42
+ outputs extends readonly AbiParameter[] = abiFunction['outputs'],
43
+ primitiveTypes extends readonly unknown[] = AbiParametersToPrimitiveTypes<outputs, 'outputs', true>,
44
+ > = [abiFunction] extends [never]
45
+ ? unknown // `abiFunction` was not inferrable (e.g. `abi` declared as `Abi`)
46
+ : readonly unknown[] extends primitiveTypes
47
+ ? unknown // `abiFunction` was not inferrable (e.g. `abi` not const-asserted)
48
+ : primitiveTypes extends readonly [] // unwrap `primitiveTypes`
49
+ ? // biome-ignore lint/suspicious/noConfusingVoidType: <explanation>
50
+ void // no outputs
51
+ : primitiveTypes extends readonly [infer primitiveType]
52
+ ? primitiveType // single output
53
+ : primitiveTypes;
54
+
55
+ export type Error<messages extends string | string[]> = messages extends string
56
+ ? [
57
+ // Surrounding with array to prevent `messages` from being widened to `string`
58
+ `Error: ${messages}`,
59
+ ]
60
+ : {
61
+ [key in keyof messages]: messages[key] extends infer message extends string ? `Error: ${message}` : never;
62
+ };
63
+
64
+ type PartialBy<TType, TKeys extends keyof TType> = ExactPartial<Pick<TType, TKeys>> & Omit<TType, TKeys>;
65
+ type ExactPartial<T> = { [K in keyof T]?: T[K] | undefined };
66
+
67
+ export type MaybePartialBy<TType, TKeys extends string> = TKeys extends keyof TType ? PartialBy<TType, TKeys> : TType;
68
+
69
+ export type ReadonlyWiden<TType> =
70
+ | (TType extends Function ? TType : never)
71
+ | (TType extends ResolvedRegister['bigIntType'] ? bigint : never)
72
+ | (TType extends boolean ? boolean : never)
73
+ | (TType extends ResolvedRegister['intType'] ? number : never)
74
+ | (TType extends string
75
+ ? TType extends Address
76
+ ? Address
77
+ : TType extends ResolvedRegister['bytesType']['inputs']
78
+ ? ResolvedRegister['bytesType']
79
+ : string
80
+ : never)
81
+ | (TType extends readonly [] ? readonly [] : never)
82
+ | (TType extends Record<string, unknown> ? { [K in keyof TType]: ReadonlyWiden<TType[K]> } : never)
83
+ | (TType extends { length: number }
84
+ ? {
85
+ [K in keyof TType]: ReadonlyWiden<TType[K]>;
86
+ } extends infer Val extends unknown[]
87
+ ? readonly [...Val]
88
+ : never
89
+ : never);
90
+
91
+ export type AbiBasicType = Exclude<AbiType, SolidityTuple | SolidityArray>;
92
+
93
+ /**
94
+ * First, infer `Head` against a known size type (either fixed-length array value or `""`).
95
+ *
96
+ * | Input | Head |
97
+ * | --------------- | ------------ |
98
+ * | `string[]` | `string` |
99
+ * | `string[][][3]` | `string[][]` |
100
+ */
101
+ export type MaybeExtractArrayParameterType<type> = type extends `${infer head}[${'' | `${SolidityFixedArrayRange}`}]`
102
+ ? // * Then, infer in the opposite direction, using the known `head` to infer the exact `size` value.
103
+ // *
104
+ // * | Input | Size |
105
+ // * | ------------ | ---- |
106
+ // * | `${head}[]` | `""` |
107
+ // * | `${head}[3]` | `3` |
108
+ // */
109
+ type extends `${head}[${infer size}]`
110
+ ? [head, size]
111
+ : undefined
112
+ : undefined;
113
+
114
+ export type IsUnion<T, C = T> = T extends C ? ([C] extends [T] ? false : true) : never;
115
+ export type UnionToTuple<U, Last = LastInUnion<U>> = [U] extends [never]
116
+ ? []
117
+ : [...UnionToTuple<Exclude<U, Last>>, Last];
118
+ type LastInUnion<U> =
119
+ UnionToIntersection<U extends unknown ? (x: U) => 0 : never> extends (x: infer L) => 0 ? L : never;
120
+ type UnionToIntersection<U> = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never;
121
+
122
+ /**
123
+ * Create tuple of {@link type} type with {@link size} size
124
+ *
125
+ * @param Type - Type of tuple
126
+ * @param Size - Size of tuple
127
+ * @returns Tuple of {@link type} type with {@link size} size
128
+ *
129
+ * @example
130
+ * type Result = Tuple<string, 2>
131
+ * // ^? type Result = [string, string]
132
+ */
133
+ // https://github.com/Microsoft/TypeScript/issues/26223#issuecomment-674500430
134
+ export type Tuple<type, size extends number> = size extends size
135
+ ? number extends size
136
+ ? type[]
137
+ : _TupleOf<type, size, []>
138
+ : never;
139
+ type _TupleOf<length, size extends number, acc extends readonly unknown[]> = acc['length'] extends size
140
+ ? acc
141
+ : _TupleOf<length, size, readonly [length, ...acc]>;
142
+
143
+ /**
144
+ * Merges two object types into new type
145
+ *
146
+ * @param object1 - Object to merge into
147
+ * @param object2 - Object to merge and override keys from {@link object1}
148
+ * @returns New object type with keys from {@link object1} and {@link object2}. If a key exists in both {@link object1} and {@link object2}, the key from {@link object2} will be used.
149
+ *
150
+ * @example
151
+ * type Result = Merge<{ foo: string }, { foo: number; bar: string }>
152
+ * // ^? type Result = { foo: number; bar: string }
153
+ */
154
+ export type Merge<object1, object2> = Omit<object1, keyof object2> & object2;
155
+
156
+ // FUNCTIONS
157
+
158
+ /**
159
+ * Extracts array parameter type information from a type string.
160
+ * Always returns a tuple, with undefined for non-array types.
161
+ *
162
+ * @param type - Type string like "tuple", "tuple[]", or "tuple[2]"
163
+ * @returns [head, size] where:
164
+ * - head is the base type
165
+ * - size is undefined for non-arrays, "" for dynamic arrays, or the number as a string for fixed arrays
166
+ *
167
+ * @example
168
+ * extractArrayParameterType("tuple") // ["tuple", undefined]
169
+ * extractArrayParameterType("tuple[]") // ["tuple", ""]
170
+ * extractArrayParameterType("tuple[2]") // ["tuple", "2"]
171
+ */
172
+ export function extractArrayParameterType<T extends string | undefined>(type: T): [T, string | undefined] {
173
+ if (type == null) return [type, undefined];
174
+
175
+ const match = type.match(/^(.+)\[(\d*)\]$/);
176
+
177
+ if (!match) {
178
+ // Not an array type, return [type, undefined]
179
+ return [type, undefined];
180
+ }
181
+
182
+ const head = match[1];
183
+ const size = match[2]; // Empty string for dynamic arrays, or digits for fixed arrays
184
+
185
+ // Return empty string for dynamic arrays, or the size string for fixed arrays
186
+ return [head as T, size === '' ? '' : size];
187
+ }
188
+
189
+ type AbiItem = AbiConstructor | AbiError | AbiEvent | AbiFallback | AbiFunction | AbiReceive;
190
+
191
+ export function isAbiFunction(item: AbiItem | unknown): item is AbiFunction {
192
+ if (typeof item !== 'object' || item === null) return false;
193
+ return 'type' in item && item.type === 'function' && 'name' in item && 'outputs' in item;
194
+ }
195
+
196
+ export function getAbiFunction<TAbi extends Abi | readonly unknown[], TFunctionName extends string>(
197
+ abi: TAbi,
198
+ functionName: TFunctionName
199
+ ): AbiFunction | undefined {
200
+ return abi.find((item) => {
201
+ const isFunction = isAbiFunction(item);
202
+ if (!isFunction) return false;
203
+ return item.name === functionName;
204
+ }) as AbiFunction | undefined;
205
+ }