Company Simulator

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

fund_investor() allows investment after company insolvency (zero or negative net worth)

Root + Impact

Description

  • Normal behavior:

    fund_investor() accepts ETH and mints new shares to investors based on the company’s current share_price.

    It assumes that the company is solvent — meaning company_balance ≥ holding_debt — so that shares have non-zero underlying value.

  • Specific issue:

    When the company’s debt grows larger than its balance (holding_debt > company_balance), the net_worth becomes zero (or negative, clamped to 0).

    However, fund_investor() still allows new investments. Since get_share_price() then returns 0, a division by zero or an absurdly large share issuance can occur, granting infinite or free shares to the new investor.

// Root cause in the codebase with @> marks to highlight the relevant section
@external
def fund_investor():
...
@> share_price: uint256 = self.get_share_price()
@> shares_to_issue: uint256 = msg.value / share_price # share_price may be 0 after insolvency
self.shares[msg.sender] += shares_to_issue
self.issued_shares += shares_to_issue

Risk

Likelihood:

  • Likelihood

Medium : Occurs whenever debt accumulates beyond current balance, or after an external call reduces company_balance (e.g., withdrawals) without reducing debt proportionally.

Realistic in multi-party environments where debt fluctuates or accounting is delayed.

Impact:

  • High — Allows free minting of shares or a revert that halts all investments:

  • New investors obtain arbitrarily large share allocations at zero cost.

  • Old investors are permanently diluted or lose ownership.

  • Company valuation and accounting collapse, halting future redemptions.

  • Can lead to insolvency and loss of funds.

Proof of Concept

Explanation:

Once the company’s net_worth = 0, the computed share_price is 0.

Calling fund_investor() triggers msg.value / share_price → division-by-zero panic (0x12).

If rounding or unsafe math bypasses the check, it mints extreme amounts of shares for 1 wei.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*
PoC: Demonstrates investment during insolvency when netWorth == 0
*/
interface IVulnerable {
function fund_cyfrin(uint256 action) external payable;
function get_share_price() external view returns (uint256);
}
contract InsolvencyPoC {
IVulnerable public target;
constructor(address _target) {
target = IVulnerable(_target);
}
function triggerInsolvency() external payable {
// assume company has holding_debt > company_balance
// so share_price = 0
uint256 price = target.get_share_price();
require(price == 0, "not insolvent yet");
// try to invest; will either revert or mint infinite shares
target.fund_cyfrin{value: msg.value}(1);
}
}

Recommended Mitigation

Explanation (brief)

Add an explicit solvency check before accepting investments.

Reject new funding when company_balance ≤ holding_debt.

- remove this code
+ add this code
@external
def fund_investor():
...
- share_price: uint256 = self.get_share_price()
- shares_to_issue: uint256 = msg.value / share_price
+ # Prevent investment if company is insolvent or share price = 0
+ net_worth: uint256 = 0
+ if self.company_balance > self.holding_debt:
+ net_worth = self.company_balance - self.holding_debt
+
+ assert net_worth > 0, "Company insolvent — cannot accept new investments"
+
+ share_price: uint256 = self.get_share_price()
+ assert share_price > 0, "Share price is zero — halt funding"
+
+ shares_to_issue: uint256 = msg.value / share_price
Updates

Lead Judging Commences

0xshaedyw Lead Judge
7 days ago
0xshaedyw Lead Judge 5 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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