UNPKG

@lifi/composer-sdk

Version:

Public Composer SDK for building and submitting flows

84 lines (70 loc) 2.93 kB
import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec'; import { createComposeSdk, raw } from '../index.js'; import type { Address } from '../types.js'; import { BASE_URL } from './config.js'; // Real ERC-4626 vault (Steakhouse USDC on Ethereum mainnet) queried through // the untyped escape hatch. const VAULT = '0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB'; export interface UntypedOpWithTypedRefInput { readonly owner: Address; } /** * Insert an untyped operation node, then feed its output into typed builder * methods using `raw.ref<T>()`. * * `untypedOp` is the escape hatch for ops the installed SDK's typed surface * doesn't cover — typically backend ops newer than your SDK version. The op * id is passed as a plain string and its bind/config shapes are not * type-checked, so the backend validates them at compile time instead. * * This example routes a real op (`core.staticCall`) through the untyped path * so the flow compiles end-to-end; the mechanism is identical for any op id. * * Demonstrates: * - `builder.untypedOp()` to add a node the typed API doesn't cover. * - `raw.ref<'uint256'>()` to reference the untyped node's output in a typed * `Bindable<'uint256'>` slot — the explicit type parameter is required * so the compiler verifies the ref is used in a compatible slot. * - Mixing untyped and typed nodes in the same flow. */ export const buildUntypedOpWithTypedRef = ({ owner, }: UntypedOpWithTypedRefInput): { flow: Flow; request: ComposeCompileRequest; } => { const sdk = createComposeSdk({ baseUrl: BASE_URL }); const builder = sdk.flow(1, { name: 'untyped-op-with-typed-ref', inputs: {}, }); // Insert an untyped node querying the vault. untypedOp returns void — // the typed builder has no knowledge of this node's outputs. builder.untypedOp('vault-query', 'core.staticCall', { bind: {}, config: { target: VAULT, functionSignature: 'function totalAssets() view returns (uint256)', }, }); // Use raw.ref to bridge the untyped node's output into a typed operation. // The type parameter <'uint256'> tells the compiler this ref produces a // uint256, so it's accepted by Bindable<'uint256'> slots. const vaultResult = raw.ref<'uint256'>('vault-query.result'); // Feed the raw result into typed arithmetic — the compiler checks that // vaultResult (typed as uint256) is compatible with the uint256 bind slot. const scaled = builder.core.multiply('scale', { bind: { a: vaultResult, b: vaultResult }, }); // Continue with fully typed operations downstream. builder.core.assertGte('check-min', { bind: { a: scaled.result, b: vaultResult }, }); const flow = builder.build(); // No sweepTo: the flow reads state and asserts — it produces no resources. const request = sdk.request(flow, { signer: owner, inputs: {}, }); return { flow, request }; };