`ThunderLoan` exposes `getFeePrecision()` as a public function returning `s_feePrecision`, and `IThunderLoanFixed` declares it as part of the protocol interface, allowing external integrators and receivers to query the fee precision value.
In `ThunderLoanUpgraded`, `s_feePrecision` is replaced by the constant `FEE_PRECISION` and `getFeePrecision()` is never reimplemented — any external contract or integrator that calls `getFeePrecision()` through `IThunderLoanFixed` after the upgrade will hit a selector with no matching function in the new implementation, causing the call to revert silently or fall through to the fallback, breaking any off-chain or on-chain logic that depends on this value.
Likelihood:
This issue manifests any time an external contract or integrator that was built against IThunderLoanFixed calls getFeePrecision() after the protocol is upgraded to ThunderLoanUpgraded, as the function no longer exists in the new implementation.
Impact:
Any on-chain contract that calls getFeePrecision() through IThunderLoanFixed after the upgrade will revert unexpectedly, breaking flash loan receivers that rely on this value to correctly compute and approve the repayment amount, potentially causing legitimate flash loans to fail.
Off-chain integrations and frontends that query getFeePrecision() to display or compute fee information will receive unexpected errors after the upgrade, degrading the user experience and potentially causing incorrect fee estimations that lead users to underpay and have their transactions reverted.
The test verifies that `getFeePrecision()`, declared in `IThunderLoanFixed` and implemented in `ThunderLoan`, is silently removed in `ThunderLoanUpgraded` where `s_feePrecision` is replaced by the constant `FEE_PRECISION` without reimplementing the function. First, it confirms that `getFeePrecision()` returns `1e18` correctly before the upgrade. After upgrading to `ThunderLoanUpgraded`, the same call through `IThunderLoanFixed` reverts because no matching selector exists in the new implementation. Finally, it proves the concrete impact by showing that `FlashLoanAttacker`, which relies on `getFeePrecision()` to compute the maximum borrowable amount, reverts entirely after the upgrade — demonstrating that any on-chain integrator built against `IThunderLoanFixed` is permanently broken without redeployment.
function testGetFeePrecisionRevertsAfterUpgrade() public {
// SETUP: allow tokenA and fund the depositer
assetTokenA = thunderLoan.setAllowedToken(
IERC20(address(tokenA)),
true
);
This is the implementation fixed used for the test:
Since `FEE_PRECISION` is already declared as a public constant in `ThunderLoanUpgraded`, the simplest fix is to reintroduce `getFeePrecision()` as a `pure` function that returns it directly, preserving full compatibility with `IThunderLoanFixed` at zero storage cost. This approach requires no architectural changes and ensures that any existing integrator, receiver, or off-chain tool built against the original interface continues to work correctly after the upgrade without requiring redeployment or migration.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.