Frankencoin - yellowBirdy's results

A decentralized and fully collateralized stablecoin.

General Information

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

Frankencoin

Findings Distribution

Researcher Performance

Rank: 41/199

Findings: 1

Award: $199.25

🌟 Selected for report: 1

πŸš€ Solo Findings: 0

Findings Information

🌟 Selected for report: yellowBirdy

Also found by: BenRai, ChrisTina, GreedyGoblin, Norah, carrotsmuggler

Labels

bug
2 (Med Risk)
downgraded by judge
primary issue
selected for report
sponsor confirmed
M-05

Awards

199.2523 USDC - $199.25

External Links

Lines of code

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

Vulnerability details

Denying a position puts it into perma cooldown state ie. cooldown ends at expiry. It’s impossible to withdraw collateral in the cooldown state.

Impact

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

Proof of Concept

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(); _; }
  1. Successful call all to deny will set cooldown = expiry
  2. Subsequent call to withdrawCollateral will be reverted by noCooldown modifier

Tools Used

VS 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

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