Company Simulator

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

Share Price Can Round Down to Zero Allowing Infinite Shares for Minimal Cost

Description

The share price calculation in fund_investor uses integer division which can round down to zero when the net worth is less than the number of issued shares. When share_price = 0, investors can acquire shares for free or at near-zero cost.

@payable
@internal
def fund_investor():
# Calculate shares based on contribution
net_worth: uint256 = 0
if self.company_balance > self.holding_debt:
net_worth = self.company_balance - self.holding_debt
@> share_price: uint256 = (
@> net_worth // max(self.issued_shares, 1) # Line 316
@> if self.issued_shares > 0
@> else INITIAL_SHARE_PRICE
@> )
@> new_shares: uint256 = msg.value // share_price # Line 320

If net_worth < issued_shares, then share_price = net_worth // issued_shares = 0.

When share_price = 0, line 320 would cause division by zero: msg.value // 0.

While line 308 has an assertion assert (self.company_balance > self.holding_debt) which prevents net_worth = 0 in most cases, there's still a rounding issue where share_price can be very small (1 wei) when net_worth is barely above zero, allowing attackers to acquire massive amounts of shares for minimal cost.

Risk

Likelihood:

  • This occurs when the company's net worth is very low relative to issued shares

  • Can happen after the company pays most debts or has minimal balance

  • The assertion on line 308 prevents the worst case but not the rounding exploit

Impact:

  • Investors can acquire disproportionate shares for minimal ETH

  • Dilutes existing shareholders unfairly

  • Breaks the economic model of the company

  • Can be exploited repeatedly to drain value from the company

Proof of Concept

# Scenario: Company has minimal net worth
company_balance = 1000 wei
holding_debt = 999 wei
issued_shares = 1,000,000
# Net worth calculation:
net_worth = 1000 - 999 = 1 wei
# Share price calculation:
share_price = 1 wei // 1,000,000 = 0 wei (rounds down!)
# If not caught by assertion, investor could do:
msg.value = 1 wei
new_shares = 1 wei // 0 wei = DIVISION BY ZERO!
# Even with assertion preventing net_worth=0:
# If net_worth = 100 wei, issued_shares = 1,000,000:
share_price = 100 // 1,000,000 = 0 wei (still rounds to zero!)

Recommended Mitigation

@payable
@internal
def fund_investor():
assert msg.value > 0, "Must send ETH!!!"
assert (
self.issued_shares <= self.public_shares_cap
), "Share cap reached!!!"
assert (self.company_balance > self.holding_debt), "Company is insolvent!!!"
# Calculate shares based on contribution
net_worth: uint256 = 0
if self.company_balance > self.holding_debt:
net_worth = self.company_balance - self.holding_debt
share_price: uint256 = (
net_worth // max(self.issued_shares, 1)
if self.issued_shares > 0
else INITIAL_SHARE_PRICE
)
+
+ # Prevent zero or near-zero share price
+ assert share_price >= INITIAL_SHARE_PRICE, "Share price too low!"
+
new_shares: uint256 = msg.value // share_price
# Cap shares if exceeding visible limit
available: uint256 = self.public_shares_cap - self.issued_shares
if new_shares > available:
new_shares = available
self.shares[msg.sender] += new_shares
self.issued_shares += new_shares
self.company_balance += msg.value
if self.share_received_time[msg.sender] == 0:
self.share_received_time[msg.sender] = block.timestamp
log SharesIssued(investor=msg.sender, amount=new_shares)
Updates

Lead Judging Commences

0xshaedyw Lead Judge
10 days ago
0xshaedyw Lead Judge 8 days ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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