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: 71/199
Findings: 3
Award: $43.73
🌟 Selected for report: 1
🚀 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.0973 USDC - $0.10
https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/Equity.sol#L313
Incorrect typo in function restructureCapTable()
leading to only burning tokens of first address of addressToWipe
array arguement.
Here, in L313, addressToWipe[0] only takes first address of the array. While ignoring the rest and also since first address's tokens are burned it will fail addressesToWipe
array has more than one addresses.
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
Change address current = addressesToWipe[0];
==> address current = addressesToWipe[i];
#0 - c4-pre-sort
2023-04-20T14:11:32Z
0xA5DF marked the issue as primary issue
#1 - c4-sponsor
2023-04-29T23:16:20Z
luziusmeisser marked the issue as sponsor confirmed
#2 - c4-judge
2023-05-04T05:56:27Z
hansfriese marked the issue as satisfactory
#3 - c4-judge
2023-05-18T17:00:20Z
hansfriese marked the issue as selected for report
🌟 Selected for report: juancito
Also found by: 0xAgro, 0xNorman, 0xSmartContract, 0xStalin, 0xTheC0der, 0xWaitress, 0xhacksmithh, 0xnev, 3dgeville, 8olidity, Arz, Aymen0909, BGSecurity, BRONZEDISC, Bauchibred, Bauer, BenRai, ChainHunters, ChrisTina, CodeFoxInc, DedOhWale, DishWasher, EloiManuel, IceBear, Inspex, Jorgect, Kaysoft, LeoGold, LewisBroadhurst, Madalad, MiloTruck, MohammedRizwan, Nyx, Polaris_tow, RaymondFam, SaharDevep, SanketKogekar, Sathish9098, SolidityATL, Udsen, W0RR1O, aria, ayden, berlin-101, bin2chen, catellatech, codeslide, crc32, decade, descharre, evmboi32, eyexploit, fatherOfBlocks, georgits, giovannidisiena, joestakey, karanctf, kodyvim, ltyu, lukris02, m9800, matrix_0wl, mov, mrpathfindr, nadin, niser93, p0wd3r, parlayan_yildizlar_takimi, pavankv, pontifex, qpzm, ravikiranweb3, rbserver, santipu_, shealtielanz, slvDev, tnevler, wonjun, xmxanuel, yixxas
22.6007 USDC - $22.60
suggestMinter()
_transfer(msg.sender, address(reserve), _applicationFee);
_minApplicationPeriod
to be greater than zero in constructor.constructor(uint256 _minApplicationPeriod) ERC20(18){ MIN_APPLICATION_PERIOD = _minApplicationPeriod; reserve = new Equity(this); }
Source : https://github.com/Uniswap/v3-core/blob/main/audits/tob/audit.pdf
reserve
in Frankencoin.sol in an unlikely scenario.Ownable
contracts directly transfers ownership of the contract in a single transaction.#0 - 0xA5DF
2023-04-27T10:28:59Z
5 is in automated findings
#1 - c4-judge
2023-05-16T16:07:44Z
hansfriese marked the issue as grade-b
🌟 Selected for report: c3phas
Also found by: 0xDACA, 0xRB, 0xSmartContract, 0xhacksmithh, 0xnev, Aymen0909, BenRai, Breeje, DishWasher, Erko, EvanW, JCN, MohammedRizwan, NoamYakov, Polaris_tow, Proxy, Rageur, Raihan, RaymondFam, ReyAdmirado, SAAJ, Sathish9098, Satyam_Sharma, Udsen, __141345__, aria, codeslide, decade, fatherOfBlocks, hunter_w3b, karanctf, matrix_0wl, nadin, naman1778, niser93, pavankv, petrichor, pfapostol, sebghatullah, slvDev, trysam2003, xmxanuel
21.0255 USDC - $21.03
totalSupply()
unnecessarily checked twice.function suggestMinter(address _minter, uint256 _applicationPeriod, uint256 _applicationFee, string calldata _message) override external { if (_applicationPeriod < MIN_APPLICATION_PERIOD && totalSupply() > 0) revert PeriodTooShort(); if (_applicationFee < MIN_FEE && totalSupply() > 0) revert FeeTooLow();
&&
operand, if the first check is fails, second is check is not reached and if statement returns false. Hence, putting totalSupply()>0
check first will not require you to put it twice._minCollateral
require check in function openPosition()
can be shifted above so that users have to less fee on transaction failure.openPosition()
function, it first deploys a position contract and then checks the below require statement.require(_initialCollateral >= _minCollateral, "must start with min col");
createClone()
return value inside Position
interface in clonePosition()
function.function clonePosition(address _existing) external returns (address) { Position existing = Position(_existing); Position clone = Position(createClone(existing.original())); return address(clone); }
createClone()
returns address itself. There is not need for wrapping it under Position
interface and then return address(clone)
. You can directly return the address.#0 - c4-judge
2023-05-16T13:55:53Z
hansfriese marked the issue as grade-b