Algo Ssstablecoinsss

AI First Flight #2
Beginner FriendlyDeFi
EXP
View results
Submission Details
Impact: high
Likelihood: medium
Invalid

Fee-on-transfer collateral is over-credited during deposit

Root + Impact

Description

  • The protocol should credit users with the actual amount of collateral received by the engine.

  • _deposit_collateral records amount_collateral before calling transferFrom and never compares the engine's balance before and after the transfer. For fee-on-transfer collateral, the engine receives less than amount_collateral but credits the user with the full amount. The user can then mint DSC against collateral that the protocol does not actually hold.

# src/dsc_engine.vy
@internal
def _deposit_collateral(
token_collateral_address: address, amount_collateral: uint256
):
assert amount_collateral > 0, "DSCEngine_NeedsMoreThanZero"
assert self.token_address_to_price_feed[token_collateral_address] != empty(
address
), "DSCEngine__TokenNotAllowed"
@> self.user_to_token_address_to_amount_deposited[msg.sender][
@> token_collateral_address
@> ] += amount_collateral
log CollateralDeposited(msg.sender, amount_collateral)
@> success: bool = extcall IERC20(token_collateral_address).transferFrom(
@> msg.sender, self, amount_collateral
@> )
assert success, "DSCEngine_TransferFailed"

Risk

Likelihood:

  • This occurs when the codebase is forked with a supported collateral token that charges a transfer fee.

  • The README states that the codebase should work after swapping WETH and WBTC for any basket of assets.

Impact:

  • The engine overstates user collateral balances and lets users mint more DSC than the protocol's real collateral supports.

  • Protocol solvency can be reduced because recorded collateral exceeds actual token balances held by the engine.

Proof of Concept

def test_fee_on_transfer_collateral_is_overcredited():
# Example with a 10% fee-on-transfer collateral token.
user_requested_deposit = 100 * 10**18
transfer_fee = 10 * 10**18
actual_received_by_engine = user_requested_deposit - transfer_fee
# Current DSCEngine accounting credits the requested amount, not the received amount.
credited_to_user = user_requested_deposit
assert credited_to_user > actual_received_by_engine
assert credited_to_user == 100 * 10**18
assert actual_received_by_engine == 90 * 10**18

With a $1 collateral token, the engine values the user at $100 of collateral while holding only $90. At a 50% threshold, the user can mint against $50 of effective collateral, while the protocol only has $45 of threshold-adjusted real collateral.

Recommended Mitigation

- self.user_to_token_address_to_amount_deposited[msg.sender][
- token_collateral_address
- ] += amount_collateral
- success: bool = extcall IERC20(token_collateral_address).transferFrom(
- msg.sender, self, amount_collateral
- )
- assert success, "DSCEngine_TransferFailed"
+ balance_before: uint256 = staticcall IERC20(token_collateral_address).balanceOf(self)
+ success: bool = extcall IERC20(token_collateral_address).transferFrom(
+ msg.sender, self, amount_collateral
+ )
+ assert success, "DSCEngine_TransferFailed"
+ balance_after: uint256 = staticcall IERC20(token_collateral_address).balanceOf(self)
+ actual_received: uint256 = balance_after - balance_before
+ assert actual_received > 0, "DSCEngine_NeedsMoreThanZero"
+ self.user_to_token_address_to_amount_deposited[msg.sender][
+ token_collateral_address
+ ] += actual_received

Credit the user based on the actual token balance increase, or explicitly reject fee-on-transfer and rebasing collateral tokens.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!