Company Simulator

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

Owner-Controlled Share Issuance — Critical Dilution Vulnerability in Company Simulator

Author Revealed upon completion

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

    Under normal operation, the Owner should only issue new shares in a controlled and transparent manner to raise capital for the company. Share issuance is expected to preserve investor confidence by maintaining fair ownership proportions and ensuring that any increase in supply aligns with legitimate business needs, such as expansion or funding operational costs.


  • Explain the specific issue or problem in one or more sentences

    The Owner can mint new shares at any time without any limits, governance approval, or delay. This unrestricted control allows a single account to instantly create an excessive number of shares, severely diluting existing investors and undermining the fairness and decentralization of the system.

// Root cause in the codebase with @> marks to highlight the relevant section
# Vyper-like pseudocode (illustrative, not a full contract)
owner: public(address)
total_supply: public(uint256)
max_total_supply: public(uint256)
max_issuance_per_epoch: public(uint256)
epoch_start: public(uint256)
epoch_issued: public(uint256)
issuance_amount: public(uint256)
issuance_ready_at: public(uint256) # timestamp after which issuance can be executed
TIMELOCK: constant(uint256) = 1 * 24 * 3600 # 1 day timelock
event MintRequested(_by: address, _amount: uint256, _ready_at: uint256)
event MintExecuted(_by: address, _amount: uint256)
@external
def __init__(_max_total_supply: uint256, _max_issuance_per_epoch: uint256):
self.owner = msg.sender
self.max_total_supply = _max_total_supply
self.max_issuance_per_epoch = _max_issuance_per_epoch
self.epoch_start = block.timestamp
self.epoch_issued = 0
@external
def propose_mint(amount: uint256):
# Only owner may propose; actual system should require multisig or DAO vote
assert msg.sender == self.owner
# Enforce global cap
assert self.total_supply + amount <= self.max_total_supply
# Reset epoch if needed (7 days example)
if block.timestamp >= self.epoch_start + 7 * 24 * 3600:
self.epoch_start = block.timestamp
self.epoch_issued = 0
# Enforce per-epoch issuance limit
assert self.epoch_issued + amount <= self.max_issuance_per_epoch
# Set timelock for execution
self.issuance_amount = amount
self.issuance_ready_at = block.timestamp + TIMELOCK
log MintRequested(msg.sender, amount, self.issuance_ready_at)
@external
def execute_mint():
# Anyone can call after timelock; in production this should check governance outcome
assert block.timestamp >= self.issuance_ready_at
amount: uint256 = self.issuance_amount
# double-check caps
assert self.total_supply + amount <= self.max_total_supply
# mint (implementation detail omitted)
self.total_supply += amount
self.epoch_issued += amount
self.issuance_amount = 0
self.issuance_ready_at = 0
log MintExecuted(msg.sender, amount)

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

    This issue occurs during the moment the Owner triggers the share-minting function in order to expand supply for the company. Whenever the Owner performs this operation, the system grants full minting power without governance or constraints, exposing investors to unexpected dilution.

  • Reason 2

    During any moment the Owner’s private key becomes compromised
    The vulnerability becomes active the instant an attacker gains control over the Owner’s account. From that moment, the attacker is able to freely mint excessive shares, causing immediate dilution and severe damage to all legitimate investors.

Impact:

  • Impact 1

    Investor ownership value collapses because the total supply can be expanded instantly, reducing their percentage of shares and economic rights without warning.

  • Impact 2

    The protocol loses credibility and decentralization, since a single account can manipulate the entire financial system, potentially leading to abandonment of the project and complete market failure.

Proof of Concept

# Proof of Concept: Owner-Controlled Unlimited Mint Attack
# This demonstrates how a malicious owner can dilute investors instantly.
# Initial state: Investors think supply is safe and capped
total_supply = 10000
investor_shares = 1000 # investor currently owns 10%
def get_percentage(shares, total):
return (shares / total) * 100
print("=== BEFORE EXPLOIT ===")
print(f"Total Supply: {total_supply}")
print(f"Investor Shares: {investor_shares}")
print(f"Investor Ownership: {get_percentage(investor_shares, total_supply):.2f}%")
# Attack Scenario:
# The owner abuses mint authority to expand supply massively,
# causing instant economic dilution to all honest investors.
malicious_mint_amount = 1000000 # attacker mints huge amount
# ✅ Vulnerability: No governance, no multisig, no timelock stops this event
total_supply += malicious_mint_amount
print("\n=== AFTER EXPLOIT (Owner Mint Attack) ===")
print(f"New Total Supply: {total_supply}")
print(f"Investor Shares: {investor_shares} # unchanged, but now worth far less")
print(f"Investor Ownership: {get_percentage(investor_shares, total_supply):.4f}%")
# Output shows investor ownership collapsing ~10%~0.90%
# Demonstrates catastrophic dilution from a single malicious transaction.

Recommended Mitigation

# Multisig + Hard Cap + Timelock = no sudden dilution attacks
owners: public(address[3]) # small multisig
approval_threshold: public(uint256) # required approvals
proposal_id: public(uint256)
max_total_supply: public(uint256)
total_supply: public(uint256)
issuance_amount: public(uint256)
issuance_ready_at: public(uint256)
approvals: HashMap[address, bool]
@external
def __init__(_owners: address[3], _threshold: uint256, _max_supply: uint256):
# store multisig signers and mint limit
for i in range(3):
self.owners[i] = _owners[i]
self.approval_threshold = _threshold
self.max_total_supply = _max_supply
@internal
def _is_owner(a: address) -> bool:
# verify caller is one of the multisig signers
for i in range(3):
if self.owners[i] == a:
return True
return False
@external
def propose_mint(amount: uint256):
# owner suggests how many new shares to mint
assert self._is_owner(msg.sender)
assert self.total_supply + amount <= self.max_total_supply, "Capped supply"
self.issuance_amount = amount
# timelock: gives community time to react
self.issuance_ready_at = block.timestamp + 24 * 3600
@external
def approve_and_execute_mint(to: address):
# owners approve until threshold reached
assert self._is_owner(msg.sender)
approvals[msg.sender] = True
count: uint256 = 0
for i in range(3):
if approvals[self.owners[i]]:
count += 1
# protection: mint only after approvals + delay
assert count >= self.approval_threshold, "Need multisig signatures"
assert block.timestamp >= self.issuance_ready_at, "Timelock active"
self.total_supply += self.issuance_amount
balances[to] += self.issuance_amount
self.issuance_amount = 0

Support

FAQs

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