Company Simulator

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

Reputation Lockout - permanent

Root + Impact

Description

  • The normal behavior of the system allows customer demands to be triggered only if the company's reputation is at or above 60, with successful sales increasing reputation by 2 (up to 100) and failed sales (due to insufficient inventory) decreasing it by 5 (down to 0). This simulates market trust based on fulfillment.

  • The specific issue is that once reputation drops below 60 due to repeated failed sales, no further demands can be triggered, preventing any successful sales needed to recover reputation, creating a permanent lockout state with no built-in recovery mechanism.

# In CustomerEngine.trigger_demand:
assert rep >= MIN_REPUTATION, "Reputation too low for demand!!!"
# In CompanyGame.sell_to_customer (on failure):
@> self.reputation = min(max(self.reputation - REPUTATION_PENALTY, 0), 100) # Deducts without recovery path
# Reputation only increases on success, which requires demands:
@> self.reputation = min(self.reputation + REPUTATION_REWARD, 100)

Risk

Likelihood: Medium

  • Multiple failed demands occur during periods of low inventory, which can happen naturally if production is not kept up with demand triggers.

  • Users or attackers repeatedly trigger demands after cooldowns when inventory is depleted, leading to progressive reputation loss.

Impact: High

  • Permanent denial of service for sales and revenue generation, rendering the core simulation functionality unusable.

  • Accumulated holding debts from unsold inventory could lead to bankruptcy, indirectly risking all company funds and investor payouts.

Proof of Concept

The test script funds the company, produces minimal inventory (1 item), and simulates multiple customer demands over time (with cooldown bypass via time travel), causing repeated sale failures that drop reputation below 60, after which further demands revert, demonstrating irrecoverable lockout.

import boa
from eth_utils import to_wei
# Assuming OWNER and PATRICK are defined as in examples
# industry_contract is CompanyGame, customer_engine_contract is CustomerEngine
def test_permanent_reputation_lockout(industry_contract, customer_engine_contract, OWNER, PATRICK):
# Arrange: Fund the company and produce limited inventory
boa.env.set_balance(OWNER, to_wei(10, "ether"))
with boa.env.prank(OWNER):
industry_contract.fund_cyfrin(0, value=to_wei(10, "ether"))
industry_contract.produce(1) # Produce only 1 item to allow quick depletion
assert industry_contract.inventory() == 1, "Inventory should be 1"
assert industry_contract.reputation() == 100, "Initial reputation should be 100"
# Act: Trigger multiple demands to cause failed sales and drop reputation below 60
# Each failure deducts 5 reputation; need at least 9 failures to drop from 100 to 55
for _ in range(10):
with boa.env.prank(PATRICK):
try:
customer_engine_contract.trigger_demand(value=to_wei(0.1, "ether"))
except:
pass # Some may succeed initially, then fail
boa.env.time_travel(61) # Advance time to bypass cooldown
# Assert: Reputation is below 60, and further demands revert
assert industry_contract.reputation() < 60, "Reputation should be below 60 after failures"
with boa.reverts("Reputation too low for demand!!!"):
with boa.env.prank(PATRICK):
customer_engine_contract.trigger_demand(value=to_wei(0.1, "ether"))

Recommended Mitigation

Add an owner-only function boost_reputation to incrementally increase reputation (capped at 10 per call) and emit a ReputationChanged event, allowing manual recovery.

+@external
+def boost_reputation(amount: uint256):
+ """
+ @notice Allows owner to boost reputation for recovery.
+ @param amount Amount to add (capped at 10).
+ """
+ assert msg.sender == OWNER, "Not the owner!!!"
+ boost: uint256 = min(amount, 10) # Cap to prevent abuse
+ self.reputation = min(self.reputation + boost, 100)
+ log ReputationChanged(new_reputation=self.reputation)
Updates

Lead Judging Commences

0xshaedyw Lead Judge
7 days ago
0xshaedyw Lead Judge 5 days ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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