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: 82/119
Findings: 2
Award: $29.42
π 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/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/FixedPrice.sol#L107-L112 https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/OpenEdition.sol#L89-L94 https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/OpenEdition.sol#L120-L123 https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/LPDA.sol#L81-L88
All three sale contracts rely on auto sending protocolFee and sales proceeds on auction end. The sending algorithm is the same regardless of different sales end conditions and mechanisms, namely
ISaleFactory(factory).feeReceiver().transfer(fee);
saleReceiver
sale.saleReceiver.transfer(totalSale - fee);
OR selfdestruct(_sale.saleReceiver);
If any of the transfer
calls reverts the other won't be executed either. Most significantly if the fee can't be transferred the auction beneficiary will not receive the proceeds. There are two scenarios when this may happen:
Imagine test below, in the context of the provided forge test suite. where in the setUp()
we are setting the fee receiver to an unreceptive contracts address. As a consequence test_WhenMintsOut_Buy()
passes by reverting the sale ending tx while the beneficiaries balances remain the same.
contract Unreceptive { receive() external payable {revert("Don't want your money!");} } contract FixedPriceTest is FixedPriceBaseTest { FixedPrice public sale; Unreceptive private unreceptiveFeeReceiver; event End(FixedPrice.Sale _saleInfo); mapping(uint256 => address) ownersOf; uint256 startId; uint256 finalId; uint256 currentId; function setUp() public override { super.setUp(); // change feeReceiver to unreceptive contract unreceptiveFeeReceiver = new Unreceptive(); fixedSales.setFeeReceiver(payable(unreceptiveFeeReceiver)); } function test_Buy() public { // make the fixed price sale sale = FixedPrice(fixedSales.createFixedSale(fixedSale)); // authorize the fixed price sale to mint tokens edition.grantRole(edition.MINTER_ROLE(), address(sale)); // lets buy some NFTs sale.buy{value: 1 ether}(1); assertEq(address(sale).balance, 1 ether); } function test_WhenMintsOut_Buy() public { test_Buy(); uint256 cached_balance = address(this).balance - 9 ether; // set sale state to be emitted fixedSale.currentId = uint48(10); vm.expectEmit(true, true, false, true); emit End(fixedSale); vm.expectRevert("Don't want your money!"); sale.buy{value: 9 ether}(9); assertEq(address(69).balance, 0); assertEq(address(unreceptiveFeeReceiver).balance, 0 ); } }
Forge
1: Minimum - replace transfer
with call{value:amount}("")
so the sales beneficiary won't be affected in case of failure
2: optimal: refactor the ending mechanism such that
isFinished
, so is the feeAmount
#0 - c4-judge
2022-12-10T12:09:43Z
berndartmueller marked the issue as duplicate of #99
#1 - c4-judge
2023-01-03T12:49:50Z
berndartmueller changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-01-03T12:49:54Z
berndartmueller marked the issue as satisfactory
π Selected for report: tnevler
Also found by: 0xDecorativePineapple, 0xRobocop, 0xbepresent, Chom, Ruhum, Soosh, imare, lukris02, pashov, yellowBirdy, yixxas
28.8137 USDC - $28.81
https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/FixedPrice.sol#L107-L112 https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/OpenEdition.sol#L120-L124
FixedSale.sol and OpenEdition.sol use sefldestruct
to finalise the sale and deliver the proceeds to the designated beneficiary. selfdesgtruct
is, however officially deprecated and will be removed from EVM. Also Escher contracts are non upgradeable. Hence future sales created with these wonβt be able to conclude and beneficiaries wonβt be able to retrieve funds.
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-6049.md
NA
Use address.call{value: amount}("")
instead of selfdestruct
to execute the transfer. It could, also be beneficial to use the 'withdraw' pattern instead of auto sending the proceeds in the sales _end()
function.
#0 - c4-judge
2022-12-11T18:34:59Z
berndartmueller marked the issue as duplicate of #377
#1 - c4-judge
2022-12-11T18:35:04Z
berndartmueller changed the severity to 2 (Med Risk)
#2 - berndartmueller
2023-01-03T15:32:30Z
Applying partial credit as the warden did not demonstrate a concrete impact
#3 - c4-judge
2023-01-03T15:32:35Z
berndartmueller marked the issue as partial-50