Hardhat V3 实战:期货合约从开发到部署的完整实现

  • 木西
  • 发布于 14小时前
  • 阅读 47

前言本文围绕金融衍生品中的期货展开,一方面系统梳理其核心理论知识,包括期货的定义、应用场景、针对行业痛点的解决价值、优劣势分析,以及从多维度对期货与现货进行对比研究;另一方面基于HardhatV3开发框架,结合OpenZeppelinV5与Solidity0.8.24技术栈,完

前言

本文围绕金融衍生品中的期货展开,一方面系统梳理其核心理论知识,包括期货的定义、应用场景、针对行业痛点的解决价值、优劣势分析,以及从多维度对期货与现货进行对比研究;另一方面基于 Hardhat V3 开发框架,结合 OpenZeppelin V5 与 Solidity 0.8.24 技术栈,完整实现了期货相关合约从开发、测试到部署的全流程落地。

知识梳理

概述

现货是一手交钱一手交货的即时交易,期货是约定未来时间、价格交割的远期合约交易。

一、什么是期货

期货是交易所统一制定的标准化远期交易合约,交易双方约定未来特定时间、以固定价格买卖规定数量/质量的标的物(实物商品/金融资产),交易标的为合约本身而非标的物。核心特征:杠杆保证金(5%-15%)、双向交易(多/空)、T+0、交易所集中交易。分类:商品期货(原油、黄金、农产品等)、金融期货(股指、国债、外汇等)。

二、期货能做什么

  1. 企业端:套期保值,锁定未来购销价格,稳定生产经营节奏;
  2. 投资端:低买高卖合约投机盈利,赚取价格波动价差;
  3. 配置端:作为独立品种搭配股票、债券等,分散投资组合整体风险。

三、期货解决了什么

(一)企业端(核心)

  1. 解决现货价格波动风险,规避原材料采购、产品销售的价格不确定性,避免利润被吞噬;
  2. 解决未来定价难问题,依托期货权威价格提前锁定成本/利润,实现计划性经营。

(二)市场/投资端

  1. 解决现货定价缺乏前瞻性问题,集中竞价形成的期货价格成为现货定价基准;
  2. 解决投资渠道单一、仅单边盈利痛点,双向交易让涨跌均有盈利/避险机会;
  3. 解决市场风险无法转移问题,实现风险在套期保值者与投机者间合理分配,提升市场抗风险能力。

四、期货的核心优劣势

(一)核心优势

  1. 交易灵活:T+0日内无限次交易,双向交易,资金周转效率高,涨跌均有盈利空间;
  2. 杠杆增效:小资金撬动大额合约,提升资金利用率,实现以小博大;
  3. 价格公正:交易所集中竞价,信息公开透明,价格具权威性,无暗箱操作;
  4. 风险对冲:唯一能有效规避现货价格波动的工具,为企业经营保驾护航;
  5. 流动性足:市场参与者众多,合约成交活跃,开平仓无明显滑点。

(二)核心劣势

  1. 风险放大:杠杆双面性,亏损按合约全额计算,极端行情下易亏光保证金、被强平;
  2. 专业门槛高:对投资者分析能力要求高,新手因不熟悉规则易亏损;
  3. 强平风险:每日无负债结算,保证金不足未追加会被强制平仓,造成被动亏损;
  4. 交割限制:个人投资者不能参与实物交割,临近交割月需及时换月;
  5. 波动剧烈:受宏观、国际消息、资金炒作影响,短期波动远超现货,易引发非理性交易。

五、现货与期货多维度对比表

对比维度 期货 现货
交易标的 交易所标准化合约 标的物本身(实物/金融资产)
交易目的 套期保值、投机、资产配置 获取标的物,满足生产/消费/贸易需求
交易规则 T+0、双向交易、杠杆保证金(5%-15%) 多为T+1、单向交易为主、全款交易
结算方式 每日无负债结算(逐日盯市) 成交后即时/短期一次性结算
交收方式 95%以上对冲平仓,极少交割 必须完成标的物交收(一手交钱一手交货)
交易场所 正规期货交易所(集中交易) 场外市场(批发市场/电商)/部分现货交易所
价格属性 前瞻性,反映未来供需预期 即时性,反映当前供需关系
风险程度 高(杠杆+强平+价格剧烈波动) 低(仅承担标的物本身价格波动风险)
合约属性 交易所统一制定,条款固定 交易双方协商,非标准化
参与主体 企业(套期保值)、个人/机构投资者(投机) 生产/贸易/消费企业、普通采购者/投资者
资金门槛 较低(保证金制度,小资金可参与) 较高(全款交易,需足额资金)
监管主体 国家金融监管部门(如证监会) 市场监管部门,部分场外无明确监管

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

智能合约

  • 代币合约
    
    // 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 {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

/**

  • @title SimpleFutures
  • @dev 基于 USDC 保证金的简易期货合约 */ contract SimpleFutures is Ownable, ReentrancyGuard { using SafeERC20 for IERC20;

    IERC20 public immutable marginToken; uint256 public constant LEVERAGE = 10; // 固定10倍杠杆 uint256 public constant MAINTENANCE_MARGIN_RATE = 5; // 5% 维持保证金率 uint256 public constant PRECISION = 1e18; struct Position { uint256 margin; // 投入的保证金 uint256 entryPrice; // 开仓价格 uint256 size; // 合约头寸数量 (margin * leverage / entryPrice) bool isLong; // 方向 bool isOpen; // 状态 }

    mapping(address => Position) public positions;

    event PositionOpened(address indexed user, bool isLong, uint256 margin, uint256 entryPrice); event PositionClosed(address indexed user, int256 pnl, uint256 payout);

    constructor(address _marginToken) Ownable(msg.sender) { marginToken = IERC20(_marginToken); }

    /**

    • @notice 开启头寸
    • @param _margin 保证金金额
    • @param _entryPrice 当前预言机价格 (模拟输入)
    • @param _isLong 是否看涨 */ function openPosition(uint256 _margin, uint256 _entryPrice, bool _isLong) external nonReentrant { require(!positions[msg.sender].isOpen, "Position already exists"); require(_margin > 0, "Margin must be > 0");

      marginToken.safeTransferFrom(msg.sender, address(this), _margin);

      uint256 size = (_margin LEVERAGE PRECISION) / _entryPrice;

      positions[msg.sender] = Position({ margin: _margin, entryPrice: _entryPrice, size: size, isLong: _isLong, isOpen: true });

      emit PositionOpened(msg.sender, _isLong, _margin, _entryPrice); }

    /**

    • @notice 到期结算 (简单实现:由用户或后端调用,传入结算价格) */ function closePosition(uint256 _settlePrice) external nonReentrant { Position storage pos = positions[msg.sender]; require(pos.isOpen, "No active position");

      int256 pnl; if (pos.isLong) { pnl = (int256(pos.size) int256(_settlePrice) / int256(PRECISION)) - (int256(pos.size) int256(pos.entryPrice) / int256(PRECISION)); } else { pnl = (int256(pos.size) int256(pos.entryPrice) / int256(PRECISION)) - (int256(pos.size) int256(_settlePrice) / int256(PRECISION)); }

      uint256 payout; int256 totalValue = int256(pos.margin) + pnl;

      if (totalValue <= 0) { payout = 0; // 爆仓 } else { payout = uint256(totalValue); }

      pos.isOpen = false; if (payout > 0) { marginToken.safeTransfer(msg.sender, payout); }

      emit PositionClosed(msg.sender, pnl, payout); } }

      ### 测试脚本
      **测试用例**:
      1. **初始化参数验证**
      2. **能够成功开多仓**
      3. **平仓结算逻辑:价格上涨多头应盈利**
      4. **风险测试:跌破维持保证金应导致爆仓(Payout为0)**

      import assert from "node:assert/strict"; import { describe, it, beforeEach } from "node:test"; import hre from "hardhat"; import { parseUnits, getAddress } from "viem";

describe("期货合约集成测试", async function () { // 获取连接 const { viem } = await hre.network.connect();

let owner: any, user: any;
let publicClient: any;
let futuresContract: any;
let tokenContract: any;

const INITIAL_PRICE = parseUnits("2000", 6); // 模拟初始价格 2000
const MARGIN = parseUnits("100", 6);        // 100 USDC 保证金

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

    // 1. 部署代币 (假设 BoykaYuriToken 是标准的 ERC20)
    tokenContract = await viem.deployContract("BoykaYuriToken", [
        owner.account.address,
        owner.account.address
    ]);

    // 2. 部署期货合约
    futuresContract = await viem.deployContract("SimpleFutures", [
        tokenContract.address
    ]);
    // ⭐ 关键修复:给期货合约注入初始资金,用于支付用户的盈利
    const initialLiquidity = parseUnits("10000", 6); 
    await tokenContract.write.transfer([
        futuresContract.address, 
        initialLiquidity
    ], { account: owner.account });
    // 3. 给测试用户准备代币并授权
    // 假设合约中有 mint 函数,或者从 owner 转账
    await tokenContract.write.transfer([user.account.address, parseUnits("1000", 6)]);

    // 用户授权给期货合约
    await tokenContract.write.approve([futuresContract.address, MARGIN], {
        account: user.account
    });
});

it("初始化参数验证", async function () {
    const marginToken = await futuresContract.read.marginToken();
    assert.equal(
        getAddress(marginToken), 
        getAddress(tokenContract.address), 
        "保证金代币地址不匹配"
    );

    const leverage = await futuresContract.read.LEVERAGE();
    assert.equal(leverage, 10n, "杠杆倍数应为10");
});

it("应该能够成功开多仓", async function () {
    // 调用 openPosition
    const isLong = true;
    await futuresContract.write.openPosition([MARGIN, INITIAL_PRICE, isLong], {
        account: user.account
    });

    const pos = await futuresContract.read.positions([user.account.address]);

    assert.equal(pos[4], true, "仓位应当是开启状态"); // pos.isOpen
    assert.equal(pos[0], MARGIN, "保证金金额不符");
    assert.equal(pos[3], isLong, "方向应当是看涨(Long)");
});

it("平仓结算逻辑:价格上涨多头应盈利", async function () {
    // 1. 开仓
    await futuresContract.write.openPosition([MARGIN, INITIAL_PRICE, true], {
        account: user.account
    });

    // 2. 模拟价格上涨到 2200 (涨幅 10%)
    // 10倍杠杆下,100 保证金应赚取 100 * (2200-2000)/2000 * 10 = 100
    // 最终取回 100(本金) + 100(盈利) = 200
    const settlePrice = parseUnits("2200", 6);
    const balanceBefore = await tokenContract.read.balanceOf([user.account.address]);

    await futuresContract.write.closePosition([settlePrice], {
        account: user.account
    });

    const balanceAfter = await tokenContract.read.balanceOf([user.account.address]);
    const pos = await futuresContract.read.positions([user.account.address]);

    assert.equal(pos[4], false, "仓位应当已关闭");
    assert.ok(balanceAfter > balanceBefore, "结算后的余额应增加");

    // 验证具体数值 (200)
    const expectedPayout = parseUnits("200", 6);
    assert.equal(balanceAfter - balanceBefore, expectedPayout, "盈利金额计算错误");
});

it("风险测试:跌破维持保证金应导致爆仓(Payout为0)", async function () {
    // 开多仓
    await futuresContract.write.openPosition([MARGIN, INITIAL_PRICE, true], {
        account: user.account
    });

    // 价格大跌到 1500 (跌幅 25%,10倍杠杆直接穿仓)
    const settlePrice = parseUnits("1500", 6);

    await futuresContract.write.closePosition([settlePrice], {
        account: user.account
    });

    const pos = await futuresContract.read.positions([user.account.address]);
    assert.equal(pos[4], false);
    // 具体的余额校验逻辑可根据业务对“穿仓”后的处理添加断言
});

});

### 部署脚本

// scripts/deploy.js import { network, artifacts } from "hardhat"; import { parseUnits } from "viem"; async function main() { // 连接网络 const { viem } = await network.connect({ network: network.name });//指定网络进行链接

// 获取客户端 const [deployer, investor] = await viem.getWalletClients(); const publicClient = await viem.getPublicClient();

const deployerAddress = deployer.account.address; console.log("部署者的地址:", deployerAddress); // 加载合约代币 const MyTokenArtifact = await artifacts.readArtifact("BoykaYuriToken");

// 部署(构造函数参数:recipient, initialOwner) const MyTokenHash = await deployer.deployContract({ abi: MyTokenArtifact.abi,//获取abi bytecode: MyTokenArtifact.bytecode,//硬编码 args: [deployerAddress, investor.account.address],// });

// 等待确认并打印地址 const MyTokenReceipt = await publicClient.waitForTransactionReceipt({ hash: MyTokenHash }); console.log("MyToken合约地址:", MyTokenReceipt.contractAddress); // 部署SimpleFutures合约 const SimpleFuturesArtifact = await artifacts.readArtifact("SimpleFutures"); // 1. 部署合约并获取交易哈希 const SimpleFuturesHash = await deployer.deployContract({ abi: SimpleFuturesArtifact.abi, bytecode: SimpleFuturesArtifact.bytecode, args: [MyTokenReceipt.contractAddress], }); const SimpleFuturesReceipt = await publicClient.waitForTransactionReceipt({ hash: SimpleFuturesHash }); console.log("SimpleFutures合约地址:", SimpleFuturesReceipt.contractAddress); }

main().catch(console.error);


# 结语
至此,本文已完成对期货这一金融衍生品核心理论知识的系统梳理,同时基于 Hardhat V3、OpenZeppelin V5 与 Solidity 0.8.24 技术栈,完整落地了期货相关代码从开发、测试到部署的全流程实践。从理论认知到技术落地的全维度覆盖,为期货相关的技术研究与应用落地提供了清晰的参考路径。
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

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