@lifi/composer-sdk
Version:
Public Composer SDK for building and submitting flows
147 lines (144 loc) • 6.25 kB
text/typescript
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 };