拒绝利率波动!用代码把「固定收益」写进合约:固定利率协议开发实录

10 阅读6分钟

前言

本文聚焦去中心化固定利率协议,一方面梳理其理论体系,具体涵盖核心定义与定位、主流实现机制、典型协议与应用场景、核心价值与局限;另一方面依托 OpenZeppelin V5 和 Solidity 0.8.24,完整实现协议合约的开发、测试与部署落地全流程。

去中心化固定利率协议

一、核心定义与定位

  • 核心目标:锁定资金成本 / 收益,消除利率波动不确定性,适配机构与稳健型用户的低风险需求。
  • 关键特征:去中心化(无中介)、智能合约自动执行、利率预先确定、到期按约定清算。
  • 与浮动利率协议区别:浮动利率(如 Aave、Compound)随供需实时调整,固定利率通过金融工程锁定到期收益 / 成本。

二、主流实现机制

机制类型核心逻辑代表协议用户流程
零息债券代币化将借贷现金流转化为可交易零息债券(如 fCash、fyDai),折价发行隐含固定利率Notional、Yield Protocol1. 出借人用稳定币折价买债券代币;2. 借款人抵押资产铸造债券代币换稳定币;3. 到期按面值兑付,折价 = 利息
收益拆分(PT/YT)拆分未来收益为 “本金代币(PT)” 和 “收益代币(YT)”,PT 持有者获固定收益Pendle Finance1. 存入计息资产(如 aUSDC)拆分为 PT+YT;2. 持有 PT 到期取回本金 + 固定利息;3. YT 可交易,承担浮动收益波动
风险分级结构化资金池分优先级 / 劣后级,优先级获固定收益,劣后级承担风险并获剩余收益BarnBridge、Saffron1. 优先级资金(如 AA 档)获约定固定利率;2. 劣后级(如 S 档)吸收波动,博取高收益;3. 协议动态平衡风险与收益
利率互换 / 远期合约通过合约交换浮动与固定利率现金流,对冲波动Pledge、Dank Protocol1. 双方约定利率互换期限与固定利率;2. 浮动利率端按市场计息,固定利率端按约定支付;3. 到期轧差结算

三、典型协议与应用场景

  1. Notional Finance

    • 核心:fCash 零息债券,支持多资产(ETH、WBTC、稳定币),期限灵活(1 天 - 5 年)。
    • 场景:锁定长期借款利率(如偿还房贷)、固定收益理财。
  2. Pendle Finance

    • 核心:PT/YT 拆分,支持任意计息资产(借贷、质押、流动性挖矿)。
    • 场景:将浮动收益转化为固定收益、对冲 DeFi 收益波动。
  3. BarnBridge

    • 核心:SMART Alpha 分级衍生品,分离价格波动与利率风险。
    • 场景:机构资金低风险配置、固定收益 + 波动收益组合投资。

四、核心价值与局限

  • 核心价值

    • 风险对冲:锁定利率,规避 DeFi 利率剧烈波动。
    • 财务规划:可预测现金流,适配长期借贷 / 理财需求。
    • 机构友好:降低传统金融进入 DeFi 的门槛。
  • 主要局限

    • 流动性成本:固定期限导致资金灵活性降低,二级市场深度影响退出成本。
    • 利率定价风险:市场剧烈波动可能导致固定利率偏离实际收益,存在机会成本。
    • 智能合约风险:代码漏洞、预言机攻击可能造成损失。

五、总结

去中心化固定利率协议通过零息债券、收益拆分、风险分级等金融工程手段,在 DeFi 中提供确定性利率,是浮动利率体系的重要补充,适用于追求稳定收益的用户与机构。随着 DeFi 生态成熟,固定利率协议有望成为连接传统金融与去中心化金融的关键基础设施。


智能合约开发、测试、部署


智能合约

  • 代币合约
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

/**
 * @dev 测试网专用 USDT,任意人都能 mint
 */
contract TestUSDT is ERC20 {
    uint8 private _decimals;

    constructor(
        string memory name,
        string memory symbol,
        uint8 decimals_
    ) ERC20(name, symbol) {
        _decimals = decimals_;
    }

    function decimals() public view override returns (uint8) {
        return _decimals;
    }

    function mint(address to, uint256 amount) external {
        _mint(to, amount);
    }
}
  • 固定利率协议
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title FixedRateVault
 * @dev 简易固定利率协议:折价购入,到期按面值赎回
 */
contract FixedRateVault is ERC20, Ownable {
    using SafeERC20 for IERC20;

    IERC20 public immutable underlying; // 基础资产,如 USDC
    uint256 public immutable maturity;  // 到期时间戳
    uint256 public immutable fixedRate; // 年化利率基点 (1% = 100 bps)

    constructor(
        address _underlying,
        uint256 _duration,
        uint256 _fixedRate
    ) ERC20("Fixed Rate Token", "fToken") Ownable(msg.sender) {
        underlying = IERC20(_underlying);
        maturity = block.timestamp + _duration;
        fixedRate = _fixedRate;
    }

    /**
     * @notice 存款并锁定固定利率
     * @param assets 存入的基础资产数量
     */
    function deposit(uint256 assets) external {
        require(block.timestamp < maturity, "Vault expired");
        
        // 计算折价后的份额 (简化模型:shares = assets / (1 - discount))
        // 实际应用中需根据剩余时间计算精确折现率
        uint256 shares = (assets * 10000) / (10000 - (fixedRate / 4)); // 假设固定周期
        
        underlying.safeTransferFrom(msg.sender, address(this), assets);
        _mint(msg.sender, shares);
    }

    /**
     * @notice 到期后 1:1 赎回基础资产
     */
    function redeem(uint256 shares) external {
        require(block.timestamp >= maturity, "Not matured yet");
        _burn(msg.sender, shares);
        underlying.safeTransfer(msg.sender, shares);
    }
}

测试脚本

测试用例:协议设定 30 天固定利率周期,支持用户完成存款操作,并在锁仓周期结束后可赎回本金及对应固定收益。

import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { network } from "hardhat";
import { parseUnits } from "viem";
describe("FixedRateVault 测试", function () {
  async function deployFixture() {
     // 获取测试客户端
    const { viem } = await network.connect();
    const [owner, user] = await viem.getWalletClients();
    const publicClient = await viem.getPublicClient();
     const testClient = await viem.getTestClient();
    const USDC_DECIMALS = 6;
    // 1. 部署模拟代币 (Mock USDT)
    const mockUSDT = await viem.deployContract("TestUSDT", ["USDC", "USDC", USDC_DECIMALS]);
    // 2. 部署固定利率合约 (30天到期, 5% 年化)
    const duration = 30 * 24 * 60 * 60;
    const vault = await viem.deployContract("FixedRateVault", [
      mockUSDT.address,
      BigInt(duration),
      500n
    ]);
    const reserveAmount = parseUnits("1000", 18); 
    await mockUSDT.write.mint([vault.address, reserveAmount]);
    return { vault, mockUSDT, owner, user, publicClient, duration,testClient };
  }

  it("应允许用户存款和到期后赎回", async function () {
    const { vault, mockUSDT, user, publicClient, duration,testClient } = await deployFixture();
    const depositAmount = parseUnits("100", 6);

    // 准备资金
    await mockUSDT.write.mint([user.account.address, depositAmount]);
    await mockUSDT.write.approve([vault.address, depositAmount], { account: user.account });

    // 执行存款
    await vault.write.deposit([depositAmount], { account: user.account });

    // 验证持有的 fToken 数量 (应多于 100,因为是折价购买)
    const fBalance = await vault.read.balanceOf([user.account.address]);
    assert.ok(fBalance > depositAmount, "fToken balance should be higher than deposit due to discount");
    // 把链上时间推进(30 天)
    await testClient.increaseTime({ seconds: duration});
    await testClient.mine({ blocks: 1 });
    // 赎回
    await vault.write.redeem([fBalance], { account: user.account });

    // 验证赎回后的 USDT 余额
    const finalBalance = await mockUSDT.read.balanceOf([user.account.address]);
    assert.strictEqual(finalBalance, fBalance, "Should redeem 1:1 at maturity");
    console.log(`Profit realized: ${finalBalance - depositAmount} wei`);
  });
});

部署脚本

// scripts/deploy.js
import { network, artifacts } from "hardhat";
async function main() {
  // 连接网络
  const { viem } = await network.connect({ network: network.name });//指定网络进行链接
  
  // 获取客户端
  const [deployer] = await viem.getWalletClients();
  const publicClient = await viem.getPublicClient();
 
  const deployerAddress = deployer.account.address;
   console.log("部署者的地址:", deployerAddress);
  // FixedRateVault加载合约
  const TestUSDTArtifact = await artifacts.readArtifact("TestUSDT");
  const TestUSDTDeployHash = await deployer.deployContract({
    abi: TestUSDTArtifact.abi,//获取abi
    bytecode: TestUSDTArtifact.bytecode,//硬编码
    args: ["USDT", "USDT", 6],//process.env.RECIPIENT, process.env.OWNER
  });
  const TestUSDTReceipt = await publicClient.waitForTransactionReceipt({ hash: TestUSDTDeployHash });
  console.log("模拟USDT合约地址:", TestUSDTReceipt.contractAddress);
  const FixedRateVaultArtifact = await artifacts.readArtifact("FixedRateVault");

  // 部署(构造函数参数:recipient, initialOwner)
  const  FixedRateVaultHash = await deployer.deployContract({
    abi: FixedRateVaultArtifact.abi,//获取abi
    bytecode: FixedRateVaultArtifact.bytecode,//硬编码
    args: [TestUSDTReceipt.contractAddress, BigInt(30 * 24 * 60 * 60), 500n],//
  });//资产,时间,年化率5%

  // 等待确认并打印地址
  const FixedRateVaultReceipt = await publicClient.waitForTransactionReceipt({ FixedRateVaultHash });
  console.log("去中心化固定利率协议合约地址:", FixedRateVaultReceipt.contractAddress);
  
}

main().catch(console.error);

结语

至此,去中心化固定利率协议的理论解析与代码实践已全部完成!无论是协议的核心逻辑、主流玩法,还是基于 Solidity 的合约开发、测试部署,我们都逐一拆解落地。愿这份理论 + 实操的内容,能帮你真正吃透去中心化固定利率协议的核心玩法。