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: 20/199
Findings: 2
Award: $420.57
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: decade
Also found by: 0x3b, 0xDACA, 0xWaitress, 0xWeiss, 0xkaju, Arz, Aymen0909, BPZ, EloiManuel, HaCk0, J4de, Jerry0x, Jiamin, John, Juntao, Kek, Lalanda, MiloTruck, Mukund, PNS, RedTiger, Ruhum, Satyam_Sharma, ToonVH, Tricko, Udsen, ak1, anodaram, bin2chen, carrotsmuggler, cccz, circlelooper, deadrxsezzz, giovannidisiena, jasonxiale, joestakey, juancito, karanctf, kenta, kodyvim, ladboy233, lil_eth, lukino, markus_ether, marwen, mrpathfindr, nobody2018, parlayan_yildizlar_takimi, peakbolt, ravikiranweb3, rbserver, rvierdiiev, silviaxyz, volodya, zhuXKET, zzebra83
0.0748 USDC - $0.07
As described in the section below, the restructureCapTable
only burns the funds from the first address in addressesToWipe
, therefore breaking this method functionality. Despite affecting one of the protocol functionalities, I consider this issue a medium severity one because restructureCapTable
will only be needed on the extreme case where reserve is below the minimum equity.
As we can see from the code snippet below, during the executing of the for loop, the current
variable is set to addressesToWipe[0]
in all iterations. Consequently only funds from the address in index zero of addressesToWipe
will be burned, all the other ones will remain the same.
function restructureCapTable(address[] calldata helpers, address[] calldata addressesToWipe) public { require(zchf.equity() < MINIMUM_EQUITY); checkQualified(msg.sender, helpers); for (uint256 i = 0; i<addressesToWipe.length; i++){ address current = addressesToWipe[0]; _burn(current, balanceOf(current)); } }
Manual Review
Please consider modifyig restructureCapTable
's code as shown in the diff below.
diff --git a/Equity.orig.sol b/Equity.sol index 7057ed6..7159cc4 100644 --- a/Equity.orig.sol +++ b/Equity.sol @@ -309,9 +309,9 @@ contract Equity is ERC20PermitLight, MathUtil, IReserve { function restructureCapTable(address[] calldata helpers, address[] calldata addressesToWipe) public { require(zchf.equity() < MINIMUM_EQUITY); checkQualified(msg.sender, helpers); for (uint256 i = 0; i<addressesToWipe.length; i++){ - address current = addressesToWipe[0]; + address current = addressesToWipe[i]; _burn(current, balanceOf(current)); } }
#0 - c4-pre-sort
2023-04-20T14:22:16Z
0xA5DF marked the issue as duplicate of #941
#1 - c4-judge
2023-05-18T14:27:08Z
hansfriese marked the issue as satisfactory
420.4967 USDC - $420.50
https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/Equity.sol#L59 https://github.com/code-423n4/2023-04-frankencoin/blob/1022cb106919fba963a89205d3b90bf62543f68f/contracts/Equity.sol#L172-L174
The use of block.number
as a timing mechanism, as done on Equity
contract, can have a significant impact on the reliability of those measurements if Frankencoin were to expand to other chains. While block.number
works reasonably well on Ethereum's mainnet, where new blocks are produced at ~12s intervals, it can become problematic on other chains, especially L2 networks, where blocks can be produced much more frequently, potentially every few seconds, and/or have variable rates of production. If Frankencoin is deployed to other chains in the future, this can cause inconsistencies in the timing mechanism, therefore affecting significantly the time-weighted aspect of the votes.
For example on two of the biggest L2 networks, Optimism and Arbitrum, each transaction generates a new block, so new blocks are produced at a much faster rate than on mainnet, directly affecting hardcoded contants in the code like uint256 public constant MIN_HOLDING_DURATION = 90*7200
. Consequently instead of the expected 90 days of holding time, user's will be able to redeem much earlier.
More importantly, on those networks the rate of block production is not constant, so in periods of high network activity, there will be higher block throughput. So all the votes calculations on the Equity
contract will be unreliable. On periods of high or low activity, anchorTime()
will increase at different rates, affecting the growth of the time-weighted votes.
Manual Review
Please consider using block.timestamp
instead of block.number
, and modify the code and constants accordingly.
#0 - c4-pre-sort
2023-04-27T17:44:22Z
0xA5DF marked the issue as duplicate of #914
#1 - c4-judge
2023-05-18T10:43:23Z
hansfriese marked the issue as satisfactory