Owner has the privilege to block a token whenever they want, and this will lead to permanent block of funds, and liquidity providers can't redeem back their tokens.
If the protocol owner block a token by the ThunderLoan::setAllowedToken(), then all the liquidity providers who deposited their tokens for the corresponding Asset Token, will not be able to redeem their tokens back.
Lock of funds of liquidity providers for a blocked token by the owner.
To have a timelock facility, when a token is blocked by the owner then a timelock will be set for x units, and after x units of time has passed, no more functions can be called for that blocked token.
diff --git a/src/protocol/ThunderLoan.sol b/src/protocol/ThunderLoan.sol
index e67674e..1085d7c 100644
--- a/src/protocol/ThunderLoan.sol
+++ b/src/protocol/ThunderLoan.sol
@@ -83,6 +83,8 @@ contract ThunderLoan is Initializable, OwnableUpgradeable, UUPSUpgradeable, Orac
error ThunderLoan__ExhangeRateCanOnlyIncrease();
error ThunderLoan__NotCurrentlyFlashLoaning();
error ThunderLoan__BadNewFee();
+ error ThunderLoan__DurationLessThanMinimum();
+ error ThunderLoan__TimelockAlreadySet();
using SafeERC20 for IERC20;
using Address for address;
@@ -92,9 +94,16 @@ contract ThunderLoan is Initializable, OwnableUpgradeable, UUPSUpgradeable, Orac
mapping(IERC20 => AssetToken) public s_tokenToAssetToken;
+
+ * @notice If a token is blocked then it will still remain functional for some deadline stored in this mapping
+ * @notice If the timelock is zero and there is a asset token for a token, then it is a allowed token
+ */
+ mapping(IERC20 => uint256) public s_timelock;
+
uint256 private s_feePrecision;
uint256 private s_flashLoanFee;
+ uint256 private constant MIN_DEADLINE_DURATION = 2 days;
mapping(IERC20 token => bool currentlyFlashLoaning) private s_currentlyFlashLoaning;
@@ -177,7 +186,7 @@ contract ThunderLoan is Initializable, OwnableUpgradeable, UUPSUpgradeable, Orac
assetToken.transferUnderlyingTo(msg.sender, amountUnderlying);
}
- function flashloan(address receiverAddress, IERC20 token, uint256 amount, bytes calldata params) external {
+ function flashloan(address receiverAddress, IERC20 token, uint256 amount, bytes calldata params) external revertIfNotAllowedToken(to
ken) {
AssetToken assetToken = s_tokenToAssetToken[token];
uint256 startingBalance = IERC20(token).balanceOf(address(assetToken));
uint256 startingBalance = IERC20(token).balanceOf(address(assetToken));
@@ -224,20 +233,29 @@ contract ThunderLoan is Initializable, OwnableUpgradeable, UUPSUpgradeable, Orac
token.safeTransferFrom(msg.sender, address(assetToken), amount);
}
- function setAllowedToken(IERC20 token, bool allowed) external onlyOwner returns (AssetToken) {
+ function setAllowedToken(IERC20 token, bool allowed, uint256 deadlineDuration) external onlyOwner returns (AssetToken) {
if (allowed) {
- if (address(s_tokenToAssetToken[token]) != address(0)) {
+ if (isAllowedToken(token)) {
revert ThunderLoan__AlreadyAllowed();
}
string memory name = string.concat("ThunderLoan ", IERC20Metadata(address(token)).name());
string memory symbol = string.concat("tl", IERC20Metadata(address(token)).symbol());
AssetToken assetToken = new AssetToken(address(this), token, name, symbol);
s_tokenToAssetToken[token] = assetToken;
+ s_timelock[token] = 0;
emit AllowedTokenSet(token, assetToken, allowed);
return assetToken;
} else {
+ if (s_timelock[token] != 0) {
+ revert ThunderLoan__TimelockAlreadySet();
+ }
+
+ if (deadlineDuration < MIN_DEADLINE_DURATION) {
+ revert ThunderLoan__DurationLessThanMinimum();
+ }
+
+
AssetToken assetToken = s_tokenToAssetToken[token];
- delete s_tokenToAssetToken[token];
+ s_timelock[token] = block.timestamp + deadlineDuration;
emit AllowedTokenSet(token, assetToken, allowed);
return assetToken;
}
@@ -258,7 +276,7 @@ contract ThunderLoan is Initializable, OwnableUpgradeable, UUPSUpgradeable, Orac
}
function isAllowedToken(IERC20 token) public view returns (bool) {
- return address(s_tokenToAssetToken[token]) != address(0);
+ return address(s_tokenToAssetToken[token]) != address(0) && (s_timelock[token] == 0 || s_timelock[token] >= block.timestamp);
}
function getAssetFromToken(IERC20 token) public view returns (AssetToken) {