> ## 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.

# Solana vs EVM

> 在 Solana 上使用 LI.FI Intents 时，集成方或求解器相较于 EVM 链需要做哪些不同的处理：平台概念、地址、端点、结算和证明。

Solana 是受支持的 Intents 链（主网 `solana:1151111081099710`）。它不是一个兼容 EVM 的运行时：程序取代了合约，账户取代了存储，租金、PDA 和交易大小限制共同塑造了订单的运作方式。高层的心智模型不变——输入结算、输出结算、预言机验证——但许多具体机制有所不同。

本页是一份差异参考：对于每个关注点，说明 EVM 的做法以及 Solana 的替代做法。开头会为来自 EVM 背景的读者提供一份简短的平台入门介绍。

> [!NOTE]
>
> 关于 LI.FI 各产品对 Solana 的通用（非 Intents）支持，请参见 [Solana 生态系统页面](/introduction/solana-ecosystem)。

***

## 概览

下表总结了本页涉及的每一处差异；下方各小节逐一解释。

| 关注点 | EVM | Solana |
| - | - | - |
| 地址格式 | `0x` 十六进制，20 字节 | base58，32 字节 |
| 结算类型 | 托管（Escrow）**和** Compact | **仅托管（Escrow）** |
| 订单开启路径 | 链上开启 **或** 无 gas 的 `POST /orders/submit` | **仅限链上开启**（由赞助者出资，永远不是无 gas 的） |
| 每笔订单的输入数量 | 多个（`uint256[2][]`） | **一个**（`MandateInput`） |
| 金额宽度 | `uint256` | **`u64`**（SPL 限制） |
| 订单大小 | 实际上不受限制 | **`finalise` 必须放进一笔交易**（在 `open` 时强制检查） |
| 证明存储 | 合约存储 | **PDA 账户** |
| 批量填充 | `fillOrderOutputs` 一次填充整个订单 | **每个输出**一条指令 |
| 先释放输入的同链结算 | 来自 `finalise` 的回调 | **钉住的后置填充**，仅支持单个输出 |
| 交付时调用 | 支持 | **不支持** |

***

## Solana 六十秒入门

Solana 相关页面依赖若干平台基础知识。如果你来自 EVM 背景，以下内容足以让你跟上——每一项都链接到官方文档以供深入了解。

* **程序派生地址（PDA）。** 一个由程序 id 和一组种子确定性派生出的账户地址。PDA 没有私钥——只有派生它的程序才能为其签名。意图系统在各处使用 PDA：托管、认证和配置全都位于任何人都可以重新计算出的地址上，这正是"账户存在于预期的 PDA 上"本身就能充当证明的原因。[Solana 文档：PDA](https://solana.com/docs/core/pda)
* **跨程序调用（CPI）。** 一个程序在同一笔交易内调用另一个程序上的指令——相当于 Solana 版本的合约调用合约。输出结算器通过 CPI 调用 `intents_protocol` 程序来创建认证账户。[Solana 文档：CPI](https://solana.com/docs/core/cpi)
* **关联代币账户（ATA）与租金。** SPL 代币余额存放在独立的代币账户中，而不是钱包地址本身；ATA 是某个 (owner, mint) 对所对应的规范代币账户。每个账户都必须持有一小笔 SOL 押金（"租金"）才能存在——创建账户的人支付租金，账户关闭时会退还给他们。这就是为什么订单会跟踪一个*赞助者*（在 `open` 时支付租金，并在订单关闭时收回托管代币账户和 `order_context` 的租金——`consumed_order` 的租金则是每笔订单永久性的成本），为什么当接收方的 ATA 尚不存在时，求解器可能需要支付租金，也是为什么填充记录和认证会记下由谁支付了它们，以便之后退还租金。[SPL 文档：ATA](https://spl.solana.com/associated-token-account)
* **Anchor。** 编写意图相关程序所使用的框架。它声明每条指令所需的账户，为客户端生成一份 IDL（类似于 EVM 的 ABI），并为事件和账户添加判别器前缀。TypeScript 示例使用的是 Anchor 的客户端库 `@coral-xyz/anchor`。[Anchor 文档](https://www.anchor-lang.com/docs)
* **`remaining_accounts`。** Anchor 指令会声明一份固定的账户列表，但调用者可以在末尾追加额外的账户。意图系统用它来处理长度取决于订单的列表——例如 `finalise` 时每个输出对应一个认证账户加上一个租金退还账户。
* **Borsh。** Anchor 用于指令数据、账户和事件的二进制序列化格式。订单 id 就是由它派生的：`order_id = keccak256(borsh_serialize(StandardOrder))`。[Borsh](https://borsh.io/)
* **Base58 与 bytes32。** Solana 地址是 32 字节的 ed25519 公钥，以 base58 字符串形式展示（例如 `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`）。跨链订单格式将所有地址都存储为原始的 32 字节值：Solana 地址就是 base58 解码后的 32 字节，而 20 字节的 EVM 地址会在左侧补零。
* **可验证操作认证（VAA）。** Wormhole 的签名消息格式——由 Wormhole 守护者网络对某个源链上发生的事件所做的认证。Wormhole 预言机以 VAA 形式发布填充证明。[Wormhole 文档：VAA](https://wormhole.com/docs/protocol/infrastructure/vaas/)

***

## 链 ID

在 API 请求中，Solana 链使用 `solana` 命名空间下、源自 CAIP-2 的 id：

| 集群 | 链 id |
| - | - |
| Solana 主网 | `solana:1151111081099710` |
| Solana Devnet | `solana:1151111081099712` |

在链上，协议使用的是存储在 `intents_protocol` 的 `ChainId` PDA 中的 `u128` 链 id。出现在 `StandardOrder.origin_chain_id` 和 `MandateOutput.chain_id` 中、并被各程序用来校验的，是这个值，而不是 EVM 风格的原生 `chainId`。

Solana 没有单一的"链 id"——三个命名空间同时存在，每个使用场景都只期望其中一个：

| 命名空间 | Solana 主网的值 | 出现位置 |
| - | - | - |
| CAIP-2 / API id | `solana:1151111081099710`（在求解器报价端点上是不带前缀的 `1151111081099710`） | API 请求和响应 |
| 规范链上 id | 作为 `u128` 的 `1151111081099710` | `StandardOrder.origin_chain_id`、`MandateOutput.chain_id`、`ChainId` PDA |
| Polymer 协议 id | `2` | Polymer 的 Prove API（`srcChainId`）以及该预言机的链映射 |

使用了错误命名空间下的值，会以间接的方式失败。以 `eip155:` 报价一条 Solana 分支会被拒绝，但错误信息说的是*资产编码*问题，而非命名空间错误。用规范 id 而非 `2` 去请求 Polymer 证明，会被 Prove API 拒绝——Polymer 流程见[预言机系统](/lifi-intents/architecture/oracle-systems#polymer-on-solana)。

## 报价

Solana 由标准报价端点 `POST /api/v1/integrator/quote/request` 提供服务——与 EVM 和 Tron 使用的是同一个端点。将 Solana 地址以 base58 格式、配合 `solana:` CAIP-2 链 id 传入，取回的也是 base58 格式。请求和响应示例见[请求报价](/lifi-intents/intents-api/request-quote#solana-request)。Solana 不由旧版 V0 端点提供服务。

对于源自 Solana 的订单，胜出的报价返回的是一笔可直接开启的交易，而不是一个待提交的订单。`order.openIntentTx` 的字段与 EVM 不同：

* `to` 是输入结算器的程序 id，以 base58 表示。
* `data` 是一个 **base58 编码、序列化后的 v0 `VersionedTransaction`**，而不是 calldata。它的 `recentBlockhash` 是一个全零的占位值（`11111111111111111111111111111111`），必须在签名前替换为一个新的区块哈希——Solana 的区块哈希大约一分钟后就会过期。
* `computeUnitsRequired` 是该交易所需计算单元的预估值（十进制字符串）。序列化后的模板不包含 Compute Budget 指令；请在签名前用 `ComputeBudgetProgram.setComputeUnitLimit` 重建该消息，如[在 Solana 上开启订单](/lifi-intents/intents-api/create-and-submit#opening-on-solana)所示。
* `checks.allowances` 始终为 `[]`：SPL 转账发生在 `open` 指令内部，因此不存在预先授权的步骤。
* 如果设置了 `metadata.exclusiveFor`，它是在独占窗口内拥有独占填充权的输入链求解器身份。对于源自 Solana 的订单，它必须是将对 `finalise` 签名的 base58 格式 Solana 公钥，即使输出在 EVM 上也是如此。

## 代币支持

| 代币类型 | 作为输入 | 作为输出 | 备注 |
| - | - | - | - |
| 经典 SPL | ✓ | ✓ | 报价端点支持 |
| 原生 SOL | ✗ | ✓ | `open` 要求输入为 SPL 铸币；作为输出时，将代币编码为 32 字节的零，求解器通过 `native_fill` 交付 |
| Wrapped SOL（WSOL） | ✓ | ✓ | 被当作普通 SPL 铸币处理——两端都不会自动包装或解包 |
| 兼容的 Token-2022 | ✓ | ✓ | 支持，除非该铸币启用了转账钩子、设置了非零转账手续费，或带有不可转让扩展 |

xStocks 使用 Token-2022，在某个具体铸币的当前配置兼容时，可以使用标准报价流程。兼容性是针对每个铸币、根据其链上配置逐一评估的。出现以下任一情况时，该铸币会被拒绝：

* **启用了转账钩子** —— 转账钩子扩展设置了钩子程序。该扩展存在但未配置程序时，铸币仍视为兼容。
* **非零转账手续费** —— 转账手续费扩展在当前或计划中的纪元设置了大于零的手续费。零手续费配置（如 PYUSD）是兼容的。
* **不可转让** —— 该铸币带有不可转让扩展，因此任何转账都不可能成功。
* **无法解析的铸币** —— 该铸币账户不存在，或不属于已知的代币程序。

其他扩展，包括该服务尚未识别的扩展，本身不会导致铸币被拒绝。这项资格检查并不保证每一次转账都会成功：发行方控制的限制（例如暂停或冻结）仍可能导致模拟或执行失败。转账钩子程序和手续费率也可能发生变化，因此某个铸币的兼容性可能会随时间改变。

同一策略会被执行两次：报价端点对不兼容的铸币（或无法读取的铸币）返回空报价列表，而在链上开启的、引用了不兼容铸币的订单不会被分发给求解器。

## 结算：仅托管

Solana 只支持托管输入结算器（`input_settler_escrow`）。不存在 Compact 或资源锁流程，因此 [Compact 订单](/lifi-intents/intents-api/compact-orders)指南不适用于 Solana。

## 每笔订单一个输入

一笔 Solana 订单只携带恰好一个输入——一个 `{ token, amount }` 对（`MandateInput`）——而 EVM 订单可以携带一个输入数组。如果你需要花费多种代币，就需要多笔订单。

## 金额是 u64

Solana 将协议格式中 32 字节的金额字段以及荷兰式拍卖的 `slope` 字段收窄为 `u64`（SPL 代币的限制）；EVM 则将它们视为完整宽度的 `uint256`。在构造发往 Solana 的跨链荷兰式订单时，请确保 slope 和最大结算金额都落在 `u64` 范围内，否则该输出将无法被填充。定价公式见[拍卖](/lifi-intents/for-solvers/auctions)。

## 订单大小

在 EVM 上，订单大小实际上不受限制。在 Solana 上，整个 `StandardOrder` 都要放进 `open` 和 `finalise` 的指令数据中，而 `finalise` 还会为每个输出附加一个认证账户和一个租金退还账户——这一切都必须塞进 Solana 1,232 字节的交易包中。

托管结算器在 `open` 时强制执行这一边界：它将 `N` 个输出对应的完结交易预估为 `760 + 302·N + Σ(|callback_data| + |context|)` 字节，超过 1,142 字节（数据包上限减去为 compute-budget 指令预留的余量，若赞助者就是用户则会返还 32 字节）就拒绝该订单（`FinaliseTxTooLarge`）。因此，一笔能够成功开启的订单，总能用一笔"裸"的完结交易完结；只有当你自己追加了额外指令时，才需要重新检查这个边界。

实际后果是：\*\*在 Solana 上开启的订单恰好只有一个输出。\*\*一个输出的预估为 1,062 字节（赞助者即用户时为 1,030 字节），为 `callback_data` 和 `context` 留出 80 字节（112 字节）——在没有回调数据时，足以容纳所有订单类型（最大的 context，即独占荷兰式拍卖，为 73 字节）。两个输出的预估至少为 1,332 字节，总会被拒绝。源自其他链的订单不受此限制，可以在 Solana 上交付多个输出。

## 证明即账户

EVM 把填充证明存放在验证接口背后的合约存储中。Solana 把它们存储为确定性的 PDA 账户：Solana 上的一次填充对应一个 `LocalAttestation`，一次由预言机注册的远程证明对应一个 `Attestation`。完整的模型——种子、载荷编码和端到端流程——见 [Solana 程序中的"认证"](/lifi-intents/architecture/solana-programs#attestations)。

## 同链交换

两个平台都支持同链意图，但实现方式不同。

在 Solana 上，这类订单会将输出结算器的状态 PDA 填入全部三个身份槽位（`settler`、`oracle`、`inputOracle`）——见[身份矩阵](/lifi-intents/architecture/solana-programs#which-identity-goes-in-which-field)。在标准路径上，`fill` 会创建一个 `LocalAttestation`，完结时直接验证该账户——不涉及任何预言机程序。在这种编码下，托管程序不是该认证的消费方，因此该认证的租金不会被退还。在下文的先释放输入路径上，则根本不会创建任何认证。

在 EVM 上，同链被建模为一种预言机配置（输出结算器同时充当预言机）。

两个平台都可以在输出交付之前释放输入，但执行模型不同：

| | EVM | Solana |
| - | - | - |
| 机制 | 来自 `finalise` 的 `orderFinalised` 回调，在输入支付之后、证明检查之前运行 | `finalise_with_prefill`，要求同一笔交易的稍后部分存在一个匹配的紧凑填充（compact fill） |
| 输出 | 一个或多个同链输出 | 恰好一个同链输出 |
| 策略代码所在位置 | 求解器自己的合约，被回调 | 位于两条结算指令之间的普通指令；两者都不能由某个程序调用 |
| 定价类型 | 全部 | 仅限限价和独占限价 |
| 硬性限制 | 区块 gas 上限 | 一笔 1,232 字节的交易 |

求解器工作流和约束条件见[先释放输入的 Solana 结算](/lifi-intents/for-solvers/settlement#input-first-solana-settlement)。

## 每条指令一次填充

Solana 上没有类似 `fillOrderOutputs` 这样能一次性原子填充多个输出的机制。你需要用各自独立的指令逐一填充每个输出。这一点对于源自其他链、带有多个 Solana 输出的订单才有意义；在 Solana 上开启的订单只有一个输出。

***

## 后续步骤

> **[Solana 程序](/lifi-intents/architecture/solana-programs)**
>
> 支撑 Solana 意图的 Anchor 程序、结构体和指令


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.