Omniscia Tren Finance Audit
FlashLoan Manual Review Findings
FlashLoan Manual Review Findings
FLN-01M: Inexistent Support of Stablecoin Boxes
Type | Severity | Location |
---|---|---|
Logical Fault | FlashLoan.sol:L207 |
Description:
The FlashLoan::swapTokens
function will fail to swap the vault's collateral to the debt token if the vault's collateral is the stableCoin
itself which is incorrect.
Impact:
Any Tren box backed by the stableCoin
will not be able to be repaid via a flash-loan due to the Uniswap V3 exact output swap failing when attempting to find a suitable pool to execute the swap step in.
Example:
189/**190 * @dev Swaps as little as possible of one token for `amountOut` of another along the specified191 * path (reversed).192 * @param _tokenIn The address of input collateral.193 * @param _collAmountIn The amount of collateral which will be swapped.194 * @param _debtAmountOut The amount of trenUSD which will (should) be received.195 */196function swapTokens(address _tokenIn, uint256 _collAmountIn, uint256 _debtAmountOut) private {197 // Approve swapRouter to spend amountInMaximum198 IERC20(_tokenIn).approve(address(swapRouter), _collAmountIn);199
200 // The tokenIn/tokenOut field is the shared token between the two pools used in the multiple201 // pool swap. In this case stable coin is the "shared" token.202 // For an exactOutput swap, the first swap that occurs is the swap which returns the203 // eventual desired token.204 // In this case, our desired output token is debtToken so that swap happpens first, and is205 // encoded in the path accordingly.206 IUniswapRouterV3.ExactOutputParams memory params = IUniswapRouterV3.ExactOutputParams({207 path: abi.encodePacked(address(debtToken), uint24(3000), stableCoin, uint24(3000), _tokenIn),208 recipient: address(this),209 deadline: block.timestamp,210 amountOut: _debtAmountOut,211 amountInMaximum: _collAmountIn212 });213
214 // Executes the swap, returning the amountIn actually spent.215 uint256 amountIn = swapRouter.exactOutput(params);216
217 // If the swap did not require the full _collAmountIn to achieve the exact amountOut then we218 // refund msg.sender and approve the router to spend 0.219 if (amountIn < _collAmountIn) {220 IERC20(_tokenIn).approve(address(swapRouter), 0);221 IERC20(_tokenIn).transfer(msg.sender, _collAmountIn - amountIn);222 }223}
Recommendation:
We advise the code to encode a different path if the _tokenIn
matches the stableCoin
and potentially use the more efficient IUniswapRouterV3::exactOutputSingle
function, ensuring that Tren boxes backed by the stableCoin
can be repaid via a flash-loan.
Alleviation (f6f1ad0b8f):
The code was updated to differentiate the stableCoin
by evaluating the collateral token's decimals which is an approach we do not advise as many tokens may have 6
decimals.
We advise a proper comparison to be made of the _collTokenIn
address itself, potentially utilizing a mapping
if multiple tokens are expected to be directly swapped to the debt token.
Alleviation (73b9546eb9):
A proper mapping
has been introduced that can be mutated by the owner of the contract and indicates whether a direct swap should be performed, alleviating this exhibit.
FLN-02M: Invalid Call Context
Type | Severity | Location |
---|---|---|
Logical Fault | FlashLoan.sol:L124-L126 |
Description:
The FlashLoan::flashLoanForRepay
function will fail to behave as expected due to improperly performing a repayment for the asset of the caller. Specifically, the invocation of BorrowerOperations::repayDebtTokens
function will result in the caller being the FlashLoan
contract itself instead of the caller of the FlashLoan::flashLoanForRepay
function, resulting in the Tren box of the FlashLoan
contract to be repaid instead of the Tren box of the caller.
Impact:
The FlashLoan::flashLoanForRepay
function flow will not behave as expected in its current state, rendering it invalid.
Example:
104/**105 * @notice Executes a flash loan specifically to repay a debt on the provided asset.106 * @param _asset The address of the asset for which the debt is to be repaid.107 * @dev This function initiates a flash loan transaction to repay a debt on the specified asset.108 */109function flashLoanForRepay(address _asset) external nonReentrant {110 if (!IAdminContract(adminContract).getIsActive(_asset)) {111 revert FlashLoan__CollateralIsNotActive();112 }113 uint256 debt = ITrenBoxManager(trenBoxManager).getTrenBoxDebt(_asset, msg.sender);114 uint256 gasCompensation = IAdminContract(adminContract).getDebtTokenGasCompensation(_asset);115 uint256 refund = IFeeCollector(feeCollector).simulateRefund(msg.sender, _asset, 1 ether);116 uint256 netDebt = debt - gasCompensation - refund;117
118 mintTokens(netDebt);119
120 IDebtToken(debtToken).transfer(msg.sender, netDebt);121
122 uint256 fee = calculateFee(netDebt);123
124 IBorrowerOperations(borrowerOperations).repayDebtTokens(125 _asset, netDebt, address(0), address(0)126 );127
128 uint256 collAmountIn = IERC20(_asset).balanceOf(address(this));129 uint256 debtTokensToGet = netDebt + fee;130
131 swapTokens(_asset, collAmountIn, debtTokensToGet);132
133 if (IDebtToken(debtToken).balanceOf(address(this)) < debtTokensToGet) {134 revert FlashLoan__LoanIsNotRepayable();135 }136
137 burnTokens(netDebt);138
139 sendFeeToCollector();140
141 emit FlashLoanExecuted(msg.sender, netDebt, fee);142}
Recommendation:
We advise the flash-loan based repayment flow to be revised, either by introducing a special function in the BorrowerOperations
contract or by once again performing a callback to the caller.
Alleviation (f6f1ad0b8f24a96ade345db1dd05a1878eb0f761):
A new BorrowerOperations::repayDebtTokensWithFlashloan
function was introduced that permits the Tren box of another user to be repaid, and logic within the Tren box closure function was introduced to transmit tokens to the flash-loan contract address instead of the borrower whenever the flash-loan contract performs the Tren box closure.
As such, we consider this exhibit fully alleviated.