Teller finance introduces a feature that allows for loan rollover using a flash loan. In one transaction the old loan is repaid and a new one is created. The issue is that not all popular tokens on Ethereum are fully ERC20 compliant. Thus, the rollover feature will not work with these tokens.
One such example is the USDT token. As mentioned in the contest readme, the protocol should be able to work with it:
We are allowing any standard token that would be compatible with Uniswap V3 to work with our codebase, just as was the case for the original audit of TellerV2.sol. The tokens are assumed to be able to work with Uniswap V3 .
Vulnerability Detail
USDT is a major token, supported by Uniswap V3 and according to the contest rules should be supported by the protocol. Unfortunately, said token has some weird behaviour like - approval race protection and missing return values - making integrations with is more intricate.
Impact
Flashloan rollover feature is unusable with not fully ERC20 compliant tokens. This renders key protocol functionality unusable with one of the biggest tokens in crypto.
Code Snippet
The FlashRolloverLoan_G5 contract uses the standard transferFrom, approve and transfer functions, which are problematic when working with USDT.
The following PoC demonstrates how flashloan rollover fails when used on mainnet with USDT.
You can swap tokenAddress value to see that the same test will succeed with USDC
Add rpc url to .env file MAINNET_RPC_URL="https://…”
Set rpc config in foundry.toml:
[rpc_endpoints]
mainnet = "${MAINNET_RPC_URL}"
Add the following code under contracts/tests/LenderCommitmentForwarder/extensions/USDTAppoveFlashLoan.t.sol
pragma solidity ^0.8.0;
import { Testable } from "../../../Testable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { FlashRolloverLoan } from "../../../../contracts/LenderCommitmentForwarder/extensions/FlashRolloverLoan.sol";
## Tool used
Manual Review
## Recommendation
Use the `SafeERC20` library in `FlashRolloverLoan_G5`. Update the necessary mocks so tests could run as well. Here is a [patch](https://gist.github.com/georgiIvanov/0346be833b4743d52b0f0832fc6e8155) with all the files that need an update.
Duplicate of #140
givn
medium
Flashloan rollover doesn't work with USDT
Summary
Teller finance introduces a feature that allows for loan rollover using a flash loan. In one transaction the old loan is repaid and a new one is created. The issue is that not all popular tokens on Ethereum are fully ERC20 compliant. Thus, the rollover feature will not work with these tokens.
One such example is the USDT token. As mentioned in the contest readme, the protocol should be able to work with it:
Vulnerability Detail
USDT is a major token, supported by Uniswap V3 and according to the contest rules should be supported by the protocol. Unfortunately, said token has some weird behaviour like - approval race protection and missing return values - making integrations with is more intricate.
Impact
Flashloan rollover feature is unusable with not fully ERC20 compliant tokens. This renders key protocol functionality unusable with one of the biggest tokens in crypto.
Code Snippet
The
FlashRolloverLoan_G5
contract uses the standardtransferFrom
,approve
andtransfer
functions, which are problematic when working with USDT.The following PoC demonstrates how flashloan rollover fails when used on mainnet with USDT.
.env
fileMAINNET_RPC_URL="https://…”
foundry.toml
:contracts/tests/LenderCommitmentForwarder/extensions/USDTAppoveFlashLoan.t.sol
import { Testable } from "../../../Testable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { FlashRolloverLoan } from "../../../../contracts/LenderCommitmentForwarder/extensions/FlashRolloverLoan.sol";
import "../../../../contracts/interfaces/ILenderCommitmentForwarder.sol"; import "../../../../contracts/interfaces/IFlashRolloverLoan_G4.sol";
import "../../../integration/IntegrationTestHelpers.sol";
import { TellerV2SolMock } from "../../../../contracts/mock/TellerV2SolMock.sol"; import { LenderCommitmentForwarderMock } from "../../../../contracts/mock/LenderCommitmentForwarderMock.sol"; import { MarketRegistryMock } from "../../../../contracts/mock/MarketRegistryMock.sol";
import { AavePoolAddressProviderMock } from "../../../../contracts/mock/aave/AavePoolAddressProviderMock.sol"; import { AavePoolMock } from "../../../../contracts/mock/aave/AavePoolMock.sol";
import { FlashRolloverLoan_G5 } from "../../../../contracts/LenderCommitmentForwarder/extensions/FlashRolloverLoan_G5.sol";
contract FlashRolloverLoanOverride is FlashRolloverLoan_G5 { constructor( address _tellerV2, address _lenderCommitmentForwarder, address _aaveAddressProvider ) FlashRolloverLoan_G5( _tellerV2, _aaveAddressProvider ) {}
}
contract USDTFlashLoanRollover is Testable { constructor() {}
}