Company Simulator

First Flight #51
Beginner FriendlyDeFi
100 EXP
Submission Details
Impact: high
Likelihood: high

Share Price Zero DOS

Author Revealed upon completion

Root + Impact

Description

get_share_price values the business purely on company_balance - holding_debt. Operational spend, such as calling produce, legitimately drives company_balance toward zero while outstanding shares stay constant. When liquidity reaches zero, the function returns zero and every consumer trusts that result.

@view
@internal
def get_share_price() -> uint256:
if self.issued_shares == 0:
return INITIAL_SHARE_PRICE
net_worth: uint256 = max(self.company_balance - self.holding_debt, 0)
@> return net_worth // self.issued_shares

Risk

Likelihood: Normal operations (production, debt service) reduce liquid cash. No attacker permissions are required; the owner can trigger the failure path with routine calls.

Impact:

  • fund_investor computes msg.value // share_price; if the price is zero the function reverts, permanently freezing new investment inflows.

  • withdraw_shares pays shares_owned * share_price. Zero price means investors receive no ETH, effectively confiscating their stake.

  • Attackers or insiders can deliberately zero out company_balance to lock the system or grief shareholders.

Proof of Concept

  1. Investors fund the company until issued_shares > 0.

  2. Owner calls produce(amount) enough times to nearly exhaust company_balance.

  3. Deposit 1 wei as the owner so the solvency assertion passes.

  4. fund_cyfrin(1) now reverts (division by zero), while withdraw_shares returns 0 wei.

Test scaffold:

def test_share_price_zero_dos(hub, owner, investor):
hub.fund_cyfrin(action=1, value=10**18, sender=investor)
hub.produce(amount=10**2, sender=owner) # drains cash
hub.fund_cyfrin(action=0, value=1, sender=owner)
with reverts():
hub.fund_cyfrin(action=1, value=10**16, sender=investor)
assert hub.get_share_price() == 0

Recommended Mitigation

  1. Enforce a minimum share price of 1 wei whenever issued_shares > 0 to prevent division-by-zero and guarantee a non-zero withdrawal payout.

  2. Incorporate inventory or other productive assets into the net_worth calculation so temporary cash dips do not annihilate the price.

  3. Optionally pause withdrawals and investments when solvency is under-collateralized, emitting clear events for operators.

Patch sketch:

net_worth: uint256 = max(self.company_balance - self.holding_debt, 0)
-return net_worth // self.issued_shares
+price: uint256 = net_worth // self.issued_shares
+if price == 0:
+ return 1
+return price

Support

FAQs

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