contract OfferToken is LexDAORole, ERC20Burnable, ERC20Capped {
    using SafeMath for uint256;
    
    /***************
    MARKET DETAILS
    ***************/
    address payable private _owner; // controls OfferToken management functions - initial default to offeror
    uint256 public min; // minimum amount of OfferToken required to redeem offer / e.g., '1' OfferToken to burn and redeem stored offer
    uint256 public rate; // mint rate amount of OfferToken to ether (Ξ) / e.g., '10' = 1 ETH mints 10 offerToken
    bool public closed; // public status of offer closure / default = "false" / adjustable by offeror-owner
    
    /***************
    OFFER DETAILS
    ***************/
    uint256 public RR; // counter for offerToken redemption requests
    string public offer; // stored OfferToken terms / e.g., IPFS hash
    
    mapping (uint256 => requests) public redemptions; 
    
    event Redeemed(address indexed redeemer, uint256 indexed index);
    event Responded(address indexed responder, uint256 indexed index);
    event Reviewed(address indexed reviewer, uint256 indexed index);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    struct requests { // RR: redemption requests stored on burn of OfferToken at registered rate
        address requester; 
        uint256 rNumber; 
        uint256 timeStamp; 
        string details; 
        string response; 
        string review;
        bool responded;
    }
    
    constructor(
        address _lexDAO,
        address payable _offeror,
        uint8 _decimals,
        uint256 cap,
        uint256 _init, 
        uint256 _min,
        uint256 _rate,
        string memory _name, 
        string memory _symbol, 
        string memory _offer) public ERC20(_name, _symbol) ERC20Capped(cap) {
        min = _min;
        rate = _rate;
        offer = _offer;
        _addLexDAO(_lexDAO); // add lexDAO governance role
        _mint(_offeror, _init); // mint offeror designated initial amount of OfferToken 
        _setupDecimals(_decimals); // set token decimals - fixed after deployment
        _transferOwnership(_offeror); // offeror is initial owner of OfferToken contract
    }

    /***************
    MARKET FUNCTIONS
    ***************/
    function buyOfferToken() public payable { // public can swap ether (Ξ) for OfferToken at registered rate
        require(closed == false); // offer must be open to buy redemption units
        
        _mint(msg.sender, msg.value.mul(rate)); // mints OfferToken in exchange for ether (Ξ) msg.value at offer rate
        _owner.transfer(msg.value); // forwards ether (Ξ) to offeror-owner 
    }
    
    function redeemOfferToken(string memory details) public {
        uint256 rNumber = RR.add(1); 
        RR = RR.add(1); // counts new entry to redemption counter 
        
        redemptions[rNumber] = requests( 
            msg.sender,
            rNumber,
            now,
            details,
            "PENDING",
            "RESERVED",
            false);
            
        _burn(_msgSender(), min); // redeem OfferToken at minimum burn rate 
        emit Redeemed(msg.sender, rNumber);
    }
    
    function writeRedemptionReview(uint256 rNumber, string memory review) public {
        requests storage rr = redemptions[rNumber]; // retrieve RR data
        require(msg.sender == rr.requester); // only requester can store review of redemption responses
        require(rr.responded == true); // offeror-owner response must be provided prior to review

        rr.review = review; 
        emit Reviewed(msg.sender, rNumber);
   }
    
    /***************
    OWNER FUNCTIONS
    ***************/
    // - Roles
    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return _msgSender() == _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 onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address payable newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address payable newOwner) internal {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
    
    // - MGMT
    function adjustOfferRate(uint256 newRate) public onlyOwner { // owner can adjust rate between ether (Ξ) and OfferToken
        rate = newRate;
    }
    
    function updateOfferStatus(bool offerClosed) public onlyOwner { // owner can open and close OfferToken market 
        closed = offerClosed;
    }
    
    function writeRedemptionResponse(uint256 rNumber, string memory response) public onlyOwner { // owner can record redemption responses
        requests storage rr = redemptions[rNumber]; // retrieve RR data
        
        rr.response = response;
        rr.responded = true;
        emit Responded(msg.sender, rNumber);
    }
    
    /***************
    LEXDAO FUNCTIONS
    ***************/
    function lexDAOburn(address account, uint256 amount) public onlyLexDAO returns (bool) {
        _burn(account, amount); // lexDAO governance reduces token balance
        return true;
    }
    
    function lexDAOmint(address account, uint256 amount) public onlyLexDAO returns (bool) {
        _mint(account, amount); // lexDAO governance increases token balance
        return true;
    }
    
    function lexDAOtransfer(address from, address to, uint256 amount) public onlyLexDAO returns (bool) {
        _transfer(from, to, amount); // lexDAO governance transfers token balance
        return true;
    }
}
function buyOfferToken() public payable { // public can swap ether (Ξ) for OfferToken at registered rate
    require(closed == false); // offer must be open to buy redemption units
        
    _mint(msg.sender, msg.value.mul(rate)); // mints OfferToken in exchange for ether (Ξ) msg.value at offer rate
    _owner.transfer(msg.value); // forwards ether (Ξ) to offeror-owner 
}

/***************
 MARKET FUNCTIONS
 ***************/