# 双重 Stacking 智能合约

## 概述

双重叠加合约允许参与者通过持有 sBTC，并可选择叠加 STX，来获得增强的 sBTC 奖励。它按周期运行，并通过定期快照根据持仓和叠加参与情况计算奖励。

{% hint style="info" %}
要查看主网上正在运行的双重叠加合约，请访问合约页面 [这里](https://explorer.hiro.so/txid/SP1HFCRKEJ8BYW4D0E3FAWHFDX8A25PPAA83HWWZ9.dual-stacking-v1?chain=mainnet).

2025 年 12 月 15 日，双重叠加合约已升级为 [`.dual-stacking-v2_0_2`](https://explorer.hiro.so/txid/SP1HFCRKEJ8BYW4D0E3FAWHFDX8A25PPAA83HWWZ9.dual-stacking-v2_0_2?chain=mainnet)
{% endhint %}

### 去中心化架构

* 无许可操作：任何人都可以执行关键的周期操作，包括快照捕获、比例提议/验证、权重计算和奖励分发。
* 仅链上数据：所有参与者数据（sBTC 余额、STX 叠加数量）都直接从区块链读取——不需要链下预言机或可信数据源。
* 竞争性比例发现：多个参与者可以提出不同的黄金比例；系统依据数学标准（第 95 百分位）进行验证，而不是管理员批准。
* 透明执行：所有操作都在链上执行，结果可验证，并带有公开事件日志。
* 自助式加入：用户可以独立报名、退出并管理自己的参与情况。

### 主要操作

{% stepper %}
{% step %}
**初始化**

合约只初始化一次，使用一个 Stacks 区块高度参数；该高度为所指定比特币区块中的第一个对应区块，或者在没有任何 STX 区块锚定到该比特币区块时，取其之后的第一个对应区块。
{% endstep %}

{% step %}
**报名**

用户可自行报名参与，并自定义奖励地址。DeFi 协议可由管理员以自定义的跟踪地址、叠加地址和奖励地址进行报名。所有参与者都可以随时退出或更改地址。
{% endstep %}

{% step %}
**快照与周期**

任何人都可以触发定期快照，根据预定义的区块间隔，从链上数据中捕获参与者的 sBTC 余额和 STX 叠加数量。
{% endstep %}

{% step %}
**比例计算**

快照完成后，任何人都可以提议一个黄金比例（最优 STX/sBTC 比例），统计参与者分布，并验证其提议是否满足第 95 百分位标准，以确定最大奖励基准。
{% endstep %}

{% step %}
**权重计算**

任何人都可以使用经过验证的双重叠加公式触发参与者权重计算；对于达到或超过黄金比例的人，可获得最高 10 倍提升（可配置）。
{% endstep %}

{% step %}
**奖励**

任何人都可以在每个周期根据计算出的权重触发奖励分发。管理员可以更新 APR、收益提升倍数、快照长度以及每个周期的快照数量等配置。
{% endstep %}

{% step %}
**管理控制**

管理员维护协议参数，以特殊地址配置报名/退出 DeFi 协议，管理白名单和黑名单，并可在需要时执行紧急操作。
{% endstep %}
{% endstepper %}

***

## 周期结构

* 每个周期由固定数量的快照组成（默认 14 个）。
* 每个快照在经过设定数量的比特币区块后发生（默认 150 个）。
* 因此，默认情况下整个周期长度为 2100 个比特币区块（14 个快照 × 150 个区块）。
* 这些默认值可针对生产环境进行调整（例如，每天 1 个快照，并设置相应的区块数量）。

## 双重叠加公式

权重计算：

$$
w\_i = \cfrac{\[B\_i \cdot (1 + M \cdot \sqrt{r\_i})]}{n}
$$

其中：

* *w*<sub>*i*</sub>*&#x20;= 用户 i 的权重*
* *B*<sub>*i*</sub>*&#x20;= 用户 i 的 sBTC 余额（跨所有快照求和）*
* *M = 收益提升倍数（默认 9，表示最高 10 倍提升）*
* *r*<sub>*i*</sub>*&#x20;= min(d*<sub>*i*</sub>*/D, 1)，比例调整因子*
* *d*<sub>*i*</sub>*&#x20;= S*<sub>*i*</sub>*/B*<sub>*i*</sub>*，用户的个人 STX/sBTC 比例*
* *S*<sub>*i*</sub>*&#x20;= 用户 i 叠加的 STX 数量（跨所有快照求和）*
* *D = 黄金比例（所有参与者中 STX/sBTC 比例的第 95 百分位）*
* *n = 每个周期的快照数量（默认 14）*

奖励分配：

$$
R\_i = (\frac{w\_i}{Σw}) \cdot Total Rewards
$$

其中：

* *R*<sub>*i*</sub>*&#x20;= 用户 i 的奖励*
* *Σw = 所有参与者权重之和*
* *总奖励 = min(资金池余额，基于 APR 的上限)*

关键属性：

* *满足 d*<sub>*i*</sub>*&#x20;≥ D 的参与者可获得最大提升 (M+1) x（默认 10 倍）。*
* *满足 d*<sub>*i*</sub>*&#x20;= 0（未叠加 STX）时获得基础奖励（1 倍）。*
* *在中间值区间，提升幅度按比例的平方根进行缩放。*
* *白名单中的 DeFi 跟踪地址会自动获得最大提升，无需叠加 STX。*
* *权重会除以每个周期的快照数量，以在整个周期长度上进行归一化。*

***

## 周期工作流程

双重叠加智能合约按周期运行，每个周期再划分为多个快照。该流程通过一系列任何人都可执行的无许可操作，确保奖励分配准确。

{% stepper %}
{% step %}
**快照阶段（任何人都可执行）**

* capture-snapshot-balances：任何人都可以在每个快照后为已报名用户捕获余额。
* advance-to-next-snapshot：任何人都可以切换到下一个快照。
* finalize-snapshots：任何人都可以在最后一个快照后结束所有快照数据。
  {% endstep %}

{% step %}
**比例验证阶段（竞争性且无许可）**

* propose-golden-ratio：任何人都可以提出黄金比例。
* tally-participant-ratios：提议者统计参与者相对于其所提比例的比例情况。
* validate-ratio：提议者验证其提案——仅当其代表第 95 百分位时才算成功。
* 可以提交多个提案；第一个有效提案将锁定该周期。
  {% endstep %}

{% step %}
**权重计算阶段（任何人都可执行）**

* calculate-participant-weights：任何人都可以使用双重叠加公式计算参与者权重。
* finalize-weight-computation：任何人都可以完成权重计算。
  {% endstep %}

{% step %}
**奖励分发阶段（任何人都可执行）**

* set-is-distribution-enabled：任何人都可以通过确定可用奖励池来启用奖励分发。
* distribute-rewards：任何人都可以根据权重将奖励分发给已报名用户。
* finalize-reward-distribution：在所有参与者都获得奖励后，任何人都可以完成奖励分发。
  {% endstep %}

{% step %}
**周期切换（任何人都可执行）**

* advance-to-next-cycle：在所有奖励分发完成后，任何人都可以推进到下一个周期。
  {% endstep %}
  {% endstepper %}

注意：所有操作都直接从区块链读取数据（sBTC 余额来自 sBTC 代币合约，STX 叠加来自原生 Stacks 协议）。不需要链下数据源或可信中介。

***

## 公开函数

### 1. 合约初始化

#### initialize-contract

以初始周期激活合约。

* 参数：stx-block-height（uint）
* 断言：
  * 当前比特币区块高度必须大于等于配置的周期起始比特币区块高度。
  * 合约必须未处于激活状态。
  * Stacks 区块高度必须包围所配置的比特币区块高度。
* 效果：
  * 初始化第一个周期的状态变量，并将合约标记为激活。
  * 记录周期数据和第一个快照。

#### update-initialize-block

在合约激活前更新初始化用的比特币区块高度。

* 参数：new-bitcoin-block-height（uint）
* 断言：
  * 合约必须未处于激活状态。
  * 调用者必须是管理员。
* 效果：
  * 更新第一个周期的起始比特币区块高度。

#### update-cycle-data-before-initialized

在初始化前更新第一个周期的每周期快照数和每快照区块数。

* 参数：updated-snapshots-per-cycle（uint），updated-blocks-per-snapshot（uint）
* 断言：
  * 合约必须未处于激活状态。
  * 调用者必须是管理员。
* 效果：
  * 设置第一个周期的周期结构。

***

### 2. 报名

#### enroll

为调用者报名，以便在未来周期获得奖励。

* 参数：rewarded-address（可选 principal）
* 断言：
  * 调用者当前不得已报名。
  * 调用者不得在黑名单中。
  * 调用者必须至少持有最低要求的 sBTC 数量。
* 效果：
  * 将调用者加入参与者映射，并适当设置跟踪、叠加和奖励地址。
  * 增加下一个周期的参与者计数。

#### enroll-defi

为 DeFi 协议报名以获取奖励，并使用自定义地址（仅管理员）。

* 参数：
  * defi-contract（principal）
  * tracking-address（principal）
  * rewarded-address（principal）
  * stacking-address（可选 principal）
* 断言：
  * 调用者必须是管理员。
  * 该 DeFi 合约不得已报名。
  * 该 DeFi 合约不得在黑名单中。
* 效果：
  * 将该 DeFi 协议及其自定义地址加入参与者映射。
  * 增加下一个周期的参与者计数。

#### enroll-defi-batch

批量为多个 DeFi 协议报名（仅管理员）。

* 参数：defi-contracts（list 900 {...}）
* 断言：
  * 调用者必须是管理员。
* 效果：
  * 在单笔交易中为多个 DeFi 协议报名。

#### opt-out

允许调用者退出未来周期的参与。

* 断言：
  * 调用者必须已报名。
* 效果：
  * 将调用者从参与者映射中移除。
  * 减少下一个周期的参与者计数。

#### opt-out-defi

使一个 DeFi 协议退出参与（仅管理员）。

* 参数：defi-contract（principal）
* 断言：
  * 调用者必须是管理员。
  * DeFi 合约必须已报名。
* 效果：
  * 将该 DeFi 协议从参与者映射中移除。

#### opt-out-defi-batch

批量使多个 DeFi 协议退出（仅管理员）。

* 参数：defi-contracts（list 200 principal）
* 断言：
  * 调用者必须是管理员。
* 效果：
  * 在单笔交易中使多个 DeFi 协议退出。

***

### 3. 参与者地址管理

* change-reward-address
* change-reward-address-defi
* change-stacking-address-defi
* change-tracking-address-defi
* change-addresses-defi
* change-addresses-defi-batch

（每个函数都有参数、适用时的管理员断言，以及原始规范中所述的参与者/DeFi 地址更新效果。）

***

### 4. 快照与周期

#### capture-snapshot-balances

在当前快照区块高度为一组参与者捕获快照余额。无许可。

* 参数：principals（最多 900 个 principal 的列表）
* 断言：
  * 合约必须处于激活状态。
  * 当前快照的 Stacks 区块高度必须可用。
* 效果：
  * 从 sBTC 代币合约读取每个参与者的 sBTC 余额。
  * 从原生 Stacks 协议读取 STX 叠加数量（如启用流动性叠加，则包括该部分）。
  * 更新快照总计和参与者持仓。
  * 跟踪叠加和跟踪地址。

#### advance-to-next-snapshot

将合约推进到当前周期内的下一个快照。无许可。

* 参数：new-stx-block-height（uint）
* 断言：
  * 合约必须处于激活状态。
  * 所有参与者都必须已完成快照。
  * 当前比特币区块高度必须已达到下一个快照区块。
  * 周期不得已结束。
  * Stacks 区块高度必须包围下一个快照的比特币区块高度。
* 效果：
  * 递增快照索引。
  * 将快照总计汇总到周期总计中。
  * 重置快照计数器。
  * 记录新的快照区块高度。

#### finalize-snapshots

在最后一个快照完成后，完成当前周期的所有快照。无许可。

* 断言：
  * 合约必须处于激活状态。
  * 快照不得已完成最终确认。
  * 必须处于周期的最后一个快照。
  * 最终快照中所有参与者都必须已完成快照。
* 效果：
  * 将最终快照总计汇总到周期总计中。
  * 将快照标记为已完成最终确认。
  * 将最后操作状态设置为“已结束”。
  * 启用比例提议阶段。

#### advance-to-next-cycle

在所有奖励分发完成后，将合约推进到下一个周期。无许可。

* 参数：stx-block-height（uint）
* 断言：
  * 合约必须处于激活状态。
  * 当前比特币区块高度必须已达到下一个周期。
  * 所有参与者都必须已获得奖励。
  * 奖励分发必须已完成最终确认。
  * Stacks 区块高度必须包围下一个周期的比特币区块高度。
* 效果：
  * 递增周期 ID。
  * 重置新周期的状态变量。
  * 从下一个周期设置更新周期配置。
  * 初始化新周期的第一个快照。

***

### 5. 比例计算与验证

#### propose-golden-ratio

为当前周期提议黄金比例。无许可。

* 参数：ratio（uint）——按 10^8 缩放的提议比例
* 断言：
  * 快照必须已完成最终确认。
  * 该周期不得已存在已验证比例。
  * 调用者不得已为该周期提议过比例。
* 效果：
  * 记录调用者所提议的比例。
  * 初始化参与者统计跟踪。
  * 将最后操作状态设置为“已提议比例”。

#### change-proposed-golden-ratio

在验证前更改先前提议的黄金比例。

* 参数：ratio（uint）
* 断言：
  * 调用者必须已提议过比例。
  * 该比例尚未被验证。
* 效果：
  * 更新提议的比例并重置统计数据。

#### tally-participant-ratios

统计有多少参与者的比例高于、低于或等于所提议的黄金比例。

* 参数：principals（最多 900 个 principal 的列表）
* 断言：
  * 调用者必须已提议过比例。
  * 该比例尚未被验证。
  * 不得已统计完所有参与者。
* 效果：
  * 计算每个参与者的 STX/sBTC 比例。
  * 跟踪高于、低于以及等于所提议比例的 sBTC 数量。
  * 递增已统计参与者数量。

#### validate-ratio

验证所提议的比例代表参与者比例的第 95 百分位。

* 断言：
  * 调用者必须已提议过比例。
  * 所有参与者都必须已统计。
  * 该比例不得已验证。
  * 如果无人叠加 STX，则比例必须等于 1.0（基线）。
  * 高于该比例的 sBTC 必须小于等于总 sBTC 的 5%。
  * 达到或高于该比例的 sBTC 必须大于等于总 sBTC 的 5%。
* 效果：
  * 将该比例标记为已验证。
  * 记录该周期已验证的比例。
  * 将最后操作状态设置为“比例已验证”。

#### set-max-percentage-above-ratio

更新用于验证的百分比阈值（仅管理员）。

* 参数：new-max-percentage-above-ratio（uint）——默认 500 = 5%
* 断言：
  * 调用者必须是管理员。
* 效果：
  * 更新验证阈值。

***

### 6. 权重计算

#### calculate-participant-weights

使用双重叠加公式计算参与者权重。无许可。

* 参数：principals（最多 900 个 principal 的列表）
* 断言：
  * 比例必须已验证。
  * 当前周期的 Stacks 区块高度必须可用。
* 效果：
  * 获取已验证的黄金比例 D。
  * 对 D 应用最小阈值，以防止除零 $$D = max(D, 10^-8)$$.
  * 对每个参与者，使用以下公式计算权重： $$w\_i = \cfrac{\[B\_i \cdot (1 + M \cdot √r\_i)]}{n}$$
  * 将总权重累加到 total-weights-sum 中。
  * 按跟踪地址记录各自权重（不是按已报名地址）。

注意：

* 多个共享同一跟踪地址的已报名地址将共享相同的权重。
* 可按最多 900 名参与者的批次调用。

#### finalize-weight-computation

完成权重计算阶段。无许可。

* 断言：
  * 比例必须已验证。
  * 权重不得已完成最终确认。
  * 所有参与者的权重都必须已计算。
* 效果：
  * 将权重标记为已计算。
  * 将最后操作状态设置为“权重已完成”。
  * 启用奖励分发阶段。

***

### 7. 奖励分发

#### set-is-distribution-enabled

通过确定可用奖励池，为分发奖励做准备。无许可。

* 断言：
  * 合约必须处于激活状态。
  * 分发不得已启用。
  * 权重必须已计算。
* 效果：
  * 读取合约的 sBTC 余额。
  * 计算要分发的奖励：min(资金池余额，基于 APR 的上限)。
  * 上限为： $$(CPR × total-weights-sum) / (M + 1)$$
  * 将奖励标记为可分发。
  * 将最后操作状态设置为“可分发已设置”。

#### distribute-rewards

根据已计算的权重将奖励分发给参与者。无许可。

* 参数：principals（最多 900 个 principal 的列表）
* 断言：
  * 分发必须已启用。
* 效果：
  * 计算每个参与者的奖励：(权重 / 总权重) × 总奖励
  * 将 sBTC 奖励转账到奖励地址。
  * 将参与者标记为已获得奖励。
  * 按奖励地址汇总奖励。

注意：

* 可按最多 900 名参与者的批次调用。
* 多个共享同一奖励地址的已报名地址会汇总奖励，并且每个跟踪地址只触发一次转账。

#### finalize-reward-distribution

将当前周期的奖励分发标记为已完成最终确认。无许可。

* 断言：
  * 合约必须处于激活状态。
  * 所有参与者都必须已获得奖励。
  * 分发必须已启用。
  * 不得已完成最终确认。
* 效果：
  * 记录完成最终确认的区块高度。
  * 将最后操作状态设置为“已完成最终确认”。
  * 启用周期推进。
  * 触发外部 DeFi 协议分发其内部奖励（它们会监听此最终确认事件）。

***

### 8. 管理控制

* 更新管理员
* 更新报名所需的最低 sBTC 持有量
* 更新快照长度
* 更新每周期快照数
* 更新周期数据
* 更新每年比特币区块数
* 更新 APR
* 更新收益提升倍数
* 设置流动性叠加
* 紧急提取 sBTC

（上述每一项都有参数、适用时仅管理员可调用的断言，以及原始规范中描述的效果。值得注意的限制包括 APR 范围和倍数范围。）

***

### 9. 黑名单管理

* 添加黑名单
* 批量添加黑名单
* 移除黑名单
* 批量移除黑名单

（仅管理员可执行的黑名单管理操作；添加已报名地址会自动使其退出。）

***

### 10. DeFi 白名单管理

#### 白名单化 DeFi 跟踪地址

将一个 DeFi 跟踪地址加入白名单（自动获得最大权重提升）。

* 参数：defi-rewards-contract（principal）
* 断言：
  * 调用者必须是管理员。
  * 该地址不得已在白名单中。
* 效果：
  * 将该跟踪地址加入白名单。
  * 白名单地址在权重计算中获得最大提升（r<sub>i</sub> = 1.0）。
  * 在快照期间，白名单地址的 STX 叠加会被记录为 0（它们无需叠加 STX 即可获得最大提升）。

#### 移除白名单化 DeFi 跟踪地址

将一个 DeFi 跟踪地址从白名单中移除（仅管理员）。

* 参数：defi-rewards-contract（principal）
* 断言：
  * 调用者必须是管理员。
  * 该地址必须在白名单中。
* 效果：
  * 从白名单中移除该跟踪地址。

#### 批量移除白名单化 DeFi 跟踪地址

批量将多个 DeFi 跟踪地址从白名单中移除（仅管理员）。

* 参数：defi-rewards-contract（list 200 principal）
* 断言：
  * 调用者必须是管理员。
* 效果：
  * 从白名单中移除多个跟踪地址。

***

## 私有函数

* update-snapshot-for-new-cycle：重置快照计数器并设置初始快照区块高度。
* reset-state-for-cycle：应用下一个周期的配置，重置标志和总计，记录周期数据，更新参与者数量。
* capture-participant-balances：读取快照高度的 sBTC 和 STX 叠加数量，更新持仓并汇总总计。
* calculate-participant-weight：使用双重叠加公式按跟踪地址计算权重（整数数学细节见原始规范）。
* tally-user-ratio：根据提议比例对用户比例进行分类并累加 sBTC 总量。
* distribute-reward-user：按跟踪地址转账奖励并更新已获奖励状态。
* remove-participant：删除参与者并减少计数。
* enroll-defi-one / change-addresses-defi-one：批量操作的辅助函数。
* is-blacklisted：检查是否在黑名单中。

（私有函数实现所描述的效果和整数缩放考虑；有关数学/缩放行为，请参见上文的函数详情。）

***

## 只读函数

### 周期信息

* 获取当前周期 ID
* 周期数据
* 获取当前周期状态
* 当前概览数据
* 获取收益周期数据
* 每年周期数
* 周期百分比率

### 快照信息

* 快照数据
* 获取周期快照的 Stacks 区块高度
* 获取周期快照的比特币区块高度

### 奖励信息

* 获取奖励分发状态
* 是否可分发
* 某周期和地址的奖励金额
* 某周期和奖励地址的奖励金额
* 当前周期的分发是否已完成最终确认
* 获取完成最终确认时的区块高度

### 比例与权重信息

* 获取比例数据
* 获取权重计算状态
* 获取参与者权重

### 参与者信息

* 是否已报名参加下一个周期
* 是否已报名参加当前周期
* 获取是否在黑名单中
* 获取黑名单列表
* 获取是否为白名单 DeFi
* 获取最新奖励地址
* 获取参与者周期信息

### 状态与配置

* 获取最后操作状态
* 获取管理员
* 获取合约是否激活
* 获取当前比特币区块高度
* 获取最低报名金额
* 获取下一操作的比特币高度
* 获取合约 sBTC 余额
* 获取 APR 数据

### STX 叠加查询

* 获取已叠加的 STX 数量
* 获取在指定区块高度叠加的 STX 数量
* 获取在指定区块高度叠加的数量
* 立即获取已叠加数量


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.stacks.co/learn/zh/dual-stacking/contracts.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
