Company Simulator

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

Finding H-01 Investor Overpayment Not Refunded

Root + Impact

Description

Under normal operation, investors fund the company via fund_cyfrin(1) to purchase shares up to the public share cap. However, when the cap is reached or close to being reached, the contract still accepts excess msg.value without refunding the overpayment.

Impact Summary

This issue results in a direct, quantifiable loss of ETH for investors and leaves the company’s internal accounting overstated.
Since the overpayment occurs through a public entry point (fund_cyfrin), any investor interacting close to the share cap is vulnerable.
The lack of a refund pathway makes the loss permanent and violates expected ERC20-like behavior for share issuance fairness.

// Root cause in the codebase with @> marks to highlight the relevant section
# src/Cyfrin_Hub.vy
def fund_investor():
...
assert self.issued_shares <= self.public_shares_cap, "Share cap reached!!!"
# @> No refund logic for excess msg.value

Risk

Likelihood:

  • Occurs whenever an investor sends more ETH than required for the remaining available shares.

  • Most likely during race conditions or when the public share cap is nearly full.

Impact:

  • Trapped funds permanently increase company_balance, misrepresenting equity supply.

Loss of funds for investors.

  • Potential accounting drift between real ETH balance and issued shares.

Proof of Concept

The following test from tests/test_phase2_findings.py reproduces the issue by simulating an investor sending more ETH than required for the final available shares.
The contract accepts the entire payment, confirming that no refund logic is triggered.

# tests/test_phase2_findings.py::test_investor_overpayment_not_refunded
hub.fund_cyfrin(1, value=10 * share_price, sender=investor)
# Overpayment accepted; no refund returned.
# Assertion confirms post_investor_balance < expected threshold.

Recommended Mitigation

Add refund logic to return excess ETH to the investor after share issuance.
This prevents trapped funds and aligns the contract with expected DeFi payment safety practices.

@@ def fund_investor():
...
available: uint256 = self.public_shares_cap - self.issued_shares
used_value: uint256 = available * share_price
new_shares: uint256 = msg.value / share_price
+
+ # Refund unused ETH to sender
+ if msg.value > used_value:
+ send(msg.sender, msg.value - used_value)
Updates

Lead Judging Commences

0xshaedyw Lead Judge
6 days ago
0xshaedyw Lead Judge 4 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Medium – Excess Contribution Not Refunded

Investor ETH above share cap is accepted without refund or shares, breaking fairness.

Support

FAQs

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