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

# 租金和可回收租金

> Solana 在 SIMD-0437 下分五个步骤将租金豁免最低额削减 90%。在每个步骤之前创建的账户持有的资金超过所需——了解这个超额是什么、哪些程序会返还它，以及如何找到并回收它。

<Info>
  **本页内容由 AI 自动翻译，所有内容以英文版本为准。**

  [查看英文版 →](/solana-fundamentals/rent-and-reclaimable-rent)
</Info>

<Info>
  租金是可退款的保证金，不是费用。SIMD-0437 通过五个独立的特性门分阶段降低每个账户必须持有的余额。在某个步骤之前创建的账户保持其被资助时的余额，因此每个步骤都会让它们超额资助。SPL Token 和 Token-2022 程序可以通过 `WithdrawExcessLamports` 返还这个差额，而无需关闭账户或触及其代币余额。没有过期时间——超额部分会一直保留在你自己的账户中，直到你选择移动它。
</Info>

如果你是 Solana 账户资助方式的新手，请先阅读[账户模型](/zh/solana-fundamentals/account-model)。

## 租金实际上是什么

Solana 上的每个账户都持有一个由其占用空间大小决定的 SOL 保证金。它不会被消耗——当账户关闭时会全额返还。公式如下：

```
minimum_balance(data_len) = (ACCOUNT_STORAGE_OVERHEAD + data_len) × lamports_per_byte
```

`ACCOUNT_STORAGE_OVERHEAD` 是一个固定的 128 字节，每个账户都要为此付费，无论其有效载荷如何。`lamports_per_byte` 是网络范围的常数，SIMD-0437 会改变它。

因此，一个标准的 165 字节 SPL 代币账户的成本一直是 `(128 + 165) × 6,960 = 2,039,280` lamports——即你在钱包打开关联代币账户时看到扣除的约 0.00203928 SOL。

## SIMD-0437 改变了什么

SIMD-0437 将 `lamports_per_byte` 从 6,960 降低到 696——减少 90%——通过五个独立的特性门分阶段推出，以便验证者可以逐步吸收状态增长的影响。

| 步骤    | `lamports_per_byte` | 相对原始值的削减 | 165 字节代币账户的租金      |
| ----- | ------------------- | -------- | ------------------ |
| —（原始） | 6,960               | —        | 2,039,280 lamports |
| 1     | 6,333               | 9%       | 1,855,569 lamports |
| 2     | 5,080               | 27%      | 1,488,440 lamports |
| 3     | 2,575               | 63%      | 754,475 lamports   |
| 4     | 1,322               | 81%      | 387,346 lamports   |
| 5     | 696                 | 90%      | 203,928 lamports   |

步骤 1 在 2026 年 9 月 3 日在主网上激活。其余步骤在其特性门启用时推出；将时间表视为可能变化，并从集群读取实时值而不是硬编码。

<Note>
  SIMD-0437 依赖于 SIMD-0194，后者弃用了租金豁免阈值"以避免在特性激活时设置租金参数时进行不必要的浮点数学运算"。实际上，`Rent` sysvar 现在携带 `lamports_per_byte_year = 6,333` 和 `exemption_threshold = 1.0`，而不是产生 6,960 的旧 `3,480 × 2` 分割。不要自己乘以这两个字段——调用 `getMinimumBalanceForRentExemption` 并让集群回答。
</Note>

## 为什么现有账户持有过多

降低常数改变了账户*需要*的东西。它不改变账户*拥有*的东西。在 6,960 lamports 每字节时资助的账户在步骤 1 激活后保持该余额，因此它的超额资助为：

```
excess = (128 + data_len) × (old_rate − new_rate)
```

对于步骤 1 后的 165 字节 SPL 代币账户，这是 `293 × (6,960 − 6,333) = 183,711` lamports，或每个账户约 0.000184 SOL。携带扩展的 Token-2022 账户更大，因此它持有的比例更多——一个 182 字节的账户超额资助 `310 × 627 = 194,370` lamports。

单独来看这是微不足道的。一个多年来与数百个代币交互过的钱包持有的是这个数字的有意义的倍数，到步骤 5 时，每个 165 字节的账户在其最低值之上有 1,835,352 lamports（约 0.00184 SOL）。

## 哪些账户可以返还它

程序拥有的账户中的超额 lamports 只能由该程序移动。因此，你是否可以在不关闭账户的情况下回收租金完全取决于哪个程序拥有它。

<CardGroup cols={2}>
  <Card title="SPL Token 和 Token-2022" icon="circle-check">
    两者都公开了 `WithdrawExcessLamports`。账户保持打开状态，保留其代币余额，只是降至当前最低值。
  </Card>

  <Card title="其他所有程序" icon="circle-xmark">
    没有等效指令。租金仅在账户关闭时释放——这是一个具有自身前置条件的破坏性操作，不是租金扫描。
  </Card>
</CardGroup>

具体来说，对于 Raydium 用户持有的账户类型：

| 账户               | 所有者                                           | 可以就地返还超额吗？                                                  |
| ---------------- | --------------------------------------------- | ----------------------------------------------------------- |
| SPL 代币账户         | `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` | 是——`WithdrawExcessLamports`                                 |
| Token-2022 代币账户  | `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | 是——`WithdrawExcessLamports`                                 |
| 包装 SOL（原生）代币账户   | 任一代币程序                                        | 否——见下文                                                      |
| CLMM 头寸          | Raydium CLMM                                  | 否——租金在头寸关闭时返还                                               |
| OpenBook v1 开放订单 | OpenBook v1                                   | 否——仅 `CloseOpenOrders`                                      |
| OpenBook v2 开放订单 | OpenBook v2                                   | 否——仅 `close_open_orders_account`                            |
| 质押账户             | 质押程序                                          | 否——SIMD-0490 将 `rent_exempt_reserve` 固定在 2,282,880 lamports |

包装 SOL 账户是唯一的代币程序例外：它们的 lamport 余额*就是*它们的代币余额，所以两个程序都以 `TokenError::NativeNotSupported` 拒绝它们。Token-2022 为此添加了 `UnwrapLamports`（判别式 45）；`@solana/spl-token` 从 0.4.15 开始为其提供 `createUnwrapLamportsInstruction`。在租金扫描中跳过原生账户并有意处理它们。

## `WithdrawExcessLamports` 指令

两个代币程序指令枚举中的判别式 **38**。来自 `spl-token-interface`：

```rust theme={null}
/// This instruction is to be used to rescue SOL sent to any TokenProgram
/// owned account by sending them to any other account, leaving behind only
/// lamports for rent exemption.
///
/// 0. `[writable]` Source Account owned by the token program
/// 1. `[writable]` Destination account
/// 2. `[signer]` Authority
/// 3. `..3+M` `[signer]` M signer accounts
WithdrawExcessLamports,
```

三个属性使其可以安全地对钱包中的每个账户执行：

* **它不需要金额。** 程序自己计算 `source.lamports − rent.minimum_balance(source.data_len())`，所以它永远不能将账户降至当前最低值以下，并且当后续步骤激活时保持正确。
* **它不关闭任何东西。** 账户保留其数据、所有者和代币余额。
* **它是幂等的。** 对已在最低值的账户运行它会移动零 lamports 并成功。

冻结的代币账户仍然符合条件：冻结限制代币移动，而不是 lamports。

### 构建指令

`@solana/spl-token` 不导出其构建器。从 0.4.15 开始，枚举条目仍然被注释掉：

```ts theme={null}
// packages/spl-token/src/instructions/types.ts
export enum TokenInstruction {
    // ...
    TransferHookExtension = 36,
    // ConfidentialTransferFeeExtension = 37,
    // WithdrawalExcessLamports = 38,   // ← not exposed
    MetadataPointerExtension = 39,
    // ...
}
```

直接编码它。有效载荷是单个判别式字节：

```ts theme={null}
import { PublicKey, TransactionInstruction } from "@solana/web3.js";

export function createWithdrawExcessLamportsInstruction(params: {
  source: PublicKey;        // the token account holding excess lamports
  destination: PublicKey;   // where the excess goes — usually the wallet itself
  authority: PublicKey;     // owner of `source`, or the multisig account
  multiSigners?: PublicKey[];
  programId: PublicKey;     // TOKEN_PROGRAM_ID or TOKEN_2022_PROGRAM_ID
}): TransactionInstruction {
  const { source, destination, authority, multiSigners = [], programId } = params;
  return new TransactionInstruction({
    programId,
    keys: [
      { pubkey: source, isSigner: false, isWritable: true },
      { pubkey: destination, isSigner: false, isWritable: true },
      { pubkey: authority, isSigner: !multiSigners.length, isWritable: false },
      ...multiSigners.map((pubkey) => ({ pubkey, isSigner: true, isWritable: false })),
    ],
    data: Buffer.from([38]),
  });
}
```

为每个账户传递其自己的 `programId`。SPL Token 和 Token-2022 指令可以共享一个交易，但每个都必须寻址到拥有其源账户的程序。

在主网上测量，该指令在 SPL Token 程序上花费 270 个计算单位，在 Token-2022 上花费 1,414 个——两种方式都可以忽略不计。真正的限制是交易大小，而不是计算。

## 查找可回收账户

不要从硬编码的费率推导超额。询问集群每个账户现在需要什么，这样相同的代码在所有五个步骤中都能继续工作：

```ts theme={null}
const [tokenResp, token2022Resp] = await Promise.all([
  connection.getTokenAccountsByOwner(owner, { programId: TOKEN_PROGRAM_ID }),
  connection.getTokenAccountsByOwner(owner, { programId: TOKEN_2022_PROGRAM_ID }),
]);
const raw = [...tokenResp.value, ...token2022Resp.value];

// one lookup per distinct account size — a wallet normally has two or three
const spaces = Array.from(new Set(raw.map(({ account }) => account.data.length)));
const minimums = new Map(
  await Promise.all(
    spaces.map(async (space) => [space, await connection.getMinimumBalanceForRentExemption(space)] as const),
  ),
);

const reclaimable = raw.filter(({ account }) => {
  // wrapped SOL carries its token balance as lamports — both programs refuse it.
  // The `is_native` COption tag sits at offset 109 in the token account layout,
  // which Token-2022 preserves before its extension data.
  if (account.data.readUInt32LE(109) === 1) return false;
  return account.lamports > minimums.get(account.data.length)!;
});
```

`getMinimumBalanceForRentExemption(0)` 是一个有用的旁路：它返回恰好 `128 × lamports_per_byte`，所以除以 128 可以告诉你集群处于哪个推出步骤，而无需解析 `Rent` sysvar。

## 批处理：一个交易中能放多少

每个 `WithdrawExcessLamports` 指令贡献一个唯一的可写账户密钥——编译消息中的 32 字节——加上约 7 字节的指令编码。目标、权限和费用支付者都是同一个钱包，所以它们之间只花费一个密钥。

对于 1,232 字节的交易限制，一旦计算预算指令和区块哈希被计算，大约 26 条指令能放入。**每个交易 20 条**是安全的工作数字，这也是 Raydium 自己的实现使用的。因此，一个有 116 个可回收账户的钱包在六个交易中进行扫描，每个交易收取一个 5,000 lamport 的基本费用。

注意经济学：费用按交易收取，而不是按账户收取。回收更少的账户不会花费更少，这就是为什么部分扫描很少值得额外的往返。

## 通过 Raydium 回收

[raydium.io/reclaim-rent](https://raydium.io/reclaim-rent) 页面扫描连接钱包的 SPL Token 和 Token-2022 账户，显示按程序分割的总额，并在批处理交易中扫描所有内容。扫描是只读的——直到你按下**回收所有租金**才会签名。

该页面故意仅涵盖代币账户。只能通过关闭来释放租金的账户类型被排除而不是列为不可用，因为关闭账户是一个不同的、破坏性的操作。

## 从 SDK 演示中回收

<Info>
  **版本横幅。** 演示针对 Solana mainnet-beta 上的 `@raydium-io/raydium-sdk-v2@0.2.42-alpha`，已验证 2026-09。`WithdrawExcessLamports` 是手工编码的，独立于 SDK 版本；SDK 仅用于交易构建和批处理。
</Info>

[`raydium-sdk-V2-demo/src/rent`](https://github.com/raydium-io/raydium-sdk-V2-demo/tree/master/src/rent) 中的两个脚本：

```bash theme={null}
# read-only: what can this wallet reclaim, and what would it be worth after all five steps
yarn dev src/rent/checkReclaimableRent.ts
yarn dev src/rent/checkReclaimableRent.ts <any wallet address>

# build, simulate (DRY_RUN = true by default), then send the batched sweep
yarn dev src/rent/reclaimRent.ts
```

`reclaimRent.ts` 以每个交易 20 个账户的速率批处理，并在一次通过中签署所有批次：

```ts theme={null}
const batches = chunk(report.accounts, ACCOUNTS_PER_TX);

const builtTxs = await Promise.all(
  batches.map(async (batch) => {
    const builder = new TxBuilder({
      connection,
      feePayer: owner.publicKey,
      cluster: raydium.cluster,
      owner: raydium.owner,
    });
    builder.addInstruction({
      instructions: batch.map((account) =>
        createWithdrawExcessLamportsInstruction({
          source: account.pubkey,
          destination: owner.publicKey,
          authority: owner.publicKey,
          programId: account.programId,
        }),
      ),
    });
    return builder.versionBuild({ txVersion });
  }),
);

// versionMultiBuild puts the calling builder's transaction first and appends
// extraPreBuildData after it, so batch 1 drives and batches 2..n follow in order
const [firstTx, ...restTxs] = builtTxs;
const { execute } = await firstTx.builder.versionMultiBuild({ txVersion, extraPreBuildData: restTxs });
const { txIds } = await execute({ sequentially: true });
```

在发送前进行模拟。使用 `accounts.addresses` 的 `simulateTransaction` 返回执行后的 lamport 余额，这是确认算术与集群实际执行内容匹配的最便宜方式。

## 你应该现在回收还是等待？

两者都可以，差异很小：

* **超额不会消失。** 它保留在你自己的账户中。没有过期时间，没有扫描，没有截止日期。
* **等待会复利。** 每个步骤从相同的账户释放更多，步骤 5 后的一次扫描花费的费用与今天的一次扫描相同。
* **现在回收不会放弃后续步骤。** 你今天扫描的账户只是处于当前最低值；下一步会再次使其超额资助，你可以再次扫描它。

现在回收的唯一真实成本是基本费用，等待的唯一真实成本是 lamports 保持不动一段时间。

## 进一步阅读

<CardGroup cols={2}>
  <Card title="SIMD-0437" icon="file-code" href="https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0437-incremental-rent-reduction.md">
    提案本身——五个特性门和分阶段削减的理由。
  </Card>

  <Card title="降低的租金" icon="book" href="https://solana.com/upgrades/reduced-rent">
    Solana 的推出页面：当前步骤、时间表和新账户的变化。
  </Card>

  <Card title="租金削减：数据支持的分析" icon="chart-line" href="https://solana.com/news/rent-reduction-deep-dive">
    经济学和分阶段推出旨在管理的状态增长风险。
  </Card>

  <Card title="账户模型" icon="database" href="/zh/solana-fundamentals/account-model">
    Solana 账户如何被资助、拥有和关闭——上述所有内容的背景。
  </Card>
</CardGroup>
