Platform: Code4rena
Start Date: 06/12/2022
Pot Size: $36,500 USDC
Total HM: 16
Participants: 119
Period: 3 days
Judge: berndartmueller
Total Solo HM: 2
Id: 189
League: ETH
Rank: 71/119
Findings: 1
Award: $31.16
š Selected for report: 0
š Solo Findings: 0
š Selected for report: 0xSmartContract
Also found by: 0x1f8b, 0x4non, 0xA5DF, 0xNazgul, 0xRobocop, 0xfuje, Bnke0x0, HollaDieWaldfee, Lambda, RaymondFam, TomJ, _Adam, adriro, ajtra, carrotsmuggler, cccz, danyams, gasperpre, hansfriese, helios, immeas, joestakey, ladboy233, nameruse, obront, oyc_109, rvierdiiev, saian, sakshamguruji, seyni, slvDev, yixxas, zaskoh
31.1555 USDC - $31.16
Issue | |
---|---|
L-01 | Use of native transfer function is not recommended |
L-02 | Unsafe cast in FixedPrice.buy() |
L-03 | Wrong error string in Escher._grantRole for any other role than CREATOR_ROLE |
L-04 | Remove unused code |
https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/FixedPrice.sol#L109
The protocol contracts use the native transfer
function to transfer ETH to the feeReceiver
or the creator
.
This function will revert if the recipient is a smart contract and its fallback function uses more than 2300 gas.
To avoid any issue with a careless creator
, prefer to use call
to send ETH.
FixedPrice.buy()
While mathematical operations revert on overflow/underflow in Solidity, this is not the case when casting a uint256
.
57: function buy(uint256 _amount) external payable { 58: Sale memory sale_ = sale; 59: IEscher721 nft = IEscher721(sale_.edition); 60: require(block.timestamp >= sale_.startTime, "TOO SOON"); 61: require(_amount * sale_.price == msg.value, "WRONG PRICE"); 62: uint48 newId = uint48(_amount) + sale_.currentId; // @audit - can overflow
If the buyer supplies a _amount
greater than type(uint48).max
, uint48(_amount)
will overflow.
The issue is that at this line, the user will have paid the price of _amount
of tokens, while only receiving a much lower number of NFTs.
As type(uint48).max== 281474976710655
, it is however very unlikely to see this happening - unless the collection has a very high number of tokens.
buy()
should take a uint48
input.
-57: function buy(uint256 _amount) external payable +57: function buy(uint48 _amount) external payable
Escher._grantRole
for any other role than CREATOR_ROLE
61: require(balanceOf(_account, uint256(_role)) == 0, "Already Creator"); //@audit - wrong error if calling for other role
_grantRole
reverts with an "Already Creator" error, which is incorrect if called for another _role
, such as CREATOR_ROLE
or MINTER_ROLE
.
Use a revert string matching the role being added - as it is done in the OpenZeppelin AccessControl
implementation
if (balanceOf(_account, uint256(_role)) != 0) { revert(string(abi.encodePacked( "Already ", Strings.toHexString(uint256(_role), 32); }
This function is not called by any contract in the project
27: function tokenToSeed(uint256 _tokenId) internal view returns (bytes32)
Either remove it, or consider changing the function visibility to external
#0 - c4-judge
2023-01-04T10:37:01Z
berndartmueller marked the issue as grade-b