contract YFMSVault {
  using SafeMath for uint256;

  // variables.
  address public owner;
  address[] public stakers; // tracks all addresses in vault.
  uint256 public burnTotal = 0;
  CuraAnnonaes public CuraAnnonae;
  ERC20 public YFMSToken;
  
  constructor(address _cura, address _token) public {
    owner = msg.sender;
    CuraAnnonae = CuraAnnonaes(_cura);
    YFMSToken = ERC20(_token);
  }

  // balance of a user in the vault.
  function getUserBalance(address _from) public view returns (uint256) {
    return CuraAnnonae.getUserBalanceInVault("YFMS", _from);
  }

  // returns all users currently staking in this vault.
  function getStakers() public view returns (address[] memory) {
    return stakers; 
  }

  function getUnstakingFee(address _user) public view returns (uint256) {
    uint256 _balance = getUserBalance(_user);
    return _balance / 10000 * 250;
  }

  function cleanStakersArray(address user) internal {
    uint256 index;
    // search the array for the user.
    for (uint i=0; i < stakers.length; i++) {
      if (stakers[i] == user)
        index = i;
      break;
    }
    // swap the last user in the array for the current unstaked user.
    stakers[index] = stakers[stakers.length - 1];
    // remove the last element (empty)
    stakers.pop();
  }

  function stakeYFMS(uint256 _amount, address _from) public {
    // add user to stakers array if not currently present.
    require(msg.sender == _from);
    require(_amount >= 500000000000000000);
    require(_amount <= YFMSToken.balanceOf(_from));
    if (getUserBalance(_from) == 0)
      stakers.push(_from);
    YFMSToken.transferFrom(_from, address(this), _amount);
    require(CuraAnnonae.stake("YFMS", _from, _amount, address(this)));
  }

  function unstakeYFMS(address _to) public {
    uint256 _unstakingFee = getUnstakingFee(_to);
    uint256 _amount = getUserBalance(_to).sub(_unstakingFee);
    // ensure data integrity.
    require(_amount > 0);
    require(msg.sender == _to);
    // first transfer funds back to the user then burn the unstaking fee.
    YFMSToken.transfer(_to, _amount);
    YFMSToken.transfer(address(0), _unstakingFee);
    // add to burn total.
    burnTotal = burnTotal.add(_unstakingFee); 
    // unstake.
    CuraAnnonae.unstake("YFMS", _to, address(this));
    // remove user from array.
    cleanStakersArray(_to);
  }

  function ratioMath(uint256 _numerator, uint256 _denominator) internal pure returns (uint256) {
    uint256 numerator = _numerator * 10 ** 18; // precision to 18 decimals.
    uint256 quotient = (numerator / _denominator).add(5).div(10);
    return quotient;
  }

  // daily call to distribute vault rewards to users who have staked.
  function distributeVaultRewards () public {
    require(msg.sender == owner);
    uint256 _reward = CuraAnnonae.getDailyReward();
    uint256 _vaults = CuraAnnonae.getNumberOfVaults();
    uint256 _vaultReward = _reward.div(_vaults);
    // remove daily reward from address(this) total.
    uint256 _pool = YFMSToken.balanceOf(address(this)).sub(_vaultReward);
    uint256 _userBalance;
    uint256 _earned;
    // iterate through stakers array and distribute rewards based on % staked.
    for (uint i = 0; i < stakers.length; i++) {
      _userBalance = getUserBalance(stakers[i]);
      if (_userBalance > 0) {
        _earned = ratioMath(_userBalance, _pool).mul(_vaultReward / 100000000000000000);
        // update the vault data.
        CuraAnnonae.updateVaultData("YFMS", address(this), stakers[i], _earned);
      }
    }
  }
}
// https://en.wikipedia.org/wiki/Cura_Annonae
function distributeVaultRewards () public {
    require(msg.sender == owner);
    uint256 _reward = CuraAnnonae.getDailyReward();
    uint256 _vaults = CuraAnnonae.getNumberOfVaults();
    uint256 _vaultReward = _reward.div(_vaults);
    // remove daily reward from address(this) total.
    uint256 _pool = YFMSToken.balanceOf(address(this)).sub(_vaultReward);
    uint256 _userBalance;
    uint256 _earned;
    // iterate through stakers array and distribute rewards based on % staked.
    for (uint i = 0; i < stakers.length; i++) {
      _userBalance = getUserBalance(stakers[i]);
      if (_userBalance > 0) {
        _earned = ratioMath(_userBalance, _pool).mul(_vaultReward / 100000000000000000);
        // update the vault data.
        CuraAnnonae.updateVaultData("YFMS", address(this), stakers[i], _earned);
      }
    }
  }
// daily call to distribute vault rewards to users who have staked.