123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- const { ethers } = require('hardhat');
- const { expect } = require('chai');
- const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
- const name = 'My Token';
- const symbol = 'MTKN';
- const initialSupply = 100n;
- const loanValue = 10_000_000_000_000n;
- async function fixture() {
- const [holder, other] = await ethers.getSigners();
- const token = await ethers.deployContract('$ERC20FlashMintMock', [name, symbol]);
- await token.$_mint(holder, initialSupply);
- return { holder, other, token };
- }
- describe('ERC20FlashMint', function () {
- beforeEach(async function () {
- Object.assign(this, await loadFixture(fixture));
- });
- describe('maxFlashLoan', function () {
- it('token match', async function () {
- expect(await this.token.maxFlashLoan(this.token)).to.equal(ethers.MaxUint256 - initialSupply);
- });
- it('token mismatch', async function () {
- expect(await this.token.maxFlashLoan(ethers.ZeroAddress)).to.equal(0n);
- });
- });
- describe('flashFee', function () {
- it('token match', async function () {
- expect(await this.token.flashFee(this.token, loanValue)).to.equal(0n);
- });
- it('token mismatch', async function () {
- await expect(this.token.flashFee(ethers.ZeroAddress, loanValue))
- .to.be.revertedWithCustomError(this.token, 'ERC3156UnsupportedToken')
- .withArgs(ethers.ZeroAddress);
- });
- });
- describe('flashFeeReceiver', function () {
- it('default receiver', async function () {
- expect(await this.token.$_flashFeeReceiver()).to.equal(ethers.ZeroAddress);
- });
- });
- describe('flashLoan', function () {
- it('success', async function () {
- const receiver = await ethers.deployContract('ERC3156FlashBorrowerMock', [true, true]);
- const tx = await this.token.flashLoan(receiver, this.token, loanValue, '0x');
- await expect(tx)
- .to.emit(this.token, 'Transfer')
- .withArgs(ethers.ZeroAddress, receiver, loanValue)
- .to.emit(this.token, 'Transfer')
- .withArgs(receiver, ethers.ZeroAddress, loanValue)
- .to.emit(receiver, 'BalanceOf')
- .withArgs(this.token, receiver, loanValue)
- .to.emit(receiver, 'TotalSupply')
- .withArgs(this.token, initialSupply + loanValue);
- await expect(tx).to.changeTokenBalance(this.token, receiver, 0);
- expect(await this.token.totalSupply()).to.equal(initialSupply);
- expect(await this.token.allowance(receiver, this.token)).to.equal(0n);
- });
- it('missing return value', async function () {
- const receiver = await ethers.deployContract('ERC3156FlashBorrowerMock', [false, true]);
- await expect(this.token.flashLoan(receiver, this.token, loanValue, '0x'))
- .to.be.revertedWithCustomError(this.token, 'ERC3156InvalidReceiver')
- .withArgs(receiver);
- });
- it('missing approval', async function () {
- const receiver = await ethers.deployContract('ERC3156FlashBorrowerMock', [true, false]);
- await expect(this.token.flashLoan(receiver, this.token, loanValue, '0x'))
- .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientAllowance')
- .withArgs(this.token, 0, loanValue);
- });
- it('unavailable funds', async function () {
- const receiver = await ethers.deployContract('ERC3156FlashBorrowerMock', [true, true]);
- const data = this.token.interface.encodeFunctionData('transfer', [this.other.address, 10]);
- await expect(this.token.flashLoan(receiver, this.token, loanValue, data))
- .to.be.revertedWithCustomError(this.token, 'ERC20InsufficientBalance')
- .withArgs(receiver, loanValue - 10n, loanValue);
- });
- it('more than maxFlashLoan', async function () {
- const receiver = await ethers.deployContract('ERC3156FlashBorrowerMock', [true, true]);
- const data = this.token.interface.encodeFunctionData('transfer', [this.other.address, 10]);
- await expect(this.token.flashLoan(receiver, this.token, ethers.MaxUint256, data))
- .to.be.revertedWithCustomError(this.token, 'ERC3156ExceededMaxLoan')
- .withArgs(ethers.MaxUint256 - initialSupply);
- });
- describe('custom flash fee & custom fee receiver', function () {
- const receiverInitialBalance = 200_000n;
- const flashFee = 5_000n;
- beforeEach('init receiver balance & set flash fee', async function () {
- this.receiver = await ethers.deployContract('ERC3156FlashBorrowerMock', [true, true]);
- const tx = await this.token.$_mint(this.receiver, receiverInitialBalance);
- await expect(tx)
- .to.emit(this.token, 'Transfer')
- .withArgs(ethers.ZeroAddress, this.receiver, receiverInitialBalance);
- await expect(tx).to.changeTokenBalance(this.token, this.receiver, receiverInitialBalance);
- await this.token.setFlashFee(flashFee);
- expect(await this.token.flashFee(this.token, loanValue)).to.equal(flashFee);
- });
- it('default flash fee receiver', async function () {
- const tx = await this.token.flashLoan(this.receiver, this.token, loanValue, '0x');
- await expect(tx)
- .to.emit(this.token, 'Transfer')
- .withArgs(ethers.ZeroAddress, this.receiver, loanValue)
- .to.emit(this.token, 'Transfer')
- .withArgs(this.receiver, ethers.ZeroAddress, loanValue + flashFee)
- .to.emit(this.receiver, 'BalanceOf')
- .withArgs(this.token, this.receiver, receiverInitialBalance + loanValue)
- .to.emit(this.receiver, 'TotalSupply')
- .withArgs(this.token, initialSupply + receiverInitialBalance + loanValue);
- await expect(tx).to.changeTokenBalances(this.token, [this.receiver, ethers.ZeroAddress], [-flashFee, 0]);
- expect(await this.token.totalSupply()).to.equal(initialSupply + receiverInitialBalance - flashFee);
- expect(await this.token.allowance(this.receiver, this.token)).to.equal(0n);
- });
- it('custom flash fee receiver', async function () {
- const flashFeeReceiverAddress = this.other;
- await this.token.setFlashFeeReceiver(flashFeeReceiverAddress);
- expect(await this.token.$_flashFeeReceiver()).to.equal(flashFeeReceiverAddress);
- const tx = await this.token.flashLoan(this.receiver, this.token, loanValue, '0x');
- await expect(tx)
- .to.emit(this.token, 'Transfer')
- .withArgs(ethers.ZeroAddress, this.receiver, loanValue)
- .to.emit(this.token, 'Transfer')
- .withArgs(this.receiver, ethers.ZeroAddress, loanValue)
- .to.emit(this.token, 'Transfer')
- .withArgs(this.receiver, flashFeeReceiverAddress, flashFee)
- .to.emit(this.receiver, 'BalanceOf')
- .withArgs(this.token, this.receiver, receiverInitialBalance + loanValue)
- .to.emit(this.receiver, 'TotalSupply')
- .withArgs(this.token, initialSupply + receiverInitialBalance + loanValue);
- await expect(tx).to.changeTokenBalances(
- this.token,
- [this.receiver, flashFeeReceiverAddress],
- [-flashFee, flashFee],
- );
- expect(await this.token.totalSupply()).to.equal(initialSupply + receiverInitialBalance);
- expect(await this.token.allowance(this.receiver, flashFeeReceiverAddress)).to.equal(0n);
- });
- });
- });
- });
|