Marketplace

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "StashMarketPlace/stashmarketplace.sol": {
      "content": "// File: @openzeppelin/contracts/utils/Context.sol\r
\r
\r
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\r
\r
pragma solidity ^0.8.20;\r
\r
\r
\r
/**\r
 * @dev Provides information about the current execution context, including the\r
 * sender of the transaction and its data. While these are generally available\r
 * via msg.sender and msg.data, they should not be accessed in such a direct\r
 * manner, since when dealing with meta-transactions the account sending and\r
 * paying for execution may not be the actual sender (as far as an application\r
 * is concerned).\r
 *\r
 * This contract is only required for intermediate, library-like contracts.\r
 */\r
abstract contract Context {\r
    function _msgSender() internal view virtual returns (address) {\r
        return msg.sender;\r
    }\r
\r
    function _msgData() internal view virtual returns (bytes calldata) {\r
        return msg.data;\r
    }\r
\r
    function _contextSuffixLength() internal view virtual returns (uint256) {\r
        return 0;\r
    }\r
}\r
\r
// File: @openzeppelin/contracts/access/Ownable.sol\r
\r
\r
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\r
\r
pragma solidity ^0.8.20;\r
\r
\r
/**\r
 * @dev Contract module which provides a basic access control mechanism, where\r
 * there is an account (an owner) that can be granted exclusive access to\r
 * specific functions.\r
 *\r
 * The initial owner is set to the address provided by the deployer. This can\r
 * later be changed with {transferOwnership}.\r
 *\r
 * This module is used through inheritance. It will make available the modifier\r
 * `onlyOwner`, which can be applied to your functions to restrict their use to\r
 * the owner.\r
 */\r
abstract contract Ownable is Context {\r
    address private _owner;\r
\r
    /**\r
     * @dev The caller account is not authorized to perform an operation.\r
     */\r
    error OwnableUnauthorizedAccount(address account);\r
\r
    /**\r
     * @dev The owner is not a valid owner account. (eg. `address(0)`)\r
     */\r
    error OwnableInvalidOwner(address owner);\r
\r
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\r
\r
    /**\r
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\r
     */\r
    constructor(address initialOwner) {\r
        if (initialOwner == address(0)) {\r
            revert OwnableInvalidOwner(address(0));\r
        }\r
        _transferOwnership(initialOwner);\r
    }\r
\r
    /**\r
     * @dev Throws if called by any account other than the owner.\r
     */\r
    modifier onlyOwner() {\r
        _checkOwner();\r
        _;\r
    }\r
\r
    /**\r
     * @dev Returns the address of the current owner.\r
     */\r
    function owner() public view virtual returns (address) {\r
        return _owner;\r
    }\r
\r
    /**\r
     * @dev Throws if the sender is not the owner.\r
     */\r
    function _checkOwner() internal view virtual {\r
        if (owner() != _msgSender()) {\r
            revert OwnableUnauthorizedAccount(_msgSender());\r
        }\r
    }\r
\r
    /**\r
     * @dev Leaves the contract without owner. It will not be possible to call\r
     * `onlyOwner` functions. Can only be called by the current owner.\r
     *\r
     * NOTE: Renouncing ownership will leave the contract without an owner,\r
     * thereby disabling any functionality that is only available to the owner.\r
     */\r
    function renounceOwnership() public virtual onlyOwner {\r
        _transferOwnership(address(0));\r
    }\r
\r
    /**\r
     * @dev Transfers ownership of the contract to a new account (`newOwner`).\r
     * Can only be called by the current owner.\r
     */\r
    function transferOwnership(address newOwner) public virtual onlyOwner {\r
        if (newOwner == address(0)) {\r
            revert OwnableInvalidOwner(address(0));\r
        }\r
        _transferOwnership(newOwner);\r
    }\r
\r
    /**\r
     * @dev Transfers ownership of the contract to a new account (`newOwner`).\r
     * Internal function without access restriction.\r
     */\r
    function _transferOwnership(address newOwner) internal virtual {\r
        address oldOwner = _owner;\r
        _owner = newOwner;\r
        emit OwnershipTransferred(oldOwner, newOwner);\r
    }\r
}\r
\r
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol\r
\r
\r
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)\r
\r
pragma solidity ^0.8.20;\r
\r
/**\r
 * @dev Interface of the ERC20 standard as defined in the EIP.\r
 */\r
interface IERC20 {\r
    /**\r
     * @dev Emitted when `value` tokens are moved from one account (`from`) to\r
     * another (`to`).\r
     *\r
     * Note that `value` may be zero.\r
     */\r
    event Transfer(address indexed from, address indexed to, uint256 value);\r
\r
    /**\r
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\r
     * a call to {approve}. `value` is the new allowance.\r
     */\r
    event Approval(address indexed owner, address indexed spender, uint256 value);\r
\r
    /**\r
     * @dev Returns the value of tokens in existence.\r
     */\r
    function totalSupply() external view returns (uint256);\r
\r
    /**\r
     * @dev Returns the value of tokens owned by `account`.\r
     */\r
    function balanceOf(address account) external view returns (uint256);\r
\r
    /**\r
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\r
     *\r
     * Returns a boolean value indicating whether the operation succeeded.\r
     *\r
     * Emits a {Transfer} event.\r
     */\r
    function transfer(address to, uint256 value) external returns (bool);\r
\r
    /**\r
     * @dev Returns the remaining number of tokens that `spender` will be\r
     * allowed to spend on behalf of `owner` through {transferFrom}. This is\r
     * zero by default.\r
     *\r
     * This value changes when {approve} or {transferFrom} are called.\r
     */\r
    function allowance(address owner, address spender) external view returns (uint256);\r
\r
    /**\r
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\r
     * caller's tokens.\r
     *\r
     * Returns a boolean value indicating whether the operation succeeded.\r
     *\r
     * IMPORTANT: Beware that changing an allowance with this method brings the risk\r
     * that someone may use both the old and the new allowance by unfortunate\r
     * transaction ordering. One possible solution to mitigate this race\r
     * condition is to first reduce the spender's allowance to 0 and set the\r
     * desired value afterwards:\r
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\r
     *\r
     * Emits an {Approval} event.\r
     */\r
    function approve(address spender, uint256 value) external returns (bool);\r
\r
    /**\r
     * @dev Moves a `value` amount of tokens from `from` to `to` using the\r
     * allowance mechanism. `value` is then deducted from the caller's\r
     * allowance.\r
     *\r
     * Returns a boolean value indicating whether the operation succeeded.\r
     *\r
     * Emits a {Transfer} event.\r
     */\r
    function transferFrom(address from, address to, uint256 value) external returns (bool);\r
}\r
\r
// File: @openzeppelin/contracts/security/ReentrancyGuard.sol\r
\r
\r
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)\r
\r
pragma solidity ^0.8.0;\r
\r
/**\r
 * @dev Contract module that helps prevent reentrant calls to a function.\r
 *\r
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\r
 * available, which can be applied to functions to make sure there are no nested\r
 * (reentrant) calls to them.\r
 *\r
 * Note that because there is a single `nonReentrant` guard, functions marked as\r
 * `nonReentrant` may not call one another. This can be worked around by making\r
 * those functions `private`, and then adding `external` `nonReentrant` entry\r
 * points to them.\r
 *\r
 * TIP: If you would like to learn more about reentrancy and alternative ways\r
 * to protect against it, check out our blog post\r
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\r
 */\r
abstract contract ReentrancyGuard {\r
    // Booleans are more expensive than uint256 or any type that takes up a full\r
    // word because each write operation emits an extra SLOAD to first read the\r
    // slot's contents, replace the bits taken up by the boolean, and then write\r
    // back. This is the compiler's defense against contract upgrades and\r
    // pointer aliasing, and it cannot be disabled.\r
\r
    // The values being non-zero value makes deployment a bit more expensive,\r
    // but in exchange the refund on every call to nonReentrant will be lower in\r
    // amount. Since refunds are capped to a percentage of the total\r
    // transaction's gas, it is best to keep them low in cases like this one, to\r
    // increase the likelihood of the full refund coming into effect.\r
    uint256 private constant _NOT_ENTERED = 1;\r
    uint256 private constant _ENTERED = 2;\r
\r
    uint256 private _status;\r
\r
    constructor() {\r
        _status = _NOT_ENTERED;\r
    }\r
\r
    /**\r
     * @dev Prevents a contract from calling itself, directly or indirectly.\r
     * Calling a `nonReentrant` function from another `nonReentrant`\r
     * function is not supported. It is possible to prevent this from happening\r
     * by making the `nonReentrant` function external, and making it call a\r
     * `private` function that does the actual work.\r
     */\r
    modifier nonReentrant() {\r
        _nonReentrantBefore();\r
        _;\r
        _nonReentrantAfter();\r
    }\r
\r
    function _nonReentrantBefore() private {\r
        // On the first call to nonReentrant, _status will be _NOT_ENTERED\r
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");\r
\r
        // Any calls to nonReentrant after this point will fail\r
        _status = _ENTERED;\r
    }\r
\r
    function _nonReentrantAfter() private {\r
        // By storing the original value once again, a refund is triggered (see\r
        // https://eips.ethereum.org/EIPS/eip-2200)\r
        _status = _NOT_ENTERED;\r
    }\r
\r
    /**\r
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a\r
     * `nonReentrant` function in the call stack.\r
     */\r
    function _reentrancyGuardEntered() internal view returns (bool) {\r
        return _status == _ENTERED;\r
    }\r
}\r
\r
// File: contracts/Marketplace.sol\r
\r
\r
pragma solidity ^0.8.0;\r
\r
\r
\r
\r
interface IUniswapV2Locker {\r
    // Getter function to fetch details about a specific lock for a user\r
    function getUserLockForTokenAtIndex(\r
        address user,\r
        address lpAddress,\r
        uint256 index\r
    )\r
        external\r
        view\r
        returns (uint256, uint256, uint256, uint256, uint256, address);\r
\r
    function tokenLocks(\r
        address lpAddress,\r
        uint256 lockID\r
    )\r
        external\r
        view\r
        returns (uint256, uint256, uint256, uint256, uint256, address);\r
\r
    // Function to transfer the ownership of a lock\r
    function transferLockOwnership(\r
        address lpAddress,\r
        uint256 index,\r
        uint256 lockID,\r
        address payable newOwner\r
    ) external;\r
\r
    function getUserNumLocksForToken(\r
        address _user,\r
        address _lpAddress\r
    ) external view returns (uint256);\r
}\r
\r
/// @title Marketplace for LP Token Lock Ownershiph\r
/// @notice This contract allows users to list and sell their Uniswap V2 LP token lock ownerships locked through Unicrypt.\r
contract Marketplace is Ownable, ReentrancyGuard {\r
    // Unicrypt V2 Locker address\r
    IUniswapV2Locker public uniswapV2Locker;\r
\r
    // Native token address\r
    IERC20 public STASH;\r
    address payable public feeWallet;\r
    uint256 public listingCount;\r
    address public marketplaceOwner;\r
    uint256 public activeListings;\r
    uint256 public listedLPsCount;\r
    uint256 public totalValueListedInEth;\r
    uint256 public totalValueListedInSTASH;\r
    uint256 public ethFee;\r
    uint256 public referralBonus;\r
\r
    // Zero address constant\r
    address constant ZERO_ADDRESS = address(0);\r
\r
    // Relevant listing info\r
    struct Listing {\r
        uint256 lockID;\r
        uint256 listingID;\r
        uint256 listingIndex;\r
        address payable seller;\r
        address lpAddress;\r
        uint256 priceInETH;\r
        uint256 priceInSTASH;\r
        uint256 listDate;\r
        bool isActive;\r
        bool isSold;\r
        address payable referral;\r
        bool isVerified;\r
        bool forAuction;\r
        uint256 auctionIndex;\r
    }\r
\r
    struct Bid {\r
        address bidder;\r
        uint256 STASHBid;\r
        uint256 ethBid;\r
        uint256 listingID;\r
    }\r
\r
    struct ListingDetail {\r
        uint256 lockID;\r
        address lpAddress;\r
    }\r
\r
    struct AuctionDetails {\r
        Bid topEthBid;\r
        Bid topSTASHBid;\r
    }\r
\r
    // lpAddress + lockID -> returns Listing\r
    mapping(address => mapping(uint256 => Listing)) public lpToLockID;\r
    mapping(uint256 => ListingDetail) public listingDetail;\r
    mapping(address => bool) public isLPListed;\r
    mapping(address => Bid[]) public userBids;\r
    mapping(address => mapping(uint256 => Bid[])) public lpBids;\r
\r
    // Auctions:\r
    AuctionDetails[] public auctions;\r
    uint256 public auctionCount;\r
\r
    // Events\r
    event NewBid(\r
        address indexed bidder,\r
        address indexed lpAddress,\r
        uint256 indexed lockID,\r
        uint256 bidInSTASH,\r
        uint256 bidInEth\r
    );\r
    event BidRedacted(\r
        address indexed bidder,\r
        address indexed lpAddress,\r
        uint256 indexed lockId,\r
        uint256 bidInSTASH,\r
        uint256 bidInEth\r
    );\r
    event BidAccepted(\r
        address indexed lpToken,\r
        uint256 indexed lockId,\r
        uint256 profitInEth,\r
        uint256 feeEth,\r
        uint256 profitInSTASH\r
    );\r
    event LockPurchasedWithETH(\r
        address indexed lpToken,\r
        uint256 indexed lockID,\r
        uint256 profitInETH,\r
        uint256 feeETH\r
    );\r
    event LockPurchasedWithSTASH(\r
        address indexed lpToken,\r
        uint256 indexed lockID,\r
        uint256 profitInSTASH\r
    );\r
    event ListingInitiated(\r
        address indexed lpToken,\r
        uint256 indexed lockID,\r
        address indexed seller,\r
        bool forAuction\r
    );\r
    event NewActiveListing(\r
        address indexed lpToken,\r
        uint256 indexed lockID,\r
        uint256 priceInETH,\r
        uint256 priceInSTASH\r
    );\r
    event LockVerified(\r
        address indexed lpToken,\r
        uint256 indexed lockID,\r
        bool status\r
    );\r
    event ListingRedacted(\r
        address indexed lpToken,\r
        uint256 indexed lockID,\r
        address indexed seller\r
    );\r
    event ListingWithdrawn(address indexed lpToken, uint256 indexed lockID);\r
    event STASHAddressUpdated(address indexed _STASHAddress);\r
    event FeeAddressUpdated(address indexed _feeWallet);\r
    event LockerAddressUpdated(address indexed _lockerAddress);\r
    event ChangedETHFee(uint256 _ethFee);\r
    event ChangedReferralBonus(uint256 _referralBonus);\r
\r
    /// @notice Initialize the contract with Uniswap V2 Locker, Fee Wallet, and Token addresses\r
    /// @dev Sets the contract's dependencies and the owner upon deployment\r
    /// @param _uniswapV2Locker Address of the Uniswap V2 Locker contract\r
    /// @param _feeWallet Address of the wallet where fees will be collected\r
    /// @param _STASHAddress Address of the token contract\r
    constructor(\r
        address _uniswapV2Locker,\r
        address payable _feeWallet,\r
        address _STASHAddress\r
    ) Ownable(msg.sender) {\r
        require(\r
            _uniswapV2Locker != ZERO_ADDRESS,\r
            "Invalid Uniswap V2 Locker address"\r
        );\r
        require(_feeWallet != ZERO_ADDRESS, "Invalid fee wallet address");\r
        require(_STASHAddress != ZERO_ADDRESS, "Invalid token address");\r
\r
        uniswapV2Locker = IUniswapV2Locker(_uniswapV2Locker);\r
        feeWallet = _feeWallet;\r
        marketplaceOwner = msg.sender;\r
        STASH = IERC20(_STASHAddress);\r
        ethFee = 10;\r
        referralBonus = 0;\r
    }\r
\r
    // =====================================================================\r
    // ⚡️ Public Write Functions - Modify Contract State\r
    // =====================================================================\r
\r
    /// @notice List an LP token lock for sale or auction\r
    /// @dev The seller must be the owner of the lock and approve this contract to manage the lock\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _lockId The ID of the lock\r
    /// @param _priceInETH The selling price in ETH (set to 0 if not selling for ETH)\r
    /// @param _priceInSTASH The selling price in tokens (set to 0 if not selling for tokens)\r
    /// @param _referral The address of the referrer (set to address(0) if no referral)\r
    /// @param _forAuction Whether this listing is for auction or direct sale\r
    function initiateListing(\r
        address _lpAddress,\r
        uint256 _lockId,\r
        uint256 _priceInETH,\r
        uint256 _priceInSTASH,\r
        address payable _referral,\r
        bool _forAuction\r
    ) external {\r
        (, , , , , address owner) = uniswapV2Locker.tokenLocks(\r
            _lpAddress,\r
            _lockId\r
        );\r
        require(msg.sender == owner, "You don't own that lock.");\r
        require(\r
            (_priceInETH > 0) || (_priceInSTASH > 0),\r
            "You must set a price in tokens or ETH"\r
        );\r
\r
        (bool lockFound, uint256 index) = _getIndexForUserLock(\r
            _lpAddress,\r
            _lockId,\r
            _msgSender()\r
        );\r
        require(lockFound, "Lock not found!");\r
\r
        uint256 newListingId;\r
        AuctionDetails memory tempDetails;\r
\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockId];\r
        if (tempListing.listingID == 0) {\r
            listingCount++;\r
            newListingId = listingCount;\r
            listingDetail[newListingId] = ListingDetail(_lockId, _lpAddress);\r
        } else {\r
            newListingId = tempListing.listingID;\r
            require(!tempListing.isActive, "Listing is already active");\r
        }\r
\r
        if (_forAuction) {\r
            tempDetails = _initializeAuctionDetails(newListingId);\r
            auctions.push(tempDetails);\r
            auctionCount++;\r
        }\r
\r
        lpToLockID[_lpAddress][_lockId] = Listing({\r
            lockID: _lockId,\r
            listingID: newListingId,\r
            listingIndex: index,\r
            seller: payable(msg.sender),\r
            lpAddress: _lpAddress,\r
            priceInETH: _priceInETH,\r
            priceInSTASH: _priceInSTASH,\r
            listDate: block.timestamp,\r
            isActive: false,\r
            isSold: false,\r
            referral: _referral,\r
            isVerified: false,\r
            forAuction: _forAuction,\r
            auctionIndex: _forAuction ? auctionCount - 1 : 0\r
        });\r
\r
        if (!isLPListed[_lpAddress]) {\r
            isLPListed[_lpAddress] = true;\r
            listedLPsCount++;\r
        }\r
\r
        totalValueListedInEth += _priceInETH;\r
        totalValueListedInSTASH += _priceInSTASH;\r
\r
        emit ListingInitiated(_lpAddress, _lockId, msg.sender, _forAuction);\r
    }\r
\r
    /// @notice Activate an initiated listing\r
    /// @dev The seller must have transferred lock ownership to address(this)\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _lockId Unique lockID (per lpAddress) of the lock\r
    function activateListing(address _lpAddress, uint256 _lockId) external {\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockId];\r
        require(tempListing.seller == msg.sender, "Lock doesn't belong to you");\r
        require(!tempListing.isActive, "Listing is already active");\r
        require(!tempListing.isSold, "Listing has already been sold");\r
\r
        (, , , , , address owner) = uniswapV2Locker.tokenLocks(\r
            _lpAddress,\r
            _lockId\r
        );\r
        require(owner == address(this), "Lock ownership not yet transferred");\r
\r
        // Update storage\r
        lpToLockID[_lpAddress][_lockId].isActive = true;\r
        activeListings++;\r
\r
        // Clear any existing bids for this listing\r
        if (lpBids[_lpAddress][_lockId].length > 0) {\r
            delete lpBids[_lpAddress][_lockId];\r
        }\r
\r
        emit NewActiveListing(\r
            _lpAddress,\r
            _lockId,\r
            tempListing.priceInETH,\r
            tempListing.priceInSTASH\r
        );\r
    }\r
\r
    /// @notice Bid on a listing with Ethereum - transfer ETH to CA until bid is either beat, accepted, or withdrawn\r
    /// @dev Bidder must not be listing owner.\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _lockId The ID of the lock\r
    function bidEth(\r
        address _lpAddress,\r
        uint256 _lockId\r
    ) external payable nonReentrant {\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockId];\r
        require(tempListing.forAuction, "Listing not for auction");\r
        require(\r
            tempListing.seller != msg.sender,\r
            "Unable to bid on own listing"\r
        );\r
        require(tempListing.isActive, "Listing inactive");\r
        require(!tempListing.isSold, "Listing already sold");\r
\r
        AuctionDetails memory currentAuction = auctions[\r
            tempListing.auctionIndex\r
        ];\r
\r
        uint256 minBid = currentAuction.topEthBid.ethBid + 1; // Minimum increment of 1 wei\r
        require(msg.value >= minBid, "Bid must be higher than current bid");\r
\r
        address previousBidder = currentAuction.topEthBid.bidder;\r
        uint256 previousBidAmount = currentAuction.topEthBid.ethBid;\r
\r
        Bid memory newBid = Bid({\r
            bidder: msg.sender,\r
            STASHBid: 0,\r
            ethBid: msg.value,\r
            listingID: tempListing.listingID\r
        });\r
\r
        auctions[tempListing.auctionIndex].topEthBid = newBid;\r
        userBids[msg.sender].push(newBid);\r
        lpBids[_lpAddress][_lockId].push(newBid);\r
\r
        if (previousBidAmount > 0) {\r
            (bool success, ) = payable(previousBidder).call{\r
                value: previousBidAmount\r
            }("");\r
            require(success, "Failed to refund previous bidder");\r
        }\r
\r
        emit NewBid(msg.sender, _lpAddress, _lockId, 0, msg.value);\r
    }\r
\r
    /// @notice Bid on a listing with tokens - transfer tokens to CA until bid is either beat, accepted, or withdrawn\r
    /// @dev Bidder must not be listing owner\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _lockId The ID of the lock\r
    /// @param _amount Amount of tokens to bid with\r
    function bidSTASH(\r
        address _lpAddress,\r
        uint256 _lockId,\r
        uint256 _amount\r
    ) external nonReentrant {\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockId];\r
        require(tempListing.forAuction, "Listing not for auction");\r
        require(\r
            tempListing.seller != msg.sender,\r
            "Unable to bid on own listing"\r
        );\r
        require(tempListing.isActive, "Listing inactive");\r
        require(!tempListing.isSold, "Listing already sold");\r
\r
        AuctionDetails memory currentAuction = auctions[\r
            tempListing.auctionIndex\r
        ];\r
\r
        uint256 minBid = currentAuction.topSTASHBid.STASHBid + 1; // Minimum increment of 1 token\r
        require(_amount >= minBid, "Bid must be higher than current bid");\r
\r
        address previousBidder = currentAuction.topSTASHBid.bidder;\r
        uint256 previousBidAmount = currentAuction.topSTASHBid.STASHBid;\r
\r
        require(\r
            STASH.transferFrom(msg.sender, address(this), _amount),\r
            "Failed to transfer tokens"\r
        );\r
\r
        Bid memory newBid = Bid({\r
            bidder: msg.sender,\r
            STASHBid: _amount,\r
            ethBid: 0,\r
            listingID: tempListing.listingID\r
        });\r
\r
        auctions[tempListing.auctionIndex].topSTASHBid = newBid;\r
        userBids[msg.sender].push(newBid);\r
        lpBids[_lpAddress][_lockId].push(newBid);\r
\r
        if (previousBidAmount > 0) {\r
            require(\r
                STASH.transfer(previousBidder, previousBidAmount),\r
                "Failed to refund previous bidder"\r
            );\r
        }\r
\r
        emit NewBid(msg.sender, _lpAddress, _lockId, _amount, 0);\r
    }\r
\r
    function acceptBid(\r
        address _lpAddress,\r
        uint256 _lockId,\r
        bool _eth\r
    ) external nonReentrant {\r
        Listing storage tempListing = lpToLockID[_lpAddress][_lockId];\r
        require(tempListing.isActive, "Listing is not active");\r
        require(!tempListing.isSold, "Listing is already sold");\r
        require(tempListing.forAuction, "Listing is not for auction");\r
        require(\r
            tempListing.seller == msg.sender,\r
            "Only the seller can accept a bid"\r
        );\r
\r
        AuctionDetails storage tempAuction = auctions[tempListing.auctionIndex];\r
\r
        Bid storage topBid = _eth\r
            ? tempAuction.topEthBid\r
            : tempAuction.topSTASHBid;\r
        require(\r
            (_eth && topBid.ethBid > 0) || (!_eth && topBid.STASHBid > 0),\r
            "No valid bid to accept"\r
        );\r
\r
        // Return the other type of bid if it exists\r
        if (_eth && tempAuction.topSTASHBid.STASHBid > 0) {\r
            _returnBid(\r
                _lpAddress,\r
                _lockId,\r
                false,\r
                tempListing,\r
                tempAuction.topSTASHBid.bidder\r
            );\r
        } else if (!_eth && tempAuction.topEthBid.ethBid > 0) {\r
            _returnBid(\r
                _lpAddress,\r
                _lockId,\r
                true,\r
                tempListing,\r
                tempAuction.topEthBid.bidder\r
            );\r
        }\r
\r
        _winAuction(tempListing, topBid, _eth);\r
    }\r
\r
    /// @notice Redact your bid on select lock - must be done prior to the expiry date of auction.\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _lockId The ID of the lock\r
    /// @param _eth True if bidder is redacting a bid in ETH, false if bid is in tokens\r
    function redactBid(\r
        address _lpAddress,\r
        uint256 _lockId,\r
        bool _eth\r
    ) external nonReentrant {\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockId];\r
        require(tempListing.forAuction, "No auction for this listing");\r
        require(tempListing.isActive, "Auction is not active");\r
\r
        AuctionDetails memory currentAuction = auctions[\r
            tempListing.auctionIndex\r
        ];\r
\r
        if (_eth) {\r
            require(currentAuction.topEthBid.ethBid > 0, "No ETH bid present");\r
        } else {\r
            require(\r
                currentAuction.topSTASHBid.STASHBid > 0,\r
                "No token bid present"\r
            );\r
        }\r
\r
        _returnBid(_lpAddress, _lockId, _eth, tempListing, msg.sender);\r
    }\r
\r
    /// @notice Purchase a listed LP token lock with ETH\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _lockId The ID of the lock\r
    function buyLockWithETH(\r
        address _lpAddress,\r
        uint256 _lockId\r
    ) external payable nonReentrant {\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockId];\r
        require(tempListing.isActive, "Listing must be active.");\r
        require(tempListing.priceInETH > 0, "Listing not for sale in ETH.");\r
        require(\r
            msg.value == tempListing.priceInETH,\r
            "Incorrect amount of ETH."\r
        );\r
\r
        (bool lockFound, uint256 index) = _getIndex(_lpAddress, tempListing);\r
        require(lockFound, "Mismatch in inputs");\r
\r
        uint256 feeAmount = (msg.value * ethFee) / 100;\r
        uint256 toPay = msg.value - feeAmount;\r
\r
        if (tempListing.referral != ZERO_ADDRESS) {\r
            uint256 feeForReferral = (feeAmount * referralBonus) / 100;\r
            feeAmount = feeAmount - feeForReferral;\r
            (bool success, ) = tempListing.referral.call{value: feeForReferral}(\r
                ""\r
            );\r
            require(success, "Referral fee transfer failed");\r
        }\r
\r
        (bool feeSuccess, ) = feeWallet.call{value: feeAmount}("");\r
        require(feeSuccess, "Fee transfer failed");\r
\r
        (bool sellerSuccess, ) = payable(tempListing.seller).call{value: toPay}(\r
            ""\r
        );\r
        require(sellerSuccess, "Seller payment failed");\r
\r
        if (tempListing.forAuction) {\r
            AuctionDetails memory currentAuction = auctions[\r
                tempListing.auctionIndex\r
            ];\r
\r
            if (currentAuction.topSTASHBid.STASHBid > 0) {\r
                _returnBid(\r
                    _lpAddress,\r
                    _lockId,\r
                    false,\r
                    tempListing,\r
                    currentAuction.topSTASHBid.bidder\r
                );\r
            }\r
            if (currentAuction.topEthBid.ethBid > 0) {\r
                _returnBid(\r
                    _lpAddress,\r
                    _lockId,\r
                    true,\r
                    tempListing,\r
                    currentAuction.topEthBid.bidder\r
                );\r
            }\r
        }\r
\r
        lpToLockID[_lpAddress][_lockId].isActive = false;\r
        lpToLockID[_lpAddress][_lockId].isSold = true;\r
        activeListings--;\r
\r
        uniswapV2Locker.transferLockOwnership(\r
            _lpAddress,\r
            index,\r
            _lockId,\r
            payable(msg.sender)\r
        );\r
\r
        emit LockPurchasedWithETH(\r
            tempListing.lpAddress,\r
            tempListing.lockID,\r
            toPay,\r
            feeAmount\r
        );\r
    }\r
\r
    /// @notice Purchase a listed LP token lock with tokens\r
    /// @dev Requires approval to transfer tokens to cover the purchase price\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _lockId The ID of the lock\r
    function buyLockWithSTASH(\r
        address _lpAddress,\r
        uint256 _lockId\r
    ) external nonReentrant {\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockId];\r
\r
        require(tempListing.isActive, "Listing must be active");\r
        require(tempListing.priceInSTASH > 0, "Listing not for sale in tokens");\r
        require(\r
            STASH.balanceOf(msg.sender) >= tempListing.priceInSTASH,\r
            "Insufficient STASH"\r
        );\r
\r
        (bool lockFound, uint256 index) = _getIndex(_lpAddress, tempListing);\r
        require(lockFound, "Mismatch in inputs");\r
\r
        // Transfer tokens from buyer to seller\r
        require(\r
            STASH.transferFrom(\r
                msg.sender,\r
                tempListing.seller,\r
                tempListing.priceInSTASH\r
            ),\r
            "Tokens transfer failed"\r
        );\r
\r
        if (tempListing.forAuction) {\r
            AuctionDetails memory currentAuction = auctions[\r
                tempListing.auctionIndex\r
            ];\r
\r
            if (currentAuction.topSTASHBid.STASHBid > 0) {\r
                _returnBid(\r
                    _lpAddress,\r
                    _lockId,\r
                    false,\r
                    tempListing,\r
                    currentAuction.topSTASHBid.bidder\r
                );\r
            }\r
            if (currentAuction.topEthBid.ethBid > 0) {\r
                _returnBid(\r
                    _lpAddress,\r
                    _lockId,\r
                    true,\r
                    tempListing,\r
                    currentAuction.topEthBid.bidder\r
                );\r
            }\r
        }\r
\r
        lpToLockID[_lpAddress][_lockId].isActive = false;\r
        lpToLockID[_lpAddress][_lockId].isSold = true;\r
        activeListings--;\r
\r
        uniswapV2Locker.transferLockOwnership(\r
            _lpAddress,\r
            index,\r
            _lockId,\r
            payable(msg.sender)\r
        );\r
\r
        emit LockPurchasedWithSTASH(\r
            tempListing.lpAddress,\r
            tempListing.lockID,\r
            tempListing.priceInSTASH\r
        );\r
    }\r
\r
    /// @notice Withdraw a listed LP token lock\r
    /// @dev Only the seller can withdraw the listing\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _lockId The ID of the lock\r
    function withdrawListing(\r
        address _lpAddress,\r
        uint256 _lockId\r
    ) external nonReentrant {\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockId];\r
        require(\r
            tempListing.seller == msg.sender,\r
            "This listing does not belong to you."\r
        );\r
\r
        (, , , , , address owner) = uniswapV2Locker.tokenLocks(\r
            _lpAddress,\r
            _lockId\r
        );\r
        require(owner == address(this), "Marketplace does not own your lock");\r
\r
        (bool lockFound, uint256 index) = _getIndex(_lpAddress, tempListing);\r
        require(lockFound, "Mismatch in inputs.");\r
\r
        if (tempListing.forAuction) {\r
            AuctionDetails memory currentAuction = auctions[\r
                tempListing.auctionIndex\r
            ];\r
\r
            if (currentAuction.topSTASHBid.STASHBid > 0) {\r
                _returnBid(\r
                    _lpAddress,\r
                    _lockId,\r
                    false,\r
                    tempListing,\r
                    currentAuction.topSTASHBid.bidder\r
                );\r
            }\r
\r
            if (currentAuction.topEthBid.ethBid > 0) {\r
                _returnBid(\r
                    _lpAddress,\r
                    _lockId,\r
                    true,\r
                    tempListing,\r
                    currentAuction.topEthBid.bidder\r
                );\r
            }\r
        }\r
\r
        if (tempListing.isActive) {\r
            lpToLockID[_lpAddress][_lockId].isActive = false;\r
            activeListings--;\r
        }\r
\r
        uniswapV2Locker.transferLockOwnership(\r
            _lpAddress,\r
            index,\r
            _lockId,\r
            payable(msg.sender)\r
        );\r
\r
        emit ListingWithdrawn(_lpAddress, _lockId);\r
    }\r
\r
    /// @notice Change the ETH price of a listing\r
    /// @dev Only seller can change price\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _lockID Unique lock ID (per lpAddress) of the lock\r
    /// @param newPriceInETH Updated ETH price of listing\r
    function changePriceInETH(\r
        address _lpAddress,\r
        uint256 _lockID,\r
        uint256 newPriceInETH\r
    ) external nonReentrant {\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockID];\r
        require(\r
            tempListing.seller == msg.sender,\r
            "This listing does not belong to you."\r
        );\r
        require(newPriceInETH > 0, "Price must be greater than zero");\r
\r
        lpToLockID[_lpAddress][_lockID].priceInETH = newPriceInETH;\r
    }\r
\r
    /// @notice Change the price of a listing in tokens\r
    /// @dev Only seller can change price\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _lockID Unique lock ID (per lpAddress) of the lock\r
    /// @param newPriceInSTASH Updated price of listing\r
    function changePriceInSTASH(\r
        address _lpAddress,\r
        uint256 _lockID,\r
        uint256 newPriceInSTASH\r
    ) external nonReentrant {\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockID];\r
        require(\r
            tempListing.seller == msg.sender,\r
            "This listing does not belong to you."\r
        );\r
        require(newPriceInSTASH > 0, "Price must be greater than zero");\r
\r
        lpToLockID[_lpAddress][_lockID].priceInSTASH = newPriceInSTASH;\r
    }\r
\r
    // =====================================================================\r
    // ???? Public Read Functions - Query Contract State\r
    // =====================================================================\r
\r
    function userBidsCount(address _user) external view returns (uint256) {\r
        return userBids[_user].length;\r
    }\r
\r
    function lpBidsCount(\r
        address _lpAddress,\r
        uint256 _lockID\r
    ) public view returns (uint256) {\r
        return lpBids[_lpAddress][_lockID].length;\r
    }\r
\r
    function getIndex(\r
        address _user,\r
        address _lpAddress,\r
        uint256 _lockId\r
    ) external view returns (bool, uint256) {\r
        return _getIndexForUserLock(_lpAddress, _lockId, _user);\r
    }\r
\r
    // =====================================================================\r
    // ???? Only Owner Functions - Restricted Access\r
    // =====================================================================\r
\r
    /// @notice Set the referral fee (in percentage)\r
    /// @dev This function can only be called by the contract owner\r
    /// @param _referralBonus Referral fee percentage for buyLockWithETH\r
    function setReferralFee(uint256 _referralBonus) external onlyOwner {\r
        require(\r
            _referralBonus <= 50,\r
            "Maximum referral bonus is 50% of the fee"\r
        );\r
        require(referralBonus != _referralBonus, "You must change the bonus");\r
        referralBonus = _referralBonus;\r
        emit ChangedReferralBonus(_referralBonus);\r
    }\r
\r
    /// @notice Set the eth fee (in percentage)\r
    /// @dev This function can only be called by the contract owner\r
    /// @param _ethFee Fee percentage for buyLockWithETH\r
    function setETHFee(uint256 _ethFee) external onlyOwner {\r
        require(_ethFee <= 10, "Maximum fee is 10%");\r
        require(ethFee != _ethFee, "You must change the fee");\r
        ethFee = _ethFee;\r
        emit ChangedETHFee(_ethFee);\r
    }\r
\r
    /// @notice Set the address of the token\r
    /// @dev This function can only be called by the contract owner\r
    /// @param _STASHAddress The address of the token contract\r
    function setSTASHAddress(address _STASHAddress) external onlyOwner {\r
        require(\r
            address(STASH) != _STASHAddress,\r
            "Must input different contract address"\r
        );\r
        require(\r
            _STASHAddress != ZERO_ADDRESS,\r
            "Cannot set STASH address as zero address"\r
        );\r
        STASH = IERC20(_STASHAddress);\r
        emit STASHAddressUpdated(_STASHAddress);\r
    }\r
\r
    /// @notice Set the address of the fee wallet\r
    /// @dev This function can only be called by the contract owner\r
    /// @param _feeWallet The address of the new fee wallet\r
    function setFeeWallet(address payable _feeWallet) external onlyOwner {\r
        require(feeWallet != _feeWallet, "Same wallet");\r
        require(\r
            _feeWallet != ZERO_ADDRESS,\r
            "Cannot set fee wallet as zero address"\r
        );\r
        feeWallet = _feeWallet;\r
        emit FeeAddressUpdated(_feeWallet);\r
    }\r
\r
    /// @notice Set the address of the liquidity locker\r
    /// @dev This function can only be called by the contract owner\r
    /// @param _uniswapV2Locker The address of the new liquidity locker\r
    function setLockerAddress(address _uniswapV2Locker) external onlyOwner {\r
        require(\r
            address(uniswapV2Locker) != _uniswapV2Locker,\r
            "Must input different contract address"\r
        );\r
        require(\r
            _uniswapV2Locker != ZERO_ADDRESS,\r
            "Cannot set locker address as zero address"\r
        );\r
        uniswapV2Locker = IUniswapV2Locker(_uniswapV2Locker);\r
        emit LockerAddressUpdated(_uniswapV2Locker);\r
    }\r
\r
    /// @notice Verify a listing as safe\r
    /// @dev Only owner can verify listings\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _lockID Unique lock ID (per lpAddress) of the lock\r
    /// @param _status Status of verification\r
    function verifyListing(\r
        address _lpAddress,\r
        uint256 _lockID,\r
        bool _status\r
    ) external onlyOwner {\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockID];\r
        require(\r
            _status != tempListing.isVerified,\r
            "Must change listing status"\r
        );\r
        lpToLockID[_lpAddress][_lockID].isVerified = _status;\r
        emit LockVerified(_lpAddress, _lockID, _status);\r
    }\r
\r
    /// @notice Return ownership of a lock to the original seller and remove the listing\r
    /// @dev Only the contract owner can call this function\r
    /// @param _lpAddress Address of the LP token associated with the lock\r
    /// @param _lockId The ID of the lock to be redacted\r
    function redactListing(\r
        address _lpAddress,\r
        uint256 _lockId\r
    ) external onlyOwner {\r
        Listing memory tempListing = lpToLockID[_lpAddress][_lockId];\r
\r
        require(tempListing.seller != address(0), "Listing does not exist.");\r
\r
        (bool lockFound, uint256 index) = _getIndex(_lpAddress, tempListing);\r
        require(lockFound, "Lock not found.");\r
\r
        if (tempListing.forAuction) {\r
            AuctionDetails memory currentAuction = auctions[\r
                tempListing.auctionIndex\r
            ];\r
\r
            if (\r
                currentAuction.topSTASHBid.STASHBid > 0 &&\r
                currentAuction.topEthBid.ethBid > 0\r
            ) {\r
                _returnBid(\r
                    _lpAddress,\r
                    _lockId,\r
                    true,\r
                    tempListing,\r
                    currentAuction.topEthBid.bidder\r
                );\r
\r
                _returnBid(\r
                    _lpAddress,\r
                    _lockId,\r
                    false,\r
                    tempListing,\r
                    currentAuction.topSTASHBid.bidder\r
                );\r
            } else if (currentAuction.topEthBid.ethBid > 0) {\r
                _returnBid(\r
                    _lpAddress,\r
                    _lockId,\r
                    true,\r
                    tempListing,\r
                    currentAuction.topEthBid.bidder\r
                );\r
            } else if (currentAuction.topSTASHBid.STASHBid > 0) {\r
                _returnBid(\r
                    _lpAddress,\r
                    _lockId,\r
                    false,\r
                    tempListing,\r
                    currentAuction.topSTASHBid.bidder\r
                );\r
            }\r
        }\r
\r
        uniswapV2Locker.transferLockOwnership(\r
            _lpAddress,\r
            index,\r
            _lockId,\r
            tempListing.seller\r
        );\r
\r
        if (tempListing.isActive) {\r
            lpToLockID[_lpAddress][_lockId].isActive = false;\r
            activeListings--;\r
        }\r
\r
        delete lpToLockID[_lpAddress][_lockId];\r
        emit ListingRedacted(_lpAddress, _lockId, tempListing.seller);\r
    }\r
\r
    // =====================================================================\r
    // ???? Private/Internal Functions - Contract's Inner Workings\r
    // =====================================================================\r
\r
    function _initializeAuctionDetails(\r
        uint256 _listingId\r
    ) internal pure returns (AuctionDetails memory) {\r
        return\r
            AuctionDetails({\r
                topEthBid: Bid({\r
                    bidder: ZERO_ADDRESS,\r
                    STASHBid: 0,\r
                    ethBid: 0,\r
                    listingID: _listingId\r
                }),\r
                topSTASHBid: Bid({\r
                    bidder: ZERO_ADDRESS,\r
                    STASHBid: 0,\r
                    ethBid: 0,\r
                    listingID: _listingId\r
                })\r
            });\r
    }\r
\r
    function _winAuction(\r
        Listing storage _tempListing,\r
        Bid storage _winningBid,\r
        bool _eth\r
    ) private {\r
        require(_tempListing.isActive, "Listing must be active.");\r
        (bool lockFound, uint256 index) = _getIndex(\r
            _tempListing.lpAddress,\r
            _tempListing\r
        );\r
        require(lockFound, "Mismatch in inputs");\r
\r
        uint256 profitInEth = 0;\r
        uint256 feeEth = 0;\r
        uint256 profitInSTASH = 0;\r
\r
        if (_eth) {\r
            require(\r
                address(this).balance >= _winningBid.ethBid,\r
                "Insufficient contract balance"\r
            );\r
            feeEth = (_winningBid.ethBid * ethFee) / 100;\r
            profitInEth = _winningBid.ethBid - feeEth;\r
\r
            if (_tempListing.referral != ZERO_ADDRESS) {\r
                uint256 feeForReferral = (feeEth * referralBonus) / 100;\r
                feeEth -= feeForReferral;\r
                (bool referralSuccess, ) = _tempListing.referral.call{\r
                    value: feeForReferral\r
                }("");\r
                require(referralSuccess, "Failed to send referral fee");\r
            }\r
\r
            (bool feeSuccess, ) = feeWallet.call{value: feeEth}("");\r
            require(feeSuccess, "Failed to send fee");\r
\r
            (bool sellerSuccess, ) = payable(_tempListing.seller).call{\r
                value: profitInEth\r
            }("");\r
            require(sellerSuccess, "Failed to send payment to seller");\r
\r
            _winningBid.ethBid = 0;\r
        } else {\r
            require(\r
                STASH.balanceOf(address(this)) >= _winningBid.STASHBid,\r
                "Insufficient STASH balance"\r
            );\r
\r
            profitInSTASH = _winningBid.STASHBid;\r
            require(\r
                STASH.transfer(_tempListing.seller, profitInSTASH),\r
                "Failed to transfer tokens to seller"\r
            );\r
            _winningBid.STASHBid = 0;\r
        }\r
\r
        _tempListing.isActive = false;\r
        _tempListing.isSold = true;\r
        activeListings--;\r
\r
        uniswapV2Locker.transferLockOwnership(\r
            _tempListing.lpAddress,\r
            index,\r
            _tempListing.lockID,\r
            payable(_winningBid.bidder)\r
        );\r
\r
        emit BidAccepted(\r
            _tempListing.lpAddress,\r
            _tempListing.lockID,\r
            profitInEth,\r
            feeEth,\r
            profitInSTASH\r
        );\r
    }\r
\r
    function _returnBid(\r
        address _lpAddress,\r
        uint256 _lockId,\r
        bool _eth,\r
        Listing memory _tempListing,\r
        address _sender\r
    ) internal {\r
        AuctionDetails storage currentAuction = auctions[\r
            _tempListing.auctionIndex\r
        ];\r
\r
        Bid storage topBid = _eth\r
            ? currentAuction.topEthBid\r
            : currentAuction.topSTASHBid;\r
\r
        require(\r
            topBid.bidder == _sender,\r
            _eth\r
                ? "You are not the current ETH bidder"\r
                : "You are not the current token bidder"\r
        );\r
\r
        uint256 amount = _eth ? topBid.ethBid : topBid.STASHBid;\r
\r
        // Reset the bid\r
        topBid.bidder = address(0);\r
        topBid.ethBid = 0;\r
        topBid.STASHBid = 0;\r
        topBid.listingID = _tempListing.listingID;\r
\r
        if (amount > 0) {\r
            if (_eth) {\r
                payable(_sender).transfer(amount);\r
            } else {\r
                require(\r
                    STASH.transfer(_sender, amount),\r
                    "Failed to transfer tokens"\r
                );\r
            }\r
\r
            emit BidRedacted(\r
                _sender,\r
                _lpAddress,\r
                _lockId,\r
                _eth ? 0 : amount,\r
                _eth ? amount : 0\r
            );\r
        }\r
    }\r
\r
    /// @notice Find unique (per lpAddress) lock index in order to transfer lock ownership\r
    /// @param _lpAddress Address of the LP token\r
    /// @param _listing Listing in question\r
    /// @return lockFound Boolean indicating if the lock was found\r
    /// @return index The index of the found lock\r
    function _getIndex(\r
        address _lpAddress,\r
        Listing memory _listing\r
    ) internal view returns (bool lockFound, uint256 index) {\r
        uint256 numLocksAtAddress = uniswapV2Locker.getUserNumLocksForToken(\r
            address(this),\r
            _lpAddress\r
        );\r
\r
        if (numLocksAtAddress == 0) {\r
            return (false, 0);\r
        }\r
\r
        if (numLocksAtAddress == 1) {\r
            return (true, 0);\r
        }\r
\r
        for (uint256 i = 0; i < numLocksAtAddress; i++) {\r
            (, , , , uint256 _lockId, ) = uniswapV2Locker\r
                .getUserLockForTokenAtIndex(address(this), _lpAddress, i);\r
            if (_lockId == _listing.lockID) {\r
                return (true, i);\r
            }\r
        }\r
\r
        return (false, 0);\r
    }\r
\r
    function _getIndexForUserLock(\r
        address _lpAddress,\r
        uint256 _lockId,\r
        address user\r
    ) internal view returns (bool lockFound, uint256 index) {\r
        uint256 numLocksAtAddress = uniswapV2Locker.getUserNumLocksForToken(\r
            user,\r
            _lpAddress\r
        );\r
\r
        if (numLocksAtAddress == 0) {\r
            return (false, 0);\r
        }\r
\r
        if (numLocksAtAddress == 1) {\r
            return (true, 0);\r
        }\r
\r
        for (uint256 i = 0; i < numLocksAtAddress; i++) {\r
            (, , , , uint256 _tempLockID, ) = uniswapV2Locker\r
                .getUserLockForTokenAtIndex(user, _lpAddress, i);\r
            if (_tempLockID == _lockId) {\r
                return (true, i);\r
            }\r
        }\r
\r
        return (false, 0);\r
    }\r
}"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "remappings": [],
    "evmVersion": "cancun"
  }
}}

Tags:
ERC20, Multisig, Multi-Signature, Factory|addr:0x552db5fa5136a5e0c8bfdc7cc05c05c19f3602a2|verified:true|block:23498622|tx:0x9d8c9fb2d06759611b26e05ac28436e2ee6033553ab6aa98629f73888189d305|first_check:1759514644

Submitted on: 2025-10-03 20:04:06

Comments

Log in to comment.

No comments yet.