Company Simulator

First Flight #51
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: medium
Valid

trigger_demand can cause Refund Loss

Root + Impact

Description

trigger_demand refunds any overpayment with Vyper’s send, which forwards only 2300 gas and returns a boolean. The code ignores that boolean, so a caller with a complex fallback (or one that always reverts) silently loses the refund.

excess: uint256 = msg.value - total_cost
if excess > 0:
@> send(msg.sender, excess)

Risk

Likelihood: Many smart-contract callers require more than 2300 gas in their fallback (e.g., logging, accounting hooks). Any such contract interacting with the engine and overpaying hits the failure path. Attackers can also design grief contracts to exploit the blind refund.

Impact:

  • Honest users permanently lose their change, contradicting the contract’s promise to refund.

  • MEV bots or aggregators may stop integrating, reducing volume.

  • Attackers can create automated griefers that force routers to bleed ETH each time they interact.

Proof of Concept

  1. Deploy helper GreedyRefund whose fallback emits an event (consuming >2300 gas).

  2. Call trigger_demand from that contract with deliberate overpayment.

  3. Observe no refund was received; the transaction succeeded anyway.

Example test scaffold:

def test_refund_lost(engine, hub, greedy):
hub.set_customer_engine(engine.address, sender=hub.owner())
engine.trigger_demand(value=10 * ITEM_PRICE, sender=greedy)
assert greedy.balance == 0 # excess never returned

Recommended Mitigation

  1. Replace send with raw_call(msg.sender, b"", value=excess, revert_on_failure=True) so the transaction reverts if the refund fails.

  2. Alternatively, assert the boolean returned by send and revert on failure if you must cap gas.

  3. Add regression tests covering both EOAs and contracts as demand callers.

Patch sketch:

-if excess > 0:
- send(msg.sender, excess)
+if excess > 0:
+ raw_call(msg.sender, b"", value=excess, revert_on_failure=True)
Updates

Lead Judging Commences

0xshaedyw Lead Judge
about 1 month ago
0xshaedyw Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

Medium – Refund DOS via send()

Vyper’s send() reverts on failure, so refund attempts to contract wallets with complex fallback logic can halt trigger_demand() entirely. This causes a denial-of-service for affected users but does not result in fund loss.

Appeal created

0xshaedyw Lead Judge
about 1 month ago

Support

FAQs

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

Give us feedback!