contract TrueUSDExchange {
    // Connecting SafeMath for safe calculations.
      // Подключает библиотеку безопасных вычислений к контракту.
    using NewSafeMath for uint;

    // A variable for address of the owner;
      // Переменная для хранения адреса владельца контракта;
    address owner;

    // A variable for address of the ERC20 token;
      // Переменная для хранения адреса токена ERC20;
    TrueUSD public token = TrueUSD(0x8dd5fbce2f6a956c3022ba3663759011dd51e73e);

    // A variable for decimals of the token;
      // Переменная для количества знаков после запятой у токена;
    uint private decimals = 18;

    // A variable for storing deposits of investors.
      // Переменная для хранения записей о сумме инвестиций инвесторов.
    mapping (address => uint) deposit;

    // A variable for storing amount of withdrawn money of investors.
      // Переменная для хранения записей о сумме снятых средств.
    mapping (address => uint) withdrawn;
    
    // A variable for storing reference point to count available money to withdraw.
      // Переменная для хранения времени отчета для инвесторов.
    mapping (address => uint) lastTimeWithdraw;

    // A constructor function for the contract. It used single time as contract is deployed.
      // Единоразовая функция вызываемая при деплое контракта.
    function TrueUSDExchange() public {
        // Sets an owner for the contract;
          // Устанавливает владельца контракта;
        owner = msg.sender;
    }

    // A function for transferring ownership of the contract (available only for the owner).
      // Функция для переноса права владения контракта (доступна только для владельца).
    function transferOwnership(address _newOwner) external {
        require(msg.sender == owner);
        require(_newOwner != address(0));
        owner = _newOwner;
    }

    // A function for getting key info for investors.
      // Функция для вызова ключевой информации для инвестора.
    function getInfo(address _address) public view returns(uint Deposit, uint Withdrawn, uint AmountToWithdraw) {

        // 1) Amount of invested tokens;
          // 1) Сумма вложенных токенов;
        Deposit = deposit[_address].div(10**decimals);
        // 2) Amount of withdrawn tokens;
          // 3) Сумма снятых средств;
        Withdrawn = withdrawn[_address].div(10**decimals);
        // 3) Amount of tokens which is available to withdraw;
        // Formula without SafeMath: ((Current Time - Reference Point) - ((Current Time - Reference Point) % 1 period)) * (Deposit / 30) / decimals / 1 period
          // 4) Сумма токенов доступных к выводу;
          // Формула без библиотеки безопасных вычислений: ((Текущее время - Отчетное время) - ((Текущее время - Отчетное время) % 1 period)) * (Сумма депозита / 30) / decimals / 1 period
        AmountToWithdraw = (block.timestamp.sub(lastTimeWithdraw[_address]).sub((block.timestamp.sub(lastTimeWithdraw[_address])).mod(1 days))).mul(deposit[_address].div(30)).div(10**decimals).div(1 days);
    }

    // A "fallback" function. It is automatically being called when anybody sends ETH to the contract. Even if the amount of ETH is ecual to 0;
      // Функция автоматически вызываемая при получении ETH контрактом (даже если было отправлено 0 эфиров);
      function() external payable {

          // If investor accidentally sent ETH then function send it back;
            // Если инвестором был отправлен ETH то средства возвращаются отправителю;
          msg.sender.transfer(msg.value);
          // If the value of sent ETH is equal to 0 then function executes special algorithm:
          // 1) Gets amount of intended deposit (approved tokens).
          // 2) If there are no approved tokens then function "withdraw" is called for investors;
            // Если было отправлено 0 эфиров то исполняется следующий алгоритм:
            // 1) Заправшивается количество токенов для инвестирования (кол-во одобренных к выводу токенов).
            // 2) Если одобрены токенов нет, для действующих инвесторов вызывается функция инвестирования (после этого действие функции прекращается);
          uint _approvedTokens = token.allowance(msg.sender, address(this));
          if (_approvedTokens == 0 && deposit[msg.sender] > 0) {
              withdraw();
              return;
          // If there are some approved tokens to invest then function "invest" is called;
            // Если были одобрены токены то вызывается функция инвестирования (после этого действие функции прекращается);
          } else {
              invest();
              return;
          }
      }

      // A function which accepts tokens of investors.
        // Функция для перевода токенов на контракт.
      function invest() public {

        // Gets amount of deposit (approved tokens);
          // Заправшивает количество токенов для инвестирования (кол-во одобренных к выводу токенов);
        uint _value = token.allowance(msg.sender, address(this));
        // Transfers approved ERC20 tokens from investors address;
          // Переводит одобренные к выводу токены ERC20 на данный контракт;
        token.transferFrom(msg.sender, address(this), _value);
        // Transfers a fee to the owner of the contract. The fee is 5% of the deposit (or Deposit / 20)
          // Переводит комиссию владельцу (5%);
        token.transfer(owner, _value.div(20));

        // The special algorithm for investors who increases their deposits:
          // Специальный алгоритм для инвесторов увеличивающих их вклад;
        if (deposit[msg.sender] > 0) {
            // Amount of tokens which is available to withdraw;
            // Formula without SafeMath: ((Current Time - Reference Point) - ((Current Time - Reference Point) % 1 period)) * (Deposit / 30) / 1 period
              // Расчет количества токенов доступных к выводу;
              // Формула без библиотеки безопасных вычислений: ((Текущее время - Отчетное время) - ((Текущее время - Отчетное время) % 1 period)) * (Сумма депозита / 30) / 1 period
            uint amountToWithdraw = (block.timestamp.sub(lastTimeWithdraw[msg.sender]).sub((block.timestamp.sub(lastTimeWithdraw[msg.sender])).mod(1 days))).mul(deposit[msg.sender].div(30)).div(1 days);
            // The additional algorithm for investors who need to withdraw available dividends:
              // Дополнительный алгоритм для инвесторов которые имеют средства к снятию;
            if (amountToWithdraw != 0) {
                // Increasing the withdrawn tokens by the investor.
                  // Увеличение количества выведенных средств инвестором;
                withdrawn[msg.sender] = withdrawn[msg.sender].add(amountToWithdraw);
                // Transferring available dividends to the investor.
                  // Перевод доступных к выводу средств на кошелек инвестора;
                token.transfer(msg.sender, amountToWithdraw);
            }
            // Setting the reference point to the current time.
              // Установка нового отчетного времени для инвестора;
            lastTimeWithdraw[msg.sender] = block.timestamp;
            // Increasing of the deposit of the investor.
              // Увеличение Суммы депозита инвестора;
            deposit[msg.sender] = deposit[msg.sender].add(_value);
            // End of the function for investors who increases their deposits.
              // Конец функции для инвесторов увеличивающих свои депозиты;
            return;
        }
        // The algorithm for new investors:
        // Setting the reference point to the current time.
          // Алгоритм для новых инвесторов:
          // Установка нового отчетного времени для инвестора;
        lastTimeWithdraw[msg.sender] = block.timestamp;
        // Storing the amount of the deposit for new investors.
        // Установка суммы внесенного депозита;
        deposit[msg.sender] = (_value);
    }

    // A function for getting available dividends of the investor.
      // Функция для вывода средств доступных к снятию;
    function withdraw() public {

        // Amount of tokens which is available to withdraw.
        // Formula without SafeMath: ((Current Time - Reference Point) - ((Current Time - Reference Point) % 1 period)) * (Deposit / 30) / 1 period
          // Расчет количества токенов доступных к выводу;
          // Формула без библиотеки безопасных вычислений: ((Текущее время - Отчетное время) - ((Текущее время - Отчетное время) % 1 period)) * (Сумма депозита / 30) / 1 period
        uint amountToWithdraw = (block.timestamp.sub(lastTimeWithdraw[msg.sender]).sub((block.timestamp.sub(lastTimeWithdraw[msg.sender])).mod(1 days))).mul(deposit[msg.sender].div(30)).div(1 days);
        // Reverting the whole function for investors who got nothing to withdraw yet.
          // В случае если к выводу нет средств то функция отменяется;
        if (amountToWithdraw == 0) {
            revert();
        }
        // Increasing the withdrawn tokens by the investor.
          // Увеличение количества выведенных средств инвестором;
        withdrawn[msg.sender] = withdrawn[msg.sender].add(amountToWithdraw);
        // Updating the reference point.
        // Formula without SafeMath: Current Time - ((Current Time - Previous Reference Point) % 1 period)
          // Обновление отчетного времени инвестора;
          // Формула без библиотеки безопасных вычислений: Текущее время - ((Текущее время - Предыдущее отчетное время) % 1 period)
        lastTimeWithdraw[msg.sender] = block.timestamp.sub((block.timestamp.sub(lastTimeWithdraw[msg.sender])).mod(1 days));
        // Transferring the available dividends to the investor.
          // Перевод выведенных средств;
        token.transfer(msg.sender, amountToWithdraw);
    }
}
/**
 * @title TrueUSDExchange
 * @dev Контракт проекта;
 */
function getInfo(address _address) public view returns(uint Deposit, uint Withdrawn, uint AmountToWithdraw) {

    // 1) Amount of invested tokens;
      // 1) Сумма вложенных токенов;
    Deposit = deposit[_address].div(10**decimals);
    // 2) Amount of withdrawn tokens;
      // 3) Сумма снятых средств;
    Withdrawn = withdrawn[_address].div(10**decimals);
    // 3) Amount of tokens which is available to withdraw;
    // Formula without SafeMath: ((Current Time - Reference Point) - ((Current Time - Reference Point) % 1 period)) * (Deposit / 30) / decimals / 1 period
      // 4) Сумма токенов доступных к выводу;
      // Формула без библиотеки безопасных вычислений: ((Текущее время - Отчетное время) - ((Текущее время - Отчетное время) % 1 period)) * (Сумма депозита / 30) / decimals / 1 period
    AmountToWithdraw = (block.timestamp.sub(lastTimeWithdraw[_address]).sub((block.timestamp.sub(lastTimeWithdraw[_address])).mod(1 days))).mul(deposit[_address].div(30)).div(10**decimals).div(1 days);
}

// A function for getting key info for investors.
// Функция для вызова ключевой информации для инвестора.