Platform: Code4rena
Start Date: 12/04/2023
Pot Size: $60,500 USDC
Total HM: 21
Participants: 199
Period: 7 days
Judge: hansfriese
Total Solo HM: 5
Id: 231
League: ETH
Rank: 41/199
Findings: 1
Award: $199.25
π Selected for report: 1
π Solo Findings: 0
π Selected for report: yellowBirdy
Also found by: BenRai, ChrisTina, GreedyGoblin, Norah, carrotsmuggler
199.2523 USDC - $199.25
https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/Position.sol#L112 https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/Position.sol#L263 https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/Position.sol#L373-L376
Denying a position puts it into perma cooldown state ie. cooldown ends at expiry. Itβs impossible to withdraw collateral in the cooldown state.
Locks owner funds until expiry, expiry time is not capped and can be expected to be long. There is no benefit to the owner to set it shorter and be forced to repay the position at an inconvenient time. Hence a high risk exists to lock the collateral semi permanently
Consider owner trying to call withdrawCollateral
on a denied position
function deny(address[] calldata helpers, string calldata message) public { if (block.timestamp >= start) revert TooLate(); IReserve(zchf.reserve()).checkQualified(msg.sender, helpers); cooldown = expiration; // since expiration is immutable, we put it under cooldown until the end emit PositionDenied(msg.sender, message); } function withdrawCollateral(address target, uint256 amount) public onlyOwner noChallenge noCooldown { uint256 balance = internalWithdrawCollateral(target, amount); checkCollateral(balance, price); } modifier noCooldown() { if (block.timestamp <= cooldown) revert Hot(); _; }
deny
will set cooldown = expiry
withdrawCollateral
will be reverted by noCooldown
modifierVS code, Pen and Paper
Return the collateral to the owner at the end of deny
function deny(address[] calldata helpers, string calldata message) public { if (block.timestamp >= start) revert TooLate(); IReserve(zchf.reserve()).checkQualified(msg.sender, helpers); cooldown = expiration; // since expiration is immutable, we put it under cooldown until the end internalWithdrawCollateral(owner, IERC20(collateral).balanceOf(address(this))); emit PositionDenied(msg.sender, message); }
#0 - c4-pre-sort
2023-04-21T11:08:47Z
0xA5DF marked the issue as primary issue
#1 - 0xA5DF
2023-04-21T11:10:14Z
Might be a design choice, will leave open for sponsor to comment Severity should be medium since funds aren't lost but are locked for some period of time
#2 - luziusmeisser
2023-04-30T00:00:04Z
Excellent point! This is not intended and will be addressed. The owner of a denied position should be allowed to withdraw their collateral. Severity high is ok due to the high likelihood of this happening to innocent users, even though it is not a real loss of assets.
#3 - c4-sponsor
2023-04-30T00:00:10Z
luziusmeisser marked the issue as sponsor confirmed
#4 - c4-judge
2023-05-17T18:53:10Z
hansfriese changed the severity to 2 (Med Risk)
#5 - c4-judge
2023-05-18T15:59:27Z
hansfriese marked the issue as selected for report