Escher contest - joestakey's results

A decentralized curated marketplace for editioned artwork.

General Information

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

Escher

Findings Distribution

Researcher Performance

Rank: 71/119

Findings: 1

Award: $31.16

QA:
grade-b

🌟 Selected for report: 0

šŸš€ Solo Findings: 0

Awards

31.1555 USDC - $31.16

Labels

bug
grade-b
QA (Quality Assurance)
Q-32

External Links

Summary

Low Risk

Issue
L-01Use of native transfer function is not recommended
L-02Unsafe cast in FixedPrice.buy()
L-03Wrong error string in Escher._grantRole for any other role than CREATOR_ROLE
L-04Remove unused code

Low

[L‑01] Use of native transfer function is not recommended

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.

[L‑02] Unsafe cast in 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.

Recommendation

buy() should take a uint48 input.

-57:  function buy(uint256 _amount) external payable
+57:  function buy(uint48 _amount) external payable

[L‑03] Wrong error string in Escher._grantRole for any other role than CREATOR_ROLE

https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/Escher.sol#L61

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.

Recommendation

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);
}

[L‑04] Remove unused code

This function is not called by any contract in the project

https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/uris/Generative.sol#L27

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

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Ā© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter