Platform: Code4rena
Start Date: 13/11/2023
Pot Size: $24,500 USDC
Total HM: 3
Participants: 120
Period: 4 days
Judge: 0xTheC0der
Id: 306
League: ETH
Rank: 42/120
Findings: 2
Award: $27.31
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xVolcano
Also found by: 0xAnah, 0xhex, 0xta, 100su, JCK, K42, MrPotatoMagic, chaduke, cheatc0d3, hunter_w3b, lsaudit, mgf15, parlayan_yildizlar_takimi, sivanesh_808, tabriz, tala7985
8.2749 USDC - $8.27
Possible Optimization 1 =
(shareData[_id].shareCreatorPool, shareData[_id].shareHolderRewardsPerTokenScaled, platformPool)
. By reducing the number of state writes, we can save gas. Accumulate the fees in memory variables and write them to the state in a single operation at the end of the function instead of current implementation to save gas.Here is the optimized code snippet:
function _splitFees(uint256 _id, uint256 _fee, uint256 _tokenCount) internal { uint256 shareHolderFee = (_fee * HOLDER_CUT_BPS) / 10_000; uint256 shareCreatorFee = (_fee * CREATOR_CUT_BPS) / 10_000; uint256 platformFee = _fee - shareHolderFee - shareCreatorFee; uint256 newShareCreatorPool = shareData[_id].shareCreatorPool + shareCreatorFee; uint256 newShareHolderRewardsPerTokenScaled = shareData[_id].shareHolderRewardsPerTokenScaled; uint256 newPlatformPool = platformPool + platformFee; if (_tokenCount > 0) { newShareHolderRewardsPerTokenScaled += (shareHolderFee * 1e18) / _tokenCount; } else { newPlatformPool += shareHolderFee; } shareData[_id].shareCreatorPool = newShareCreatorPool; shareData[_id].shareHolderRewardsPerTokenScaled = newShareHolderRewardsPerTokenScaled; platformPool = newPlatformPool; }
Possible Optimization 2 =
shareData[_id]
are read multiple times in functions like buy(), sell(), mintNFT(), and burnNFT(). Each storage read is gas-intensive. Cache shareData[_id]
in a memory variable at the start of the function and use this cached value throughout.Here is the optimized code:
function buy(uint256 _id, uint256 _amount) external { ShareData memory cachedShareData = shareData[_id]; require(cachedShareData.creator != msg.sender, "Creator cannot buy"); // ... rest of the function using cachedShareData instead of shareData[_id] }
Possible Optimization 3 =
string
with bytes32
.Here is the optimized code snippet:
struct ShareData { // ... other variables bytes32 metadataURI; // Instead of string } function createNewShare( bytes32 _shareName, // Instead of string // ... other parameters ) external onlyShareCreator returns (uint256 id) { // ... function logic }
Possible Optimization 1 =
Here is the optimized code snippet:
function getPriceAndFee(uint256 shareCount, uint256 amount) external view override returns (uint256 price, uint256 fee) { uint256 endShareCount = shareCount + amount; // Calculate total price using arithmetic series sum formula price = (amount * (2 * priceIncrease * shareCount + (amount - 1) * priceIncrease)) / 2; // Calculate total fee for (uint256 i = shareCount; i < endShareCount; i++) { uint256 tokenPrice = priceIncrease * i; fee += (getFee(i) * tokenPrice) / 1e18; } }
amount
value.Possible Optimization 2 =
shareCount
value within the loop.Here is the optimized code:
function getPriceAndFee(uint256 shareCount, uint256 amount) external view override returns (uint256 price, uint256 fee) { uint256 endShareCount = shareCount + amount; price = (amount * (2 * priceIncrease * shareCount + (amount - 1) * priceIncrease)) / 2; uint256 lastLog2; uint256 lastShareCount = 0; for (uint256 i = shareCount; i < endShareCount; i++) { uint256 tokenPrice = priceIncrease * i; if (i != lastShareCount) { lastLog2 = log2(i); lastShareCount = i; } fee += (1e17 / (lastLog2 > 1 ? lastLog2 : 1) * tokenPrice) / 1e18; } }
log2
. The savings are more significant for larger loops.#0 - c4-judge
2023-11-29T19:55:42Z
MarioPoneder marked the issue as grade-b
🌟 Selected for report: 0xSmartContract
Also found by: 0xbrett8571, 0xepley, Bauchibred, K42, Kose, MrPotatoMagic, Myd, Sathish9098, aariiif, cats, clara, emerald7017, fouzantanveer, hunter_w3b, invitedtea, unique
19.0443 USDC - $19.04
createNewShare()
: Creates new shares, involves multiple state changes.buy()
: Allows buying of shares, complex logic for pricing and fee calculations.sell()
: Facilitates selling of shares, similar complexity to buy()
.mintNFT()
: Mints NFTs, involves fee calculations.burnNFT()
: Burns NFTs, centralizes fee distribution.ShareData
: Manages token counts, circulation, rewards, and metadata.shareIDs
: Maps share names to their unique IDs.whitelistedBondingCurves
: Tracks approved bonding curves.onlyOwner
modifier in administrative functions could lead to centralization.getPriceAndFee()
: Calculates price and fee for shares, complexity in fee calculations.getFee()
: Determines fee amount, dependent on log2
function.log2()
: Assembly code for logarithmic calculations, security critical.priceIncrease
: Immutable variable defining the price increase per share.create()
: Allows creation of asD tokens, central to token creation mechanism.cNote
: Immutable variable storing the address of the cNOTE token.isAsD
: Mapping to track legitimate asD tokens.mint()
: Mints asD tokens, relies on external contract CErc20Interface
.burn()
: Burns asD tokens for redeeming NOTE, critical for exchange rate integrity.withdrawCarry()
: Withdraw function, centralization risk due to owner-only access.cNote
: Immutable variable storing the address of the cNOTE token.withdrawCarry
function is only callable by the owner.I made function interaction graphs for the key contracts to better visualize interactions, as seen below:
Link to Graph for Market.sol.
Link to Graph for LinearBondingCurve.sol.
Link to Graph for asDFactory.sol.
16 hours
#0 - c4-judge
2023-11-29T20:47:57Z
MarioPoneder marked the issue as grade-b