分层收益模型实战:一套代码打通理论到落地全流程

3 阅读6分钟

前言

本文系统梳理了收益分层交易的相关知识体系,具体涵盖其核心定义、核心能力、解决的行业痛点、典型行业应用场景、核心优劣势,以及关键认知要点;在代码落地层面,将基于 Hardhat V3 开发框架,结合 OpenZeppelin V5 库与 Solidity 0.8.24 及以上版本,完整实现该业务从开发、测试到部署的全流程。

一、是什么

DeFi领域基于区块链的结构化金融合约,核心是按风险、兑付优先级拆分资产池为不同层级份额,自动分配交易收益、亏损与清算,全程去中心化、无人工干预,底层依托公链与预言机实现闭环。

核心分层:优先级(低风险低收益,优先兑付)、劣后级(高风险高收益,兜底亏损)、夹层(风险收益居中,可选设)。

二、能做什么与解决的问题

(一)核心能力

  • 拆分风险收益,匹配不同偏好投资者;
  • 链上自动执行分配、清算,结果可审计;
  • 分层份额代币化,支持交易、质押流通;
  • 设定风险阈值,管控极端行情风险。

(二)核心痛点

  • 解决投资者风险偏好与产品不匹配问题;
  • 规避传统结构化产品中心化、不透明、门槛高的弊端;
  • 提升链上资产利用率,整合闲置资金与交易需求;
  • 消除收益分配纠纷,实现透明化执行。

三、行业应用

主要落地于DeFi赛道,核心场景包括:

  1. 去中心化借贷:分层分配借贷利息,平衡风险与收益;
  2. 收益聚合器:拆分多策略收益池,适配不同风险需求;
  3. 衍生品与杠杆交易:保证金池分层,提供流动性与风险备付;
  4. NFTFi:拆分NFT收益池,覆盖租赁、抵押等收益;
  5. 实体资产通证化:分层分配实体资产现金流,降低参与门槛。

四、优劣势

(一)优势

  • 透明可审计,信任成本低,无暗箱操作;
  • 自动执行无偏差,7×24小时运行;
  • 精准匹配风险收益,用户覆盖面广、门槛低;
  • 提升资产效率,支持份额组合金融化。

(二)劣势

  • 合约逻辑复杂,存在代码漏洞与审计风险;
  • 专业门槛高,普通用户易产生认知误区;
  • 极端行情下有连环清算、预言机依赖风险;
  • 监管模糊,流动性碎片化,链上性能受限。

五、关键认知

  1. 非保本合约,优先层仅相对优先兑付;
  2. 核心价值是风险分层,而非单纯收益分配;
  3. 目前处于DeFi小众创新阶段,尚未大规模普及。

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

智能合约

  • 代币合约
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.5.0
pragma solidity ^0.8.24;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

contract BoykaYuriToken is ERC20, ERC20Burnable, Ownable, ERC20Permit {
    constructor(address recipient, address initialOwner)
        ERC20("MyToken", "MTK")
        Ownable(initialOwner)
        ERC20Permit("MyToken")
    {
        _mint(recipient, 1000000 * 10 ** decimals());
    }
    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}
  • 收益分层交易合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

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

// 模拟 PT 和 YT 代币
contract YieldToken is ERC20 {
    constructor(string memory name, string memory symbol) ERC20(name, symbol) {}
    function mint(address to, uint256 amount) external { _mint(to, amount); }
    function burn(address from, uint256 amount) external { _burn(from, amount); }
}

contract YieldSplitVault is ReentrancyGuard {
    IERC20 public immutable yieldBearingToken; // 生息代币 (如 stETH)
    YieldToken public pt; // 本金代币
    YieldToken public yt; // 收益代币

    uint256 public totalUnderlying; // 记录初始存入的总本金
    uint256 public expiry;          // 到期时间

    constructor(address _ybt, uint256 _duration) {
        yieldBearingToken = IERC20(_ybt);
        expiry = block.timestamp + _duration;
        pt = new YieldToken("Principal Token", "PT");
        yt = new YieldToken("Yield Token", "YT");
    }

    // 存入本金,铸造 PT 和 YT
    function deposit(uint256 amount) external nonReentrant {
        require(block.timestamp < expiry, "Expired");
        
        yieldBearingToken.transferFrom(msg.sender, address(this), amount);
        
        pt.mint(msg.sender, amount);
        yt.mint(msg.sender, amount);
        
        totalUnderlying += amount;
    }

    // 只有 YT 持有者可以随时提取当前产生的利息
    function collectInterest() external nonReentrant {
        uint256 currentBalance = yieldBearingToken.balanceOf(address(this));
        // 利息 = 当前总余额 - 锁定的本金
        uint256 interest = currentBalance > totalUnderlying ? currentBalance - totalUnderlying : 0;
        require(interest > 0, "No interest");

        // 简化的逻辑:根据调用者的 YT 持股比例分利(此处演示直接全取)
        yieldBearingToken.transfer(msg.sender, interest);
    }

    // 到期后,销毁 PT 取回本金
    function redeem(uint256 amount) external nonReentrant {
        require(block.timestamp >= expiry, "Not expired yet");
        
        pt.burn(msg.sender, amount);
        totalUnderlying -= amount;
        yieldBearingToken.transfer(msg.sender, amount);
    }
}

测试脚本

测试用例:

  • 用户存入资产应获得等额的 PT 和 YT
  • 利息产生后,YT 持有者应能提取收益
  • 未到期前无法赎回本金
import assert from "node:assert/strict";
import { describe, it, beforeEach } from "node:test";
import { parseEther, formatEther } from 'viem';
import { network } from "hardhat";

describe("YieldSplitVault 收益分层测试", function () {
  let publicClient: any, vault: any, stETH: any;
  let owner: any, user: any;

  beforeEach(async function () {
    const { viem } = await network.connect();
    publicClient = await viem.getPublicClient();
    [owner, user] = await viem.getWalletClients();

    // 1. 部署一个模拟的 stETH (生息代币)
    stETH = await viem.deployContract("BoykaYuriToken", [owner.account.address, owner.account.address]);
    
    // 2. 部署分层金库 (有效期 1 小时)
    vault = await viem.deployContract("YieldSplitVault", [stETH.address, 3600n]);

    // 给用户一些 stETH
    await stETH.write.transfer([user.account.address, parseEther("100")], { account: owner.account });
  });

  it("用户存入资产应获得等额的 PT 和 YT", async function () {
    const depositAmount = parseEther("10");

    // 授权并存入
    await stETH.write.approve([vault.address, depositAmount], { account: user.account });
    await vault.write.deposit([depositAmount], { account: user.account });

    // 获取 PT 和 YT 地址
    const ptAddress = await vault.read.pt();
    const ytAddress = await vault.read.yt();

    // 检查余额
    const ptBalance = await publicClient.readContract({
        address: ptAddress,
        abi: [{ name: "balanceOf", type: "function", inputs: [{ type: "address" }], outputs: [{ type: "uint256" }] }],
        functionName: "balanceOf",
        args: [user.account.address]
    });

    assert.equal(ptBalance, depositAmount, "PT 铸造数量错误");
    console.log("✅ 成功铸造 PT 和 YT");
  });

  it("利息产生后,YT 持有者应能提取收益", async function () {
    const depositAmount = parseEther("10");
    await stETH.write.approve([vault.address, depositAmount], { account: user.account });
    await vault.write.deposit([depositAmount], { account: user.account });

    // 模拟产生了 1 ETH 的利息 (直接向金库转账)
    await stETH.write.transfer([vault.address, parseEther("1")], { account: owner.account });

    const balanceBefore = await stETH.read.balanceOf([user.account.address]);
    await vault.write.collectInterest({ account: user.account });
    const balanceAfter = await stETH.read.balanceOf([user.account.address]);

    assert.ok(balanceAfter > balanceBefore, "未成功提取利息");
    console.log(`✅ 提取利息成功: ${formatEther(balanceAfter - balanceBefore)} stETH`);
  });

  it("未到期前无法赎回本金", async function () {
    const depositAmount = parseEther("10");
    await stETH.write.approve([vault.address, depositAmount], { account: user.account });
    await vault.write.deposit([depositAmount], { account: user.account });

    // 尝试提前赎回
    await assert.rejects(
        vault.write.redeem([depositAmount], { account: user.account }),
        /Not expired yet/,
        "不应允许提前赎回"
    );
  });
});

部署脚本

// scripts/deploy.js
import { network, artifacts } from "hardhat";
import { parseUnits } from "viem";
async function main() {
  // 获取客户端(hardhat-viem 插件会自动处理网络连接)
   const { viem } = await network.connect({ network: network.name });//指定网络进行链接
   
  const [deployer, user1, user2] = await viem.getWalletClients();
  const publicClient = await viem.getPublicClient();
  const deployerAddress = deployer.account.address;
  console.log("部署者地址:", deployerAddress);

  // 1. 获取 ABI 和 Bytecode
  const BoykaYuriTokenArtifact = await artifacts.readArtifact("BoykaYuriToken");
  const YieldSplitVaultArtifact = await artifacts.readArtifact("YieldSplitVault");

  // 2. 部署 BoykaYuriToken 合约
  const BoykaYuriTokenHash = await deployer.deployContract({
    abi: BoykaYuriTokenArtifact.abi,
    bytecode: BoykaYuriTokenArtifact.bytecode,
    args: [deployerAddress, deployerAddress],
  });
  const BoykaYuriToken= await publicClient.waitForTransactionReceipt({ hash: BoykaYuriTokenHash });
  console.log("BoykaYuriToken 地址:", BoykaYuriToken.contractAddress);
  // 3. 部署 YieldSplitVault 合约
  const YieldSplitVaultHash = await deployer.deployContract({
    abi: YieldSplitVaultArtifact.abi,
    bytecode: YieldSplitVaultArtifact.bytecode,
    args: [BoykaYuriToken.contractAddress,3600n],
  });
  const YieldSplitVault= await publicClient.waitForTransactionReceipt({ hash: YieldSplitVaultHash });
  console.log("YieldSplitVault 地址:", YieldSplitVault.contractAddress);
  
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});

结语

至此,我们已完整实现收益分层交易从理论认知到代码实践的全流程落地,覆盖开发、测试与部署各核心环节。