前言本文系统梳理了收益分层交易的相关知识体系,具体涵盖其核心定义、核心能力、解决的行业痛点、典型行业应用场景、核心优劣势,以及关键认知要点;在代码落地层面,将基于HardhatV3开发框架,结合OpenZeppelinV5库与Solidity0.8.24及以上版本,完整实现该业
本文系统梳理了收益分层交易的相关知识体系,具体涵盖其核心定义、核心能力、解决的行业痛点、典型行业应用场景、核心优劣势,以及关键认知要点;在代码落地层面,将基于 Hardhat V3 开发框架,结合 OpenZeppelin V5 库与 Solidity 0.8.24 及以上版本,完整实现该业务从开发、测试到部署的全流程。
一、是什么
DeFi领域基于区块链的结构化金融合约,核心是按风险、兑付优先级拆分资产池为不同层级份额,自动分配交易收益、亏损与清算,全程去中心化、无人工干预,底层依托公链与预言机实现闭环。
核心分层:优先级(低风险低收益,优先兑付)、劣后级(高风险高收益,兜底亏损)、夹层(风险收益居中,可选设)。
主要落地于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); } }
* **收益分层交易合约**
```js
// 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);
}
}
测试用例:
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/,
"不应允许提前赎回"
);
}); });
### 部署脚本
```js
// 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);
});
至此,我们已完整实现收益分层交易从理论认知到代码实践的全流程落地,覆盖开发、测试与部署各核心环节。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!