30 分钟搭建可升级的 ETH 超额抵押稳定币系统:OpenZeppelin + Chainlink 实战指南

  • 木西
  • 发布于 10小时前
  • 阅读 89

前言基于openzeppelin库实现一个质押稳定币,主要包含稳定币合约、保险合约、以及chailink喂价合约;稳定币和ERC20关联关系一句话总结:ERC20只是通用代币身份证,稳定币是“必须保持价格锚定”的ERC20代币

前言

基于openzeppelin库实现一个质押稳定币,主要包含稳定币合约、保险合约、以及chailink喂价合约;

稳定币和ERC20关联关系

一句话总结:ERC20 只是通用代币身份证,稳定币是“必须保持价格锚定”的 ERC20 代币 维度 稳定币(举例:USDC、DAI) 普通 ERC20(举例:UNI、APE)
接口层 ✅ 100 % 符合 ERC20 ✅ 100 % 符合 ERC20
价格目标 锚定 1 美元(或 1 克黄金、1 CNY 等) 市场自由浮动
价值支撑 必须有链下/链上储备或算法调节(见下表) 取决于项目基本面、投机需求
额外合约逻辑 清算、铸币/销毁、喂价、KYC 黑名单、升级代理 通常只有转账 + 授权

稳定币类型

类型 机制 优点 缺点 案例
法币抵押型 1:1 储备美元等法币 简单、稳定 中心化、需审计 USDC、USDT
加密资产抵押型 超额抵押 ETH 等链上资产 去中心化、透明 复杂、需清算机制 DAI、sUSD
算法型 通过智能合约调节供需 无需抵押、资本效率高 易受市场恐慌影响 UST(失败案例)

核心应用场景

场景 说明
加密交易媒介 现货交易
DeFi 基础设施 作为借贷、AMM、收益农场的基础资产
跨境支付 / 汇款 国际贸易结算
价值存储与避险 市场波动期快速换成稳定币,降低资产回撤
RWA(现实世界资产)交易 代表现实资产(债券、房产)的代币以稳定币计价,提升链上流动性

开发合约

  • 稳定币合约

    
    // SPDX-License-Identifier: MIT
    // Compatible with OpenZeppelin Contracts ^5.0.0
    pragma solidity ^0.8.27;

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 USDStableCoin is ERC20, ERC20Burnable, Ownable, ERC20Permit { constructor(address initialOwner) ERC20("MyStablecoin", "MST") Ownable(initialOwner) ERC20Permit("MyStablecoin") {}

function mint(address to, uint256 amount) public onlyOwner {
    _mint(to, amount);
}

}

* ### chainlink喂价合约
     - **第一种**
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import "@chainlink/contracts/src/v0.8/tests/MockV3Aggregator.sol";
```
- **第二种**
```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

interface AggregatorV3Interface {
    function decimals() external view returns (uint8);
    function description() external view returns (string memory);
    function version() external pure returns (uint256);
    function latestRoundData() external view returns (
        uint80 roundId,
        int256 answer,
        uint256 startedAt,
        uint256 updatedAt,
        uint80 answeredInRound
    );
}

contract MockV3Aggregator is AggregatorV3Interface {
    uint8   public decimals;
    string  public description;
    int256  private s_answer;

    constructor(uint8 _d, string memory _desc, int256 _initial) {
        decimals = _d;
        description = _desc;
        s_answer = _initial;
    }

    /* ===== 实现接口 ===== */
    function version() external pure override returns (uint256) {
        return 1;
    }

    function latestRoundData() external view override returns (
        uint80,int256,uint256,uint256,uint80
    ) {
        return (0, s_answer, 0, block.timestamp, 0);
    }
}
```
- **第三种**
```
直接使用chainlink部署现成的合约地址:
例如:sepolia链上: 0x694AA1769357215DE4FAC081bf1f309aDC325306; // Sepolia ETH/USD
```
- **特别说明**:`部署前两种时,要部署MockV3Aggregator否则会报警告:这个合约可能是抽象的,它可能没有完全实现抽象父类的方法,或者它可能没有正确调用继承合约的构造函数。部署时根据构造函数的参数填写`
  • 保险库合约

    
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import "./USDStableCoin.sol";

/*

  • 功能极简版:
    1. 允许用户超额抵押 ETH 铸造 USC;
    1. 抵押率 150%;
    1. 价格喂价使用 Chainlink Sepolia ETH/USD */ contract Vault is Ownable { USDStableCoin public immutable usc; AggregatorV3Interface internal priceFeed;

    uint256 public constant COLLATERAL_RATIO = 150; // 150% uint8 public constant FEED_DECIMALS = 8;

    mapping(address => uint256) public collateral;

    constructor(address _usc, address _priceFeed) Ownable(msg.sender) { usc = USDStableCoin(_usc); priceFeed = AggregatorV3Interface(_priceFeed); }

    / ========== 用户接口 ========== / function depositAndMint() external payable { require(msg.value > 0, "No ETH"); collateral[msg.sender] += msg.value; uint256 mintAmount = (getEthUsd(msg.value) * 100) / COLLATERAL_RATIO; usc.mint(msg.sender, mintAmount); }

    function repayAndWithdraw(uint256 uscAmount, uint256 ethAmount) external { usc.burn(uscAmount); uint256 neededUsd = (ethAmount * getEthUsd(1 ether)) / 1e18; require(neededUsd <= uscAmount, "Under repay"); collateral[msg.sender] -= ethAmount; payable(msg.sender).transfer(ethAmount); }

    / ========== 内部函数 ========== / function getEthUsd(uint256 weiAmount) public view returns (uint256) { (, int256 price,,,) = priceFeed.latestRoundData(); // 8 位小数 require(price > 0, "Bad price"); return (weiAmount * uint256(price)) / 10 ** FEED_DECIMALS; }

    receive() external payable {} }

    # 测试

    const { expect } = require("chai"); const { ethers } = require("hardhat");

describe("Vault & USDStableCoin Integration", function () { let usc, vault, mockV3; let owner, alice;

const DECIMALS = 8; const ETH_USD_PRICE = 2000 * 10 ** DECIMALS; // 2000 USD 8 位小数

beforeEach(async function () { [owner, alice] = await ethers.getSigners();

/* 1. 部署 USDStableCoin */
const USC = await ethers.getContractFactory("USDStableCoin");
usc = await USC.deploy();
await usc.deployed();

/* 2. 部署 MockV3Aggregator */
const Mock = await ethers.getContractFactory("MockV3Aggregator");
mockV3 = await Mock.deploy(DECIMALS, "ETH / USD", ETH_USD_PRICE);
await mockV3.deployed();

/* 3. 部署 Vault */
const Vault = await ethers.getContractFactory("Vault");
vault = await Vault.deploy(usc.address, mockV3.address);
await vault.deployed();

/* 4. 把铸币权限转给 vault */
await usc.transferOwnership(vault.address);

});

describe("depositAndMint", function () { it("should mint USC after depositing ETH", async function () { const ethDeposit = ethers.utils.parseEther("1"); // 1 ETH await vault.connect(alice).depositAndMint({ value: ethDeposit });

  const expected = ethDeposit.mul(ETH_USD_PRICE).div(10 ** DECIMALS).mul(100).div(150);
  expect(await usc.balanceOf(alice.address)).to.eq(expected);
});

it("should revert if deposit 0 ETH", async function () {
  await expect(vault.connect(alice).depositAndMint({ value: 0 }))
    .to.be.revertedWith("No ETH");
});

});

describe("repayAndWithdraw", function () { beforeEach(async function () { await vault.connect(alice).depositAndMint({ value: ethers.utils.parseEther("1") }); });

it("should redeem ETH after burning USC", async function () {
  const uscBalance = await usc.balanceOf(alice.address);
  const ethToWithdraw = ethers.utils.parseEther("0.5");

  const neededUsd = ethToWithdraw.mul(ETH_USD_PRICE).div(ethers.utils.parseEther("1"));
  const burnAmount = neededUsd.add(1); // 多烧 1 wei 避免精度问题

  await usc.connect(alice).approve(vault.address, burnAmount);
  const tx = await vault.connect(alice).repayAndWithdraw(burnAmount, ethToWithdraw);

  await expect(tx).to.changeEtherBalance(alice, ethToWithdraw);
  expect(await usc.balanceOf(alice.address)).to.lt(uscBalance);
});

it("should revert if repay amount &lt; needed USD", async function () {
  const ethToWithdraw = ethers.utils.parseEther("1");
  const uscBalance = await usc.balanceOf(alice.address);

  await usc.connect(alice).approve(vault.address, uscBalance);
  await expect(
    vault.connect(alice).repayAndWithdraw(uscBalance, ethToWithdraw)
  ).to.be.revertedWith("Under repay");
});

}); });

# 部署
### MockV3Aggregator 合约部署

module.exports = async ({ getNamedAccounts, deployments }) => { const { deploy } = deployments; const getNamedAccount = (await getNamedAccounts()).firstAccount; const MockV3Aggregator=await deploy("MockV3Aggregator", { from: getNamedAccount, args: [], // 传递的参数 log: true, }); console.log("MockV3Aggregator deployed at:", MockV3Aggregator.address); }; // 部署治理 npx hardhat deploy --tags Token 指定的部署文件 部署的文件包是Deploy module.exports.tags = ["all", "MockV3Aggregator"];

### USDStableCoin 合约部署:同上
### Vault 合约部署

module.exports = async ({ getNamedAccounts, deployments }) => { const { deploy } = deployments; const getNamedAccount = (await getNamedAccounts()).firstAccount; const MockV3Aggregator=await deployments.get("MockV3Aggregator");//读取MockV3Aggregator合约地址 const USDStableCoin=await deployments.get("USDStableCoin");//读取USDStableCoin合约地址 const Vault=await deploy("Vault", { from: getNamedAccount, args: [USDStableCoin.address,MockV3Aggregator.MockV3Aggregator], // 传递的参数 log: true, }); console.log("Vault deployed at:", Vault.address); }; // 部署治理 npx hardhat deploy --tags Token 指定的部署文件 部署的文件包是Deploy module.exports.tags = ["all", "Vault"];


# 总结
本文用 OpenZeppelin 快速拼装了一个**可超额抵押 ETH 生成链上稳定币**的完整最小系统:

-   **稳定币**(ERC20 + 铸币/销毁)
-   **保险库**(150 % 抵押率,Chainlink 喂价)
-   **测试**(Hardhat + MockV3Aggregator)
-   **部署**(hardhat-deploy 脚本一键上链)

开发者拿到代码即可在本地/Sepolia 三分钟跑通“存 ETH → 领稳定币 → 赎回”全流程,并基于这套骨架继续扩展清算、升级、RWA 等复杂 DeFi 场景。
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
木西
木西
0x5D5C...2dD7
江湖只有他的大名,没有他的介绍。