/** * Trail expander — turn an abstract Trail + params + vault context into a concrete ExecutionPlan. * * What expansion DOES: * • Validates supplied params against the Trail's param schema. * • Substitutes `$paramName` references everywhere they appear in step args. * • Resolves `subtrail` steps by recursively expanding the referenced Trail and splicing its * steps inline (with the outer trail's args/binding scope). * • Looks up each `leaf` step against the Catalog to resolve the abstract (protocol, action) * into a concrete (target, selector, decoder, templateId) entry — the data the on-chain * Manager actually needs. * • For atomic mode: verifies the flashloan step exists, marks the bracket, and notes which * leaves get bundled into the callback payload. * * What expansion does NOT do: * • RUN automation scripts — outputs are deferred; the runner (W4) calls them. * • CALL venue APIs — same, the runner handles those. * • BROADCAST anything — pure synchronous compute; no chain or network IO. * * That separation matters: the expander is fast, pure, and testable. The runner is async, * side-effectful, and the place where simulation / abort / monitoring concerns land. */ import { type Trail, type ChainName } from "./trail.js"; import type { TrailLibrary } from "./trailLibrary.js"; /** * A catalog adapter resolves the abstract `(protocol, action, chain, args)` of a leaf step into * a concrete on-chain leaf — the (target, selector, decoder, …) tuple a Manager call needs. * * The SDK ships with the interface; concrete adapters live alongside the surfaces that have a * catalog source: * • UI (lucidly-pro): `StrategyLibraryCatalog` wraps the in-page `STRATEGY_LIBRARY` array. * • Server (future): `OnChainCatalog` queries the `LucidlyRegistry` directly for the latest * whitelisted templates per chain. * • Tests: `InMemoryCatalog` with hand-crafted entries. */ export interface CatalogEntry { /** Stable id from whatever catalog source. UI-side this matches STRATEGY_LIBRARY[i].id. */ policyId: string; /** Friendly description (often the policy name) — useful for traces + LLM context. */ name?: string; /** Chain this entry belongs to. */ chain: ChainName; /** The protocol the leaf calls into (e.g. "morphoBlue"). */ protocol: string; /** Action verb (e.g. "supply"). */ action: string; /** On-chain Manager-call fields. */ target: `0x${string}`; selector: `0x${string}`; decoder: `0x${string}`; /** Optional — non-zero msg.value flag for the leaf. */ valueIsNonZero?: boolean; /** Optional — argsHash if the template is FIXED-ARGS rather than VAULT-BOUND. */ argsHash?: `0x${string}`; } export interface CatalogQuery { protocol: string; action: string; chain: ChainName; /** Resolved args — adapters MAY use these to disambiguate when multiple leaves match the * same (protocol, action) but differ in args (e.g. per-asset approve, per-market supply). */ args: Record; } export interface Catalog { resolve(q: CatalogQuery): CatalogEntry | null; } export interface ResolvedLeafStep { kind: "leaf"; /** Optional step id from the source Trail — used by sub-trail output references. */ id?: string; description?: string; /** Concrete catalog entry the abstract (protocol, action) resolved to. */ entry: CatalogEntry; /** Args with all `$refs` substituted. May still contain object/array structures. */ args: Record; } export interface ResolvedApiStep { kind: "api"; id?: string; description?: string; venue: string; method: string; params: Record; } export interface ResolvedAutomationStep { kind: "automation"; id?: string; description?: string; scriptId: string; inputs: Record; outputs: string[]; } export type ResolvedStep = ResolvedLeafStep | ResolvedApiStep | ResolvedAutomationStep; export interface ExecutionPlan { trailId: string; trailName: string; chain: ChainName; vault: `0x${string}`; mode: "atomic" | "sequential" | "parallel"; steps: ResolvedStep[]; /** * For atomic mode: the index (in `steps`) of the flashloan-initiation leaf. Steps after this * are bundled into the flashloan callback payload by the byte-encoder downstream (W4). */ flashloanStepIndex?: number; /** Resolved param values used for this expansion — handy for traces + audit. */ resolvedParams: Record; /** Diagnostics — warnings the caller may want to surface but didn't block expansion. */ warnings: string[]; } export declare class TrailExpansionError extends Error { readonly trailId: string; readonly reason: string; constructor(trailId: string, reason: string); } export interface ExpandOpts { trail: Trail; params: Record; chain: ChainName; vault: `0x${string}`; catalog: Catalog; /** Required when the trail (or any nested sub-trail) contains `subtrail` steps. */ library?: TrailLibrary; /** Strategist EOA — substituted for `$strategist` references. */ strategist?: `0x${string}`; /** Max recursion depth for sub-trail nesting. Default 4 (deep enough for realistic strategies). */ maxSubtrailDepth?: number; } export declare function expandTrail(opts: ExpandOpts): ExecutionPlan;