//SPDX-License-Identifier: UNLICENSED pragma solidity 0.6.12; pragma experimental ABIEncoderV2; interface iExchangeRates { function effectiveValue( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey ) external view returns (uint value); } interface collateralContract{ function getLoan(address _account, uint256 _loanID) external view returns ( address account, uint256 collateralAmount, uint256 loanAmount, uint256 timeCreated, uint256 loanID, uint256 timeClosed, uint256 accruedInterest, uint256 totalFees ); function openLoanIDsByAccount(address _account) external view returns (uint256[] memory); function calculateMintingFee(address _account, uint256 _loanID) external view returns (uint256); } /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ 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) { require(b <= a, "SafeMath: subtraction overflow"); 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-solidity/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) { // Solidity only automatically asserts when dividing by 0 require(b > 0, "SafeMath: division by zero"); 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) { require(b != 0, "SafeMath: modulo by zero"); return a % b; } } library SafeDecimalMath { using SafeMath for uint; /* Number of decimal places in the representations. */ uint8 public constant decimals = 18; uint8 public constant highPrecisionDecimals = 27; /* The number representing 1.0. */ uint public constant UNIT = 10**uint(decimals); /* The number representing 1.0 for higher fidelity numbers. */ uint public constant PRECISE_UNIT = 10**uint(highPrecisionDecimals); uint private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR = 10**uint(highPrecisionDecimals - decimals); /** * @return Provides an interface to UNIT. */ function unit() external pure returns (uint) { return UNIT; } /** * @return Provides an interface to PRECISE_UNIT. */ function preciseUnit() external pure returns (uint) { return PRECISE_UNIT; } /** * @return The result of multiplying x and y, interpreting the operands as fixed-point * decimals. * * @dev A unit factor is divided out after the product of x and y is evaluated, * so that product must be less than 2**256. As this is an integer division, * the internal division always rounds down. This helps save on gas. Rounding * is more expensive on gas. */ function multiplyDecimal(uint x, uint y) internal pure returns (uint) { /* Divide by UNIT to remove the extra factor introduced by the product. */ return x.mul(y) / UNIT; } /** * @return The result of safely multiplying x and y, interpreting the operands * as fixed-point decimals of the specified precision unit. * * @dev The operands should be in the form of a the specified unit factor which will be * divided out after the product of x and y is evaluated, so that product must be * less than 2**256. * * Unlike multiplyDecimal, this function rounds the result to the nearest increment. * Rounding is useful when you need to retain fidelity for small decimal numbers * (eg. small fractions or percentages). */ function _multiplyDecimalRound( uint x, uint y, uint precisionUnit ) private pure returns (uint) { /* Divide by UNIT to remove the extra factor introduced by the product. */ uint quotientTimesTen = x.mul(y) / (precisionUnit / 10); if (quotientTimesTen % 10 >= 5) { quotientTimesTen += 10; } return quotientTimesTen / 10; } /** * @return The result of safely multiplying x and y, interpreting the operands * as fixed-point decimals of a precise unit. * * @dev The operands should be in the precise unit factor which will be * divided out after the product of x and y is evaluated, so that product must be * less than 2**256. * * Unlike multiplyDecimal, this function rounds the result to the nearest increment. * Rounding is useful when you need to retain fidelity for small decimal numbers * (eg. small fractions or percentages). */ function multiplyDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) { return _multiplyDecimalRound(x, y, PRECISE_UNIT); } /** * @return The result of safely multiplying x and y, interpreting the operands * as fixed-point decimals of a standard unit. * * @dev The operands should be in the standard unit factor which will be * divided out after the product of x and y is evaluated, so that product must be * less than 2**256. * * Unlike multiplyDecimal, this function rounds the result to the nearest increment. * Rounding is useful when you need to retain fidelity for small decimal numbers * (eg. small fractions or percentages). */ function multiplyDecimalRound(uint x, uint y) internal pure returns (uint) { return _multiplyDecimalRound(x, y, UNIT); } /** * @return The result of safely dividing x and y. The return value is a high * precision decimal. * * @dev y is divided after the product of x and the standard precision unit * is evaluated, so the product of x and UNIT must be less than 2**256. As * this is an integer division, the result is always rounded down. * This helps save on gas. Rounding is more expensive on gas. */ function divideDecimal(uint x, uint y) internal pure returns (uint) { /* Reintroduce the UNIT factor that will be divided out by y. */ return x.mul(UNIT).div(y); } /** * @return The result of safely dividing x and y. The return value is as a rounded * decimal in the precision unit specified in the parameter. * * @dev y is divided after the product of x and the specified precision unit * is evaluated, so the product of x and the specified precision unit must * be less than 2**256. The result is rounded to the nearest increment. */ function _divideDecimalRound( uint x, uint y, uint precisionUnit ) private pure returns (uint) { uint resultTimesTen = x.mul(precisionUnit * 10).div(y); if (resultTimesTen % 10 >= 5) { resultTimesTen += 10; } return resultTimesTen / 10; } /** * @return The result of safely dividing x and y. The return value is as a rounded * standard precision decimal. * * @dev y is divided after the product of x and the standard precision unit * is evaluated, so the product of x and the standard precision unit must * be less than 2**256. The result is rounded to the nearest increment. */ function divideDecimalRound(uint x, uint y) internal pure returns (uint) { return _divideDecimalRound(x, y, UNIT); } /** * @return The result of safely dividing x and y. The return value is as a rounded * high precision decimal. * * @dev y is divided after the product of x and the high precision unit * is evaluated, so the product of x and the high precision unit must * be less than 2**256. The result is rounded to the nearest increment. */ function divideDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) { return _divideDecimalRound(x, y, PRECISE_UNIT); } /** * @dev Convert a standard decimal representation to a high precision one. */ function decimalToPreciseDecimal(uint i) internal pure returns (uint) { return i.mul(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR); } /** * @dev Convert a high precision decimal to a standard decimal representation. */ function preciseDecimalToDecimal(uint i) internal pure returns (uint) { uint quotientTimesTen = i / (UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR / 10); if (quotientTimesTen % 10 >= 5) { quotientTimesTen += 10; } return quotientTimesTen / 10; } } contract sAssetsOracle { using SafeMath for uint256; using SafeDecimalMath for uint256; // Got this address from the Synthetix contracts iExchangeRates exchangeRates = iExchangeRates(0xd69b189020EF614796578AfE4d10378c5e7e1138); uint256 unit = 10**uint(18); uint256 public liquidationPenalty = unit / 10; bytes32 private constant sUSD = "sUSD"; bytes32 public constant COLLATERAL = "ETH"; function getCollateralAmountSUSD(bytes32 sourceCurrencyKey, uint amountToLiquidate, bytes32 destinationCurrencyKey) public view returns (uint){ uint256 collateralRedeemed = exchangeRates.effectiveValue(sourceCurrencyKey, amountToLiquidate, destinationCurrencyKey); uint256 totalCollateralLiquidated = collateralRedeemed.multiplyDecimal( unit.add(liquidationPenalty) ); return totalCollateralLiquidated; } function batchGetLoanInformationSETH(address[] calldata _addresses, uint256[] calldata _loanIDs, address _contractAddress) external view returns ( uint256[] memory, uint256[] memory ){ uint256[] memory totalRepayment = new uint256[](_addresses.length); uint256[] memory totalCollateralLiquidated = new uint256[](_addresses.length); for (uint i = 0; i < _addresses.length; i++){ // Really we only need these three things uint collateralAmount; uint loanAmount; uint accruedInterest; // So we drop everything else (, collateralAmount, loanAmount,,,, accruedInterest, ) = collateralContract(_contractAddress).getLoan(_addresses[i], _loanIDs[i]); uint256 mintingFee = collateralContract(_contractAddress).calculateMintingFee(_addresses[i], _loanIDs[i]); uint totalFeeETH = accruedInterest.add(mintingFee); totalRepayment[i] = loanAmount + totalFeeETH; totalCollateralLiquidated[i] = collateralAmount.sub(totalFeeETH); } return (totalRepayment, totalCollateralLiquidated); } function batchGetLoanInformation(address[] calldata _addresses, uint256[] calldata _loanIDs, address _contractAddress) external view returns ( uint256[] memory, uint256[] memory){ uint256[] memory totalRepayment = new uint256[](_addresses.length); uint256[] memory totalCollateralLiquidated = new uint256[](_addresses.length); for (uint i = 0; i < _addresses.length; i++){ uint loanAmount; uint accruedInterest; (,, loanAmount,,,, accruedInterest, ) = collateralContract(_contractAddress).getLoan(_addresses[i], _loanIDs[i]); totalRepayment[i] = loanAmount + accruedInterest; totalCollateralLiquidated[i] = getCollateralAmountSUSD(sUSD, totalRepayment[i], COLLATERAL); } return (totalRepayment, totalCollateralLiquidated); } function batchOpenLoanIDsByAccount(address[] calldata _addresses, address _contractAddress, uint256 _loanLength) external view returns (address[] memory, uint256[] memory){ address[] memory addressesResult = new address[](_loanLength); uint256[] memory loansResult = new uint256[](_loanLength); uint resultIndex = (0); for (uint i = 0; i < _addresses.length; i++){ uint256[] memory loans = collateralContract(_contractAddress).openLoanIDsByAccount(_addresses[i]); if (loans.length == 0){ addressesResult[resultIndex] = _addresses[i]; loansResult[resultIndex] = uint(0); resultIndex++; } for (uint j = 0; j < loans.length; j++){ addressesResult[resultIndex] = (_addresses[i]); loansResult[resultIndex] = loans[j]; resultIndex++; } } return (addressesResult, loansResult); } }