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: 117/119
Findings: 1
Award: $0.61
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: RaymondFam
Also found by: 0xdeadbeef0x, 0xhacksmithh, AkshaySrivastav, Awesome, Bnke0x0, CRYP70, HollaDieWaldfee, JC, Parth, Rahoz, Tutturu, __141345__, ahmedov, ajtra, asgeir, aviggiano, bin2chen, btk, carrotsmuggler, cccz, chaduke, cryptonue, dic0de, fatherOfBlocks, fs0c, hansfriese, jonatascm, karanctf, ladboy233, lumoswiz, martin, obront, pashov, pauliax, rvierdiiev, shark, simon135, supernova, tourist, yellowBirdy, zapaz, zaskoh
0.6136 USDC - $0.61
https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L85-L86 https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L105 https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/FixedPrice.sol#L66 https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L74 https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/OpenEdition.sol#L67
Note: C4 declared this finding as an unsafe ERC20 operation in the c4udit output, which is not true (The Escher protocol does not support erc20 tokens). This is a deprecated transfer()
that could significantly damage the protocol and the users.
If saleReceiver
and feeReceiver
where smart contracts and one of the scenarios described in the POC occurred, neither of them will be able to withdraw their funds, and they (funds) will be frozen in the contract, resulting in a loss of funds for both users and the protocol.
The use of the deprecated transfer()
function for a payable address will certainly make the transaction fail when:
The claimer smart contract does not implement a payable fallback function.
The claimer smart contract does implement a payable fallback which uses more than 2300 gas unit.
The claimer smart contract implements a payable fallback function that needs less than 2300 gas units but is called through proxy, raising the call's gas usage above 2300.
Some multisig wallets might also require higher gas than 2300.
A more detailed explanation can be found here.
Lines of code:
https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L85-L86
https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L105
Manual Review
Consider using .call() instead with the CEI pattern implemented correctly. .
(bool success, ) = payable(msg.sender).call{value: owed}(""); require(success, "Call failed");
(bool success, ) = ISaleFactory(factory).feeReceiver().call{value: address(this).balance / 20}(""); require(success, "Call failed");
Users could lost their NFTs.
The msg.sender
will be minted an nft when buy is called.
However, if msg.sender is a contract address that does not support ERC721, the NFT can be frozen in the contract forever.
As per the documentation of EIP-721:
A wallet/broker/auction application MUST implement the wallet interface if it will accept safe transfers.
Ref: https://eips.ethereum.org/EIPS/eip-721
As per the documentation of ERC721.sol by Openzeppelin:
Lines of code:
https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/FixedPrice.sol#L66
https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L74
https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/OpenEdition.sol#L67
Manual Review
Use safeMint instead of mint
to check received address support for ERC721 implementation.
function _safeMint( address to, uint256 tokenId, bytes memory data ) internal virtual { _mint(to, tokenId); require( _checkOnERC721Received(address(0), to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer" ); }
#0 - c4-judge
2022-12-10T00:31:11Z
berndartmueller marked the issue as duplicate of #99
#1 - c4-judge
2023-01-03T12:48:59Z
berndartmueller marked the issue as satisfactory