> ## Documentation Index
> Fetch the complete documentation index at: https://docs.li.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# References

> 编译器能理解的三种 ref 作用域、保留前缀，以及 SDK handles 如何变成 refs。

> 既然你已经知道被传递的是什么（resources、handles），本页将介绍把它们接线到一起的 dotpath 语法。

**ref** 是一个包裹在 `{ "$ref": "…" }` 中的 dotpath 字符串。Ref 让某个 `Call` 能够将它的某个输入 port 绑定到 Flow 中别处产生的值：一个声明的输入、前一个 call 的输出，或一个运行时上下文值。

该语法识别**三**种作用域，并保留少量前缀以避免歧义。

## 三种 ref 作用域

### 1. `input.<name>`

引用在 `flow.inputs` 中声明的一个 flow 级输入。

```json theme={"system"}
{ "$ref": "input.amountIn" }
```

解析为 `{ scope: "input", port: "amountIn" }`。

### 2. `context.<key>`

引用一个运行时上下文值。目前仅识别两个键：

| 键                  | 含义                            |
| ------------------ | ----------------------------- |
| `sender`           | 交易签名者的 `address`。             |
| `executionAddress` | 编译后的 flow 运行所在的预测执行 proxy 地址。 |

```json theme={"system"}
{ "$ref": "context.sender" }
```

解析为 `{ scope: "context", key: "sender" }`。`context.*` 下的任何其他键都会被拒绝。

### 3. `<nodeId>.<port>`

任何前缀*不是* `input` 或 `context` 的 ref 都会被视为对另一个 call 的输出 port 的引用。`<nodeId>` 是你添加该节点时传入的**用户自定义 id**（例如，`builder.lifi.swap('swap', …)` 会产生形如 `swap.<port>` 的 refs）。

```json theme={"system"}
{ "$ref": "swap.amountOut" }
```

解析为 `{ scope: "output", node: "swap", port: "amountOut" }`。

## 保留前缀

有三个前缀是保留的，不能用作节点 id：`input`、`context` 和 `literal`。Flow 校验会拒绝 `id` 与其中任何一个匹配的节点。

`literal` **不是** ref 作用域；它被保留是为了让语法保持无歧义。字面*值*通过一种独立机制表达，即 `LiteralBinding`，它与 refs 一起存在于节点的 `bind` 记录中：

```json theme={"system"}
{
  "bind": {
    "amount": { "kind": "uint256", "value": "1000000000000000000" }
  }
}
```

## SDK handles 如何变成 refs

合作伙伴代码通常不会直接构造 refs。SDK 为你提供 **handles**（由 builder 返回的类型化 JavaScript 值），并在序列化 call 时将它们转换为 refs。

Handles 有三种形态：

* **`InputHandle`。** 由 `builder.inputs.<name>` 返回。序列化为 `input.<name>`。
* **`ResourceInputHandle`。** `InputHandle` 的一个子类型，用于 resource input，携带资源声明。
* **`OutputHandle`。** 由 op 调用返回。`builder.lifi.swap('swap', …).amountOut` 序列化为 `swap.amountOut`（其中 `swap` 是你作为第一个参数传入的用户自定义节点 id）。

转换是自动的。你把 handles 传入 `bind`，SDK 就会生成正确的 `$ref` 字符串。上下文 refs 通过 `builder.context` 暴露：

* `builder.context.sender` → `{ $ref: "context.sender" }`
* `builder.context.executionAddress` → `{ $ref: "context.executionAddress" }`

## 原始 refs（应急出口）

当你需要引用一个你通过 `builder.untypedOp(...)` 创建的 call 的输出，或引用类型化 API 未涵盖的路径时，使用 `raw.ref<T>(path)` 来获得一个可被 `Bindable<T>` 槽位接受的类型化 `TypedRef`：

```ts theme={"system"}
import { raw } from '@lifi/composer-sdk';

builder.untypedOp('custom', 'some.op', {
  bind: { x: { $ref: 'input.token' } },
  config: {},
});

builder.core.asResource('wrapped', {
  bind: { handle: raw.ref<'uint256'>('custom.result') },
  config: { resource: /* ... */ },
});
```

`raw.ref` 不做运行时校验；调用方负责选择正确的类型参数。

## 小结

* 存在三种作用域：`input.<name>`、`context.<sender|executionAddress>`、`<nodeId>.<port>`。
* `<name>` 和 `<nodeId>` 是用户自定义的：你在编写 flow 时挑选它们。
* `literal` 是一个*保留前缀*，而不是 ref 作用域。字面值使用 `LiteralBinding`，而不是 ref。
* SDK handles 会自动转换为 refs。你几乎不需要手写 ref 字符串。
* 仅当类型化 API 无法表达你所需的 ref 时，才使用 `raw.ref<T>(path)`。

> 一旦 Flow 接线完成，你就把它 POST 到 `/compose`。下一页 [Execution Model](/composer/composer-api/concepts/execution-model) 介绍这条流水线。
