NFT Dealers

First Flight #58
Beginner FriendlyFoundry
100 EXP
Submission Details
Impact: low
Likelihood: low

buy() and mintNft() Accept ETH via payable Modifier — ETH Permanently Locked

Author Revealed upon completion

Root + Impact

buy() and mintNft() Accept ETH via payable Modifier — ETH Permanently Locked

Description

  • buy() and mintNft() are both marked payable but operate exclusively with USDC via transferFrom. They have no logic to handle, refund, or account for ETH.

Any ETH sent alongside these calls is accepted by the contract and permanently locked. The contract has no receive() function, no withdraw() for ETH, and no fallback,there is no recovery path.

@> function mintNft() external payable {
// Only transfers USDC — no ETH handling
usdc.transferFrom(msg.sender, address(this), collateralAmount);
// ...
}
@> function buy(uint256 _listingId) external payable {
// Only transfers USDC — no ETH handling
usdc.transferFrom(msg.sender, address(this), listing.price);
// ...
}

Risk

Likelihood:

  • Users interacting directly with the contract (not via a frontend) may send ETH assuming it is the payment method, especially given the payable modifier's presence in the ABI

Wallet interfaces display a value field for payable functions, inviting ETH attachment

Impact:

  • Any ETH sent is permanently locked with no recovery mechanism

Users lose ETH with no way to retrieve it — no owner withdrawal function exists for native ETH

Proof of Concept

function test_NM008_EthLockedForever() public {
address alice = makeAddr("alice");
deal(alice, 1 ether);
deal(address(usdc), alice, 100e6);
vm.startPrank(alice);
usdc.approve(address(nftDealers), 100e6);
// Alice accidentally sends 0.5 ETH with mintNft
nftDealers.mintNft{value: 0.5 ether}();
vm.stopPrank();
// ETH is locked in the contract forever
assertEq(address(nftDealers).balance, 0.5 ether);
// No way to recover — no receive(), no withdraw(), no selfdestruct
}

Recommended Mitigation

- function mintNft() external payable {
+ function mintNft() external {
- function buy(uint256 _listingId) external payable {
+ function buy(uint256 _listingId) external {

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!