揭秘闪电贷合约:不为人知的套利黑科技

  • 木西
  • 发布于 2026-01-06 16:20
  • 阅读 1254

前言本文聚焦DeFi领域的闪电贷智能合约,系统梳理了其定义内涵、核心功能、痛点解决价值及行业落地场景;同时结合HardhatV3开发框架与OpenZeppelin工具库,完整呈现了闪电贷智能合约从开发、测试到部署上线的全流程实践方案。一、DeFi闪电贷是什么去中心化金融协议提供的无

前言

本文聚焦DeFi 领域的闪电贷智能合约,系统梳理了其定义内涵、核心功能、痛点解决价值及行业落地场景;同时结合Hardhat V3 开发框架OpenZeppelin 工具库,完整呈现了闪电贷智能合约从开发、测试到部署上线的全流程实践方案。

一、DeFi闪电贷是什么

去中心化金融协议提供的无抵押贷款,基于区块链交易原子性,借贷与还款必须在同一笔交易中完成,未按时还款则交易回滚,无需抵押、无信用审核,仅需支付少量手续费

二、能做什么

类别 核心功能 具体场景
DeFi 闪电贷 套利;清算;债务 / 资产迁移;流动性管理 跨平台 / 协议价格套利;清算抵押不足头寸赚奖励;跨协议债务转换、资产迁移;流动性池资金转移优化收益

三、解决了什么痛点

  • 无抵押大额借贷难:DeFi 闪电贷无需抵押,可快速借入大额资金,满足复杂金融操作的资金需求。
  • 跨协议操作繁琐:作为 “资金桥梁”,助力用户在不同 DeFi 协议间高效迁移资产或债务,打破协议壁垒。
  • 清算效率低、风险高:可快速提供清算资金,及时处理抵押不足头寸,降低系统性风险累积。

四、行业应用(DeFi)

  • 套利服务:成为专业交易者的常用工具,利用价格差异快速完成套利,如三明治套利、跨交易所套利等。
  • 协议清算:为 DeFi 借贷协议提供高效清算机制,清算人用闪电贷资金清算抵押不足头寸并获取奖励。
  • 跨协议资产管理:实现资产在不同协议间的灵活转移,如借贷关系从 A 协议迁移到 B 协议,优化资金配置。
  • 安全测试与协议优化:安全研究人员用闪电贷测试协议安全性,发现漏洞并推动协议完善,同时也可用于缓解价格预言机攻击带来的影响。

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

开发、测试、部署相关指令

# 编译指令
npx hardhat compile
# 测试指令
npx hardhat test ./scripts/xxx.ts
# 部署指令
npx hardhat run ./test/xxx.ts

智能合约

1.代币合约

// 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);
    }
}

2.闪电贷合约

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

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

interface IFlashLoanReceiver {
    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        address initiator,
        bytes calldata params
    ) external returns (bool);
}

contract FlashLoan is ReentrancyGuard, Ownable {
    using SafeERC20 for IERC20;

    // 费率分母保持 10000,当前 0.05%
    uint256 public flashLoanFee = 5; 
    uint256 public constant FEE_PRECISION = 10000;
    
    error InsufficientLiquidity();
    error TransferFailed();
    error RepaymentFailed();
    error InvalidReceiver();

    event FlashLoanExecuted(address indexed receiver, address indexed token, uint256 amount, uint256 fee);
    event FeeUpdated(uint256 newFee);

    constructor() Ownable(msg.sender) {}

    /**
     * @notice 计算闪电贷费用
     */
    function getFee(uint256 amount) public view returns (uint256) {
        return (amount * flashLoanFee) / FEE_PRECISION;
    }

    /**
     * @dev 执行闪电贷
     */
    function flashLoan(
        address receiver,
        address token,
        uint256 amount,
        bytes calldata params
    ) external nonReentrant {
        IERC20 asset = IERC20(token);
        uint256 balanceBefore = asset.balanceOf(address(this));
        if (balanceBefore < amount) revert InsufficientLiquidity();

        uint256 fee = getFee(amount);

        // 使用 SafeERC20 处理转账
        asset.safeTransfer(receiver, amount);

        // 执行回调
        if (!IFlashLoanReceiver(receiver).executeOperation(
            token,
            amount,
            fee,
            msg.sender,
            params
        )) revert InvalidReceiver();

        // 验证还款:使用 safeTransferFrom 的逻辑或直接余额检查
        // 建议要求接收者将资金发回,而不是让 FlashLoan 合约主动拉取,除非已授权
        uint256 balanceAfter = asset.balanceOf(address(this));
        if (balanceAfter < balanceBefore + fee) revert RepaymentFailed();

        emit FlashLoanExecuted(receiver, token, amount, fee);
    }

    function updateFee(uint256 _newFee) external onlyOwner {
        require(_newFee <= 500, "Fee too high"); // 最高不超过 5%
        flashLoanFee = _newFee;
        emit FeeUpdated(_newFee);
    }
}

3.接收者合约

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

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract FlashLoanReceiver {
    using SafeERC20 for IERC20;

    address public immutable pool;

    error OnlyPoolAllowed();
    error ArbitrageFailed();

    constructor(address _pool) {
        pool = _pool;
    }

    /**
     * @dev 闪电贷回调
     */
    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        address /* initiator */,
        bytes calldata /*params*/
    ) external returns (bool) {
        // 1. 安全检查:只允许受信任的闪电贷池调用
        if (msg.sender != pool) revert OnlyPoolAllowed();

        // 2. 在此处编写你的套利或清算逻辑
        // 例如:在 DEX A 买入,在 DEX B 卖出
        // _performArbitrage(asset, amount, params);

        // 3. 确保当前合约余额足以支付本金 + 手续费
        uint256 amountToRepay = amount + premium;
        uint256 currentBalance = IERC20(asset).balanceOf(address(this));
        
        if (currentBalance < amountToRepay) revert ArbitrageFailed();

        // 4. 还款给闪电贷池
        IERC20(asset).safeTransfer(pool, amountToRepay);

        return true;
    }
    
    // 允许提取利润
    function withdraw(address token) external {
        // 应当添加仅限管理员调用的 modifier
        uint256 balance = IERC20(token).balanceOf(address(this));
        IERC20(token).safeTransfer(msg.sender, balance);
    }
}

4.恶意的接收者合约

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

/**
 * @title MaliciousNonPayer
 * @dev 用于测试闪电贷失败场景的恶意合约
 */
contract MaliciousNonPayer {
    /**
     * @notice 恶意回调:不还款且返回 true
     * @dev 修复说明:
     * 1. 添加了 pure 关键字(因为不读写合约状态)
     * 2. 移除了未使用的变量名(仅保留类型)以消除 Unused parameter 警告
     */
    function executeOperation(
        address,         // asset
        uint256,         // amount
        uint256,         // premium
        address,         // initiator
        bytes calldata   // params
    ) external pure returns (bool) {
        // 恶意逻辑:这里故意不进行任何 IERC20(asset).transfer(...) 操作
        
        // 返回 true 诱导闪电贷合约继续执行,从而触发其最后的余额安全检查
        return true; 
    }
}

合约测试

import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { parseEther, formatEther } from 'viem';
import { network ,artifacts} from "hardhat";

describe("FlashLoan 核心功能测试 (2026版)", async function () {
    // 1. 部署环境准备
    async function deployFixture() {
        const { viem } = await network.connect();
        const publicClient = await viem.getPublicClient();
        const [owner, user] = await viem.getWalletClients();

        // 部署 Token 合约 (参数:owner, initialOwner)
        
        const tokenContract = await viem.deployContract("BoykaYuriToken", [owner.account.address, owner.account.address]);
        console.log("Token 部署成功,地址:", tokenContract.address);
        //部署FlashLoan合约
        const flashLoanContract = await viem.deployContract("FlashLoan", []);
        console.log("FlashLoan 部署成功,地址:", flashLoanContract.address);
        //部署FlashLoanReceiver合约
        const flashLoanReceiverContract = await viem.deployContract("FlashLoanReceiver", [flashLoanContract.address]);
        console.log("FlashLoanReceiver 部署成功,地址:", flashLoanReceiverContract.address);
        //部署MaliciousNonPayer合约
        const maliciousNonPayerContract = await viem.deployContract("MaliciousNonPayer");
        console.log("MaliciousNonPayer 部署成功,地址:", maliciousNonPayerContract.address);
        // // 2. 初始化 FlashLoan 合约转1000 MTK
        // await tokenContract.write.transfer([
        //     flashLoanContract.address,
        //     parseEther("1000")
        // ]);
        // //查看FlashLoan合约余额
        // const flashLoanBalance = await tokenContract.read.balanceOf([flashLoanContract.address]);
        // console.log("FlashLoan合约余额:", formatEther(flashLoanBalance) + " MTK");
        return { tokenContract, flashLoanContract, maliciousNonPayerContract, owner, user, publicClient, flashLoanReceiverContract };
    }

    it("闪电贷执行成功", async function () {
        const { tokenContract, flashLoanContract, flashLoanReceiverContract, owner, user, publicClient } = await deployFixture();
        // --- Step 1: owner查看账户余额 ---
        const initialBalance = await tokenContract.read.balanceOf([owner.account.address]);
        console.log("用户账户余额:", formatEther(initialBalance) + " MTK");
        // 2. 初始化 FlashLoan 合约转1000 MTK
        await tokenContract.write.transfer([
            flashLoanContract.address,
            parseEther("1000")
        ]);
        //查看FlashLoan合约余额
        const flashLoanBalancei = await tokenContract.read.balanceOf([flashLoanContract.address]);
        console.log("FlashLoan合约余额:", formatEther(flashLoanBalancei) + " MTK");
        //
         const loanAmount = parseEther("100");
            const fee = (loanAmount * BigInt(5)) / BigInt(10000); // 0.05% fee

            // 给接收者转一些代币用于支付费用
            await tokenContract.write.transfer([
                await flashLoanReceiverContract.address,
                fee
            ]);

            // 执行闪电贷
            await 
                flashLoanContract.write.flashLoan([
                    await flashLoanReceiverContract.address,
                    await tokenContract.address,
                    loanAmount,
                    "0x"
                ])
            // 验证 FlashLoan 合约的余额
             const flashLoanBalance = await tokenContract.read.balanceOf([flashLoanContract.address]);
             console.log("FlashLoan合约余额:", formatEther(flashLoanBalance) + " MTK");
             console.log("预期FlashLoan合约余额:", formatEther(parseEther("1000") + fee) + " MTK");
    });
    it("贷款金额为0 失败", async function () {
         const { tokenContract, flashLoanContract, flashLoanReceiverContract, owner, user, publicClient } = await deployFixture();
           const flashLoanTx = await flashLoanContract.write.flashLoan([
                    await flashLoanReceiverContract.address,
                    await tokenContract.address,
                    0n,
                    "0x"
                ])
                console.log("贷款金额为0 失败:", flashLoanTx);
                // .catch((error) => {
                //     console.log("贷款金额为0 失败:", error.message);
                // });
        });
        it("token不足 失败", async function () {
            const { tokenContract, flashLoanContract, flashLoanReceiverContract, owner, user, publicClient } = await deployFixture();
            // 2. 初始化 FlashLoan 合约转1000 MTK
        await tokenContract.write.transfer([
            flashLoanContract.address,
            parseEther("1000")
        ]);
        //查看FlashLoan合约余额
        const flashLoanBalancei = await tokenContract.read.balanceOf([flashLoanContract.address]);
        console.log("FlashLoan合约余额:", formatEther(flashLoanBalancei) + " MTK");
            const hugeAmount = parseEther("10000");
            await flashLoanContract.write.flashLoan([
                    await flashLoanReceiverContract.address,
                    await tokenContract.address,
                    hugeAmount,
                    "0x"
                ]).catch((error) => {
                    console.log("token不足 失败:", error.message);
                });
        });
        it("贷款未偿还 失败", async function () {
            const { viem } = await network.connect();
            const { tokenContract, flashLoanContract, maliciousNonPayerContract, owner, user, publicClient } = await deployFixture();
            // 部署一个恶意的接收者合约,它不会还款
        //    const maliciousReceiver = await viem.deployContract("MaliciousNonPayer");
        //    const maliciousReceiverAddress = maliciousReceiver.address;
        // 2. 确保闪电贷池子里有足够的钱借出去
        await tokenContract.write.transfer([flashLoanContract.address, parseEther("1000")]);
            // 不给恶意接收者转入费用代币
            await flashLoanContract.write.flashLoan([
                    await maliciousNonPayerContract.address,
                    await tokenContract.address,
                    parseEther("100"),
                    "0x"
                ]).catch((error) => {
                    console.log("贷款未偿还 失败:", error.message);
                });

        });
});

合约部署

import { network, artifacts } from "hardhat";
import { parseEther, parseEventLogs, getAddress } from "viem";

async function main() {
  console.log(`--- 开始在网络: ${network.name} 部署 ---`);

  // 1. 初始化客户端
  const { viem } = await network.connect();
  const [deployer] = await viem.getWalletClients();
  const publicClient = await viem.getPublicClient();
  const deployerAddress = deployer.account.address;

  // 2. 部署 Token 合约
  console.log("正在部署 Token...");
  const TokenArtifact = await artifacts.readArtifact("BoykaYuriToken");
  const tokenHash = await deployer.deployContract({
    abi: TokenArtifact.abi,
    bytecode: TokenArtifact.bytecode as `0x${string}`,
    args: [deployerAddress, deployerAddress],
  });
  const TokenReceipt = await publicClient.waitForTransactionReceipt({ hash: tokenHash });
  console.log("Token 部署成功,地址:", TokenReceipt.contractAddress!);
  // 3. 部署 FlashLoan 合约
  console.log("正在部署 FlashLoan...");
  const FlashLoanArtifact = await artifacts.readArtifact("FlashLoan");
  const flashLoanHash = await deployer.deployContract({
    abi: FlashLoanArtifact.abi,
    bytecode: FlashLoanArtifact.bytecode as `0x${string}`,
    args: [],
  });
  const FlashLoanReceipt = await publicClient.waitForTransactionReceipt({ hash: flashLoanHash });
  console.log("FlashLoan 部署成功,地址:", FlashLoanReceipt.contractAddress!);
  //接受合约
  console.log("正在部署 FlashLoanReceiver...");
  const FlashLoanReceiverArtifact = await artifacts.readArtifact("FlashLoanReceiver");
  const flashLoanReceiverHash = await deployer.deployContract({
    abi: FlashLoanReceiverArtifact.abi,
    bytecode: FlashLoanReceiverArtifact.bytecode as `0x${string}`,
    args: [FlashLoanReceipt.contractAddress!],
  });
  const FlashLoanReceiverReceipt = await publicClient.waitForTransactionReceipt({ hash: flashLoanReceiverHash });
  console.log("FlashLoanReceiver 部署成功,地址:", FlashLoanReceiverReceipt.contractAddress!);
  // 4. 部署 MaliciousNonPayer 合约
  console.log("正在部署 MaliciousNonPayer...");
  const MaliciousNonPayerArtifact = await artifacts.readArtifact("MaliciousNonPayer");
  const maliciousNonPayerHash = await deployer.deployContract({
    abi: MaliciousNonPayerArtifact.abi,
    bytecode: MaliciousNonPayerArtifact.bytecode as `0x${string}`,
    args: [],
  });
  const MaliciousNonPayerReceipt = await publicClient.waitForTransactionReceipt({ hash: maliciousNonPayerHash });
  console.log("MaliciousNonPayer 部署成功,地址:", MaliciousNonPayerReceipt.contractAddress!);
}

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

总结

至此,本文完成了 DeFi 闪电贷从理论到落地的全链路梳理与实现:明确其无抵押、原子化核心属性,厘清功能、痛点与行业应用,依托 Hardhat V3 与 OpenZeppelin 实现核心合约开发,并配套测试与部署流程,形成完整实践闭环。 闪电贷的价值实现依赖合约安全与逻辑严谨,本文提供的合约设计及全场景测试思路可提供实用参考。未来随着 DeFi 演进,其应用场景将进一步拓展,合约安全优化与多链适配仍是核心探索方向。

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

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