Frankencoin - Nyx'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: 36/199

Findings: 3

Award: $252.00

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: carrotsmuggler

Also found by: Ace-30, KIntern_NA, Nyx, bin2chen, cccz, juancito, mahdikarimi, mov, nobody2018

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
duplicate-945

Awards

201.1223 USDC - $201.12

External Links

Lines of code

https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/MintingHub.sol#L140-L148 https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/MintingHub.sol#L199-L229

Vulnerability details

Impact

The adjustPrice function allows the position owner to frontrun and adjust the price lower when opening a challenge. This may cause the challenger to sell their collateral at a lower price than expected, putting them in an unfavorable position.

function adjustPrice(uint256 newPrice) public onlyOwner noChallenge { if (newPrice > price) { restrictMinting(3 days); } else { checkCollateral(collateralBalance(), newPrice); } price = newPrice; emitUpdate(); }

When the position owner lowers the price, averting a challenge becomes easier. The challenger would be in an unfavorable position because their collateral can be sold at a lower price than they were expecting.

function tryAvertChallenge( uint256 _collateralAmount, uint256 _bidAmountZCHF ) external onlyHub returns (bool) { if (block.timestamp >= expiration) { return false; // position expired, let every challenge succeed } else if (_bidAmountZCHF * ONE_DEC18 >= price * _collateralAmount) {
if ( challenge.position.tryAvertChallenge(challenge.size, _bidAmountZCHF) ) { // bid was high enough, let bidder buy collateral from challenger zchf.transferFrom(msg.sender, challenge.challenger, _bidAmountZCHF); challenge.position.collateral().transfer( msg.sender, challenge.size );

Proof of Concept

https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/MintingHub.sol#L140-L148

https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/MintingHub.sol#L199-L229

Tools Used

Manual Review

* @param expectedSize size verification to guard against frontrunners doing a split-challenge-attack

Like in bid() function, consider adding a price verification parameter.

#0 - c4-pre-sort

2023-04-21T11:53:46Z

0xA5DF marked the issue as primary issue

#1 - c4-pre-sort

2023-04-21T11:56:54Z

0xA5DF marked the issue as duplicate of #945

#2 - c4-judge

2023-05-18T14:50:14Z

hansfriese marked the issue as satisfactory

#3 - c4-judge

2023-05-18T17:02:52Z

hansfriese changed the severity to 3 (High Risk)

Findings Information

🌟 Selected for report: Josiah

Also found by: 0xDACA, Diana, Emmanuel, Kumpa, Nyx, RaymondFam, Ruhum, __141345__, bin2chen, carlitox477, lil_eth, nobody2018, rbserver

Labels

bug
2 (Med Risk)
satisfactory
duplicate-932

Awards

28.2764 USDC - $28.28

External Links

Lines of code

https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/Position.sol#L88-L101

Vulnerability details

Impact

/** * Adjust this position's limit to give away half of the remaining limit to the clone. * Invariant: global limit stays the same.

https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/Position.sol#L88-L101

The function comment states that the function should adjust the position's limit to give away half of the remaining limit to the clone. However, the current implementation does not reflect this behavior:

uint256 reduction = (limit - minted - _minimum) / 2; limit -= reduction + _minimum; return reduction + _minimum;

For example , Expected behavior based on the comment would be:

limit = 1000 minted = 100

remaining_limit = limit - minted = 1000 - 100 = 900 clone_limit = remaining_limit / 2 = 450

However, the current implementation returns a clone_limit of 500 instead of the expected 450.

Limit calculation is wrong and can be advantageous for the clone.

Proof of Concept

https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/Position.sol#L88-L101

Tools Used

Manual Review

#0 - c4-pre-sort

2023-04-26T13:51:52Z

0xA5DF marked the issue as duplicate of #932

#1 - c4-judge

2023-05-18T14:16:04Z

hansfriese marked the issue as satisfactory

Low - 1 - Centralization Issue

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); }

Shareholders can prevent opening any position they want.

Low - 2 - burn() function doesnt check horizon

function mintInternal(address target, uint256 amount) internal { require(block.timestamp <= horizon, "expired"); require(chf.balanceOf(address(this)) <= limit, "limit"); zchf.mint(target, amount); }

The mint() function includes a check to determine whether the bridge is expired or not, but the burn() function does not have a similar check in place.

#0 - c4-pre-sort

2023-04-26T16:56:39Z

0xA5DF marked the issue as low quality report

#1 - 0xA5DF

2023-04-26T16:56:58Z

#1 is by design #2 - not clear why there should be such a check

#2 - c4-judge

2023-05-16T03:18:09Z

hansfriese marked the issue as grade-c

#3 - c4-judge

2023-05-18T07:51:13Z

hansfriese 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