prePO contest - 0Kage's results

Decentralized Exchange for Pre-IPO Stocks & Pre-IDO Tokens.

General Information

Platform: Code4rena

Start Date: 09/12/2022

Pot Size: $36,500 USDC

Total HM: 9

Participants: 69

Period: 3 days

Judge: Picodes

Total Solo HM: 2

Id: 190

League: ETH

prePO

Findings Distribution

Researcher Performance

Rank: 28/69

Findings: 2

Award: $238.90

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: Trust

Also found by: 0Kage, Parth, aviggiano, ayeslick, bin2chen, cccz, chaduke, fs0c, hansfriese, imare, mert_eren, rvierdiiev

Labels

bug
3 (High Risk)
satisfactory
duplicate-310

Awards

210.7761 USDC - $210.78

External Links

Lines of code

https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/WithdrawHook.sol#L59

Vulnerability details

Impact

Checks for Global and User Withdraw Limit Per Period are missing for the first withdrawal request right AFTER period length expires and a new period begins. First withdrawal request amount after period length expires can be way higher than globalAmountWithdrawnThisPeriod.

Since timestamp when a next period begins is known well in advance, a large whale can time his withdrawal exactly when a period ends. By dumping a large size of $preCT tokens, a whale can create serious arbitrage opportunities viz-a-viz Uniswap V3 pool. Since this can have a significant short term disruption in supply, I've marked it as HIGH risk

In the WithdrawHook contract line 59, when block.timestamp > lastGlobalPeriodReset + globalPeriodLength, two variables lastGlobalPeriodReset and globalAmountWithdrawnThisPeriod are being reset.

However, for the first withdrawal request when the two above variables are being reset, check if _amountBeforeFee exceeds globalWithdrawLimitPerPeriod is missing. This check is done for the next request in line 63 when lastGlobalPeriodReset is set to previous block timestamp

Same problem also exists for lastUserPeriodReset and userToAmountWithdrawnThisPeriod[_sender] variables in

If a user makes a large withdrawal request right after a period expires, that request will be honored in existing codebase. This is in direct violation of the designed logic that puts restriction on global withdrawal and user specific withdrawal in each period

Proof of Concept

Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

Suppose global withdrawal limit per period if 10m USDC and each period is for 100 seconds. Let's say current timestamp is 10000 -> Bob, a whale depositor times a withdrawal request at exactly 10100 sec for 50m USDC -> Bob sources this liquidity of preCT tokens by dumping Long/Short tokens and draining preCT from UniV3 pools. This can cause massive mispricing of these tokens in short term

Although it can create massive arbitrage opportunities-> in current bear markets with low liquidity, such arbitrage might not be quickly closed. This can result in user panic & loss of confidence in protocol.

Tools Used

Add the check when period gets reset. Added checks to following code block

//global limit check if (lastGlobalPeriodReset + globalPeriodLength < block.timestamp) { lastGlobalPeriodReset = block.timestamp; / require(_amountBeforeFee <= globalWithdrawLimitPerPeriod, "global withdraw limit exceeded"); globalAmountWithdrawnThisPeriod = _amountBeforeFee; / } else { require(globalAmountWithdrawnThisPeriod + _amountBeforeFee <= globalWithdrawLimitPerPeriod, "global withdraw limit exceeded"); globalAmountWithdrawnThisPeriod += _amountBeforeFee; } // user limit check if (lastUserPeriodReset + userPeriodLength < block.timestamp) { lastUserPeriodReset = block.timestamp; require(_amountBeforeFee <= userWithdrawLimitPerPeriod, "user withdraw limit exceeded"); userToAmountWithdrawnThisPeriod[_sender] = _amountBeforeFee; } else { require(userToAmountWithdrawnThisPeriod[_sender] + _amountBeforeFee <= userWithdrawLimitPerPeriod, "user withdraw limit exceeded"); userToAmountWithdrawnThisPeriod[_sender] += _amountBeforeFee; }

#0 - hansfriese

2022-12-13T14:54:39Z

duplicate of #310

#1 - c4-judge

2022-12-13T19:11:03Z

Picodes marked the issue as duplicate of #310

#2 - c4-judge

2023-01-01T17:19:13Z

Picodes marked the issue as satisfactory

Awards

28.124 USDC - $28.12

Labels

bug
grade-b
QA (Quality Assurance)
Q-32

External Links

Code Documentation errors

Inline documentation of code has following minor errors that can be corrected

  1. Natspec for IPrePOMarket event has duplicate entries

  2. Parameter definition for amountAfterFee in Redemption event is incorrect

  3. Comments in IMarketHook incorrectly label parameters

Recommendations

  1. Remove a duplicate line in Natspec * @param shortToken Market Short token address in line 20 of IPrePOMarket
  2. Replace * @param amountAfterFee The amount of Long/Short tokens minted with * @param amountAfterFee The amount of Long/Short tokens redeemed in line 39 of IPrePOMarket
  3. Replace amountBeforeFee with amountAfterFee in line 16 of IMarketHook

#0 - c4-judge

2022-12-19T13:46:48Z

Picodes marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter