contract LexGrow is LexDAORole {  
    using SafeMath for uint256;
    
    // $DAI details:
    address private daiAddress = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    IERC20 public dai = IERC20(daiAddress);
    
    // $CHAI details:
    address private chaiAddress = 0x06AF07097C9Eeb7fD685c692751D5C66dB49c215;
    IChai public chai = IChai(chaiAddress);
    
    // <$> LXG <$> details:
    address private vault = address(this);
    address payable public manager;
    uint8 public version = 1;
    uint256 public escrowFee;
    uint256 public lxg; // index for registered LexGrow
    string public emoji = "⚖️🌱⚔️";
    mapping (uint256 => Escrow) public escrow; 

    struct Escrow {  
        address client; 
        address provider;
        uint256 payment;
        uint256 wrap;
        uint256 termination;
        uint256 index;
        string details; 
        bool disputed; 
        bool released;
    }
    	
    // LXG Contract Events:
    event Registered(address indexed client, address indexed provider, uint256 indexed index);  
    event Released(uint256 indexed index); 
    event Disputed(uint256 indexed index, string indexed details); 
    event Resolved(uint256 indexed index, string indexed details); 
    event ManagerTransferred(address indexed manager, string indexed details);
    
    constructor () public {
        dai.approve(chaiAddress, uint(-1));
        manager = msg.sender;
        escrowFee = 0;
    } 
    
    /***************
    ESCROW FUNCTIONS
    ***************/
    function register( // register $DAI locker with DSR via $CHAI; arbitration via lexDAO
        address provider,
        uint256 payment, 
        uint256 termination,
        string memory details) public payable {
        require(msg.value == escrowFee);
	    uint256 index = lxg.add(1); 
	    lxg = lxg.add(1);
	    
	    dai.transferFrom(msg.sender, vault, payment); // deposit $DAI
        uint256 balance = chai.balanceOf(vault);
        chai.join(vault, payment); // wrap into $CHAI and store in vault
                
            escrow[index] = Escrow( 
                msg.sender, 
                provider,
                payment, 
                chai.balanceOf(vault).sub(balance),
                termination,
                index,
                details, 
                false, 
                false);
        
        address(manager).transfer(msg.value);
        
        emit Registered(msg.sender, provider, index); 
    }
    
    function release(uint256 index) public { 
    	Escrow storage escr = escrow[index];
	    require(escr.disputed == false); // program safety check / status
    	require(now <= escr.termination); // program safety check / time
    	require(msg.sender == escr.client); // program safety check / authorization

    	chai.transfer(escr.provider, escr.wrap); 
        
        escr.released = true; 
        
	    emit Released(index); 
    }
    
    function withdraw(uint256 index) public { // client can withdraw $CHAI if termination time passes
    	Escrow storage escr = escrow[index];
        require(escr.disputed == false); // program safety check / status
    	require(now >= escr.termination); // program safety check / time
    	require(msg.sender == escr.client); // program safety check / authorization
        
    	chai.transfer(escr.client, escr.wrap); 
        
        escr.released = true; 
        
	    emit Released(index); 
    }
    
    /***************
    ARBITRATION FUNCTIONS
    ***************/
    function dispute(uint256 index, string memory details) public {
        Escrow storage escr = escrow[index]; 
        require(escr.released == false); // program safety check / status
        require(now <= escr.termination); // program safety check / time
        require(msg.sender == escr.client || msg.sender == escr.provider); // program safety check / authorization

	    escr.disputed = true; 
	    
	    emit Disputed(index, details);
    }
    
    function resolve(uint256 index, uint256 clientAward, uint256 providerAward, string memory details) public onlyLexDAO {
        Escrow storage escr = escrow[index];
	    uint256 lexFee = escr.wrap.div(20); // calculates 5% lexDAO resolution fee
	    require(escr.disputed == true); // program safety check / status
	    require(clientAward.add(providerAward) == escr.wrap.sub(lexFee)); // program safety check / economics
        require(msg.sender != escr.client || msg.sender != escr.provider); // program safety check / authorization  
        
        chai.transfer(escr.client, clientAward); 
        chai.transfer(escr.provider, providerAward); 
    	chai.transfer(msg.sender, lexFee); 
    	
	    escr.released = true; 
	    
	    emit Resolved(index, details);
    }
    
    /***************
    MGMT FUNCTIONS
    ***************/
    function newEscrowFee(uint256 weiAmount) public {
        require(msg.sender == manager);
        escrowFee = weiAmount;
    }
    
    function transferManager(address payable newManager, string memory details) public {
        require(msg.sender == manager);
        manager = newManager; 
        
        emit ManagerTransferred(manager, details);
    }
}
/***************
 LXG CONTRACT
 ***************/
function newEscrowFee(uint256 weiAmount) public {
    require(msg.sender == manager);
    escrowFee = weiAmount;
}

/***************
 MGMT FUNCTIONS
 ***************/