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

# LaunchLab 曲线规则

> 平台如何约束创建者可以选择的启动参数：检查组、(字段、操作、值)约束模型，以及九个实战方案——精确分级、价值区间、毕业估值上限、迁移下限、代币类型门控和时间限制的促销。

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

  [查看英文版 →](/products/launchlab/curve-rules)
</Info>

<Info>
  **曲线规则**是平台对"我愿意托管哪些启动"的回答。[`GlobalConfig`](/zh/products/launchlab/global-config) 设置协议下限——至少 10M 供应量、至少 20% 的供应量在曲线上销售等——这些下限故意设得很宽，以便每种平台都能符合。曲线规则是你的平台将其缩小到你的产品实际支持的形状的地方。

  规则存储在自己的 `PlatformCurveRule` 账户中，每个（平台、[`GlobalConfig`](/zh/products/launchlab/global-config)）对应一个。它们只能缩小配置已允许的内容；规则永远无法扩大协议限制。
</Info>

## 心智模型

从外到内三个层级：

```
GlobalConfig       协议下限      "supply >= 10M, sell rate >= 20%, ..."
  └─ PlatformCurveRule   你的规则     "在我的平台上：这些形状之一"
       └─ check group    一个形状     "supply = 1B AND 筹资在 [80, 90] SOL"
            └─ constraint 一个检查    "(Supply, Eq, 1_000_000_000)"
```

两个层级的嵌套使这变得富有表现力：

* **一个组内的约束是 AND 的。** 所有约束都必须满足。
* **一个规则内的组是 OR 的。** 启动一旦满足任何单个组就被允许。

所以一个组是一个允许的*形状*，规则是你提供的形状菜单。一个规则最多可以有 10 个组，一个组最多 25 个约束。

有两个边界情况值得记住：

| 状态             | 含义                   |
| -------------- | -------------------- |
| 没有规则账户，或规则有零个组 | 平台不限制该配置的启动参数。       |
| 一个有零个约束的组      | 该组匹配每个启动，所以整个规则允许一切。 |

规则仅在 `PlatformConfig.restrict_curve_param` 为 `1` 时生效。在 `0` 时，程序根本不读取规则，这也是你用来推出规则和回滚规则的开关。

## 约束

约束是一个 `(field, op, value)` 三元组。仅此而已——没有表达式，没有嵌套。

```rust theme={null}
pub struct ParamConstraint {
    pub field: u8,    // 哪个启动参数，见下表
    pub op:    u8,    // 0 Eq, 1 Gte (min), 2 Lte (max), 3 Neq
    pub value: u128,
}
```

**范围是同一组内同一字段上的两个约束**：一个 `Gte` 作为下限，一个 `Lte` 作为上限。同一个 `(field, op)` 对在一个组内不能出现两次，这防止你写出两个矛盾的最小值。

### 字段

<Info>
  字段 id 是永久的。新字段只会被追加，所以一旦规则账户持有某个 id，它的含义就永远不会改变。
</Info>

| id | 字段                       | 单位          | 备注                                                 |
| -- | ------------------------ | ----------- | -------------------------------------------------- |
| 0  | `CurveType`              | 枚举          | 0 常数乘积，1 固定价格，2 线性价格                               |
| 1  | `MigrateType`            | 枚举          | 新启动必须是 `1`（CPMM）                                   |
| 2  | `MigrateCpmmFeeOn`       | 枚举          | 0 仅报价，1 两个代币                                       |
| 3  | `Supply`                 | 基础单位        | 完整代币供应量                                            |
| 4  | `TotalSellA`             | 基础单位        | 在曲线上销售。仅常数乘积配置——见[曲线类型限制](#曲线类型限制)                 |
| 5  | `TotalFundRaisingB`      | 报价单位        | 毕业目标                                               |
| 6  | `TotalLockedAmount`      | 基础单位        | 归属预算                                               |
| 7  | `CliffPeriod`            | 秒           | 解锁开始前的等待时间                                         |
| 8  | `UnlockPeriod`           | 秒           | 解锁持续时间                                             |
| 9  | `BaseTokenProgram`       | 枚举          | 0 SPL Token，1 Token-2022                           |
| 10 | `TransferFeeEnabled`     | 布尔值         | 1 当基础 mint 携带 `TransferFeeConfig`                  |
| 11 | `TransferFeeBasisPoints` | 1/10 000    | 没有扩展时为 0                                           |
| 12 | `TransferFeeMaximumFee`  | 基础单位        | 没有扩展时为 0                                           |
| 13 | `SellRateA`              | 1/1 000 000 | 派生：`TotalSellA / Supply`。仅常数乘积                     |
| 14 | `LockRate`               | 1/1 000 000 | 派生：`TotalLockedAmount / Supply`                    |
| 15 | `MigrateAmountA`         | 基础单位        | 派生：`Supply − TotalSellA − TotalLockedAmount`。仅常数乘积 |
| 16 | `MigrateRateA`           | 1/1 000 000 | 派生：`MigrateAmountA / Supply`。仅常数乘积                 |
| 17 | `FundRaisingRateB`       | 1/1 000 000 | 派生：`TotalFundRaisingB / Supply`                    |
| 18 | `UnixTimestamp`          | 秒           | 启动的区块时间                                            |

派生字段是使规则可移植的字段。将 `Supply` 和 `TotalFundRaisingB` 固定到精确数字会固定一个启动形状；约束 `FundRaisingRateB` 会固定它们之间的*关系*，让创建者选择任何保持该关系的供应量。

<Warning>
  速率字段只能在一个 `GlobalConfig` 内比较，因为它们的分母取决于该配置的报价 mint 及其小数位。这在实践中不是限制：规则在构造上就限定在一个配置。
</Warning>

## 九个实战方案

下面的每个方案都是一个规则。约束写作 `(field, op, value)`。

### 1. 一个标准分级

最简单的规则，也是已弃用的曲线参数白名单提供的精确行为：一个形状，固定。

| 组 | 约束                                                                                                |
| - | ------------------------------------------------------------------------------------------------- |
| 0 | `(Supply, Eq, 1_000_000_000e6)`、`(TotalSellA, Eq, 800_000_000e6)`、`(TotalFundRaisingB, Eq, 85e9)` |

任何偏离这三个中任何一个的启动都会被拒绝，错误为 `CurveParamNotMatchPlatformRule`。

### 2. 区间而非数字

区间存在的原因：创建者选择你满意的筹资目标，而无需你列举每个值。

| 组 | 约束                                                                                                 |
| - | -------------------------------------------------------------------------------------------------- |
| 0 | `(Supply, Eq, 1_000_000_000e6)`、`(TotalFundRaisingB, Gte, 50e9)`、`(TotalFundRaisingB, Lte, 200e9)` |

一个组，四个约束，创建者有 50–200 SOL 的走廊。在旧白名单下，这需要每个允许值一个条目，十个条目的上限使其不可能。

### 3. 并排的分级

组是 OR 的，所以每个分级是一个组。

| 组 | 约束                                                                 | 分级 |
| - | ------------------------------------------------------------------ | -- |
| 0 | `(Supply, Eq, 1_000_000_000e6)`、`(TotalFundRaisingB, Eq, 85e9)`    | 标准 |
| 1 | `(Supply, Eq, 100_000_000e6)`、`(TotalFundRaisingB, Eq, 30e9)`      | 小型 |
| 2 | `(Supply, Eq, 10_000_000_000e6)`、`(TotalFundRaisingB, Gte, 500e9)` | 鲸鱼 |

顺序对计算有影响，对语义无影响：评估在第一个匹配的组处停止，所以把最常用的分级放在前面。

### 4. 毕业估值区间

`FundRaisingRateB` 是 `TotalFundRaisingB / Supply` 的百万分之一。约束它会限制代币毕业的丰富程度，无论创建者选择的供应量如何。

| 组 | 约束                                                                   |
| - | -------------------------------------------------------------------- |
| 0 | `(FundRaisingRateB, Gte, 60_000)`、`(FundRaisingRateB, Lte, 120_000)` |

使用 1e12 供应量和 9 位小数的报价 mint，`85e9 / 1e12 × 1e6 = 85_000` 位于该区间内。加倍供应量的创建者必须大致加倍目标以保持在其中——这就是重点。两个约束替代了原本需要的 `(supply, target)` 对表。

### 5. 迁移下限

`MigrateRateA` 是实际进入 CPMM 池的供应量份额：`Supply − TotalSellA − TotalLockedAmount`，除以供应量。它是毕业池的深度，也是规则出现前唯一没有平台端等价物的协议旋钮。

| 组 | 约束                             |
| - | ------------------------------ |
| 0 | `(MigrateRateA, Gte, 150_000)` |

至少 15% 的供应量进入池。创建者不能在曲线上销售 95% 并留下浅薄的账簿。

<Note>
  如果参数不相加——锁定金额大于曲线销售后剩余的——派生值无法计算，约束失败关闭，所以启动被拒绝而不是默默允许。
</Note>

### 6. 你实际执行的归属

`GlobalConfig.max_lock_rate` 从上方限制归属。规则可以在其下放置下限，并要求真正的悬崖。

| 组 | 约束                                                                                   |
| - | ------------------------------------------------------------------------------------ |
| 0 | `(LockRate, Gte, 50_000)`、`(LockRate, Lte, 200_000)`、`(CliffPeriod, Gte, 2_592_000)` |

5% 到 20% 的供应量归属，至少 30 天的悬崖。对于其宣传是"无即时解锁启动"的平台很有用。

### 7. 代币类型门控

`BaseTokenProgram` 和 `TransferFeeEnabled` 是独立的，这很重要：没有 `TransferFeeConfig` 的 Token-2022 mint 报告 `TransferFeeEnabled = 0`，就像 SPL Token mint 一样。

| 意图              | 组 | 约束                                                        |
| --------------- | - | --------------------------------------------------------- |
| 仅 SPL Token     | 0 | `(BaseTokenProgram, Eq, 0)`                               |
| 仅 Token-2022    | 0 | `(BaseTokenProgram, Eq, 1)`                               |
| Token-2022，无转账费 | 0 | `(BaseTokenProgram, Eq, 1)`、`(TransferFeeEnabled, Eq, 0)` |
| 无转账费，任一程序       | 0 | `(TransferFeeEnabled, Eq, 0)`                             |

### 8. 条件转账费上限

没有"if"操作符，也不需要——两个组表达条件。

| 组 | 约束                                                                                                       | 读作                  |
| - | -------------------------------------------------------------------------------------------------------- | ------------------- |
| 0 | `(TransferFeeEnabled, Eq, 0)`                                                                            | 无转账费：可以             |
| 1 | `(TransferFeeEnabled, Eq, 1)`、`(TransferFeeBasisPoints, Lte, 100)`、`(TransferFeeMaximumFee, Gte, 1_000)` | 有费用：最多 1%，且最大费用实际设置 |

零速率 `TransferFeeConfig` 不会通过第 0 组：扩展存在，所以 `TransferFeeEnabled` 是 `1`，只有第 1 组可以接受它。

### 9. 时间限制的促销，提前安排

`UnixTimestamp` 是启动的区块时间，所以一个组可以携带自己的有效期窗口。你今天写两个组，切换在自己发生。

| 组 | 约束                                                                     | 窗口             |
| - | ---------------------------------------------------------------------- | -------------- |
| 0 | `(UnixTimestamp, Lte, 1_767_225_600)`、`(TotalFundRaisingB, Gte, 30e9)` | 促销期：30 SOL 最小值 |
| 1 | `(UnixTimestamp, Gte, 1_767_225_601)`、`(TotalFundRaisingB, Gte, 80e9)` | 之后：80 SOL 最小值  |

边界处不需要交易。成本是两个组槽而不是一个。

## 曲线类型限制

四个字段读取 `TotalSellA`：`TotalSellA`、`SellRateA`、`MigrateAmountA` 和 `MigrateRateA`。在常数乘积配置上，创建者提供该数字。在固定价格或线性价格配置上，曲线派生它，程序比较的值是 `0`，这会拒绝每个启动。

与其让你写一个默默阻止自己配置的规则，程序在非常数乘积配置上的写入时拒绝这四个字段，错误为 `CurveRuleFieldNotSupportedByCurve`。目前只存在常数乘积配置，所以实际上你不会遇到此错误。

## 发送前检查

链上检查的两个方向都可以离线获得，所以创建者和平台都不必通过观看交易回滚来学习规则。

<Info>
  **版本横幅。**

  * SDK：`@raydium-io/raydium-sdk-v2@0.2.42-alpha` 是本网站上所有其他代码演示固定的版本。下面的两个助手随着 SDK 发布曲线规则支持而到达；在那之前，从程序的 `platform_curve_rule.rs` 移植它们或调用程序并读取错误代码。
  * 集群：先在 Solana `devnet` 上测试——见[先在 devnet 上测试](#先在-devnet-上测试)。
  * 程序 ID：见[`reference/program-addresses`](/zh/reference/program-addresses)

  两个助手都是纯函数。它们不接触 RPC，所以在表单的每次按键上运行都是安全的。
</Info>

### 启动前：这些参数会通过吗？

`checkLaunchAgainstCurveRule` 精确镜像程序的启动时检查，包括其失败关闭行为。在你的启动表单中运行它，你可以用原因禁用提交按钮，而不是让创建者为回滚的交易付费。

```ts theme={null}
import {
  checkLaunchAgainstCurveRule,
  getPdaPlatformCurveRule,
  LaunchpadCurveRuleBaseTokenProgram,
  LaunchpadCurveRuleField,
  PlatformCurveRule,
} from "@raydium-io/raydium-sdk-v2";
import BN from "bn.js";

const platformConfig = await raydium.launchpad.getPlatformConfig(platformConfigId);

// 在 0 时程序根本不读取规则，所以没有什么要检查的
if (platformConfig.restrictCurveParam !== 0) {
  const ruleId = getPdaPlatformCurveRule(programId, platformConfigId, configId).publicKey;
  const ruleAccount = await connection.getAccountInfo(ruleId);

  const result = checkLaunchAgainstCurveRule({
    // 当账户不存在时为 undefined——平台没有为此配置写规则
    rule: ruleAccount === null ? undefined : PlatformCurveRule.decode(ruleAccount.data),
    context: {
      curveType: globalConfig.curveType,
      migrateType: 1,
      migrateCpmmFeeOn: 0,
      supply: new BN("1000000000000000"),
      totalSellA: new BN("793100000000000"),
      totalFundRaisingB: new BN("85000000000"),
      totalLockedAmount: new BN(0),
      cliffPeriod: new BN(0),
      unlockPeriod: new BN(0),
      baseTokenProgram: LaunchpadCurveRuleBaseTokenProgram.SplToken,
      // 当基础 mint 不携带转账费扩展时完全省略 transferFee
      unixTimestamp: new BN(Math.floor(Date.now() / 1000)),
    },
  });

  if (!result.ok) {
    // 每个组都拒绝了启动；每个条目列出该组的所有失败约束
    for (const group of result.groupFailures) {
      for (const c of group.unsatisfied) {
        console.log(
          `group ${group.groupId}: ${LaunchpadCurveRuleField[c.field]} is ${c.actual ?? "not computable"},`,
          `rule wants op ${c.op} ${c.value.toString()}`,
        );
      }
    }
  }
}
```

助手重现而不是近似的三件事：

* **缺失的规则账户和没有组的规则都通过。** 没有约束的组也是。为不存在的账户传递 `rule: undefined`；不要将其视为拒绝。
* **不可计算的值失败关闭。** 零供应量没有速率，锁定金额大于曲线销售后剩余的没有迁移金额。`actual` 返回 `undefined`，约束计为不满足，完全如链上。
* **报告所有失败的约束，不仅仅是第一个。** 程序短路因为它只需要一个判决；助手收集一切，所以你的表单可以一次列出每个问题。

它无法知道的唯一事情是你的交易实际着陆的区块时间。如果规则在边界附近使用 `UnixTimestamp`，将通过视为临时。

### 写规则前：这个组有效吗？

`checkCurveRuleGroupWritable` 镜像 `UpdatePlatformCurveRule` 的写入时验证——约束 id、重复 `(field, op)` 规则、两个计数限制和曲线类型限制。在签名前在你的平台管理工具中运行它。

```ts theme={null}
import {
  checkCurveRuleGroupWritable,
  LaunchpadCurveRuleField,
  LaunchpadCurveRuleOp,
} from "@raydium-io/raydium-sdk-v2";

const constraints = [
  { field: LaunchpadCurveRuleField.Supply, op: LaunchpadCurveRuleOp.Eq, value: new BN("1000000000000000") },
  { field: LaunchpadCurveRuleField.TotalFundRaisingB, op: LaunchpadCurveRuleOp.Gte, value: new BN("50000000000") },
  { field: LaunchpadCurveRuleField.TotalFundRaisingB, op: LaunchpadCurveRuleOp.Lte, value: new BN("200000000000") },
];

const writable = checkCurveRuleGroupWritable({
  groupId: 0,
  constraints,
  curveType: globalConfig.curveType,
  // 规则已持有的 id，所以替换组不会被误认为添加
  existingGroupIds: existingRule?.groups.map((g) => g.groupId) ?? [],
});

// 每个错误都携带交易会失败的程序错误代码
if (!writable.ok) console.log(writable.errors);
```

<Note>
  通过此检查意味着交易不会因格式错误而被拒绝。它对规则是否是你的意思一无所知——一个组可以完全有效，仍然拒绝你的 UI 可以产生的每个启动。这就是上面的启动端检查的用途：写入一个组后，通过 `checkLaunchAgainstCurveRule` 运行你的产品提供的每个形状，并确认每个都仍然找到一个组。
</Note>

### 先在 devnet 上测试

在主网上启用 `restrict_curve_param` 会立即改变你的创建者可以做的事，对每个启动。在触及主网之前，在 devnet 上排练整个序列：

1. 在 devnet 上创建平台配置和规则，并写入你打算发布的相同组。
2. 通过 `checkLaunchAgainstCurveRule` 运行你的 UI 可以产生的每个启动形状，并确认判决是你期望的——应该通过的形状和应该被拒绝的形状。
3. 启用 `restrict_curve_param`，然后实际启动一个应该通过的代币和一个应该被拒绝的。第二个应该失败，错误为 `CurveParamNotMatchPlatformRule`（`6025`），而不是 `NotEnoughRemainingAccounts`（`6018`）——后者意味着你的构建器没有追加规则 PDA，检查没有真正被执行。
4. 仅在主网上重复，顺序相同。

将 SDK 指向 devnet，加载时使用 `cluster: "devnet"`，并从[`reference/program-addresses`](/zh/reference/program-addresses)获取 devnet 程序 ID。

第 3 步是值得坚持的。离线助手和链上程序是相同规则的两个实现，devnet 启动是证明它们对你的规则一致的——包括你的启动构建器是否完全传递账户。

## 操作规则

### 委托管理器

编辑规则是日常工作；平台管理员密钥通常是多签。`PlatformConfig.curve_rule_manager` 正是为此而存在：通过 `UpdatePlatformConfig::CurveRuleManager` 设置一次，该热钱包就可以自己创建、更新、删除和关闭规则账户。平台管理员保留相同的权力并行，所以丢失的管理器密钥是可恢复的——用另一个管理员调用轮换它。

受损管理器密钥的范围：它可以放松或删除你的参数规则，可以回收规则账户的租金。它不能接触费用钱包、归属、CPMM 配置、不能翻转 `restrict_curve_param`，也不能破坏 `GlobalConfig` 限制。将其视为配置密钥，而不是金库密钥。

### 租金跟随内容

规则账户创建时不持有组，每次更改时调整大小，所以你为实际写入的规则付费。删除组会将差额退款给签名者。

| 规则内容          | 账户大小     | 租金           |
| ------------- | -------- | ------------ |
| 空（刚创建）        | 150 字节   | \~0.0019 SOL |
| 1 组，2 约束      | 200 字节   | \~0.0023 SOL |
| 3 组，每组 4 约束   | 408 字节   | \~0.0037 SOL |
| 10 组，每组 25 约束 | 4 790 字节 | \~0.034 SOL  |

### 推出顺序

1. 在 devnet 上排练整个序列——见[先在 devnet 上测试](#先在-devnet-上测试)。
2. 创建规则账户并写入其组。还没有任何改变——`restrict_curve_param` 仍为 `0` 时程序不读取它们。
3. 用 [`checkLaunchAgainstCurveRule`](#启动前这些参数会通过吗) 离线检查规则：对于你的 UI 可以产生的每个启动形状，确认某个组接受它。
4. 将 `restrict_curve_param` 设置为 `1`。从那一刻起，你的创建者的启动被检查。
5. 要回滚，再次将其设置为 `0`。规则账户保持完整。

<Warning>
  当 `restrict_curve_param` 为 `1` 时，你的启动构建器必须将规则 PDA 追加到 `remaining_accounts`。程序要求账户存在，即使它还不存在，所以创建者不能通过省略它来跳过检查——缺失账户是 `NotEnoughRemainingAccounts`，而不是通过。派生是 `[b"platform_curve_rule", platform_config, global_config]`。
</Warning>

## 启动时成本

检查在启用时对每个启动运行，所以其计算成本是每个启动的税。端到端测量——PDA 派生、`remaining_accounts` 扫描、反序列化和评估：

| 规则内容          | 便宜字段   | 派生字段   |
| ------------- | ------ | ------ |
| 启用，无规则账户      | 1 936  | —      |
| 1 组，2 约束      | 2 576  | 2 926  |
| 3 组，每组 4 约束   | 3 624  | 5 724  |
| 10 组，每组 25 约束 | 24 344 | 68 094 |

"便宜"是直接字段读取，如 `Supply`；"派生"是计算的，如 `MigrateRateA`，每个约束成本约 223 CU，而直接读取约 48。即使是完全加载的派生约束规则也保持在默认 200 000 CU 每指令预算的三分之一以内，现实的三组规则在 6 000 以下。组被评估直到一个匹配，所以将你的常见分级放在前面是免费节省。

## 接下来去哪里

* [`products/launchlab/platform-config`](/zh/products/launchlab/platform-config) — 门控和委托规则的 `PlatformConfig` 字段。
* [`products/launchlab/global-config`](/zh/products/launchlab/global-config) — 规则缩小的协议下限。
* [`products/launchlab/instructions`](/zh/products/launchlab/instructions) — 四个规则指令及其账户。
* [`products/launchlab/accounts`](/zh/products/launchlab/accounts) — 账户清单中的 `PlatformCurveRule`。
* [`sdk-api/typescript-sdk`](/zh/sdk-api/typescript-sdk) — 两个检查助手所在的 SDK 表面。
* [`reference/changelog/2026-08-31-launchlab-platform-curve-rules`](/zh/reference/changelog/2026-08-31-launchlab-platform-curve-rules) — 替代曲线参数白名单的内容，以及解码器必须改变的内容。

来源：

* `raydium-launch/programs/launchpad/src/states/platform_curve_rule.rs` — `PlatformCurveRule`、`CurveRuleGroup`、`ParamConstraint`、字段和操作 id 空间，以及 `CurveRuleContext::value_of`。
* `raydium-launch/programs/launchpad/src/utils/platform_curve_rule.rs` — 启动时检查。
* `raydium-launch/programs/launchpad/src/instructions/platform/` — `create`、`update`、`remove` 和 `close_platform_curve_rule`。
