前言本文通过开发、测试、部署闪电贷合约,测试闪电贷合约另外需要代币合约、借款人合约,主要借助openzeppelin相关库的使用,简化开发过程,该合约在Defi领用是非常重要的一部分;闪电贷闪电贷:一种特殊的贷款形式,允许用户在同一个交易中借入大量资金,并在交易结束前归还这些资金,如果用户
本文通过开发、测试、部署闪电贷合约,测试闪电贷合约另外需要代币合约、借款人合约,主要借助openzeppelin相关库的使用,简化开发过程,该合约在Defi领用是非常重要的一部分;
闪电贷
闪电贷:一种特殊的贷款形式,允许用户在同一个交易中借入大量资金,并在交易结束前归还这些资金,如果用户在交易结束前未能归还贷款,整个交易将被回滚,就像从未发生过一样,在去中心化金融(DeFi)领域非常有用;
工作原理
- 请求贷款:用户调用闪电贷合约的
flashLoan
函数,请求借入一定数量的代币。- 转移代币:闪电贷合约将代币转移到用户指定的地址。
- 执行操作:用户在同一个交易中执行一些操作,如套利或清算。
- 归还代币:用户将借入的代币归还给闪电贷合约。
- 检查归还:闪电贷合约检查代币是否已归还,如果归还成功,则交易完成;如果归还失败,则交易回滚。
特点
- 无需抵押:用户无需提供任何抵押品即可借入资金。
- 原子性:整个交易必须在同一个区块内完成,如果在交易结束前未能归还贷款,整个交易将被回滚。
- 高风险高回报:用户可以在同一个交易中进行复杂的操作,如套利和清算,这些操作可能带来高回报,但同时也伴随着高风险。
用途
- 套利:用户可以利用不同交易所或借贷平台之间的价格差异进行套利。例如,用户可以借入一种代币,将其转换为另一种代币,然后在另一个平台上以更高的价格卖出,最后归还借入的代币并保留利润。
- 清算:用户可以利用闪电贷来清算其他用户的抵押不足的头寸。例如,如果某个用户的抵押品价值下降,导致其贷款头寸抵押不足,闪电贷用户可以借入资金,清算该头寸,并获得清算奖励。
- 流动性提供:用户可以利用闪电贷在不同的流动性池之间转移资金,以获取更高的收益。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import "hardhat/console.sol";
interface IFlashLoanReceiver {
function executeOperation(
address asset,
uint256 amount,
uint256 premium,
address initiator,
bytes calldata params
) external returns (bool);
}
contract FlashLoan is ReentrancyGuard {
using Math for uint256;
// 闪电贷费率 0.05% = 5/10000
uint256 public constant FLASH_LOAN_FEE = 5;
uint256 public constant FLASH_LOAN_FEE_PRECISION = 10000;
// 闪电贷事件
event FlashLoan(
address indexed receiver,
address indexed token,
uint256 amount,
uint256 fee
);
/**
* @dev 执行闪电贷
* @param receiver 接收闪电贷的合约地址
* @param token 借贷的代币地址
* @param amount 借贷金额
* @param params 附加参数
*/
function flashLoan(
address receiver,
address token,
uint256 amount,
bytes calldata params
) external nonReentrant {
require(amount > 0, "FlashLoan: Amount must be greater than 0");
IERC20 asset = IERC20(token);
uint256 balanceBefore = asset.balanceOf(address(this));
require(balanceBefore >= amount, "FlashLoan: Not enough tokens");
// 计算费用
uint256 fee = amount.mulDiv(FLASH_LOAN_FEE, FLASH_LOAN_FEE_PRECISION);
// 转账代币到接收者
asset.transfer(receiver, amount);
// 调用接收者的回调函数
require(
IFlashLoanReceiver(receiver).executeOperation(
token,
amount,
fee,
msg.sender,
params
),
"FlashLoan: Invalid flash loan executor return"
);
// 验证还款金额
uint256 balanceAfter = asset.balanceOf(address(this));
require(
balanceAfter >= balanceBefore + fee,
"FlashLoan: Flash loan not repaid"
);
emit FlashLoan(receiver, token, amount, fee);
}
}
# 编译指令
# npx hardhat compile
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken1 is ERC20 {
constructor() ERC20("Mock Token", "MTK") {
_mint(msg.sender, 1000000 * 10 ** decimals());
}
}
# 编译指令
# npx hardhat compile
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract FlashLoanReceiver {
// 闪电贷回调函数
function executeOperation(
address asset,
uint256 amount,
uint256 premium,
address initiator,
bytes calldata params
) external returns (bool) {
// 在这里执行闪电贷逻辑
// 还款
uint256 amountToRepay = amount + premium;
IERC20(asset).transfer(msg.sender, amountToRepay);
return true;
}
}
# 编译指令
# npx hardhat compile
测试说明:主要对闪电贷合约,成功调用、余额不足、未贷款、未还款功的测试
const { ethers ,getNamedAccounts,deployments} = require("hardhat");
const { expect } = require("chai");
describe("闪电贷合约测试", function () {
let flashLoan;
let mockToken;
let flashLoanReceiver;
let owner;
let addr1;
let addr2;
beforeEach(async function () {
await deployments.fixture(["Token", "FlashLoan", "FlashLoanReceiver"]);
[owner, addr1, addr2] = await ethers.getSigners();
// 部署 MockToken
const tokenDeployment = await deployments.get("MyToken");
mockToken = await ethers.getContractAt("MyToken",tokenDeployment.address);//已经部署的合约交互
// 部署 FlashLoan
const flashLoanDeployment = await deployments.get("FlashLoan");
flashLoan = await ethers.getContractAt("FlashLoan",flashLoanDeployment.address);//已经部署的合约交互
// 部署 FlashLoanReceiver
const flashLoanReceiverDeployment = await deployments.get("FlashLoanReceiver");
flashLoanReceiver = await ethers.getContractAt("FlashLoanReceiver",flashLoanReceiverDeployment.address);//已经部署的合约交互
// 转一些代币到 FlashLoan 合约
await mockToken.transfer(
await flashLoan.getAddress(),
ethers.parseEther("1000")
);
});
describe("闪电贷 操作", function () {
it("闪电贷执行成功", async function () {
const loanAmount = ethers.parseEther("100");
const fee = (loanAmount * BigInt(5)) / BigInt(10000); // 0.05% fee
// 给接收者转一些代币用于支付费用
await mockToken.transfer(
await flashLoanReceiver.getAddress(),
fee
);
// 执行闪电贷
await expect(
flashLoan.flashLoan(
await flashLoanReceiver.getAddress(),
await mockToken.getAddress(),
loanAmount,
"0x"
)
)
.to.emit(flashLoan, "FlashLoan")
.withArgs(
await flashLoanReceiver.getAddress(),
await mockToken.getAddress(),
loanAmount,
fee
);
// 验证 FlashLoan 合约的余额
expect(await mockToken.balanceOf(await flashLoan.getAddress())).to.equal(
ethers.parseEther("1000") + fee
);
});
it("贷款金额为0 失败", async function () {
await expect(
flashLoan.flashLoan(
await flashLoanReceiver.getAddress(),
await mockToken.getAddress(),
0,
"0x"
)
).to.be.revertedWith("FlashLoan: Amount must be greater than 0");
});
it("token不足 失败", async function () {
const hugeAmount = ethers.parseEther("10000");
await expect(
flashLoan.flashLoan(
await flashLoanReceiver.getAddress(),
await mockToken.getAddress(),
hugeAmount,
"0x"
)
).to.be.revertedWith("FlashLoan: Not enough tokens");
});
it("贷款未偿还 失败", async function () {
// 部署一个恶意的接收者合约,它不会还款
const MaliciousReceiver = await ethers.getContractFactory("FlashLoanReceiver");
const maliciousReceiver = await MaliciousReceiver.deploy();
await maliciousReceiver.waitForDeployment();
// 不给恶意接收者转入费用代币
await expect(
flashLoan.flashLoan(
await maliciousReceiver.getAddress(),
await mockToken.getAddress(),
ethers.parseEther("100"),
"0x"
)
).to.be.revertedWith("FlashLoan: Flash loan not repaid");
});
});
});
# 测试指令
# npx hardhat test ./test/xxx.js
module.exports = async function ({getNamedAccounts,deployments}) {
const firstAccount = (await getNamedAccounts()).firstAccount;
const {deploy,log} = deployments;
const FlashLoan=await deploy("FlashLoan",{
contract: "FlashLoan",
from: firstAccount,
args: [],//参数 owner
log: true,
// waitConfirmations: 1,
})
console.log("FlashLoan合约地址",FlashLoan.address)
}
module.exports.tags = ["all", "FlashLoan"]
# 部署指令
# npx hardhat deploy
module.exports = async ({getNamedAccounts,deployments})=>{
const getNamedAccount = (await getNamedAccounts()).firstAccount;
// const getNamedAccount = (await getNamedAccounts()).secondAccount;
console.log('getNamedAccount',getNamedAccount)
const TokenName = "ETHToken";
const TokenSymbol = "ETH";
const {deploy,log} = deployments;
const Token=await deploy("MyToken1",{
from:getNamedAccount,
args: [],//参数
log: true,
})
// await hre.run("verify:verify", {
// address: TokenC.address,
// constructorArguments: [TokenName, TokenSymbol],
// });
console.log('合约地址1',Token.address)
}
module.exports.tags = ["all", "Token"];
# 部署指令
# npx hardhat deploy
module.exports = async function ({getNamedAccounts,deployments}) {
const firstAccount = (await getNamedAccounts()).firstAccount;
const {deploy,log} = deployments;
const FlashLoanReceiver=await deploy("FlashLoanReceiver",{
contract: "FlashLoanReceiver",
from: firstAccount,
args: [],//参数 owner
log: true,
// waitConfirmations: 1,
})
console.log("FlashLoanReceiver合约地址",FlashLoanReceiver.address)
}
module.exports.tags = ["all", "FlashLoanReceiver"]
# 部署指令
# npx hardhat deploy
以上就是闪电贷合约的开发、测试、部署以及闪电贷相关概念使用场景等相关介绍,注意
:测试中ethers版本为v6,使用v5版本部分方法有所不同,v5中在测试中会出现Cannot read properties of undefined (reading 'provider')相关问题;
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!