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: 55/120
Findings: 2
Award: $12.35
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: chaduke
Also found by: 0xpiken, Bauchibred, Matin, MohammedRizwan, MrPotatoMagic, OMEN, Pheonix, SandNallani, T1MOH, Topmark, ZanyBonzy, adriro, aslanbek, ayden, bareli, bart1e, bin2chen, btk, cheatc0d3, codynhat, critical-or-high, d3e4, erebus, firmanregar, hunter_w3b, jasonxiale, kaveyjoe, ksk2345, lsaudit, max10afternoon, merlinboii, nailkhalimov, osmanozdemir1, peanuts, pep7siup, pontifex, sbaudh6, shenwilly, sl1, tourist, wisdomn_, young, zhaojie
4.0797 USDC - $4.08
https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L91
Using tx.origin
can lead to security vulnerabilities. It's generally safer to use msg.sender
for authentication purposes.
constructor(string memory _uri, address _paymentToken) ERC1155(_uri) Ownable() { token = IERC20(_paymentToken); if (block.chainid == 7700 || block.chainid == 7701) { // Register CSR on Canto main- and testnet Turnstile turnstile = Turnstile(0xEcf044C5B4b867CFda001101c617eCd347095B44); turnstile.register(msg.sender); } }
Access Control
#0 - c4-pre-sort
2023-11-20T02:23:58Z
minhquanym marked the issue as primary issue
#1 - c4-pre-sort
2023-11-20T04:49:25Z
minhquanym marked the issue as insufficient quality report
#2 - minhquanym
2023-11-20T04:49:26Z
QA
#3 - c4-judge
2023-11-29T16:01:43Z
MarioPoneder changed the severity to QA (Quality Assurance)
#4 - c4-judge
2023-11-29T21:15:39Z
MarioPoneder marked the issue as grade-b
🌟 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
https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L80
The condition msg.sender == owner()
is moved to the beginning. Since the owner of the contract should always have permission, checking this condition first can potentially save gas if the sender is the owner, as it avoids the other checks.
First, check if the sender is the owner (most privileged role), then check if the share creation is not restricted (general condition), and finally, check if the sender is a whitelisted share creator (specific privilege).
modifier onlyShareCreator() { require( msg.sender == owner() || !shareCreationRestricted || whitelistedShareCreators[msg.sender], "Not allowed" ); _; }
shareData[id]
struct to save gas in createNewShare
function. (GAS)https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L114
function createNewShare( string calldata _shareName, address _bondingCurve, string calldata _metadataURI ) external onlyShareCreator returns (uint256 id) { require(whitelistedBondingCurves[_bondingCurve], "Bonding curve not whitelisted"); require(shareIDs[_shareName] == 0, "Share already exists"); id = ++shareCount; shareIDs[_shareName] = id; ShareData storage newShare = shareData[id]; newShare.bondingCurve = _bondingCurve; newShare.creator = msg.sender; newShare.metadataURI = _metadataURI; emit ShareCreated(id, _shareName, _bondingCurve, msg.sender); }
getBuyPrice
function (GAS)The shareData[_id]
storage is accessed twice. We can optimize this by storing the needed data in a local variable.
https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L132
function getBuyPrice(uint256 _id, uint256 _amount) public view returns (uint256 price, uint256 fee) { ShareData storage share = shareData[_id]; require(share.bondingCurve != address(0), "Invalid ID"); (price, fee) = IBondingCurve(share.bondingCurve).getPriceAndFee(share.tokenCount + 1, _amount); }
getSellPrice
function (GAS)function getSellPrice(uint256 _id, uint256 _amount) public view returns (uint256 price, uint256 fee) { ShareData storage share = shareData[_id]; require(share.bondingCurve != address(0), "Invalid ID"); (price, fee) = IBondingCurve(share.bondingCurve).getPriceAndFee(tokenCount - _amount + 1, _amount); }
buy
and sell
functions (GAS)https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L150 https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L174
ShareData storage share = shareData[_id]; uint256 sellerBalance = tokensByAddress[_id][msg.sender];
buy
and sell
functions to save gas. (GAS)For subtraction operations like shareData[_id].tokensInCirculation -= _amount;
that cannot underflow, we can use unchecked {} to avoid the safe math check and save gas.
https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L211
getNFTMintingPrice
function. (GAS)https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L194
ShareData storage share = shareData[_id];
getNFTMintingPrice
function (GAS)https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L194
Directly call the interface function without storing the intermediate values.
function getNFTMintingPrice(uint256 _id, uint256 _amount) public view returns (uint256 fee) { ShareData storage share = shareData[_id]; require(share.bondingCurve != address(0), "Invalid ID"); unchecked { (, uint256 priceForOne) = IBondingCurve(share.bondingCurve).getPriceAndFee(share.tokenCount, 1); fee = (priceForOne * _amount * NFT_FEE_BPS) / 10_000; } }
getNFTMintingPrice
function calculation to sava gas (GAS)https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L194
function getNFTMintingPrice(uint256 _id, uint256 _amount) public view returns (uint256 fee) { ShareData storage share = shareData[_id]; require(share.bondingCurve != address(0), "Invalid ID"); unchecked { (, uint256 priceForOne) = IBondingCurve(share.bondingCurve).getPriceAndFee(share.tokenCount, 1); fee = (priceForOne * _amount * NFT_FEE_BPS) / 10_000; } }
mintNFT
and burnNFT
function (GAS)https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L203 https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L226
ShareData storage share = shareData[_id]; uint256 sellerBalance = tokensByAddress[_id][msg.sender];
mintNFT
and burnNFT
function (GAS)https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L203 https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L226
unchecked { shareData[_id].tokensInCirculation -= _amount; tokensByAddress[_id][msg.sender] = sellerBalance - _amount; }
burnNFT
function to potentially save gas (GAS)https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L226
uint256 rewardsSinceLastClaim = _getRewardsSinceLastClaim(_id); if (rewardsSinceLastClaim > 0) { SafeERC20.safeTransfer(token, msg.sender, rewardsSinceLastClaim); }
#0 - c4-pre-sort
2023-11-24T06:52:33Z
minhquanym marked the issue as high quality report
#1 - c4-sponsor
2023-11-27T09:28:23Z
OpenCoreCH (sponsor) confirmed
#2 - c4-judge
2023-11-29T19:43:38Z
MarioPoneder marked the issue as grade-b