UNPKG

@lifi/composer-sdk

Version:

Public Composer SDK for building and submitting flows

147 lines (144 loc) 6.25 kB
import { Ref } from '@lifi/compose-spec'; import { ParseAbiItem } from 'abitype'; import { AbiTypeToOutputKind, TypedGuard } from '../types.cjs'; import { AnyBindable, CallArgs } from './FlowBuilderCore.cjs'; import { Bindable, OutputHandle } from './handles.cjs'; /** * Type-level extraction of named bind keys from a function signature string. * When `TSig` is a string literal matching a Solidity function signature, * resolves to a record whose keys are the parameter names and values are * `Bindable<kind>` where the kind is derived from each parameter's Solidity * type via `AbiTypeToOutputKind`. For recognised `SolType` parameters * (e.g. `uint256`, `address`, `uint128`) the bind slot is precisely typed; * for other Solidity types (e.g. tuples) the slot accepts any scalar handle. * Falls back to `Record<string, AnyBindable>` for non-literal strings. */ type SignatureBind<TSig extends string> = string extends TSig ? Record<string, AnyBindable> : ParseAbiItem<TSig> extends { type: 'function'; inputs: infer I extends readonly { name: string; type: string; }[]; } ? { readonly [P in I[number] as P['name']]: Bindable<AbiTypeToOutputKind<P['type']>>; } : Record<string, AnyBindable>; /** * Describes the output type(s) of a parsed function signature as a * human-readable string for inclusion in type-level error messages. */ type DescribeOutputs<O extends readonly { type: string; }[]> = O extends readonly [{ type: infer T extends string; }] ? T : 'multiple values'; /** * A string-literal type that surfaces a clear error when the user provides a * function signature whose return type is not supported by core.call. * The message is visible in IDE tooltips and compiler diagnostics. */ type UnsupportedReturnTypeError<TLabel extends string, TSig extends string> = ParseAbiItem<TSig> extends { type: 'function'; outputs: infer O extends readonly { type: string; }[]; } ? `[TypeError] ${TLabel}: return type '${DescribeOutputs<O>}' is not supported — only 'uint256' or void signatures are allowed` : `[TypeError] ${TLabel}: unable to parse return type from signature`; /** * Conditional output type for `core.call` based on the function signature's * return type: * * - `returns (uint256)` → `{ result: OutputHandle }` * - No returns clause → `{ result: undefined }` * - Anything else → `{ result: "[TypeError] ..." }` (compile-time error) * * When `TSig` is a non-literal `string` (e.g. a variable, not a string * constant), the type falls back to `{ result: OutputHandle | undefined }` * so that both void and uint256 call sites remain assignable. */ type CoreCallResult<TSig extends string> = string extends TSig ? { readonly result: OutputHandle<'uint256'> | undefined; } : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly []; } ? { readonly result: undefined; } : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly [{ type: 'uint256'; }]; } ? { readonly result: OutputHandle<'uint256'>; } : { readonly result: UnsupportedReturnTypeError<'core.call', TSig>; }; /** * Conditional output type for `core.staticCall`. staticCall always reads a * value, so void signatures are also rejected: * * - `returns (uint256)` → `{ result: OutputHandle }` * - No returns / other → `{ result: "[TypeError] ..." }` (compile-time error) */ type StaticCallResult<TSig extends string> = string extends TSig ? { readonly result: OutputHandle<'uint256'>; } : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly [{ type: 'uint256'; }]; } ? { readonly result: OutputHandle<'uint256'>; } : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly []; } ? { readonly result: `[TypeError] core.staticCall: function has no return value — staticCall requires a 'returns (uint256)' signature`; } : { readonly result: UnsupportedReturnTypeError<'core.staticCall', TSig>; }; /** * Parses a Solidity function signature and returns parameter names in order. * Delegates to abitype's runtime `parseAbiItem` — the same parser that powers * the compile-time `ParseAbiItem<TSig>` type — so runtime and type-level * parsing can never diverge. * * @throws if the signature is not a valid function signature or a parameter is unnamed */ declare const parseFunctionParams: (functionSignature: string) => string[]; /** * Converts any `AnyBindable` to a `Ref` (`{ $ref: string }`). * Handles carry a `_tag` property and are converted via `handleToRef`; * `Ref` objects pass through as-is. */ declare const toBindRef: (bindable: AnyBindable) => Ref; interface BuildCallWireFormatInput { readonly resource?: AnyBindable; readonly bind: Record<string, AnyBindable>; readonly config: Record<string, unknown>; readonly guards?: readonly TypedGuard[]; } interface CallWireResult { readonly op: 'core.call' | 'core.invoke'; readonly bind: Record<string, Ref>; readonly config: Record<string, unknown>; readonly guards?: readonly TypedGuard[]; } /** * Transforms the user-facing `resource` + named `bind` API into the wire * format that the backend expects: * - With resource: `core.call` with `bind: { input: <resourceRef> }` * - Without resource: `core.invoke` with `bind: {}` * - `config: { ...userConfig, args: [positional refs...] }` */ declare const buildCallWireFormat: (args: BuildCallWireFormatInput) => CallWireResult; interface BuildStaticCallWireFormatInput { readonly bind: Record<string, AnyBindable>; readonly config: Record<string, unknown>; readonly guards?: readonly TypedGuard[]; } /** * Transforms the user-facing named `bind` API into the wire format that the * backend expects for `core.staticCall`. Same as `buildCallWireFormat` but * with no `resource` field — staticCall has no resource inputs. */ declare const buildStaticCallWireFormat: (args: BuildStaticCallWireFormatInput) => CallArgs; export { type BuildCallWireFormatInput, type BuildStaticCallWireFormatInput, type CallWireResult, type CoreCallResult, type SignatureBind, type StaticCallResult, buildCallWireFormat, buildStaticCallWireFormat, parseFunctionParams, toBindRef };