pragma solidity 0.6.8;
pragma experimental ABIEncoderV2;

contract Initializable {



  /**

   * @dev Indicates that the contract has been initialized.

   */

  bool private initialized;



  /**

   * @dev Indicates that the contract is in the process of being initialized.

   */

  bool private initializing;



  /**

   * @dev Modifier to use in the initializer function of a contract.

   */

  modifier initializer() {

    require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");



    bool isTopLevelCall = !initializing;

    if (isTopLevelCall) {

      initializing = true;

      initialized = true;

    }



    _;



    if (isTopLevelCall) {

      initializing = false;

    }

  }



  /// @dev Returns true if and only if the function is running in the constructor

  function isConstructor() private view returns (bool) {

    // extcodesize checks the size of the code stored in an address, and

    // address returns the current address. Since the code is still not

    // deployed when running a constructor, any checks on its code size will

    // yield zero, making it an effective way to detect if a contract is

    // under construction or not.

    address self = address(this);

    uint256 cs;

    assembly { cs := extcodesize(self) }

    return cs == 0;

  }



  // Reserved storage space to allow for layout changes in the future.

  uint256[50] private ______gap;

}

library Math {

    /**

     * @dev Returns the largest of two numbers.

     */

    function max(uint256 a, uint256 b) internal pure returns (uint256) {

        return a >= b ? a : b;

    }



    /**

     * @dev Returns the smallest of two numbers.

     */

    function min(uint256 a, uint256 b) internal pure returns (uint256) {

        return a < b ? a : b;

    }



    /**

     * @dev Returns the average of two numbers. The result is rounded towards

     * zero.

     */

    function average(uint256 a, uint256 b) internal pure returns (uint256) {

        // (a + b) / 2 can overflow, so we distribute

        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);

    }

}

library SafeMath {

    /**

     * @dev Returns the addition of two unsigned integers, reverting on

     * overflow.

     *

     * Counterpart to Solidity's `+` operator.

     *

     * Requirements:

     * - Addition cannot overflow.

     */

    function add(uint256 a, uint256 b) internal pure returns (uint256) {

        uint256 c = a + b;

        require(c >= a, "SafeMath: addition overflow");



        return c;

    }



    /**

     * @dev Returns the subtraction of two unsigned integers, reverting on

     * overflow (when the result is negative).

     *

     * Counterpart to Solidity's `-` operator.

     *

     * Requirements:

     * - Subtraction cannot overflow.

     */

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {

        return sub(a, b, "SafeMath: subtraction overflow");

    }



    /**

     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on

     * overflow (when the result is negative).

     *

     * Counterpart to Solidity's `-` operator.

     *

     * Requirements:

     * - Subtraction cannot overflow.

     */

    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {

        require(b <= a, errorMessage);

        uint256 c = a - b;



        return c;

    }



    /**

     * @dev Returns the multiplication of two unsigned integers, reverting on

     * overflow.

     *

     * Counterpart to Solidity's `*` operator.

     *

     * Requirements:

     * - Multiplication cannot overflow.

     */

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {

        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the

        // benefit is lost if 'b' is also tested.

        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522

        if (a == 0) {

            return 0;

        }



        uint256 c = a * b;

        require(c / a == b, "SafeMath: multiplication overflow");



        return c;

    }



    /**

     * @dev Returns the integer division of two unsigned integers. Reverts on

     * division by zero. The result is rounded towards zero.

     *

     * Counterpart to Solidity's `/` operator. Note: this function uses a

     * `revert` opcode (which leaves remaining gas untouched) while Solidity

     * uses an invalid opcode to revert (consuming all remaining gas).

     *

     * Requirements:

     * - The divisor cannot be zero.

     */

    function div(uint256 a, uint256 b) internal pure returns (uint256) {

        return div(a, b, "SafeMath: division by zero");

    }



    /**

     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on

     * division by zero. The result is rounded towards zero.

     *

     * Counterpart to Solidity's `/` operator. Note: this function uses a

     * `revert` opcode (which leaves remaining gas untouched) while Solidity

     * uses an invalid opcode to revert (consuming all remaining gas).

     *

     * Requirements:

     * - The divisor cannot be zero.

     */

    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {

        // Solidity only automatically asserts when dividing by 0

        require(b > 0, errorMessage);

        uint256 c = a / b;

        // assert(a == b * c + a % b); // There is no case in which this doesn't hold



        return c;

    }



    /**

     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),

     * Reverts when dividing by zero.

     *

     * Counterpart to Solidity's `%` operator. This function uses a `revert`

     * opcode (which leaves remaining gas untouched) while Solidity uses an

     * invalid opcode to revert (consuming all remaining gas).

     *

     * Requirements:

     * - The divisor cannot be zero.

     */

    function mod(uint256 a, uint256 b) internal pure returns (uint256) {

        return mod(a, b, "SafeMath: modulo by zero");

    }



    /**

     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),

     * Reverts with custom message when dividing by zero.

     *

     * Counterpart to Solidity's `%` operator. This function uses a `revert`

     * opcode (which leaves remaining gas untouched) while Solidity uses an

     * invalid opcode to revert (consuming all remaining gas).

     *

     * Requirements:

     * - The divisor cannot be zero.

     */

    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {

        require(b != 0, errorMessage);

        return a % b;

    }

}

interface IERC20 {

    /**

     * @dev Returns the amount of tokens in existence.

     */

    function totalSupply() external view returns (uint256);



    /**

     * @dev Returns the amount of tokens owned by `account`.

     */

    function balanceOf(address account) external view returns (uint256);



    /**

     * @dev Moves `amount` tokens from the caller's account to `recipient`.

     *

     * Returns a boolean value indicating whether the operation succeeded.

     *

     * Emits a {Transfer} event.

     */

    function transfer(address recipient, uint256 amount) external returns (bool);



    /**

     * @dev Returns the remaining number of tokens that `spender` will be

     * allowed to spend on behalf of `owner` through {transferFrom}. This is

     * zero by default.

     *

     * This value changes when {approve} or {transferFrom} are called.

     */

    function allowance(address owner, address spender) external view returns (uint256);



    /**

     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.

     *

     * Returns a boolean value indicating whether the operation succeeded.

     *

     * IMPORTANT: Beware that changing an allowance with this method brings the risk

     * that someone may use both the old and the new allowance by unfortunate

     * transaction ordering. One possible solution to mitigate this race

     * condition is to first reduce the spender's allowance to 0 and set the

     * desired value afterwards:

     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729

     *

     * Emits an {Approval} event.

     */

    function approve(address spender, uint256 amount) external returns (bool);



    /**

     * @dev Moves `amount` tokens from `sender` to `recipient` using the

     * allowance mechanism. `amount` is then deducted from the caller's

     * allowance.

     *

     * Returns a boolean value indicating whether the operation succeeded.

     *

     * Emits a {Transfer} event.

     */

    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);



    /**

     * @dev Emitted when `value` tokens are moved from one account (`from`) to

     * another (`to`).

     *

     * Note that `value` may be zero.

     */

    event Transfer(address indexed from, address indexed to, uint256 value);



    /**

     * @dev Emitted when the allowance of a `spender` for an `owner` is set by

     * a call to {approve}. `value` is the new allowance.

     */

    event Approval(address indexed owner, address indexed spender, uint256 value);

}

library Address {

    /**

     * @dev Returns true if `account` is a contract.

     *

     * [IMPORTANT]

     * ====

     * It is unsafe to assume that an address for which this function returns

     * false is an externally-owned account (EOA) and not a contract.

     *

     * Among others, `isContract` will return false for the following

     * types of addresses:

     *

     *  - an externally-owned account

     *  - a contract in construction

     *  - an address where a contract will be created

     *  - an address where a contract lived, but was destroyed

     * ====

     */

    function isContract(address account) internal view returns (bool) {

        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts

        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned

        // for accounts without code, i.e. `keccak256('')`

        bytes32 codehash;

        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;

        // solhint-disable-next-line no-inline-assembly

        assembly { codehash := extcodehash(account) }

        return (codehash != accountHash && codehash != 0x0);

    }



    /**

     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to

     * `recipient`, forwarding all available gas and reverting on errors.

     *

     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost

     * of certain opcodes, possibly making contracts go over the 2300 gas limit

     * imposed by `transfer`, making them unable to receive funds via

     * `transfer`. {sendValue} removes this limitation.

     *

     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].

     *

     * IMPORTANT: because control is transferred to `recipient`, care must be

     * taken to not create reentrancy vulnerabilities. Consider using

     * {ReentrancyGuard} or the

     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].

     */

    function sendValue(address payable recipient, uint256 amount) internal {

        require(address(this).balance >= amount, "Address: insufficient balance");



        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value

        (bool success, ) = recipient.call{ value: amount }("");

        require(success, "Address: unable to send value, recipient may have reverted");

    }

}

library Accountant {

  using SafeMath for uint256;



  uint public constant interestDecimals = 1e18;

  uint public constant blocksPerDay = 5760;

  uint public constant blocksPerYear = (blocksPerDay * 365);



  struct PaymentAllocation {

    uint interestPayment;

    uint principalPayment;

    uint additionalBalancePayment;

  }



  function calculateInterestAndPrincipalAccrued(CreditLine cl, uint blockNumber) public view returns(uint, uint) {

    uint totalPayment = calculateAnnuityPayment(cl.balance(), cl.interestApr(), cl.termInDays(), cl.paymentPeriodInDays());

    uint interestAccrued = calculateInterestAccrued(cl, blockNumber);

    uint principalAccrued = calculatePrincipalAccrued(cl, totalPayment, interestAccrued, blockNumber);

    return (interestAccrued, principalAccrued);

  }



  function calculatePrincipalAccrued(CreditLine cl, uint periodPayment, uint interestAccrued, uint blockNumber) public view returns(uint) {

    uint blocksPerPaymentPeriod = blocksPerDay * cl.paymentPeriodInDays();

    // Math.min guards against overflow. See comment in the calculateInterestAccrued for further explanation.

    uint lastUpdatedBlock = Math.min(blockNumber, cl.lastUpdatedBlock());

    uint numBlocksElapsed = blockNumber.sub(lastUpdatedBlock);

    int128 fractionOfPeriod = FPMath.divi(int256(numBlocksElapsed), int256(blocksPerPaymentPeriod));

    uint periodPaymentFraction = uint(FPMath.muli(fractionOfPeriod, int256(periodPayment)));

    return periodPaymentFraction.sub(interestAccrued);

  }



  function calculateInterestAccrued(CreditLine cl, uint blockNumber) public view returns(uint) {

    // We use Math.min here to prevent integer overflow (ie. go negative) when calculating

    // numBlocksElapsed. Typically this shouldn't be possible, because

    // the lastUpdatedBlock couldn't be *after* the current blockNumber. However, when assessing

    // we allow this function to be called with a past block number, which raises the possibility

    // of overflow.

    // This use of min should not generate incorrect interest calculations, since

    // this functions purpose is just to normalize balances, and  will be called any time

    // a balance affecting action takes place (eg. drawdown, repayment, assessment)

    uint lastUpdatedBlock = Math.min(blockNumber, cl.lastUpdatedBlock());



    uint numBlocksElapsed = blockNumber.sub(lastUpdatedBlock);

    uint totalInterestPerYear = (cl.balance().mul(cl.interestApr())).div(interestDecimals);

    return totalInterestPerYear.mul(numBlocksElapsed).div(blocksPerYear);

  }



  function calculateAnnuityPayment(uint balance, uint interestApr, uint termInDays, uint paymentPeriodInDays) public pure returns(uint) {

    /*

    This is the standard amortization formula for an annuity payment amount.

    See: https://en.wikipedia.org/wiki/Amortization_calculator



    The specific formula we're interested in can be expressed as:

    `balance * (periodRate / (1 - (1 / ((1 + periodRate) ^ periods_per_term))))`



    FPMath is a library designed for emulating floating point numbers in solidity.

    At a high level, we are just turning all our uint256 numbers into floating points and

    doing the formula above, and then turning it back into an int64 at the end.

    */





    // Components used in the formula

    uint periodsPerTerm = termInDays / paymentPeriodInDays;

    int128 one = FPMath.fromInt(int256(1));

    int128 annualRate = FPMath.divi(int256(interestApr), int256(interestDecimals));

    int128 dailyRate = FPMath.div(annualRate, FPMath.fromInt(int256(365)));

    int128 periodRate = FPMath.mul(dailyRate, FPMath.fromInt(int256(paymentPeriodInDays)));

    int128 termRate = FPMath.pow(FPMath.add(one, periodRate), periodsPerTerm);



    int128 denominator = FPMath.sub(one, FPMath.div(one, termRate));

    if (denominator == 0) {

      return balance / periodsPerTerm;

    }

    int128 paymentFractionFP = FPMath.div(periodRate, denominator);

    uint paymentFraction = uint(FPMath.muli(paymentFractionFP, int256(1e18)));



    return (balance * paymentFraction) / 1e18;

  }



  function allocatePayment(uint paymentAmount, uint balance, uint interestOwed, uint principalOwed) public pure returns(PaymentAllocation memory) {

    uint paymentRemaining = paymentAmount;

    uint interestPayment = Math.min(interestOwed, paymentRemaining);

    paymentRemaining = paymentRemaining.sub(interestPayment);



    uint principalPayment = Math.min(principalOwed, paymentRemaining);

    paymentRemaining = paymentRemaining.sub(principalPayment);



    uint balanceRemaining = balance.sub(principalPayment);

    uint additionalBalancePayment = Math.min(paymentRemaining, balanceRemaining);



    return PaymentAllocation({

      interestPayment: interestPayment,

      principalPayment: principalPayment,

      additionalBalancePayment: additionalBalancePayment

    });

  }



}

ibrary FPMath {

  /**

  @dev

   * Minimum value signed 64.64-bit fixed point number may have.

   */

  int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;



  /**

  @dev

   * Maximum value signed 64.64-bit fixed point number may have.

   */

  int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;



  /**

   * Convert signed 256-bit integer number into signed 64.64-bit fixed point

   * number.  Revert on overflow.

   *

   * @param x signed 256-bit integer number

   * @return signed 64.64-bit fixed point number

   */

  function fromInt (int256 x) internal pure returns (int128) {

    require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);

    return int128 (x << 64);

  }



  /**

   * Convert signed 64.64 fixed point number into signed 64-bit integer number

   * rounding down.

   *

   * @param x signed 64.64-bit fixed point number

   * @return signed 64-bit integer number

   */

  function toInt (int128 x) internal pure returns (int64) {

    return int64 (x >> 64);

  }



  /**

   * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point

   * number.  Revert on overflow.

   *

   * @param x unsigned 256-bit integer number

   * @return signed 64.64-bit fixed point number

   */

  function fromUInt (uint256 x) internal pure returns (int128) {

    require (x <= 0x7FFFFFFFFFFFFFFF);

    return int128 (x << 64);

  }



  /**

   * Convert signed 64.64 fixed point number into unsigned 64-bit integer

   * number rounding down.  Revert on underflow.

   *

   * @param x signed 64.64-bit fixed point number

   * @return unsigned 64-bit integer number

   */

  function toUInt (int128 x) internal pure returns (uint64) {

    require (x >= 0);

    return uint64 (x >> 64);

  }



  /**

   * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point

   * number rounding down.  Revert on overflow.

   *

   * @param x signed 128.128-bin fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function from128x128 (int256 x) internal pure returns (int128) {

    int256 result = x >> 64;

    require (result >= MIN_64x64 && result <= MAX_64x64);

    return int128 (result);

  }



  /**

   * Convert signed 64.64 fixed point number into signed 128.128 fixed point

   * number.

   *

   * @param x signed 64.64-bit fixed point number

   * @return signed 128.128 fixed point number

   */

  function to128x128 (int128 x) internal pure returns (int256) {

    return int256 (x) << 64;

  }



  /**

   * Calculate x + y.  Revert on overflow.

   *

   * @param x signed 64.64-bit fixed point number

   * @param y signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function add (int128 x, int128 y) internal pure returns (int128) {

    int256 result = int256(x) + y;

    require (result >= MIN_64x64 && result <= MAX_64x64);

    return int128 (result);

  }



  /**

   * Calculate x - y.  Revert on overflow.

   *

   * @param x signed 64.64-bit fixed point number

   * @param y signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function sub (int128 x, int128 y) internal pure returns (int128) {

    int256 result = int256(x) - y;

    require (result >= MIN_64x64 && result <= MAX_64x64);

    return int128 (result);

  }



  /**

   * Calculate x * y rounding down.  Revert on overflow.

   *

   * @param x signed 64.64-bit fixed point number

   * @param y signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function mul (int128 x, int128 y) internal pure returns (int128) {

    int256 result = int256(x) * y >> 64;

    require (result >= MIN_64x64 && result <= MAX_64x64);

    return int128 (result);

  }



  /**

   * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point

   * number and y is signed 256-bit integer number.  Revert on overflow.

   *

   * @param x signed 64.64 fixed point number

   * @param y signed 256-bit integer number

   * @return signed 256-bit integer number

   */

  function muli (int128 x, int256 y) internal pure returns (int256) {

    if (x == MIN_64x64) {

      require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&

        y <= 0x1000000000000000000000000000000000000000000000000);

      return -y << 63;

    } else {

      bool negativeResult = false;

      if (x < 0) {

        x = -x;

        negativeResult = true;

      }

      if (y < 0) {

        y = -y; // We rely on overflow behavior here

        negativeResult = !negativeResult;

      }

      uint256 absoluteResult = mulu (x, uint256 (y));

      if (negativeResult) {

        require (absoluteResult <=

          0x8000000000000000000000000000000000000000000000000000000000000000);

        return -int256 (absoluteResult); // We rely on overflow behavior here

      } else {

        require (absoluteResult <=

          0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

        return int256 (absoluteResult);

      }

    }

  }



  /**

   * Calculate x * y rounding down, where x is signed 64.64 fixed point number

   * and y is unsigned 256-bit integer number.  Revert on overflow.

   *

   * @param x signed 64.64 fixed point number

   * @param y unsigned 256-bit integer number

   * @return unsigned 256-bit integer number

   */

  function mulu (int128 x, uint256 y) internal pure returns (uint256) {

    if (y == 0) return 0;



    require (x >= 0);



    uint256 lo = (uint256 (x) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;

    uint256 hi = uint256 (x) * (y >> 128);



    require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

    hi <<= 64;



    require (hi <=

      0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo);

    return hi + lo;

  }



  /**

   * Calculate x / y rounding towards zero.  Revert on overflow or when y is

   * zero.

   *

   * @param x signed 64.64-bit fixed point number

   * @param y signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function div (int128 x, int128 y) internal pure returns (int128) {

    require (y != 0);

    int256 result = (int256 (x) << 64) / y;

    require (result >= MIN_64x64 && result <= MAX_64x64);

    return int128 (result);

  }



  /**

   * Calculate x / y rounding towards zero, where x and y are signed 256-bit

   * integer numbers.  Revert on overflow or when y is zero.

   *

   * @param x signed 256-bit integer number

   * @param y signed 256-bit integer number

   * @return signed 64.64-bit fixed point number

   */

  function divi (int256 x, int256 y) internal pure returns (int128) {

    require (y != 0);



    bool negativeResult = false;

    if (x < 0) {

      x = -x; // We rely on overflow behavior here

      negativeResult = true;

    }

    if (y < 0) {

      y = -y; // We rely on overflow behavior here

      negativeResult = !negativeResult;

    }

    uint128 absoluteResult = divuu (uint256 (x), uint256 (y));

    if (negativeResult) {

      require (absoluteResult <= 0x80000000000000000000000000000000);

      return -int128 (absoluteResult); // We rely on overflow behavior here

    } else {

      require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

      return int128 (absoluteResult); // We rely on overflow behavior here

    }

  }



  /**

   * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit

   * integer numbers.  Revert on overflow or when y is zero.

   *

   * @param x unsigned 256-bit integer number

   * @param y unsigned 256-bit integer number

   * @return signed 64.64-bit fixed point number

   */

  function divu (uint256 x, uint256 y) internal pure returns (int128) {

    require (y != 0);

    uint128 result = divuu (x, y);

    require (result <= uint128 (MAX_64x64));

    return int128 (result);

  }



  /**

   * Calculate -x.  Revert on overflow.

   *

   * @param x signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function neg (int128 x) internal pure returns (int128) {

    require (x != MIN_64x64);

    return -x;

  }



  /**

   * Calculate |x|.  Revert on overflow.

   *

   * @param x signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function abs (int128 x) internal pure returns (int128) {

    require (x != MIN_64x64);

    return x < 0 ? -x : x;

  }



  /**

   * Calculate 1 / x rounding towards zero.  Revert on overflow or when x is

   * zero.

   *

   * @param x signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function inv (int128 x) internal pure returns (int128) {

    require (x != 0);

    int256 result = int256 (0x100000000000000000000000000000000) / x;

    require (result >= MIN_64x64 && result <= MAX_64x64);

    return int128 (result);

  }



  /**

   * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.

   *

   * @param x signed 64.64-bit fixed point number

   * @param y signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function avg (int128 x, int128 y) internal pure returns (int128) {

    return int128 ((int256 (x) + int256 (y)) >> 1);

  }



  /**

   * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.

   * Revert on overflow or in case x * y is negative.

   *

   * @param x signed 64.64-bit fixed point number

   * @param y signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function gavg (int128 x, int128 y) internal pure returns (int128) {

    int256 m = int256 (x) * int256 (y);

    require (m >= 0);

    require (m <

        0x4000000000000000000000000000000000000000000000000000000000000000);

    return int128 (sqrtu (uint256 (m), uint256 (x) + uint256 (y) >> 1));

  }



  /**

   * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number

   * and y is unsigned 256-bit integer number.  Revert on overflow.

   *

   * @param x signed 64.64-bit fixed point number

   * @param y uint256 value

   * @return signed 64.64-bit fixed point number

   */

  function pow (int128 x, uint256 y) internal pure returns (int128) {

    uint256 absoluteResult;

    bool negativeResult = false;

    if (x >= 0) {

      absoluteResult = powu (uint256 (x) << 63, y);

    } else {

      // We rely on overflow behavior here

      absoluteResult = powu (uint256 (uint128 (-x)) << 63, y);

      negativeResult = y & 1 > 0;

    }



    absoluteResult >>= 63;



    if (negativeResult) {

      require (absoluteResult <= 0x80000000000000000000000000000000);

      return -int128 (absoluteResult); // We rely on overflow behavior here

    } else {

      require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

      return int128 (absoluteResult); // We rely on overflow behavior here

    }

  }



  /**

   * Calculate sqrt (x) rounding down.  Revert if x < 0.

   *

   * @param x signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function sqrt (int128 x) internal pure returns (int128) {

    require (x >= 0);

    return int128 (sqrtu (uint256 (x) << 64, 0x10000000000000000));

  }



  /**

   * Calculate binary logarithm of x.  Revert if x <= 0.

   *

   * @param x signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function log_2 (int128 x) internal pure returns (int128) {

    require (x > 0);



    int256 msb = 0;

    int256 xc = x;

    if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }

    if (xc >= 0x100000000) { xc >>= 32; msb += 32; }

    if (xc >= 0x10000) { xc >>= 16; msb += 16; }

    if (xc >= 0x100) { xc >>= 8; msb += 8; }

    if (xc >= 0x10) { xc >>= 4; msb += 4; }

    if (xc >= 0x4) { xc >>= 2; msb += 2; }

    if (xc >= 0x2) msb += 1;  // No need to shift xc anymore



    int256 result = msb - 64 << 64;

    uint256 ux = uint256 (x) << 127 - msb;

    for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {

      ux *= ux;

      uint256 b = ux >> 255;

      ux >>= 127 + b;

      result += bit * int256 (b);

    }



    return int128 (result);

  }



  /**

   * Calculate natural logarithm of x.  Revert if x <= 0.

   *

   * @param x signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function ln (int128 x) internal pure returns (int128) {

    require (x > 0);



    return int128 (

        uint256 (log_2 (x)) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128);

  }



  /**

   * Calculate binary exponent of x.  Revert on overflow.

   *

   * @param x signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function exp_2 (int128 x) internal pure returns (int128) {

    require (x < 0x400000000000000000); // Overflow



    if (x < -0x400000000000000000) return 0; // Underflow



    uint256 result = 0x80000000000000000000000000000000;



    if (x & 0x8000000000000000 > 0)

      result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128;

    if (x & 0x4000000000000000 > 0)

      result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128;

    if (x & 0x2000000000000000 > 0)

      result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128;

    if (x & 0x1000000000000000 > 0)

      result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128;

    if (x & 0x800000000000000 > 0)

      result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128;

    if (x & 0x400000000000000 > 0)

      result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128;

    if (x & 0x200000000000000 > 0)

      result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128;

    if (x & 0x100000000000000 > 0)

      result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128;

    if (x & 0x80000000000000 > 0)

      result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128;

    if (x & 0x40000000000000 > 0)

      result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128;

    if (x & 0x20000000000000 > 0)

      result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128;

    if (x & 0x10000000000000 > 0)

      result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128;

    if (x & 0x8000000000000 > 0)

      result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128;

    if (x & 0x4000000000000 > 0)

      result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128;

    if (x & 0x2000000000000 > 0)

      result = result * 0x1000162E525EE054754457D5995292026 >> 128;

    if (x & 0x1000000000000 > 0)

      result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128;

    if (x & 0x800000000000 > 0)

      result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128;

    if (x & 0x400000000000 > 0)

      result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128;

    if (x & 0x200000000000 > 0)

      result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128;

    if (x & 0x100000000000 > 0)

      result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128;

    if (x & 0x80000000000 > 0)

      result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128;

    if (x & 0x40000000000 > 0)

      result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128;

    if (x & 0x20000000000 > 0)

      result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128;

    if (x & 0x10000000000 > 0)

      result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128;

    if (x & 0x8000000000 > 0)

      result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128;

    if (x & 0x4000000000 > 0)

      result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128;

    if (x & 0x2000000000 > 0)

      result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128;

    if (x & 0x1000000000 > 0)

      result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128;

    if (x & 0x800000000 > 0)

      result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128;

    if (x & 0x400000000 > 0)

      result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128;

    if (x & 0x200000000 > 0)

      result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128;

    if (x & 0x100000000 > 0)

      result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128;

    if (x & 0x80000000 > 0)

      result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128;

    if (x & 0x40000000 > 0)

      result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128;

    if (x & 0x20000000 > 0)

      result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128;

    if (x & 0x10000000 > 0)

      result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128;

    if (x & 0x8000000 > 0)

      result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128;

    if (x & 0x4000000 > 0)

      result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128;

    if (x & 0x2000000 > 0)

      result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128;

    if (x & 0x1000000 > 0)

      result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128;

    if (x & 0x800000 > 0)

      result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128;

    if (x & 0x400000 > 0)

      result = result * 0x100000000002C5C85FDF477B662B26945 >> 128;

    if (x & 0x200000 > 0)

      result = result * 0x10000000000162E42FEFA3AE53369388C >> 128;

    if (x & 0x100000 > 0)

      result = result * 0x100000000000B17217F7D1D351A389D40 >> 128;

    if (x & 0x80000 > 0)

      result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128;

    if (x & 0x40000 > 0)

      result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128;

    if (x & 0x20000 > 0)

      result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128;

    if (x & 0x10000 > 0)

      result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128;

    if (x & 0x8000 > 0)

      result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128;

    if (x & 0x4000 > 0)

      result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128;

    if (x & 0x2000 > 0)

      result = result * 0x1000000000000162E42FEFA39F02B772C >> 128;

    if (x & 0x1000 > 0)

      result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128;

    if (x & 0x800 > 0)

      result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128;

    if (x & 0x400 > 0)

      result = result * 0x100000000000002C5C85FDF473DEA871F >> 128;

    if (x & 0x200 > 0)

      result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128;

    if (x & 0x100 > 0)

      result = result * 0x100000000000000B17217F7D1CF79E949 >> 128;

    if (x & 0x80 > 0)

      result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128;

    if (x & 0x40 > 0)

      result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128;

    if (x & 0x20 > 0)

      result = result * 0x100000000000000162E42FEFA39EF366F >> 128;

    if (x & 0x10 > 0)

      result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128;

    if (x & 0x8 > 0)

      result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128;

    if (x & 0x4 > 0)

      result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128;

    if (x & 0x2 > 0)

      result = result * 0x1000000000000000162E42FEFA39EF358 >> 128;

    if (x & 0x1 > 0)

      result = result * 0x10000000000000000B17217F7D1CF79AB >> 128;



    result >>= 63 - (x >> 64);

    require (result <= uint256 (MAX_64x64));



    return int128 (result);

  }



  /**

   * Calculate natural exponent of x.  Revert on overflow.

   *

   * @param x signed 64.64-bit fixed point number

   * @return signed 64.64-bit fixed point number

   */

  function exp (int128 x) internal pure returns (int128) {

    require (x < 0x400000000000000000); // Overflow



    if (x < -0x400000000000000000) return 0; // Underflow



    return exp_2 (

        int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128));

  }



  /**

   * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit

   * integer numbers.  Revert on overflow or when y is zero.

   *

   * @param x unsigned 256-bit integer number

   * @param y unsigned 256-bit integer number

   * @return unsigned 64.64-bit fixed point number

   */

  function divuu (uint256 x, uint256 y) private pure returns (uint128) {

    require (y != 0);



    uint256 result;



    if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)

      result = (x << 64) / y;

    else {

      uint256 msb = 192;

      uint256 xc = x >> 192;

      if (xc >= 0x100000000) { xc >>= 32; msb += 32; }

      if (xc >= 0x10000) { xc >>= 16; msb += 16; }

      if (xc >= 0x100) { xc >>= 8; msb += 8; }

      if (xc >= 0x10) { xc >>= 4; msb += 4; }

      if (xc >= 0x4) { xc >>= 2; msb += 2; }

      if (xc >= 0x2) msb += 1;  // No need to shift xc anymore



      result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1);

      require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);



      uint256 hi = result * (y >> 128);

      uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);



      uint256 xh = x >> 192;

      uint256 xl = x << 64;



      if (xl < lo) xh -= 1;

      xl -= lo; // We rely on overflow behavior here

      lo = hi << 128;

      if (xl < lo) xh -= 1;

      xl -= lo; // We rely on overflow behavior here



      assert (xh == hi >> 128);



      result += xl / y;

    }



    require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

    return uint128 (result);

  }



  /**

   * Calculate x^y assuming 0^0 is 1, where x is unsigned 129.127 fixed point

   * number and y is unsigned 256-bit integer number.  Revert on overflow.

   *

   * @param x unsigned 129.127-bit fixed point number

   * @param y uint256 value

   * @return unsigned 129.127-bit fixed point number

   */

  function powu (uint256 x, uint256 y) private pure returns (uint256) {

    if (y == 0) return 0x80000000000000000000000000000000;

    else if (x == 0) return 0;

    else {

      int256 msb = 0;

      uint256 xc = x;

      if (xc >= 0x100000000000000000000000000000000) { xc >>= 128; msb += 128; }

      if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }

      if (xc >= 0x100000000) { xc >>= 32; msb += 32; }

      if (xc >= 0x10000) { xc >>= 16; msb += 16; }

      if (xc >= 0x100) { xc >>= 8; msb += 8; }

      if (xc >= 0x10) { xc >>= 4; msb += 4; }

      if (xc >= 0x4) { xc >>= 2; msb += 2; }

      if (xc >= 0x2) msb += 1;  // No need to shift xc anymore



      int256 xe = msb - 127;

      if (xe > 0) x >>= xe;

      else x <<= -xe;



      uint256 result = 0x80000000000000000000000000000000;

      int256 re = 0;



      while (y > 0) {

        if (y & 1 > 0) {

          result = result * x;

          y -= 1;

          re += xe;

          if (result >=

            0x8000000000000000000000000000000000000000000000000000000000000000) {

            result >>= 128;

            re += 1;

          } else result >>= 127;

          if (re < -127) return 0; // Underflow

          require (re < 128); // Overflow

        } else {

          x = x * x;

          y >>= 1;

          xe <<= 1;

          if (x >=

            0x8000000000000000000000000000000000000000000000000000000000000000) {

            x >>= 128;

            xe += 1;

          } else x >>= 127;

          if (xe < -127) return 0; // Underflow

          require (xe < 128); // Overflow

        }

      }



      if (re > 0) result <<= re;

      else if (re < 0) result >>= -re;



      return result;

    }

  }



  /**

   * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer

   * number.

   *

   * @param x unsigned 256-bit integer number

   * @return unsigned 128-bit integer number

   */

  function sqrtu (uint256 x, uint256 r) private pure returns (uint128) {

    if (x == 0) return 0;

    else {

      require (r > 0);

      while (true) {

        uint256 rr = x / r;

        if (r == rr || r + 1 == rr) return uint128 (r);

        else if (r == rr + 1) return uint128 (rr);

        r = r + rr + 1 >> 1;

      }

    }

  }

}

contract ContextUpgradeSafe is Initializable {

    // Empty internal constructor, to prevent people from mistakenly deploying

    // an instance of this contract, which should be used via inheritance.



    function __Context_init() internal initializer {

        __Context_init_unchained();

    }



    function __Context_init_unchained() internal initializer {





    }





    function _msgSender() internal view virtual returns (address payable) {

        return msg.sender;

    }



    function _msgData() internal view virtual returns (bytes memory) {

        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691

        return msg.data;

    }



    uint256[50] private __gap;

}

contract OwnableUpgradeSafe is Initializable, ContextUpgradeSafe {

    address private _owner;



    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);



    /**

     * @dev Initializes the contract setting the deployer as the initial owner.

     */



    function __Ownable_init() internal initializer {

        __Context_init_unchained();

        __Ownable_init_unchained();

    }



    function __Ownable_init_unchained() internal initializer {





        address msgSender = _msgSender();

        _owner = msgSender;

        emit OwnershipTransferred(address(0), msgSender);



    }





    /**

     * @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(_owner == _msgSender(), "Ownable: caller is not the 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 virtual 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 newOwner) public virtual onlyOwner {

        require(newOwner != address(0), "Ownable: new owner is the zero address");

        emit OwnershipTransferred(_owner, newOwner);

        _owner = newOwner;

    }



    uint256[49] private __gap;

}

contract ERC20UpgradeSafe is Initializable, ContextUpgradeSafe, IERC20 {

    using SafeMath for uint256;

    using Address for address;



    mapping (address => uint256) private _balances;



    mapping (address => mapping (address => uint256)) private _allowances;



    uint256 private _totalSupply;



    string private _name;

    string private _symbol;

    uint8 private _decimals;



    /**

     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with

     * a default value of 18.

     *

     * To select a different value for {decimals}, use {_setupDecimals}.

     *

     * All three of these values are immutable: they can only be set once during

     * construction.

     */



    function __ERC20_init(string memory name, string memory symbol) internal initializer {

        __Context_init_unchained();

        __ERC20_init_unchained(name, symbol);

    }



    function __ERC20_init_unchained(string memory name, string memory symbol) internal initializer {





        _name = name;

        _symbol = symbol;

        _decimals = 18;



    }





    /**

     * @dev Returns the name of the token.

     */

    function name() public view returns (string memory) {

        return _name;

    }



    /**

     * @dev Returns the symbol of the token, usually a shorter version of the

     * name.

     */

    function symbol() public view returns (string memory) {

        return _symbol;

    }



    /**

     * @dev Returns the number of decimals used to get its user representation.

     * For example, if `decimals` equals `2`, a balance of `505` tokens should

     * be displayed to a user as `5,05` (`505 / 10 ** 2`).

     *

     * Tokens usually opt for a value of 18, imitating the relationship between

     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is

     * called.

     *

     * NOTE: This information is only used for _display_ purposes: it in

     * no way affects any of the arithmetic of the contract, including

     * {IERC20-balanceOf} and {IERC20-transfer}.

     */

    function decimals() public view returns (uint8) {

        return _decimals;

    }



    /**

     * @dev See {IERC20-totalSupply}.

     */

    function totalSupply() public view override returns (uint256) {

        return _totalSupply;

    }



    /**

     * @dev See {IERC20-balanceOf}.

     */

    function balanceOf(address account) public view override returns (uint256) {

        return _balances[account];

    }



    /**

     * @dev See {IERC20-transfer}.

     *

     * Requirements:

     *

     * - `recipient` cannot be the zero address.

     * - the caller must have a balance of at least `amount`.

     */

    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {

        _transfer(_msgSender(), recipient, amount);

        return true;

    }



    /**

     * @dev See {IERC20-allowance}.

     */

    function allowance(address owner, address spender) public view virtual override returns (uint256) {

        return _allowances[owner][spender];

    }



    /**

     * @dev See {IERC20-approve}.

     *

     * Requirements:

     *

     * - `spender` cannot be the zero address.

     */

    function approve(address spender, uint256 amount) public virtual override returns (bool) {

        _approve(_msgSender(), spender, amount);

        return true;

    }



    /**

     * @dev See {IERC20-transferFrom}.

     *

     * Emits an {Approval} event indicating the updated allowance. This is not

     * required by the EIP. See the note at the beginning of {ERC20};

     *

     * Requirements:

     * - `sender` and `recipient` cannot be the zero address.

     * - `sender` must have a balance of at least `amount`.

     * - the caller must have allowance for ``sender``'s tokens of at least

     * `amount`.

     */

    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {

        _transfer(sender, recipient, amount);

        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));

        return true;

    }



    /**

     * @dev Atomically increases the allowance granted to `spender` by the caller.

     *

     * This is an alternative to {approve} that can be used as a mitigation for

     * problems described in {IERC20-approve}.

     *

     * Emits an {Approval} event indicating the updated allowance.

     *

     * Requirements:

     *

     * - `spender` cannot be the zero address.

     */

    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {

        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));

        return true;

    }



    /**

     * @dev Atomically decreases the allowance granted to `spender` by the caller.

     *

     * This is an alternative to {approve} that can be used as a mitigation for

     * problems described in {IERC20-approve}.

     *

     * Emits an {Approval} event indicating the updated allowance.

     *

     * Requirements:

     *

     * - `spender` cannot be the zero address.

     * - `spender` must have allowance for the caller of at least

     * `subtractedValue`.

     */

    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {

        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));

        return true;

    }



    /**

     * @dev Moves tokens `amount` from `sender` to `recipient`.

     *

     * This is internal function is equivalent to {transfer}, and can be used to

     * e.g. implement automatic token fees, slashing mechanisms, etc.

     *

     * Emits a {Transfer} event.

     *

     * Requirements:

     *

     * - `sender` cannot be the zero address.

     * - `recipient` cannot be the zero address.

     * - `sender` must have a balance of at least `amount`.

     */

    function _transfer(address sender, address recipient, uint256 amount) internal virtual {

        require(sender != address(0), "ERC20: transfer from the zero address");

        require(recipient != address(0), "ERC20: transfer to the zero address");



        _beforeTokenTransfer(sender, recipient, amount);



        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");

        _balances[recipient] = _balances[recipient].add(amount);

        emit Transfer(sender, recipient, amount);

    }



    /** @dev Creates `amount` tokens and assigns them to `account`, increasing

     * the total supply.

     *

     * Emits a {Transfer} event with `from` set to the zero address.

     *

     * Requirements

     *

     * - `to` cannot be the zero address.

     */

    function _mint(address account, uint256 amount) internal virtual {

        require(account != address(0), "ERC20: mint to the zero address");



        _beforeTokenTransfer(address(0), account, amount);



        _totalSupply = _totalSupply.add(amount);

        _balances[account] = _balances[account].add(amount);

        emit Transfer(address(0), account, amount);

    }



    /**

     * @dev Destroys `amount` tokens from `account`, reducing the

     * total supply.

     *

     * Emits a {Transfer} event with `to` set to the zero address.

     *

     * Requirements

     *

     * - `account` cannot be the zero address.

     * - `account` must have at least `amount` tokens.

     */

    function _burn(address account, uint256 amount) internal virtual {

        require(account != address(0), "ERC20: burn from the zero address");



        _beforeTokenTransfer(account, address(0), amount);



        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");

        _totalSupply = _totalSupply.sub(amount);

        emit Transfer(account, address(0), amount);

    }



    /**

     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.

     *

     * This is internal function is equivalent to `approve`, and can be used to

     * e.g. set automatic allowances for certain subsystems, etc.

     *

     * Emits an {Approval} event.

     *

     * Requirements:

     *

     * - `owner` cannot be the zero address.

     * - `spender` cannot be the zero address.

     */

    function _approve(address owner, address spender, uint256 amount) internal virtual {

        require(owner != address(0), "ERC20: approve from the zero address");

        require(spender != address(0), "ERC20: approve to the zero address");



        _allowances[owner][spender] = amount;

        emit Approval(owner, spender, amount);

    }



    /**

     * @dev Sets {decimals} to a value other than the default one of 18.

     *

     * WARNING: This function should only be called from the constructor. Most

     * applications that interact with token contracts will not expect

     * {decimals} to ever change, and may work incorrectly if it does.

     */

    function _setupDecimals(uint8 decimals_) internal {

        _decimals = decimals_;

    }



    /**

     * @dev Hook that is called before any transfer of tokens. This includes

     * minting and burning.

     *

     * Calling conditions:

     *

     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens

     * will be to transferred to `to`.

     * - when `from` is zero, `amount` tokens will be minted for `to`.

     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.

     * - `from` and `to` are never both zero.

     *

     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].

     */

    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }



    uint256[44] private __gap;

}

contract PausableUpgradeSafe is Initializable, ContextUpgradeSafe {

    /**

     * @dev Emitted when the pause is triggered by `account`.

     */

    event Paused(address account);



    /**

     * @dev Emitted when the pause is lifted by `account`.

     */

    event Unpaused(address account);



    bool private _paused;



    /**

     * @dev Initializes the contract in unpaused state.

     */



    function __Pausable_init() internal initializer {

        __Context_init_unchained();

        __Pausable_init_unchained();

    }



    function __Pausable_init_unchained() internal initializer {





        _paused = false;



    }





    /**

     * @dev Returns true if the contract is paused, and false otherwise.

     */

    function paused() public view returns (bool) {

        return _paused;

    }



    /**

     * @dev Modifier to make a function callable only when the contract is not paused.

     */

    modifier whenNotPaused() {

        require(!_paused, "Pausable: paused");

        _;

    }



    /**

     * @dev Modifier to make a function callable only when the contract is paused.

     */

    modifier whenPaused() {

        require(_paused, "Pausable: not paused");

        _;

    }



    /**

     * @dev Triggers stopped state.

     */

    function _pause() internal virtual whenNotPaused {

        _paused = true;

        emit Paused(_msgSender());

    }



    /**

     * @dev Returns to normal state.

     */

    function _unpause() internal virtual whenPaused {

        _paused = false;

        emit Unpaused(_msgSender());

    }



    uint256[49] private __gap;

}

contract CreditLine is Initializable, OwnableUpgradeSafe {

  // Credit line terms

  address public borrower;

  address public underwriter;

  uint public collateral;

  uint public limit;

  uint public interestApr;

  uint public minCollateralPercent;

  uint public paymentPeriodInDays;

  uint public termInDays;



  // Accounting variables

  uint public balance;

  uint public interestOwed;

  uint public principalOwed;

  uint public prepaymentBalance;

  uint public collateralBalance;

  uint public termEndBlock;

  uint public nextDueBlock;

  uint public lastUpdatedBlock;



  function initialize(

    address _borrower,

    address _underwriter,

    uint _limit,

    uint _interestApr,

    uint _minCollateralPercent,

    uint _paymentPeriodInDays,

    uint _termInDays

  ) public initializer {

    __Ownable_init();

    borrower = _borrower;

    underwriter = _underwriter;

    limit = _limit;

    interestApr = _interestApr;

    minCollateralPercent = _minCollateralPercent;

    paymentPeriodInDays = _paymentPeriodInDays;

    termInDays = _termInDays;

    lastUpdatedBlock = block.number;

  }



  function setTermEndBlock(uint newTermEndBlock) external onlyOwner returns (uint) {

    return termEndBlock = newTermEndBlock;

  }



  function setNextDueBlock(uint newNextDueBlock) external onlyOwner returns (uint) {

    return nextDueBlock = newNextDueBlock;

  }



  function setBalance(uint newBalance) external onlyOwner returns(uint) {

    return balance = newBalance;

  }



  function setInterestOwed(uint newInterestOwed) external onlyOwner returns (uint) {

    return interestOwed = newInterestOwed;

  }



  function setPrincipalOwed(uint newPrincipalOwed) external onlyOwner returns (uint) {

    return principalOwed = newPrincipalOwed;

  }



  function setPrepaymentBalance(uint newPrepaymentBalance) external onlyOwner returns (uint) {

    return prepaymentBalance = newPrepaymentBalance;

  }



  function setCollateralBalance(uint newCollateralBalance) external onlyOwner returns (uint) {

    return collateralBalance = newCollateralBalance;

  }



  function setLastUpdatedBlock(uint newLastUpdatedBlock) external onlyOwner returns (uint) {

    return lastUpdatedBlock = newLastUpdatedBlock;

  }



  function setLimit(uint newAmount) external onlyOwnerOrUnderwriter returns (uint) {

    return limit = newAmount;

  }



  function authorizePool(address poolAddress) external onlyOwner {

    address erc20address = Pool(poolAddress).erc20address();



    // Approve the pool for an infinite amount

    ERC20UpgradeSafe(erc20address).approve(poolAddress, uint(-1));

  }



  modifier onlyOwnerOrUnderwriter() {

    require((msg.sender == owner() || msg.sender == underwriter), "Restricted to owner or underwriter");

    _;

  }

}

contract OwnerPausable is OwnableUpgradeSafe, PausableUpgradeSafe {



  function __OwnerPausable__init() public {

    __Pausable_init_unchained();

  }



  /**

    * @dev Pauses all functions guarded by Pause

    *

    * See {Pausable-_pause}.

    *

    * Requirements:

    *

    * - the caller must be the owner.

    */

  function pause() public onlyOwner {

      _pause();

  }



  /**

    * @dev Unpauses the contract

    *

    * See {Pausable-_unpause}.

    *

    * Requirements:

    *

    * - the caller must be the owner`.

    */

  function unpause() public onlyOwner {

      _unpause();

  }

}

contract Pool is Initializable, OwnableUpgradeSafe, OwnerPausable {

  using SafeMath for uint256;

  uint public sharePrice;

  uint mantissa;

  uint public totalShares;

  mapping(address => uint) public capitalProviders;

  address public erc20address;

  string name;

  uint public totalFundsLimit = 0;

  uint public transactionLimit = 0;



  event DepositMade(address indexed capitalProvider, uint amount);

  event WithdrawalMade(address indexed capitalProvider, uint amount);

  event TransferMade(address indexed from, address indexed to, uint amount);

  event InterestCollected(address indexed payer, uint amount);

  event PrincipalCollected(address indexed payer, uint amount);

  event LimitChanged(address indexed owner, string limitType, uint amount);



  function initialize(address _erc20address, string memory _name, uint _mantissa) public initializer {

    __Context_init_unchained();

    __Ownable_init_unchained();

    __OwnerPausable__init();

    name = _name;

    erc20address = _erc20address;

    mantissa = _mantissa;

    sharePrice = _mantissa;



    // Sanity check the address

    ERC20UpgradeSafe(erc20address).totalSupply();



    // Unlock self for infinite amount

    ERC20UpgradeSafe(erc20address).approve(address(this), uint(-1));

  }



  function deposit(uint amount) external payable whenNotPaused {

    require(transactionWithinLimit(amount), "Amount is over the per-transaction limit.");

    // Determine current shares the address has, and the amount of new shares to be added

    uint currentShares = capitalProviders[msg.sender];

    uint depositShares = getNumShares(amount, mantissa, sharePrice);

    uint potentialNewTotalShares = totalShares.add(depositShares);

    require(poolWithinLimit(potentialNewTotalShares), "Deposit would put the Pool over the total limit.");



    doERC20Transfer(msg.sender, address(this), amount);



    // Add the new shares to both the pool and the address

    totalShares = totalShares.add(depositShares);

    capitalProviders[msg.sender] = currentShares.add(depositShares);



    emit DepositMade(msg.sender, amount);

  }



  function withdraw(uint amount) external whenNotPaused {

    // Determine current shares the address has and the shares requested to withdraw

    require(transactionWithinLimit(amount), "Amount is over the per-transaction limit");

    uint currentShares = capitalProviders[msg.sender];

    uint withdrawShares = getNumShares(amount, mantissa, sharePrice);



    // Ensure the address has enough value in the pool

    require(withdrawShares <= currentShares, "Amount requested is greater than what this address owns");



    // Remove the new shares from both the pool and the address

    totalShares = totalShares.sub(withdrawShares);

    capitalProviders[msg.sender] = currentShares.sub(withdrawShares);



    // Send the amount to the address

    doERC20Transfer(address(this), msg.sender, amount);

    emit WithdrawalMade(msg.sender, amount);

  }



  function collectInterestRepayment(address from, uint amount) external whenNotPaused {

    doERC20Transfer(from, address(this), amount);

    uint increment = amount.mul(mantissa).div(totalShares);

    sharePrice = sharePrice + increment;

    emit InterestCollected(from, amount);

  }



  function collectPrincipalRepayment(address from, uint amount) external whenNotPaused {

    // Purposefully does nothing except receive money. No share price updates for principal.

    doERC20Transfer(from, address(this), amount);

    emit PrincipalCollected(from, amount);

  }



  function setTotalFundsLimit(uint amount) public onlyOwner whenNotPaused {

    totalFundsLimit = amount;

    emit LimitChanged(msg.sender, "totalFundsLimit", amount);

  }



  function setTransactionLimit(uint amount) public onlyOwner whenNotPaused {

    transactionLimit = amount;

    emit LimitChanged(msg.sender, "transactionLimit", amount);

  }



  function transferFrom(address from, address to, uint amount) public onlyOwner whenNotPaused returns (bool) {

    bool result = doERC20Transfer(from, to, amount);

    emit TransferMade(from, to, amount);

    return result;

  }



  function enoughBalance(address user, uint amount) public view whenNotPaused returns(bool) {

    return ERC20UpgradeSafe(erc20address).balanceOf(user) >= amount;

  }



  /* Internal Functions */



  function poolWithinLimit(uint _totalShares) internal view returns (bool) {

    return _totalShares.mul(sharePrice).div(mantissa) <= totalFundsLimit;

  }



  function transactionWithinLimit(uint amount) internal view returns (bool) {

    return amount <= transactionLimit;

  }



  function getNumShares(uint amount, uint multiplier, uint price) internal pure returns (uint) {

    return amount.mul(multiplier).div(price);

  }



  function doERC20Transfer(address from, address to, uint amount) internal returns (bool) {

    ERC20UpgradeSafe erc20 = ERC20UpgradeSafe(erc20address);

    uint balanceBefore = erc20.balanceOf(to);



    bool success = erc20.transferFrom(from, to, amount);



    // Calculate the amount that was *actually* transferred

    uint balanceAfter = erc20.balanceOf(to);

    require(balanceAfter >= balanceBefore, "Token Transfer Overflow Error");

    return success;

  }



  function doERC20Withdraw(address payable to, uint amount) internal returns (bool) {

    ERC20UpgradeSafe erc20 = ERC20UpgradeSafe(erc20address);

    bool success = erc20.transfer(to, amount);



    require(success, "Token Withdraw Failed");

    return success;

  }

}

contract FakeV2CreditDesk is Initializable, OwnableUpgradeSafe, OwnerPausable {

  using SafeMath for uint256;



  // Approximate number of blocks

  uint public constant blocksPerDay = 5760;

  address public poolAddress;

  uint public maxUnderwriterLimit = 0;

  uint public transactionLimit = 0;



  struct Underwriter {

    uint governanceLimit;

    address[] creditLines;

  }



  struct Borrower {

    address[] creditLines;

  }



  event PaymentMade(address indexed payer, address indexed creditLine, uint interestAmount, uint principalAmount, uint remainingAmount);

  event PrepaymentMade(address indexed payer, address indexed creditLine, uint prepaymentAmount);

  event DrawdownMade(address indexed borrower, address indexed creditLine, uint drawdownAmount);

  event CreditLineCreated(address indexed borrower, address indexed creditLine);

  event PoolAddressUpdated(address indexed oldAddress, address indexed newAddress);

  event GovernanceUpdatedUnderwriterLimit(address indexed underwriter, uint newLimit);

  event LimitChanged(address indexed owner, string limitType, uint amount);



  mapping(address => Underwriter) public underwriters;

  mapping(address => Borrower) private borrowers;



  function initialize(address _poolAddress) public initializer {

    __Ownable_init();

    poolAddress = _poolAddress;

  }



  function someBrandNewFunction() public pure returns(uint) {

    return 5;

  }



  function getUnderwriterCreditLines(address underwriterAddress) public view returns (address[] memory) {

    return underwriters[underwriterAddress].creditLines;

  }



  /*

   * Internal Functions

  */



}

contract TestERC20 is ERC20UpgradeSafe {

    constructor(uint256 initialSupply, uint8 decimals) public {

        __ERC20_init("USDC", "USDC");

        _setupDecimals(decimals);

        _mint(msg.sender, initialSupply);

    }

}

contract TestPool is Pool {

    function _getNumShares(uint amount, uint multiplier, uint price) public pure returns (uint) {

      return getNumShares(amount, multiplier, price);

    }

}

contract CreditDesk is Initializable, OwnableUpgradeSafe, OwnerPausable {

  using SafeMath for uint256;



  // Approximate number of blocks

  uint public constant blocksPerDay = 5760;

  address public poolAddress;

  uint public maxUnderwriterLimit = 0;

  uint public transactionLimit = 0;



  struct Underwriter {

    uint governanceLimit;

    address[] creditLines;

  }



  struct Borrower {

    address[] creditLines;

  }



  event PaymentMade(address indexed payer, address indexed creditLine, uint interestAmount, uint principalAmount, uint remainingAmount);

  event PrepaymentMade(address indexed payer, address indexed creditLine, uint prepaymentAmount);

  event DrawdownMade(address indexed borrower, address indexed creditLine, uint drawdownAmount);

  event CreditLineCreated(address indexed borrower, address indexed creditLine);

  event PoolAddressUpdated(address indexed oldAddress, address indexed newAddress);

  event GovernanceUpdatedUnderwriterLimit(address indexed underwriter, uint newLimit);

  event LimitChanged(address indexed owner, string limitType, uint amount);



  mapping(address => Underwriter) public underwriters;

  mapping(address => Borrower) private borrowers;



  function initialize(address _poolAddress) public initializer {

    __Ownable_init();

    __OwnerPausable__init();

    setPoolAddress(_poolAddress);

  }



  function setUnderwriterGovernanceLimit(address underwriterAddress, uint limit) external onlyOwner whenNotPaused {

    Underwriter storage underwriter = underwriters[underwriterAddress];

    require(withinMaxUnderwriterLimit(limit), "This limit is greater than the max allowed by the protocol");

    underwriter.governanceLimit = limit;

    emit GovernanceUpdatedUnderwriterLimit(underwriterAddress, limit);

  }



  function createCreditLine(

    address _borrower,

    uint _limit,

    uint _interestApr,

    uint _minCollateralPercent,

    uint _paymentPeriodInDays,

    uint _termInDays

  ) external whenNotPaused {

    Underwriter storage underwriter = underwriters[msg.sender];

    Borrower storage borrower = borrowers[_borrower];

    require(underwriterCanCreateThisCreditLine(_limit, underwriter), "The underwriter cannot create this credit line");



    CreditLine cl = new CreditLine();

    cl.initialize(_borrower, msg.sender, _limit, _interestApr, _minCollateralPercent, _paymentPeriodInDays, _termInDays);

    cl.authorizePool(poolAddress);



    underwriter.creditLines.push(address(cl));

    borrower.creditLines.push(address(cl));

    emit CreditLineCreated(_borrower, address(cl));

	}



  function drawdown(uint amount, address creditLineAddress) external whenNotPaused {

    CreditLine cl = CreditLine(creditLineAddress);

    require(cl.borrower() == msg.sender, "You do not belong to this credit line");

    // Not strictly necessary, but provides a better error message to the user

    require(getPool().enoughBalance(poolAddress, amount), "Pool does not have enough balance for this drawdown");

    require(withinTransactionLimit(amount), "Amount is over the per-transaction limit");

    require(withinCreditLimit(amount, cl), "The borrower does not have enough credit limit for this drawdown");



    if (cl.balance() == 0) {

      cl.setTermEndBlock(calculateNewTermEndBlock(cl));

      cl.setNextDueBlock(calculateNextDueBlock(cl));

    }

    (uint interestOwed, uint principalOwed) = getInterestAndPrincipalOwedAsOf(cl, block.number);

    uint balance = cl.balance().add(amount);



    updateCreditLineAccounting(cl, balance, interestOwed, principalOwed);

    getPool().transferFrom(poolAddress, msg.sender, amount);



    emit DrawdownMade(msg.sender, address(cl), amount);

  }



  function pay(address creditLineAddress, uint amount) external payable whenNotPaused {

    CreditLine cl = CreditLine(creditLineAddress);



    require(withinTransactionLimit(amount), "Amount is over the per-transaction limit");

    // Not strictly necessary, but provides a faster/better error message to the user

    require(getPool().enoughBalance(msg.sender, amount), "You have insufficent balance for this payment");



    (uint paymentRemaining, uint interestPayment, uint principalPayment) = handlePayment(cl, amount, block.number, true);

    if (paymentRemaining > 0) {

      getPool().transferFrom(msg.sender, creditLineAddress, paymentRemaining);

      cl.setCollateralBalance(cl.collateralBalance().add(paymentRemaining));

    }

    if (interestPayment > 0) {

      getPool().collectInterestRepayment(msg.sender, interestPayment);

    }

    if (principalPayment > 0) {

      getPool().collectPrincipalRepayment(msg.sender, principalPayment);

    }



    emit PaymentMade(cl.borrower(), address(cl), interestPayment, principalPayment, paymentRemaining);

  }



  function prepay(address payable creditLineAddress, uint amount) external payable whenNotPaused {

    CreditLine cl = CreditLine(creditLineAddress);



    require(withinTransactionLimit(amount), "Amount is over the per-transaction limit");



    getPool().transferFrom(msg.sender, creditLineAddress, amount);

    uint newPrepaymentBalance = cl.prepaymentBalance().add(amount);

    cl.setPrepaymentBalance(newPrepaymentBalance);



    emit PrepaymentMade(msg.sender, address(cl), amount);

  }



  function addCollateral(address payable creditLineAddress, uint amount) external payable whenNotPaused {

    CreditLine cl = CreditLine(creditLineAddress);



    getPool().transferFrom(msg.sender, creditLineAddress, amount);

    uint newCollateralBalance = cl.collateralBalance().add(amount);

    cl.setCollateralBalance(newCollateralBalance);

  }



  function assessCreditLine(address creditLineAddress) external whenNotPaused {

    CreditLine cl = CreditLine(creditLineAddress);

    // Do not assess until a full period has elapsed

    if (block.number < cl.nextDueBlock()) {

      return;

    }



    (uint paymentRemaining, uint interestPayment, uint principalPayment) = handlePayment(cl, cl.prepaymentBalance(), cl.nextDueBlock(), false);



    cl.setPrepaymentBalance(paymentRemaining);

    getPool().collectInterestRepayment(msg.sender, interestPayment);

    getPool().collectPrincipalRepayment(msg.sender, principalPayment);

    cl.setNextDueBlock(calculateNextDueBlock(cl));

    if (cl.principalOwed() > 0) {

      handleLatePayments(cl);

    }

    emit PaymentMade(cl.borrower(), address(cl), interestPayment, principalPayment, paymentRemaining);

  }



  function setPoolAddress(address newPoolAddress) public onlyOwner whenNotPaused returns (address) {

    // Sanity check the new address;

    Pool(newPoolAddress).totalShares();



    emit PoolAddressUpdated(poolAddress, newPoolAddress);

    return poolAddress = newPoolAddress;

  }



  function setMaxUnderwriterLimit(uint amount) public onlyOwner whenNotPaused {

    maxUnderwriterLimit = amount;

    emit LimitChanged(msg.sender, "maxUnderwriterLimit", amount);

  }



  function setTransactionLimit(uint amount) public onlyOwner whenNotPaused {

    transactionLimit = amount;

    emit LimitChanged(msg.sender, "transactionLimit", amount);

  }



  // Public View Functions (Getters)



  function getUnderwriterCreditLines(address underwriterAddress) public view whenNotPaused returns (address[] memory) {

    return underwriters[underwriterAddress].creditLines;

  }



  function getBorrowerCreditLines(address borrowerAddress) public view whenNotPaused returns (address[] memory) {

    return borrowers[borrowerAddress].creditLines;

  }



  /*

   * Internal Functions

  */



  function handlePayment(CreditLine cl, uint paymentAmount, uint asOfBlock, bool allowFullBalancePayOff) internal returns (uint, uint, uint) {

    (uint interestOwed, uint principalOwed) = getInterestAndPrincipalOwedAsOf(cl, asOfBlock);

    Accountant.PaymentAllocation memory pa = Accountant.allocatePayment(paymentAmount, cl.balance(), interestOwed, principalOwed);



    uint newBalance = cl.balance().sub(pa.principalPayment);

    if (allowFullBalancePayOff) {

      newBalance = newBalance.sub(pa.additionalBalancePayment);

    }

    uint totalPrincipalPayment = cl.balance().sub(newBalance);

    uint paymentRemaining = paymentAmount.sub(pa.interestPayment).sub(totalPrincipalPayment);



    updateCreditLineAccounting(cl, newBalance, interestOwed.sub(pa.interestPayment), principalOwed.sub(pa.principalPayment));



    assert(paymentRemaining.add(pa.interestPayment).add(totalPrincipalPayment) == paymentAmount);



    return (paymentRemaining, pa.interestPayment, totalPrincipalPayment);

  }



  function handleLatePayments(CreditLine cl) internal {

    // No op for now;

  }



  function getPool() internal view returns (Pool) {

    return Pool(poolAddress);

  }



  function getInterestAndPrincipalOwedAsOf(CreditLine cl, uint blockNumber) internal view returns (uint, uint) {

    (uint interestAccrued, uint principalAccrued) = Accountant.calculateInterestAndPrincipalAccrued(cl, blockNumber);

    return (cl.interestOwed().add(interestAccrued), cl.principalOwed().add(principalAccrued));

  }



  function withinCreditLimit(uint amount, CreditLine cl) internal view returns(bool) {

    return cl.balance().add(amount) <= cl.limit();

  }



  function withinTransactionLimit(uint amount) internal view returns(bool) {

    return amount <= transactionLimit;

  }



  function calculateNewTermEndBlock(CreditLine cl) internal view returns (uint) {

    return block.number.add(blocksPerDay.mul(cl.termInDays()));

  }



  function calculateNextDueBlock(CreditLine cl) internal view returns (uint) {

    uint blocksPerPeriod = cl.paymentPeriodInDays().mul(blocksPerDay);

    uint currentNextDueBlock;

    if (cl.nextDueBlock() != 0) {

      currentNextDueBlock = cl.nextDueBlock();

    } else {

      currentNextDueBlock = block.number;

    }

    return currentNextDueBlock.add(blocksPerPeriod);

  }



  function underwriterCanCreateThisCreditLine(uint newAmount, Underwriter storage underwriter) internal view returns(bool) {

    uint creditCurrentlyExtended = getCreditCurrentlyExtended(underwriter);

    uint totalToBeExtended = creditCurrentlyExtended.add(newAmount);

    return totalToBeExtended <= underwriter.governanceLimit;

  }



  function withinMaxUnderwriterLimit(uint amount) internal view returns (bool) {

    return amount <= maxUnderwriterLimit;

  }



  function getCreditCurrentlyExtended(Underwriter storage underwriter) internal view returns (uint) {

    uint creditExtended = 0;

    for (uint i = 0; i < underwriter.creditLines.length; i++) {

      CreditLine cl = CreditLine(underwriter.creditLines[i]);

      creditExtended = creditExtended.add(cl.limit());

    }

    return creditExtended;

  }



  function updateCreditLineAccounting(CreditLine cl, uint balance, uint interestOwed, uint principalOwed) internal {

    cl.setBalance(balance);

    cl.setInterestOwed(interestOwed);

    cl.setPrincipalOwed(principalOwed);

    cl.setLastUpdatedBlock(block.number);



    if (balance == 0) {

      cl.setTermEndBlock(0);

      cl.setNextDueBlock(0);

    }

  }

}
