ETH Price: $2,514.49 (+1.93%)

Contract Diff Checker

Contract Name:
GFarmNFTExchange

Contract Source Code:

File 1 of 1 : GFarmNFTExchange

// File: contracts\GFarmNFTInterface.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

interface GFarmNFTInterface{
    function balanceOf(address owner) external view returns (uint256 balance);
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
    function leverageID(uint8 _leverage) external pure returns(uint8);
    function idToLeverage(uint id) external view returns(uint8);
    function transferFrom(address from, address to, uint256 tokenId) external;
}

// File: contracts\GFarmTokenInterface.sol

pragma solidity 0.7.5;

interface GFarmTokenInterface{
	function balanceOf(address account) external view returns (uint256);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
    function transfer(address to, uint256 value) external returns (bool);
    function approve(address spender, uint256 value) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function burn(address from, uint256 amount) external;
    function mint(address to, uint256 amount) external;
}

// File: @uniswap\v2-core\contracts\interfaces\IUniswapV2Pair.sol

pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

// File: @openzeppelin\contracts\math\SafeMath.sol

pragma solidity ^0.7.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

// File: contracts\GFarmNFTExchange.sol

pragma solidity 0.7.5;





contract GFarmNFTExchange {

    using SafeMath for uint;

    // 1. Tokens
    GFarmNFTInterface public immutable nft;
    GFarmTokenInterface public immutable token;
    IUniswapV2Pair public immutable lp;
    address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    IUniswapV2Pair constant ETH_USDC_PAIR = IUniswapV2Pair(0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc);

    // 2. Store all NFTs for sale
    struct Bidding{
        uint nftID;
        address payable seller;
        uint start; // block
        uint duration; // blocks
        uint minBid; // 1e18
        uint highestBid; // 1e18
        uint highestBidMinusFees; // 1e18
        address payable highestBidder;
        uint bidCount;
    }
    mapping(uint => Bidding[]) public biddingsByLeverage;
    mapping(uint => uint) public idToBiddingsByLeverageIndex; // nft id => index in biddingsByLeverage

    // 3. GFARM2 pool
    uint public accETHperGFARM; // 1e18
    struct User{
        uint provided; //1e18
        uint debt; //1e18
    }
    mapping(address => User) public users;

    // 4. Fees
    uint public BID_FEE_P = 5;
    uint public HIGHEST_BID_FEE_P = 25;
    address public GOV;

    // 5. Stats & Events
    uint public totalEthDistributed;

    event NewBidding(
        address indexed seller,
        uint nftID,
        uint leverage,
        uint duration,
        uint minBid
    );

    event NewBid(
        address indexed seller,
        address indexed bidder,
        uint nftID,
        uint leverage,
        uint amount
    );

    constructor(
        GFarmNFTInterface _nft,
        GFarmTokenInterface _token,
        IUniswapV2Pair _lp,
        address _gov){
        nft = _nft;
        token = _token;
        lp = _lp;
        GOV = _gov;
    }

    // UPDATE FEE PERCENTAGES - GOVERNANCE (EXCHANGE)

    modifier onlyGov(){
        require(msg.sender == GOV);
        _;
    }
    function set_GOV(address _gov) external onlyGov{
        GOV = _gov;
    }
    function set_BID_FEE_P(uint _fee) external onlyGov{
        BID_FEE_P = _fee;
    }
    function set_HIGHEST_BID_FEE_P(uint _fee) external onlyGov{
        HIGHEST_BID_FEE_P = _fee;
    }

    // PRIVATE FUNCTIONS (EXCHANGE)

    function removeBidding(uint _nftID) private{
        uint leverage = nft.idToLeverage(_nftID);
        Bidding[] storage leverageBiddings = biddingsByLeverage[leverage];

        // 1. Update index of the last bidding of the array to this nft's index in the array
        idToBiddingsByLeverageIndex[leverageBiddings[leverageBiddings.length - 1].nftID] = idToBiddingsByLeverageIndex[_nftID];

        // 2. Copy last bidding of the array to replace this nft ID's listings with the last one
        leverageBiddings[idToBiddingsByLeverageIndex[_nftID]] = leverageBiddings[leverageBiddings.length - 1];

        // 3. Remove the last element of the biddings array
        leverageBiddings.pop();

        // 4. Remove index from mapping for this NFT
        delete idToBiddingsByLeverageIndex[_nftID];

    }

    // EXTERNAL FUNCTIONS (EXCHANGE)

    // 0. Modifiers
    modifier notContract(){
        require(tx.origin == msg.sender, "Contracts not allowed.");
        _;
    }
    modifier listed(uint id){
        require(isListed(id), "This NFT isn't listed.");
        _;
    }

    // 1. Put an NFT for sale
    function sell(uint _nftID, uint _minBid, uint _bidDuration) external notContract{
        // 1. Bidding duration should be between 100 blocks and 100k blocks
        require(_bidDuration >= 100 && _bidDuration <= 100000, "Bidding duration should be between 100 and 100k blocks.");

        // 2. Transfer the NFT to this contract
        nft.transferFrom(msg.sender, address(this), _nftID);

        // 3. Store bidding
        uint leverage = nft.idToLeverage(_nftID);
        Bidding memory b = Bidding(_nftID, msg.sender, block.number, _bidDuration, _minBid, 0, 0, address(0), 0);

        biddingsByLeverage[leverage].push(b);
        idToBiddingsByLeverageIndex[_nftID] = biddingsByLeverage[leverage].length - 1;

        // 4. Emit event
        emit NewBidding(
            msg.sender,
            _nftID,
            leverage,
            _bidDuration,
            _minBid
        );
    }

    // 2. Place a bid for an NFT
    function bid(uint _nftID) external payable notContract listed(_nftID){
        uint leverage = nft.idToLeverage(_nftID);
        Bidding storage b = biddingsByLeverage[leverage][idToBiddingsByLeverageIndex[_nftID]];

        // 1. Verify msg.sender isn't the seller
        require(msg.sender != b.seller, "You can't bid for your own NFT.");

        // 2. Verify the bid hasn't ended
        require(block.number < b.start.add(b.duration), "Bidding has already ended.");

        // 3. Verify the bid is higher than the min bid
        require(msg.value >= b.minBid && msg.value > b.highestBid, "You must bid higher than the min bid and the highest bid.");

        // 4. If there was already a bid, send back the ETH to old bidder
        if(b.highestBidder != address(0) && b.highestBid != 0){
            b.highestBidder.transfer(b.highestBidMinusFees);
        }

        // 5. Set highest bidder & highest bid & add 1 to bid count
        // 5% in fees go to GFARM2 stakers in the pool
        uint fee = msg.value.mul(BID_FEE_P).div(100);
        b.highestBid = msg.value;
        b.highestBidMinusFees = msg.value.sub(fee);
        b.highestBidder = msg.sender;
        b.bidCount = b.bidCount.add(1);

        // 6. Update pool accETHperGFARM
        uint tokenBalance = token.balanceOf(address(this));
        if(tokenBalance > 0){
            accETHperGFARM = accETHperGFARM.add(
                fee.mul(1e18).div(tokenBalance)
            );
        }

        // 7. Store stats
        totalEthDistributed = totalEthDistributed.add(fee);

        // 8. Emit event
        emit NewBid(
            b.seller,
            msg.sender,
            _nftID,
            leverage,
            msg.value
        );
    }

    // 3. Claim an NFT after the bid has ended
    function claim(uint _nftID) external notContract listed(_nftID){
        Bidding memory b = biddingsByLeverage[nft.idToLeverage(_nftID)][idToBiddingsByLeverageIndex[_nftID]];

        // 1. Verify the bid has ended
        require(block.number >= b.start.add(b.duration), "Bidding hasn't ended.");

        // 2. Verify there was at least one bid
        require(b.bidCount > 0, "No bid was made.");

        // 3. Transfer the ETH to the seller (25% fee)
        uint fee = b.highestBidMinusFees.mul(HIGHEST_BID_FEE_P).div(100);
        b.seller.transfer(b.highestBidMinusFees.sub(fee));

        // 4. Transfer the NFT to the highest bidder
        nft.transferFrom(address(this), b.highestBidder, _nftID);

        // 5. Remove the bidding
        removeBidding(_nftID);

        // 6. Pool
        uint tokenBalance = token.balanceOf(address(this));
        if(tokenBalance > 0){
            accETHperGFARM = accETHperGFARM.add(
                fee.mul(1e18).div(tokenBalance)
            );
        }

        // 7. Store stats
        totalEthDistributed = totalEthDistributed.add(fee);
    }

    // 4. Claim unsold NFT after bidding ends
    function claimBack(uint _nftID) external notContract listed(_nftID){
        Bidding memory b = biddingsByLeverage[nft.idToLeverage(_nftID)][idToBiddingsByLeverageIndex[_nftID]];

        // 1. Verify there was 0 bid
        require(b.bidCount == 0, "Bids were made.");

        // 2. Verify the bid has ended
        require(block.number >= b.start.add(b.duration), "Bidding hasn't ended.");

        // 3. Send the NFT back to the seller
        nft.transferFrom(address(this), b.seller, _nftID);

        // 4. Remove the bidding
        removeBidding(_nftID);
    }

    // 5. Biddings count for a leverage
    function biddingsCountLeverage(uint _leverage) external view returns(uint){
        return biddingsByLeverage[_leverage].length;
    }

    // PUBLIC FUNCTIONS (EXCHANGE)

    function isListed(uint _nftID) public view returns(bool){
        return biddingsByLeverage[nft.idToLeverage(_nftID)].length > 0 &&
        biddingsByLeverage[nft.idToLeverage(_nftID)][idToBiddingsByLeverageIndex[_nftID]].nftID == _nftID;
    }

    // PUBLIC FUNCTIONS (POOL)

    // 1. Harvest pending ETH rewards
    function harvest() public notContract{
        uint pending = pendingReward();
        if(pending == 0){ return; }

        // 1. Send pending rewards
        msg.sender.transfer(pendingReward());

        // 2. Update debt
        User storage u = users[msg.sender];
        u.debt = u.provided.mul(accETHperGFARM).div(1e18);
    }

    // 2. Get ETH pending reward
    function pendingReward() view public returns(uint){
        User memory u = users[msg.sender];
        return u.provided.mul(accETHperGFARM).div(1e18).sub(u.debt);
    }

    // EXTERNAL FUNCTIONS (POOL)

    // 1. Stake GFARM2 + harvest
    function stake(uint amount) external notContract{
        // 1. Transfer the GFARM2 to the contract
        token.transferFrom(msg.sender, address(this), amount);

        // 2. Harvest pending rewards
        uint pending = pendingReward();
        if(pending > 0){ msg.sender.transfer(pendingReward()); }

        // 3. Set user provided & debt
        User storage u = users[msg.sender];
        u.provided = u.provided.add(amount);
        u.debt = u.provided.mul(accETHperGFARM).div(1e18);
    }

    // 2. Unstake GFARM2 + harvest
    function unstake(uint amount) external notContract{
        // 1. Verify he doesn't unstake more than provided
        User storage u = users[msg.sender];
        require(amount <= u.provided, "Unstaking more than provided.");

        // 2. Harvest pending rewards
        uint pending = pendingReward();
        if(pending > 0){ msg.sender.transfer(pendingReward()); }

        // 3. Set user provided & debt
        u.provided = u.provided.sub(amount);
        u.debt = u.provided.mul(accETHperGFARM).div(1e18);

        // 4. Transfer the GFARM2 to the address
        token.transfer(msg.sender, amount);

        // 5. Burn 10%
        token.burn(msg.sender, amount.mul(10).div(100));
    }

    // USEFUL FRONT-END FUNCTIONS

    // 1. ETH/USD price (1e5)
    function getEthPrice() private view returns(uint){
        (uint112 reserves0, uint112 reserves1, ) = ETH_USDC_PAIR.getReserves();
        uint reserveUSDC;
        uint reserveETH;

        if(WETH == ETH_USDC_PAIR.token0()){
            reserveETH = reserves0;
            reserveUSDC = reserves1;
        }else{
            reserveUSDC = reserves0;
            reserveETH = reserves1;
        }
        // Divide number of USDC by number of ETH
        // we multiply by 1e12 because USDC only has 6 decimals
        return reserveUSDC.mul(1e12).mul(1e5).div(reserveETH);
    }

    // 2. GFARM/ETH price (1e5)
    function getGFarmPriceEth() private view returns(uint){
        (uint112 reserves0, uint112 reserves1, ) = lp.getReserves();

        uint reserveETH;
        uint reserveGFARM;

        if(WETH == lp.token0()){
            reserveETH = reserves0;
            reserveGFARM = reserves1;
        }else{
            reserveGFARM = reserves0;
            reserveETH = reserves1;
        }

        return reserveETH.mul(1e5).div(reserveGFARM);
    }

    // TVL of GFARM2 staked in $ (1e5)
    function TVL() external view returns(uint){
        return token.balanceOf(address(this)).mul(getGFarmPriceEth()).mul(getEthPrice()).div(1e23);
    }

}

Please enter a contract address above to load the contract details and source code.

Context size (optional):