Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

[feat] Reserve Auction v1.0#122

Merged
kulkarohan merged 36 commits intomainfrom
reserve-auction
Mar 22, 2022
Merged

[feat] Reserve Auction v1.0#122
kulkarohan merged 36 commits intomainfrom
reserve-auction

Conversation

@kulkarohan
Copy link
Contributor

@kulkarohan kulkarohan commented Jan 25, 2022

New Module Proposal

Description

This module outlines a way for any ERC-721 token holder to create a permissionless reserve auction.

Motivation and Context

ReserveAuctionV1 is an update to ZORA V2 Auction House. NFTs are taken into escrow after the reserve price is met, as well as the current highest bid. After an auction has ended, anyone can settle to payout respective parties and transfer the NFT to the winning bidder.

How has this been tested?

Relevant tests have been included via Foundry.

Checklist:

  • The module includes tests written for Foundry
  • The module is documented with NATSPEC
  • The documentation includes UML Diagrams for external and public functions
  • The module is a Hyperstructure

@kulkarohan kulkarohan added Draft / RFC Community Audit Awaiting Community Audits and removed Draft / RFC Community Audit Awaiting Community Audits labels Jan 25, 2022
@tbtstl tbtstl marked this pull request as ready for review February 9, 2022 21:00
@tbtstl tbtstl added Community Audit Awaiting Community Audits ZORA Bug Bounty Bug Bounty Rewards Available and removed Draft / RFC labels Feb 9, 2022
Copy link
Contributor

@almndbtr almndbtr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ourzora - Thanks again for opening this to community review. ✌🏽

// Mark as first bid
firstBid = true;
// Transfer NFT into escrow
erc721TransferHelper.transferFrom(_tokenContract, auction.seller, address(this), _tokenId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose that this is changed to safely transfer the ERC-721 into escrow:

Suggested change
erc721TransferHelper.transferFrom(_tokenContract, auction.seller, address(this), _tokenId);
erc721TransferHelper.safeTransferFrom(_tokenContract, auction.seller, address(this), _tokenId);

Context

Given the docs for transferFrom:

"Transfers the ownership of a given token ID to another address. Usage of this method is discouraged, use safeTransferFrom whenever possible. Requires the msg.sender to be the owner, approved, or operator."

Because the smart contract takes the ERC-721 in escrow, there might also need to be some additional work for implementing IERC721Receiver.onERC721Received, which is called upon a safe transfer as noted in these docs.

Stepping back, there could be a reason that it's designed this way, and I'm keeping an open mind. I'd love to get feedback from the team: is there a specific reason/rationale for taking this approach? If there is, noting that in the annotations would improve what's already here. 💭

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good question - this is by design. safeTransferFrom is protection on the user side to prevent sending NFTs to contracts that may not be able to handle them. in our case, since we know we're the recipient and can handle them, we can use the simpler transferFrom and save gas

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that context helps! Thank you @kulkarohan.

I was mulling over proposing a follow up change including an annotation there, but I think your input is enough for someone to look at this in the future and figure out why we use the simpler function. Considering this resolved 👍

Suggested change
erc721TransferHelper.transferFrom(_tokenContract, auction.seller, address(this), _tokenId);
// `transferFrom` is enough to move the NFT into escrow since the contract is the recipient
erc721TransferHelper.transferFrom(_tokenContract, auction.seller, address(this), _tokenId);

auctions.settleAuction(address(token), 0);
}

function test_ETHIntegration() public {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertions in this function take into account the royalties paid out to the creator and the finder respectively. However, there doesn’t seem to be an assertion that checks the balances if a protocol fee is set at the time the auction is settled:

remainingProfit = _handleProtocolFeePayout(remainingProfit, auction.currency);

Even if no protocol fee were actually set in these test cases (zero), would the team be open to adding one more require to show that is truly the case, or include a separate set of tests that illustrate what happens when an auction is settled with a non-zero protocol fee? 💭

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your 👍 @kulkarohan -- I'll take some time this week and cut a PR that introduces a test for the protocol fee. 🔪

(If there's a known, hard deadline, do let me know and I can prioritize accordingly.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kulkarohan - Hi again! 👋

I opened #140 introducing tests for when the protocol fee is set to 1 basis point (1 bps or 0.01%). I'd love your evaluative review on it so we have complete test coverage for this module. Thanks in advance!

@almndbtr
Copy link
Contributor

👋 @kulkarohan -- I see some updates being pushed 👀

Is it best to hold off from further review until those settle? 💭

@kulkarohan
Copy link
Contributor Author

hey @almndbtr sorry about that haha we found a few small tweaks earlier that we wanted to make. feel free to further review!

/// @notice This contract allows users to list and bid on ERC-721 tokens with timed reserve auctions
contract ReserveAuctionV1 is ReentrancyGuard, UniversalExchangeEventV1, IncomingTransferSupportV1, FeePayoutSupportV1, ModuleNamingSupportV1 {
/// @dev The indicator to pass all remaining gas when paying out royalties
uint256 private constant USE_ALL_GAS_FLAG = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused var here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cut a PR to replace this with a new constant for the outgoing transfer gas limit: #137

Rohan Kulkarni and others added 2 commits February 21, 2022 20:40
Reserve Auction v1.0: add tests when the protocol fee is non-zero
@kulkarohan
Copy link
Contributor Author

hey @reefloretto ! we're just restructuring a couple things that we'll push today. planning to deploy Friday!

@almndbtr
Copy link
Contributor

planning to deploy Friday!

🙏 Hoping the deploy goes well! Godspeed, Zora team 🚀

address sellerFundsRecipient;
address bidder;
address finder;
uint16 findersFeeBps;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way to save 2 slots would be to limit the size of a few fields to a more reasonable number.

Duration can easily be uint32 (probably not a great idea to have an auction beyond 100 years etc).

uint32+uint16+address is still less than uint256 (one storage slot)

firstBidTime and startTime can both be uint126 to save one slot as well

address
uint16 finderFeeBps
uint32 duration
uint128 firstBidTime
uint128 startTime

saves two slots over the current configuration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iainnash hahaha i have these changes in the update. pushing soon!

@kulkarohan
Copy link
Contributor Author

@everyone

I've restructured Reserve Auction into four gas-optimized variants:

  • Core ETH
  • Core ERC-20
  • Finders ETH
  • Finders ERC-20

'Core' are minimal reserve auctions and 'Finders' add support for finders fee.

An important note: while ETH variants are ETH-only, the ERC-20 variants are still compatible with ETH via setting address(0) as the currency.

So if you'd like the simplicity of interacting with a sole contract, the Finders ERC-20 variant will support all four use-cases (ETH, ERC20s, no finders fee, finders fee) as initially designed. But if you'd like to optimize per use-case, you'll now be able to do so as well.

Lemme know if there are any Qs -- thanks for your patience.

@kulkarohan kulkarohan merged commit d231be2 into main Mar 22, 2022
@kulkarohan kulkarohan deleted the reserve-auction branch March 22, 2022 18:19
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Community Audit Awaiting Community Audits ZORA Bug Bounty Bug Bounty Rewards Available

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants