The contract enforces a 30-day lockup for investor withdrawals; withdrawing before the lockup should apply a 10% penalty to the total payout.
The lockup timestamp share_received_time[msg.sender] is only set once (on first share acquisition) and never updated on subsequent purchases. An investor can set this timestamp with an earlier buy, wait out the lockup, then make a large purchase and immediately withdraw without penalty, since the timestamp is not refreshed.// Root cause in the codebase with @> marks to highlight the relevant section
Likelihood:
This occurs whenever an investor performs any initial purchase to set the timestamp, waits at least LOCKUP_PERIOD , then adds more shares.
The contract lacks per-lot tracking and does not refresh the lock timestamp on additional buys, so all later buys inherit the old timestamp.
Impact:
Investors avoid the 10% early-withdrawal penalty on large later purchases, undermining the lockup mechanism’s intent.
Financial loss and unfair advantage: large positions can be withdrawn immediately, draining company funds faster than designed.
Make a small initial buy to set share_received_time .
Wait longer than LOCKUP_PERIOD .
Make a large second buy; the timestamp doesn’t refresh.
Withdraw immediately; no 10% penalty applies to the new shares (subject to MAX_PAYOUT_PER_SHARE and solvency).
Refresh the lock timestamp on any successful share acquisition (i.e., when new_shares > 0 ). This is the simplest and safest fix given the contract’s “whole-position” withdrawal design.
Optional defense-in-depth: If you need per-lot fairness (old lots unlocked, new lots locked), track purchase lots with timestamps and add a partial withdraw function; this is a broader change
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.