From e3c8ecd4ae9782093cdb964f20af7a6684fd576c Mon Sep 17 00:00:00 2001 From: spengrah Date: Fri, 14 Jun 2024 17:40:47 -0500 Subject: [PATCH] contract compiling --- src/LatestNounsBuilderNFTEligibility.sol | 110 +++++++++++++++++++++++ src/lib/IAuction.sol | 42 +++++++++ src/lib/IToken.sol | 25 ++++++ 3 files changed, 177 insertions(+) create mode 100644 src/LatestNounsBuilderNFTEligibility.sol create mode 100644 src/lib/IAuction.sol create mode 100644 src/lib/IToken.sol diff --git a/src/LatestNounsBuilderNFTEligibility.sol b/src/LatestNounsBuilderNFTEligibility.sol new file mode 100644 index 0000000..2a686a7 --- /dev/null +++ b/src/LatestNounsBuilderNFTEligibility.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +// import { console2 } from "forge-std/Test.sol"; // remove before deploy +import { HatsModule, HatsEligibilityModule, IHatsEligibility } from "hats-module/HatsEligibilityModule.sol"; +import { IToken } from "./lib/IToken.sol"; +import { IAuction } from "./lib/IAuction.sol"; + +contract LatestNounsBuilderNFTEligibility is HatsEligibilityModule { + /*////////////////////////////////////////////////////////////// + CONSTANTS + //////////////////////////////////////////////////////////////*/ + + /** + * This contract is a clone with immutable args, which means that it is deployed with a set of + * immutable storage variables (ie constants). Accessing these constants is cheaper than accessing + * regular storage variables (such as those set on initialization of a typical EIP-1167 clone), + * but requires a slightly different approach since they are read from calldata instead of storage. + * + * Below is a table of constants and their location. + * + * For more, see here: https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args + * + * ----------------------------------------------------------------------+ + * CLONE IMMUTABLE "STORAGE" | + * ----------------------------------------------------------------------| + * Offset | Constant | Type | Length | Source | + * ----------------------------------------------------------------------| + * 0 | IMPLEMENTATION | address | 20 | HatsModule | + * 20 | HATS | address | 20 | HatsModule | + * 40 | hatId | uint256 | 32 | HatsModule | + * 72 | TOKEN | IToken | 20 | this | + * ----------------------------------------------------------------------+ + */ + function TOKEN() public pure returns (IToken) { + return IToken(_getArgAddress(72)); + } + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + /// @notice Deploy the implementation contract and set its version + /// @dev This is only used to deploy the implementation contract, and should not be used to deploy clones + constructor(string memory _version) HatsModule(_version) { } + + /*////////////////////////////////////////////////////////////// + INITIALIZOR + //////////////////////////////////////////////////////////////*/ + + /// @inheritdoc HatsModule + function _setUp(bytes calldata _initData) internal override { + // decode init data + } + + /*////////////////////////////////////////////////////////////// + HATS ELIGIBILITY FUNCTION + //////////////////////////////////////////////////////////////*/ + + /// @inheritdoc IHatsEligibility + function getWearerStatus(address _wearer, uint256 /*_hatId*/ ) + public + view + virtual + override + returns (bool eligible, bool standing) + { + // The wearer is eligible only if they own the latest token id + // We need to catch the case where the previous auction was settled without a winner + try TOKEN().ownerOf(getLastAuctionedTokenId()) returns (address owner) { + // the last auctioned token id has a value owner, so we check if it matches the wearer + eligible = owner == _wearer; + } catch { + // if the last auctioned token id does not have an owner, so we know the wearer is not eligible + // eligible is false by default + } + + // This module does not deal with standing, so we default to good standing (true) + standing = true; + } + + /*////////////////////////////////////////////////////////////// + VIEW FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Get the id of the most recently auctioned token for `_token`. + * If the auction was settled without a winner, the returned token id will not have an owner. + * + * @dev Return the present auction's token id if it has been settled with a winner, otherwise it returns + * the id of the previous auction. + * + * @return The id of the most recently auctioned token. + */ + function getLastAuctionedTokenId() public view returns (uint256) { + // get the auction contract + IAuction auctionContract = IAuction(TOKEN().auction()); + + // get the data for the current auction + IAuction.Auction memory currentAuction = auctionContract.auction(); + + // if the auction is settled with a winner, we want the current auction's token; + if (currentAuction.settled && currentAuction.highestBidder > address(0)) { + return currentAuction.tokenId; + } else { + // otherwise, we want the previous auction's token + return currentAuction.tokenId - 1; + } + } +} diff --git a/src/lib/IAuction.sol b/src/lib/IAuction.sol new file mode 100644 index 0000000..f80125d --- /dev/null +++ b/src/lib/IAuction.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +/// @notice Sourced from +/// https://github.com/ourzora/nouns-protocol/blob/98b65e2368c52085ff3844779afd45162eb1cc7d/src/auction/storage/AuctionStorageV1 +interface IAuction { + /// @notice The auction type + /// @param tokenId The ERC-721 token id + /// @param highestBid The highest amount of ETH raised + /// @param highestBidder The leading bidder + /// @param startTime The timestamp the auction starts + /// @param endTime The timestamp the auction ends + /// @param settled If the auction has been settled + struct Auction { + uint256 tokenId; + uint256 highestBid; + address highestBidder; + uint40 startTime; + uint40 endTime; + bool settled; + } + + /// @notice The current auction + function auction() external view returns (Auction memory); + + /// @notice Settles the current auction and creates the next one + function settleCurrentAndCreateNewAuction() external; + + /// @notice Pauses the auction house + function pause() external; + + /// @notice Unpauses the auction house + function unpause() external; + + /// @notice Settles the latest auction when the contract is paused + function settleAuction() external; + + function createBid(uint256 tokenId) external payable; + + /// @notice The owner of the auction house + function owner() external view returns (address); +} diff --git a/src/lib/IToken.sol b/src/lib/IToken.sol new file mode 100644 index 0000000..ed1b69c --- /dev/null +++ b/src/lib/IToken.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +/// @dev Excerpt sourced from +/// https://github.com/ourzora/nouns-protocol/blob/98b65e2368c52085ff3844779afd45162eb1cc7d/src/token/IToken.sol +interface IToken { + /// @notice The address of the auction house + function auction() external view returns (address); + + /// @notice The total number of tokens that can be claimed from the reserve + function remainingTokensInReserve() external view returns (uint256); + + /// @notice The total supply of tokens + function totalSupply() external view returns (uint256); + + /// @notice The owner of a token + /// @param tokenId The ERC-721 token id + function ownerOf(uint256 tokenId) external view returns (address); + + /// @notice Mints the specified amount of tokens to the recipient and handles founder vesting + function mintTo(address recipient) external returns (uint256 tokenId); + + /// @notice Transfers a token from one address to another + function transferFrom(address from, address to, uint256 tokenId) external; +}