@lifi/composer-sdk 构建一个 Flow 的每一步:为生产环境配置 SDK、声明 inputs、链接 ops、供应运行时值、附加安全不变量,以及提交。首次集成时请从头读到尾;之后可作为参考回到特定小节。
关于 5 分钟的首个 flow 演示,参见 Quickstart。关于 SDK 类型面,参见 SDK API reference。
生产环境设置
配置 SDK
ComposeSdkOptions:
接入一个 signer
@lifi/composer-sdk 本身不签名或广播交易。它返回一个 transactionRequest 对象({ to, data, value, gasLimit? }),你把它交给你的钱包库。
run 中的 signer 字段对编译器而言纯粹是提示性的;它据此派生用户的执行 proxy 地址并获取授权。SDK 不会验证该地址是否控制某个私钥。
自定义 fetch(可选)
AbortSignal 接线。
声明 inputs
Inputs 是一个具名记录。每一项要么是一个资源声明(token),要么是一个标量类型名。builder.inputs.<name> 的形式暴露类型化 handles:
builder.inputs.amountIn是一个携带 WETH 资源的ResourceInputHandle。
'address'、'uint256'),每一个都以 InputHandle<T> 的形式呈现。仅当之后某个 op 或 sweepTo 实际消费某个标量输入时才声明它 —— 例如一个你转发给 sweepTo 的 recipient: 'address'(参见 swapToRecipient.ts)。
options.name 是可选的;当省略时,SDK 会为该 Flow 的 id 字段生成一个 UUID。资源语义(线性消费、asResource 晋升、port 行为)在 Resource Model 中介绍。
在你的 flow 中链接 ops
SDK 为 Composer API 支持的每个 op 提供一个类型化方法:builder.lifi.swap(id, { bind, config, guards? })builder.lifi.zap(id, { bind, config, guards? })builder.core.call(id, { bind, config, ... })builder.core.asResource(id, ...)、builder.core.balanceOf(id, ...),以及算术方法builder.core.add(id, ...)、subtract、multiply、divideDown、divideUp、bpsDown、bpsUp—— 每个算术 op 一个方法。
bind 映射:
'resource' handle 传入一个非资源槽位是编译期错误。SDK 最终序列化为的 dotpath 字符串(例如 swap.amountOut)在 References 中介绍。
运行时上下文
编译器在执行时填入的运行时值通过builder.context 访问:
builder.context.sender是对签名者地址(address)的一个类型化 ref。builder.context.executionAddress是对预测的执行 proxy 地址(address)的一个类型化 ref。
bind 槽位,或作为 sweepTo 目标。
untypedOp 应急出口
builder.untypedOp 是针对类型化 SDK 面尚未暴露的能力的应急出口 —— 一个生成的方法尚未涵盖的新 op,或一个实验性的 config 字段。它让你直接编写该节点,而不必等待某个 SDK 发布:
untypedOp 返回 void,并在其 bind 映射中接受原始的 Ref 值 —— 没有 handle 转换,没有类型检查。要把一个 untypedOp 节点的输出馈送到一个类型化的下游 call 中,用 raw.ref<T>(path) 包裹该路径;你提供幻影类型(phantom type),SDK 会在任何 Bindable<T> 槽位中接受它(没有运行时校验):
untypedOp。
接入运行时值
一个 flow 声明每个输入期望哪种类型的值。具体的值在编译时通过run.inputs 供应,即当你调用 builder.compile(run) 或 sdk.request(flow, run) 时。
三种输入形态
对于 flow 声明的每个输入,run.inputs[name] 必须是以下三种形态之一:
bigint。 一个字面数值。用于声明为uint256的标量输入,以及数量你已预先知晓的资源输入。在线格式上序列化为一个0x填充的 32 字节十六进制字符串。string。 一个预编码的值。对于标量输入,该字符串必须已符合所声明的类型(例如,address输入用0x…地址,uint256用十进制或十六进制整数)。MaterialiserInput。 一个描述符{ kind: "<materialiser>", …config },告诉服务如何计算该值(见下文)。
run.inputs 中。缺失或未知的键会以 validation_error 失败。
materialiser 是什么
materialiser 是一个确定性纯函数(flow, context) → binding,它在编译时解析一个 flow 输入。它告诉服务如何在程序开头产生 VM 所消费的 handle:例如,“通过 transferFrom 恰好存入 1 WETH”(directDeposit),或”在执行时读取签名者的链上 USDC 余额”(balanceOf)。
为什么不总是用字面值?
- 该值直到执行时才知道。
balanceOf在交易时读取签名者的余额,这让你能编写像”把我现在持有的任意数量的 USDC 都 zap 掉”这样的 flow。 - 该值需要特定的 VM 指令。
directDeposit不只是推入一个数字;它还烘焙进了把代币拉入 proxy 的transferFrom语义,以及(对于精确存入而言)一个校验预期数量已到账的相等性检查。
关于实时列表和逐 kind 的 config schema,参见 materialisers catalog。
运行时数量的假设
当你使用一个运行时 materialiser(balanceOf、call,或带 allowNonExact: true 的 directDeposit)时,编译器在编译时并不知道具体的数量。大多数情况下这没问题;VM 会在执行时读取该数量。但当编译器需要该数量来规划路由时(例如,为一次 lifi.swap 挑选一个聚合器报价),它会退回到 run.assumptions[name],即一个逐输入的 bigint 提示。
preparation_error(HTTP 422)失败。每当你预期运行时数量会与默认值有实质性差异时,都提供一个假设。
添加安全性:preconditions 和 guards
一个 flow 的安全故事有两半。Preconditions(前置条件) 描述在交易运行之前必须为真的状态,作为覆盖馈送给模拟器。Guards 是 VM 在交易期间强制执行的不变量,被烘焙进 calldata。Preconditions:执行前的状态
一个 precondition 是关于链上状态的一个声明式断言。注册了三种类型:
SDK 暴露类型化工厂:
- 显式。 你在
run.preconditions中传入的任何内容。 - 由 materialiser 产生。 例如,对一个 ERC-20 资源使用
directDeposit({ amount })会自动发出一个Erc20Balanceprecondition 和一个Erc20Allowanceprecondition。
balanceOf 来获取一个本应已在 proxy 上的数量时。
在编译时,preconditions 作为状态覆盖要求(state override requirements)发送给模拟器。模拟器会打补丁修改存储槽,使钱包拥有指定的余额 / 授权,然后针对该合成状态运行模拟。模拟不会仅因为实际链上状态不匹配某个 precondition 而失败;其全部要点就是让模拟器仿佛该 precondition 成立那样继续。调用方负责在提交交易之前等待真实状态赶上。
Guards:执行期间的不变量
Guards 作为 op 调用上的guards 选项传入:
producedResources[*].simulated.amountOutMin 上。
不要施加会重复某个 op 内部行为的 guards。每当某个 op 接受 slippage config 字段时,就在 op 本身上配置 slippage,而不是通过 guard。lifi.swap 是典型例子:传入 slippage: 0.03 会将 3% 的下限烘焙进聚合器报价,并且 amountOut port 携带 providesMinimum: true,因此一个冗余的滑点 guard 会在编译期被拒绝。
何时用哪个
用 preconditions 来描述在交易运行之前必须为真的东西。用 guards 来强制在交易期间必须为真的东西。
提交 flow
builder.compile(run) 是标准的一步式路径:它调用 builder.build(),通过 sdk.request 构造一个 ComposeCompileRequest,并 POST 到 /compose。关于更底层的模式(排队、服务端 proxy、签名请求检查),直接调用 builder.build() 和 sdk.request(flow, run)。参见 SDK API reference。
在 SDK 级别不存在 sdk.compile()。Compile 位于 builder 上。
run 输入
每次提交都传入一个ComposeRunInput:逐输入的值、签名者地址、可选的 preconditions、针对终端资源的 sweepTo 目标,以及策略字段(simulationPolicy、checkOnChainAllowances、maxPriceImpactBps)。关于完整的类型签名,参见 SDK API reference。
sweepTo 是一个兜底项,它把每一份残余的 proxy 余额移动到目标 —— 但某些终端资源无法被移动(例如支撑一笔未平仓债务的 aToken)。关于何时改为发出显式转账,参见 Terminal Resources。
Approvals
ComposeCompileResult.approvals 列出在 compose 交易可以执行之前,签名者必须授予的任何 ERC-20 授权。签名者必须为每个输入代币向执行 proxy 授权。先提交这些授权,再提交 compose 交易。
在 run 中传入 checkOnChainAllowances: true,让服务器检查当前链上授权并省略那些已经充足的授权。
你可能会看到的错误
编译期错误:
模拟期和运行时错误:
在
simulationPolicy: "allow-revert" 下,一个 simulation_revert 会以 HTTP 206 返回编译后的 calldata 以及回滚诊断信息,而不是彻底失败。当你想向用户呈现回滚原因时使用它。
关于完整的错误目录,参见 Errors。
另请参阅
- Quickstart —— 五分钟的首个 flow 演练。
- Recipes —— 三个你可以复制的典型 flow。
- SDK API reference —— 详尽的 API 面。
- Concepts —— Flow 形态、refs、resources、执行模型、guards。
- Errors —— 完整的
ComposeError目录。

