ETH Price: $2,275.30 (+7.31%)

Transaction Decoder

Block:
18336963 at Oct-12-2023 09:17:47 PM +UTC
Transaction Fee:
0.000487192249900582 ETH $1.11
Gas Used:
64,166 Gas / 7.592685377 Gwei

Emitted Events:

131 Azuki.Approval( owner=[Receiver] 0x29469395eaf6f95920e59f858042f0e28d98a20b, approved=0x00000000...000000000, tokenId=8908 )
132 Azuki.Transfer( from=[Receiver] 0x29469395eaf6f95920e59f858042f0e28d98a20b, to=[Sender] 0x5a1c3e75838e13b3bfa33d5c63cb3c05bd0dfa27, tokenId=8908 )
133 0x29469395eaf6f95920e59f858042f0e28d98a20b.0xb71caf41fe0e019dbe21a1ae3493f11a729c31548ed1e304ae7f6e8c8df275de( 0xb71caf41fe0e019dbe21a1ae3493f11a729c31548ed1e304ae7f6e8c8df275de, 0000000000000000000000000000000000000000000000000000000000017f25, 000000000000000000000000ed5af388653567af2f388e6224dc7c4b3241c544 )

Account State Difference:

  Address   Before After State Difference Code
0x29469395...28D98a20B
(Blur: Blend)
(Titan Builder)
8.521803930161929697 Eth8.521810346761929697 Eth0.0000064166
0x5a1C3e75...5Bd0DfA27
0.868674910871194561 Eth
Nonce: 748
0.868187718621293979 Eth
Nonce: 749
0.000487192249900582
0xED5AF388...b3241C544

Execution Trace

Blur: Blend.5b43226f( )
  • Blend.seize( lienPointers= )
    • Azuki.safeTransferFrom( from=0x29469395eAf6f95920E59F858042f0e28D98a20B, to=0x5a1C3e75838E13B3Bfa33d5c63CB3C05Bd0DfA27, tokenId=8908 )
      File 1 of 2: Azuki
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
      import "./ERC721A.sol";
      import "@openzeppelin/contracts/utils/Strings.sol";
      contract Azuki is Ownable, ERC721A, ReentrancyGuard {
        uint256 public immutable maxPerAddressDuringMint;
        uint256 public immutable amountForDevs;
        uint256 public immutable amountForAuctionAndDev;
        struct SaleConfig {
          uint32 auctionSaleStartTime;
          uint32 publicSaleStartTime;
          uint64 mintlistPrice;
          uint64 publicPrice;
          uint32 publicSaleKey;
        }
        SaleConfig public saleConfig;
        mapping(address => uint256) public allowlist;
        constructor(
          uint256 maxBatchSize_,
          uint256 collectionSize_,
          uint256 amountForAuctionAndDev_,
          uint256 amountForDevs_
        ) ERC721A("Azuki", "AZUKI", maxBatchSize_, collectionSize_) {
          maxPerAddressDuringMint = maxBatchSize_;
          amountForAuctionAndDev = amountForAuctionAndDev_;
          amountForDevs = amountForDevs_;
          require(
            amountForAuctionAndDev_ <= collectionSize_,
            "larger collection size needed"
          );
        }
        modifier callerIsUser() {
          require(tx.origin == msg.sender, "The caller is another contract");
          _;
        }
        function auctionMint(uint256 quantity) external payable callerIsUser {
          uint256 _saleStartTime = uint256(saleConfig.auctionSaleStartTime);
          require(
            _saleStartTime != 0 && block.timestamp >= _saleStartTime,
            "sale has not started yet"
          );
          require(
            totalSupply() + quantity <= amountForAuctionAndDev,
            "not enough remaining reserved for auction to support desired mint amount"
          );
          require(
            numberMinted(msg.sender) + quantity <= maxPerAddressDuringMint,
            "can not mint this many"
          );
          uint256 totalCost = getAuctionPrice(_saleStartTime) * quantity;
          _safeMint(msg.sender, quantity);
          refundIfOver(totalCost);
        }
        function allowlistMint() external payable callerIsUser {
          uint256 price = uint256(saleConfig.mintlistPrice);
          require(price != 0, "allowlist sale has not begun yet");
          require(allowlist[msg.sender] > 0, "not eligible for allowlist mint");
          require(totalSupply() + 1 <= collectionSize, "reached max supply");
          allowlist[msg.sender]--;
          _safeMint(msg.sender, 1);
          refundIfOver(price);
        }
        function publicSaleMint(uint256 quantity, uint256 callerPublicSaleKey)
          external
          payable
          callerIsUser
        {
          SaleConfig memory config = saleConfig;
          uint256 publicSaleKey = uint256(config.publicSaleKey);
          uint256 publicPrice = uint256(config.publicPrice);
          uint256 publicSaleStartTime = uint256(config.publicSaleStartTime);
          require(
            publicSaleKey == callerPublicSaleKey,
            "called with incorrect public sale key"
          );
          require(
            isPublicSaleOn(publicPrice, publicSaleKey, publicSaleStartTime),
            "public sale has not begun yet"
          );
          require(totalSupply() + quantity <= collectionSize, "reached max supply");
          require(
            numberMinted(msg.sender) + quantity <= maxPerAddressDuringMint,
            "can not mint this many"
          );
          _safeMint(msg.sender, quantity);
          refundIfOver(publicPrice * quantity);
        }
        function refundIfOver(uint256 price) private {
          require(msg.value >= price, "Need to send more ETH.");
          if (msg.value > price) {
            payable(msg.sender).transfer(msg.value - price);
          }
        }
        function isPublicSaleOn(
          uint256 publicPriceWei,
          uint256 publicSaleKey,
          uint256 publicSaleStartTime
        ) public view returns (bool) {
          return
            publicPriceWei != 0 &&
            publicSaleKey != 0 &&
            block.timestamp >= publicSaleStartTime;
        }
        uint256 public constant AUCTION_START_PRICE = 1 ether;
        uint256 public constant AUCTION_END_PRICE = 0.15 ether;
        uint256 public constant AUCTION_PRICE_CURVE_LENGTH = 340 minutes;
        uint256 public constant AUCTION_DROP_INTERVAL = 20 minutes;
        uint256 public constant AUCTION_DROP_PER_STEP =
          (AUCTION_START_PRICE - AUCTION_END_PRICE) /
            (AUCTION_PRICE_CURVE_LENGTH / AUCTION_DROP_INTERVAL);
        function getAuctionPrice(uint256 _saleStartTime)
          public
          view
          returns (uint256)
        {
          if (block.timestamp < _saleStartTime) {
            return AUCTION_START_PRICE;
          }
          if (block.timestamp - _saleStartTime >= AUCTION_PRICE_CURVE_LENGTH) {
            return AUCTION_END_PRICE;
          } else {
            uint256 steps = (block.timestamp - _saleStartTime) /
              AUCTION_DROP_INTERVAL;
            return AUCTION_START_PRICE - (steps * AUCTION_DROP_PER_STEP);
          }
        }
        function endAuctionAndSetupNonAuctionSaleInfo(
          uint64 mintlistPriceWei,
          uint64 publicPriceWei,
          uint32 publicSaleStartTime
        ) external onlyOwner {
          saleConfig = SaleConfig(
            0,
            publicSaleStartTime,
            mintlistPriceWei,
            publicPriceWei,
            saleConfig.publicSaleKey
          );
        }
        function setAuctionSaleStartTime(uint32 timestamp) external onlyOwner {
          saleConfig.auctionSaleStartTime = timestamp;
        }
        function setPublicSaleKey(uint32 key) external onlyOwner {
          saleConfig.publicSaleKey = key;
        }
        function seedAllowlist(address[] memory addresses, uint256[] memory numSlots)
          external
          onlyOwner
        {
          require(
            addresses.length == numSlots.length,
            "addresses does not match numSlots length"
          );
          for (uint256 i = 0; i < addresses.length; i++) {
            allowlist[addresses[i]] = numSlots[i];
          }
        }
        // For marketing etc.
        function devMint(uint256 quantity) external onlyOwner {
          require(
            totalSupply() + quantity <= amountForDevs,
            "too many already minted before dev mint"
          );
          require(
            quantity % maxBatchSize == 0,
            "can only mint a multiple of the maxBatchSize"
          );
          uint256 numChunks = quantity / maxBatchSize;
          for (uint256 i = 0; i < numChunks; i++) {
            _safeMint(msg.sender, maxBatchSize);
          }
        }
        // // metadata URI
        string private _baseTokenURI;
        function _baseURI() internal view virtual override returns (string memory) {
          return _baseTokenURI;
        }
        function setBaseURI(string calldata baseURI) external onlyOwner {
          _baseTokenURI = baseURI;
        }
        function withdrawMoney() external onlyOwner nonReentrant {
          (bool success, ) = msg.sender.call{value: address(this).balance}("");
          require(success, "Transfer failed.");
        }
        function setOwnersExplicit(uint256 quantity) external onlyOwner nonReentrant {
          _setOwnersExplicit(quantity);
        }
        function numberMinted(address owner) public view returns (uint256) {
          return _numberMinted(owner);
        }
        function getOwnershipData(uint256 tokenId)
          external
          view
          returns (TokenOwnership memory)
        {
          return ownershipOf(tokenId);
        }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _setOwner(_msgSender());
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _setOwner(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _setOwner(newOwner);
          }
          function _setOwner(address newOwner) private {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev Contract module that helps prevent reentrant calls to a function.
       *
       * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
       * available, which can be applied to functions to make sure there are no nested
       * (reentrant) calls to them.
       *
       * Note that because there is a single `nonReentrant` guard, functions marked as
       * `nonReentrant` may not call one another. This can be worked around by making
       * those functions `private`, and then adding `external` `nonReentrant` entry
       * points to them.
       *
       * TIP: If you would like to learn more about reentrancy and alternative ways
       * to protect against it, check out our blog post
       * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
       */
      abstract contract ReentrancyGuard {
          // Booleans are more expensive than uint256 or any type that takes up a full
          // word because each write operation emits an extra SLOAD to first read the
          // slot's contents, replace the bits taken up by the boolean, and then write
          // back. This is the compiler's defense against contract upgrades and
          // pointer aliasing, and it cannot be disabled.
          // The values being non-zero value makes deployment a bit more expensive,
          // but in exchange the refund on every call to nonReentrant will be lower in
          // amount. Since refunds are capped to a percentage of the total
          // transaction's gas, it is best to keep them low in cases like this one, to
          // increase the likelihood of the full refund coming into effect.
          uint256 private constant _NOT_ENTERED = 1;
          uint256 private constant _ENTERED = 2;
          uint256 private _status;
          constructor() {
              _status = _NOT_ENTERED;
          }
          /**
           * @dev Prevents a contract from calling itself, directly or indirectly.
           * Calling a `nonReentrant` function from another `nonReentrant`
           * function is not supported. It is possible to prevent this from happening
           * by making the `nonReentrant` function external, and make it call a
           * `private` function that does the actual work.
           */
          modifier nonReentrant() {
              // On the first call to nonReentrant, _notEntered will be true
              require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
              // Any calls to nonReentrant after this point will fail
              _status = _ENTERED;
              _;
              // By storing the original value once again, a refund is triggered (see
              // https://eips.ethereum.org/EIPS/eip-2200)
              _status = _NOT_ENTERED;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
      import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
      import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
      import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
      import "@openzeppelin/contracts/utils/Address.sol";
      import "@openzeppelin/contracts/utils/Context.sol";
      import "@openzeppelin/contracts/utils/Strings.sol";
      import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
      /**
       * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
       * the Metadata and Enumerable extension. Built to optimize for lower gas during batch mints.
       *
       * Assumes serials are sequentially minted starting at 0 (e.g. 0, 1, 2, 3..).
       *
       * Assumes the number of issuable tokens (collection size) is capped and fits in a uint128.
       *
       * Does not support burning tokens to address(0).
       */
      contract ERC721A is
        Context,
        ERC165,
        IERC721,
        IERC721Metadata,
        IERC721Enumerable
      {
        using Address for address;
        using Strings for uint256;
        struct TokenOwnership {
          address addr;
          uint64 startTimestamp;
        }
        struct AddressData {
          uint128 balance;
          uint128 numberMinted;
        }
        uint256 private currentIndex = 0;
        uint256 internal immutable collectionSize;
        uint256 internal immutable maxBatchSize;
        // Token name
        string private _name;
        // Token symbol
        string private _symbol;
        // Mapping from token ID to ownership details
        // An empty struct value does not necessarily mean the token is unowned. See ownershipOf implementation for details.
        mapping(uint256 => TokenOwnership) private _ownerships;
        // Mapping owner address to address data
        mapping(address => AddressData) private _addressData;
        // Mapping from token ID to approved address
        mapping(uint256 => address) private _tokenApprovals;
        // Mapping from owner to operator approvals
        mapping(address => mapping(address => bool)) private _operatorApprovals;
        /**
         * @dev
         * `maxBatchSize` refers to how much a minter can mint at a time.
         * `collectionSize_` refers to how many tokens are in the collection.
         */
        constructor(
          string memory name_,
          string memory symbol_,
          uint256 maxBatchSize_,
          uint256 collectionSize_
        ) {
          require(
            collectionSize_ > 0,
            "ERC721A: collection must have a nonzero supply"
          );
          require(maxBatchSize_ > 0, "ERC721A: max batch size must be nonzero");
          _name = name_;
          _symbol = symbol_;
          maxBatchSize = maxBatchSize_;
          collectionSize = collectionSize_;
        }
        /**
         * @dev See {IERC721Enumerable-totalSupply}.
         */
        function totalSupply() public view override returns (uint256) {
          return currentIndex;
        }
        /**
         * @dev See {IERC721Enumerable-tokenByIndex}.
         */
        function tokenByIndex(uint256 index) public view override returns (uint256) {
          require(index < totalSupply(), "ERC721A: global index out of bounds");
          return index;
        }
        /**
         * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
         * This read function is O(collectionSize). If calling from a separate contract, be sure to test gas first.
         * It may also degrade with extremely large collection sizes (e.g >> 10000), test for your use case.
         */
        function tokenOfOwnerByIndex(address owner, uint256 index)
          public
          view
          override
          returns (uint256)
        {
          require(index < balanceOf(owner), "ERC721A: owner index out of bounds");
          uint256 numMintedSoFar = totalSupply();
          uint256 tokenIdsIdx = 0;
          address currOwnershipAddr = address(0);
          for (uint256 i = 0; i < numMintedSoFar; i++) {
            TokenOwnership memory ownership = _ownerships[i];
            if (ownership.addr != address(0)) {
              currOwnershipAddr = ownership.addr;
            }
            if (currOwnershipAddr == owner) {
              if (tokenIdsIdx == index) {
                return i;
              }
              tokenIdsIdx++;
            }
          }
          revert("ERC721A: unable to get token of owner by index");
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId)
          public
          view
          virtual
          override(ERC165, IERC165)
          returns (bool)
        {
          return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            interfaceId == type(IERC721Enumerable).interfaceId ||
            super.supportsInterface(interfaceId);
        }
        /**
         * @dev See {IERC721-balanceOf}.
         */
        function balanceOf(address owner) public view override returns (uint256) {
          require(owner != address(0), "ERC721A: balance query for the zero address");
          return uint256(_addressData[owner].balance);
        }
        function _numberMinted(address owner) internal view returns (uint256) {
          require(
            owner != address(0),
            "ERC721A: number minted query for the zero address"
          );
          return uint256(_addressData[owner].numberMinted);
        }
        function ownershipOf(uint256 tokenId)
          internal
          view
          returns (TokenOwnership memory)
        {
          require(_exists(tokenId), "ERC721A: owner query for nonexistent token");
          uint256 lowestTokenToCheck;
          if (tokenId >= maxBatchSize) {
            lowestTokenToCheck = tokenId - maxBatchSize + 1;
          }
          for (uint256 curr = tokenId; curr >= lowestTokenToCheck; curr--) {
            TokenOwnership memory ownership = _ownerships[curr];
            if (ownership.addr != address(0)) {
              return ownership;
            }
          }
          revert("ERC721A: unable to determine the owner of token");
        }
        /**
         * @dev See {IERC721-ownerOf}.
         */
        function ownerOf(uint256 tokenId) public view override returns (address) {
          return ownershipOf(tokenId).addr;
        }
        /**
         * @dev See {IERC721Metadata-name}.
         */
        function name() public view virtual override returns (string memory) {
          return _name;
        }
        /**
         * @dev See {IERC721Metadata-symbol}.
         */
        function symbol() public view virtual override returns (string memory) {
          return _symbol;
        }
        /**
         * @dev See {IERC721Metadata-tokenURI}.
         */
        function tokenURI(uint256 tokenId)
          public
          view
          virtual
          override
          returns (string memory)
        {
          require(
            _exists(tokenId),
            "ERC721Metadata: URI query for nonexistent token"
          );
          string memory baseURI = _baseURI();
          return
            bytes(baseURI).length > 0
              ? string(abi.encodePacked(baseURI, tokenId.toString()))
              : "";
        }
        /**
         * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
         * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
         * by default, can be overriden in child contracts.
         */
        function _baseURI() internal view virtual returns (string memory) {
          return "";
        }
        /**
         * @dev See {IERC721-approve}.
         */
        function approve(address to, uint256 tokenId) public override {
          address owner = ERC721A.ownerOf(tokenId);
          require(to != owner, "ERC721A: approval to current owner");
          require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721A: approve caller is not owner nor approved for all"
          );
          _approve(to, tokenId, owner);
        }
        /**
         * @dev See {IERC721-getApproved}.
         */
        function getApproved(uint256 tokenId) public view override returns (address) {
          require(_exists(tokenId), "ERC721A: approved query for nonexistent token");
          return _tokenApprovals[tokenId];
        }
        /**
         * @dev See {IERC721-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public override {
          require(operator != _msgSender(), "ERC721A: approve to caller");
          _operatorApprovals[_msgSender()][operator] = approved;
          emit ApprovalForAll(_msgSender(), operator, approved);
        }
        /**
         * @dev See {IERC721-isApprovedForAll}.
         */
        function isApprovedForAll(address owner, address operator)
          public
          view
          virtual
          override
          returns (bool)
        {
          return _operatorApprovals[owner][operator];
        }
        /**
         * @dev See {IERC721-transferFrom}.
         */
        function transferFrom(
          address from,
          address to,
          uint256 tokenId
        ) public override {
          _transfer(from, to, tokenId);
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(
          address from,
          address to,
          uint256 tokenId
        ) public override {
          safeTransferFrom(from, to, tokenId, "");
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(
          address from,
          address to,
          uint256 tokenId,
          bytes memory _data
        ) public override {
          _transfer(from, to, tokenId);
          require(
            _checkOnERC721Received(from, to, tokenId, _data),
            "ERC721A: transfer to non ERC721Receiver implementer"
          );
        }
        /**
         * @dev Returns whether `tokenId` exists.
         *
         * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
         *
         * Tokens start existing when they are minted (`_mint`),
         */
        function _exists(uint256 tokenId) internal view returns (bool) {
          return tokenId < currentIndex;
        }
        function _safeMint(address to, uint256 quantity) internal {
          _safeMint(to, quantity, "");
        }
        /**
         * @dev Mints `quantity` tokens and transfers them to `to`.
         *
         * Requirements:
         *
         * - there must be `quantity` tokens remaining unminted in the total collection.
         * - `to` cannot be the zero address.
         * - `quantity` cannot be larger than the max batch size.
         *
         * Emits a {Transfer} event.
         */
        function _safeMint(
          address to,
          uint256 quantity,
          bytes memory _data
        ) internal {
          uint256 startTokenId = currentIndex;
          require(to != address(0), "ERC721A: mint to the zero address");
          // We know if the first token in the batch doesn't exist, the other ones don't as well, because of serial ordering.
          require(!_exists(startTokenId), "ERC721A: token already minted");
          require(quantity <= maxBatchSize, "ERC721A: quantity to mint too high");
          _beforeTokenTransfers(address(0), to, startTokenId, quantity);
          AddressData memory addressData = _addressData[to];
          _addressData[to] = AddressData(
            addressData.balance + uint128(quantity),
            addressData.numberMinted + uint128(quantity)
          );
          _ownerships[startTokenId] = TokenOwnership(to, uint64(block.timestamp));
          uint256 updatedIndex = startTokenId;
          for (uint256 i = 0; i < quantity; i++) {
            emit Transfer(address(0), to, updatedIndex);
            require(
              _checkOnERC721Received(address(0), to, updatedIndex, _data),
              "ERC721A: transfer to non ERC721Receiver implementer"
            );
            updatedIndex++;
          }
          currentIndex = updatedIndex;
          _afterTokenTransfers(address(0), to, startTokenId, quantity);
        }
        /**
         * @dev Transfers `tokenId` from `from` to `to`.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         *
         * Emits a {Transfer} event.
         */
        function _transfer(
          address from,
          address to,
          uint256 tokenId
        ) private {
          TokenOwnership memory prevOwnership = ownershipOf(tokenId);
          bool isApprovedOrOwner = (_msgSender() == prevOwnership.addr ||
            getApproved(tokenId) == _msgSender() ||
            isApprovedForAll(prevOwnership.addr, _msgSender()));
          require(
            isApprovedOrOwner,
            "ERC721A: transfer caller is not owner nor approved"
          );
          require(
            prevOwnership.addr == from,
            "ERC721A: transfer from incorrect owner"
          );
          require(to != address(0), "ERC721A: transfer to the zero address");
          _beforeTokenTransfers(from, to, tokenId, 1);
          // Clear approvals from the previous owner
          _approve(address(0), tokenId, prevOwnership.addr);
          _addressData[from].balance -= 1;
          _addressData[to].balance += 1;
          _ownerships[tokenId] = TokenOwnership(to, uint64(block.timestamp));
          // If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
          // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
          uint256 nextTokenId = tokenId + 1;
          if (_ownerships[nextTokenId].addr == address(0)) {
            if (_exists(nextTokenId)) {
              _ownerships[nextTokenId] = TokenOwnership(
                prevOwnership.addr,
                prevOwnership.startTimestamp
              );
            }
          }
          emit Transfer(from, to, tokenId);
          _afterTokenTransfers(from, to, tokenId, 1);
        }
        /**
         * @dev Approve `to` to operate on `tokenId`
         *
         * Emits a {Approval} event.
         */
        function _approve(
          address to,
          uint256 tokenId,
          address owner
        ) private {
          _tokenApprovals[tokenId] = to;
          emit Approval(owner, to, tokenId);
        }
        uint256 public nextOwnerToExplicitlySet = 0;
        /**
         * @dev Explicitly set `owners` to eliminate loops in future calls of ownerOf().
         */
        function _setOwnersExplicit(uint256 quantity) internal {
          uint256 oldNextOwnerToSet = nextOwnerToExplicitlySet;
          require(quantity > 0, "quantity must be nonzero");
          uint256 endIndex = oldNextOwnerToSet + quantity - 1;
          if (endIndex > collectionSize - 1) {
            endIndex = collectionSize - 1;
          }
          // We know if the last one in the group exists, all in the group exist, due to serial ordering.
          require(_exists(endIndex), "not enough minted yet for this cleanup");
          for (uint256 i = oldNextOwnerToSet; i <= endIndex; i++) {
            if (_ownerships[i].addr == address(0)) {
              TokenOwnership memory ownership = ownershipOf(i);
              _ownerships[i] = TokenOwnership(
                ownership.addr,
                ownership.startTimestamp
              );
            }
          }
          nextOwnerToExplicitlySet = endIndex + 1;
        }
        /**
         * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
         * The call is not executed if the target address is not a contract.
         *
         * @param from address representing the previous owner of the given token ID
         * @param to target address that will receive the tokens
         * @param tokenId uint256 ID of the token to be transferred
         * @param _data bytes optional data to send along with the call
         * @return bool whether the call correctly returned the expected magic value
         */
        function _checkOnERC721Received(
          address from,
          address to,
          uint256 tokenId,
          bytes memory _data
        ) private returns (bool) {
          if (to.isContract()) {
            try
              IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data)
            returns (bytes4 retval) {
              return retval == IERC721Receiver(to).onERC721Received.selector;
            } catch (bytes memory reason) {
              if (reason.length == 0) {
                revert("ERC721A: transfer to non ERC721Receiver implementer");
              } else {
                assembly {
                  revert(add(32, reason), mload(reason))
                }
              }
            }
          } else {
            return true;
          }
        }
        /**
         * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
         *
         * startTokenId - the first token id to be transferred
         * quantity - the amount to be transferred
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         */
        function _beforeTokenTransfers(
          address from,
          address to,
          uint256 startTokenId,
          uint256 quantity
        ) internal virtual {}
        /**
         * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
         * minting.
         *
         * startTokenId - the first token id to be transferred
         * quantity - the amount to be transferred
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero.
         * - `from` and `to` are never both zero.
         */
        function _afterTokenTransfers(
          address from,
          address to,
          uint256 startTokenId,
          uint256 quantity
        ) internal virtual {}
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev String operations.
       */
      library Strings {
          bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
          /**
           * @dev Converts a `uint256` to its ASCII `string` decimal representation.
           */
          function toString(uint256 value) internal pure returns (string memory) {
              // Inspired by OraclizeAPI's implementation - MIT licence
              // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
              if (value == 0) {
                  return "0";
              }
              uint256 temp = value;
              uint256 digits;
              while (temp != 0) {
                  digits++;
                  temp /= 10;
              }
              bytes memory buffer = new bytes(digits);
              while (value != 0) {
                  digits -= 1;
                  buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                  value /= 10;
              }
              return string(buffer);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
           */
          function toHexString(uint256 value) internal pure returns (string memory) {
              if (value == 0) {
                  return "0x00";
              }
              uint256 temp = value;
              uint256 length = 0;
              while (temp != 0) {
                  length++;
                  temp >>= 8;
              }
              return toHexString(value, length);
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
           */
          function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
              bytes memory buffer = new bytes(2 * length + 2);
              buffer[0] = "0";
              buffer[1] = "x";
              for (uint256 i = 2 * length + 1; i > 1; --i) {
                  buffer[i] = _HEX_SYMBOLS[value & 0xf];
                  value >>= 4;
              }
              require(value == 0, "Strings: hex length insufficient");
              return string(buffer);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /*
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC721 compliant contract.
       */
      interface IERC721 is IERC165 {
          /**
           * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
           */
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
           */
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
           */
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          /**
           * @dev Returns the number of tokens in ``owner``'s account.
           */
          function balanceOf(address owner) external view returns (uint256 balance);
          /**
           * @dev Returns the owner of the `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function ownerOf(uint256 tokenId) external view returns (address owner);
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
           * are aware of the ERC721 protocol to prevent tokens from being forever locked.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external;
          /**
           * @dev Transfers `tokenId` token from `from` to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external;
          /**
           * @dev Gives permission to `to` to transfer `tokenId` token to another account.
           * The approval is cleared when the token is transferred.
           *
           * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
           *
           * Requirements:
           *
           * - The caller must own the token or be an approved operator.
           * - `tokenId` must exist.
           *
           * Emits an {Approval} event.
           */
          function approve(address to, uint256 tokenId) external;
          /**
           * @dev Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
          /**
           * @dev Approve or remove `operator` as an operator for the caller.
           * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
           *
           * Requirements:
           *
           * - The `operator` cannot be the caller.
           *
           * Emits an {ApprovalForAll} event.
           */
          function setApprovalForAll(address operator, bool _approved) external;
          /**
           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
           *
           * See {setApprovalForAll}
           */
          function isApprovedForAll(address owner, address operator) external view returns (bool);
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes calldata data
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @title ERC721 token receiver interface
       * @dev Interface for any contract that wants to support safeTransfers
       * from ERC721 asset contracts.
       */
      interface IERC721Receiver {
          /**
           * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
           * by `operator` from `from`, this function is called.
           *
           * It must return its Solidity selector to confirm the token transfer.
           * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
           *
           * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
           */
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../IERC721.sol";
      /**
       * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
       * @dev See https://eips.ethereum.org/EIPS/eip-721
       */
      interface IERC721Metadata is IERC721 {
          /**
           * @dev Returns the token collection name.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the token collection symbol.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
           */
          function tokenURI(uint256 tokenId) external view returns (string memory);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../IERC721.sol";
      /**
       * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
       * @dev See https://eips.ethereum.org/EIPS/eip-721
       */
      interface IERC721Enumerable is IERC721 {
          /**
           * @dev Returns the total amount of tokens stored by the contract.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
           * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
           */
          function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
          /**
           * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
           * Use along with {totalSupply} to enumerate all tokens.
           */
          function tokenByIndex(uint256 index) external view returns (uint256);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
              uint256 size;
              assembly {
                  size := extcodesize(account)
              }
              return size > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              (bool success, bytes memory returndata) = target.staticcall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          function _verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) private pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "./IERC165.sol";
      /**
       * @dev Implementation of the {IERC165} interface.
       *
       * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
       * for the additional interface id that will be supported. For example:
       *
       * ```solidity
       * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
       *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
       * }
       * ```
       *
       * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
       */
      abstract contract ERC165 is IERC165 {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
              return interfaceId == type(IERC165).interfaceId;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others ({ERC165Checker}).
       *
       * For an implementation, see {ERC165}.
       */
      interface IERC165 {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      

      File 2 of 2: Blend
      // SPDX-License-Identifier: BSL 1.1 - Blend (c) Non Fungible Trading Ltd.
      pragma solidity 0.8.17;
      import "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
      import "./Helpers.sol";
      import "./lib/Structs.sol";
      import "./OfferController.sol";
      import "./interfaces/IBlend.sol";
      import "../pool/interfaces/IBlurPool.sol";
      contract Blend is IBlend, OfferController, UUPSUpgradeable {
          IExchange private immutable _EXCHANGE;
          IExchangeV2 private immutable _EXCHANGE_V2;
          IBlurPool private immutable _POOL;
          address private immutable _SELL_MATCHING_POLICY;
          address private immutable _BID_MATCHING_POLICY;
          address private immutable _DELEGATE;
          address private immutable _DELEGATE_V2;
          uint256 private constant _BASIS_POINTS = 10_000;
          uint256 private constant _MAX_AUCTION_DURATION = 432_000;
          uint256 private constant _LIQUIDATION_THRESHOLD = 100_000;
          uint256 private _nextLienId;
          mapping(uint256 => bytes32) public liens;
          mapping(bytes32 => uint256) public amountTaken;
          // required by the OZ UUPS module
          function _authorizeUpgrade(address) internal override onlyOwner {}
          constructor(
              address pool,
              address exchange,
              address exchangeV2,
              address sellMatchingPolicy,
              address bidMatchingPolicy,
              address delegate,
              address delegateV2
          ) {
              _POOL = IBlurPool(pool);
              _EXCHANGE = IExchange(exchange);
              _EXCHANGE_V2 = IExchangeV2(exchangeV2);
              _SELL_MATCHING_POLICY = sellMatchingPolicy;
              _BID_MATCHING_POLICY = bidMatchingPolicy;
              _DELEGATE = delegate;
              _DELEGATE_V2 = delegateV2;
              _disableInitializers();
          }
          function initialize() external initializer {
              __UUPSUpgradeable_init();
              __Ownable_init();
          }
          /*//////////////////////////////////////////////////
                          BORROW FLOWS
          //////////////////////////////////////////////////*/
          /**
           * @notice Verifies and takes loan offer; then transfers loan and collateral assets
           * @param offer Loan offer
           * @param signature Lender offer signature
           * @param loanAmount Loan amount in ETH
           * @param collateralTokenId Token id to provide as collateral
           * @return lienId New lien id
           */
          function borrow(
              LoanOffer calldata offer,
              bytes calldata signature,
              uint256 loanAmount,
              uint256 collateralTokenId
          ) external returns (uint256 lienId) {
              lienId = _borrow(offer, signature, loanAmount, collateralTokenId);
              /* Lock collateral token. */
              offer.collection.safeTransferFrom(msg.sender, address(this), collateralTokenId);
              /* Transfer loan to borrower. */
              _POOL.transferFrom(offer.lender, msg.sender, loanAmount);
          }
          /**
           * @notice Repays loan and retrieves collateral
           * @param lien Lien preimage
           * @param lienId Lien id
           */
          function repay(
              Lien calldata lien,
              uint256 lienId
          ) external validateLien(lien, lienId) lienIsActive(lien) {
              uint256 debt = _repay(lien, lienId);
              /* Return NFT to borrower. */
              lien.collection.safeTransferFrom(address(this), lien.borrower, lien.tokenId);
              /* Repay loan to lender. */
              _POOL.transferFrom(msg.sender, lien.lender, debt);
          }
          /**
           * @notice Verifies and takes loan offer; creates new lien
           * @param offer Loan offer
           * @param signature Lender offer signature
           * @param loanAmount Loan amount in ETH
           * @param collateralTokenId Token id to provide as collateral
           * @return lienId New lien id
           */
          function _borrow(
              LoanOffer calldata offer,
              bytes calldata signature,
              uint256 loanAmount,
              uint256 collateralTokenId
          ) internal returns (uint256 lienId) {
              if (offer.auctionDuration > _MAX_AUCTION_DURATION) {
                  revert InvalidAuctionDuration();
              }
              Lien memory lien = Lien({
                  lender: offer.lender,
                  borrower: msg.sender,
                  collection: offer.collection,
                  tokenId: collateralTokenId,
                  amount: loanAmount,
                  startTime: block.timestamp,
                  rate: offer.rate,
                  auctionStartBlock: 0,
                  auctionDuration: offer.auctionDuration
              });
              /* Create lien. */
              unchecked {
                  liens[lienId = _nextLienId++] = keccak256(abi.encode(lien));
              }
              /* Take the loan offer. */
              _takeLoanOffer(offer, signature, lien, lienId);
          }
          /**
           * @notice Computes the current debt repayment and burns the lien
           * @dev Does not transfer assets
           * @param lien Lien preimage
           * @param lienId Lien id
           * @return debt Current amount of debt owed on the lien
           */
          function _repay(Lien calldata lien, uint256 lienId) internal returns (uint256 debt) {
              debt = Helpers.computeCurrentDebt(lien.amount, lien.rate, lien.startTime);
              delete liens[lienId];
              emit Repay(lienId, address(lien.collection));
          }
          /**
           * @notice Verifies and takes loan offer
           * @dev Does not transfer loan and collateral assets; does not update lien hash
           * @param offer Loan offer
           * @param signature Lender offer signature
           * @param lien Lien preimage
           * @param lienId Lien id
           */
          function _takeLoanOffer(
              LoanOffer calldata offer,
              bytes calldata signature,
              Lien memory lien,
              uint256 lienId
          ) internal {
              bytes32 hash = _hashOffer(offer);
              _validateOffer(
                  hash,
                  offer.lender,
                  offer.oracle,
                  signature,
                  offer.expirationTime,
                  offer.salt
              );
              if (offer.rate > _LIQUIDATION_THRESHOLD) {
                  revert RateTooHigh();
              }
              if (lien.amount > offer.maxAmount || lien.amount < offer.minAmount) {
                  revert InvalidLoan();
              }
              uint256 _amountTaken = amountTaken[hash];
              if (offer.totalAmount - _amountTaken < lien.amount) {
                  revert InsufficientOffer();
              }
              unchecked {
                  amountTaken[hash] = _amountTaken + lien.amount;
              }
              emit LoanOfferTaken(
                  hash,
                  lienId,
                  address(offer.collection),
                  lien.lender,
                  lien.borrower,
                  lien.amount,
                  lien.rate,
                  lien.tokenId,
                  lien.auctionDuration
              );
          }
          /*//////////////////////////////////////////////////
                          REFINANCING FLOWS
          //////////////////////////////////////////////////*/
          /**
           * @notice Starts Dutch Auction on lien ownership
           * @dev Must be called by lien owner
           * @param lienId Lien token id
           */
          function startAuction(Lien calldata lien, uint256 lienId) external validateLien(lien, lienId) {
              if (msg.sender != lien.lender) {
                  revert Unauthorized();
              }
              /* Cannot start if auction has already started. */
              if (lien.auctionStartBlock != 0) {
                  revert AuctionIsActive();
              }
              /* Add auction start block to lien. */
              liens[lienId] = keccak256(
                  abi.encode(
                      Lien({
                          lender: lien.lender,
                          borrower: lien.borrower,
                          collection: lien.collection,
                          tokenId: lien.tokenId,
                          amount: lien.amount,
                          startTime: lien.startTime,
                          rate: lien.rate,
                          auctionStartBlock: block.number,
                          auctionDuration: lien.auctionDuration
                      })
                  )
              );
              emit StartAuction(lienId, address(lien.collection));
          }
          /**
           * @notice Seizes collateral from defaulted lien, skipping liens that are not defaulted
           * @param lienPointers List of lien, lienId pairs
           */
          function seize(LienPointer[] calldata lienPointers) external {
              uint256 length = lienPointers.length;
              for (uint256 i; i < length; ) {
                  Lien calldata lien = lienPointers[i].lien;
                  uint256 lienId = lienPointers[i].lienId;
                  if (msg.sender != lien.lender) {
                      revert Unauthorized();
                  }
                  if (!_validateLien(lien, lienId)) {
                      revert InvalidLien();
                  }
                  /* Check that the auction has ended and lien is defaulted. */
                  if (_lienIsDefaulted(lien)) {
                      delete liens[lienId];
                      /* Seize collateral to lender. */
                      lien.collection.safeTransferFrom(address(this), lien.lender, lien.tokenId);
                      emit Seize(lienId, address(lien.collection));
                  }
                  unchecked {
                      ++i;
                  }
              }
          }
          /**
           * @notice Refinances to different loan amount and repays previous loan
           * @dev Must be called by lender; previous loan must be repaid with interest
           * @param lien Lien struct
           * @param lienId Lien id
           * @param offer Loan offer
           * @param signature Offer signatures
           */
          function refinance(
              Lien calldata lien,
              uint256 lienId,
              LoanOffer calldata offer,
              bytes calldata signature
          ) external validateLien(lien, lienId) lienIsActive(lien) {
              if (msg.sender != lien.lender) {
                  revert Unauthorized();
              }
              /* Interest rate must be at least as good as current. */
              if (offer.rate > lien.rate || offer.auctionDuration != lien.auctionDuration) {
                  revert InvalidRefinance();
              }
              uint256 debt = Helpers.computeCurrentDebt(lien.amount, lien.rate, lien.startTime);
              _refinance(lien, lienId, debt, offer, signature);
              /* Repay initial loan. */
              _POOL.transferFrom(offer.lender, lien.lender, debt);
          }
          /**
           * @notice Refinance lien in auction at the current debt amount where the interest rate ceiling increases over time
           * @dev Interest rate must be lower than the interest rate ceiling
           * @param lien Lien struct
           * @param lienId Lien token id
           * @param rate Interest rate (in bips)
           * @dev Formula: https://www.desmos.com/calculator/urasr71dhb
           */
          function refinanceAuction(
              Lien calldata lien,
              uint256 lienId,
              uint256 rate
          ) external validateLien(lien, lienId) auctionIsActive(lien) {
              /* Rate must be below current rate limit. */
              uint256 rateLimit = Helpers.calcRefinancingAuctionRate(
                  lien.auctionStartBlock,
                  lien.auctionDuration,
                  lien.rate
              );
              if (rate > rateLimit) {
                  revert RateTooHigh();
              }
              uint256 debt = Helpers.computeCurrentDebt(lien.amount, lien.rate, lien.startTime);
              /* Reset the lien with the new lender and interest rate. */
              liens[lienId] = keccak256(
                  abi.encode(
                      Lien({
                          lender: msg.sender, // set new lender
                          borrower: lien.borrower,
                          collection: lien.collection,
                          tokenId: lien.tokenId,
                          amount: debt, // new loan begins with previous debt
                          startTime: block.timestamp,
                          rate: rate,
                          auctionStartBlock: 0, // close the auction
                          auctionDuration: lien.auctionDuration
                      })
                  )
              );
              emit Refinance(
                  lienId,
                  address(lien.collection),
                  msg.sender,
                  debt,
                  rate,
                  lien.auctionDuration
              );
              /* Repay the initial loan. */
              _POOL.transferFrom(msg.sender, lien.lender, debt);
          }
          /**
           * @notice Refinances to different loan amount and repays previous loan
           * @param lien Lien struct
           * @param lienId Lien id
           * @param offer Loan offer
           * @param signature Offer signatures
           */
          function refinanceAuctionByOther(
              Lien calldata lien,
              uint256 lienId,
              LoanOffer calldata offer,
              bytes calldata signature
          ) external validateLien(lien, lienId) auctionIsActive(lien) {
              /* Rate must be below current rate limit and auction duration must be the same. */
              uint256 rateLimit = Helpers.calcRefinancingAuctionRate(
                  lien.auctionStartBlock,
                  lien.auctionDuration,
                  lien.rate
              );
              if (offer.rate > rateLimit || offer.auctionDuration != lien.auctionDuration) {
                  revert InvalidRefinance();
              }
              uint256 debt = Helpers.computeCurrentDebt(lien.amount, lien.rate, lien.startTime);
              _refinance(lien, lienId, debt, offer, signature);
              /* Repay initial loan. */
              _POOL.transferFrom(offer.lender, lien.lender, debt);
          }
          /**
           * @notice Refinances to different loan amount and repays previous loan
           * @dev Must be called by borrower; previous loan must be repaid with interest
           * @param lien Lien struct
           * @param lienId Lien id
           * @param loanAmount New loan amount
           * @param offer Loan offer
           * @param signature Offer signatures
           */
          function borrowerRefinance(
              Lien calldata lien,
              uint256 lienId,
              uint256 loanAmount,
              LoanOffer calldata offer,
              bytes calldata signature
          ) external validateLien(lien, lienId) lienIsActive(lien) {
              if (msg.sender != lien.borrower) {
                  revert Unauthorized();
              }
              if (offer.auctionDuration > _MAX_AUCTION_DURATION) {
                  revert InvalidAuctionDuration();
              }
              _refinance(lien, lienId, loanAmount, offer, signature);
              uint256 debt = Helpers.computeCurrentDebt(lien.amount, lien.rate, lien.startTime);
              if (loanAmount >= debt) {
                  /* If new loan is more than the previous, repay the initial loan and send the remaining to the borrower. */
                  _POOL.transferFrom(offer.lender, lien.lender, debt);
                  unchecked {
                      _POOL.transferFrom(offer.lender, lien.borrower, loanAmount - debt);
                  }
              } else {
                  /* If new loan is less than the previous, borrower must supply the difference to repay the initial loan. */
                  _POOL.transferFrom(offer.lender, lien.lender, loanAmount);
                  unchecked {
                      _POOL.transferFrom(lien.borrower, lien.lender, debt - loanAmount);
                  }
              }
          }
          function _refinance(
              Lien calldata lien,
              uint256 lienId,
              uint256 loanAmount,
              LoanOffer calldata offer,
              bytes calldata signature
          ) internal {
              if (lien.collection != offer.collection) {
                  revert CollectionsDoNotMatch();
              }
              /* Update lien with new loan details. */
              Lien memory newLien = Lien({
                  lender: offer.lender, // set new lender
                  borrower: lien.borrower,
                  collection: lien.collection,
                  tokenId: lien.tokenId,
                  amount: loanAmount,
                  startTime: block.timestamp,
                  rate: offer.rate,
                  auctionStartBlock: 0, // close the auction
                  auctionDuration: offer.auctionDuration
              });
              liens[lienId] = keccak256(abi.encode(newLien));
              /* Take the loan offer. */
              _takeLoanOffer(offer, signature, newLien, lienId);
              emit Refinance(
                  lienId,
                  address(offer.collection),
                  offer.lender,
                  loanAmount,
                  offer.rate,
                  offer.auctionDuration
              );
          }
          /*/////////////////////////////////////////////////////////////
                                MARKETPLACE FLOWS
          /////////////////////////////////////////////////////////////*/
          /**
           * @notice Purchase an NFT and use as collateral for a loan
           * @param offer Loan offer to take
           * @param signature Lender offer signature
           * @param loanAmount Loan amount in ETH
           * @param execution Marketplace execution data
           * @return lienId Lien id
           */
          function buyToBorrow(
              LoanOffer calldata offer,
              bytes calldata signature,
              uint256 loanAmount,
              ExecutionV1 calldata execution
          ) public returns (uint256 lienId) {
              if (execution.makerOrder.order.trader == address(this)) {
                  revert Unauthorized();
              }
              if (offer.auctionDuration > _MAX_AUCTION_DURATION) {
                  revert InvalidAuctionDuration();
              }
              uint256 collateralTokenId = execution.makerOrder.order.tokenId;
              uint256 price = execution.makerOrder.order.price;
              /* Create lien. */
              Lien memory lien = Lien({
                  lender: offer.lender,
                  borrower: msg.sender,
                  collection: offer.collection,
                  tokenId: collateralTokenId,
                  amount: loanAmount,
                  startTime: block.timestamp,
                  rate: offer.rate,
                  auctionStartBlock: 0,
                  auctionDuration: offer.auctionDuration
              });
              unchecked {
                  liens[lienId = _nextLienId++] = keccak256(abi.encode(lien));
              }
              /* Take the loan offer. */
              _takeLoanOffer(offer, signature, lien, lienId);
              /* Create the buy side order coming from Blend. */
              Helpers.executeTakeAsk(
                  offer,
                  execution,
                  loanAmount,
                  collateralTokenId,
                  price,
                  _POOL,
                  _EXCHANGE,
                  _SELL_MATCHING_POLICY
              );
          }
          function buyToBorrowV2(
              LoanOffer calldata offer,
              bytes calldata signature,
              uint256 loanAmount,
              AskExecutionV2 calldata execution
          ) public returns (uint256 lienId) {
              if (offer.auctionDuration > _MAX_AUCTION_DURATION) {
                  revert InvalidAuctionDuration();
              }
              uint256 collateralTokenId = execution.listing.tokenId;
              uint256 price = execution.listing.price;
              /* Create lien. */
              Lien memory lien = Lien({
                  lender: offer.lender,
                  borrower: msg.sender,
                  collection: offer.collection,
                  tokenId: collateralTokenId,
                  amount: loanAmount,
                  startTime: block.timestamp,
                  rate: offer.rate,
                  auctionStartBlock: 0,
                  auctionDuration: offer.auctionDuration
              });
              unchecked {
                  liens[lienId = _nextLienId++] = keccak256(abi.encode(lien));
              }
              /* Take the loan offer. */
              _takeLoanOffer(offer, signature, lien, lienId);
              /* Execute order using ETH currently in contract. */
              Helpers.executeTakeAskV2(
                  offer,
                  execution,
                  loanAmount,
                  collateralTokenId,
                  price,
                  _POOL,
                  _EXCHANGE_V2
              );
          }
          /**
           * @notice Purchase a locked NFT; repay the initial loan; lock the token as collateral for a new loan
           * @param lien Lien preimage struct
           * @param sellInput Sell offer and signature
           * @param loanInput Loan offer and signature
           * @return lienId Lien id
           */
          function buyToBorrowLocked(
              Lien calldata lien,
              SellInput calldata sellInput,
              LoanInput calldata loanInput,
              uint256 loanAmount
          )
              public
              validateLien(lien, sellInput.offer.lienId)
              lienIsActive(lien)
              returns (uint256 lienId)
          {
              if (lien.collection != loanInput.offer.collection) {
                  revert CollectionsDoNotMatch();
              }
              (uint256 priceAfterFees, uint256 debt) = _buyLocked(
                  lien,
                  sellInput.offer,
                  sellInput.signature
              );
              lienId = _borrow(loanInput.offer, loanInput.signature, loanAmount, lien.tokenId);
              /* Transfer funds. */
              /* Need to repay the original loan and payout any surplus from the sell or loan funds. */
              if (loanAmount < debt) {
                  /* loanAmount < debt < priceAfterFees */
                  /* Repay loan with funds from new lender to old lender. */
                  _POOL.transferFrom(loanInput.offer.lender, lien.lender, loanAmount); // doesn't cover debt
                  unchecked {
                      /* Supplement difference from new borrower. */
                      _POOL.transferFrom(msg.sender, lien.lender, debt - loanAmount); // cover rest of debt
                      /* Send rest of sell funds to borrower. */
                      _POOL.transferFrom(msg.sender, sellInput.offer.borrower, priceAfterFees - debt);
                  }
              } else if (loanAmount < priceAfterFees) {
                  /* debt < loanAmount < priceAfterFees */
                  /* Repay loan with funds from new lender to old lender. */
                  _POOL.transferFrom(loanInput.offer.lender, lien.lender, debt);
                  unchecked {
                      /* Send rest of loan from new lender to old borrower. */
                      _POOL.transferFrom(
                          loanInput.offer.lender,
                          sellInput.offer.borrower,
                          loanAmount - debt
                      );
                      /* Send rest of sell funds from new borrower to old borrower. */
                      _POOL.transferFrom(
                          msg.sender,
                          sellInput.offer.borrower,
                          priceAfterFees - loanAmount
                      );
                  }
              } else {
                  /* debt < priceAfterFees < loanAmount */
                  /* Repay loan with funds from new lender to old lender. */
                  _POOL.transferFrom(loanInput.offer.lender, lien.lender, debt);
                  unchecked {
                      /* Send rest of sell funds from new lender to old borrower. */
                      _POOL.transferFrom(
                          loanInput.offer.lender,
                          sellInput.offer.borrower,
                          priceAfterFees - debt
                      );
                      /* Send rest of loan from new lender to new borrower. */
                      _POOL.transferFrom(loanInput.offer.lender, msg.sender, loanAmount - priceAfterFees);
                  }
              }
          }
          /**
           * @notice Purchases a locked NFT and uses the funds to repay the loan
           * @param lien Lien preimage
           * @param offer Sell offer
           * @param signature Lender offer signature
           */
          function buyLocked(
              Lien calldata lien,
              SellOffer calldata offer,
              bytes calldata signature
          ) public validateLien(lien, offer.lienId) lienIsActive(lien) {
              (uint256 priceAfterFees, uint256 debt) = _buyLocked(lien, offer, signature);
              /* Send token to buyer. */
              lien.collection.safeTransferFrom(address(this), msg.sender, lien.tokenId);
              /* Repay lender. */
              _POOL.transferFrom(msg.sender, lien.lender, debt);
              /* Send surplus to borrower. */
              unchecked {
                  _POOL.transferFrom(msg.sender, lien.borrower, priceAfterFees - debt);
              }
          }
          /**
           * @notice Takes a bid on a locked NFT and use the funds to repay the lien
           * @dev Must be called by the borrower
           * @param lien Lien preimage
           * @param lienId Lien id
           * @param execution Marketplace execution data
           */
          function takeBid(
              Lien calldata lien,
              uint256 lienId,
              ExecutionV1 calldata execution
          ) external validateLien(lien, lienId) lienIsActive(lien) {
              if (execution.makerOrder.order.trader == address(this) || msg.sender != lien.borrower) {
                  revert Unauthorized();
              }
              /* Repay loan with funds received from the sale. */
              uint256 debt = _repay(lien, lienId);
              Helpers.executeTakeBid(
                  lien,
                  lienId,
                  execution,
                  debt,
                  _POOL,
                  _EXCHANGE,
                  _DELEGATE,
                  _BID_MATCHING_POLICY
              );
          }
          function takeBidV2(
              Lien calldata lien,
              uint256 lienId,
              BidExecutionV2 calldata execution
          ) external validateLien(lien, lienId) lienIsActive(lien) {
              if (msg.sender != lien.borrower) {
                  revert Unauthorized();
              }
              /* Repay loan with funds received from the sale. */
              uint256 debt = _repay(lien, lienId);
              Helpers.executeTakeBidV2(lien, execution, debt, _POOL, _EXCHANGE_V2, _DELEGATE_V2);
          }
          /**
           * @notice Verify and take sell offer for token locked in lien; use the funds to repay the debt on the lien
           * @dev Does not transfer assets
           * @param lien Lien preimage
           * @param offer Loan offer
           * @param signature Loan offer signature
           * @return priceAfterFees Price of the token (after fees), debt Current debt amount
           */
          function _buyLocked(
              Lien calldata lien,
              SellOffer calldata offer,
              bytes calldata signature
          ) internal returns (uint256 priceAfterFees, uint256 debt) {
              if (lien.borrower != offer.borrower) {
                  revert Unauthorized();
              }
              priceAfterFees = _takeSellOffer(offer, signature);
              /* Repay loan with funds received from the sale. */
              debt = _repay(lien, offer.lienId);
              if (priceAfterFees < debt) {
                  revert InvalidRepayment();
              }
              emit BuyLocked(
                  offer.lienId,
                  address(lien.collection),
                  msg.sender,
                  lien.borrower,
                  lien.tokenId
              );
          }
          /**
           * @notice Validates, fulfills, and transfers fees on sell offer
           * @param sellOffer Sell offer
           * @param sellSignature Sell offer signature
           */
          function _takeSellOffer(
              SellOffer calldata sellOffer,
              bytes calldata sellSignature
          ) internal returns (uint256 priceAfterFees) {
              _validateOffer(
                  _hashSellOffer(sellOffer),
                  sellOffer.borrower,
                  sellOffer.oracle,
                  sellSignature,
                  sellOffer.expirationTime,
                  sellOffer.salt
              );
              /* Mark the sell offer as fulfilled. */
              cancelledOrFulfilled[sellOffer.borrower][sellOffer.salt] = 1;
              /* Transfer fees. */
              uint256 totalFees = _transferFees(sellOffer.fees, msg.sender, sellOffer.price);
              unchecked {
                  priceAfterFees = sellOffer.price - totalFees;
              }
          }
          function _transferFees(
              Fee[] calldata fees,
              address from,
              uint256 price
          ) internal returns (uint256 totalFee) {
              uint256 feesLength = fees.length;
              for (uint256 i = 0; i < feesLength; ) {
                  uint256 fee = (price * fees[i].rate) / _BASIS_POINTS;
                  _POOL.transferFrom(from, fees[i].recipient, fee);
                  totalFee += fee;
                  unchecked {
                      ++i;
                  }
              }
              if (totalFee > price) {
                  revert FeesTooHigh();
              }
          }
          receive() external payable {
              if (msg.sender != address(_POOL) && msg.sender != address(_EXCHANGE_V2)) {
                  revert Unauthorized();
              }
          }
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external pure returns (bytes4) {
              return this.onERC721Received.selector;
          }
          /*/////////////////////////////////////////////////////////////
                              PAYABLE WRAPPERS
          /////////////////////////////////////////////////////////////*/
          /**
           * @notice buyToBorrow wrapper that deposits ETH to pool
           */
          function buyToBorrowETH(
              LoanOffer calldata offer,
              bytes calldata signature,
              uint256 loanAmount,
              ExecutionV1 calldata execution
          ) external payable returns (uint256 lienId) {
              _POOL.deposit{ value: msg.value }(msg.sender);
              return buyToBorrow(offer, signature, loanAmount, execution);
          }
          /**
           * @notice buyToBorrow wrapper that deposits ETH to pool
           */
          function buyToBorrowV2ETH(
              LoanOffer calldata offer,
              bytes calldata signature,
              uint256 loanAmount,
              AskExecutionV2 calldata execution
          ) external payable returns (uint256 lienId) {
              _POOL.deposit{ value: msg.value }(msg.sender);
              return buyToBorrowV2(offer, signature, loanAmount, execution);
          }
          /**
           * @notice buyToBorrowLocked wrapper that deposits ETH to pool
           */
          function buyToBorrowLockedETH(
              Lien calldata lien,
              SellInput calldata sellInput,
              LoanInput calldata loanInput,
              uint256 loanAmount
          ) external payable returns (uint256 lienId) {
              _POOL.deposit{ value: msg.value }(msg.sender);
              return buyToBorrowLocked(lien, sellInput, loanInput, loanAmount);
          }
          /**
           * @notice buyLocked wrapper that deposits ETH to pool
           */
          function buyLockedETH(
              Lien calldata lien,
              SellOffer calldata offer,
              bytes calldata signature
          ) external payable {
              _POOL.deposit{ value: msg.value }(msg.sender);
              return buyLocked(lien, offer, signature);
          }
          /*/////////////////////////////////////////////////////////////
                              VALIDATION MODIFIERS
          /////////////////////////////////////////////////////////////*/
          modifier validateLien(Lien calldata lien, uint256 lienId) {
              if (!_validateLien(lien, lienId)) {
                  revert InvalidLien();
              }
              _;
          }
          modifier lienIsActive(Lien calldata lien) {
              if (_lienIsDefaulted(lien)) {
                  revert LienIsDefaulted();
              }
              _;
          }
          modifier auctionIsActive(Lien calldata lien) {
              if (!_auctionIsActive(lien)) {
                  revert AuctionIsNotActive();
              }
              _;
          }
          function _validateLien(Lien calldata lien, uint256 lienId) internal view returns (bool) {
              return liens[lienId] == keccak256(abi.encode(lien));
          }
          function _lienIsDefaulted(Lien calldata lien) internal view returns (bool) {
              return
                  lien.auctionStartBlock != 0 &&
                  lien.auctionStartBlock + lien.auctionDuration < block.number;
          }
          function _auctionIsActive(Lien calldata lien) internal view returns (bool) {
              return
                  lien.auctionStartBlock != 0 &&
                  lien.auctionStartBlock + lien.auctionDuration >= block.number;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/UUPSUpgradeable.sol)
      pragma solidity ^0.8.0;
      import "../../interfaces/draft-IERC1822Upgradeable.sol";
      import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
      import "./Initializable.sol";
      /**
       * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
       * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
       *
       * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
       * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
       * `UUPSUpgradeable` with a custom implementation of upgrades.
       *
       * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
       *
       * _Available since v4.1._
       */
      abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
          function __UUPSUpgradeable_init() internal onlyInitializing {
          }
          function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
          }
          /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
          address private immutable __self = address(this);
          /**
           * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
           * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
           * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
           * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
           * fail.
           */
          modifier onlyProxy() {
              require(address(this) != __self, "Function must be called through delegatecall");
              require(_getImplementation() == __self, "Function must be called through active proxy");
              _;
          }
          /**
           * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
           * callable on the implementing contract but not through proxies.
           */
          modifier notDelegated() {
              require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
              _;
          }
          /**
           * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
           * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
           *
           * 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. This is guaranteed by the `notDelegated` modifier.
           */
          function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
              return _IMPLEMENTATION_SLOT;
          }
          /**
           * @dev Upgrade the implementation of the proxy to `newImplementation`.
           *
           * Calls {_authorizeUpgrade}.
           *
           * Emits an {Upgraded} event.
           */
          function upgradeTo(address newImplementation) external virtual onlyProxy {
              _authorizeUpgrade(newImplementation);
              _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
          }
          /**
           * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
           * encoded in `data`.
           *
           * Calls {_authorizeUpgrade}.
           *
           * Emits an {Upgraded} event.
           */
          function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
              _authorizeUpgrade(newImplementation);
              _upgradeToAndCallUUPS(newImplementation, data, true);
          }
          /**
           * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
           * {upgradeTo} and {upgradeToAndCall}.
           *
           * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
           *
           * ```solidity
           * function _authorizeUpgrade(address) internal override onlyOwner {}
           * ```
           */
          function _authorizeUpgrade(address newImplementation) internal virtual;
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[50] private __gap;
      }
      // SPDX-License-Identifier: BSL 1.1 - Blend (c) Non Fungible Trading Ltd.
      pragma solidity 0.8.17;
      import "lib/solmate/src/utils/SignedWadMath.sol";
      import { InvalidRepayment } from "./lib/Errors.sol";
      import "./lib/Structs.sol";
      import "../pool/interfaces/IBlurPool.sol";
      import { IBlurExchangeV2 as IExchangeV2 } from "../exchangeV2/interfaces/IBlurExchangeV2.sol";
      import { Order as OrderV1, SignatureVersion, Side } from "../exchangeV1/lib/OrderStructs.sol";
      import {
          TakeAskSingle,
          TakeBidSingle,
          FeeRate,
          Taker,
          Exchange,
          Order as OrderV2,
          AssetType
      } from "../exchangeV2/lib/Structs.sol";
      interface IExchange {
          function execute(Input calldata sell, Input calldata buy) external payable;
      }
      library Helpers {
          int256 private constant _YEAR_WAD = 365 days * 1e18;
          uint256 private constant _LIQUIDATION_THRESHOLD = 100_000;
          uint256 private constant _BASIS_POINTS = 10_000;
          error InvalidExecution();
          /**
           * @dev Computes the current debt of a borrow given the last time it was touched and the last computed debt.
           * @param amount Principal in ETH
           * @param startTime Start time of the loan
           * @param rate Interest rate (in bips)
           * @dev Formula: https://www.desmos.com/calculator/l6omp0rwnh
           */
          function computeCurrentDebt(
              uint256 amount,
              uint256 rate,
              uint256 startTime
          ) public view returns (uint256) {
              uint256 loanTime = block.timestamp - startTime;
              int256 yearsWad = wadDiv(int256(loanTime) * 1e18, _YEAR_WAD);
              return uint256(wadMul(int256(amount), wadExp(wadMul(yearsWad, bipsToSignedWads(rate)))));
          }
          /**
           * @dev Calculates the current maximum interest rate a specific refinancing
           * auction could settle at currently given the auction's start block and duration.
           * @param startBlock The block the auction started at
           * @param oldRate Previous interest rate (in bips)
           * @dev Formula: https://www.desmos.com/calculator/urasr71dhb
           */
          function calcRefinancingAuctionRate(
              uint256 startBlock,
              uint256 auctionDuration,
              uint256 oldRate
          ) public view returns (uint256) {
              uint256 currentAuctionBlock = block.number - startBlock;
              int256 oldRateWads = bipsToSignedWads(oldRate);
              uint256 auctionT1 = auctionDuration / 5;
              uint256 auctionT2 = (4 * auctionDuration) / 5;
              int256 maxRateWads;
              {
                  int256 aInverse = -bipsToSignedWads(15000);
                  int256 b = 2;
                  int256 maxMinRateWads = bipsToSignedWads(500);
                  if (oldRateWads < -((b * aInverse) / 2)) {
                      maxRateWads = maxMinRateWads + (oldRateWads ** 2) / aInverse + b * oldRateWads;
                  } else {
                      maxRateWads = maxMinRateWads - ((b ** 2) * aInverse) / 4;
                  }
              }
              int256 startSlope = maxRateWads / int256(auctionT1); // wad-bips per block
              int256 middleSlope = bipsToSignedWads(9000) / int256((3 * auctionDuration) / 5) + 1; // wad-bips per block (add one to account for rounding)
              int256 middleB = maxRateWads - int256(auctionT1) * middleSlope;
              if (currentAuctionBlock < auctionT1) {
                  return signedWadsToBips(startSlope * int256(currentAuctionBlock));
              } else if (currentAuctionBlock < auctionT2) {
                  return signedWadsToBips(middleSlope * int256(currentAuctionBlock) + middleB);
              } else if (currentAuctionBlock < auctionDuration) {
                  int256 endSlope;
                  int256 endB;
                  {
                      endSlope =
                          (bipsToSignedWads(_LIQUIDATION_THRESHOLD) -
                              ((int256(auctionT2) * middleSlope) + middleB)) /
                          int256(auctionDuration - auctionT2); // wad-bips per block
                      endB =
                          bipsToSignedWads(_LIQUIDATION_THRESHOLD) -
                          int256(auctionDuration) *
                          endSlope;
                  }
                  return signedWadsToBips(endSlope * int256(currentAuctionBlock) + endB);
              } else {
                  return _LIQUIDATION_THRESHOLD;
              }
          }
          /**
           * @dev Converts an integer bips value to a signed wad value.
           */
          function bipsToSignedWads(uint256 bips) public pure returns (int256) {
              return int256((bips * 1e18) / _BASIS_POINTS);
          }
          /**
           * @dev Converts a signed wad value to an integer bips value.
           */
          function signedWadsToBips(int256 wads) public pure returns (uint256) {
              return uint256((wads * int256(_BASIS_POINTS)) / 1e18);
          }
          function executeTakeBid(
              Lien calldata lien,
              uint256 lienId,
              ExecutionV1 calldata execution,
              uint256 debt,
              IBlurPool pool,
              IExchange exchange,
              address delegate,
              address matchingPolicy
          ) external {
              /* Create sell side order from Blend. */
              OrderV1 memory sellOrder = OrderV1({
                  trader: address(this),
                  side: Side.Sell,
                  matchingPolicy: matchingPolicy,
                  collection: address(lien.collection),
                  tokenId: lien.tokenId,
                  amount: 1,
                  paymentToken: address(pool),
                  price: execution.makerOrder.order.price,
                  listingTime: execution.makerOrder.order.listingTime + 1, // listingTime determines maker/taker
                  expirationTime: type(uint256).max,
                  fees: new Fee[](0),
                  salt: lienId, // prevent reused order hash
                  extraParams: "\\x01" // require oracle signature
              });
              Input memory sell = Input({
                  order: sellOrder,
                  v: 0,
                  r: bytes32(0),
                  s: bytes32(0),
                  extraSignature: execution.extraSignature,
                  signatureVersion: SignatureVersion.Single,
                  blockNumber: execution.blockNumber
              });
              /* Execute marketplace order. */
              uint256 balanceBefore = pool.balanceOf(address(this));
              lien.collection.approve(delegate, lien.tokenId);
              exchange.execute(sell, execution.makerOrder);
              /* Determine the funds received from the sale (after fees). */
              uint256 amountReceivedFromSale = pool.balanceOf(address(this)) - balanceBefore;
              if (amountReceivedFromSale < debt) {
                  revert InvalidRepayment();
              }
              /* Repay lender. */
              pool.transferFrom(address(this), lien.lender, debt);
              /* Send surplus to borrower. */
              unchecked {
                  pool.transferFrom(address(this), lien.borrower, amountReceivedFromSale - debt);
              }
          }
          function executeTakeAskV2(
              LoanOffer calldata offer, 
              AskExecutionV2 calldata execution,
              uint256 loanAmount,
              uint256 collateralTokenId,
              uint256 price,
              IBlurPool pool,
              IExchangeV2 exchangeV2
          ) external {
              OrderV2 calldata order = execution.order;
              if (address(offer.collection) != order.collection || order.assetType != AssetType.ERC721) {
                  revert InvalidExecution();
              }
              /* Transfer funds. */
              /* Need to retrieve the ETH to fund the marketplace execution. */
              if (loanAmount < price) {
                  /* Take funds from lender. */
                  pool.withdrawFrom(offer.lender, address(this), loanAmount);
                  /* Supplement difference from borrower. */
                  unchecked {
                      pool.withdrawFrom(msg.sender, address(this), price - loanAmount);
                  }
              } else {
                  /* Take funds from lender. */
                  pool.withdrawFrom(offer.lender, address(this), price);
                  /* Send surplus to borrower. */
                  unchecked {
                      pool.transferFrom(offer.lender, msg.sender, loanAmount - price);
                  }
              }
              TakeAskSingle memory execute = TakeAskSingle({
                  order: execution.order,
                  exchange: Exchange({
                      index: 0,
                      proof: execution.proof,
                      listing: Listing({
                          index: execution.listing.index,
                          tokenId: collateralTokenId,
                          amount: 1,
                          price: price
                      }),
                      taker: Taker({ tokenId: collateralTokenId, amount: 1 })
                  }),
                  takerFee: FeeRate(address(0), 0),
                  signature: execution.signature,
                  tokenRecipient: address(this)
              });
              exchangeV2.takeAskSingle{ value: price }(execute, execution.oracleSignature);
          }
          function executeTakeAsk(
              LoanOffer calldata offer,
              ExecutionV1 calldata execution,
              uint256 loanAmount,
              uint256 collateralTokenId,
              uint256 price,
              IBlurPool pool,
              IExchange exchange,
              address matchingPolicy
          ) external {
              /* Transfer funds. */
              /* Need to retrieve the ETH to fund the marketplace execution. */
              if (loanAmount < price) {
                  /* Take funds from lender. */
                  pool.withdrawFrom(offer.lender, address(this), loanAmount);
                  /* Supplement difference from borrower. */
                  unchecked {
                      pool.withdrawFrom(msg.sender, address(this), price - loanAmount);
                  }
              } else {
                  /* Take funds from lender. */
                  pool.withdrawFrom(offer.lender, address(this), price);
                  /* Send surplus to borrower. */
                  unchecked {
                      pool.transferFrom(offer.lender, msg.sender, loanAmount - price);
                  }
              }
              OrderV1 memory buyOrder = OrderV1({
                  trader: address(this),
                  side: Side.Buy,
                  matchingPolicy: matchingPolicy,
                  collection: address(offer.collection),
                  tokenId: collateralTokenId,
                  amount: 1,
                  paymentToken: address(0),
                  price: price,
                  listingTime: execution.makerOrder.order.listingTime + 1, // listingTime determines maker/taker
                  expirationTime: type(uint256).max,
                  fees: new Fee[](0),
                  salt: uint160(execution.makerOrder.order.trader), // prevent reused order hash
                  extraParams: "\\x01" // require oracle signature
              });
              Input memory buy = Input({
                  order: buyOrder,
                  v: 0,
                  r: bytes32(0),
                  s: bytes32(0),
                  extraSignature: execution.extraSignature,
                  signatureVersion: SignatureVersion.Single,
                  blockNumber: execution.blockNumber
              });
              /* Execute order using ETH currently in contract. */
              exchange.execute{ value: price }(execution.makerOrder, buy);
          }
          function executeTakeBidV2(
              Lien calldata lien,
              BidExecutionV2 calldata execution,
              uint256 debt,
              IBlurPool pool,
              IExchangeV2 exchangeV2,
              address delegateV2
          ) external {
              OrderV2 calldata order = execution.order;
              if (address(lien.collection) != order.collection || order.assetType != AssetType.ERC721) {
                  revert InvalidExecution();
              }
              uint256 balanceBefore = pool.balanceOf(address(this));
              TakeBidSingle memory execute = TakeBidSingle({
                  order: execution.order,
                  exchange: Exchange({
                      index: 0,
                      proof: execution.proof,
                      listing: execution.listing,
                      taker: Taker({ tokenId: lien.tokenId, amount: 1 })
                  }),
                  takerFee: FeeRate(address(0), 0),
                  signature: execution.signature
              });
              /* Execute marketplace order. */
              lien.collection.approve(delegateV2, lien.tokenId);
              exchangeV2.takeBidSingle(execute, execution.oracleSignature);
              /* Determine the funds received from the sale (after fees). */
              uint256 amountReceivedFromSale = pool.balanceOf(address(this)) - balanceBefore;
              if (amountReceivedFromSale < debt) {
                  revert InvalidRepayment();
              }
              /* Repay lender. */
              pool.transferFrom(address(this), lien.lender, debt);
              /* Send surplus to borrower. */
              unchecked {
                  pool.transferFrom(address(this), lien.borrower, amountReceivedFromSale - debt);
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.17;
      import "lib/solmate/src/tokens/ERC721.sol";
      import { Input, Fee } from "../../exchangeV1/lib/OrderStructs.sol";
      import { Order as OrderV2, Listing } from "../../exchangeV2/lib/Structs.sol";
      struct LienPointer {
          Lien lien;
          uint256 lienId;
      }
      struct SellOffer {
          address borrower;
          uint256 lienId;
          uint256 price;
          uint256 expirationTime;
          uint256 salt;
          address oracle;
          Fee[] fees;
      }
      struct Lien {
          address lender;
          address borrower;
          ERC721 collection;
          uint256 tokenId;
          uint256 amount;
          uint256 startTime;
          uint256 rate;
          uint256 auctionStartBlock;
          uint256 auctionDuration;
      }
      struct LoanOffer {
          address lender;
          ERC721 collection;
          uint256 totalAmount;
          uint256 minAmount;
          uint256 maxAmount;
          uint256 auctionDuration;
          uint256 salt;
          uint256 expirationTime;
          uint256 rate;
          address oracle;
      }
      struct LoanInput {
          LoanOffer offer;
          bytes signature;
      }
      struct SellInput {
          SellOffer offer;
          bytes signature;
      }
      struct ExecutionV1 {
          Input makerOrder;
          bytes extraSignature;
          uint256 blockNumber;
      }
      struct BidExecutionV2 {
          OrderV2 order;
          Listing listing;
          bytes32[] proof;
          bytes signature;
          bytes oracleSignature;
      }
      struct AskExecutionV2 {
          OrderV2 order;
          Listing listing;
          bytes32[] proof;
          bytes signature;
          bytes oracleSignature;
      }
      // SPDX-License-Identifier: BSL 1.1 - Blend (c) Non Fungible Trading Ltd.
      pragma solidity 0.8.17;
      import "lib/openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgradeable.sol";
      import "./lib/Signatures.sol";
      import "./interfaces/IOfferController.sol";
      abstract contract OfferController is IOfferController, Signatures, Ownable2StepUpgradeable {
          mapping(address => mapping(uint256 => uint256)) public cancelledOrFulfilled;
          uint256[50] private _gap;
          /**
           * @notice Assert offer validity
           * @param offerHash Offer hash
           * @param signer Address of offer signer
           * @param oracle Address of oracle
           * @param signature Packed signature array
           * @param expirationTime Offer expiration time
           * @param salt Offer salt
           */
          function _validateOffer(
              bytes32 offerHash,
              address signer,
              address oracle,
              bytes calldata signature,
              uint256 expirationTime,
              uint256 salt
          ) internal view {
              _verifyOfferAuthorization(offerHash, signer, oracle, signature);
              if (expirationTime < block.timestamp) {
                  revert OfferExpired();
              }
              if (cancelledOrFulfilled[signer][salt] == 1) {
                  revert OfferUnavailable();
              }
          }
          /*/////////////////////////////////////////
                        CANCEL FUNCTIONS
          /////////////////////////////////////////*/
          /**
           * @notice Cancels offer salt for caller
           * @param salt Unique offer salt
           */
          function cancelOffer(uint256 salt) external {
              _cancelOffer(msg.sender, salt);
          }
          /**
           * @notice Cancels offers in bulk for caller
           * @param salts List of offer salts
           */
          function cancelOffers(uint256[] calldata salts) external {
              uint256 saltsLength = salts.length;
              for (uint256 i; i < saltsLength; ) {
                  _cancelOffer(msg.sender, salts[i]);
                  unchecked {
                      ++i;
                  }
              }
          }
          /**
           * @notice Cancels all offers by incrementing caller nonce
           */
          function incrementNonce() external {
              _incrementNonce(msg.sender);
          }
          /**
           * @notice Cancel offer by user and salt
           * @param user Address of user
           * @param salt Unique offer salt
           */
          function _cancelOffer(address user, uint256 salt) private {
              cancelledOrFulfilled[user][salt] = 1;
              emit OfferCancelled(user, salt);
          }
          /**
           * @notice Cancel all orders by incrementing the user nonce
           * @param user Address of user
           */
          function _incrementNonce(address user) internal {
              emit NonceIncremented(user, ++nonces[user]);
          }
          /*/////////////////////////////////////////
                        ADMIN FUNCTIONS
          /////////////////////////////////////////*/
          /**
           * @notice Set approval for an oracle address
           * @param oracle Address of oracle
           * @param approved Whether the oracle is approved
           */
          function setOracle(address oracle, bool approved) external onlyOwner {
              if (approved) {
                  oracles[oracle] = 1;
              } else {
                  oracles[oracle] = 0;
              }
          }
          /**
           * @notice Set the block range expiry of oracle signatures
           * @param _blockRange Block range
           */
          function setBlockRange(uint256 _blockRange) external onlyOwner {
              blockRange = _blockRange;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../lib/Structs.sol";
      import "./IOfferController.sol";
      interface IBlend is IOfferController {
          event LoanOfferTaken(
              bytes32 offerHash,
              uint256 lienId,
              address collection,
              address lender,
              address borrower,
              uint256 loanAmount,
              uint256 rate,
              uint256 tokenId,
              uint256 auctionDuration
          );
          event Repay(uint256 lienId, address collection);
          event StartAuction(uint256 lienId, address collection);
          event Refinance(
              uint256 lienId,
              address collection,
              address newLender,
              uint256 newAmount,
              uint256 newRate,
              uint256 newAuctionDuration
          );
          event Seize(uint256 lienId, address collection);
          event BuyLocked(
              uint256 lienId,
              address collection,
              address buyer,
              address seller,
              uint256 tokenId
          );
          function amountTaken(bytes32 offerHash) external view returns (uint256);
          function liens(uint256 lienId) external view returns (bytes32);
          /*//////////////////////////////////////////////////
                          BORROW FLOWS
          //////////////////////////////////////////////////*/
          function borrow(
              LoanOffer calldata offer,
              bytes calldata signature,
              uint256 loanAmount,
              uint256 collateralId
          ) external returns (uint256 lienId);
          function repay(Lien calldata lien, uint256 lienId) external;
          /*//////////////////////////////////////////////////
                          REFINANCING FLOWS
          //////////////////////////////////////////////////*/
          function startAuction(Lien calldata lien, uint256 lienId) external;
          function seize(LienPointer[] calldata lienPointers) external;
          function refinance(
              Lien calldata lien,
              uint256 lienId,
              LoanOffer calldata offer,
              bytes calldata signature
          ) external;
          function refinanceAuction(Lien calldata lien, uint256 lienId, uint256 rate) external;
          function refinanceAuctionByOther(
              Lien calldata lien,
              uint256 lienId,
              LoanOffer calldata offer,
              bytes calldata signature
          ) external;
          function borrowerRefinance(
              Lien calldata lien,
              uint256 lienId,
              uint256 loanAmount,
              LoanOffer calldata offer,
              bytes calldata signature
          ) external;
          /*//////////////////////////////////////////////////
                          MARKETPLACE FLOWS
          //////////////////////////////////////////////////*/
          function buyToBorrow(
              LoanOffer calldata offer,
              bytes calldata signature,
              uint256 loanAmount,
              ExecutionV1 calldata execution
          ) external returns (uint256 lienId);
          function buyToBorrowETH(
              LoanOffer calldata offer,
              bytes calldata signature,
              uint256 loanAmount,
              ExecutionV1 calldata execution
          ) external payable returns (uint256 lienId);
          function buyToBorrowLocked(
              Lien calldata lien,
              SellInput calldata sellInput,
              LoanInput calldata loanInput,
              uint256 loanAmount
          ) external returns (uint256 lienId);
          function buyToBorrowLockedETH(
              Lien calldata lien,
              SellInput calldata sellInput,
              LoanInput calldata loanInput,
              uint256 loanAmount
          ) external payable returns (uint256 lienId);
          function buyLocked(
              Lien calldata lien,
              SellOffer calldata offer,
              bytes calldata signature
          ) external;
          function buyLockedETH(
              Lien calldata lien,
              SellOffer calldata offer,
              bytes calldata signature
          ) external payable;
          function takeBid(Lien calldata lien, uint256 lienId, ExecutionV1 calldata execution) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      interface IBlurPool {
          event Transfer(address indexed from, address indexed to, uint256 amount);
          function initialize() external;
          function decimals() external pure returns (uint8);
          function totalSupply() external view returns (uint256);
          function balanceOf(address user) external view returns (uint256);
          function deposit() external payable;
          function deposit(address user) external payable;
          function withdraw(uint256 amount) external;
          function withdrawFrom(address from, address to, uint256 amount) external;
          function transferFrom(address from, address to, uint256 amount) external returns (bool);
      }
      // 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 IERC1822ProxiableUpgradeable {
          /**
           * @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.8.3) (proxy/ERC1967/ERC1967Upgrade.sol)
      pragma solidity ^0.8.2;
      import "../beacon/IBeaconUpgradeable.sol";
      import "../../interfaces/IERC1967Upgradeable.sol";
      import "../../interfaces/draft-IERC1822Upgradeable.sol";
      import "../../utils/AddressUpgradeable.sol";
      import "../../utils/StorageSlotUpgradeable.sol";
      import "../utils/Initializable.sol";
      /**
       * @dev This abstract contract provides getters and event emitting update functions for
       * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
       *
       * _Available since v4.1._
       *
       * @custom:oz-upgrades-unsafe-allow delegatecall
       */
      abstract contract ERC1967UpgradeUpgradeable is Initializable, IERC1967Upgradeable {
          function __ERC1967Upgrade_init() internal onlyInitializing {
          }
          function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
          }
          // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
          bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
          /**
           * @dev Storage slot with the address of the current implementation.
           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
           * validated in the constructor.
           */
          bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
          /**
           * @dev Returns the current implementation address.
           */
          function _getImplementation() internal view returns (address) {
              return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
          }
          /**
           * @dev Stores a new address in the EIP1967 implementation slot.
           */
          function _setImplementation(address newImplementation) private {
              require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
              StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
          }
          /**
           * @dev Perform implementation upgrade
           *
           * Emits an {Upgraded} event.
           */
          function _upgradeTo(address newImplementation) internal {
              _setImplementation(newImplementation);
              emit Upgraded(newImplementation);
          }
          /**
           * @dev Perform implementation upgrade with additional setup call.
           *
           * Emits an {Upgraded} event.
           */
          function _upgradeToAndCall(
              address newImplementation,
              bytes memory data,
              bool forceCall
          ) internal {
              _upgradeTo(newImplementation);
              if (data.length > 0 || forceCall) {
                  _functionDelegateCall(newImplementation, data);
              }
          }
          /**
           * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
           *
           * Emits an {Upgraded} event.
           */
          function _upgradeToAndCallUUPS(
              address newImplementation,
              bytes memory data,
              bool forceCall
          ) internal {
              // Upgrades from old implementations will perform a rollback test. This test requires the new
              // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
              // this special case will break upgrade paths from old UUPS implementation to new ones.
              if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
                  _setImplementation(newImplementation);
              } else {
                  try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                      require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                  } catch {
                      revert("ERC1967Upgrade: new implementation is not UUPS");
                  }
                  _upgradeToAndCall(newImplementation, data, forceCall);
              }
          }
          /**
           * @dev Storage slot with the admin of the contract.
           * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
           * validated in the constructor.
           */
          bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
          /**
           * @dev Returns the current admin.
           */
          function _getAdmin() internal view returns (address) {
              return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
          }
          /**
           * @dev Stores a new address in the EIP1967 admin slot.
           */
          function _setAdmin(address newAdmin) private {
              require(newAdmin != address(0), "ERC1967: new admin is the zero address");
              StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
          }
          /**
           * @dev Changes the admin of the proxy.
           *
           * Emits an {AdminChanged} event.
           */
          function _changeAdmin(address newAdmin) internal {
              emit AdminChanged(_getAdmin(), newAdmin);
              _setAdmin(newAdmin);
          }
          /**
           * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
           * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
           */
          bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
          /**
           * @dev Returns the current beacon.
           */
          function _getBeacon() internal view returns (address) {
              return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
          }
          /**
           * @dev Stores a new beacon in the EIP1967 beacon slot.
           */
          function _setBeacon(address newBeacon) private {
              require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
              require(
                  AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
                  "ERC1967: beacon implementation is not a contract"
              );
              StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
          }
          /**
           * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
           * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
           *
           * Emits a {BeaconUpgraded} event.
           */
          function _upgradeBeaconToAndCall(
              address newBeacon,
              bytes memory data,
              bool forceCall
          ) internal {
              _setBeacon(newBeacon);
              emit BeaconUpgraded(newBeacon);
              if (data.length > 0 || forceCall) {
                  _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
              }
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
              require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
          }
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[50] private __gap;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)
      pragma solidity ^0.8.2;
      import "../../utils/AddressUpgradeable.sol";
      /**
       * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
       * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
       * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
       * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
       *
       * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
       * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
       * case an upgrade adds a module that needs to be initialized.
       *
       * For example:
       *
       * [.hljs-theme-light.nopadding]
       * ```
       * contract MyToken is ERC20Upgradeable {
       *     function initialize() initializer public {
       *         __ERC20_init("MyToken", "MTK");
       *     }
       * }
       * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
       *     function initializeV2() reinitializer(2) public {
       *         __ERC20Permit_init("MyToken");
       *     }
       * }
       * ```
       *
       * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
       * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
       *
       * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
       * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
       *
       * [CAUTION]
       * ====
       * Avoid leaving a contract uninitialized.
       *
       * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
       * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
       * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
       *
       * [.hljs-theme-light.nopadding]
       * ```
       * /// @custom:oz-upgrades-unsafe-allow constructor
       * constructor() {
       *     _disableInitializers();
       * }
       * ```
       * ====
       */
      abstract contract Initializable {
          /**
           * @dev Indicates that the contract has been initialized.
           * @custom:oz-retyped-from bool
           */
          uint8 private _initialized;
          /**
           * @dev Indicates that the contract is in the process of being initialized.
           */
          bool private _initializing;
          /**
           * @dev Triggered when the contract has been initialized or reinitialized.
           */
          event Initialized(uint8 version);
          /**
           * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
           * `onlyInitializing` functions can be used to initialize parent contracts.
           *
           * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
           * constructor.
           *
           * Emits an {Initialized} event.
           */
          modifier initializer() {
              bool isTopLevelCall = !_initializing;
              require(
                  (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                  "Initializable: contract is already initialized"
              );
              _initialized = 1;
              if (isTopLevelCall) {
                  _initializing = true;
              }
              _;
              if (isTopLevelCall) {
                  _initializing = false;
                  emit Initialized(1);
              }
          }
          /**
           * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
           * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
           * used to initialize parent contracts.
           *
           * A reinitializer may be used after the original initialization step. This is essential to configure modules that
           * are added through upgrades and that require initialization.
           *
           * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
           * cannot be nested. If one is invoked in the context of another, execution will revert.
           *
           * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
           * a contract, executing them in the right order is up to the developer or operator.
           *
           * WARNING: setting the version to 255 will prevent any future reinitialization.
           *
           * Emits an {Initialized} event.
           */
          modifier reinitializer(uint8 version) {
              require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
              _initialized = version;
              _initializing = true;
              _;
              _initializing = false;
              emit Initialized(version);
          }
          /**
           * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
           * {initializer} and {reinitializer} modifiers, directly or indirectly.
           */
          modifier onlyInitializing() {
              require(_initializing, "Initializable: contract is not initializing");
              _;
          }
          /**
           * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
           * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
           * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
           * through proxies.
           *
           * Emits an {Initialized} event the first time it is successfully executed.
           */
          function _disableInitializers() internal virtual {
              require(!_initializing, "Initializable: contract is initializing");
              if (_initialized < type(uint8).max) {
                  _initialized = type(uint8).max;
                  emit Initialized(type(uint8).max);
              }
          }
          /**
           * @dev Returns the highest version that has been initialized. See {reinitializer}.
           */
          function _getInitializedVersion() internal view returns (uint8) {
              return _initialized;
          }
          /**
           * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
           */
          function _isInitializing() internal view returns (bool) {
              return _initializing;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev This is the interface that {BeaconProxy} expects of its beacon.
       */
      interface IBeaconUpgradeable {
          /**
           * @dev Must return an address that can be used as a delegate call target.
           *
           * {BeaconProxy} will check that this address is a contract.
           */
          function implementation() external view returns (address);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.8.3) (interfaces/IERC1967.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
       *
       * _Available since v4.9._
       */
      interface IERC1967Upgradeable {
          /**
           * @dev Emitted when the implementation is upgraded.
           */
          event Upgraded(address indexed implementation);
          /**
           * @dev Emitted when the admin account has changed.
           */
          event AdminChanged(address previousAdmin, address newAdmin);
          /**
           * @dev Emitted when the beacon is changed.
           */
          event BeaconUpgraded(address indexed beacon);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @dev Collection of functions related to the address type
       */
      library AddressUpgradeable {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
           * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
           *
           * _Available since v4.8._
           */
          function verifyCallResultFromTarget(
              address target,
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              if (success) {
                  if (returndata.length == 0) {
                      // only check isContract if the call was successful and the return data is empty
                      // otherwise we already know that it was a contract
                      require(isContract(target), "Address: call to non-contract");
                  }
                  return returndata;
              } else {
                  _revert(returndata, errorMessage);
              }
          }
          /**
           * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason or using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  _revert(returndata, errorMessage);
              }
          }
          function _revert(bytes memory returndata, string memory errorMessage) private pure {
              // Look for revert reason and bubble it up if present
              if (returndata.length > 0) {
                  // The easiest way to bubble the revert reason is using memory via assembly
                  /// @solidity memory-safe-assembly
                  assembly {
                      let returndata_size := mload(returndata)
                      revert(add(32, returndata), returndata_size)
                  }
              } else {
                  revert(errorMessage);
              }
          }
      }
      // 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 StorageSlotUpgradeable {
          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.0;
      /// @notice Signed 18 decimal fixed point (wad) arithmetic library.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SignedWadMath.sol)
      /// @author Modified from Remco Bloemen (https://xn--2-umb.com/22/exp-ln/index.html)
      /// @dev Will not revert on overflow, only use where overflow is not possible.
      function toWadUnsafe(uint256 x) pure returns (int256 r) {
          /// @solidity memory-safe-assembly
          assembly {
              // Multiply x by 1e18.
              r := mul(x, 1000000000000000000)
          }
      }
      /// @dev Takes an integer amount of seconds and converts it to a wad amount of days.
      /// @dev Will not revert on overflow, only use where overflow is not possible.
      /// @dev Not meant for negative second amounts, it assumes x is positive.
      function toDaysWadUnsafe(uint256 x) pure returns (int256 r) {
          /// @solidity memory-safe-assembly
          assembly {
              // Multiply x by 1e18 and then divide it by 86400.
              r := div(mul(x, 1000000000000000000), 86400)
          }
      }
      /// @dev Takes a wad amount of days and converts it to an integer amount of seconds.
      /// @dev Will not revert on overflow, only use where overflow is not possible.
      /// @dev Not meant for negative day amounts, it assumes x is positive.
      function fromDaysWadUnsafe(int256 x) pure returns (uint256 r) {
          /// @solidity memory-safe-assembly
          assembly {
              // Multiply x by 86400 and then divide it by 1e18.
              r := div(mul(x, 86400), 1000000000000000000)
          }
      }
      /// @dev Will not revert on overflow, only use where overflow is not possible.
      function unsafeWadMul(int256 x, int256 y) pure returns (int256 r) {
          /// @solidity memory-safe-assembly
          assembly {
              // Multiply x by y and divide by 1e18.
              r := sdiv(mul(x, y), 1000000000000000000)
          }
      }
      /// @dev Will return 0 instead of reverting if y is zero and will
      /// not revert on overflow, only use where overflow is not possible.
      function unsafeWadDiv(int256 x, int256 y) pure returns (int256 r) {
          /// @solidity memory-safe-assembly
          assembly {
              // Multiply x by 1e18 and divide it by y.
              r := sdiv(mul(x, 1000000000000000000), y)
          }
      }
      function wadMul(int256 x, int256 y) pure returns (int256 r) {
          /// @solidity memory-safe-assembly
          assembly {
              // Store x * y in r for now.
              r := mul(x, y)
              // Equivalent to require(x == 0 || (x * y) / x == y)
              if iszero(or(iszero(x), eq(sdiv(r, x), y))) {
                  revert(0, 0)
              }
              // Scale the result down by 1e18.
              r := sdiv(r, 1000000000000000000)
          }
      }
      function wadDiv(int256 x, int256 y) pure returns (int256 r) {
          /// @solidity memory-safe-assembly
          assembly {
              // Store x * 1e18 in r for now.
              r := mul(x, 1000000000000000000)
              // Equivalent to require(y != 0 && ((x * 1e18) / 1e18 == x))
              if iszero(and(iszero(iszero(y)), eq(sdiv(r, 1000000000000000000), x))) {
                  revert(0, 0)
              }
              // Divide r by y.
              r := sdiv(r, y)
          }
      }
      /// @dev Will not work with negative bases, only use when x is positive.
      function wadPow(int256 x, int256 y) pure returns (int256) {
          // Equivalent to x to the power of y because x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)
          return wadExp((wadLn(x) * y) / 1e18); // Using ln(x) means x must be greater than 0.
      }
      function wadExp(int256 x) pure returns (int256 r) {
          unchecked {
              // When the result is < 0.5 we return zero. This happens when
              // x <= floor(log(0.5e18) * 1e18) ~ -42e18
              if (x <= -42139678854452767551) return 0;
              // When the result is > (2**255 - 1) / 1e18 we can not represent it as an
              // int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135.
              if (x >= 135305999368893231589) revert("EXP_OVERFLOW");
              // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96
              // for more intermediate precision and a binary basis. This base conversion
              // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
              x = (x << 78) / 5**18;
              // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
              // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
              // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
              int256 k = ((x << 96) / 54916777467707473351141471128 + 2**95) >> 96;
              x = x - k * 54916777467707473351141471128;
              // k is in the range [-61, 195].
              // Evaluate using a (6, 7)-term rational approximation.
              // p is made monic, we'll multiply by a scale factor later.
              int256 y = x + 1346386616545796478920950773328;
              y = ((y * x) >> 96) + 57155421227552351082224309758442;
              int256 p = y + x - 94201549194550492254356042504812;
              p = ((p * y) >> 96) + 28719021644029726153956944680412240;
              p = p * x + (4385272521454847904659076985693276 << 96);
              // We leave p in 2**192 basis so we don't need to scale it back up for the division.
              int256 q = x - 2855989394907223263936484059900;
              q = ((q * x) >> 96) + 50020603652535783019961831881945;
              q = ((q * x) >> 96) - 533845033583426703283633433725380;
              q = ((q * x) >> 96) + 3604857256930695427073651918091429;
              q = ((q * x) >> 96) - 14423608567350463180887372962807573;
              q = ((q * x) >> 96) + 26449188498355588339934803723976023;
              /// @solidity memory-safe-assembly
              assembly {
                  // Div in assembly because solidity adds a zero check despite the unchecked.
                  // The q polynomial won't have zeros in the domain as all its roots are complex.
                  // No scaling is necessary because p is already 2**96 too large.
                  r := sdiv(p, q)
              }
              // r should be in the range (0.09, 0.25) * 2**96.
              // We now need to multiply r by:
              // * the scale factor s = ~6.031367120.
              // * the 2**k factor from the range reduction.
              // * the 1e18 / 2**96 factor for base conversion.
              // We do this all at once, with an intermediate result in 2**213
              // basis, so the final right shift is always by a positive amount.
              r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k));
          }
      }
      function wadLn(int256 x) pure returns (int256 r) {
          unchecked {
              require(x > 0, "UNDEFINED");
              // We want to convert x from 10**18 fixed point to 2**96 fixed point.
              // We do this by multiplying by 2**96 / 10**18. But since
              // ln(x * C) = ln(x) + ln(C), we can simply do nothing here
              // and add ln(2**96 / 10**18) at the end.
              /// @solidity memory-safe-assembly
              assembly {
                  r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
                  r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
                  r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
                  r := or(r, shl(4, lt(0xffff, shr(r, x))))
                  r := or(r, shl(3, lt(0xff, shr(r, x))))
                  r := or(r, shl(2, lt(0xf, shr(r, x))))
                  r := or(r, shl(1, lt(0x3, shr(r, x))))
                  r := or(r, lt(0x1, shr(r, x)))
              }
              // Reduce range of x to (1, 2) * 2**96
              // ln(2^k * x) = k * ln(2) + ln(x)
              int256 k = r - 96;
              x <<= uint256(159 - k);
              x = int256(uint256(x) >> 159);
              // Evaluate using a (8, 8)-term rational approximation.
              // p is made monic, we will multiply by a scale factor later.
              int256 p = x + 3273285459638523848632254066296;
              p = ((p * x) >> 96) + 24828157081833163892658089445524;
              p = ((p * x) >> 96) + 43456485725739037958740375743393;
              p = ((p * x) >> 96) - 11111509109440967052023855526967;
              p = ((p * x) >> 96) - 45023709667254063763336534515857;
              p = ((p * x) >> 96) - 14706773417378608786704636184526;
              p = p * x - (795164235651350426258249787498 << 96);
              // We leave p in 2**192 basis so we don't need to scale it back up for the division.
              // q is monic by convention.
              int256 q = x + 5573035233440673466300451813936;
              q = ((q * x) >> 96) + 71694874799317883764090561454958;
              q = ((q * x) >> 96) + 283447036172924575727196451306956;
              q = ((q * x) >> 96) + 401686690394027663651624208769553;
              q = ((q * x) >> 96) + 204048457590392012362485061816622;
              q = ((q * x) >> 96) + 31853899698501571402653359427138;
              q = ((q * x) >> 96) + 909429971244387300277376558375;
              /// @solidity memory-safe-assembly
              assembly {
                  // Div in assembly because solidity adds a zero check despite the unchecked.
                  // The q polynomial is known not to have zeros in the domain.
                  // No scaling required because p is already 2**96 too large.
                  r := sdiv(p, q)
              }
              // r is in the range (0, 0.125) * 2**96
              // Finalization, we need to:
              // * multiply by the scale factor s = 5.549…
              // * add ln(2**96 / 10**18)
              // * add k * ln(2)
              // * multiply by 10**18 / 2**96 = 5**18 >> 78
              // mul s * 5e18 * 2**96, base is now 5**18 * 2**192
              r *= 1677202110996718588342820967067443963516166;
              // add ln(2) * k * 5e18 * 2**192
              r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k;
              // add ln(2**96 / 10**18) * 5e18 * 2**192
              r += 600920179829731861736702779321621459595472258049074101567377883020018308;
              // base conversion: mul 2**18 / 2**192
              r >>= 174;
          }
      }
      /// @dev Will return 0 instead of reverting if y is zero.
      function unsafeDiv(int256 x, int256 y) pure returns (int256 r) {
          /// @solidity memory-safe-assembly
          assembly {
              // Divide x by y.
              r := sdiv(x, y)
          }
      }
      // SPDX-License-Identifier: BSL 1.1 - Blend (c) Non Fungible Trading Ltd.
      pragma solidity 0.8.17;
      // Blend
      error Unauthorized();
      error InvalidLoan();
      error InvalidLien();
      error InsufficientOffer();
      error InvalidRepayment();
      error LienIsDefaulted();
      error LienNotDefaulted();
      error AuctionIsActive();
      error AuctionIsNotActive();
      error InvalidRefinance();
      error RateTooHigh();
      error FeesTooHigh();
      error CollectionsDoNotMatch();
      error InvalidAuctionDuration();
      // OfferController
      error OfferExpired();
      error OfferUnavailable();
      // Signatures
      error UnauthorizedOracle();
      error SignatureExpired();
      error InvalidSignature();
      error InvalidVParameter();
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.17;
      import {
          TakeAsk,
          TakeBid,
          TakeAskSingle,
          TakeBidSingle,
          Order,
          Exchange,
          Fees,
          FeeRate,
          AssetType,
          OrderType,
          Transfer,
          FungibleTransfers,
          StateUpdate,
          Cancel,
          Listing
      } from "../lib/Structs.sol";
      interface IBlurExchangeV2 {
          error InsufficientFunds();
          error TokenTransferFailed();
          error InvalidOrder();
          error ProtocolFeeTooHigh();
          event NewProtocolFee(address indexed recipient, uint16 indexed rate);
          event NewGovernor(address indexed governor);
          event NewBlockRange(uint256 blockRange);
          event CancelTrade(address indexed user, bytes32 hash, uint256 index, uint256 amount);
          event NonceIncremented(address indexed user, uint256 newNonce);
          event SetOracle(address indexed user, bool approved);
          function initialize() external;
          function setProtocolFee(address recipient, uint16 rate) external;
          function setGovernor(address _governor) external;
          function setOracle(address oracle, bool approved) external;
          function setBlockRange(uint256 _blockRange) external;
          function cancelTrades(Cancel[] memory cancels) external;
          function incrementNonce() external;
          /*//////////////////////////////////////////////////////////////
                                EXECUTION WRAPPERS
          //////////////////////////////////////////////////////////////*/
          function takeAsk(TakeAsk memory inputs, bytes calldata oracleSignature) external payable;
          function takeBid(TakeBid memory inputs, bytes calldata oracleSignature) external;
          function takeAskSingle(TakeAskSingle memory inputs, bytes calldata oracleSignature) external payable;
          function takeBidSingle(TakeBidSingle memory inputs, bytes calldata oracleSignature) external;
          /*//////////////////////////////////////////////////////////////
                              EXECUTION POOL WRAPPERS
          //////////////////////////////////////////////////////////////*/
          function takeAskSinglePool(
              TakeAskSingle memory inputs,
              bytes calldata oracleSignature,
              uint256 amountToWithdraw
          ) external payable;
          function takeAskPool(
              TakeAsk memory inputs,
              bytes calldata oracleSignature,
              uint256 amountToWithdraw
          ) external payable;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity 0.8.17;
      enum Side { Buy, Sell }
      enum SignatureVersion { Single, Bulk }
      enum AssetType { ERC721, ERC1155 }
      struct Fee {
          uint16 rate;
          address payable recipient;
      }
      struct Order {
          address trader;
          Side side;
          address matchingPolicy;
          address collection;
          uint256 tokenId;
          uint256 amount;
          address paymentToken;
          uint256 price;
          uint256 listingTime;
          /* Order expiration timestamp - 0 for oracle cancellations. */
          uint256 expirationTime;
          Fee[] fees;
          uint256 salt;
          bytes extraParams;
      }
      struct Input {
          Order order;
          uint8 v;
          bytes32 r;
          bytes32 s;
          bytes extraSignature;
          SignatureVersion signatureVersion;
          uint256 blockNumber;
      }
      struct Execution {
        Input sell;
        Input buy;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.17;
      struct TakeAsk {
          Order[] orders;
          Exchange[] exchanges;
          FeeRate takerFee;
          bytes signatures;
          address tokenRecipient;
      }
      struct TakeAskSingle {
          Order order;
          Exchange exchange;
          FeeRate takerFee;
          bytes signature;
          address tokenRecipient;
      }
      struct TakeBid {
          Order[] orders;
          Exchange[] exchanges;
          FeeRate takerFee;
          bytes signatures;
      }
      struct TakeBidSingle {
          Order order;
          Exchange exchange;
          FeeRate takerFee;
          bytes signature;
      }
      enum AssetType {
          ERC721,
          ERC1155
      }
      enum OrderType {
          ASK,
          BID
      }
      struct Exchange { // Size: 0x80
          uint256 index; // 0x00
          bytes32[] proof; // 0x20
          Listing listing; // 0x40
          Taker taker; // 0x60
      }
      struct Listing { // Size: 0x80
          uint256 index; // 0x00
          uint256 tokenId; // 0x20
          uint256 amount; // 0x40
          uint256 price; // 0x60
      }
      struct Taker { // Size: 0x40
          uint256 tokenId; // 0x00
          uint256 amount; // 0x20
      }
      struct Order { // Size: 0x100
          address trader; // 0x00
          address collection; // 0x20
          bytes32 listingsRoot; // 0x40
          uint256 numberOfListings; // 0x60
          uint256 expirationTime; // 0x80
          AssetType assetType; // 0xa0
          FeeRate makerFee; // 0xc0
          uint256 salt; // 0xe0
      }
      /*
      Reference only; struct is composed manually using calldata formatting in execution
      struct ExecutionBatch { // Size: 0x80
          address taker; // 0x00
          OrderType orderType; // 0x20
          Transfer[] transfers; // 0x40
          uint256 length; // 0x60
      }
      */
      struct Transfer { // Size: 0xa0
          address trader; // 0x00
          uint256 id; // 0x20
          uint256 amount; // 0x40
          address collection; // 0x60
          AssetType assetType; // 0x80
      }
      struct FungibleTransfers {
          uint256 totalProtocolFee;
          uint256 totalSellerTransfer;
          uint256 totalTakerFee;
          uint256 feeRecipientId;
          uint256 makerId;
          address[] feeRecipients;
          address[] makers;
          uint256[] makerTransfers;
          uint256[] feeTransfers;
          AtomicExecution[] executions;
      }
      struct AtomicExecution { // Size: 0xe0
          uint256 makerId; // 0x00
          uint256 sellerAmount; // 0x20
          uint256 makerFeeRecipientId; // 0x40
          uint256 makerFeeAmount; // 0x60
          uint256 takerFeeAmount; // 0x80
          uint256 protocolFeeAmount; // 0xa0
          StateUpdate stateUpdate; // 0xc0
      }
      struct StateUpdate { // Size: 0xa0
          address trader; // 0x00
          bytes32 hash; // 0x20
          uint256 index; // 0x40
          uint256 value; // 0x60
          uint256 maxAmount; // 0x80
      }
      struct Fees { // Size: 0x40
          FeeRate protocolFee; // 0x00
          FeeRate takerFee; // 0x20
      }
      struct FeeRate { // Size: 0x40
          address recipient; // 0x00
          uint16 rate; // 0x20
      }
      struct Cancel {
          bytes32 hash;
          uint256 index;
          uint256 amount;
      }
      // SPDX-License-Identifier: AGPL-3.0-only
      pragma solidity >=0.8.0;
      /// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
      abstract contract ERC721 {
          /*//////////////////////////////////////////////////////////////
                                       EVENTS
          //////////////////////////////////////////////////////////////*/
          event Transfer(address indexed from, address indexed to, uint256 indexed id);
          event Approval(address indexed owner, address indexed spender, uint256 indexed id);
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          /*//////////////////////////////////////////////////////////////
                               METADATA STORAGE/LOGIC
          //////////////////////////////////////////////////////////////*/
          string public name;
          string public symbol;
          function tokenURI(uint256 id) public view virtual returns (string memory);
          /*//////////////////////////////////////////////////////////////
                            ERC721 BALANCE/OWNER STORAGE
          //////////////////////////////////////////////////////////////*/
          mapping(uint256 => address) internal _ownerOf;
          mapping(address => uint256) internal _balanceOf;
          function ownerOf(uint256 id) public view virtual returns (address owner) {
              require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
          }
          function balanceOf(address owner) public view virtual returns (uint256) {
              require(owner != address(0), "ZERO_ADDRESS");
              return _balanceOf[owner];
          }
          /*//////////////////////////////////////////////////////////////
                               ERC721 APPROVAL STORAGE
          //////////////////////////////////////////////////////////////*/
          mapping(uint256 => address) public getApproved;
          mapping(address => mapping(address => bool)) public isApprovedForAll;
          /*//////////////////////////////////////////////////////////////
                                     CONSTRUCTOR
          //////////////////////////////////////////////////////////////*/
          constructor(string memory _name, string memory _symbol) {
              name = _name;
              symbol = _symbol;
          }
          /*//////////////////////////////////////////////////////////////
                                    ERC721 LOGIC
          //////////////////////////////////////////////////////////////*/
          function approve(address spender, uint256 id) public virtual {
              address owner = _ownerOf[id];
              require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
              getApproved[id] = spender;
              emit Approval(owner, spender, id);
          }
          function setApprovalForAll(address operator, bool approved) public virtual {
              isApprovedForAll[msg.sender][operator] = approved;
              emit ApprovalForAll(msg.sender, operator, approved);
          }
          function transferFrom(
              address from,
              address to,
              uint256 id
          ) public virtual {
              require(from == _ownerOf[id], "WRONG_FROM");
              require(to != address(0), "INVALID_RECIPIENT");
              require(
                  msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
                  "NOT_AUTHORIZED"
              );
              // Underflow of the sender's balance is impossible because we check for
              // ownership above and the recipient's balance can't realistically overflow.
              unchecked {
                  _balanceOf[from]--;
                  _balanceOf[to]++;
              }
              _ownerOf[id] = to;
              delete getApproved[id];
              emit Transfer(from, to, id);
          }
          function safeTransferFrom(
              address from,
              address to,
              uint256 id
          ) public virtual {
              transferFrom(from, to, id);
              require(
                  to.code.length == 0 ||
                      ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                      ERC721TokenReceiver.onERC721Received.selector,
                  "UNSAFE_RECIPIENT"
              );
          }
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              bytes calldata data
          ) public virtual {
              transferFrom(from, to, id);
              require(
                  to.code.length == 0 ||
                      ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                      ERC721TokenReceiver.onERC721Received.selector,
                  "UNSAFE_RECIPIENT"
              );
          }
          /*//////////////////////////////////////////////////////////////
                                    ERC165 LOGIC
          //////////////////////////////////////////////////////////////*/
          function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
              return
                  interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                  interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
                  interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
          }
          /*//////////////////////////////////////////////////////////////
                              INTERNAL MINT/BURN LOGIC
          //////////////////////////////////////////////////////////////*/
          function _mint(address to, uint256 id) internal virtual {
              require(to != address(0), "INVALID_RECIPIENT");
              require(_ownerOf[id] == address(0), "ALREADY_MINTED");
              // Counter overflow is incredibly unrealistic.
              unchecked {
                  _balanceOf[to]++;
              }
              _ownerOf[id] = to;
              emit Transfer(address(0), to, id);
          }
          function _burn(uint256 id) internal virtual {
              address owner = _ownerOf[id];
              require(owner != address(0), "NOT_MINTED");
              // Ownership check above ensures no underflow.
              unchecked {
                  _balanceOf[owner]--;
              }
              delete _ownerOf[id];
              delete getApproved[id];
              emit Transfer(owner, address(0), id);
          }
          /*//////////////////////////////////////////////////////////////
                              INTERNAL SAFE MINT LOGIC
          //////////////////////////////////////////////////////////////*/
          function _safeMint(address to, uint256 id) internal virtual {
              _mint(to, id);
              require(
                  to.code.length == 0 ||
                      ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                      ERC721TokenReceiver.onERC721Received.selector,
                  "UNSAFE_RECIPIENT"
              );
          }
          function _safeMint(
              address to,
              uint256 id,
              bytes memory data
          ) internal virtual {
              _mint(to, id);
              require(
                  to.code.length == 0 ||
                      ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                      ERC721TokenReceiver.onERC721Received.selector,
                  "UNSAFE_RECIPIENT"
              );
          }
      }
      /// @notice A generic interface for a contract which properly accepts ERC721 tokens.
      /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
      abstract contract ERC721TokenReceiver {
          function onERC721Received(
              address,
              address,
              uint256,
              bytes calldata
          ) external virtual returns (bytes4) {
              return ERC721TokenReceiver.onERC721Received.selector;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)
      pragma solidity ^0.8.0;
      import "./OwnableUpgradeable.sol";
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Contract module which provides access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership} and {acceptOwnership}.
       *
       * This module is used through inheritance. It will make available all functions
       * from parent (Ownable).
       */
      abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
          function __Ownable2Step_init() internal onlyInitializing {
              __Ownable_init_unchained();
          }
          function __Ownable2Step_init_unchained() internal onlyInitializing {
          }
          address private _pendingOwner;
          event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Returns the address of the pending owner.
           */
          function pendingOwner() public view virtual returns (address) {
              return _pendingOwner;
          }
          /**
           * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual override onlyOwner {
              _pendingOwner = newOwner;
              emit OwnershipTransferStarted(owner(), newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual override {
              delete _pendingOwner;
              super._transferOwnership(newOwner);
          }
          /**
           * @dev The new owner accepts the ownership transfer.
           */
          function acceptOwnership() external {
              address sender = _msgSender();
              require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
              _transferOwnership(sender);
          }
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[49] private __gap;
      }
      // SPDX-License-Identifier: BSL 1.1 - Blend (c) Non Fungible Trading Ltd.
      pragma solidity 0.8.17;
      import "./Structs.sol";
      import "./Errors.sol";
      import "../interfaces/ISignatures.sol";
      abstract contract Signatures is ISignatures {
          bytes32 private immutable _LOAN_OFFER_TYPEHASH;
          bytes32 private immutable _FEE_TYPEHASH;
          bytes32 private immutable _SELL_OFFER_TYPEHASH;
          bytes32 private immutable _ORACLE_OFFER_TYPEHASH;
          bytes32 private immutable _EIP_712_DOMAIN_TYPEHASH;
          string private constant _NAME = "Blend";
          string private constant _VERSION = "1.0";
          mapping(address => uint256) public nonces;
          mapping(address => uint256) public oracles;
          uint256 public blockRange;
          uint256[50] private _gap;
          constructor() {
              (
                  _LOAN_OFFER_TYPEHASH,
                  _SELL_OFFER_TYPEHASH,
                  _FEE_TYPEHASH,
                  _ORACLE_OFFER_TYPEHASH,
                  _EIP_712_DOMAIN_TYPEHASH
              ) = _createTypehashes();
          }
          function information() external view returns (string memory version, bytes32 domainSeparator) {
              version = _VERSION;
              domainSeparator = _hashDomain(
                  _EIP_712_DOMAIN_TYPEHASH,
                  keccak256(bytes(_NAME)),
                  keccak256(bytes(_VERSION))
              );
          }
          function getSellOfferHash(SellOffer calldata offer) external view returns (bytes32) {
              return _hashSellOffer(offer);
          }
          function getOfferHash(LoanOffer calldata offer) external view returns (bytes32) {
              return _hashOffer(offer);
          }
          function getOracleOfferHash(bytes32 hash, uint256 blockNumber) external view returns (bytes32) {
              return _hashOracleOffer(hash, blockNumber);
          }
          /**
           * @notice Generate all EIP712 Typehashes
           */
          function _createTypehashes()
              internal
              view
              returns (
                  bytes32 loanOfferTypehash,
                  bytes32 sellOfferTypehash,
                  bytes32 feeTypehash,
                  bytes32 oracleOfferTypehash,
                  bytes32 eip712DomainTypehash
              )
          {
              eip712DomainTypehash = keccak256(
                  bytes.concat(
                      "EIP712Domain(",
                      "string name,",
                      "string version,",
                      "uint256 chainId,",
                      "address verifyingContract",
                      ")"
                  )
              );
              oracleOfferTypehash = keccak256(
                  bytes.concat("OracleOffer(", "bytes32 hash,", "uint256 blockNumber", ")")
              );
              loanOfferTypehash = keccak256(
                  bytes.concat(
                      "LoanOffer(",
                      "address lender,",
                      "address collection,",
                      "uint256 totalAmount,",
                      "uint256 minAmount,",
                      "uint256 maxAmount,",
                      "uint256 auctionDuration,",
                      "uint256 salt,",
                      "uint256 expirationTime,",
                      "uint256 rate,",
                      "address oracle,",
                      "uint256 nonce",
                      ")"
                  )
              );
              bytes memory feeTypestring = bytes.concat("Fee(", "uint16 rate,", "address recipient", ")");
              feeTypehash = keccak256(feeTypestring);
              sellOfferTypehash = keccak256(
                  bytes.concat(
                      "SellOffer(",
                      "address borrower,",
                      "uint256 lienId,",
                      "uint256 price,",
                      "uint256 expirationTime,",
                      "uint256 salt,",
                      "address oracle,",
                      "Fee[] fees,",
                      "uint256 nonce",
                      ")",
                      feeTypestring
                  )
              );
          }
          function _hashDomain(
              bytes32 eip712DomainTypehash,
              bytes32 nameHash,
              bytes32 versionHash
          ) internal view returns (bytes32) {
              return
                  keccak256(
                      abi.encode(
                          eip712DomainTypehash,
                          nameHash,
                          versionHash,
                          block.chainid,
                          address(this)
                      )
                  );
          }
          function _hashFee(Fee calldata fee) internal view returns (bytes32) {
              return keccak256(abi.encode(_FEE_TYPEHASH, fee.rate, fee.recipient));
          }
          function _packFees(Fee[] calldata fees) internal view returns (bytes32) {
              bytes32[] memory feeHashes = new bytes32[](fees.length);
              uint256 feesLength = fees.length;
              for (uint256 i; i < feesLength; ) {
                  feeHashes[i] = _hashFee(fees[i]);
                  unchecked {
                      ++i;
                  }
              }
              return keccak256(abi.encodePacked(feeHashes));
          }
          function _hashSellOffer(SellOffer calldata offer) internal view returns (bytes32) {
              return
                  keccak256(
                      abi.encode(
                          _SELL_OFFER_TYPEHASH,
                          offer.borrower,
                          offer.lienId,
                          offer.price,
                          offer.expirationTime,
                          offer.salt,
                          offer.oracle,
                          _packFees(offer.fees),
                          nonces[offer.borrower]
                      )
                  );
          }
          function _hashOffer(LoanOffer calldata offer) internal view returns (bytes32) {
              return
                  keccak256(
                      abi.encode(
                          _LOAN_OFFER_TYPEHASH,
                          offer.lender,
                          offer.collection,
                          offer.totalAmount,
                          offer.minAmount,
                          offer.maxAmount,
                          offer.auctionDuration,
                          offer.salt,
                          offer.expirationTime,
                          offer.rate,
                          offer.oracle,
                          nonces[offer.lender]
                      )
                  );
          }
          function _hashOracleOffer(bytes32 hash, uint256 blockNumber) internal view returns (bytes32) {
              return keccak256(abi.encode(_ORACLE_OFFER_TYPEHASH, hash, blockNumber));
          }
          function _hashToSign(bytes32 hash) internal view returns (bytes32) {
              return keccak256(
                  bytes.concat(
                      bytes2(0x1901),
                      _hashDomain(
                          _EIP_712_DOMAIN_TYPEHASH,
                          keccak256(bytes(_NAME)),
                          keccak256(bytes(_VERSION))
                      ),
                      hash
                  )
              );
          }
          function _hashToSignOracle(bytes32 hash, uint256 blockNumber) internal view returns (bytes32) {
              return
                  keccak256(
                      bytes.concat(
                          bytes2(0x1901),
                          _hashDomain(
                              _EIP_712_DOMAIN_TYPEHASH,
                              keccak256(bytes(_NAME)),
                              keccak256(bytes(_VERSION))
                          ),
                          _hashOracleOffer(hash, blockNumber)
                      )
                  );
          }
          /**
           * @notice Verify authorization of offer
           * @param offerHash Hash of offer struct
           * @param lender Lender address
           * @param oracle Oracle address
           * @param signature Packed offer signature (with oracle signature if necessary)
           */
          function _verifyOfferAuthorization(
              bytes32 offerHash,
              address lender,
              address oracle,
              bytes calldata signature
          ) internal view {
              bytes32 hashToSign = _hashToSign(offerHash);
              bytes32 r;
              bytes32 s;
              uint8 v;
              assembly {
                  r := calldataload(signature.offset)
                  s := calldataload(add(signature.offset, 0x20))
                  v := shr(248, calldataload(add(signature.offset, 0x40)))
              }
              _verify(lender, hashToSign, v, r, s);
              /* Verify oracle signature if required. */
              if (oracle != address(0)) {
                  uint256 blockNumber;
                  assembly {
                      r := calldataload(add(signature.offset, 0x41))
                      s := calldataload(add(signature.offset, 0x61))
                      v := shr(248, calldataload(add(signature.offset, 0x81)))
                      blockNumber := calldataload(add(signature.offset, 0x82))
                  }
                  if (oracles[oracle] == 0) {
                      revert UnauthorizedOracle();
                  }
                  if (blockNumber + blockRange < block.number) {
                      revert SignatureExpired();
                  }
                  hashToSign = _hashToSignOracle(offerHash, blockNumber);
                  _verify(oracle, hashToSign, v, r, s);
              }
          }
          /**
           * @notice Verify signature of digest
           * @param signer Address of expected signer
           * @param digest Signature digest
           * @param v v parameter
           * @param r r parameter
           * @param s s parameter
           */
          function _verify(address signer, bytes32 digest, uint8 v, bytes32 r, bytes32 s) internal pure {
              if (v != 27 && v != 28) {
                  revert InvalidVParameter();
              }
              address recoveredSigner = ecrecover(digest, v, r, s);
              if (recoveredSigner == address(0) || signer != recoveredSigner) {
                  revert InvalidSignature();
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "./ISignatures.sol";
      interface IOfferController is ISignatures {
          event OfferCancelled(address indexed user, uint256 salt);
          event NonceIncremented(address indexed user, uint256 newNonce);
          function cancelOffer(uint256 salt) external;
          function cancelOffers(uint256[] calldata salts) external;
          function incrementNonce() external;
          /* Admin */
          function setOracle(address oracle, bool approved) external;
          function setBlockRange(uint256 blockRange) external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import "../utils/ContextUpgradeable.sol";
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          function __Ownable_init() internal onlyInitializing {
              __Ownable_init_unchained();
          }
          function __Ownable_init_unchained() internal onlyInitializing {
              _transferOwnership(_msgSender());
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              _checkOwner();
              _;
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if the sender is not the owner.
           */
          function _checkOwner() internal view virtual {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _transferOwnership(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _transferOwnership(newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[49] private __gap;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
      pragma solidity ^0.8.0;
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract ContextUpgradeable is Initializable {
          function __Context_init() internal onlyInitializing {
          }
          function __Context_init_unchained() internal onlyInitializing {
          }
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[50] private __gap;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../lib/Structs.sol";
      interface ISignatures {
          function information()
              external
              view
              returns (
                  string memory version,
                  bytes32 domainSeparator
              );
          function getOracleOfferHash(bytes32 hash, uint256 blockNumber) external view returns (bytes32);
          function getSellOfferHash(SellOffer calldata offer) external view returns (bytes32);
          function getOfferHash(LoanOffer calldata offer) external view returns (bytes32);
          function cancelledOrFulfilled(address user, uint256 salt) external view returns (uint256);
          function nonces(address user) external view returns (uint256);
      }