import type { Abi, AbiConstructor, AbiError, AbiEvent, AbiFallback, AbiFunction, AbiParameter, AbiParametersToPrimitiveTypes, AbiReceive, AbiType, Address, ExtractAbiFunction, ResolvedRegister, SolidityArray, SolidityFixedArrayRange, SolidityTuple, } from 'abitype'; export type ContractReturnType< abi extends Abi | readonly unknown[] = Abi, functionName extends string = string, args extends readonly unknown[] | undefined = readonly unknown[] | undefined, /// abiFunction extends AbiFunction = ( abi extends Abi ? ExtractAbiFunction : AbiFunction ) extends infer abiFunction_ extends AbiFunction ? IsUnion extends true // narrow overloads by `args` by converting to tuple and filtering out overloads that don't match ? UnionToTuple extends infer abiFunctions extends readonly AbiFunction[] ? { [K in keyof abiFunctions]: ( readonly unknown[] | undefined extends args // for functions that don't have inputs, `args` can be `undefined` so fallback to `readonly []` ? readonly [] : args ) extends AbiParametersToPrimitiveTypes ? abiFunctions[K] : never; }[number] // convert back to union (removes `never` tuple entries: `['foo', never, 'bar'][number]` => `'foo' | 'bar'`) : never : abiFunction_ : never, outputs extends readonly AbiParameter[] = abiFunction['outputs'], primitiveTypes extends readonly unknown[] = AbiParametersToPrimitiveTypes, > = [abiFunction] extends [never] ? unknown // `abiFunction` was not inferrable (e.g. `abi` declared as `Abi`) : readonly unknown[] extends primitiveTypes ? unknown // `abiFunction` was not inferrable (e.g. `abi` not const-asserted) : primitiveTypes extends readonly [] // unwrap `primitiveTypes` ? // biome-ignore lint/suspicious/noConfusingVoidType: void // no outputs : primitiveTypes extends readonly [infer primitiveType] ? primitiveType // single output : primitiveTypes; export type Error = messages extends string ? [ // Surrounding with array to prevent `messages` from being widened to `string` `Error: ${messages}`, ] : { [key in keyof messages]: messages[key] extends infer message extends string ? `Error: ${message}` : never; }; type PartialBy = ExactPartial> & Omit; type ExactPartial = { [K in keyof T]?: T[K] | undefined }; export type MaybePartialBy = TKeys extends keyof TType ? PartialBy : TType; export type ReadonlyWiden = | (TType extends Function ? TType : never) | (TType extends ResolvedRegister['bigIntType'] ? bigint : never) | (TType extends boolean ? boolean : never) | (TType extends ResolvedRegister['intType'] ? number : never) | (TType extends string ? TType extends Address ? Address : TType extends ResolvedRegister['bytesType']['inputs'] ? ResolvedRegister['bytesType'] : string : never) | (TType extends readonly [] ? readonly [] : never) | (TType extends Record ? { [K in keyof TType]: ReadonlyWiden } : never) | (TType extends { length: number } ? { [K in keyof TType]: ReadonlyWiden; } extends infer Val extends unknown[] ? readonly [...Val] : never : never); export type AbiBasicType = Exclude; /** * First, infer `Head` against a known size type (either fixed-length array value or `""`). * * | Input | Head | * | --------------- | ------------ | * | `string[]` | `string` | * | `string[][][3]` | `string[][]` | */ export type MaybeExtractArrayParameterType = type extends `${infer head}[${'' | `${SolidityFixedArrayRange}`}]` ? // * Then, infer in the opposite direction, using the known `head` to infer the exact `size` value. // * // * | Input | Size | // * | ------------ | ---- | // * | `${head}[]` | `""` | // * | `${head}[3]` | `3` | // */ type extends `${head}[${infer size}]` ? [head, size] : undefined : undefined; export type IsUnion = T extends C ? ([C] extends [T] ? false : true) : never; export type UnionToTuple> = [U] extends [never] ? [] : [...UnionToTuple>, Last]; type LastInUnion = UnionToIntersection 0 : never> extends (x: infer L) => 0 ? L : never; type UnionToIntersection = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never; /** * Create tuple of {@link type} type with {@link size} size * * @param Type - Type of tuple * @param Size - Size of tuple * @returns Tuple of {@link type} type with {@link size} size * * @example * type Result = Tuple * // ^? type Result = [string, string] */ // https://github.com/Microsoft/TypeScript/issues/26223#issuecomment-674500430 export type Tuple = size extends size ? number extends size ? type[] : _TupleOf : never; type _TupleOf = acc['length'] extends size ? acc : _TupleOf; /** * Merges two object types into new type * * @param object1 - Object to merge into * @param object2 - Object to merge and override keys from {@link object1} * @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. * * @example * type Result = Merge<{ foo: string }, { foo: number; bar: string }> * // ^? type Result = { foo: number; bar: string } */ export type Merge = Omit & object2; // FUNCTIONS /** * Extracts array parameter type information from a type string. * Always returns a tuple, with undefined for non-array types. * * @param type - Type string like "tuple", "tuple[]", or "tuple[2]" * @returns [head, size] where: * - head is the base type * - size is undefined for non-arrays, "" for dynamic arrays, or the number as a string for fixed arrays * * @example * extractArrayParameterType("tuple") // ["tuple", undefined] * extractArrayParameterType("tuple[]") // ["tuple", ""] * extractArrayParameterType("tuple[2]") // ["tuple", "2"] */ export function extractArrayParameterType(type: T): [T, string | undefined] { if (type == null) return [type, undefined]; const match = type.match(/^(.+)\[(\d*)\]$/); if (!match) { // Not an array type, return [type, undefined] return [type, undefined]; } const head = match[1]; const size = match[2]; // Empty string for dynamic arrays, or digits for fixed arrays // Return empty string for dynamic arrays, or the size string for fixed arrays return [head as T, size === '' ? '' : size]; } type AbiItem = AbiConstructor | AbiError | AbiEvent | AbiFallback | AbiFunction | AbiReceive; export function isAbiFunction(item: AbiItem | unknown): item is AbiFunction { if (typeof item !== 'object' || item === null) return false; return 'type' in item && item.type === 'function' && 'name' in item && 'outputs' in item; } export function getAbiFunction( abi: TAbi, functionName: TFunctionName ): AbiFunction | undefined { return abi.find((item) => { const isFunction = isAbiFunction(item); if (!isFunction) return false; return item.name === functionName; }) as AbiFunction | undefined; }