Playbook
BareNFTAuction: Claim, Refund, and Emergency Powers (Barenft Auction Claim Refund Emergency Powers)
English auction flows: bid, claim, cancel, below-reserve refunds, and owner emergency transferNft/transferFunds trade-offs.
Bare Crypto Solidity Marketplace Protocol
Part 4 of 5
BareNFT, reserve escrow, English auction, and NFT-gated BareToken emissions built in Remix IDE with OpenZeppelin v4.1 GitHub imports — no Hardhat/Foundry — covering ADRs, weak RNG, and emergency-power trade-offs.
The clock ends; ETH and NFT (Non-Fungible Token) must unwind together
BareNFTAuction is an owner-operated English auction: the NFT sits in escrow, each bid instantly refunds the previous maxBidder, and claimTokenFromAuctionByTokenId distributes NFT and ETH by reserve rules. cancelAuctionByTokenId refunds the max bid and returns the NFT to the seller. Emergency transferNft/transferFunds again sit with the owner — recovery and rug share one door.
Owner escrow NFT → createNewAuction(endBlock, reserve, id, start)
|
bid() > maxBid ── refund previous maxBidder
|
endBlock reached
|
claim: maxBid < reserve? refund + NFT→seller
else ETH→seller, NFT→maxBidder
|
cancel / emergency transfer*
This part locks the auction lifecycle, claim/refund paths, and the ADR trade-off for emergency powers.
Concepts, defined where they first appear
📦 English auction
Open ascending bids; each new bid beats the leader and triggers a refund.
📦 Reserve price
If max bid is below reserve, no sale: NFT returns to seller, bid refunds.
📦 Claim settlement
After end, seller or maxBidder calls distribution of NFT/ETH.
📦 Push refund
Immediate ETH return to the previous maxBidder when a higher bid arrives.
Push refunds simplify UX but can stall the whole bid path on a failing receive; pull-payment is an alternate ADR.
ADR: owner-created English auction
Decision: only the owner creates auctions; endBlock must be after the current block; no new auction for a tokenId until the prior one finishes. Rationale: Bare Crypto schedules vitrine auctions on an operator calendar. Cost: no permissionless seller auctions; setTokenContract and setOwner are also owner-bound — the protocol surface hangs on the operator key.
Bid, claim, cancel flow
bid checks startingBid and maxBid, refunds the previous maxBidder via call{value}, then updates maxBid/maxBidder. claimTokenFromAuctionByTokenId: no maxBidder returns NFT to seller; maxBid below reserve refunds and returns NFT; otherwise pays seller and sends NFT to the winner. cancel refunds and returns NFT while open. Those three paths try to keep the escrow invariant that money and NFT unwind together.
bid → refund old leader → set new leader
claim:
no bid → NFT seller
below reserve → refund + NFT seller
else → pay seller + NFT winner
cancel → refund leader + NFT seller
Failure: stuck refunds, griefing, emergency
If the previous bidder is a contract that reverts on receive, the new bid dies on require(success) — a griefing vector. On claim, a failed seller.call may still risk continuing into NFT transfer if success is not enforced on that branch. Emergency transferNft pulls the NFT without settling ETH; the maxBidder's funds can stay trapped. Performance: every bid pushes onto Bid[]; long histories bloat storage and view gas.
The mappings that get confused most often
❌ English auction means anyone can open an auction
✓ In this implementation createNewAuction is owner-only
❌ Bids below reserve are burned
✓ On the claim path the maxBidder is refunded and the NFT returns to the seller
❌ Emergency NFT pull also clears bids
✓ transferNft does not auto-refund ETH; funds can remain stuck in the contract
A checklist for auditing your own system
- Who may call claim — only maxBidder, or seller too?
- What happens to a new bid if the previous bidder reverts on receive?
- After a below-reserve claim, which state does the UI show?
- How do cancel and emergency transferNft differ for funds?
- How large can Bid[] grow, and is there a view limit?
What to take away from this part
- BareNFTAuction is an owner-operated English auction with explicit claim/refund paths.
- Push refund UX convenience arrives with griefing and stuck-bid risk.
- Emergency NFT pulls do not automatically settle the ETH side.
An end block is not enough; you need a claim path that closes ETH and NFT together.
FAQ
Frequently asked questions
What is English auction?
Open ascending bids; each new bid beats the leader and triggers a refund.
What is Reserve price?
If max bid is below reserve, no sale: NFT returns to seller, bid refunds.
Is it true that "English auction means anyone can open an auction"?
In this implementation createNewAuction is owner-only
What does this part lock in?
This part locks the auction lifecycle, claim/refund paths, and the ADR trade-off for emergency powers. BareNFTAuction is an owner-operated English auction: the NFT sits in escrow, each bid instantly refunds the previous maxBidder, and claimTokenFromAuctionByTokenId distributes NFT and ETH by reserve rules. cancelAuctionByTokenId refunds the max bid and returns the NFT to the seller. Emergency transferNft/transferFunds again sit with the owner — recovery and rug share one door.
Engineering Principles Learned
- An auction ADR must write create authority, reserve rules, and refund model together.
- If push refund is chosen, document griefing and the pull-payment alternative.
- Emergency NFT/ETH exits must be designed without breaking the claim invariant.
Continue reading
Continue reading
Next in series
BareToken: NFT (Non-Fungible Token)-Gated Emissions and Abuse
Hashmasks-style NFT-gated claims: ~10e18/day, INITIAL_ALLOTMENT, 10-year emissionEnd — plus open mint surface and abuse scenarios.
Next in series
BareNFTReserve: Escrow, RandomBuy, and Weak RNG
Owner-only createNewListing, buy, and randomBuy using keccak256(revealNonce, block.difficulty, msg.sender) % 3 — weak RNG, owner-operated marketplace ADR…
Same series
BareNFT: Roles, Pausable, and Per-Token URI
BareNFT combines ERC721 Enumerable/Burnable/Pausable with AccessControl and role-gated mint(to, id, uri). Trade-offs of per-token URI and pause.