Skip to content

Commit

Permalink
some testcases for flash loan erc721
Browse files Browse the repository at this point in the history
  • Loading branch information
thorseldon committed Jan 23, 2024
1 parent 41e1aeb commit ce2bf1d
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/libraries/logic/ConfigureLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ library ConfigureLogic {

DataTypes.AssetData storage assetData = poolData.assetLookup[asset];
require(assetData.underlyingAsset != address(0), Errors.ASSET_NOT_EXISTS);
require(assetData.assetType == Constants.ASSET_TYPE_ERC20, Errors.ASSET_TYPE_NOT_ERC20);
require(assetData.assetType == Constants.ASSET_TYPE_ERC721, Errors.ASSET_TYPE_NOT_ERC721);

assetData.isFlashLoanEnabled = isEnable;

Expand Down
10 changes: 10 additions & 0 deletions test/helpers/TestUser.sol
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,14 @@ contract TestUser is ERC721Holder {
function yieldRepayERC20(uint32 poolId, address asset, uint256 amount) public {
_poolManager.yieldRepayERC20(poolId, asset, amount);
}

function flashLoanERC721(
uint32 poolId,
address[] calldata nftAssets,
uint256[] calldata nftTokenIds,
address receiverAddress,
bytes calldata params
) public {
_poolManager.flashLoanERC721(poolId, nftAssets, nftTokenIds, receiverAddress, params);
}
}
125 changes: 125 additions & 0 deletions test/integration/TestIntFlashLoanERC721.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;

import 'src/libraries/helpers/Constants.sol';
import 'src/libraries/helpers/Errors.sol';

import 'test/mocks/MockFlashLoanReceiver.sol';

import 'test/helpers/TestUser.sol';
import 'test/setup/TestWithBaseAction.sol';

contract TestIntFlashLoanERC721 is TestWithBaseAction {
MockFlashLoanReceiver mockReceiver;

struct TestCaseLocalVars {
// prepare
uint256[] baycTokenIds;
uint256[] maycTokenIds;
address[] flNftAssets;
uint256[] flTokenIds;
// results
uint256 poolBalanceBefore;
uint256 poolBalanceAfter;
}

function onSetUp() public virtual override {
super.onSetUp();

initCommonPools();

mockReceiver = new MockFlashLoanReceiver();
}

function prepareNftTokens(TestCaseLocalVars memory testVars, bool isolate) internal {
// deposit
if (isolate) {
testVars.baycTokenIds = prepareIsolateBAYC(tsDepositor1);
testVars.maycTokenIds = prepareIsolateMAYC(tsDepositor1);
} else {
testVars.baycTokenIds = prepareCrossBAYC(tsDepositor1);
testVars.maycTokenIds = prepareCrossMAYC(tsDepositor1);
}

testVars.flNftAssets = new address[](testVars.baycTokenIds.length + testVars.maycTokenIds.length);
testVars.flTokenIds = new uint256[](testVars.baycTokenIds.length + testVars.maycTokenIds.length);

uint idx = 0;
for (uint i = 0; i < testVars.baycTokenIds.length; i++) {
testVars.flNftAssets[idx] = address(tsBAYC);
testVars.flTokenIds[idx] = testVars.baycTokenIds[i];
idx++;
}
for (uint i = 0; i < testVars.maycTokenIds.length; i++) {
testVars.flNftAssets[idx] = address(tsMAYC);
testVars.flTokenIds[idx] = testVars.maycTokenIds[i];
idx++;
}
}

function test_RevertIf_IdListEmpty() public {
address[] memory flNftAssets;
uint256[] memory flTokenIds;

tsHEVM.expectRevert(bytes(Errors.INVALID_ID_LIST));
tsDepositor1.flashLoanERC721(tsCommonPoolId, flNftAssets, flTokenIds, address(mockReceiver), '');
}

function test_RevertIf_FlashLoanDisabled() public {
address[] memory flNftAssets = new address[](1);
flNftAssets[0] = address(tsBAYC);
uint256[] memory flTokenIds = new uint256[](1);
flTokenIds[0] = 10001;

tsHEVM.expectRevert(bytes(Errors.ASSET_IS_FLASHLOAN_DISABLED));
tsDepositor1.flashLoanERC721(tsCommonPoolId, flNftAssets, flTokenIds, address(mockReceiver), '');
}

function test_RevertIf_InvalidTokenOwner() public {
TestCaseLocalVars memory testVars;

tsPoolManager.setAssetFlashLoan(tsCommonPoolId, address(tsBAYC), true);
tsPoolManager.setAssetFlashLoan(tsCommonPoolId, address(tsMAYC), true);

prepareNftTokens(testVars, false);

tsHEVM.expectRevert(bytes(Errors.INVALID_TOKEN_OWNER));
tsDepositor2.flashLoanERC721(tsCommonPoolId, testVars.flNftAssets, testVars.flTokenIds, address(mockReceiver), '');
}

function test_Should_FlashLoan_CrossTokens() public {
TestCaseLocalVars memory testVars;

tsPoolManager.setAssetFlashLoan(tsCommonPoolId, address(tsBAYC), true);
tsPoolManager.setAssetFlashLoan(tsCommonPoolId, address(tsMAYC), true);

prepareNftTokens(testVars, false);

testVars.poolBalanceBefore = tsBAYC.balanceOf(address(tsPoolManager));

// flash loan
tsDepositor1.flashLoanERC721(tsCommonPoolId, testVars.flNftAssets, testVars.flTokenIds, address(mockReceiver), '');

// check results
testVars.poolBalanceAfter = tsBAYC.balanceOf(address(tsPoolManager));
assertEq(testVars.poolBalanceAfter, testVars.poolBalanceBefore, 'tsPoolManager balance');
}

function test_Should_FlashLoan_IsolateTokens() public {
TestCaseLocalVars memory testVars;

tsPoolManager.setAssetFlashLoan(tsCommonPoolId, address(tsBAYC), true);
tsPoolManager.setAssetFlashLoan(tsCommonPoolId, address(tsMAYC), true);

prepareNftTokens(testVars, true);

testVars.poolBalanceBefore = tsBAYC.balanceOf(address(tsPoolManager));

// flash loan
tsDepositor1.flashLoanERC721(tsCommonPoolId, testVars.flNftAssets, testVars.flTokenIds, address(mockReceiver), '');

// check results
testVars.poolBalanceAfter = tsBAYC.balanceOf(address(tsPoolManager));
assertEq(testVars.poolBalanceAfter, testVars.poolBalanceBefore, 'tsPoolManager balance');
}
}
66 changes: 66 additions & 0 deletions test/integration/TestIntIsolateLiquidate.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,72 @@ contract TestIntIsolateLiquidate is TestWithIsolateAction {
user.isolateAuction(tsCommonPoolId, nftAsset, tokenIds, debtAsset, bidAmounts);
}

function test_Should_LiquidateWETH() public {
TestCaseLocalVars memory testVars;

// deposit
prepareWETH(tsDepositor1);
uint256[] memory tokenIds = prepareIsolateBAYC(tsBorrower1);

// borrow
prepareBorrow(tsBorrower1, address(tsBAYC), tokenIds, address(tsWETH));

// make some interest
advanceTimes(365 days);

// drop down nft price
actionSetNftPrice(address(tsBAYC), 5000);

// auction
prepareAuction(tsLiquidator1, address(tsBAYC), tokenIds, address(tsWETH));

// end the auction
advanceTimes(25 hours);

testVars.loanDataBefore = getIsolateLoanData(tsCommonPoolId, address(tsBAYC), tokenIds);
for (uint256 i = 0; i < tokenIds.length; i++) {
testVars.totalBidAmount += testVars.loanDataBefore[i].bidAmount;
testVars.totalBidFine += testVars.loanDataBefore[i].bidFine;
testVars.totalRedeemAmount += testVars.loanDataBefore[i].redeemAmount;
testVars.totalBorrowAmount += testVars.loanDataBefore[i].borrowAmount;
}

testVars.poolBalanceBefore = tsWETH.balanceOf(address(tsPoolManager));
testVars.walletBalanceBefore1 = tsWETH.balanceOf(address(tsLiquidator1));
testVars.walletBalanceBefore2 = tsWETH.balanceOf(address(tsBorrower1));

// liquidate
tsLiquidator1.isolateLiquidate(tsCommonPoolId, address(tsBAYC), tokenIds, address(tsWETH), false);
testVars.txAuctionTimestamp = block.timestamp;

// check results
testVars.loanDataAfter = getIsolateLoanData(tsCommonPoolId, address(tsBAYC), tokenIds);
for (uint256 i = 0; i < tokenIds.length; i++) {
assertEq(testVars.loanDataAfter[i].bidStartTimestamp, 0, 'bidStartTimestamp');
assertEq(testVars.loanDataAfter[i].firstBidder, address(0), 'firstBidder');
assertEq(testVars.loanDataAfter[i].lastBidder, address(0), 'lastBidder');
assertEq(testVars.loanDataAfter[i].bidAmount, 0, 'bidAmount');
assertEq(testVars.loanDataAfter[i].borrowAmount, 0, 'borrowAmount');
}

testVars.poolBalanceAfter = tsWETH.balanceOf(address(tsPoolManager));
assertEq(
testVars.poolBalanceAfter,
testVars.poolBalanceBefore - (testVars.totalBidAmount - testVars.totalBorrowAmount),
'tsPoolManager balance'
);

testVars.walletBalanceAfter1 = tsWETH.balanceOf(address(tsLiquidator1));
assertEq(testVars.walletBalanceAfter1, testVars.walletBalanceBefore1, 'tsLiquidator1 balance');

testVars.walletBalanceAfter2 = tsWETH.balanceOf(address(tsBorrower1));
assertEq(
testVars.walletBalanceAfter2,
(testVars.walletBalanceBefore2 + (testVars.totalBidAmount - testVars.totalBorrowAmount)),
'tsBorrower1 balance'
);
}

function test_Should_LiquidateUSDT() public {
TestCaseLocalVars memory testVars;

Expand Down
56 changes: 56 additions & 0 deletions test/mocks/MockFlashLoanReceiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;

import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import {ERC721Holder} from '@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol';

contract MockFlashLoanReceiver is ERC721Holder {
address[] public savedAssets;
uint256[] public savedAmounts;
address[] public savedNftAssets;
uint256[] public savedTokenIds;
address public savedInitiator;
address public savedOperator;
bytes public savedParams;

function executeOperationERC20(
address[] calldata assets,
uint256[] calldata amounts,
address initiator,
address operator,
bytes calldata params
) public returns (bool) {
savedAssets = assets;
savedAmounts = amounts;
savedInitiator = initiator;
savedOperator = operator;
savedParams = params;

for (uint i = 0; i < assets.length; i++) {
IERC20(assets[i]).approve(operator, amounts[i]);
}

return true;
}

function executeOperationERC721(
address[] calldata nftAssets,
uint256[] calldata tokenIds,
address initiator,
address operator,
bytes calldata params
) public returns (bool) {
savedNftAssets = nftAssets;
savedTokenIds = tokenIds;
savedInitiator = initiator;
savedOperator = operator;
savedParams = params;

for (uint i = 0; i < nftAssets.length; i++) {
IERC721(nftAssets[i]).approve(operator, tokenIds[i]);
}

return true;
}
}
10 changes: 10 additions & 0 deletions test/setup/TestWithPrepare.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,18 @@ abstract contract TestWithPrepare is TestWithData {
prepareIsolateERC721(user, address(tsBAYC), tokenIds);
}

function prepareIsolateMAYC(TestUser user) internal virtual returns (uint256[] memory tokenIds) {
tokenIds = user.getTokenIds();
prepareIsolateERC721(user, address(tsMAYC), tokenIds);
}

function prepareCrossBAYC(TestUser user) internal virtual returns (uint256[] memory tokenIds) {
tokenIds = user.getTokenIds();
prepareCrossERC721(user, address(tsBAYC), tokenIds);
}

function prepareCrossMAYC(TestUser user) internal virtual returns (uint256[] memory tokenIds) {
tokenIds = user.getTokenIds();
prepareCrossERC721(user, address(tsMAYC), tokenIds);
}
}

0 comments on commit ce2bf1d

Please sign in to comment.