28852 - [SC - Insight] Reverting permit transactions caught in the cat...
Submitted on Feb 28th 2024 at 22:08:17 UTC by @MrPotatoMagic for Boost | Puffer Finance
Report ID: #28852
Report type: Smart Contract
Report severity: Insight
Target: https://etherscan.io/address/0x7276925e42f9c4054afa2fad80fa79520c453d6a
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
The functions depositWstETH()
and depositStETH()
allows users to obtain pufETH tokens by either providing WstETH or stETH. Before transferring tokens from the PufferDepositor.sol contract to the PufferVault.sol contract, a user needs to approve stETH or wstETH to the PufferDepositor contract.
The issue is that this approval is done through ERC20Permit's permit() function, which is wrapped around by a try-catch block. When a call to permit() reverts due to either ERC2612ExpiredSignature()
error or ERC2612InvalidSigner()
error, the catch block catches the issue but it does nothing in its body.
Due to this, the execution continues which causes a revert with an incorrect reason (allowance error) due to the safeTransferFrom() attempting a transfer from the depositor to vault with no allowance from the msg.sender. The real revert reason should've been ERC2612ExpiredSignature()
error or ERC2612InvalidSigner()
error.
References
See how try-catch works in the solidity docs - https://docs.soliditylang.org/en/v0.8.24/control-structures.html#try-catch
Impact Details
The issue has been marked as low-severity since:
Execution continues even though
ERC2612ExpiredSignature()
orERC2612InvalidSigner()
errors were encountered.The transaction reverts with an allowance error, which is not the real reason for the revert. This could be misleading to users who are trying to debug the issue preventing them from depositing to the vault to obtain pufETH tokens.
Vulnerability Details
Here is the whole process:
User calls function
depositStETH()
On Line 183, we call the permit() function on the
_ST_ETH
token with owner = msg.sender, spender = PufferDepositor contract and value = permitData.amount.
In the permit() function, the following occurs:
On Line 53, we check if block.timestamp is greater than the deadline specified. If the deadline specified was block.timestamp by the user but the transaction gets included in the next block or if the permit just normally expires , the transaction would revert with the
ERC2612ExpiredSignature
error.Line 62 also includes an error but that would only occur if the someone else is trying to use someone else's permit (which is unlikely).
For now, we'll use the reverting reason of
ERC2612ExpiredSignature
.
Since there is a try-catch block being used on the permit() call, the revert is captured by the catch block. But since the catch block does not revert with the reason in its body and is empty, we continue execution on Line 193. Since the depositor contract has no allowance from the msg.sender, the call would revert with an allowance error.
Mitigation
Consider removing the try-catch block since it does not serve any purpose.
OR
In the catch block, consider reverting with an error.
Proof of Concept
How to use this POC:
Add the POC to PufferDepositMainnet.fork.t.sol
In the TestHelper.sol file, make sure to add the ETH mainnet rpc url in the setUp() function.
Run the POC using
forge test --mt testStETHPermitDepositIssue -vvvvv
The traces will display how we encounter an expired deadline error but continue execution, following which we revert with an allowance error.
Traces
In the traces below, we can observe how even though we encounter a
DEADLINE_EXPIRED
revert, the execution still continues and followingly reverts with anALLOWANCE_EXCEEDED
error, which is not the real reason for the revert.
Last updated