ETH Price: $2,442.62 (+0.05%)

Transaction Decoder

Block:
16449234 at Jan-20-2023 04:32:47 PM +UTC
Transaction Fee:
0.001783097235017058 ETH $4.36
Gas Used:
55,137 Gas / 32.339395234 Gwei

Emitted Events:

289 ERC1967Proxy.0x1edd36b28569cf1cf930c008c5bcb196ea19525977951413bec3118946951561( 0x1edd36b28569cf1cf930c008c5bcb196ea19525977951413bec3118946951561, 0000000000000000000000000000000000000000000000000000000000000006, 000000000000000000000000c2efad9a2ee3f965ee773dfba7124c9aff8b9f4e, 000000000000000000000000000000000000000000000000002386f26fc10000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000063cadf75 )

Account State Difference:

  Address   Before After State Difference Code
0x25a939f3...D02a31575 0.0069 Eth0.01 Eth0.0031
(builder0x69)
2.091383781686374252 Eth2.091466487186374252 Eth0.0000827055
0x6a56f861...7f67aF0c2 0.510356749729360265 Eth0.517256749729360265 Eth0.0069
0xC2eFad9A...aFF8B9f4E
0.0503056654407529 Eth
Nonce: 750
0.038522568205735842 Eth
Nonce: 751
0.011783097235017058

Execution Trace

ETH 0.01 ERC1967Proxy.659dd2b4( )
  • ETH 0.01 Auction.createBid( _tokenId=6 )
    • ETH 0.0069 0x6a56f8613c494c95de6adccbf9747b37f67af0c2.CALL( )
      File 1 of 2: ERC1967Proxy
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      
      // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
      
      /**
       * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
       * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
       * be specified by overriding the virtual {_implementation} function.
       *
       * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
       * different contract through the {_delegate} function.
       *
       * The success and return data of the delegated call will be returned back to the caller of the proxy.
       */
      abstract contract Proxy {
          /**
           * @dev Delegates the current call to `implementation`.
           *
           * This function does not return to its internal call site, it will return directly to the external caller.
           */
          function _delegate(address implementation) internal virtual {
              assembly {
                  // Copy msg.data. We take full control of memory in this inline assembly
                  // block because it will not return to Solidity code. We overwrite the
                  // Solidity scratch pad at memory position 0.
                  calldatacopy(0, 0, calldatasize())
      
                  // Call the implementation.
                  // out and outsize are 0 because we don't know the size yet.
                  let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
      
                  // Copy the returned data.
                  returndatacopy(0, 0, returndatasize())
      
                  switch result
                  // delegatecall returns 0 on error.
                  case 0 {
                      revert(0, returndatasize())
                  }
                  default {
                      return(0, returndatasize())
                  }
              }
          }
      
          /**
           * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function
           * and {_fallback} should delegate.
           */
          function _implementation() internal view virtual returns (address);
      
          /**
           * @dev Delegates the current call to the address returned by `_implementation()`.
           *
           * This function does not return to its internal call site, it will return directly to the external caller.
           */
          function _fallback() internal virtual {
              _beforeFallback();
              _delegate(_implementation());
          }
      
          /**
           * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
           * function in the contract matches the call data.
           */
          fallback() external payable virtual {
              _fallback();
          }
      
          /**
           * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
           * is empty.
           */
          receive() external payable virtual {
              _fallback();
          }
      
          /**
           * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
           * call, or as part of the Solidity `fallback` or `receive` functions.
           *
           * If overridden should call `super._beforeFallback()`.
           */
          function _beforeFallback() internal virtual {}
      }
      
      /// @title IERC1967Upgrade
      /// @author Rohan Kulkarni
      /// @notice The external ERC1967Upgrade events and errors
      interface IERC1967Upgrade {
          ///                                                          ///
          ///                            EVENTS                        ///
          ///                                                          ///
      
          /// @notice Emitted when the implementation is upgraded
          /// @param impl The address of the implementation
          event Upgraded(address impl);
      
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
      
          /// @dev Reverts if an implementation is an invalid upgrade
          /// @param impl The address of the invalid implementation
          error INVALID_UPGRADE(address impl);
      
          /// @dev Reverts if an implementation upgrade is not stored at the storage slot of the original
          error UNSUPPORTED_UUID();
      
          /// @dev Reverts if an implementation does not support ERC1822 proxiableUUID()
          error ONLY_UUPS();
      }
      
      // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
      
      /**
       * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
       * proxy whose upgrades are fully controlled by the current implementation.
       */
      interface IERC1822Proxiable {
          /**
           * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
           * address.
           *
           * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
           * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
           * function revert if invoked through a proxy.
           */
          function proxiableUUID() external view returns (bytes32);
      }
      
      // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
      
      /**
       * @dev Library for reading and writing primitive types to specific storage slots.
       *
       * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
       * This library helps with reading and writing to such slots without the need for inline assembly.
       *
       * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
       *
       * Example usage to set ERC1967 implementation slot:
       * ```
       * contract ERC1967 {
       *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
       *
       *     function _getImplementation() internal view returns (address) {
       *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
       *     }
       *
       *     function _setImplementation(address newImplementation) internal {
       *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
       *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
       *     }
       * }
       * ```
       *
       * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
       */
      library StorageSlot {
          struct AddressSlot {
              address value;
          }
      
          struct BooleanSlot {
              bool value;
          }
      
          struct Bytes32Slot {
              bytes32 value;
          }
      
          struct Uint256Slot {
              uint256 value;
          }
      
          /**
           * @dev Returns an `AddressSlot` with member `value` located at `slot`.
           */
          function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
      
          /**
           * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
           */
          function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
      
          /**
           * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
           */
          function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
      
          /**
           * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
           */
          function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
      }
      
      /// @title EIP712
      /// @author Rohan Kulkarni
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (utils/Address.sol)
      /// - Uses custom errors `INVALID_TARGET()` & `DELEGATE_CALL_FAILED()`
      /// - Adds util converting address to bytes32
      library Address {
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
      
          /// @dev Reverts if the target of a delegatecall is not a contract
          error INVALID_TARGET();
      
          /// @dev Reverts if a delegatecall has failed
          error DELEGATE_CALL_FAILED();
      
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
      
          /// @dev Utility to convert an address to bytes32
          function toBytes32(address _account) internal pure returns (bytes32) {
              return bytes32(uint256(uint160(_account)) << 96);
          }
      
          /// @dev If an address is a contract
          function isContract(address _account) internal view returns (bool rv) {
              assembly {
                  rv := gt(extcodesize(_account), 0)
              }
          }
      
          /// @dev Performs a delegatecall on an address
          function functionDelegateCall(address _target, bytes memory _data) internal returns (bytes memory) {
              if (!isContract(_target)) revert INVALID_TARGET();
      
              (bool success, bytes memory returndata) = _target.delegatecall(_data);
      
              return verifyCallResult(success, returndata);
          }
      
          /// @dev Verifies a delegatecall was successful
          function verifyCallResult(bool _success, bytes memory _returndata) internal pure returns (bytes memory) {
              if (_success) {
                  return _returndata;
              } else {
                  if (_returndata.length > 0) {
                      assembly {
                          let returndata_size := mload(_returndata)
      
                          revert(add(32, _returndata), returndata_size)
                      }
                  } else {
                      revert DELEGATE_CALL_FAILED();
                  }
              }
          }
      }
      
      /// @title ERC1967Upgrade
      /// @author Rohan Kulkarni
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/ERC1967/ERC1967Upgrade.sol)
      /// - Uses custom errors declared in IERC1967Upgrade
      /// - Removes ERC1967 admin and beacon support
      abstract contract ERC1967Upgrade is IERC1967Upgrade {
          ///                                                          ///
          ///                          CONSTANTS                       ///
          ///                                                          ///
      
          /// @dev bytes32(uint256(keccak256('eip1967.proxy.rollback')) - 1)
          bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
      
          /// @dev bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
          bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
      
          ///                                                          ///
          ///                          FUNCTIONS                       ///
          ///                                                          ///
      
          /// @dev Upgrades to an implementation with security checks for UUPS proxies and an additional function call
          /// @param _newImpl The new implementation address
          /// @param _data The encoded function call
          function _upgradeToAndCallUUPS(
              address _newImpl,
              bytes memory _data,
              bool _forceCall
          ) internal {
              if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                  _setImplementation(_newImpl);
              } else {
                  try IERC1822Proxiable(_newImpl).proxiableUUID() returns (bytes32 slot) {
                      if (slot != _IMPLEMENTATION_SLOT) revert UNSUPPORTED_UUID();
                  } catch {
                      revert ONLY_UUPS();
                  }
      
                  _upgradeToAndCall(_newImpl, _data, _forceCall);
              }
          }
      
          /// @dev Upgrades to an implementation with an additional function call
          /// @param _newImpl The new implementation address
          /// @param _data The encoded function call
          function _upgradeToAndCall(
              address _newImpl,
              bytes memory _data,
              bool _forceCall
          ) internal {
              _upgradeTo(_newImpl);
      
              if (_data.length > 0 || _forceCall) {
                  Address.functionDelegateCall(_newImpl, _data);
              }
          }
      
          /// @dev Performs an implementation upgrade
          /// @param _newImpl The new implementation address
          function _upgradeTo(address _newImpl) internal {
              _setImplementation(_newImpl);
      
              emit Upgraded(_newImpl);
          }
      
          /// @dev Stores the address of an implementation
          /// @param _impl The implementation address
          function _setImplementation(address _impl) private {
              if (!Address.isContract(_impl)) revert INVALID_UPGRADE(_impl);
      
              StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = _impl;
          }
      
          /// @dev The address of the current implementation
          function _getImplementation() internal view returns (address) {
              return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
          }
      }
      
      /// @title ERC1967Proxy
      /// @author Rohan Kulkarni
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/ERC1967/ERC1967Proxy.sol)
      /// - Inherits a modern, minimal ERC1967Upgrade
      contract ERC1967Proxy is IERC1967Upgrade, Proxy, ERC1967Upgrade {
          ///                                                          ///
          ///                         CONSTRUCTOR                      ///
          ///                                                          ///
      
          /// @dev Initializes the proxy with an implementation contract and encoded function call
          /// @param _logic The implementation address
          /// @param _data The encoded function call
          constructor(address _logic, bytes memory _data) payable {
              _upgradeToAndCall(_logic, _data, false);
          }
      
          ///                                                          ///
          ///                          FUNCTIONS                       ///
          ///                                                          ///
      
          /// @dev The address of the current implementation
          function _implementation() internal view virtual override returns (address) {
              return ERC1967Upgrade._getImplementation();
          }
      }

      File 2 of 2: Auction
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
       * proxy whose upgrades are fully controlled by the current implementation.
       */
      interface IERC1822Proxiable {
          /**
           * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
           * address.
           *
           * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
           * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
           * function revert if invoked through a proxy.
           */
          function proxiableUUID() external view returns (bytes32);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `to`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address to, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `from` to `to` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address from,
              address to,
              uint256 amount
          ) external returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Library for reading and writing primitive types to specific storage slots.
       *
       * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
       * This library helps with reading and writing to such slots without the need for inline assembly.
       *
       * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
       *
       * Example usage to set ERC1967 implementation slot:
       * ```
       * contract ERC1967 {
       *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
       *
       *     function _getImplementation() internal view returns (address) {
       *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
       *     }
       *
       *     function _setImplementation(address newImplementation) internal {
       *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
       *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
       *     }
       * }
       * ```
       *
       * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
       */
      library StorageSlot {
          struct AddressSlot {
              address value;
          }
          struct BooleanSlot {
              bool value;
          }
          struct Bytes32Slot {
              bytes32 value;
          }
          struct Uint256Slot {
              uint256 value;
          }
          /**
           * @dev Returns an `AddressSlot` with member `value` located at `slot`.
           */
          function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
           */
          function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
           */
          function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
           */
          function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      abstract contract VersionedContract {
          function contractVersion() external pure returns (string memory) {
              return "1.1.0";
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { UUPS } from "../lib/proxy/UUPS.sol";
      import { Ownable } from "../lib/utils/Ownable.sol";
      import { ReentrancyGuard } from "../lib/utils/ReentrancyGuard.sol";
      import { Pausable } from "../lib/utils/Pausable.sol";
      import { SafeCast } from "../lib/utils/SafeCast.sol";
      import { AuctionStorageV1 } from "./storage/AuctionStorageV1.sol";
      import { Token } from "../token/Token.sol";
      import { IManager } from "../manager/IManager.sol";
      import { IAuction } from "./IAuction.sol";
      import { IWETH } from "../lib/interfaces/IWETH.sol";
      import { VersionedContract } from "../VersionedContract.sol";
      /// @title Auction
      /// @author Rohan Kulkarni
      /// @notice A DAO's auction house
      /// @custom:repo github.com/ourzora/nouns-protocol 
      /// Modified from:
      /// - NounsAuctionHouse.sol commit 2cbe6c7 - licensed under the BSD-3-Clause license.
      /// - Zora V3 ReserveAuctionCoreEth module commit 795aeca - licensed under the GPL-3.0 license.
      contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard, Pausable, AuctionStorageV1 {
          ///                                                          ///
          ///                          IMMUTABLES                      ///
          ///                                                          ///
          /// @notice Iniital time buffer for auction bids
          uint40 private immutable INITIAL_TIME_BUFFER = 5 minutes;
          /// @notice Min bid increment BPS
          uint8 private immutable INITIAL_MIN_BID_INCREMENT_PERCENT = 10;
          /// @notice The address of WETH
          address private immutable WETH;
          /// @notice The contract upgrade manager
          IManager private immutable manager;
          ///                                                          ///
          ///                          CONSTRUCTOR                     ///
          ///                                                          ///
          /// @param _manager The contract upgrade manager address
          /// @param _weth The address of WETH
          constructor(address _manager, address _weth) payable initializer {
              manager = IManager(_manager);
              WETH = _weth;
          }
          ///                                                          ///
          ///                          INITIALIZER                     ///
          ///                                                          ///
          /// @notice Initializes a DAO's auction contract
          /// @param _token The ERC-721 token address
          /// @param _founder The founder responsible for starting the first auction
          /// @param _treasury The treasury address where ETH will be sent
          /// @param _duration The duration of each auction
          /// @param _reservePrice The reserve price of each auction
          function initialize(
              address _token,
              address _founder,
              address _treasury,
              uint256 _duration,
              uint256 _reservePrice
          ) external initializer {
              // Ensure the caller is the contract manager
              if (msg.sender != address(manager)) revert ONLY_MANAGER();
              // Initialize the reentrancy guard
              __ReentrancyGuard_init();
              // Grant initial ownership to a founder
              __Ownable_init(_founder);
              // Pause the contract until the first auction
              __Pausable_init(true);
              // Store DAO's ERC-721 token
              token = Token(_token);
              // Store the auction house settings
              settings.duration = SafeCast.toUint40(_duration);
              settings.reservePrice = _reservePrice;
              settings.treasury = _treasury;
              settings.timeBuffer = INITIAL_TIME_BUFFER;
              settings.minBidIncrement = INITIAL_MIN_BID_INCREMENT_PERCENT;
          }
          ///                                                          ///
          ///                          CREATE BID                      ///
          ///                                                          ///
          /// @notice Creates a bid for the current token
          /// @param _tokenId The ERC-721 token id
          function createBid(uint256 _tokenId) external payable nonReentrant {
              // Ensure the bid is for the current token
              if (auction.tokenId != _tokenId) {
                  revert INVALID_TOKEN_ID();
              }
              // Ensure the auction is still active
              if (block.timestamp >= auction.endTime) {
                  revert AUCTION_OVER();
              }
              // Cache the amount of ETH attached
              uint256 msgValue = msg.value;
              // Cache the address of the highest bidder
              address lastHighestBidder = auction.highestBidder;
              // Cache the last highest bid
              uint256 lastHighestBid = auction.highestBid;
              // Store the new highest bid
              auction.highestBid = msgValue;
              // Store the new highest bidder
              auction.highestBidder = msg.sender;
              // Used to store whether to extend the auction
              bool extend;
              // Cannot underflow as `_auction.endTime` is ensured to be greater than the current time above
              unchecked {
                  // Compute whether the time remaining is less than the buffer
                  extend = (auction.endTime - block.timestamp) < settings.timeBuffer;
                  // If the auction should be extended
                  if (extend) {
                      // Update the end time with the additional time buffer
                      auction.endTime = uint40(block.timestamp + settings.timeBuffer);
                  }
              }
              // If this is the first bid:
              if (lastHighestBidder == address(0)) {
                  // Ensure the bid meets the reserve price
                  if (msgValue < settings.reservePrice) {
                      revert RESERVE_PRICE_NOT_MET();
                  }
                  // Else this is a subsequent bid:
              } else {
                  // Used to store the minimum bid required
                  uint256 minBid;
                  // Cannot realistically overflow
                  unchecked {
                      // Compute the minimum bid
                      minBid = lastHighestBid + ((lastHighestBid * settings.minBidIncrement) / 100);
                  }
                  // Ensure the incoming bid meets the minimum
                  if (msgValue < minBid) {
                      revert MINIMUM_BID_NOT_MET();
                  }
                  // Ensure that the second bid is not also zero
                  if (minBid == 0 && msgValue == 0 && lastHighestBidder != address(0)) {
                      revert MINIMUM_BID_NOT_MET();
                  }
                  // Refund the previous bidder
                  _handleOutgoingTransfer(lastHighestBidder, lastHighestBid);
              }
              emit AuctionBid(_tokenId, msg.sender, msgValue, extend, auction.endTime);
          }
          ///                                                          ///
          ///                    SETTLE & CREATE AUCTION               ///
          ///                                                          ///
          /// @notice Settles the current auction and creates the next one
          function settleCurrentAndCreateNewAuction() external nonReentrant whenNotPaused {
              _settleAuction();
              _createAuction();
          }
          /// @dev Settles the current auction
          function _settleAuction() private {
              // Get a copy of the current auction
              Auction memory _auction = auction;
              // Ensure the auction wasn't already settled
              if (auction.settled) revert AUCTION_SETTLED();
              // Ensure the auction had started
              if (_auction.startTime == 0) revert AUCTION_NOT_STARTED();
              // Ensure the auction is over
              if (block.timestamp < _auction.endTime) revert AUCTION_ACTIVE();
              // Mark the auction as settled
              auction.settled = true;
              // If a bid was placed:
              if (_auction.highestBidder != address(0)) {
                  // Cache the amount of the highest bid
                  uint256 highestBid = _auction.highestBid;
                  // If the highest bid included ETH: Transfer it to the DAO treasury
                  if (highestBid != 0) _handleOutgoingTransfer(settings.treasury, highestBid);
                  // Transfer the token to the highest bidder
                  token.transferFrom(address(this), _auction.highestBidder, _auction.tokenId);
                  // Else no bid was placed:
              } else {
                  // Burn the token
                  token.burn(_auction.tokenId);
              }
              emit AuctionSettled(_auction.tokenId, _auction.highestBidder, _auction.highestBid);
          }
          /// @dev Creates an auction for the next token
          function _createAuction() private returns (bool) {
              // Get the next token available for bidding
              try token.mint() returns (uint256 tokenId) {
                  // Store the token id
                  auction.tokenId = tokenId;
                  // Cache the current timestamp
                  uint256 startTime = block.timestamp;
                  // Used to store the auction end time
                  uint256 endTime;
                  // Cannot realistically overflow
                  unchecked {
                      // Compute the auction end time
                      endTime = startTime + settings.duration;
                  }
                  // Store the auction start and end time
                  auction.startTime = uint40(startTime);
                  auction.endTime = uint40(endTime);
                  // Reset data from the previous auction
                  auction.highestBid = 0;
                  auction.highestBidder = address(0);
                  auction.settled = false;
                  emit AuctionCreated(tokenId, startTime, endTime);
                  return true;
              } catch {
                  // Pause the contract if token minting failed
                  _pause();
                  return false;
              }
          }
          ///                                                          ///
          ///                             PAUSE                        ///
          ///                                                          ///
          /// @notice Unpauses the auction house
          function unpause() external onlyOwner {
              _unpause();
              // If this is the first auction:
              if (!settings.launched) {
                  // Mark the DAO as launched
                  settings.launched = true;
                  // Transfer ownership of the auction contract to the DAO
                  transferOwnership(settings.treasury);
                  // Transfer ownership of the token contract to the DAO
                  token.onFirstAuctionStarted();
                  // Start the first auction
                  if (!_createAuction()) {
                      // In cause of failure, revert.
                      revert AUCTION_CREATE_FAILED_TO_LAUNCH();
                  }
              }
              // Else if the contract was paused and the previous auction was settled:
              else if (auction.settled) {
                  // Start the next auction
                  _createAuction();
              }
          }
          /// @notice Pauses the auction house
          function pause() external onlyOwner {
              _pause();
          }
          /// @notice Settles the latest auction when the contract is paused
          function settleAuction() external nonReentrant whenPaused {
              _settleAuction();
          }
          ///                                                          ///
          ///                       AUCTION SETTINGS                   ///
          ///                                                          ///
          /// @notice The DAO treasury
          function treasury() external view returns (address) {
              return settings.treasury;
          }
          /// @notice The time duration of each auction
          function duration() external view returns (uint256) {
              return settings.duration;
          }
          /// @notice The reserve price of each auction
          function reservePrice() external view returns (uint256) {
              return settings.reservePrice;
          }
          /// @notice The minimum amount of time to place a bid during an active auction
          function timeBuffer() external view returns (uint256) {
              return settings.timeBuffer;
          }
          /// @notice The minimum percentage an incoming bid must raise the highest bid
          function minBidIncrement() external view returns (uint256) {
              return settings.minBidIncrement;
          }
          ///                                                          ///
          ///                       UPDATE SETTINGS                    ///
          ///                                                          ///
          /// @notice Updates the time duration of each auction
          /// @param _duration The new time duration
          function setDuration(uint256 _duration) external onlyOwner whenPaused {
              settings.duration = SafeCast.toUint40(_duration);
              emit DurationUpdated(_duration);
          }
          /// @notice Updates the reserve price of each auction
          /// @param _reservePrice The new reserve price
          function setReservePrice(uint256 _reservePrice) external onlyOwner whenPaused {
              settings.reservePrice = _reservePrice;
              emit ReservePriceUpdated(_reservePrice);
          }
          /// @notice Updates the time buffer of each auction
          /// @param _timeBuffer The new time buffer
          function setTimeBuffer(uint256 _timeBuffer) external onlyOwner whenPaused {
              settings.timeBuffer = SafeCast.toUint40(_timeBuffer);
              emit TimeBufferUpdated(_timeBuffer);
          }
          /// @notice Updates the minimum bid increment of each subsequent bid
          /// @param _percentage The new percentage
          function setMinimumBidIncrement(uint256 _percentage) external onlyOwner whenPaused {
              if (_percentage == 0) {
                  revert MIN_BID_INCREMENT_1_PERCENT();
              }
              settings.minBidIncrement = SafeCast.toUint8(_percentage);
              emit MinBidIncrementPercentageUpdated(_percentage);
          }
          ///                                                          ///
          ///                        TRANSFER UTIL                     ///
          ///                                                          ///
          /// @notice Transfer ETH/WETH from the contract
          /// @param _to The recipient address
          /// @param _amount The amount transferring
          function _handleOutgoingTransfer(address _to, uint256 _amount) private {
              // Ensure the contract has enough ETH to transfer
              if (address(this).balance < _amount) revert INSOLVENT();
              // Used to store if the transfer succeeded
              bool success;
              assembly {
                  // Transfer ETH to the recipient
                  // Limit the call to 50,000 gas
                  success := call(50000, _to, _amount, 0, 0, 0, 0)
              }
              // If the transfer failed:
              if (!success) {
                  // Wrap as WETH
                  IWETH(WETH).deposit{ value: _amount }();
                  // Transfer WETH instead
                  bool wethSuccess = IWETH(WETH).transfer(_to, _amount);
                  // Ensure successful transfer
                  if (!wethSuccess) {
                      revert FAILING_WETH_TRANSFER();
                  }
              }
          }
          ///                                                          ///
          ///                        AUCTION UPGRADE                   ///
          ///                                                          ///
          /// @notice Ensures the caller is authorized to upgrade the contract and the new implementation is valid
          /// @dev This function is called in `upgradeTo` & `upgradeToAndCall`
          /// @param _newImpl The new implementation address
          function _authorizeUpgrade(address _newImpl) internal view override onlyOwner whenPaused {
              // Ensure the new implementation is registered by the Builder DAO
              if (!manager.isRegisteredUpgrade(_getImplementation(), _newImpl)) revert INVALID_UPGRADE(_newImpl);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IUUPS } from "../lib/interfaces/IUUPS.sol";
      import { IOwnable } from "../lib/interfaces/IOwnable.sol";
      import { IPausable } from "../lib/interfaces/IPausable.sol";
      /// @title IAuction
      /// @author Rohan Kulkarni
      /// @notice The external Auction events, errors, and functions
      interface IAuction is IUUPS, IOwnable, IPausable {
          ///                                                          ///
          ///                            EVENTS                        ///
          ///                                                          ///
          /// @notice Emitted when a bid is placed
          /// @param tokenId The ERC-721 token id
          /// @param bidder The address of the bidder
          /// @param amount The amount of ETH
          /// @param extended If the bid extended the auction
          /// @param endTime The end time of the auction
          event AuctionBid(uint256 tokenId, address bidder, uint256 amount, bool extended, uint256 endTime);
          /// @notice Emitted when an auction is settled
          /// @param tokenId The ERC-721 token id of the settled auction
          /// @param winner The address of the winning bidder
          /// @param amount The amount of ETH raised from the winning bid
          event AuctionSettled(uint256 tokenId, address winner, uint256 amount);
          /// @notice Emitted when an auction is created
          /// @param tokenId The ERC-721 token id of the created auction
          /// @param startTime The start time of the created auction
          /// @param endTime The end time of the created auction
          event AuctionCreated(uint256 tokenId, uint256 startTime, uint256 endTime);
          /// @notice Emitted when the auction duration is updated
          /// @param duration The new auction duration
          event DurationUpdated(uint256 duration);
          /// @notice Emitted when the reserve price is updated
          /// @param reservePrice The new reserve price
          event ReservePriceUpdated(uint256 reservePrice);
          /// @notice Emitted when the min bid increment percentage is updated
          /// @param minBidIncrementPercentage The new min bid increment percentage
          event MinBidIncrementPercentageUpdated(uint256 minBidIncrementPercentage);
          /// @notice Emitted when the time buffer is updated
          /// @param timeBuffer The new time buffer
          event TimeBufferUpdated(uint256 timeBuffer);
          ///                                                          ///
          ///                           ERRORS                         ///
          ///                                                          ///
          /// @dev Reverts if a bid is placed for the wrong token
          error INVALID_TOKEN_ID();
          /// @dev Reverts if a bid is placed for an auction thats over
          error AUCTION_OVER();
          /// @dev Reverts if a bid is placed for an auction that hasn't started
          error AUCTION_NOT_STARTED();
          /// @dev Reverts if attempting to settle an active auction
          error AUCTION_ACTIVE();
          /// @dev Reverts if attempting to settle an auction that was already settled
          error AUCTION_SETTLED();
          /// @dev Reverts if a bid does not meet the reserve price
          error RESERVE_PRICE_NOT_MET();
          /// @dev Reverts if a bid does not meet the minimum bid
          error MINIMUM_BID_NOT_MET();
          /// @dev Error for when the bid increment is set to 0.
          error MIN_BID_INCREMENT_1_PERCENT();
          /// @dev Reverts if the contract does not have enough ETH
          error INSOLVENT();
          /// @dev Reverts if the caller was not the contract manager
          error ONLY_MANAGER();
          /// @dev Thrown if the WETH contract throws a failure on transfer
          error FAILING_WETH_TRANSFER();
          /// @dev Thrown if the auction creation failed
          error AUCTION_CREATE_FAILED_TO_LAUNCH();
          ///                                                          ///
          ///                          FUNCTIONS                       ///
          ///                                                          ///
          /// @notice Initializes a DAO's auction house
          /// @param token The ERC-721 token address
          /// @param founder The founder responsible for starting the first auction
          /// @param treasury The treasury address where ETH will be sent
          /// @param duration The duration of each auction
          /// @param reservePrice The reserve price of each auction
          function initialize(
              address token,
              address founder,
              address treasury,
              uint256 duration,
              uint256 reservePrice
          ) external;
          /// @notice Creates a bid for the current token
          /// @param tokenId The ERC-721 token id
          function createBid(uint256 tokenId) external payable;
          /// @notice Settles the current auction and creates the next one
          function settleCurrentAndCreateNewAuction() external;
          /// @notice Settles the latest auction when the contract is paused
          function settleAuction() external;
          /// @notice Pauses the auction house
          function pause() external;
          /// @notice Unpauses the auction house
          function unpause() external;
          /// @notice The time duration of each auction
          function duration() external view returns (uint256);
          /// @notice The reserve price of each auction
          function reservePrice() external view returns (uint256);
          /// @notice The minimum amount of time to place a bid during an active auction
          function timeBuffer() external view returns (uint256);
          /// @notice The minimum percentage an incoming bid must raise the highest bid
          function minBidIncrement() external view returns (uint256);
          /// @notice Updates the time duration of each auction
          /// @param duration The new time duration
          function setDuration(uint256 duration) external;
          /// @notice Updates the reserve price of each auction
          /// @param reservePrice The new reserve price
          function setReservePrice(uint256 reservePrice) external;
          /// @notice Updates the time buffer of each auction
          /// @param timeBuffer The new time buffer
          function setTimeBuffer(uint256 timeBuffer) external;
          /// @notice Updates the minimum bid increment of each subsequent bid
          /// @param percentage The new percentage
          function setMinimumBidIncrement(uint256 percentage) external;
          /// @notice Get the address of the treasury
          function treasury() external returns (address);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { Token } from "../../token/Token.sol";
      import { AuctionTypesV1 } from "../types/AuctionTypesV1.sol";
      /// @title AuctionStorageV1
      /// @author Rohan Kulkarni
      /// @notice The Auction storage contract
      contract AuctionStorageV1 is AuctionTypesV1 {
          /// @notice The auction settings
          Settings internal settings;
          /// @notice The ERC-721 token
          Token public token;
          /// @notice The state of the current auction
          Auction public auction;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      /// @title AuctionTypesV1
      /// @author Rohan Kulkarni
      /// @notice The Auction custom data types
      contract AuctionTypesV1 {
          /// @notice The settings type
          /// @param treasury The DAO treasury
          /// @param duration The time duration of each auction
          /// @param timeBuffer The minimum time to place a bid
          /// @param minBidIncrement The minimum percentage an incoming bid must raise the highest bid
          /// @param launched If the first auction has been kicked off
          /// @param reservePrice The reserve price of each auction
          struct Settings {
              address treasury;
              uint40 duration;
              uint40 timeBuffer;
              uint8 minBidIncrement;
              bool launched;
              uint256 reservePrice;
          }
          /// @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;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      /// @title IEIP712
      /// @author Rohan Kulkarni
      /// @notice The external EIP712 errors and functions
      interface IEIP712 {
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if the deadline has passed to submit a signature
          error EXPIRED_SIGNATURE();
          /// @dev Reverts if the recovered signature is invalid
          error INVALID_SIGNATURE();
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @notice The sig nonce for an account
          /// @param account The account address
          function nonce(address account) external view returns (uint256);
          /// @notice The EIP-712 domain separator
          function DOMAIN_SEPARATOR() external view returns (bytes32);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      /// @title IERC1967Upgrade
      /// @author Rohan Kulkarni
      /// @notice The external ERC1967Upgrade events and errors
      interface IERC1967Upgrade {
          ///                                                          ///
          ///                            EVENTS                        ///
          ///                                                          ///
          /// @notice Emitted when the implementation is upgraded
          /// @param impl The address of the implementation
          event Upgraded(address impl);
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if an implementation is an invalid upgrade
          /// @param impl The address of the invalid implementation
          error INVALID_UPGRADE(address impl);
          /// @dev Reverts if an implementation upgrade is not stored at the storage slot of the original
          error UNSUPPORTED_UUID();
          /// @dev Reverts if an implementation does not support ERC1822 proxiableUUID()
          error ONLY_UUPS();
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      /// @title IERC721
      /// @author Rohan Kulkarni
      /// @notice The external ERC721 events, errors, and functions
      interface IERC721 {
          ///                                                          ///
          ///                            EVENTS                        ///
          ///                                                          ///
          /// @notice Emitted when a token is transferred from sender to recipient
          /// @param from The sender address
          /// @param to The recipient address
          /// @param tokenId The ERC-721 token id
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          /// @notice Emitted when an owner approves an account to manage a token
          /// @param owner The owner address
          /// @param approved The account address
          /// @param tokenId The ERC-721 token id
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          /// @notice Emitted when an owner sets an approval for a spender to manage all tokens
          /// @param owner The owner address
          /// @param operator The spender address
          /// @param approved If the approval is being set or removed
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if a caller is not authorized to approve or transfer a token
          error INVALID_APPROVAL();
          /// @dev Reverts if a transfer is called with the incorrect token owner
          error INVALID_OWNER();
          /// @dev Reverts if a transfer is attempted to address(0)
          error INVALID_RECIPIENT();
          /// @dev Reverts if an existing token is called to be minted
          error ALREADY_MINTED();
          /// @dev Reverts if a non-existent token is called to be burned
          error NOT_MINTED();
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @notice The number of tokens owned
          /// @param owner The owner address
          function balanceOf(address owner) 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 The account approved to manage a token
          /// @param tokenId The ERC-721 token id
          function getApproved(uint256 tokenId) external view returns (address);
          /// @notice If an operator is authorized to manage all of an owner's tokens
          /// @param owner The owner address
          /// @param operator The operator address
          function isApprovedForAll(address owner, address operator) external view returns (bool);
          /// @notice Authorizes an account to manage a token
          /// @param to The account address
          /// @param tokenId The ERC-721 token id
          function approve(address to, uint256 tokenId) external;
          /// @notice Authorizes an account to manage all tokens
          /// @param operator The account address
          /// @param approved If permission is being given or removed
          function setApprovalForAll(address operator, bool approved) external;
          /// @notice Safe transfers a token from sender to recipient with additional data
          /// @param from The sender address
          /// @param to The recipient address
          /// @param tokenId The ERC-721 token id
          /// @param data The additional data sent in the call to the recipient
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes calldata data
          ) external;
          /// @notice Safe transfers a token from sender to recipient
          /// @param from The sender address
          /// @param to The recipient address
          /// @param tokenId The ERC-721 token id
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external;
          /// @notice Transfers a token from sender to recipient
          /// @param from The sender address
          /// @param to The recipient address
          /// @param tokenId The ERC-721 token id
          function transferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IERC721 } from "./IERC721.sol";
      import { IEIP712 } from "./IEIP712.sol";
      /// @title IERC721Votes
      /// @author Rohan Kulkarni
      /// @notice The external ERC721Votes events, errors, and functions
      interface IERC721Votes is IERC721, IEIP712 {
          ///                                                          ///
          ///                            EVENTS                        ///
          ///                                                          ///
          /// @notice Emitted when an account changes their delegate
          event DelegateChanged(address indexed delegator, address indexed from, address indexed to);
          /// @notice Emitted when a delegate's number of votes is updated
          event DelegateVotesChanged(address indexed delegate, uint256 prevTotalVotes, uint256 newTotalVotes);
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if the timestamp provided isn't in the past
          error INVALID_TIMESTAMP();
          ///                                                          ///
          ///                            STRUCTS                       ///
          ///                                                          ///
          /// @notice The checkpoint data type
          /// @param timestamp The recorded timestamp
          /// @param votes The voting weight
          struct Checkpoint {
              uint64 timestamp;
              uint192 votes;
          }
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @notice The current number of votes for an account
          /// @param account The account address
          function getVotes(address account) external view returns (uint256);
          /// @notice The number of votes for an account at a past timestamp
          /// @param account The account address
          /// @param timestamp The past timestamp
          function getPastVotes(address account, uint256 timestamp) external view returns (uint256);
          /// @notice The delegate for an account
          /// @param account The account address
          function delegates(address account) external view returns (address);
          /// @notice Delegates votes to an account
          /// @param to The address delegating votes to
          function delegate(address to) external;
          /// @notice Delegates votes from a signer to an account
          /// @param from The address delegating votes from
          /// @param to The address delegating votes to
          /// @param deadline The signature deadline
          /// @param v The 129th byte and chain id of the signature
          /// @param r The first 64 bytes of the signature
          /// @param s Bytes 64-128 of the signature
          function delegateBySig(
              address from,
              address to,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      /// @title IInitializable
      /// @author Rohan Kulkarni
      /// @notice The external Initializable events and errors
      interface IInitializable {
          ///                                                          ///
          ///                            EVENTS                        ///
          ///                                                          ///
          /// @notice Emitted when the contract has been initialized or reinitialized
          event Initialized(uint256 version);
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if incorrectly initialized with address(0)
          error ADDRESS_ZERO();
          /// @dev Reverts if disabling initializers during initialization
          error INITIALIZING();
          /// @dev Reverts if calling an initialization function outside of initialization
          error NOT_INITIALIZING();
          /// @dev Reverts if reinitializing incorrectly
          error ALREADY_INITIALIZED();
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      /// @title IOwnable
      /// @author Rohan Kulkarni
      /// @notice The external Ownable events, errors, and functions
      interface IOwnable {
          ///                                                          ///
          ///                            EVENTS                        ///
          ///                                                          ///
          /// @notice Emitted when ownership has been updated
          /// @param prevOwner The previous owner address
          /// @param newOwner The new owner address
          event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
          /// @notice Emitted when an ownership transfer is pending
          /// @param owner The current owner address
          /// @param pendingOwner The pending new owner address
          event OwnerPending(address indexed owner, address indexed pendingOwner);
          /// @notice Emitted when a pending ownership transfer has been canceled
          /// @param owner The current owner address
          /// @param canceledOwner The canceled owner address
          event OwnerCanceled(address indexed owner, address indexed canceledOwner);
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if an unauthorized user calls an owner function
          error ONLY_OWNER();
          /// @dev Reverts if an unauthorized user calls a pending owner function
          error ONLY_PENDING_OWNER();
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @notice The address of the owner
          function owner() external view returns (address);
          /// @notice The address of the pending owner
          function pendingOwner() external view returns (address);
          /// @notice Forces an ownership transfer
          /// @param newOwner The new owner address
          function transferOwnership(address newOwner) external;
          /// @notice Initiates a two-step ownership transfer
          /// @param newOwner The new owner address
          function safeTransferOwnership(address newOwner) external;
          /// @notice Accepts an ownership transfer
          function acceptOwnership() external;
          /// @notice Cancels a pending ownership transfer
          function cancelOwnershipTransfer() external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      /// @title IPausable
      /// @author Rohan Kulkarni
      /// @notice The external Pausable events, errors, and functions
      interface IPausable {
          ///                                                          ///
          ///                            EVENTS                        ///
          ///                                                          ///
          /// @notice Emitted when the contract is paused
          /// @param user The address that paused the contract
          event Paused(address user);
          /// @notice Emitted when the contract is unpaused
          /// @param user The address that unpaused the contract
          event Unpaused(address user);
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if called when the contract is paused
          error PAUSED();
          /// @dev Reverts if called when the contract is unpaused
          error UNPAUSED();
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @notice If the contract is paused
          function paused() external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.16;
      import { IERC1822Proxiable } from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
      import { IERC1967Upgrade } from "./IERC1967Upgrade.sol";
      /// @title IUUPS
      /// @author Rohan Kulkarni
      /// @notice The external UUPS errors and functions
      interface IUUPS is IERC1967Upgrade, IERC1822Proxiable {
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if not called directly
          error ONLY_CALL();
          /// @dev Reverts if not called via delegatecall
          error ONLY_DELEGATECALL();
          /// @dev Reverts if not called via proxy
          error ONLY_PROXY();
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @notice Upgrades to an implementation
          /// @param newImpl The new implementation address
          function upgradeTo(address newImpl) external;
          /// @notice Upgrades to an implementation with an additional function call
          /// @param newImpl The new implementation address
          /// @param data The encoded function call
          function upgradeToAndCall(address newImpl, bytes memory data) external payable;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.16;
      import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      /// @title IWETH
      interface IWETH is IERC20 {
          function deposit() external payable;
          function withdraw(uint256 wad) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IERC1822Proxiable } from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
      import { StorageSlot } from "@openzeppelin/contracts/utils/StorageSlot.sol";
      import { IERC1967Upgrade } from "../interfaces/IERC1967Upgrade.sol";
      import { Address } from "../utils/Address.sol";
      /// @title ERC1967Upgrade
      /// @author Rohan Kulkarni
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/ERC1967/ERC1967Upgrade.sol)
      /// - Uses custom errors declared in IERC1967Upgrade
      /// - Removes ERC1967 admin and beacon support
      abstract contract ERC1967Upgrade is IERC1967Upgrade {
          ///                                                          ///
          ///                          CONSTANTS                       ///
          ///                                                          ///
          /// @dev bytes32(uint256(keccak256('eip1967.proxy.rollback')) - 1)
          bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
          /// @dev bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
          bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
          ///                                                          ///
          ///                          FUNCTIONS                       ///
          ///                                                          ///
          /// @dev Upgrades to an implementation with security checks for UUPS proxies and an additional function call
          /// @param _newImpl The new implementation address
          /// @param _data The encoded function call
          function _upgradeToAndCallUUPS(
              address _newImpl,
              bytes memory _data,
              bool _forceCall
          ) internal {
              if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                  _setImplementation(_newImpl);
              } else {
                  try IERC1822Proxiable(_newImpl).proxiableUUID() returns (bytes32 slot) {
                      if (slot != _IMPLEMENTATION_SLOT) revert UNSUPPORTED_UUID();
                  } catch {
                      revert ONLY_UUPS();
                  }
                  _upgradeToAndCall(_newImpl, _data, _forceCall);
              }
          }
          /// @dev Upgrades to an implementation with an additional function call
          /// @param _newImpl The new implementation address
          /// @param _data The encoded function call
          function _upgradeToAndCall(
              address _newImpl,
              bytes memory _data,
              bool _forceCall
          ) internal {
              _upgradeTo(_newImpl);
              if (_data.length > 0 || _forceCall) {
                  Address.functionDelegateCall(_newImpl, _data);
              }
          }
          /// @dev Performs an implementation upgrade
          /// @param _newImpl The new implementation address
          function _upgradeTo(address _newImpl) internal {
              _setImplementation(_newImpl);
              emit Upgraded(_newImpl);
          }
          /// @dev Stores the address of an implementation
          /// @param _impl The implementation address
          function _setImplementation(address _impl) private {
              if (!Address.isContract(_impl)) revert INVALID_UPGRADE(_impl);
              StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = _impl;
          }
          /// @dev The address of the current implementation
          function _getImplementation() internal view returns (address) {
              return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IUUPS } from "../interfaces/IUUPS.sol";
      import { ERC1967Upgrade } from "./ERC1967Upgrade.sol";
      /// @title UUPS
      /// @author Rohan Kulkarni
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/utils/UUPSUpgradeable.sol)
      /// - Uses custom errors declared in IUUPS
      /// - Inherits a modern, minimal ERC1967Upgrade
      abstract contract UUPS is IUUPS, ERC1967Upgrade {
          ///                                                          ///
          ///                          IMMUTABLES                      ///
          ///                                                          ///
          /// @dev The address of the implementation
          address private immutable __self = address(this);
          ///                                                          ///
          ///                           MODIFIERS                      ///
          ///                                                          ///
          /// @dev Ensures that execution is via proxy delegatecall with the correct implementation
          modifier onlyProxy() {
              if (address(this) == __self) revert ONLY_DELEGATECALL();
              if (_getImplementation() != __self) revert ONLY_PROXY();
              _;
          }
          /// @dev Ensures that execution is via direct call
          modifier notDelegated() {
              if (address(this) != __self) revert ONLY_CALL();
              _;
          }
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @dev Hook to authorize an implementation upgrade
          /// @param _newImpl The new implementation address
          function _authorizeUpgrade(address _newImpl) internal virtual;
          /// @notice Upgrades to an implementation
          /// @param _newImpl The new implementation address
          function upgradeTo(address _newImpl) external onlyProxy {
              _authorizeUpgrade(_newImpl);
              _upgradeToAndCallUUPS(_newImpl, "", false);
          }
          /// @notice Upgrades to an implementation with an additional function call
          /// @param _newImpl The new implementation address
          /// @param _data The encoded function call
          function upgradeToAndCall(address _newImpl, bytes memory _data) external payable onlyProxy {
              _authorizeUpgrade(_newImpl);
              _upgradeToAndCallUUPS(_newImpl, _data, true);
          }
          /// @notice The storage slot of the implementation address
          function proxiableUUID() external view notDelegated returns (bytes32) {
              return _IMPLEMENTATION_SLOT;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IERC721 } from "../interfaces/IERC721.sol";
      import { Initializable } from "../utils/Initializable.sol";
      import { ERC721TokenReceiver } from "../utils/TokenReceiver.sol";
      import { Address } from "../utils/Address.sol";
      /// @title ERC721
      /// @author Rohan Kulkarni
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC721/ERC721Upgradeable.sol)
      /// - Uses custom errors declared in IERC721
      abstract contract ERC721 is IERC721, Initializable {
          ///                                                          ///
          ///                            STORAGE                       ///
          ///                                                          ///
          /// @notice The token name
          string public name;
          /// @notice The token symbol
          string public symbol;
          /// @notice The token owners
          /// @dev ERC-721 token id => Owner
          mapping(uint256 => address) internal owners;
          /// @notice The owner balances
          /// @dev Owner => Balance
          mapping(address => uint256) internal balances;
          /// @notice The token approvals
          /// @dev ERC-721 token id => Manager
          mapping(uint256 => address) internal tokenApprovals;
          /// @notice The balance approvals
          /// @dev Owner => Operator => Approved
          mapping(address => mapping(address => bool)) internal operatorApprovals;
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @dev Initializes an ERC-721 token
          /// @param _name The ERC-721 token name
          /// @param _symbol The ERC-721 token symbol
          function __ERC721_init(string memory _name, string memory _symbol) internal onlyInitializing {
              name = _name;
              symbol = _symbol;
          }
          /// @notice The token URI
          /// @param _tokenId The ERC-721 token id
          function tokenURI(uint256 _tokenId) public view virtual returns (string memory) {}
          /// @notice The contract URI
          function contractURI() public view virtual returns (string memory) {}
          /// @notice If the contract implements an interface
          /// @param _interfaceId The interface id
          function supportsInterface(bytes4 _interfaceId) external pure returns (bool) {
              return
                  _interfaceId == 0x01ffc9a7 || // ERC165 Interface ID
                  _interfaceId == 0x80ac58cd || // ERC721 Interface ID
                  _interfaceId == 0x5b5e139f; // ERC721Metadata Interface ID
          }
          /// @notice The account approved to manage a token
          /// @param _tokenId The ERC-721 token id
          function getApproved(uint256 _tokenId) external view returns (address) {
              return tokenApprovals[_tokenId];
          }
          /// @notice If an operator is authorized to manage all of an owner's tokens
          /// @param _owner The owner address
          /// @param _operator The operator address
          function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
              return operatorApprovals[_owner][_operator];
          }
          /// @notice The number of tokens owned
          /// @param _owner The owner address
          function balanceOf(address _owner) public view returns (uint256) {
              if (_owner == address(0)) revert ADDRESS_ZERO();
              return balances[_owner];
          }
          /// @notice The owner of a token
          /// @param _tokenId The ERC-721 token id
          function ownerOf(uint256 _tokenId) public view returns (address) {
              address owner = owners[_tokenId];
              if (owner == address(0)) revert INVALID_OWNER();
              return owner;
          }
          /// @notice Authorizes an account to manage a token
          /// @param _to The account address
          /// @param _tokenId The ERC-721 token id
          function approve(address _to, uint256 _tokenId) external {
              address owner = owners[_tokenId];
              if (msg.sender != owner && !operatorApprovals[owner][msg.sender]) revert INVALID_APPROVAL();
              tokenApprovals[_tokenId] = _to;
              emit Approval(owner, _to, _tokenId);
          }
          /// @notice Authorizes an account to manage all tokens
          /// @param _operator The account address
          /// @param _approved If permission is being given or removed
          function setApprovalForAll(address _operator, bool _approved) external {
              operatorApprovals[msg.sender][_operator] = _approved;
              emit ApprovalForAll(msg.sender, _operator, _approved);
          }
          /// @notice Transfers a token from sender to recipient
          /// @param _from The sender address
          /// @param _to The recipient address
          /// @param _tokenId The ERC-721 token id
          function transferFrom(
              address _from,
              address _to,
              uint256 _tokenId
          ) public {
              if (_from != owners[_tokenId]) revert INVALID_OWNER();
              if (_to == address(0)) revert ADDRESS_ZERO();
              if (msg.sender != _from && !operatorApprovals[_from][msg.sender] && msg.sender != tokenApprovals[_tokenId]) revert INVALID_APPROVAL();
              _beforeTokenTransfer(_from, _to, _tokenId);
              unchecked {
                  --balances[_from];
                  ++balances[_to];
              }
              owners[_tokenId] = _to;
              delete tokenApprovals[_tokenId];
              emit Transfer(_from, _to, _tokenId);
              _afterTokenTransfer(_from, _to, _tokenId);
          }
          /// @notice Safe transfers a token from sender to recipient
          /// @param _from The sender address
          /// @param _to The recipient address
          /// @param _tokenId The ERC-721 token id
          function safeTransferFrom(
              address _from,
              address _to,
              uint256 _tokenId
          ) external {
              transferFrom(_from, _to, _tokenId);
              if (
                  Address.isContract(_to) &&
                  ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, "") != ERC721TokenReceiver.onERC721Received.selector
              ) revert INVALID_RECIPIENT();
          }
          /// @notice Safe transfers a token from sender to recipient with additional data
          /// @param _from The sender address
          /// @param _to The recipient address
          /// @param _tokenId The ERC-721 token id
          function safeTransferFrom(
              address _from,
              address _to,
              uint256 _tokenId,
              bytes calldata _data
          ) external {
              transferFrom(_from, _to, _tokenId);
              if (
                  Address.isContract(_to) &&
                  ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) != ERC721TokenReceiver.onERC721Received.selector
              ) revert INVALID_RECIPIENT();
          }
          /// @dev Mints a token to a recipient
          /// @param _to The recipient address
          /// @param _tokenId The ERC-721 token id
          function _mint(address _to, uint256 _tokenId) internal virtual {
              if (_to == address(0)) revert ADDRESS_ZERO();
              if (owners[_tokenId] != address(0)) revert ALREADY_MINTED();
              _beforeTokenTransfer(address(0), _to, _tokenId);
              unchecked {
                  ++balances[_to];
              }
              owners[_tokenId] = _to;
              emit Transfer(address(0), _to, _tokenId);
              _afterTokenTransfer(address(0), _to, _tokenId);
          }
          /// @dev Burns a token to a recipient
          /// @param _tokenId The ERC-721 token id
          function _burn(uint256 _tokenId) internal virtual {
              address owner = owners[_tokenId];
              if (owner == address(0)) revert NOT_MINTED();
              _beforeTokenTransfer(owner, address(0), _tokenId);
              unchecked {
                  --balances[owner];
              }
              delete owners[_tokenId];
              delete tokenApprovals[_tokenId];
              emit Transfer(owner, address(0), _tokenId);
              _afterTokenTransfer(owner, address(0), _tokenId);
          }
          /// @dev Hook called before a token transfer
          /// @param _from The sender address
          /// @param _to The recipient address
          /// @param _tokenId The ERC-721 token id
          function _beforeTokenTransfer(
              address _from,
              address _to,
              uint256 _tokenId
          ) internal virtual {}
          /// @dev Hook called after a token transfer
          /// @param _from The sender address
          /// @param _to The recipient address
          /// @param _tokenId The ERC-721 token id
          function _afterTokenTransfer(
              address _from,
              address _to,
              uint256 _tokenId
          ) internal virtual {}
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IERC721Votes } from "../interfaces/IERC721Votes.sol";
      import { ERC721 } from "../token/ERC721.sol";
      import { EIP712 } from "../utils/EIP712.sol";
      /// @title ERC721Votes
      /// @author Rohan Kulkarni
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC721/extensions/draft-ERC721Votes.sol) & Nouns DAO ERC721Checkpointable.sol commit 2cbe6c7 - licensed under the BSD-3-Clause license.
      /// - Uses custom errors defined in IERC721Votes
      /// - Checkpoints are based on timestamps instead of block numbers
      /// - Tokens are self-delegated by default
      /// - The total number of votes is the token supply itself
      abstract contract ERC721Votes is IERC721Votes, EIP712, ERC721 {
          ///                                                          ///
          ///                          CONSTANTS                       ///
          ///                                                          ///
          /// @dev The EIP-712 typehash to delegate with a signature
          bytes32 internal constant DELEGATION_TYPEHASH = keccak256("Delegation(address from,address to,uint256 nonce,uint256 deadline)");
          ///                                                          ///
          ///                           STORAGE                        ///
          ///                                                          ///
          /// @notice The delegate for an account
          /// @notice Account => Delegate
          mapping(address => address) internal delegation;
          /// @notice The number of checkpoints for an account
          /// @dev Account => Num Checkpoints
          mapping(address => uint256) internal numCheckpoints;
          /// @notice The checkpoint for an account
          /// @dev Account => Checkpoint Id => Checkpoint
          mapping(address => mapping(uint256 => Checkpoint)) internal checkpoints;
          ///                                                          ///
          ///                        VOTING WEIGHT                     ///
          ///                                                          ///
          /// @notice The current number of votes for an account
          /// @param _account The account address
          function getVotes(address _account) public view returns (uint256) {
              // Get the account's number of checkpoints
              uint256 nCheckpoints = numCheckpoints[_account];
              // Cannot underflow as `nCheckpoints` is ensured to be greater than 0 if reached
              unchecked {
                  // Return the number of votes at the latest checkpoint if applicable
                  return nCheckpoints != 0 ? checkpoints[_account][nCheckpoints - 1].votes : 0;
              }
          }
          /// @notice The number of votes for an account at a past timestamp
          /// @param _account The account address
          /// @param _timestamp The past timestamp
          function getPastVotes(address _account, uint256 _timestamp) public view returns (uint256) {
              // Ensure the given timestamp is in the past
              if (_timestamp >= block.timestamp) revert INVALID_TIMESTAMP();
              // Get the account's number of checkpoints
              uint256 nCheckpoints = numCheckpoints[_account];
              // If there are none return 0
              if (nCheckpoints == 0) return 0;
              // Get the account's checkpoints
              mapping(uint256 => Checkpoint) storage accountCheckpoints = checkpoints[_account];
              unchecked {
                  // Get the latest checkpoint id
                  // Cannot underflow as `nCheckpoints` is ensured to be greater than 0
                  uint256 lastCheckpoint = nCheckpoints - 1;
                  // If the latest checkpoint has a valid timestamp, return its number of votes
                  if (accountCheckpoints[lastCheckpoint].timestamp <= _timestamp) return accountCheckpoints[lastCheckpoint].votes;
                  // If the first checkpoint doesn't have a valid timestamp, return 0
                  if (accountCheckpoints[0].timestamp > _timestamp) return 0;
                  // Otherwise, find a checkpoint with a valid timestamp
                  // Use the latest id as the initial upper bound
                  uint256 high = lastCheckpoint;
                  uint256 low;
                  uint256 middle;
                  // Used to temporarily hold a checkpoint
                  Checkpoint memory cp;
                  // While a valid checkpoint is to be found:
                  while (high > low) {
                      // Find the id of the middle checkpoint
                      middle = high - (high - low) / 2;
                      // Get the middle checkpoint
                      cp = accountCheckpoints[middle];
                      // If the timestamp is a match:
                      if (cp.timestamp == _timestamp) {
                          // Return the voting weight
                          return cp.votes;
                          // Else if the timestamp is before the one looking for:
                      } else if (cp.timestamp < _timestamp) {
                          // Update the lower bound
                          low = middle;
                          // Else update the upper bound
                      } else {
                          high = middle - 1;
                      }
                  }
                  return accountCheckpoints[low].votes;
              }
          }
          ///                                                          ///
          ///                          DELEGATION                      ///
          ///                                                          ///
          /// @notice The delegate for an account
          /// @param _account The account address
          function delegates(address _account) public view returns (address) {
              address current = delegation[_account];
              return current == address(0) ? _account : current;
          }
          /// @notice Delegates votes to an account
          /// @param _to The address delegating votes to
          function delegate(address _to) external {
              _delegate(msg.sender, _to);
          }
          /// @notice Delegates votes from a signer to an account
          /// @param _from The address delegating votes from
          /// @param _to The address delegating votes to
          /// @param _deadline The signature deadline
          /// @param _v The 129th byte and chain id of the signature
          /// @param _r The first 64 bytes of the signature
          /// @param _s Bytes 64-128 of the signature
          function delegateBySig(
              address _from,
              address _to,
              uint256 _deadline,
              uint8 _v,
              bytes32 _r,
              bytes32 _s
          ) external {
              // Ensure the signature has not expired
              if (block.timestamp > _deadline) revert EXPIRED_SIGNATURE();
              // Used to store the digest
              bytes32 digest;
              // Cannot realistically overflow
              unchecked {
                  // Compute the hash of the domain seperator with the typed delegation data
                  digest = keccak256(
                      abi.encodePacked("\\x19\\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(DELEGATION_TYPEHASH, _from, _to, nonces[_from]++, _deadline)))
                  );
              }
              // Recover the message signer
              address recoveredAddress = ecrecover(digest, _v, _r, _s);
              // Ensure the recovered signer is the voter
              if (recoveredAddress == address(0) || recoveredAddress != _from) revert INVALID_SIGNATURE();
              // Update the delegate
              _delegate(_from, _to);
          }
          /// @dev Updates delegate addresses
          /// @param _from The address delegating votes from
          /// @param _to The address delegating votes to
          function _delegate(address _from, address _to) internal {
              // If address(0) is being delegated to, update the op as a self-delegate
              if (_to == address(0)) _to = _from;
              // Get the previous delegate
              address prevDelegate = delegates(_from);
              // Store the new delegate
              delegation[_from] = _to;
              emit DelegateChanged(_from, prevDelegate, _to);
              // Transfer voting weight from the previous delegate to the new delegate
              _moveDelegateVotes(prevDelegate, _to, balanceOf(_from));
          }
          /// @dev Transfers voting weight
          /// @param _from The address delegating votes from
          /// @param _to The address delegating votes to
          /// @param _amount The number of votes delegating
          function _moveDelegateVotes(
              address _from,
              address _to,
              uint256 _amount
          ) internal {
              unchecked {
                  // If voting weight is being transferred:
                  if (_from != _to && _amount > 0) {
                      // If this isn't a token mint:
                      if (_from != address(0)) {
                          // Get the sender's number of checkpoints
                          uint256 newCheckpointId = numCheckpoints[_from];
                          // Used to store their previous checkpoint id
                          uint256 prevCheckpointId;
                          // Used to store their previous checkpoint's voting weight
                          uint256 prevTotalVotes;
                          // Used to store their previous checkpoint's timestamp
                          uint256 prevTimestamp;
                          // If this isn't the sender's first checkpoint:
                          if (newCheckpointId != 0) {
                              // Get their previous checkpoint's id
                              prevCheckpointId = newCheckpointId - 1;
                              // Get their previous checkpoint's voting weight
                              prevTotalVotes = checkpoints[_from][prevCheckpointId].votes;
                              // Get their previous checkpoint's timestamp
                              prevTimestamp = checkpoints[_from][prevCheckpointId].timestamp;
                          }
                          // Update their voting weight
                          _writeCheckpoint(_from, newCheckpointId, prevCheckpointId, prevTimestamp, prevTotalVotes, prevTotalVotes - _amount);
                      }
                      // If this isn't a token burn:
                      if (_to != address(0)) {
                          // Get the recipients's number of checkpoints
                          uint256 nCheckpoints = numCheckpoints[_to];
                          // Used to store their previous checkpoint id
                          uint256 prevCheckpointId;
                          // Used to store their previous checkpoint's voting weight
                          uint256 prevTotalVotes;
                          // Used to store their previous checkpoint's timestamp
                          uint256 prevTimestamp;
                          // If this isn't the recipient's first checkpoint:
                          if (nCheckpoints != 0) {
                              // Get their previous checkpoint's id
                              prevCheckpointId = nCheckpoints - 1;
                              // Get their previous checkpoint's voting weight
                              prevTotalVotes = checkpoints[_to][prevCheckpointId].votes;
                              // Get their previous checkpoint's timestamp
                              prevTimestamp = checkpoints[_to][prevCheckpointId].timestamp;
                          }
                          // Update their voting weight
                          _writeCheckpoint(_to, nCheckpoints, prevCheckpointId, prevTimestamp, prevTotalVotes, prevTotalVotes + _amount);
                      }
                  }
              }
          }
          /// @dev Records a checkpoint
          /// @param _account The account address
          /// @param _newId The new checkpoint id
          /// @param _prevId The previous checkpoint id
          /// @param _prevTimestamp The previous checkpoint timestamp
          /// @param _prevTotalVotes The previous checkpoint voting weight
          /// @param _newTotalVotes The new checkpoint voting weight
          function _writeCheckpoint(
              address _account,
              uint256 _newId,
              uint256 _prevId,
              uint256 _prevTimestamp,
              uint256 _prevTotalVotes,
              uint256 _newTotalVotes
          ) private {
              unchecked {
                  // If the new checkpoint is not the user's first AND has the timestamp of the previous checkpoint:
                  if (_newId > 0 && _prevTimestamp == block.timestamp) {
                      // Just update the previous checkpoint's votes
                      checkpoints[_account][_prevId].votes = uint192(_newTotalVotes);
                      // Else write a new checkpoint:
                  } else {
                      // Get the pointer to store the checkpoint
                      Checkpoint storage checkpoint = checkpoints[_account][_newId];
                      // Store the new voting weight and the current time
                      checkpoint.votes = uint192(_newTotalVotes);
                      checkpoint.timestamp = uint64(block.timestamp);
                      // Increment the account's number of checkpoints
                      ++numCheckpoints[_account];
                  }
                  emit DelegateVotesChanged(_account, _prevTotalVotes, _newTotalVotes);
              }
          }
          /// @dev Enables each NFT to equal 1 vote
          /// @param _from The token sender
          /// @param _to The token recipient
          /// @param _tokenId The ERC-721 token id
          function _afterTokenTransfer(
              address _from,
              address _to,
              uint256 _tokenId
          ) internal override {
              // Transfer 1 vote from the sender to the recipient
              _moveDelegateVotes(delegates(_from), delegates(_to), 1);
              super._afterTokenTransfer(_from, _to, _tokenId);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      /// @title EIP712
      /// @author Rohan Kulkarni
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (utils/Address.sol)
      /// - Uses custom errors `INVALID_TARGET()` & `DELEGATE_CALL_FAILED()`
      /// - Adds util converting address to bytes32
      library Address {
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if the target of a delegatecall is not a contract
          error INVALID_TARGET();
          /// @dev Reverts if a delegatecall has failed
          error DELEGATE_CALL_FAILED();
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @dev Utility to convert an address to bytes32
          function toBytes32(address _account) internal pure returns (bytes32) {
              return bytes32(uint256(uint160(_account)) << 96);
          }
          /// @dev If an address is a contract
          function isContract(address _account) internal view returns (bool rv) {
              assembly {
                  rv := gt(extcodesize(_account), 0)
              }
          }
          /// @dev Performs a delegatecall on an address
          function functionDelegateCall(address _target, bytes memory _data) internal returns (bytes memory) {
              if (!isContract(_target)) revert INVALID_TARGET();
              (bool success, bytes memory returndata) = _target.delegatecall(_data);
              return verifyCallResult(success, returndata);
          }
          /// @dev Verifies a delegatecall was successful
          function verifyCallResult(bool _success, bytes memory _returndata) internal pure returns (bytes memory) {
              if (_success) {
                  return _returndata;
              } else {
                  if (_returndata.length > 0) {
                      assembly {
                          let returndata_size := mload(_returndata)
                          revert(add(32, _returndata), returndata_size)
                      }
                  } else {
                      revert DELEGATE_CALL_FAILED();
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IEIP712 } from "../interfaces/IEIP712.sol";
      import { Initializable } from "../utils/Initializable.sol";
      /// @title EIP712
      /// @author Rohan Kulkarni
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (utils/cryptography/draft-EIP712Upgradeable.sol)
      /// - Uses custom errors declared in IEIP712
      /// - Caches `INITIAL_CHAIN_ID` and `INITIAL_DOMAIN_SEPARATOR` upon initialization
      /// - Adds mapping for account nonces
      abstract contract EIP712 is IEIP712, Initializable {
          ///                                                          ///
          ///                          CONSTANTS                       ///
          ///                                                          ///
          /// @dev The EIP-712 domain typehash
          bytes32 internal constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
          ///                                                          ///
          ///                           STORAGE                        ///
          ///                                                          ///
          /// @notice The hash of the EIP-712 domain name
          bytes32 internal HASHED_NAME;
          /// @notice The hash of the EIP-712 domain version
          bytes32 internal HASHED_VERSION;
          /// @notice The domain separator computed upon initialization
          bytes32 internal INITIAL_DOMAIN_SEPARATOR;
          /// @notice The chain id upon initialization
          uint256 internal INITIAL_CHAIN_ID;
          /// @notice The account nonces
          /// @dev Account => Nonce
          mapping(address => uint256) internal nonces;
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @dev Initializes EIP-712 support
          /// @param _name The EIP-712 domain name
          /// @param _version The EIP-712 domain version
          function __EIP712_init(string memory _name, string memory _version) internal onlyInitializing {
              HASHED_NAME = keccak256(bytes(_name));
              HASHED_VERSION = keccak256(bytes(_version));
              INITIAL_CHAIN_ID = block.chainid;
              INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
          }
          /// @notice The current nonce for an account
          /// @param _account The account address
          function nonce(address _account) external view returns (uint256) {
              return nonces[_account];
          }
          /// @notice The EIP-712 domain separator
          function DOMAIN_SEPARATOR() public view returns (bytes32) {
              return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : _computeDomainSeparator();
          }
          /// @dev Computes the EIP-712 domain separator
          function _computeDomainSeparator() private view returns (bytes32) {
              return keccak256(abi.encode(DOMAIN_TYPEHASH, HASHED_NAME, HASHED_VERSION, block.chainid, address(this)));
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IInitializable } from "../interfaces/IInitializable.sol";
      import { Address } from "../utils/Address.sol";
      /// @title Initializable
      /// @author Rohan Kulkarni
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/utils/Initializable.sol)
      /// - Uses custom errors declared in IInitializable
      abstract contract Initializable is IInitializable {
          ///                                                          ///
          ///                           STORAGE                        ///
          ///                                                          ///
          /// @dev Indicates the contract has been initialized
          uint8 internal _initialized;
          /// @dev Indicates the contract is being initialized
          bool internal _initializing;
          ///                                                          ///
          ///                          MODIFIERS                       ///
          ///                                                          ///
          /// @dev Ensures an initialization function is only called within an `initializer` or `reinitializer` function
          modifier onlyInitializing() {
              if (!_initializing) revert NOT_INITIALIZING();
              _;
          }
          /// @dev Enables initializing upgradeable contracts
          modifier initializer() {
              bool isTopLevelCall = !_initializing;
              if ((!isTopLevelCall || _initialized != 0) && (Address.isContract(address(this)) || _initialized != 1)) revert ALREADY_INITIALIZED();
              _initialized = 1;
              if (isTopLevelCall) {
                  _initializing = true;
              }
              _;
              if (isTopLevelCall) {
                  _initializing = false;
                  emit Initialized(1);
              }
          }
          /// @dev Enables initializer versioning
          /// @param _version The version to set
          modifier reinitializer(uint8 _version) {
              if (_initializing || _initialized >= _version) revert ALREADY_INITIALIZED();
              _initialized = _version;
              _initializing = true;
              _;
              _initializing = false;
              emit Initialized(_version);
          }
          ///                                                          ///
          ///                          FUNCTIONS                       ///
          ///                                                          ///
          /// @dev Prevents future initialization
          function _disableInitializers() internal virtual {
              if (_initializing) revert INITIALIZING();
              if (_initialized < type(uint8).max) {
                  _initialized = type(uint8).max;
                  emit Initialized(type(uint8).max);
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IOwnable } from "../interfaces/IOwnable.sol";
      import { Initializable } from "../utils/Initializable.sol";
      /// @title Ownable
      /// @author Rohan Kulkarni
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (access/OwnableUpgradeable.sol)
      /// - Uses custom errors declared in IOwnable
      /// - Adds optional two-step ownership transfer (`safeTransferOwnership` + `acceptOwnership`)
      abstract contract Ownable is IOwnable, Initializable {
          ///                                                          ///
          ///                            STORAGE                       ///
          ///                                                          ///
          /// @dev The address of the owner
          address internal _owner;
          /// @dev The address of the pending owner
          address internal _pendingOwner;
          ///                                                          ///
          ///                           MODIFIERS                      ///
          ///                                                          ///
          /// @dev Ensures the caller is the owner
          modifier onlyOwner() {
              if (msg.sender != _owner) revert ONLY_OWNER();
              _;
          }
          /// @dev Ensures the caller is the pending owner
          modifier onlyPendingOwner() {
              if (msg.sender != _pendingOwner) revert ONLY_PENDING_OWNER();
              _;
          }
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @dev Initializes contract ownership
          /// @param _initialOwner The initial owner address
          function __Ownable_init(address _initialOwner) internal onlyInitializing {
              _owner = _initialOwner;
              emit OwnerUpdated(address(0), _initialOwner);
          }
          /// @notice The address of the owner
          function owner() public virtual view returns (address) {
              return _owner;
          }
          /// @notice The address of the pending owner
          function pendingOwner() public view returns (address) {
              return _pendingOwner;
          }
          /// @notice Forces an ownership transfer from the last owner
          /// @param _newOwner The new owner address
          function transferOwnership(address _newOwner) public onlyOwner {
              _transferOwnership(_newOwner);
          }
          /// @notice Forces an ownership transfer from any sender
          /// @param _newOwner New owner to transfer contract to
          /// @dev Ensure is called only from trusted internal code, no access control checks.
          function _transferOwnership(address _newOwner) internal {
              emit OwnerUpdated(_owner, _newOwner);
              _owner = _newOwner;
              if (_pendingOwner != address(0)) delete _pendingOwner;
          }
          /// @notice Initiates a two-step ownership transfer
          /// @param _newOwner The new owner address
          function safeTransferOwnership(address _newOwner) public onlyOwner {
              _pendingOwner = _newOwner;
              emit OwnerPending(_owner, _newOwner);
          }
          /// @notice Accepts an ownership transfer
          function acceptOwnership() public onlyPendingOwner {
              emit OwnerUpdated(_owner, msg.sender);
              _owner = _pendingOwner;
              delete _pendingOwner;
          }
          /// @notice Cancels a pending ownership transfer
          function cancelOwnershipTransfer() public onlyOwner {
              emit OwnerCanceled(_owner, _pendingOwner);
              delete _pendingOwner;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IPausable } from "../interfaces/IPausable.sol";
      import { Initializable } from "../utils/Initializable.sol";
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (security/PausableUpgradeable.sol)
      /// - Uses custom errors declared in IPausable
      abstract contract Pausable is IPausable, Initializable {
          ///                                                          ///
          ///                            STORAGE                       ///
          ///                                                          ///
          /// @dev If the contract is paused
          bool internal _paused;
          ///                                                          ///
          ///                           MODIFIERS                      ///
          ///                                                          ///
          /// @dev Ensures the contract is paused
          modifier whenPaused() {
              if (!_paused) revert UNPAUSED();
              _;
          }
          /// @dev Ensures the contract isn't paused
          modifier whenNotPaused() {
              if (_paused) revert PAUSED();
              _;
          }
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @dev Sets whether the initial state
          /// @param _initPause If the contract should pause upon initialization
          function __Pausable_init(bool _initPause) internal onlyInitializing {
              _paused = _initPause;
          }
          /// @notice If the contract is paused
          function paused() external view returns (bool) {
              return _paused;
          }
          /// @dev Pauses the contract
          function _pause() internal virtual whenNotPaused {
              _paused = true;
              emit Paused(msg.sender);
          }
          /// @dev Unpauses the contract
          function _unpause() internal virtual whenPaused {
              _paused = false;
              emit Unpaused(msg.sender);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { Initializable } from "../utils/Initializable.sol";
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (security/ReentrancyGuardUpgradeable.sol)
      /// - Uses custom error `REENTRANCY()`
      abstract contract ReentrancyGuard is Initializable {
          ///                                                          ///
          ///                            STORAGE                       ///
          ///                                                          ///
          /// @dev Indicates a function has not been entered
          uint256 internal constant _NOT_ENTERED = 1;
          /// @dev Indicates a function has been entered
          uint256 internal constant _ENTERED = 2;
          /// @notice The reentrancy status of a function
          uint256 internal _status;
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if attempted reentrancy
          error REENTRANCY();
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @dev Initializes the reentrancy guard
          function __ReentrancyGuard_init() internal onlyInitializing {
              _status = _NOT_ENTERED;
          }
          /// @dev Ensures a function cannot be reentered
          modifier nonReentrant() {
              if (_status == _ENTERED) revert REENTRANCY();
              _status = _ENTERED;
              _;
              _status = _NOT_ENTERED;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (utils/math/SafeCast.sol)
      /// - Uses custom error `UNSAFE_CAST()`
      library SafeCast {
          error UNSAFE_CAST();
          function toUint128(uint256 x) internal pure returns (uint128) {
              if (x > type(uint128).max) revert UNSAFE_CAST();
              return uint128(x);
          }
          function toUint64(uint256 x) internal pure returns (uint64) {
              if (x > type(uint64).max) revert UNSAFE_CAST();
              return uint64(x);
          }
          function toUint48(uint256 x) internal pure returns (uint48) {
              if (x > type(uint48).max) revert UNSAFE_CAST();
              return uint48(x);
          }
          function toUint40(uint256 x) internal pure returns (uint40) {
              if (x > type(uint40).max) revert UNSAFE_CAST();
              return uint40(x);
          }
          function toUint32(uint256 x) internal pure returns (uint32) {
              if (x > type(uint32).max) revert UNSAFE_CAST();
              return uint32(x);
          }
          function toUint16(uint256 x) internal pure returns (uint16) {
              if (x > type(uint16).max) revert UNSAFE_CAST();
              return uint16(x);
          }
          function toUint8(uint256 x) internal pure returns (uint8) {
              if (x > type(uint8).max) revert UNSAFE_CAST();
              return uint8(x);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC721/utils/ERC721Holder.sol)
      abstract contract ERC721TokenReceiver {
          function onERC721Received(
              address,
              address,
              uint256,
              bytes calldata
          ) external virtual returns (bytes4) {
              return this.onERC721Received.selector;
          }
      }
      /// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC1155/utils/ERC1155Holder.sol)
      abstract contract ERC1155TokenReceiver {
          function onERC1155Received(
              address,
              address,
              uint256,
              uint256,
              bytes calldata
          ) external virtual returns (bytes4) {
              return this.onERC1155Received.selector;
          }
          function onERC1155BatchReceived(
              address,
              address,
              uint256[] calldata,
              uint256[] calldata,
              bytes calldata
          ) external virtual returns (bytes4) {
              return this.onERC1155BatchReceived.selector;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IUUPS } from "../lib/interfaces/IUUPS.sol";
      import { IOwnable } from "../lib/interfaces/IOwnable.sol";
      /// @title IManager
      /// @author Rohan Kulkarni
      /// @notice The external Manager events, errors, structs and functions
      interface IManager is IUUPS, IOwnable {
          ///                                                          ///
          ///                            EVENTS                        ///
          ///                                                          ///
          /// @notice Emitted when a DAO is deployed
          /// @param token The ERC-721 token address
          /// @param metadata The metadata renderer address
          /// @param auction The auction address
          /// @param treasury The treasury address
          /// @param governor The governor address
          event DAODeployed(address token, address metadata, address auction, address treasury, address governor);
          /// @notice Emitted when an upgrade is registered by the Builder DAO
          /// @param baseImpl The base implementation address
          /// @param upgradeImpl The upgrade implementation address
          event UpgradeRegistered(address baseImpl, address upgradeImpl);
          /// @notice Emitted when an upgrade is unregistered by the Builder DAO
          /// @param baseImpl The base implementation address
          /// @param upgradeImpl The upgrade implementation address
          event UpgradeRemoved(address baseImpl, address upgradeImpl);
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if at least one founder is not provided upon deploy
          error FOUNDER_REQUIRED();
          ///                                                          ///
          ///                            STRUCTS                       ///
          ///                                                          ///
          /// @notice The founder parameters
          /// @param wallet The wallet address
          /// @param ownershipPct The percent ownership of the token
          /// @param vestExpiry The timestamp that vesting expires
          struct FounderParams {
              address wallet;
              uint256 ownershipPct;
              uint256 vestExpiry;
          }
          /// @notice DAO Version Information information struct
          struct DAOVersionInfo {
              string token;
              string metadata;
              string auction;
              string treasury;
              string governor; 
          }
          /// @notice The ERC-721 token parameters
          /// @param initStrings The encoded token name, symbol, collection description, collection image uri, renderer base uri
          struct TokenParams {
              bytes initStrings;
          }
          /// @notice The auction parameters
          /// @param reservePrice The reserve price of each auction
          /// @param duration The duration of each auction
          struct AuctionParams {
              uint256 reservePrice;
              uint256 duration;
          }
          /// @notice The governance parameters
          /// @param timelockDelay The time delay to execute a queued transaction
          /// @param votingDelay The time delay to vote on a created proposal
          /// @param votingPeriod The time period to vote on a proposal
          /// @param proposalThresholdBps The basis points of the token supply required to create a proposal
          /// @param quorumThresholdBps The basis points of the token supply required to reach quorum
          /// @param vetoer The address authorized to veto proposals (address(0) if none desired)
          struct GovParams {
              uint256 timelockDelay;
              uint256 votingDelay;
              uint256 votingPeriod;
              uint256 proposalThresholdBps;
              uint256 quorumThresholdBps;
              address vetoer;
          }
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @notice The token implementation address
          function tokenImpl() external view returns (address);
          /// @notice The metadata renderer implementation address
          function metadataImpl() external view returns (address);
          /// @notice The auction house implementation address
          function auctionImpl() external view returns (address);
          /// @notice The treasury implementation address
          function treasuryImpl() external view returns (address);
          /// @notice The governor implementation address
          function governorImpl() external view returns (address);
          /// @notice Deploys a DAO with custom token, auction, and governance settings
          /// @param founderParams The DAO founder(s)
          /// @param tokenParams The ERC-721 token settings
          /// @param auctionParams The auction settings
          /// @param govParams The governance settings
          function deploy(
              FounderParams[] calldata founderParams,
              TokenParams calldata tokenParams,
              AuctionParams calldata auctionParams,
              GovParams calldata govParams
          )
              external
              returns (
                  address token,
                  address metadataRenderer,
                  address auction,
                  address treasury,
                  address governor
              );
          /// @notice A DAO's remaining contract addresses from its token address
          /// @param token The ERC-721 token address
          function getAddresses(address token)
              external
              returns (
                  address metadataRenderer,
                  address auction,
                  address treasury,
                  address governor
              );
          /// @notice If an implementation is registered by the Builder DAO as an optional upgrade
          /// @param baseImpl The base implementation address
          /// @param upgradeImpl The upgrade implementation address
          function isRegisteredUpgrade(address baseImpl, address upgradeImpl) external view returns (bool);
          /// @notice Called by the Builder DAO to offer opt-in implementation upgrades for all other DAOs
          /// @param baseImpl The base implementation address
          /// @param upgradeImpl The upgrade implementation address
          function registerUpgrade(address baseImpl, address upgradeImpl) external;
          /// @notice Called by the Builder DAO to remove an upgrade
          /// @param baseImpl The base implementation address
          /// @param upgradeImpl The upgrade implementation address
          function removeUpgrade(address baseImpl, address upgradeImpl) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IUUPS } from "../lib/interfaces/IUUPS.sol";
      import { IERC721Votes } from "../lib/interfaces/IERC721Votes.sol";
      import { IManager } from "../manager/IManager.sol";
      import { TokenTypesV1 } from "./types/TokenTypesV1.sol";
      /// @title IToken
      /// @author Rohan Kulkarni
      /// @notice The external Token events, errors and functions
      interface IToken is IUUPS, IERC721Votes, TokenTypesV1 {
          ///                                                          ///
          ///                            EVENTS                        ///
          ///                                                          ///
          /// @notice Emitted when a token is scheduled to be allocated
          /// @param baseTokenId The
          /// @param founderId The founder's id
          /// @param founder The founder's vesting details
          event MintScheduled(uint256 baseTokenId, uint256 founderId, Founder founder);
          /// @notice Emitted when a token allocation is unscheduled (removed)
          /// @param baseTokenId The token ID % 100
          /// @param founderId The founder's id
          /// @param founder The founder's vesting details
          event MintUnscheduled(uint256 baseTokenId, uint256 founderId, Founder founder);
          /// @notice Emitted when a tokens founders are deleted from storage
          /// @param newFounders the list of founders
          event FounderAllocationsCleared(IManager.FounderParams[] newFounders);
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if the founder ownership exceeds 100 percent
          error INVALID_FOUNDER_OWNERSHIP();
          /// @dev Reverts if the caller was not the auction contract
          error ONLY_AUCTION();
          /// @dev Reverts if no metadata was generated upon mint
          error NO_METADATA_GENERATED();
          /// @dev Reverts if the caller was not the contract manager
          error ONLY_MANAGER();
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @notice Initializes a DAO's ERC-721 token
          /// @param founders The founding members to receive vesting allocations
          /// @param initStrings The encoded token and metadata initialization strings
          /// @param metadataRenderer The token's metadata renderer
          /// @param auction The token's auction house
          function initialize(
              IManager.FounderParams[] calldata founders,
              bytes calldata initStrings,
              address metadataRenderer,
              address auction,
              address initialOwner
          ) external;
          /// @notice Mints tokens to the auction house for bidding and handles founder vesting
          function mint() external returns (uint256 tokenId);
          /// @notice Burns a token that did not see any bids
          /// @param tokenId The ERC-721 token id
          function burn(uint256 tokenId) external;
          /// @notice The URI for a token
          /// @param tokenId The ERC-721 token id
          function tokenURI(uint256 tokenId) external view returns (string memory);
          /// @notice The URI for the contract
          function contractURI() external view returns (string memory);
          /// @notice The number of founders
          function totalFounders() external view returns (uint256);
          /// @notice The founders total percent ownership
          function totalFounderOwnership() external view returns (uint256);
          /// @notice The vesting details of a founder
          /// @param founderId The founder id
          function getFounder(uint256 founderId) external view returns (Founder memory);
          /// @notice The vesting details of all founders
          function getFounders() external view returns (Founder[] memory);
          /// @notice Update the list of allocation owners
          /// @param newFounders the full list of FounderParam structs
          function updateFounders(IManager.FounderParams[] calldata newFounders) external;                                                         
             
          /// @notice The founder scheduled to receive the given token id
          /// NOTE: If a founder is returned, there's no guarantee they'll receive the token as vesting expiration is not considered
          /// @param tokenId The ERC-721 token id
          function getScheduledRecipient(uint256 tokenId) external view returns (Founder memory);
          /// @notice The total supply of tokens
          function totalSupply() external view returns (uint256);
          /// @notice The token's auction house
          function auction() external view returns (address);
          /// @notice The token's metadata renderer
          function metadataRenderer() external view returns (address);
          /// @notice The owner of the token and metadata renderer
          function owner() external view returns (address);
          /// @notice Callback called by auction on first auction started to transfer ownership to treasury from founder
          function onFirstAuctionStarted() external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { UUPS } from "../lib/proxy/UUPS.sol";
      import { ReentrancyGuard } from "../lib/utils/ReentrancyGuard.sol";
      import { ERC721Votes } from "../lib/token/ERC721Votes.sol";
      import { ERC721 } from "../lib/token/ERC721.sol";
      import { Ownable } from "../lib/utils/Ownable.sol";
      import { TokenStorageV1 } from "./storage/TokenStorageV1.sol";
      import { IBaseMetadata } from "./metadata/interfaces/IBaseMetadata.sol";
      import { IManager } from "../manager/IManager.sol";
      import { IAuction } from "../auction/IAuction.sol";
      import { IToken } from "./IToken.sol";
      import { VersionedContract } from "../VersionedContract.sol";
      /// @title Token
      /// @author Rohan Kulkarni
      /// @custom:repo github.com/ourzora/nouns-protocol 
      /// @notice A DAO's ERC-721 governance token
      contract Token is IToken, VersionedContract, UUPS, Ownable, ReentrancyGuard, ERC721Votes, TokenStorageV1 {
          ///                                                          ///
          ///                         IMMUTABLES                       ///
          ///                                                          ///
          /// @notice The contract upgrade manager
          IManager private immutable manager;
          ///                                                          ///
          ///                         CONSTRUCTOR                      ///
          ///                                                          ///
          /// @param _manager The contract upgrade manager address
          constructor(address _manager) payable initializer {
              manager = IManager(_manager);
          }
          ///                                                          ///
          ///                         INITIALIZER                      ///
          ///                                                          ///
          /// @notice Initializes a DAO's ERC-721 token contract
          /// @param _founders The DAO founders
          /// @param _initStrings The encoded token and metadata initialization strings
          /// @param _metadataRenderer The token's metadata renderer
          /// @param _auction The token's auction house
          /// @param _initialOwner The initial owner of the token
          function initialize(
              IManager.FounderParams[] calldata _founders,
              bytes calldata _initStrings,
              address _metadataRenderer,
              address _auction,
              address _initialOwner
          ) external initializer {
              // Ensure the caller is the contract manager
              if (msg.sender != address(manager)) {
                  revert ONLY_MANAGER();
              }
              // Initialize the reentrancy guard
              __ReentrancyGuard_init();
              // Setup ownable
              __Ownable_init(_initialOwner);
              // Store the founders and compute their allocations
              _addFounders(_founders);
              // Decode the token name and symbol
              (string memory _name, string memory _symbol, , , , ) = abi.decode(_initStrings, (string, string, string, string, string, string));
              // Initialize the ERC-721 token
              __ERC721_init(_name, _symbol);
              // Store the metadata renderer and auction house
              settings.metadataRenderer = IBaseMetadata(_metadataRenderer);
              settings.auction = _auction;
          }
          /// @notice Called by the auction upon the first unpause / token mint to transfer ownership from founder to treasury
          /// @dev Only callable by the auction contract
          function onFirstAuctionStarted() external override {
              if (msg.sender != settings.auction) {
                  revert ONLY_AUCTION();
              }
              // Force transfer ownership to the treasury
              _transferOwnership(IAuction(settings.auction).treasury());
          }
          /// @notice Called upon initialization to add founders and compute their vesting allocations
          /// @dev We do this by reserving an mapping of [0-100] token indices, such that if a new token mint ID % 100 is reserved, it's sent to the appropriate founder.
          /// @param _founders The list of DAO founders
          function _addFounders(IManager.FounderParams[] calldata _founders) internal {
              // Used to store the total percent ownership among the founders
              uint256 totalOwnership;
              uint8 numFoundersAdded = 0;
              unchecked {
                  // For each founder:
                  for (uint256 i; i < _founders.length; ++i) {
                      // Cache the percent ownership
                      uint256 founderPct = _founders[i].ownershipPct;
                      // Continue if no ownership is specified
                      if (founderPct == 0) {
                          continue;
                      }
                      // Update the total ownership and ensure it's valid
                      totalOwnership += founderPct;
                      // Check that founders own less than 100% of tokens
                      if (totalOwnership > 99) {
                          revert INVALID_FOUNDER_OWNERSHIP();
                      }
                      // Compute the founder's id
                      uint256 founderId = numFoundersAdded++;
                      // Get the pointer to store the founder
                      Founder storage newFounder = founder[founderId];
                      // Store the founder's vesting details
                      newFounder.wallet = _founders[i].wallet;
                      newFounder.vestExpiry = uint32(_founders[i].vestExpiry);
                      // Total ownership cannot be above 100 so this fits safely in uint8
                      newFounder.ownershipPct = uint8(founderPct);
                      // Compute the vesting schedule
                      uint256 schedule = 100 / founderPct;
                      // Used to store the base token id the founder will recieve
                      uint256 baseTokenId;
                      // For each token to vest:
                      for (uint256 j; j < founderPct; ++j) {
                          // Get the available token id
                          baseTokenId = _getNextTokenId(baseTokenId);
                          // Store the founder as the recipient
                          tokenRecipient[baseTokenId] = newFounder;
                          emit MintScheduled(baseTokenId, founderId, newFounder);
                          // Update the base token id
                          baseTokenId = (baseTokenId + schedule) % 100;
                      }
                  }
                  // Store the founders' details
                  settings.totalOwnership = uint8(totalOwnership);
                  settings.numFounders = numFoundersAdded;
              }
          }
          /// @dev Finds the next available base token id for a founder
          /// @param _tokenId The ERC-721 token id
          function _getNextTokenId(uint256 _tokenId) internal view returns (uint256) {
              unchecked {
                  while (tokenRecipient[_tokenId].wallet != address(0)) {
                      _tokenId = (++_tokenId) % 100;
                  }
                  return _tokenId;
              }
          }
          ///                                                          ///
          ///                             MINT                         ///
          ///                                                          ///
          /// @notice Mints tokens to the auction house for bidding and handles founder vesting
          function mint() external nonReentrant returns (uint256 tokenId) {
              // Cache the auction address
              address minter = settings.auction;
              // Ensure the caller is the auction
              if (msg.sender != minter) {
                  revert ONLY_AUCTION();
              }
              // Cannot realistically overflow
              unchecked {
                  do {
                      // Get the next token to mint
                      tokenId = settings.mintCount++;
                      // Lookup whether the token is for a founder, and mint accordingly if so
                  } while (_isForFounder(tokenId));
              }
              // Mint the next available token to the auction house for bidding
              _mint(minter, tokenId);
          }
          /// @dev Overrides _mint to include attribute generation
          /// @param _to The token recipient
          /// @param _tokenId The ERC-721 token id
          function _mint(address _to, uint256 _tokenId) internal override {
              // Mint the token
              super._mint(_to, _tokenId);
              // Increment the total supply
              unchecked {
                  ++settings.totalSupply;
              }
              // Generate the token attributes
              if (!settings.metadataRenderer.onMinted(_tokenId)) revert NO_METADATA_GENERATED();
          }
          /// @dev Checks if a given token is for a founder and mints accordingly
          /// @param _tokenId The ERC-721 token id
          function _isForFounder(uint256 _tokenId) private returns (bool) {
              // Get the base token id
              uint256 baseTokenId = _tokenId % 100;
              // If there is no scheduled recipient:
              if (tokenRecipient[baseTokenId].wallet == address(0)) {
                  return false;
                  // Else if the founder is still vesting:
              } else if (block.timestamp < tokenRecipient[baseTokenId].vestExpiry) {
                  // Mint the token to the founder
                  _mint(tokenRecipient[baseTokenId].wallet, _tokenId);
                  return true;
                  // Else the founder has finished vesting:
              } else {
                  // Remove them from future lookups
                  delete tokenRecipient[baseTokenId];
                  return false;
              }
          }
          ///                                                          ///
          ///                             BURN                         ///
          ///                                                          ///
          /// @notice Burns a token that did not see any bids
          /// @param _tokenId The ERC-721 token id
          function burn(uint256 _tokenId) external {
              // Ensure the caller is the auction house
              if (msg.sender != settings.auction) {
                  revert ONLY_AUCTION();
              }
              // Burn the token
              _burn(_tokenId);
          }
          function _burn(uint256 _tokenId) internal override {
              super._burn(_tokenId);
              unchecked {
                  --settings.totalSupply;
              }
          }
          ///                                                          ///
          ///                           METADATA                       ///
          ///                                                          ///
          /// @notice The URI for a token
          /// @param _tokenId The ERC-721 token id
          function tokenURI(uint256 _tokenId) public view override(IToken, ERC721) returns (string memory) {
              return settings.metadataRenderer.tokenURI(_tokenId);
          }
          /// @notice The URI for the contract
          function contractURI() public view override(IToken, ERC721) returns (string memory) {
              return settings.metadataRenderer.contractURI();
          }
          ///                                                          ///
          ///                           FOUNDERS                       ///
          ///                                                          ///
          /// @notice The number of founders
          function totalFounders() external view returns (uint256) {
              return settings.numFounders;
          }
          /// @notice The founders total percent ownership
          function totalFounderOwnership() external view returns (uint256) {
              return settings.totalOwnership;
          }
          /// @notice The vesting details of a founder
          /// @param _founderId The founder id
          function getFounder(uint256 _founderId) external view returns (Founder memory) {
              return founder[_founderId];
          }
          /// @notice The vesting details of all founders
          function getFounders() external view returns (Founder[] memory) {
              // Cache the number of founders
              uint256 numFounders = settings.numFounders;
              // Get a temporary array to hold all founders
              Founder[] memory founders = new Founder[](numFounders);
              // Cannot realistically overflow
              unchecked {
                  // Add each founder to the array
                  for (uint256 i; i < numFounders; ++i) {
                      founders[i] = founder[i];
                  }
              }
              return founders;
          }
          /// @notice The founder scheduled to receive the given token id
          /// NOTE: If a founder is returned, there's no guarantee they'll receive the token as vesting expiration is not considered
          /// @param _tokenId The ERC-721 token id
          function getScheduledRecipient(uint256 _tokenId) external view returns (Founder memory) {
              return tokenRecipient[_tokenId % 100];
          }
          /// @notice Update the list of allocation owners
          /// @param newFounders the full list of founders
          function updateFounders(IManager.FounderParams[] calldata newFounders) external onlyOwner {
              // Cache the number of founders
              uint256 numFounders = settings.numFounders;
              // Get a temporary array to hold all founders
              Founder[] memory cachedFounders = new Founder[](numFounders);
              // Cannot realistically overflow
              unchecked {
                  // Add each founder to the array
                  for (uint256 i; i < numFounders; ++i) {
                      cachedFounders[i] = founder[i];
                  }
              }
              // Keep a mapping of all the reserved token IDs we're set to clear.
              bool[] memory clearedTokenIds = new bool[](100);
              unchecked {
                  // for each existing founder:
                  for (uint256 i; i < cachedFounders.length; ++i) {
                      // copy the founder into memory
                      Founder memory cachedFounder = cachedFounders[i];
                      // Delete the founder from the stored mapping
                      delete founder[i];
                      // Some DAOs were initialized with 0 percentage ownership.
                      // This skips them to avoid a division by zero error.
                      if (cachedFounder.ownershipPct == 0) {
                          continue;
                      }
                      // using the ownership percentage, get reserved token percentages
                      uint256 schedule = 100 / cachedFounder.ownershipPct;
                      // Used to reverse engineer the indices the founder has reserved tokens in.
                      uint256 baseTokenId;
                      for (uint256 j; j < cachedFounder.ownershipPct; ++j) {
                          // Get the next index that hasn't already been cleared
                          while (clearedTokenIds[baseTokenId] != false) {
                              baseTokenId = (++baseTokenId) % 100;
                          }
                          delete tokenRecipient[baseTokenId];
                          clearedTokenIds[baseTokenId] = true;
                          emit MintUnscheduled(baseTokenId, i, cachedFounder);
                          // Update the base token id
                          baseTokenId = (baseTokenId + schedule) % 100;
                      }
                  }
              }
              settings.numFounders = 0;
              settings.totalOwnership = 0;
              emit FounderAllocationsCleared(newFounders);
              _addFounders(newFounders);
          }
          ///                                                          ///
          ///                           SETTINGS                       ///
          ///                                                          ///
          /// @notice The total supply of tokens
          function totalSupply() external view returns (uint256) {
              return settings.totalSupply;
          }
          /// @notice The address of the auction house
          function auction() external view returns (address) {
              return settings.auction;
          }
          /// @notice The address of the metadata renderer
          function metadataRenderer() external view returns (address) {
              return address(settings.metadataRenderer);
          }
          function owner() public view override(IToken, Ownable) returns (address) {
              return super.owner();
          }
          ///                                                          ///
          ///                         TOKEN UPGRADE                    ///
          ///                                                          ///
          /// @notice Ensures the caller is authorized to upgrade the contract and that the new implementation is valid
          /// @dev This function is called in `upgradeTo` & `upgradeToAndCall`
          /// @param _newImpl The new implementation address
          function _authorizeUpgrade(address _newImpl) internal view override {
              // Ensure the caller is the shared owner of the token and metadata renderer
              if (msg.sender != owner()) revert ONLY_OWNER();
              // Ensure the implementation is valid
              if (!manager.isRegisteredUpgrade(_getImplementation(), _newImpl)) revert INVALID_UPGRADE(_newImpl);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IUUPS } from "../../../lib/interfaces/IUUPS.sol";
      /// @title IBaseMetadata
      /// @author Rohan Kulkarni
      /// @notice The external Base Metadata errors and functions
      interface IBaseMetadata is IUUPS {
          ///                                                          ///
          ///                            ERRORS                        ///
          ///                                                          ///
          /// @dev Reverts if the caller was not the contract manager
          error ONLY_MANAGER();
          ///                                                          ///
          ///                           FUNCTIONS                      ///
          ///                                                          ///
          /// @notice Initializes a DAO's token metadata renderer
          /// @param initStrings The encoded token and metadata initialization strings
          /// @param token The associated ERC-721 token address
          function initialize(
              bytes calldata initStrings,
              address token
          ) external;
          /// @notice Generates attributes for a token upon mint
          /// @param tokenId The ERC-721 token id
          function onMinted(uint256 tokenId) external returns (bool);
          /// @notice The token URI
          /// @param tokenId The ERC-721 token id
          function tokenURI(uint256 tokenId) external view returns (string memory);
          /// @notice The contract URI
          function contractURI() external view returns (string memory);
          /// @notice The associated ERC-721 token
          function token() external view returns (address);
          /// @notice Get metadata owner address
          function owner() external view returns (address);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { TokenTypesV1 } from "../types/TokenTypesV1.sol";
      /// @title TokenStorageV1
      /// @author Rohan Kulkarni
      /// @notice The Token storage contract
      contract TokenStorageV1 is TokenTypesV1 {
          /// @notice The token settings
          Settings internal settings;
          /// @notice The vesting details of a founder
          /// @dev Founder id => Founder
          mapping(uint256 => Founder) internal founder;
          /// @notice The recipient of a token
          /// @dev ERC-721 token id => Founder
          mapping(uint256 => Founder) internal tokenRecipient;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.16;
      import { IBaseMetadata } from "../metadata/interfaces/IBaseMetadata.sol";
      /// @title TokenTypesV1
      /// @author Rohan Kulkarni
      /// @notice The Token custom data types
      interface TokenTypesV1 {
          /// @notice The settings type
          /// @param auction The DAO auction house
          /// @param totalSupply The number of active tokens
          /// @param numFounders The number of vesting recipients
          /// @param metadatarenderer The token metadata renderer
          /// @param mintCount The number of minted tokens
          /// @param totalPercentage The total percentage owned by founders
          struct Settings {
              address auction;
              uint88 totalSupply;
              uint8 numFounders;
              IBaseMetadata metadataRenderer;
              uint88 mintCount;
              uint8 totalOwnership;
          }
          /// @notice The founder type
          /// @param wallet The address where tokens are sent
          /// @param ownershipPct The percentage of token ownership
          /// @param vestExpiry The timestamp when vesting ends
          struct Founder {
              address wallet;
              uint8 ownershipPct;
              uint32 vestExpiry;
          }
      }