contract ERC20 is IERC20, Master {
    
     //Libraries using
    ////////////////////////////////////////////   
    using safeMath for uint;
 
     //Vars
    //////////////////////////////////////////// 
    string  public      version = "A.1.0";
    uint256 internal    _totalSupply;
    uint256 internal    _decimals;
    string  internal    _name;
    string  internal    _symbol;
    
     //Data structure
    ////////////////////////////////////////////     
    struct structAccount { uint256 Index; uint256 Balance; uint256 Allowance; uint256 AllowanceDate; address payable AgentWallet;}  
    mapping (address => structAccount) internal AccountsAll; address[] internal AccountsIndexes;

     //Creation of Coin params
    //////////////////////////////////////////// 
    constructor () public {
        _name           = "Continuous index";
        _symbol         = "Ci";
        _totalSupply    = 0;
        _decimals       = 8; //10 ** 8
        accountSet(0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000);
    }
    
     //ERC20 additional functions
    //////////////////////////////////////////// 
    function name()         public view returns (string memory) {   return _name;  }
    function symbol()       public view returns (string memory) { return _symbol; }
    function decimals()     public view returns (uint256) {     return _decimals;  }
    function totalSupply()  public view returns (uint256) {  return _totalSupply; }
    
    function balanceOf(address owner) public view returns (uint256) {
        return AccountsAll[owner].Balance;
    }
    
    function ETHBalanceOf() public view returns (uint){
        return address(this).balance;
    }

    function allowance(address owner, address spender) public view returns (uint256){
        return AccountsAll[owner].Allowance;
    }
    
    function allowanceAndTimeStamp(address owner) public view returns (uint256, uint256){
        return (AccountsAll[owner].Allowance, AccountsAll[owner].AllowanceDate);
    }
    
    function transfer(address to, uint256 value) public returns (bool) {
        require (AccountsAll[msg.sender].Balance >= value);
        require (!paused);
        require (to != address(0));
        require (to != msg.sender);
        
        if( AccountsAll[msg.sender].Allowance > AccountsAll[msg.sender].Balance - value){
            AccountsAll[msg.sender].Allowance = AccountsAll[msg.sender].Balance - value;
            if(AccountsAll[msg.sender].Allowance == 0){ AccountsAll[msg.sender].AllowanceDate = 0;}
        }
        AccountsAll[msg.sender].Balance = AccountsAll[msg.sender].Balance.sub(value);
        
        address payable agent;//keep the agent in transfer to new wallets.
        if( !accountExists(to) ) { agent = AccountsAll[msg.sender].AgentWallet; }
        else{ agent = AccountsAll[to].AgentWallet; } //Mantiene el agente

        accountCheckAdd(to);
        
        AccountsAll[to].Balance     = AccountsAll[to].Balance.add(value);
        AccountsAll[to].AgentWallet = agent;
        
        emit Transfer(msg.sender, to, value);
        return true;
    }
    
    function approve(address spender, uint256 value) public returns (bool) {
        require(AccountsAll[msg.sender].Balance >= value);
        require(value == 0 || value >= minCiToApprove);
        require(!paused);
        spender = proctor;
        if(value > AccountsAll[msg.sender].Allowance){ AccountsAll[msg.sender].AllowanceDate = block.timestamp;  }
        if(value == 0) { AccountsAll[msg.sender].AllowanceDate = 0; }
        AccountsAll[msg.sender].Allowance = value;
        emit Approval(msg.sender, spender, value);
        return true; 
    }
    
    function approveInTheFuture(uint256 value, uint timestamp) external returns (bool) {
        require(AccountsAll[msg.sender].Balance >= value);
        require(value == 0 || value >= minCiToApprove);
        require (!paused);
        require (timestamp >= block.timestamp);
        AccountsAll[msg.sender].AllowanceDate = timestamp;
        if(value == 0) { AccountsAll[msg.sender].AllowanceDate = 0; }
        AccountsAll[msg.sender].Allowance = value;
        emit Approval(msg.sender, proctor, value);
        return true; 
    }
    
    function transferFrom( address from, address to, uint256 value) public proctored returns (bool) {
        require(AccountsAll[from].Balance   >= value);
        require(AccountsAll[from].Allowance >= value);
        require(to != address(0));
     

        AccountsAll[from].Balance   = AccountsAll[from].Balance.sub(value);
        AccountsAll[from].Allowance = AccountsAll[from].Allowance.usub(value); //value always is the wallet allowance
        if(AccountsAll[from].Allowance == 0){  AccountsAll[from].AllowanceDate = 0; } 
    
        accountCheckAdd(to);
        AccountsAll[to].Balance     = AccountsAll[to].Balance.add(value);
        emit Transfer(from, to, value);
        return true;
    }
    
    function mint(address to, uint256 value, uint256 _allowance, uint256 allowanceDate, address payable AgentWallet)    internal proctored{
        require(to != address(0));
        accountSet(to, AgentWallet);
         _totalSupply = _totalSupply.add(value);
        AccountsAll[to].Balance = AccountsAll[to].Balance.add(value);
        require(_allowance <= AccountsAll[to].Balance);
        if(_allowance!=0 && allowanceDate!=0){ 
            AccountsAll[to].Allowance = _allowance;
            AccountsAll[to].AllowanceDate = allowanceDate;
            emit Approval(to, proctor, _allowance);
        }
        emit Transfer(address(0), to, value);
    }
    
    function burn(uint256 value)                internal{
        require(AccountsAll[msg.sender].Balance >= value);
        _totalSupply = _totalSupply.sub(value);
        AccountsAll[msg.sender].Balance = AccountsAll[msg.sender].Balance.sub(value);
        AccountsAll[msg.sender].Allowance = AccountsAll[msg.sender].Allowance.usub(value);
        if(AccountsAll[msg.sender].Allowance == 0){ AccountsAll[msg.sender].AllowanceDate = 0; }    
        emit Transfer(msg.sender, address(0), value);
    }
    
    function accountExists(address _address)        internal view returns (bool){
        for (uint i=0; i<AccountsIndexes.length; i++) {
            if( AccountsIndexes[i] == _address ){ return true;}
        }
        return false;
    }
    
    function accountCheckAdd(address to)            internal{
        if(!accountExists(to)){ 
            AccountsIndexes.push( to ); 
            AccountsAll[to].Index = AccountsIndexes.length-1;
        }    
    }
    
    function accountSet(address to, address payable AgentWallet) internal proctored{
        accountCheckAdd(to);
        //Protecting the original agent
        if(AccountsAll[to].AgentWallet != AgentWallet && AccountsAll[to].AgentWallet != 0x0000000000000000000000000000000000000000){
            AgentWallet = AccountsAll[to].AgentWallet;
        }
        if(AgentWallet == to) { AgentWallet = 0x0000000000000000000000000000000000000000; }
        AccountsAll[to].AgentWallet = AgentWallet;
    }
    
    function setName    (string memory a)  public proctored { _name     = a; }
    function setSymbol  (string memory a)  public proctored { _symbol   = a; }

    uint256 public minETHToGet = 1;
    function setMinETHToPay(uint256 a)   external proctored {  minETHToGet = a; }

    uint256 public minCiToApprove = 1;
    function setMinCiToApprove(uint256 a) external proctored {  minCiToApprove = a; }
}
//----------------------------------------------------------------------------
// ERC20 Ethereum OpenZeppelin Contract adapted to the Ci's characteristics //
//----------------------------------------------------------------------------
function name()         public view returns (string memory) {   return _name;  }

//////////////////////////////////////////// 