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: 105/199
Findings: 2
Award: $22.67
🌟 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
https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/Equity.sol#L309
The restructureCapTable() has a loop issue that only burns tokens from the first address in the array. Because of this the cap table isn't restructured as intended.
restructureCapTable() is a function in Equity.sol that can be called when the system is at risk and qualified FPS holders want to restructure the system. This function burns FPS tokens from the qualified FPS holders that want to restructure the system. However because the current address in the loop is always set to the first address in the array, this only burns FPS tokens from the first address in the array instead of every single address in the array. If there is a devastating loss in the system, this function doesnt help resolve the issue
The impact of this issue is that the restructureCapTable() function is not functioning as intended. Since the loop always sets the current address to the first address in the array, the function only burns FPS tokens from the first address in the array, while leaving the rest of the addresses untouched. This means that the function is not restructuring the cap table as it should, and the system remains at risk.
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)); } }
Set address current = addressesToWipe[i] so that it burns tokens from all of the addresses
#0 - c4-pre-sort
2023-04-20T14:15:43Z
0xA5DF marked the issue as duplicate of #941
#1 - c4-judge
2023-05-18T14:22:36Z
hansfriese marked the issue as satisfactory
🌟 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
Incorrect amount of shares can be minted because the cubicRoot function returns incorrect results if the given number is not a 1e+18
https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/Equity.sol
https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/MathUtil.sol
The amount of shares can be calculated incorrectly if the given number is not a 1e+18 for the cubicRoot() function in Equity.sol. Users can get more than allowed shares.
If the totalShares < 1000 * ONE_DEC18, the newTotalShares are calculated using this equation:
_mulD18(totalShares, _cubicRoot(_divD18(capitalBefore + investment, capitalBefore))
The problem is when _divD18() returns a 1e19(or bigger) number for the cubicRoot function.
The cubicRoot function expects a 1e18 and returns incorrect results if the number given is not an 1e18 number.
This leads to incorrect calculation of the shares that the user will get and could leave the user with more shares than allowed.
If _divD18() returns a number that is bigger(more zeros) than 1e18, the cubicRoot() function will receive an input that is not an 1e18 number, and will therefore return incorrect results
In the test i did what Equity.sol is doing: _divD18(capitalBefore + investment, capitalBefore)) but i set investment to be a high number so when you _divD18() these 2 you get a 1e19 number and that number is then passed to the cubicRoot() which expects a 1e18 number but because the number is a 1e19 it returns incorrect results
contract TestMathUtil is Test { MathUtil math; uint256 private constant MINIMUM_EQUITY = 1000 * ONE_DEC18; //Amount im transferring so i can make the divD18 return a 1e19 number uint256 private constant INVESTMENT = 9_000_000_000_000_000_000_000; function setUp() public { math = new MathUtil(); } uint256 internal constant ONE_DEC18 = 1018; function testDivD18() public view { //returns 1e19 uint256 incorrectResult = math._divD18(MINIMUM_EQUITY + INVESTMENT, MINIMUM_EQUITY); //_cubicRoot(uint256 _v) expects _v to be a 1e18 //this returns incorrect results console.log("THE BAD RESULT IS", math._cubicRoot(incorrectResult)); //returns 1e18 uint correctResult = math._divD18(MINIMUM_EQUITY, MINIMUM_EQUITY); //this returns the correct results console.log("THE CORRECT RESULT IS", math._cubicRoot(correctResult)); } }
Review the function for proper adjustments
#0 - c4-judge
2023-05-16T16:32:49Z
hansfriese marked the issue as grade-b