Contract Overview
Balance:
0 Ether
Token:
More Info
My Name Tag:
Not Available
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
CollateralErc20
Compiler Version
v0.5.16+commit.9c3226ce
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2021-01-13 */ /* ____ __ __ __ _ / __/__ __ ___ / /_ / / ___ / /_ (_)__ __ _\ \ / // // _ \/ __// _ \/ -_)/ __// / \ \ / /___/ \_, //_//_/\__//_//_/\__/ \__//_/ /_\_\ /___/ * Synthetix: CollateralErc20.sol * * Latest source (may be newer): https://github.com/Synthetixio/synthetix/blob/master/contracts/CollateralErc20.sol * Docs: https://docs.synthetix.io/contracts/CollateralErc20 * * Contract Dependencies: * - Collateral * - IAddressResolver * - ICollateralErc20 * - ICollateralLoan * - MixinResolver * - MixinSystemSettings * - Owned * - State * Libraries: * - SafeDecimalMath * - SafeMath * * MIT License * =========== * * Copyright (c) 2021 Synthetix * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ pragma solidity ^0.5.16; // https://docs.synthetix.io/contracts/source/contracts/owned contract Owned { address public owner; address public nominatedOwner; constructor(address _owner) public { require(_owner != address(0), "Owner address cannot be 0"); owner = _owner; emit OwnerChanged(address(0), _owner); } function nominateNewOwner(address _owner) external onlyOwner { nominatedOwner = _owner; emit OwnerNominated(_owner); } function acceptOwnership() external { require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership"); emit OwnerChanged(owner, nominatedOwner); owner = nominatedOwner; nominatedOwner = address(0); } modifier onlyOwner { _onlyOwner(); _; } function _onlyOwner() private view { require(msg.sender == owner, "Only the contract owner may perform this action"); } event OwnerNominated(address newOwner); event OwnerChanged(address oldOwner, address newOwner); } // https://docs.synthetix.io/contracts/source/interfaces/iaddressresolver interface IAddressResolver { function getAddress(bytes32 name) external view returns (address); function getSynth(bytes32 key) external view returns (address); function requireAndGetAddress(bytes32 name, string calldata reason) external view returns (address); } // https://docs.synthetix.io/contracts/source/interfaces/isynth interface ISynth { // Views function currencyKey() external view returns (bytes32); function transferableSynths(address account) external view returns (uint); // Mutative functions function transferAndSettle(address to, uint value) external returns (bool); function transferFromAndSettle( address from, address to, uint value ) external returns (bool); // Restricted: used internally to Synthetix function burn(address account, uint amount) external; function issue(address account, uint amount) external; } // https://docs.synthetix.io/contracts/source/interfaces/iissuer interface IIssuer { // Views function anySynthOrSNXRateIsInvalid() external view returns (bool anyRateInvalid); function availableCurrencyKeys() external view returns (bytes32[] memory); function availableSynthCount() external view returns (uint); function availableSynths(uint index) external view returns (ISynth); function canBurnSynths(address account) external view returns (bool); function collateral(address account) external view returns (uint); function collateralisationRatio(address issuer) external view returns (uint); function collateralisationRatioAndAnyRatesInvalid(address _issuer) external view returns (uint cratio, bool anyRateIsInvalid); function debtBalanceOf(address issuer, bytes32 currencyKey) external view returns (uint debtBalance); function issuanceRatio() external view returns (uint); function lastIssueEvent(address account) external view returns (uint); function maxIssuableSynths(address issuer) external view returns (uint maxIssuable); function minimumStakeTime() external view returns (uint); function remainingIssuableSynths(address issuer) external view returns ( uint maxIssuable, uint alreadyIssued, uint totalSystemDebt ); function synths(bytes32 currencyKey) external view returns (ISynth); function getSynths(bytes32[] calldata currencyKeys) external view returns (ISynth[] memory); function synthsByAddress(address synthAddress) external view returns (bytes32); function totalIssuedSynths(bytes32 currencyKey, bool excludeEtherCollateral) external view returns (uint); function transferableSynthetixAndAnyRateIsInvalid(address account, uint balance) external view returns (uint transferable, bool anyRateIsInvalid); // Restricted: used internally to Synthetix function issueSynths(address from, uint amount) external; function issueSynthsOnBehalf( address issueFor, address from, uint amount ) external; function issueMaxSynths(address from) external; function issueMaxSynthsOnBehalf(address issueFor, address from) external; function burnSynths(address from, uint amount) external; function burnSynthsOnBehalf( address burnForAddress, address from, uint amount ) external; function burnSynthsToTarget(address from) external; function burnSynthsToTargetOnBehalf(address burnForAddress, address from) external; function liquidateDelinquentAccount( address account, uint susdAmount, address liquidator ) external returns (uint totalRedeemed, uint amountToLiquidate); } // Inheritance // Internal references // https://docs.synthetix.io/contracts/source/contracts/addressresolver contract AddressResolver is Owned, IAddressResolver { mapping(bytes32 => address) public repository; constructor(address _owner) public Owned(_owner) {} /* ========== RESTRICTED FUNCTIONS ========== */ function importAddresses(bytes32[] calldata names, address[] calldata destinations) external onlyOwner { require(names.length == destinations.length, "Input lengths must match"); for (uint i = 0; i < names.length; i++) { bytes32 name = names[i]; address destination = destinations[i]; repository[name] = destination; emit AddressImported(name, destination); } } /* ========= PUBLIC FUNCTIONS ========== */ function rebuildCaches(MixinResolver[] calldata destinations) external { for (uint i = 0; i < destinations.length; i++) { destinations[i].rebuildCache(); } } /* ========== VIEWS ========== */ function areAddressesImported(bytes32[] calldata names, address[] calldata destinations) external view returns (bool) { for (uint i = 0; i < names.length; i++) { if (repository[names[i]] != destinations[i]) { return false; } } return true; } function getAddress(bytes32 name) external view returns (address) { return repository[name]; } function requireAndGetAddress(bytes32 name, string calldata reason) external view returns (address) { address _foundAddress = repository[name]; require(_foundAddress != address(0), reason); return _foundAddress; } function getSynth(bytes32 key) external view returns (address) { IIssuer issuer = IIssuer(repository["Issuer"]); require(address(issuer) != address(0), "Cannot find Issuer address"); return address(issuer.synths(key)); } /* ========== EVENTS ========== */ event AddressImported(bytes32 name, address destination); } // solhint-disable payable-fallback // https://docs.synthetix.io/contracts/source/contracts/readproxy contract ReadProxy is Owned { address public target; constructor(address _owner) public Owned(_owner) {} function setTarget(address _target) external onlyOwner { target = _target; emit TargetUpdated(target); } function() external { // The basics of a proxy read call // Note that msg.sender in the underlying will always be the address of this contract. assembly { calldatacopy(0, 0, calldatasize) // Use of staticcall - this will revert if the underlying function mutates state let result := staticcall(gas, sload(target_slot), 0, calldatasize, 0, 0) returndatacopy(0, 0, returndatasize) if iszero(result) { revert(0, returndatasize) } return(0, returndatasize) } } event TargetUpdated(address newTarget); } // Inheritance // Internal references // https://docs.synthetix.io/contracts/source/contracts/mixinresolver contract MixinResolver { AddressResolver public resolver; mapping(bytes32 => address) private addressCache; constructor(address _resolver) internal { resolver = AddressResolver(_resolver); } /* ========== INTERNAL FUNCTIONS ========== */ function combineArrays(bytes32[] memory first, bytes32[] memory second) internal pure returns (bytes32[] memory combination) { combination = new bytes32[](first.length + second.length); for (uint i = 0; i < first.length; i++) { combination[i] = first[i]; } for (uint j = 0; j < second.length; j++) { combination[first.length + j] = second[j]; } } /* ========== PUBLIC FUNCTIONS ========== */ // Note: this function is public not external in order for it to be overridden and invoked via super in subclasses function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {} function rebuildCache() public { bytes32[] memory requiredAddresses = resolverAddressesRequired(); // The resolver must call this function whenver it updates its state for (uint i = 0; i < requiredAddresses.length; i++) { bytes32 name = requiredAddresses[i]; // Note: can only be invoked once the resolver has all the targets needed added address destination = resolver.requireAndGetAddress( name, string(abi.encodePacked("Resolver missing target: ", name)) ); addressCache[name] = destination; emit CacheUpdated(name, destination); } } /* ========== VIEWS ========== */ function isResolverCached() external view returns (bool) { bytes32[] memory requiredAddresses = resolverAddressesRequired(); for (uint i = 0; i < requiredAddresses.length; i++) { bytes32 name = requiredAddresses[i]; // false if our cache is invalid or if the resolver doesn't have the required address if (resolver.getAddress(name) != addressCache[name] || addressCache[name] == address(0)) { return false; } } return true; } /* ========== INTERNAL FUNCTIONS ========== */ function requireAndGetAddress(bytes32 name) internal view returns (address) { address _foundAddress = addressCache[name]; require(_foundAddress != address(0), string(abi.encodePacked("Missing address: ", name))); return _foundAddress; } /* ========== EVENTS ========== */ event CacheUpdated(bytes32 name, address destination); } // https://docs.synthetix.io/contracts/source/interfaces/iflexiblestorage interface IFlexibleStorage { // Views function getUIntValue(bytes32 contractName, bytes32 record) external view returns (uint); function getUIntValues(bytes32 contractName, bytes32[] calldata records) external view returns (uint[] memory); function getIntValue(bytes32 contractName, bytes32 record) external view returns (int); function getIntValues(bytes32 contractName, bytes32[] calldata records) external view returns (int[] memory); function getAddressValue(bytes32 contractName, bytes32 record) external view returns (address); function getAddressValues(bytes32 contractName, bytes32[] calldata records) external view returns (address[] memory); function getBoolValue(bytes32 contractName, bytes32 record) external view returns (bool); function getBoolValues(bytes32 contractName, bytes32[] calldata records) external view returns (bool[] memory); function getBytes32Value(bytes32 contractName, bytes32 record) external view returns (bytes32); function getBytes32Values(bytes32 contractName, bytes32[] calldata records) external view returns (bytes32[] memory); // Mutative functions function deleteUIntValue(bytes32 contractName, bytes32 record) external; function deleteIntValue(bytes32 contractName, bytes32 record) external; function deleteAddressValue(bytes32 contractName, bytes32 record) external; function deleteBoolValue(bytes32 contractName, bytes32 record) external; function deleteBytes32Value(bytes32 contractName, bytes32 record) external; function setUIntValue( bytes32 contractName, bytes32 record, uint value ) external; function setUIntValues( bytes32 contractName, bytes32[] calldata records, uint[] calldata values ) external; function setIntValue( bytes32 contractName, bytes32 record, int value ) external; function setIntValues( bytes32 contractName, bytes32[] calldata records, int[] calldata values ) external; function setAddressValue( bytes32 contractName, bytes32 record, address value ) external; function setAddressValues( bytes32 contractName, bytes32[] calldata records, address[] calldata values ) external; function setBoolValue( bytes32 contractName, bytes32 record, bool value ) external; function setBoolValues( bytes32 contractName, bytes32[] calldata records, bool[] calldata values ) external; function setBytes32Value( bytes32 contractName, bytes32 record, bytes32 value ) external; function setBytes32Values( bytes32 contractName, bytes32[] calldata records, bytes32[] calldata values ) external; } // Internal references // https://docs.synthetix.io/contracts/source/contracts/mixinsystemsettings contract MixinSystemSettings is MixinResolver { bytes32 internal constant SETTING_CONTRACT_NAME = "SystemSettings"; bytes32 internal constant SETTING_WAITING_PERIOD_SECS = "waitingPeriodSecs"; bytes32 internal constant SETTING_PRICE_DEVIATION_THRESHOLD_FACTOR = "priceDeviationThresholdFactor"; bytes32 internal constant SETTING_ISSUANCE_RATIO = "issuanceRatio"; bytes32 internal constant SETTING_FEE_PERIOD_DURATION = "feePeriodDuration"; bytes32 internal constant SETTING_TARGET_THRESHOLD = "targetThreshold"; bytes32 internal constant SETTING_LIQUIDATION_DELAY = "liquidationDelay"; bytes32 internal constant SETTING_LIQUIDATION_RATIO = "liquidationRatio"; bytes32 internal constant SETTING_LIQUIDATION_PENALTY = "liquidationPenalty"; bytes32 internal constant SETTING_RATE_STALE_PERIOD = "rateStalePeriod"; bytes32 internal constant SETTING_EXCHANGE_FEE_RATE = "exchangeFeeRate"; bytes32 internal constant SETTING_MINIMUM_STAKE_TIME = "minimumStakeTime"; bytes32 internal constant SETTING_AGGREGATOR_WARNING_FLAGS = "aggregatorWarningFlags"; bytes32 internal constant SETTING_TRADING_REWARDS_ENABLED = "tradingRewardsEnabled"; bytes32 internal constant SETTING_DEBT_SNAPSHOT_STALE_TIME = "debtSnapshotStaleTime"; bytes32 internal constant SETTING_CROSS_DOMAIN_DEPOSIT_GAS_LIMIT = "crossDomainDepositGasLimit"; bytes32 internal constant SETTING_CROSS_DOMAIN_ESCROW_GAS_LIMIT = "crossDomainEscrowGasLimit"; bytes32 internal constant SETTING_CROSS_DOMAIN_REWARD_GAS_LIMIT = "crossDomainRewardGasLimit"; bytes32 internal constant SETTING_CROSS_DOMAIN_WITHDRAWAL_GAS_LIMIT = "crossDomainWithdrawalGasLimit"; bytes32 internal constant CONTRACT_FLEXIBLESTORAGE = "FlexibleStorage"; enum CrossDomainMessageGasLimits {Deposit, Escrow, Reward, Withdrawal} constructor(address _resolver) internal MixinResolver(_resolver) {} function resolverAddressesRequired() public view returns (bytes32[] memory addresses) { addresses = new bytes32[](1); addresses[0] = CONTRACT_FLEXIBLESTORAGE; } function flexibleStorage() internal view returns (IFlexibleStorage) { return IFlexibleStorage(requireAndGetAddress(CONTRACT_FLEXIBLESTORAGE)); } function _getGasLimitSetting(CrossDomainMessageGasLimits gasLimitType) internal pure returns (bytes32) { if (gasLimitType == CrossDomainMessageGasLimits.Deposit) { return SETTING_CROSS_DOMAIN_DEPOSIT_GAS_LIMIT; } else if (gasLimitType == CrossDomainMessageGasLimits.Escrow) { return SETTING_CROSS_DOMAIN_ESCROW_GAS_LIMIT; } else if (gasLimitType == CrossDomainMessageGasLimits.Reward) { return SETTING_CROSS_DOMAIN_REWARD_GAS_LIMIT; } else if (gasLimitType == CrossDomainMessageGasLimits.Withdrawal) { return SETTING_CROSS_DOMAIN_WITHDRAWAL_GAS_LIMIT; } else { revert("Unknown gas limit type"); } } function getCrossDomainMessageGasLimit(CrossDomainMessageGasLimits gasLimitType) internal view returns (uint) { return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, _getGasLimitSetting(gasLimitType)); } function getTradingRewardsEnabled() internal view returns (bool) { return flexibleStorage().getBoolValue(SETTING_CONTRACT_NAME, SETTING_TRADING_REWARDS_ENABLED); } function getWaitingPeriodSecs() internal view returns (uint) { return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_WAITING_PERIOD_SECS); } function getPriceDeviationThresholdFactor() internal view returns (uint) { return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_PRICE_DEVIATION_THRESHOLD_FACTOR); } function getIssuanceRatio() internal view returns (uint) { // lookup on flexible storage directly for gas savings (rather than via SystemSettings) return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_ISSUANCE_RATIO); } function getFeePeriodDuration() internal view returns (uint) { // lookup on flexible storage directly for gas savings (rather than via SystemSettings) return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_FEE_PERIOD_DURATION); } function getTargetThreshold() internal view returns (uint) { // lookup on flexible storage directly for gas savings (rather than via SystemSettings) return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_TARGET_THRESHOLD); } function getLiquidationDelay() internal view returns (uint) { return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_DELAY); } function getLiquidationRatio() internal view returns (uint) { return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_RATIO); } function getLiquidationPenalty() internal view returns (uint) { return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_PENALTY); } function getRateStalePeriod() internal view returns (uint) { return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_RATE_STALE_PERIOD); } function getExchangeFeeRate(bytes32 currencyKey) internal view returns (uint) { return flexibleStorage().getUIntValue( SETTING_CONTRACT_NAME, keccak256(abi.encodePacked(SETTING_EXCHANGE_FEE_RATE, currencyKey)) ); } function getMinimumStakeTime() internal view returns (uint) { return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_MINIMUM_STAKE_TIME); } function getAggregatorWarningFlags() internal view returns (address) { return flexibleStorage().getAddressValue(SETTING_CONTRACT_NAME, SETTING_AGGREGATOR_WARNING_FLAGS); } function getDebtSnapshotStaleTime() internal view returns (uint) { return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_DEBT_SNAPSHOT_STALE_TIME); } } pragma experimental ABIEncoderV2; interface ICollateralLoan { struct Loan { // ID for the loan uint id; // Acccount that created the loan address payable account; // Amount of collateral deposited uint collateral; // The synth that was borowed bytes32 currency; // Amount of synths borrowed uint amount; // Indicates if the position was short sold bool short; // interest amounts accrued uint accruedInterest; // last interest index uint interestIndex; // time of last interaction. uint lastInteraction; } } /** * @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; } } // Libraries // https://docs.synthetix.io/contracts/source/libraries/safedecimalmath 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; } } // Inheritance // https://docs.synthetix.io/contracts/source/contracts/state contract State is Owned { // the address of the contract that can modify variables // this can only be changed by the owner of this contract address public associatedContract; constructor(address _associatedContract) internal { // This contract is abstract, and thus cannot be instantiated directly require(owner != address(0), "Owner must be set"); associatedContract = _associatedContract; emit AssociatedContractUpdated(_associatedContract); } /* ========== SETTERS ========== */ // Change the associated contract to a new address function setAssociatedContract(address _associatedContract) external onlyOwner { associatedContract = _associatedContract; emit AssociatedContractUpdated(_associatedContract); } /* ========== MODIFIERS ========== */ modifier onlyAssociatedContract { require(msg.sender == associatedContract, "Only the associated contract can perform this action"); _; } /* ========== EVENTS ========== */ event AssociatedContractUpdated(address associatedContract); } // Inheritance // Libraries contract CollateralState is Owned, State, ICollateralLoan { using SafeMath for uint; using SafeDecimalMath for uint; mapping(address => Loan[]) public loans; constructor(address _owner, address _associatedContract) public Owned(_owner) State(_associatedContract) {} /* ========== VIEWS ========== */ // If we do not find the loan, this returns a struct with 0'd values. function getLoan(address account, uint256 loanID) external view returns (Loan memory) { Loan[] memory accountLoans = loans[account]; for (uint i = 0; i < accountLoans.length; i++) { if (accountLoans[i].id == loanID) { return (accountLoans[i]); } } } function getNumLoans(address account) external view returns (uint numLoans) { return loans[account].length; } /* ========== MUTATIVE FUNCTIONS ========== */ function createLoan(Loan memory loan) public onlyAssociatedContract { loans[loan.account].push(loan); } function updateLoan(Loan memory loan) public onlyAssociatedContract { Loan[] storage accountLoans = loans[loan.account]; for (uint i = 0; i < accountLoans.length; i++) { if (accountLoans[i].id == loan.id) { loans[loan.account][i] = loan; } } } } interface ICollateralManager { // Manager information function hasCollateral(address collateral) external view returns (bool); function isSynthManaged(bytes32 currencyKey) external view returns (bool); // State information function long(bytes32 synth) external view returns (uint amount); function short(bytes32 synth) external view returns (uint amount); function totalLong() external view returns (uint susdValue, bool anyRateIsInvalid); function totalShort() external view returns (uint susdValue, bool anyRateIsInvalid); function getBorrowRate() external view returns (uint borrowRate, bool anyRateIsInvalid); function getShortRate(bytes32 synth) external view returns (uint shortRate, bool rateIsInvalid); function getRatesAndTime(uint index) external view returns ( uint entryRate, uint lastRate, uint lastUpdated, uint newIndex ); function getShortRatesAndTime(bytes32 currency, uint index) external view returns ( uint entryRate, uint lastRate, uint lastUpdated, uint newIndex ); function exceedsDebtLimit(uint amount, bytes32 currency) external view returns (bool canIssue, bool anyRateIsInvalid); function areSynthsAndCurrenciesSet(bytes32[] calldata requiredSynthNamesInResolver, bytes32[] calldata synthKeys) external view returns (bool); function areShortableSynthsSet(bytes32[] calldata requiredSynthNamesInResolver, bytes32[] calldata synthKeys) external view returns (bool); // Loans function getNewLoanId() external returns (uint id); // Manager mutative function addCollaterals(address[] calldata collaterals) external; function removeCollaterals(address[] calldata collaterals) external; function addSynths(bytes32[] calldata synthNamesInResolver, bytes32[] calldata synthKeys) external; function removeSynths(bytes32[] calldata synths, bytes32[] calldata synthKeys) external; function addShortableSynths(bytes32[2][] calldata requiredSynthAndInverseNamesInResolver, bytes32[] calldata synthKeys) external; function removeShortableSynths(bytes32[] calldata synths) external; // State mutative function updateBorrowRates(uint rate) external; function updateShortRates(bytes32 currency, uint rate) external; function incrementLongs(bytes32 synth, uint amount) external; function decrementLongs(bytes32 synth, uint amount) external; function incrementShorts(bytes32 synth, uint amount) external; function decrementShorts(bytes32 synth, uint amount) external; } // https://docs.synthetix.io/contracts/source/interfaces/isystemstatus interface ISystemStatus { struct Status { bool canSuspend; bool canResume; } struct Suspension { bool suspended; // reason is an integer code, // 0 => no reason, 1 => upgrading, 2+ => defined by system usage uint248 reason; } // Views function accessControl(bytes32 section, address account) external view returns (bool canSuspend, bool canResume); function requireSystemActive() external view; function requireIssuanceActive() external view; function requireExchangeActive() external view; function requireSynthActive(bytes32 currencyKey) external view; function requireSynthsActive(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view; function synthSuspension(bytes32 currencyKey) external view returns (bool suspended, uint248 reason); // Restricted functions function suspendSynth(bytes32 currencyKey, uint256 reason) external; function updateAccessControl( bytes32 section, address account, bool canSuspend, bool canResume ) external; } // https://docs.synthetix.io/contracts/source/interfaces/ifeepool interface IFeePool { // Views // solhint-disable-next-line func-name-mixedcase function FEE_ADDRESS() external view returns (address); function feesAvailable(address account) external view returns (uint, uint); function feePeriodDuration() external view returns (uint); function isFeesClaimable(address account) external view returns (bool); function targetThreshold() external view returns (uint); function totalFeesAvailable() external view returns (uint); function totalRewardsAvailable() external view returns (uint); // Mutative Functions function claimFees() external returns (bool); function claimOnBehalf(address claimingForAddress) external returns (bool); function closeCurrentFeePeriod() external; // Restricted: used internally to Synthetix function appendAccountIssuanceRecord( address account, uint lockedAmount, uint debtEntryIndex ) external; function recordFeePaid(uint sUSDAmount) external; function setRewardsToDistribute(uint amount) external; } // https://docs.synthetix.io/contracts/source/interfaces/ierc20 interface IERC20 { // ERC20 Optional Views function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); // Views function totalSupply() external view returns (uint); function balanceOf(address owner) external view returns (uint); function allowance(address owner, address spender) external view returns (uint); // Mutative functions function transfer(address to, uint value) external returns (bool); function approve(address spender, uint value) external returns (bool); function transferFrom( address from, address to, uint value ) external returns (bool); // Events event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); } // https://docs.synthetix.io/contracts/source/interfaces/iexchangerates interface IExchangeRates { // Structs struct RateAndUpdatedTime { uint216 rate; uint40 time; } struct InversePricing { uint entryPoint; uint upperLimit; uint lowerLimit; bool frozenAtUpperLimit; bool frozenAtLowerLimit; } // Views function aggregators(bytes32 currencyKey) external view returns (address); function aggregatorWarningFlags() external view returns (address); function anyRateIsInvalid(bytes32[] calldata currencyKeys) external view returns (bool); function canFreezeRate(bytes32 currencyKey) external view returns (bool); function currentRoundForRate(bytes32 currencyKey) external view returns (uint); function currenciesUsingAggregator(address aggregator) external view returns (bytes32[] memory); function effectiveValue( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey ) external view returns (uint value); function effectiveValueAndRates( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey ) external view returns ( uint value, uint sourceRate, uint destinationRate ); function effectiveValueAtRound( bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, uint roundIdForSrc, uint roundIdForDest ) external view returns (uint value); function getCurrentRoundId(bytes32 currencyKey) external view returns (uint); function getLastRoundIdBeforeElapsedSecs( bytes32 currencyKey, uint startingRoundId, uint startingTimestamp, uint timediff ) external view returns (uint); function inversePricing(bytes32 currencyKey) external view returns ( uint entryPoint, uint upperLimit, uint lowerLimit, bool frozenAtUpperLimit, bool frozenAtLowerLimit ); function lastRateUpdateTimes(bytes32 currencyKey) external view returns (uint256); function oracle() external view returns (address); function rateAndTimestampAtRound(bytes32 currencyKey, uint roundId) external view returns (uint rate, uint time); function rateAndUpdatedTime(bytes32 currencyKey) external view returns (uint rate, uint time); function rateAndInvalid(bytes32 currencyKey) external view returns (uint rate, bool isInvalid); function rateForCurrency(bytes32 currencyKey) external view returns (uint); function rateIsFlagged(bytes32 currencyKey) external view returns (bool); function rateIsFrozen(bytes32 currencyKey) external view returns (bool); function rateIsInvalid(bytes32 currencyKey) external view returns (bool); function rateIsStale(bytes32 currencyKey) external view returns (bool); function rateStalePeriod() external view returns (uint); function ratesAndUpdatedTimeForCurrencyLastNRounds(bytes32 currencyKey, uint numRounds) external view returns (uint[] memory rates, uint[] memory times); function ratesAndInvalidForCurrencies(bytes32[] calldata currencyKeys) external view returns (uint[] memory rates, bool anyRateInvalid); function ratesForCurrencies(bytes32[] calldata currencyKeys) external view returns (uint[] memory); // Mutative functions function freezeRate(bytes32 currencyKey) external; } interface IVirtualSynth { // Views function balanceOfUnderlying(address account) external view returns (uint); function rate() external view returns (uint); function readyToSettle() external view returns (bool); function secsLeftInWaitingPeriod() external view returns (uint); function settled() external view returns (bool); function synth() external view returns (ISynth); // Mutative functions function settle(address account) external; } // https://docs.synthetix.io/contracts/source/interfaces/iexchanger interface IExchanger { // Views function calculateAmountAfterSettlement( address from, bytes32 currencyKey, uint amount, uint refunded ) external view returns (uint amountAfterSettlement); function isSynthRateInvalid(bytes32 currencyKey) external view returns (bool); function maxSecsLeftInWaitingPeriod(address account, bytes32 currencyKey) external view returns (uint); function settlementOwing(address account, bytes32 currencyKey) external view returns ( uint reclaimAmount, uint rebateAmount, uint numEntries ); function hasWaitingPeriodOrSettlementOwing(address account, bytes32 currencyKey) external view returns (bool); function feeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view returns (uint exchangeFeeRate); function getAmountsForExchange( uint sourceAmount, bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey ) external view returns ( uint amountReceived, uint fee, uint exchangeFeeRate ); function priceDeviationThresholdFactor() external view returns (uint); function waitingPeriodSecs() external view returns (uint); // Mutative functions function exchange( address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address destinationAddress ) external returns (uint amountReceived); function exchangeOnBehalf( address exchangeForAddress, address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey ) external returns (uint amountReceived); function exchangeWithTracking( address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address destinationAddress, address originator, bytes32 trackingCode ) external returns (uint amountReceived); function exchangeOnBehalfWithTracking( address exchangeForAddress, address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address originator, bytes32 trackingCode ) external returns (uint amountReceived); function exchangeWithVirtual( address from, bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey, address destinationAddress, bytes32 trackingCode ) external returns (uint amountReceived, IVirtualSynth vSynth); function settle(address from, bytes32 currencyKey) external returns ( uint reclaimed, uint refunded, uint numEntries ); function setLastExchangeRateForSynth(bytes32 currencyKey, uint rate) external; function suspendSynthWithInvalidRate(bytes32 currencyKey) external; } // https://docs.synthetix.io/contracts/source/interfaces/istakingrewards interface IShortingRewards { // Views function lastTimeRewardApplicable() external view returns (uint256); function rewardPerToken() external view returns (uint256); function earned(address account) external view returns (uint256); function getRewardForDuration() external view returns (uint256); function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); // Mutative function enrol(address account, uint256 amount) external; function withdraw(address account, uint256 amount) external; function getReward(address account) external; function exit(address account) external; } // Inheritance // Libraries // Internal references contract Collateral is ICollateralLoan, Owned, MixinSystemSettings { /* ========== LIBRARIES ========== */ using SafeMath for uint; using SafeDecimalMath for uint; /* ========== CONSTANTS ========== */ bytes32 private constant sUSD = "sUSD"; // ========== STATE VARIABLES ========== // The synth corresponding to the collateral. bytes32 public collateralKey; // Stores loans CollateralState public state; address public manager; // The synths that this contract can issue. bytes32[] public synths; // Map from currency key to synth contract name. mapping(bytes32 => bytes32) public synthsByKey; // Map from currency key to the shorting rewards contract mapping(bytes32 => address) public shortingRewards; // ========== SETTER STATE VARIABLES ========== // The minimum collateral ratio required to avoid liquidation. uint public minCratio; // The minimum amount of collateral to create a loan. uint public minCollateral; // The fee charged for issuing a loan. uint public issueFeeRate; // The maximum number of loans that an account can create with this collateral. uint public maxLoansPerAccount = 50; // Time in seconds that a user must wait between interacting with a loan. // Provides front running and flash loan protection. uint public interactionDelay = 300; bool public canOpenLoans = true; /* ========== ADDRESS RESOLVER CONFIGURATION ========== */ bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus"; bytes32 private constant CONTRACT_EXRATES = "ExchangeRates"; bytes32 private constant CONTRACT_EXCHANGER = "Exchanger"; bytes32 private constant CONTRACT_FEEPOOL = "FeePool"; bytes32 private constant CONTRACT_SYNTHSUSD = "SynthsUSD"; /* ========== CONSTRUCTOR ========== */ constructor( CollateralState _state, address _owner, address _manager, address _resolver, bytes32 _collateralKey, uint _minCratio, uint _minCollateral ) public Owned(_owner) MixinSystemSettings(_resolver) { manager = _manager; state = _state; collateralKey = _collateralKey; minCratio = _minCratio; minCollateral = _minCollateral; } /* ========== VIEWS ========== */ function resolverAddressesRequired() public view returns (bytes32[] memory addresses) { bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired(); bytes32[] memory newAddresses = new bytes32[](5); newAddresses[0] = CONTRACT_FEEPOOL; newAddresses[1] = CONTRACT_EXRATES; newAddresses[2] = CONTRACT_EXCHANGER; newAddresses[3] = CONTRACT_SYSTEMSTATUS; newAddresses[4] = CONTRACT_SYNTHSUSD; bytes32[] memory combined = combineArrays(existingAddresses, newAddresses); addresses = combineArrays(combined, synths); } /* ---------- Related Contracts ---------- */ function _systemStatus() internal view returns (ISystemStatus) { return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS)); } function _synth(bytes32 synthName) internal view returns (ISynth) { return ISynth(requireAndGetAddress(synthName)); } function _synthsUSD() internal view returns (ISynth) { return ISynth(requireAndGetAddress(CONTRACT_SYNTHSUSD)); } function _exchangeRates() internal view returns (IExchangeRates) { return IExchangeRates(requireAndGetAddress(CONTRACT_EXRATES)); } function _exchanger() internal view returns (IExchanger) { return IExchanger(requireAndGetAddress(CONTRACT_EXCHANGER)); } function _feePool() internal view returns (IFeePool) { return IFeePool(requireAndGetAddress(CONTRACT_FEEPOOL)); } function _manager() internal view returns (ICollateralManager) { return ICollateralManager(manager); } /* ---------- Public Views ---------- */ function collateralRatio(Loan memory loan) public view returns (uint cratio) { uint cvalue = _exchangeRates().effectiveValue(collateralKey, loan.collateral, sUSD); uint dvalue = _exchangeRates().effectiveValue(loan.currency, loan.amount.add(loan.accruedInterest), sUSD); cratio = cvalue.divideDecimal(dvalue); } // The maximum number of synths issuable for this amount of collateral function maxLoan(uint amount, bytes32 currency) public view returns (uint max) { max = issuanceRatio().multiplyDecimal(_exchangeRates().effectiveValue(collateralKey, amount, currency)); } /** * r = target issuance ratio * D = debt value in sUSD * V = collateral value in sUSD * P = liquidation penalty * Calculates amount of synths = (D - V * r) / (1 - (1 + P) * r) * Note: if you pass a loan in here that is not eligible for liquidation it will revert. * We check the ratio first in liquidateInternal and only pass eligible loans in. */ function liquidationAmount(Loan memory loan) public view returns (uint amount) { uint liquidationPenalty = getLiquidationPenalty(); uint debtValue = _exchangeRates().effectiveValue(loan.currency, loan.amount.add(loan.accruedInterest), sUSD); uint collateralValue = _exchangeRates().effectiveValue(collateralKey, loan.collateral, sUSD); uint unit = SafeDecimalMath.unit(); uint dividend = debtValue.sub(collateralValue.divideDecimal(minCratio)); uint divisor = unit.sub(unit.add(liquidationPenalty).divideDecimal(minCratio)); uint sUSDamount = dividend.divideDecimal(divisor); return _exchangeRates().effectiveValue(sUSD, sUSDamount, loan.currency); } // amount is the amount of synths we are liquidating function collateralRedeemed(bytes32 currency, uint amount) public view returns (uint collateral) { uint liquidationPenalty = getLiquidationPenalty(); collateral = _exchangeRates().effectiveValue(currency, amount, collateralKey); collateral = collateral.multiplyDecimal(SafeDecimalMath.unit().add(liquidationPenalty)); } function areSynthsAndCurrenciesSet(bytes32[] calldata _synthNamesInResolver, bytes32[] calldata _synthKeys) external view returns (bool) { if (synths.length != _synthNamesInResolver.length) { return false; } for (uint i = 0; i < _synthNamesInResolver.length; i++) { bytes32 synthName = _synthNamesInResolver[i]; if (synths[i] != synthName) { return false; } if (synthsByKey[_synthKeys[i]] != synths[i]) { return false; } } return true; } /* ---------- UTILITIES ---------- */ // Check the account has enough of the synth to make the payment function _checkSynthBalance( address payer, bytes32 key, uint amount ) internal view { require(IERC20(address(_synth(synthsByKey[key]))).balanceOf(payer) >= amount, "Not enough synth balance"); } // We set the interest index to 0 to indicate the loan has been closed. function _checkLoanAvailable(Loan memory _loan) internal view { require(_loan.interestIndex > 0, "Loan does not exist"); require(_loan.lastInteraction.add(interactionDelay) <= block.timestamp, "Loan recently interacted with"); } function issuanceRatio() internal view returns (uint ratio) { ratio = SafeDecimalMath.unit().divideDecimalRound(minCratio); } /* ========== MUTATIVE FUNCTIONS ========== */ /* ---------- Synths ---------- */ function addSynths(bytes32[] calldata _synthNamesInResolver, bytes32[] calldata _synthKeys) external onlyOwner { require(_synthNamesInResolver.length == _synthKeys.length, "Input array length mismatch"); for (uint i = 0; i < _synthNamesInResolver.length; i++) { bytes32 synthName = _synthNamesInResolver[i]; synths.push(synthName); synthsByKey[_synthKeys[i]] = synthName; } // ensure cache has the latest rebuildCache(); } /* ---------- Rewards Contracts ---------- */ function addRewardsContracts(address rewardsContract, bytes32 synth) external onlyOwner { shortingRewards[synth] = rewardsContract; } /* ---------- SETTERS ---------- */ function setMinCratio(uint _minCratio) external onlyOwner { require(_minCratio > SafeDecimalMath.unit(), "Must be greater than 1"); minCratio = _minCratio; emit MinCratioRatioUpdated(minCratio); } function setIssueFeeRate(uint _issueFeeRate) external onlyOwner { issueFeeRate = _issueFeeRate; emit IssueFeeRateUpdated(issueFeeRate); } function setInteractionDelay(uint _interactionDelay) external onlyOwner { require(_interactionDelay <= SafeDecimalMath.unit() * 3600, "Max 1 hour"); interactionDelay = _interactionDelay; emit InteractionDelayUpdated(interactionDelay); } function setManager(address _newManager) external onlyOwner { manager = _newManager; emit ManagerUpdated(manager); } function setCanOpenLoans(bool _canOpenLoans) external onlyOwner { canOpenLoans = _canOpenLoans; emit CanOpenLoansUpdated(canOpenLoans); } /* ---------- LOAN INTERACTIONS ---------- */ function openInternal( uint collateral, uint amount, bytes32 currency, bool short ) internal returns (uint id) { // 0. Check the system is active. _systemStatus().requireIssuanceActive(); require(canOpenLoans, "Opening is disabled"); // 1. Make sure the collateral rate is valid. require(!_exchangeRates().rateIsInvalid(collateralKey), "Collateral rate is invalid"); // 2. We can only issue certain synths. require(synthsByKey[currency] > 0, "Not allowed to issue this synth"); // 3. Make sure the synth rate is not invalid. require(!_exchangeRates().rateIsInvalid(currency), "Currency rate is invalid"); // 4. Collateral >= minimum collateral size. require(collateral >= minCollateral, "Not enough collateral to open"); // 5. Cap the number of loans so that the array doesn't get too big. require(state.getNumLoans(msg.sender) < maxLoansPerAccount, "Max loans exceeded"); // 6. Check we haven't hit the debt cap for non snx collateral. (bool canIssue, bool anyRateIsInvalid) = _manager().exceedsDebtLimit(amount, currency); require(canIssue && !anyRateIsInvalid, "Debt limit or invalid rate"); // 7. Require requested loan < max loan require(amount <= maxLoan(collateral, currency), "Exceeds max borrowing power"); // 8. This fee is denominated in the currency of the loan uint issueFee = amount.multiplyDecimalRound(issueFeeRate); // 9. Calculate the minting fee and subtract it from the loan amount uint loanAmountMinusFee = amount.sub(issueFee); // 10. Get a Loan ID id = _manager().getNewLoanId(); // 11. Create the loan struct. Loan memory loan = Loan({ id: id, account: msg.sender, collateral: collateral, currency: currency, amount: amount, short: short, accruedInterest: 0, interestIndex: 0, lastInteraction: block.timestamp }); // 12. Accrue interest on the loan. loan = accrueInterest(loan); // 13. Save the loan to storage state.createLoan(loan); // 14. Pay the minting fees to the fee pool _payFees(issueFee, currency); // 15. If its short, convert back to sUSD, otherwise issue the loan. if (short) { _synthsUSD().issue(msg.sender, _exchangeRates().effectiveValue(currency, loanAmountMinusFee, sUSD)); _manager().incrementShorts(currency, amount); if (shortingRewards[currency] != address(0)) { IShortingRewards(shortingRewards[currency]).enrol(msg.sender, amount); } } else { _synth(synthsByKey[currency]).issue(msg.sender, loanAmountMinusFee); _manager().incrementLongs(currency, amount); } // 16. Emit event emit LoanCreated(msg.sender, id, amount, collateral, currency, issueFee); } function closeInternal(address borrower, uint id) internal returns (uint collateral) { // 0. Check the system is active. _systemStatus().requireIssuanceActive(); // 1. Make sure the collateral rate is valid require(!_exchangeRates().rateIsInvalid(collateralKey), "Collateral rate is invalid"); // 2. Get the loan. Loan memory loan = state.getLoan(borrower, id); // 3. Check loan is open and the last interaction time. _checkLoanAvailable(loan); // 4. Accrue interest on the loan. loan = accrueInterest(loan); // 5. Work out the total amount owing on the loan. uint total = loan.amount.add(loan.accruedInterest); // 6. Check they have enough balance to close the loan. _checkSynthBalance(loan.account, loan.currency, total); // 7. Burn the synths require( !_exchanger().hasWaitingPeriodOrSettlementOwing(borrower, loan.currency), "Waiting secs or settlement owing" ); _synth(synthsByKey[loan.currency]).burn(borrower, total); // 8. Tell the manager. if (loan.short) { _manager().decrementShorts(loan.currency, loan.amount); if (shortingRewards[loan.currency] != address(0)) { IShortingRewards(shortingRewards[loan.currency]).withdraw(borrower, loan.amount); } } else { _manager().decrementLongs(loan.currency, loan.amount); } // 9. Assign the collateral to be returned. collateral = loan.collateral; // 10. Pay fees _payFees(loan.accruedInterest, loan.currency); // 11. Record loan as closed loan.amount = 0; loan.collateral = 0; loan.accruedInterest = 0; loan.interestIndex = 0; loan.lastInteraction = block.timestamp; state.updateLoan(loan); // 12. Emit the event emit LoanClosed(borrower, id); } function closeByLiquidationInternal( address borrower, address liquidator, Loan memory loan ) internal returns (uint collateral) { // 1. Work out the total amount owing on the loan. uint total = loan.amount.add(loan.accruedInterest); // 2. Store this for the event. uint amount = loan.amount; // 3. Return collateral to the child class so it knows how much to transfer. collateral = loan.collateral; // 4. Burn the synths require(!_exchanger().hasWaitingPeriodOrSettlementOwing(liquidator, loan.currency), "Waiting or settlement owing"); _synth(synthsByKey[loan.currency]).burn(liquidator, total); // 5. Tell the manager. if (loan.short) { _manager().decrementShorts(loan.currency, loan.amount); if (shortingRewards[loan.currency] != address(0)) { IShortingRewards(shortingRewards[loan.currency]).withdraw(borrower, loan.amount); } } else { _manager().decrementLongs(loan.currency, loan.amount); } // 6. Pay fees _payFees(loan.accruedInterest, loan.currency); // 7. Record loan as closed loan.amount = 0; loan.collateral = 0; loan.accruedInterest = 0; loan.interestIndex = 0; loan.lastInteraction = block.timestamp; state.updateLoan(loan); // 8. Emit the event. emit LoanClosedByLiquidation(borrower, loan.id, liquidator, amount, collateral); } function depositInternal( address account, uint id, uint amount ) internal { // 0. Check the system is active. _systemStatus().requireIssuanceActive(); // 1. Make sure the collateral rate is valid. require(!_exchangeRates().rateIsInvalid(collateralKey), "Collateral rate is invalid"); // 2. They sent some value > 0 require(amount > 0, "Deposit must be greater than 0"); // 3. Get the loan Loan memory loan = state.getLoan(account, id); // 4. Check loan is open and last interaction time. _checkLoanAvailable(loan); // 5. Accrue interest loan = accrueInterest(loan); // 6. Add the collateral loan.collateral = loan.collateral.add(amount); // 7. Update the last interaction time. loan.lastInteraction = block.timestamp; // 8. Store the loan state.updateLoan(loan); // 9. Emit the event emit CollateralDeposited(account, id, amount, loan.collateral); } function withdrawInternal(uint id, uint amount) internal returns (uint withdraw) { // 0. Check the system is active. _systemStatus().requireIssuanceActive(); // 1. Make sure the collateral rate is valid. require(!_exchangeRates().rateIsInvalid(collateralKey), "Collateral rate is invalid"); // 2. Get the loan. Loan memory loan = state.getLoan(msg.sender, id); // 3. Check loan is open and last interaction time. _checkLoanAvailable(loan); // 4. Accrue interest. loan = accrueInterest(loan); // 5. Subtract the collateral. loan.collateral = loan.collateral.sub(amount); // 6. Update the last interaction time. loan.lastInteraction = block.timestamp; // 7. Check that the new amount does not put them under the minimum c ratio. require(collateralRatio(loan) > minCratio, "Cratio too low"); // 8. Store the loan. state.updateLoan(loan); // 9. Assign the return variable. withdraw = amount; // 10. Emit the event. emit CollateralWithdrawn(msg.sender, id, amount, loan.collateral); } function liquidateInternal( address borrower, uint id, uint payment ) internal returns (uint collateralLiquidated) { // 0. Check the system is active. _systemStatus().requireIssuanceActive(); // 1. Make sure the collateral rate is valid. require(!_exchangeRates().rateIsInvalid(collateralKey), "Collateral rate is invalid"); // 2. Check the payment amount. require(payment > 0, "Payment must be greater than 0"); // 3. Get the loan. Loan memory loan = state.getLoan(borrower, id); // 4. Check loan is open and last interaction time. _checkLoanAvailable(loan); // 5. Accrue interest. loan = accrueInterest(loan); // 6. Check they have enough balance to make the payment. _checkSynthBalance(msg.sender, loan.currency, payment); // 7. Check they are eligible for liquidation. require(collateralRatio(loan) < minCratio, "Cratio above liquidation ratio"); // 8. Determine how much needs to be liquidated to fix their c ratio. uint liqAmount = liquidationAmount(loan); // 9. Only allow them to liquidate enough to fix the c ratio. uint amountToLiquidate = liqAmount < payment ? liqAmount : payment; // 10. Work out the total amount owing on the loan. uint amountOwing = loan.amount.add(loan.accruedInterest); // 11. If its greater than the amount owing, we need to close the loan. if (amountToLiquidate >= amountOwing) { return closeByLiquidationInternal(borrower, msg.sender, loan); } // 12. Process the payment to workout interest/principal split. loan = _processPayment(loan, amountToLiquidate); // 13. Work out how much collateral to redeem. collateralLiquidated = collateralRedeemed(loan.currency, amountToLiquidate); loan.collateral = loan.collateral.sub(collateralLiquidated); // 14. Update the last interaction time. loan.lastInteraction = block.timestamp; // 15. Burn the synths from the liquidator. require(!_exchanger().hasWaitingPeriodOrSettlementOwing(msg.sender, loan.currency), "Waiting or settlement owing"); _synth(synthsByKey[loan.currency]).burn(msg.sender, amountToLiquidate); // 16. Store the loan. state.updateLoan(loan); // 17. Emit the event emit LoanPartiallyLiquidated(borrower, id, msg.sender, amountToLiquidate, collateralLiquidated); } function repayInternal( address borrower, address repayer, uint id, uint payment ) internal { // 0. Check the system is active. _systemStatus().requireIssuanceActive(); // 1. Make sure the collateral rate is valid. require(!_exchangeRates().rateIsInvalid(collateralKey), "Collateral rate is invalid"); // 2. Check the payment amount. require(payment > 0, "Payment must be greater than 0"); // 3. Get loan Loan memory loan = state.getLoan(borrower, id); // 4. Check loan is open and last interaction time. _checkLoanAvailable(loan); // 5. Accrue interest. loan = accrueInterest(loan); // 6. Check the spender has enough synths to make the repayment _checkSynthBalance(repayer, loan.currency, payment); // 7. Process the payment. loan = _processPayment(loan, payment); // 8. Update the last interaction time. loan.lastInteraction = block.timestamp; // 9. Burn synths from the payer require(!_exchanger().hasWaitingPeriodOrSettlementOwing(repayer, loan.currency), "Waiting or settlement owing"); _synth(synthsByKey[loan.currency]).burn(repayer, payment); // 10. Store the loan state.updateLoan(loan); // 11. Emit the event. emit LoanRepaymentMade(borrower, repayer, id, payment, loan.amount); } function drawInternal(uint id, uint amount) internal { // 0. Check the system is active. _systemStatus().requireIssuanceActive(); // 1. Make sure the collateral rate is valid. require(!_exchangeRates().rateIsInvalid(collateralKey), "Collateral rate is invalid"); // 2. Get loan. Loan memory loan = state.getLoan(msg.sender, id); // 3. Check loan is open and last interaction time. _checkLoanAvailable(loan); // 4. Accrue interest. loan = accrueInterest(loan); // 5. Add the requested amount. loan.amount = loan.amount.add(amount); // 6. If it is below the minimum, don't allow this draw. require(collateralRatio(loan) > minCratio, "Cannot draw this much"); // 7. This fee is denominated in the currency of the loan uint issueFee = amount.multiplyDecimalRound(issueFeeRate); // 8. Calculate the minting fee and subtract it from the draw amount uint amountMinusFee = amount.sub(issueFee); // 9. If its short, let the child handle it, otherwise issue the synths. if (loan.short) { _manager().incrementShorts(loan.currency, amount); _synthsUSD().issue(msg.sender, _exchangeRates().effectiveValue(loan.currency, amountMinusFee, sUSD)); if (shortingRewards[loan.currency] != address(0)) { IShortingRewards(shortingRewards[loan.currency]).enrol(msg.sender, amount); } } else { _manager().incrementLongs(loan.currency, amount); _synth(synthsByKey[loan.currency]).issue(msg.sender, amountMinusFee); } // 10. Pay the minting fees to the fee pool _payFees(issueFee, loan.currency); // 11. Update the last interaction time. loan.lastInteraction = block.timestamp; // 12. Store the loan state.updateLoan(loan); // 13. Emit the event. emit LoanDrawnDown(msg.sender, id, amount); } // Update the cumulative interest rate for the currency that was interacted with. function accrueInterest(Loan memory loan) internal returns (Loan memory loanAfter) { loanAfter = loan; // 1. Get the rates we need. (uint entryRate, uint lastRate, uint lastUpdated, uint newIndex) = loan.short ? _manager().getShortRatesAndTime(loan.currency, loan.interestIndex) : _manager().getRatesAndTime(loan.interestIndex); // 2. Get the instantaneous rate. (uint rate, bool invalid) = loan.short ? _manager().getShortRate(synthsByKey[loan.currency]) : _manager().getBorrowRate(); require(!invalid, "Rates are invalid"); // 3. Get the time since we last updated the rate. uint timeDelta = block.timestamp.sub(lastUpdated).mul(SafeDecimalMath.unit()); // 4. Get the latest cumulative rate. F_n+1 = F_n + F_last uint latestCumulative = lastRate.add(rate.multiplyDecimal(timeDelta)); // 5. If the loan was just opened, don't record any interest. Otherwise multiple by the amount outstanding. uint interest = loan.interestIndex == 0 ? 0 : loan.amount.multiplyDecimal(latestCumulative.sub(entryRate)); // 7. Update rates with the lastest cumulative rate. This also updates the time. loan.short ? _manager().updateShortRates(loan.currency, latestCumulative) : _manager().updateBorrowRates(latestCumulative); // 8. Update loan loanAfter.accruedInterest = loan.accruedInterest.add(interest); loanAfter.interestIndex = newIndex; state.updateLoan(loanAfter); } // Works out the amount of interest and principal after a repayment is made. function _processPayment(Loan memory loanBefore, uint payment) internal returns (Loan memory loanAfter) { loanAfter = loanBefore; if (payment > 0 && loanBefore.accruedInterest > 0) { uint interestPaid = payment > loanBefore.accruedInterest ? loanBefore.accruedInterest : payment; loanAfter.accruedInterest = loanBefore.accruedInterest.sub(interestPaid); payment = payment.sub(interestPaid); _payFees(interestPaid, loanBefore.currency); } // If there is more payment left after the interest, pay down the principal. if (payment > 0) { loanAfter.amount = loanBefore.amount.sub(payment); // And get the manager to reduce the total long/short balance. if (loanAfter.short) { _manager().decrementShorts(loanAfter.currency, payment); if (shortingRewards[loanAfter.currency] != address(0)) { IShortingRewards(shortingRewards[loanAfter.currency]).withdraw(loanAfter.account, payment); } } else { _manager().decrementLongs(loanAfter.currency, payment); } } } // Take an amount of fees in a certain synth and convert it to sUSD before paying the fee pool. function _payFees(uint amount, bytes32 synth) internal { if (amount > 0) { if (synth != sUSD) { amount = _exchangeRates().effectiveValue(synth, amount, sUSD); } _synthsUSD().issue(_feePool().FEE_ADDRESS(), amount); _feePool().recordFeePaid(amount); } } // ========== EVENTS ========== // Setters event MinCratioRatioUpdated(uint minCratio); event MinCollateralUpdated(uint minCollateral); event IssueFeeRateUpdated(uint issueFeeRate); event MaxLoansPerAccountUpdated(uint maxLoansPerAccount); event InteractionDelayUpdated(uint interactionDelay); event ManagerUpdated(address manager); event CanOpenLoansUpdated(bool canOpenLoans); // Loans event LoanCreated(address indexed account, uint id, uint amount, uint collateral, bytes32 currency, uint issuanceFee); event LoanClosed(address indexed account, uint id); event CollateralDeposited(address indexed account, uint id, uint amountDeposited, uint collateralAfter); event CollateralWithdrawn(address indexed account, uint id, uint amountWithdrawn, uint collateralAfter); event LoanRepaymentMade(address indexed account, address indexed repayer, uint id, uint amountRepaid, uint amountAfter); event LoanDrawnDown(address indexed account, uint id, uint amount); event LoanPartiallyLiquidated( address indexed account, uint id, address liquidator, uint amountLiquidated, uint collateralLiquidated ); event LoanClosedByLiquidation( address indexed account, uint id, address indexed liquidator, uint amountLiquidated, uint collateralLiquidated ); } interface ICollateralErc20 { function open( uint collateral, uint amount, bytes32 currency ) external; function close(uint id) external; function deposit( address borrower, uint id, uint collateral ) external; function withdraw(uint id, uint amount) external; function repay( address borrower, uint id, uint amount ) external; function draw(uint id, uint amount) external; function liquidate( address borrower, uint id, uint amount ) external; } // Inheritance // Internal references // This contract handles the specific ERC20 implementation details of managing a loan. contract CollateralErc20 is ICollateralErc20, Collateral { // The underlying asset for this ERC20 collateral address public underlyingContract; uint public underlyingContractDecimals; constructor( CollateralState _state, address _owner, address _manager, address _resolver, bytes32 _collateralKey, uint _minCratio, uint _minCollateral, address _underlyingContract, uint _underlyingDecimals ) public Collateral(_state, _owner, _manager, _resolver, _collateralKey, _minCratio, _minCollateral) { underlyingContract = _underlyingContract; underlyingContractDecimals = _underlyingDecimals; } function open( uint collateral, uint amount, bytes32 currency ) external { require(collateral <= IERC20(underlyingContract).allowance(msg.sender, address(this)), "Allowance not high enough"); // only transfer the actual collateral IERC20(underlyingContract).transferFrom(msg.sender, address(this), collateral); // scale up before entering the system. uint scaledCollateral = scaleUpCollateral(collateral); openInternal(scaledCollateral, amount, currency, false); } function close(uint id) external { uint collateral = closeInternal(msg.sender, id); // scale down before transferring back. uint scaledCollateral = scaleDownCollateral(collateral); IERC20(underlyingContract).transfer(msg.sender, scaledCollateral); } function deposit( address borrower, uint id, uint amount ) external { require(amount <= IERC20(underlyingContract).allowance(msg.sender, address(this)), "Allowance not high enough"); IERC20(underlyingContract).transferFrom(msg.sender, address(this), amount); // scale up before entering the system. uint scaledAmount = scaleUpCollateral(amount); depositInternal(borrower, id, scaledAmount); } function withdraw(uint id, uint amount) external { // scale up before entering the system. uint scaledAmount = scaleUpCollateral(amount); uint withdrawnAmount = withdrawInternal(id, scaledAmount); // scale down before transferring back. uint scaledWithdraw = scaleDownCollateral(withdrawnAmount); IERC20(underlyingContract).transfer(msg.sender, scaledWithdraw); } function repay( address borrower, uint id, uint amount ) external { repayInternal(borrower, msg.sender, id, amount); } function draw(uint id, uint amount) external { drawInternal(id, amount); } function liquidate( address borrower, uint id, uint amount ) external { uint collateralLiquidated = liquidateInternal(borrower, id, amount); // scale down before transferring back. uint scaledCollateral = scaleDownCollateral(collateralLiquidated); IERC20(underlyingContract).transfer(msg.sender, scaledCollateral); } function scaleUpCollateral(uint collateral) public view returns (uint scaledUp) { uint conversionFactor = 10**uint(SafeMath.sub(18, underlyingContractDecimals)); scaledUp = uint(uint(collateral).mul(conversionFactor)); } function scaleDownCollateral(uint collateral) public view returns (uint scaledDown) { uint conversionFactor = 10**uint(SafeMath.sub(18, underlyingContractDecimals)); scaledDown = collateral.div(conversionFactor); } }
[{"inputs":[{"internalType":"contract CollateralState","name":"_state","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_manager","type":"address"},{"internalType":"address","name":"_resolver","type":"address"},{"internalType":"bytes32","name":"_collateralKey","type":"bytes32"},{"internalType":"uint256","name":"_minCratio","type":"uint256"},{"internalType":"uint256","name":"_minCollateral","type":"uint256"},{"internalType":"address","name":"_underlyingContract","type":"address"},{"internalType":"uint256","name":"_underlyingDecimals","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"name","type":"bytes32"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"CacheUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"canOpenLoans","type":"bool"}],"name":"CanOpenLoansUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountDeposited","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralAfter","type":"uint256"}],"name":"CollateralDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountWithdrawn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralAfter","type":"uint256"}],"name":"CollateralWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"interactionDelay","type":"uint256"}],"name":"InteractionDelayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"issueFeeRate","type":"uint256"}],"name":"IssueFeeRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"LoanClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountLiquidated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralLiquidated","type":"uint256"}],"name":"LoanClosedByLiquidation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateral","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"currency","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"issuanceFee","type":"uint256"}],"name":"LoanCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LoanDrawnDown","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountLiquidated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralLiquidated","type":"uint256"}],"name":"LoanPartiallyLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"repayer","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountRepaid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountAfter","type":"uint256"}],"name":"LoanRepaymentMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"manager","type":"address"}],"name":"ManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxLoansPerAccount","type":"uint256"}],"name":"MaxLoansPerAccountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minCollateral","type":"uint256"}],"name":"MinCollateralUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minCratio","type":"uint256"}],"name":"MinCratioRatioUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"rewardsContract","type":"address"},{"internalType":"bytes32","name":"synth","type":"bytes32"}],"name":"addRewardsContracts","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32[]","name":"_synthNamesInResolver","type":"bytes32[]"},{"internalType":"bytes32[]","name":"_synthKeys","type":"bytes32[]"}],"name":"addSynths","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32[]","name":"_synthNamesInResolver","type":"bytes32[]"},{"internalType":"bytes32[]","name":"_synthKeys","type":"bytes32[]"}],"name":"areSynthsAndCurrenciesSet","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"canOpenLoans","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"close","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"collateralKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address payable","name":"account","type":"address"},{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"bytes32","name":"currency","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"short","type":"bool"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"interestIndex","type":"uint256"},{"internalType":"uint256","name":"lastInteraction","type":"uint256"}],"internalType":"struct ICollateralLoan.Loan","name":"loan","type":"tuple"}],"name":"collateralRatio","outputs":[{"internalType":"uint256","name":"cratio","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currency","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"collateralRedeemed","outputs":[{"internalType":"uint256","name":"collateral","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"draw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"interactionDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isResolverCached","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"issueFeeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"liquidate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address payable","name":"account","type":"address"},{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"bytes32","name":"currency","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"short","type":"bool"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"interestIndex","type":"uint256"},{"internalType":"uint256","name":"lastInteraction","type":"uint256"}],"internalType":"struct ICollateralLoan.Loan","name":"loan","type":"tuple"}],"name":"liquidationAmount","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"currency","type":"bytes32"}],"name":"maxLoan","outputs":[{"internalType":"uint256","name":"max","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxLoansPerAccount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"minCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"minCratio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"currency","type":"bytes32"}],"name":"open","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"rebuildCache","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"repay","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"resolver","outputs":[{"internalType":"contract AddressResolver","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"resolverAddressesRequired","outputs":[{"internalType":"bytes32[]","name":"addresses","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"collateral","type":"uint256"}],"name":"scaleDownCollateral","outputs":[{"internalType":"uint256","name":"scaledDown","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"collateral","type":"uint256"}],"name":"scaleUpCollateral","outputs":[{"internalType":"uint256","name":"scaledUp","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bool","name":"_canOpenLoans","type":"bool"}],"name":"setCanOpenLoans","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_interactionDelay","type":"uint256"}],"name":"setInteractionDelay","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_issueFeeRate","type":"uint256"}],"name":"setIssueFeeRate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_newManager","type":"address"}],"name":"setManager","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_minCratio","type":"uint256"}],"name":"setMinCratio","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"shortingRewards","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"internalType":"contract CollateralState","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"synths","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"synthsByKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"underlyingContract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"underlyingContractDecimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040526032600d5561012c600e55600f805460ff191660011790553480156200002957600080fd5b5060405162006350380380620063508339810160408190526200004c916200018c565b888888888888888380876001600160a01b038116620000885760405162461bcd60e51b81526004016200007f90620002e0565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0383161781556040517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c91620000d5918490620002ba565b60405180910390a150600280546001600160a01b039283166001600160a01b0319918216179091556006805498831698821698909817909755600580549982169990971698909817909555600492909255600a55600b555050600f80549490921661010002610100600160a81b031990941693909317905560105550620003619650505050505050565b80516200016c8162000331565b92915050565b80516200016c816200034b565b80516200016c8162000356565b60008060008060008060008060006101208a8c031215620001ac57600080fd5b6000620001ba8c8c6200017f565b9950506020620001cd8c828d016200015f565b9850506040620001e08c828d016200015f565b9750506060620001f38c828d016200015f565b9650506080620002068c828d0162000172565b95505060a0620002198c828d0162000172565b94505060c06200022c8c828d0162000172565b93505060e06200023f8c828d016200015f565b925050610100620002538c828d0162000172565b9150509295985092959850929598565b6200026e8162000324565b82525050565b6200026e81620002fb565b60006200028e601983620002f2565b7f4f776e657220616464726573732063616e6e6f74206265203000000000000000815260200192915050565b60408101620002ca828562000263565b620002d9602083018462000274565b9392505050565b602080825281016200016c816200027f565b90815260200190565b60006200016c8262000318565b90565b60006200016c82620002fb565b6001600160a01b031690565b60006200016c826200030b565b6200033c81620002fb565b81146200034857600080fd5b50565b6200033c8162000308565b6200033c816200030b565b615fdf80620003716000396000f3fe608060405234801561001057600080fd5b50600436106102695760003560e01c806372e18b6a11610151578063b094f2c4116100c3578063d2b8035a11610087578063d2b8035a146104be578063dac8cf68146104d1578063de81eda9146104e4578063e74337c6146104f7578063eb8e3b651461050a578063f93451ed1461051257610269565b8063b094f2c414610480578063b562a1ab14610493578063ba2de9bc1461049b578063c19d93fb146104a3578063d0ebdbe7146104ab57610269565b8063899ffef411610115578063899ffef4146104225780638cd2e0c7146104375780638da5cb5b1461044a57806390abb4d914610452578063925ead1114610465578063a76cdfa51461046d57610269565b806372e18b6a146103d957806374185360146103ec57806379ba5097146103f45780637e132355146103fc578063883a22091461040f57610269565b80632af64bd3116101ea5780634065b81b116101ae5780634065b81b14610388578063441a3e7014610390578063481c6a75146103a35780634c17ace4146103ab57806353a47bb7146103be5780635eb2ad01146103c657610269565b80632af64bd31461033d57806330edd96114610352578063361e208614610365578063382453771461036d5780633cc3ffc71461038057610269565b80630efe6a8b116102315780630efe6a8b146102e757806310cfe906146102fa57806315aaf4dd1461030f5780631627540c1461031757806323d60e2e1461032a57610269565b806304f3bcec1461026e5780630710285c1461028c5780630a153c97146102a15780630aebeb4e146102c15780630cdd1c65146102d4575b600080fd5b610276610525565b6040516102839190615c4b565b60405180910390f35b61029f61029a36600461504f565b610534565b005b6102b46102af366004615196565b6105e0565b6040516102839190615be6565b61029f6102cf366004615178565b61071c565b61029f6102e2366004615178565b6107c5565b61029f6102f536600461504f565b6108b0565b6103026109f8565b6040516102839190615b31565b6102b4610a0c565b61029f610325366004614fd9565b610a12565b61029f61033836600461509c565b610a65565b610345610b25565b6040516102839190615bd8565b6102b4610360366004615178565b610c3d565b6102b4610c5b565b6102b461037b366004615178565b610c61565b6102b4610c73565b610345610c79565b61029f61039e366004615196565b610c82565b610302610c9b565b61029f6103b9366004615178565b610caa565b610302610d86565b61029f6103d4366004615015565b610d95565b6103456103e736600461509c565b610dcb565b61029f610e8d565b61029f610fe3565b6102b461040a366004615196565b61107f565b6102b461041d366004615178565b611128565b61042a61114d565b6040516102839190615bc7565b61029f61044536600461504f565b6112b2565b6103026112c3565b61029f61046036600461510c565b6112d2565b6102b461131e565b61029f61047b366004615178565b611324565b6102b461048e366004615178565b611361565b6102b4611386565b6102b461138c565b610276611392565b61029f6104b9366004614fd9565b6113a1565b61029f6104cc366004615196565b6113fa565b6102b46104df3660046151b5565b611404565b6103026104f2366004615178565b61155d565b6102b46105053660046151b5565b611578565b6102b4611862565b61029f610520366004615230565b611868565b6002546001600160a01b031681565b60006105418484846119b9565b9050600061054e82611361565b600f5460405163a9059cbb60e01b815291925061010090046001600160a01b03169063a9059cbb906105869033908590600401615b90565b602060405180830381600087803b1580156105a057600080fd5b505af11580156105b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506105d8919081019061512a565b505050505050565b6000806105eb611e1e565b90506105f5611ed0565b6001600160a01b031663654a60ac85856004546040518463ffffffff1660e01b815260040161062693929190615c30565b60206040518083038186803b15801561063e57600080fd5b505afa158015610652573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061067691908101906151f3565b915061071461070782731a60e2e2a8be0bc2b6381dd31fd3fd5f9a28de4c63907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b1580156106c357600080fd5b505af41580156106d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106fb91908101906151f3565b9063ffffffff611eeb16565b839063ffffffff611f1916565b949350505050565b60006107283383611f43565b9050600061073582611361565b600f5460405163a9059cbb60e01b815291925061010090046001600160a01b03169063a9059cbb9061076d9033908590600401615b90565b602060405180830381600087803b15801561078757600080fd5b505af115801561079b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506107bf919081019061512a565b50505050565b6107cd6124a8565b731a60e2e2a8be0bc2b6381dd31fd3fd5f9a28de4c63907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561081157600080fd5b505af4158015610825573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061084991908101906151f3565b81116108705760405162461bcd60e51b815260040161086790615c7a565b60405180910390fd5b600a8190556040517f813a44586e8ecb9390b2568dbe810e193087f80e415c8845340ef06d4cbb42a5906108a5908390615be6565b60405180910390a150565b600f54604051636eb1769f60e11b81526101009091046001600160a01b03169063dd62ed3e906108e69033903090600401615b4d565b60206040518083038186803b1580156108fe57600080fd5b505afa158015610912573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061093691908101906151f3565b8111156109555760405162461bcd60e51b815260040161086790615cba565b600f546040516323b872dd60e01b81526101009091046001600160a01b0316906323b872dd9061098d90339030908690600401615b68565b602060405180830381600087803b1580156109a757600080fd5b505af11580156109bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109df919081019061512a565b5060006109eb82611128565b90506107bf8484836124d4565b600f5461010090046001600160a01b031681565b600e5481565b610a1a6124a8565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906108a5908390615b31565b610a6d6124a8565b828114610a8c5760405162461bcd60e51b815260040161086790615c6a565b60005b83811015610b1c576000858583818110610aa557fe5b600780546001810182556000918252602090920293909301357fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6889091018190559250829160089150868686818110610af957fe5b602090810292909201358352508101919091526040016000205550600101610a8f565b506107bf610e8d565b60006060610b3161114d565b905060005b8151811015610c33576000828281518110610b4d57fe5b602090810291909101810151600081815260039092526040918290205460025492516321f8a72160e01b81529193506001600160a01b039081169216906321f8a72190610b9e908590600401615be6565b60206040518083038186803b158015610bb657600080fd5b505afa158015610bca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610bee9190810190614ff7565b6001600160a01b0316141580610c1957506000818152600360205260409020546001600160a01b0316155b15610c2a5760009350505050610c3a565b50600101610b36565b5060019150505b90565b60078181548110610c4a57fe5b600091825260209091200154905081565b600c5481565b60086020526000908152604090205481565b60105481565b600f5460ff1681565b6000610c8d82611128565b905060006105418483612765565b6006546001600160a01b031681565b610cb26124a8565b731a60e2e2a8be0bc2b6381dd31fd3fd5f9a28de4c63907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b158015610cf657600080fd5b505af4158015610d0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d2e91908101906151f3565b610e1002811115610d515760405162461bcd60e51b815260040161086790615d8a565b600e8190556040517f4d71c92b0a9dc236066597b95637bb04d58cd135e9165aee13eb68e3199c2361906108a5908390615be6565b6001546001600160a01b031681565b610d9d6124a8565b600090815260096020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b6007546000908414610ddf57506000610714565b60005b84811015610e81576000868683818110610df857fe5b9050602002013590508060078381548110610e0f57fe5b906000526020600020015414610e2a57600092505050610714565b60078281548110610e3757fe5b906000526020600020015460086000878786818110610e5257fe5b9050602002013581526020019081526020016000205414610e7857600092505050610714565b50600101610de2565b50600195945050505050565b6060610e9761114d565b905060005b8151811015610fdf576000828281518110610eb357fe5b602002602001015190506000600260009054906101000a90046001600160a01b03166001600160a01b031663dacb2d018384604051602001610ef59190615b26565b6040516020818303038152906040526040518363ffffffff1660e01b8152600401610f21929190615c10565b60206040518083038186803b158015610f3957600080fd5b505afa158015610f4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f719190810190614ff7565b6000838152600360205260409081902080546001600160a01b0319166001600160a01b038416179055519091507f88a93678a3692f6789d9546fc621bf7234b101ddb7d4fe479455112831b8aa6890610fcd9084908490615bf4565b60405180910390a15050600101610e9c565b5050565b6001546001600160a01b0316331461100d5760405162461bcd60e51b815260040161086790615caa565b6000546001546040517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c92611050926001600160a01b0391821692911690615bab565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b600061112161108c611ed0565b6001600160a01b031663654a60ac60045486866040518463ffffffff1660e01b81526004016110bd93929190615c30565b60206040518083038186803b1580156110d557600080fd5b505afa1580156110e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061110d91908101906151f3565b611115612a02565b9063ffffffff611f1916565b9392505050565b6000806111386012601054612a92565b600a0a9050611121838263ffffffff612aba16565b606080611158612af4565b60408051600580825260c08201909252919250606091906020820160a08038833901905050905066119959541bdbdb60ca1b8160008151811061119757fe5b6020026020010181815250506c45786368616e6765526174657360981b816001815181106111c157fe5b6020026020010181815250506822bc31b430b733b2b960b91b816002815181106111e757fe5b6020026020010181815250506b53797374656d53746174757360a01b8160038151811061121057fe5b6020026020010181815250506814de5b9d1a1cd554d160ba1b8160048151811061123657fe5b602002602001018181525050606061124e8383612b45565b90506112aa8160078054806020026020016040519081016040528092919081815260200182805480156112a057602002820191906000526020600020905b81548152602001906001019080831161128c575b5050505050612b45565b935050505090565b6112be83338484612c01565b505050565b6000546001600160a01b031681565b6112da6124a8565b600f805460ff191682151517908190556040517f261991749e1b2436706a31bde8bf184bb37fe21e303709b78d3b881afacadaa2916108a59160ff90911690615bd8565b600a5481565b61132c6124a8565b600c8190556040517fe7bd72551c54d568cd97b00dc52d2787b5c5d4f0070d3582c1e8ba25141f799c906108a5908390615be6565b6000806113716012601054612a92565b600a0a9050611121838263ffffffff612fbc16565b60045481565b600b5481565b6005546001600160a01b031681565b6113a96124a8565b600680546001600160a01b0319166001600160a01b0383811691909117918290556040517f2c1c11af44aa5608f1dca38c00275c30ea091e02417d36e70e9a1538689c433d926108a5921690615b31565b610fdf8282612ff1565b60008061140f611ed0565b6001600160a01b031663654a60ac6004548560400151631cd554d160e21b6040518463ffffffff1660e01b815260040161144b93929190615c30565b60206040518083038186803b15801561146357600080fd5b505afa158015611477573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061149b91908101906151f3565b905060006114a7611ed0565b6001600160a01b031663654a60ac85606001516114d58760c001518860800151611eeb90919063ffffffff16565b631cd554d160e21b6040518463ffffffff1660e01b81526004016114fb93929190615c30565b60206040518083038186803b15801561151357600080fd5b505afa158015611527573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061154b91908101906151f3565b9050610714828263ffffffff6135b116565b6009602052600090815260409020546001600160a01b031681565b600080611583611e1e565b9050600061158f611ed0565b6001600160a01b031663654a60ac85606001516115bd8760c001518860800151611eeb90919063ffffffff16565b631cd554d160e21b6040518463ffffffff1660e01b81526004016115e393929190615c30565b60206040518083038186803b1580156115fb57600080fd5b505afa15801561160f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061163391908101906151f3565b9050600061163f611ed0565b6001600160a01b031663654a60ac6004548760400151631cd554d160e21b6040518463ffffffff1660e01b815260040161167b93929190615c30565b60206040518083038186803b15801561169357600080fd5b505afa1580156116a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116cb91908101906151f3565b90506000731a60e2e2a8be0bc2b6381dd31fd3fd5f9a28de4c63907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561171357600080fd5b505af4158015611727573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061174b91908101906151f3565b90506000611774611767600a54856135b190919063ffffffff16565b859063ffffffff612a9216565b905060006117ad6117a0600a546117948987611eeb90919063ffffffff16565b9063ffffffff6135b116565b849063ffffffff612a9216565b905060006117c1838363ffffffff6135b116565b90506117cb611ed0565b6001600160a01b031663654a60ac631cd554d160e21b838c606001516040518463ffffffff1660e01b815260040161180593929190615c30565b60206040518083038186803b15801561181d57600080fd5b505afa158015611831573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061185591908101906151f3565b9998505050505050505050565b600d5481565b600f54604051636eb1769f60e11b81526101009091046001600160a01b03169063dd62ed3e9061189e9033903090600401615b4d565b60206040518083038186803b1580156118b657600080fd5b505afa1580156118ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ee91908101906151f3565b83111561190d5760405162461bcd60e51b815260040161086790615cba565b600f546040516323b872dd60e01b81526101009091046001600160a01b0316906323b872dd9061194590339030908890600401615b68565b602060405180830381600087803b15801561195f57600080fd5b505af1158015611973573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611997919081019061512a565b5060006119a384611128565b90506119b281848460006135db565b5050505050565b60006119c3613e04565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b1580156119fb57600080fd5b505afa158015611a0f573d6000803e3d6000fd5b50505050611a1b611ed0565b6001600160a01b0316632528f0fe6004546040518263ffffffff1660e01b8152600401611a489190615be6565b60206040518083038186803b158015611a6057600080fd5b505afa158015611a74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a98919081019061512a565b15611ab55760405162461bcd60e51b815260040161086790615d9a565b60008211611ad55760405162461bcd60e51b815260040161086790615cda565b611add614d53565b6005546040516350e28ac360e11b81526001600160a01b039091169063a1c5158690611b0f9088908890600401615bb9565b6101206040518083038186803b158015611b2857600080fd5b505afa158015611b3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b6091908101906151d4565b9050611b6b81613e1e565b611b7481613e7e565b9050611b853382606001518561436a565b600a54611b9182611404565b10611bae5760405162461bcd60e51b815260040161086790615d0a565b6000611bb982611578565b90506000848210611bca5784611bcc565b815b90506000611beb8460c001518560800151611eeb90919063ffffffff16565b9050808210611c0a57611bff88338661441d565b945050505050611121565b611c1484836147ec565b9350611c248460600151836105e0565b6040850151909550611c3c908663ffffffff612a9216565b604085015242610100850152611c50614a0f565b6001600160a01b031663d6f32e063386606001516040518363ffffffff1660e01b8152600401611c81929190615b90565b60206040518083038186803b158015611c9957600080fd5b505afa158015611cad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611cd1919081019061512a565b15611cee5760405162461bcd60e51b815260040161086790615e2a565b6060840151600090815260086020526040902054611d0b90614a26565b6001600160a01b0316639dc29fac33846040518363ffffffff1660e01b8152600401611d38929190615b90565b600060405180830381600087803b158015611d5257600080fd5b505af1158015611d66573d6000803e3d6000fd5b5050600554604051631137390760e21b81526001600160a01b0390911692506344dce41c9150611d9a908790600401615e3a565b600060405180830381600087803b158015611db457600080fd5b505af1158015611dc8573d6000803e3d6000fd5b50505050876001600160a01b03167fb6e43890aeea54fbe6c0ed628e78172a0ff30bbcb1d70d8b130b12c366bac4c588338589604051611e0b9493929190615e49565b60405180910390a2505050509392505050565b6000611e28614a31565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b716c69717569646174696f6e50656e616c747960701b6040518363ffffffff1660e01b8152600401611e7b929190615c02565b60206040518083038186803b158015611e9357600080fd5b505afa158015611ea7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ecb91908101906151f3565b905090565b6000611ecb6c45786368616e6765526174657360981b614a4a565b600082820183811015611f105760405162461bcd60e51b815260040161086790615cea565b90505b92915050565b6000670de0b6b3a7640000611f34848463ffffffff612aba16565b81611f3b57fe5b049392505050565b6000611f4d613e04565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b158015611f8557600080fd5b505afa158015611f99573d6000803e3d6000fd5b50505050611fa5611ed0565b6001600160a01b0316632528f0fe6004546040518263ffffffff1660e01b8152600401611fd29190615be6565b60206040518083038186803b158015611fea57600080fd5b505afa158015611ffe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612022919081019061512a565b1561203f5760405162461bcd60e51b815260040161086790615d9a565b612047614d53565b6005546040516350e28ac360e11b81526001600160a01b039091169063a1c51586906120799087908790600401615bb9565b6101206040518083038186803b15801561209257600080fd5b505afa1580156120a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506120ca91908101906151d4565b90506120d581613e1e565b6120de81613e7e565b905060006120fd8260c001518360800151611eeb90919063ffffffff16565b9050612112826020015183606001518361436a565b61211a614a0f565b6001600160a01b031663d6f32e068684606001516040518363ffffffff1660e01b815260040161214b929190615bb9565b60206040518083038186803b15801561216357600080fd5b505afa158015612177573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061219b919081019061512a565b156121b85760405162461bcd60e51b815260040161086790615d3a565b60608201516000908152600860205260409020546121d590614a26565b6001600160a01b0316639dc29fac86836040518363ffffffff1660e01b8152600401612202929190615bb9565b600060405180830381600087803b15801561221c57600080fd5b505af1158015612230573d6000803e3d6000fd5b505050508160a001511561234e57612246614aa7565b6001600160a01b0316635246f2b9836060015184608001516040518363ffffffff1660e01b815260040161227b929190615c02565b600060405180830381600087803b15801561229557600080fd5b505af11580156122a9573d6000803e3d6000fd5b5050505060608201516000908152600960205260409020546001600160a01b03161561234957606082015160009081526009602052604090819020546080840151915163f3fef3a360e01b81526001600160a01b039091169163f3fef3a391612316918991600401615bb9565b600060405180830381600087803b15801561233057600080fd5b505af1158015612344573d6000803e3d6000fd5b505050505b6123be565b612356614aa7565b6001600160a01b031663e50a31b3836060015184608001516040518363ffffffff1660e01b815260040161238b929190615c02565b600060405180830381600087803b1580156123a557600080fd5b505af11580156123b9573d6000803e3d6000fd5b505050505b816040015192506123d78260c001518360600151614ab6565b600060808301819052604080840182905260c0840182905260e0840191909152426101008401526005549051631137390760e21b81526001600160a01b03909116906344dce41c9061242d908590600401615e3a565b600060405180830381600087803b15801561244757600080fd5b505af115801561245b573d6000803e3d6000fd5b50505050846001600160a01b03167fcab22a4e95d29d40da2ace3f6ec72b49954a9bc7b2584f8fd47bf7f357a3ed6f856040516124989190615be6565b60405180910390a2505092915050565b6000546001600160a01b031633146124d25760405162461bcd60e51b815260040161086790615dba565b565b6124dc613e04565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b15801561251457600080fd5b505afa158015612528573d6000803e3d6000fd5b50505050612534611ed0565b6001600160a01b0316632528f0fe6004546040518263ffffffff1660e01b81526004016125619190615be6565b60206040518083038186803b15801561257957600080fd5b505afa15801561258d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506125b1919081019061512a565b156125ce5760405162461bcd60e51b815260040161086790615d9a565b600081116125ee5760405162461bcd60e51b815260040161086790615dfa565b6125f6614d53565b6005546040516350e28ac360e11b81526001600160a01b039091169063a1c51586906126289087908790600401615bb9565b6101206040518083038186803b15801561264157600080fd5b505afa158015612655573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061267991908101906151d4565b905061268481613e1e565b61268d81613e7e565b60408101519091506126a5908363ffffffff611eeb16565b604080830191909152426101008301526005549051631137390760e21b81526001600160a01b03909116906344dce41c906126e4908490600401615e3a565b600060405180830381600087803b1580156126fe57600080fd5b505af1158015612712573d6000803e3d6000fd5b50505050836001600160a01b03167f0b1992dffc262be88559dcaf96464e9d661d8bfca7e82f2bb73e31932a82187c8484846040015160405161275793929190615c30565b60405180910390a250505050565b600061276f613e04565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b1580156127a757600080fd5b505afa1580156127bb573d6000803e3d6000fd5b505050506127c7611ed0565b6001600160a01b0316632528f0fe6004546040518263ffffffff1660e01b81526004016127f49190615be6565b60206040518083038186803b15801561280c57600080fd5b505afa158015612820573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612844919081019061512a565b156128615760405162461bcd60e51b815260040161086790615d9a565b612869614d53565b6005546040516350e28ac360e11b81526001600160a01b039091169063a1c515869061289b9033908890600401615b90565b6101206040518083038186803b1580156128b457600080fd5b505afa1580156128c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506128ec91908101906151d4565b90506128f781613e1e565b61290081613e7e565b6040810151909150612918908463ffffffff612a9216565b604082015242610100820152600a5461293082611404565b1161294d5760405162461bcd60e51b815260040161086790615c8a565b600554604051631137390760e21b81526001600160a01b03909116906344dce41c9061297d908490600401615e3a565b600060405180830381600087803b15801561299757600080fd5b505af11580156129ab573d6000803e3d6000fd5b50505050829150336001600160a01b03167ffae26280bca25d80f1501a9e363c73d3845e651c9aaae54f1fc09a9dcd5f3303858584604001516040516129f393929190615c30565b60405180910390a25092915050565b6000611ecb600a54731a60e2e2a8be0bc2b6381dd31fd3fd5f9a28de4c63907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b158015612a4e57600080fd5b505af4158015612a62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612a8691908101906151f3565b9063ffffffff614c9a16565b600082821115612ab45760405162461bcd60e51b815260040161086790615d4a565b50900390565b600082612ac957506000611f13565b82820282848281612ad657fe5b0414611f105760405162461bcd60e51b815260040161086790615dda565b604080516001808252818301909252606091602080830190803883390190505090506e466c657869626c6553746f7261676560881b81600081518110612b3657fe5b60200260200101818152505090565b60608151835101604051908082528060200260200182016040528015612b75578160200160208202803883390190505b50905060005b8351811015612bb757838181518110612b9057fe5b6020026020010151828281518110612ba457fe5b6020908102919091010152600101612b7b565b5060005b8251811015612bfa57828181518110612bd057fe5b6020026020010151828286510181518110612be757fe5b6020908102919091010152600101612bbb565b5092915050565b612c09613e04565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b158015612c4157600080fd5b505afa158015612c55573d6000803e3d6000fd5b50505050612c61611ed0565b6001600160a01b0316632528f0fe6004546040518263ffffffff1660e01b8152600401612c8e9190615be6565b60206040518083038186803b158015612ca657600080fd5b505afa158015612cba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cde919081019061512a565b15612cfb5760405162461bcd60e51b815260040161086790615d9a565b60008111612d1b5760405162461bcd60e51b815260040161086790615cda565b612d23614d53565b6005546040516350e28ac360e11b81526001600160a01b039091169063a1c5158690612d559088908790600401615bb9565b6101206040518083038186803b158015612d6e57600080fd5b505afa158015612d82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612da691908101906151d4565b9050612db181613e1e565b612dba81613e7e565b9050612dcb8482606001518461436a565b612dd581836147ec565b426101008201529050612de6614a0f565b6001600160a01b031663d6f32e068583606001516040518363ffffffff1660e01b8152600401612e17929190615bb9565b60206040518083038186803b158015612e2f57600080fd5b505afa158015612e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e67919081019061512a565b15612e845760405162461bcd60e51b815260040161086790615e2a565b6060810151600090815260086020526040902054612ea190614a26565b6001600160a01b0316639dc29fac85846040518363ffffffff1660e01b8152600401612ece929190615bb9565b600060405180830381600087803b158015612ee857600080fd5b505af1158015612efc573d6000803e3d6000fd5b5050600554604051631137390760e21b81526001600160a01b0390911692506344dce41c9150612f30908490600401615e3a565b600060405180830381600087803b158015612f4a57600080fd5b505af1158015612f5e573d6000803e3d6000fd5b50505050836001600160a01b0316856001600160a01b03167fdf10512219e869922340b1b24b21d7d79bf71f411a6391cc7c3ef5dd2fe89e7f85858560800151604051612fad93929190615c30565b60405180910390a35050505050565b6000808211612fdd5760405162461bcd60e51b815260040161086790615d5a565b6000828481612fe857fe5b04949350505050565b612ff9613e04565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b15801561303157600080fd5b505afa158015613045573d6000803e3d6000fd5b50505050613051611ed0565b6001600160a01b0316632528f0fe6004546040518263ffffffff1660e01b815260040161307e9190615be6565b60206040518083038186803b15801561309657600080fd5b505afa1580156130aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506130ce919081019061512a565b156130eb5760405162461bcd60e51b815260040161086790615d9a565b6130f3614d53565b6005546040516350e28ac360e11b81526001600160a01b039091169063a1c51586906131259033908790600401615b90565b6101206040518083038186803b15801561313e57600080fd5b505afa158015613152573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061317691908101906151d4565b905061318181613e1e565b61318a81613e7e565b60808101519091506131a2908363ffffffff611eeb16565b6080820152600a546131b382611404565b116131d05760405162461bcd60e51b815260040161086790615c9a565b60006131e7600c5484614caf90919063ffffffff16565b905060006131fb848363ffffffff612a9216565b90508260a00151156134085761320f614aa7565b6001600160a01b031663e31f27c18460600151866040518363ffffffff1660e01b8152600401613240929190615c02565b600060405180830381600087803b15801561325a57600080fd5b505af115801561326e573d6000803e3d6000fd5b5050505061327a614cc4565b6001600160a01b031663867904b433613291611ed0565b6001600160a01b031663654a60ac876060015186631cd554d160e21b6040518463ffffffff1660e01b81526004016132cb93929190615c30565b60206040518083038186803b1580156132e357600080fd5b505afa1580156132f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061331b91908101906151f3565b6040518363ffffffff1660e01b8152600401613338929190615b90565b600060405180830381600087803b15801561335257600080fd5b505af1158015613366573d6000803e3d6000fd5b5050505060608301516000908152600960205260409020546001600160a01b0316156134035760608301516000908152600960205260409081902054905163db454a5160e01b81526001600160a01b039091169063db454a51906133d09033908890600401615b90565b600060405180830381600087803b1580156133ea57600080fd5b505af11580156133fe573d6000803e3d6000fd5b505050505b6134f0565b613410614aa7565b6001600160a01b031663eb94bbde8460600151866040518363ffffffff1660e01b8152600401613441929190615c02565b600060405180830381600087803b15801561345b57600080fd5b505af115801561346f573d6000803e3d6000fd5b50505060608401516000908152600860205260409020546134909150614a26565b6001600160a01b031663867904b433836040518363ffffffff1660e01b81526004016134bd929190615b90565b600060405180830381600087803b1580156134d757600080fd5b505af11580156134eb573d6000803e3d6000fd5b505050505b6134fe828460600151614ab6565b42610100840152600554604051631137390760e21b81526001600160a01b03909116906344dce41c90613535908690600401615e3a565b600060405180830381600087803b15801561354f57600080fd5b505af1158015613563573d6000803e3d6000fd5b50505050336001600160a01b03167f5754fe57f36ac0f121901d7555aba517e6608590429d86a81c662cf35831065486866040516135a2929190615c02565b60405180910390a25050505050565b6000611121826135cf85670de0b6b3a764000063ffffffff612aba16565b9063ffffffff612fbc16565b60006135e5613e04565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b15801561361d57600080fd5b505afa158015613631573d6000803e3d6000fd5b5050600f5460ff16915061365990505760405162461bcd60e51b815260040161086790615d7a565b613661611ed0565b6001600160a01b0316632528f0fe6004546040518263ffffffff1660e01b815260040161368e9190615be6565b60206040518083038186803b1580156136a657600080fd5b505afa1580156136ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506136de919081019061512a565b156136fb5760405162461bcd60e51b815260040161086790615d9a565b6000838152600860205260409020546137265760405162461bcd60e51b815260040161086790615dca565b61372e611ed0565b6001600160a01b0316632528f0fe846040518263ffffffff1660e01b81526004016137599190615be6565b60206040518083038186803b15801561377157600080fd5b505afa158015613785573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137a9919081019061512a565b156137c65760405162461bcd60e51b815260040161086790615d2a565b600b548510156137e85760405162461bcd60e51b815260040161086790615d1a565b600d5460055460405163382dab6f60e21b81526001600160a01b039091169063e0b6adbc9061381b903390600401615b3f565b60206040518083038186803b15801561383357600080fd5b505afa158015613847573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061386b91908101906151f3565b106138885760405162461bcd60e51b815260040161086790615cfa565b600080613893614aa7565b6001600160a01b031663b4d6cb4087876040518363ffffffff1660e01b81526004016138c0929190615c02565b604080518083038186803b1580156138d757600080fd5b505afa1580156138eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061390f9190810190615148565b9150915081801561391e575080155b61393a5760405162461bcd60e51b815260040161086790615e0a565b613944878661107f565b8611156139635760405162461bcd60e51b815260040161086790615daa565b600061397a600c5488614caf90919063ffffffff16565b9050600061398e888363ffffffff612a9216565b9050613998614aa7565b6001600160a01b031663b3b467326040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156139d257600080fd5b505af11580156139e6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613a0a91908101906151f3565b9450613a14614d53565b604051806101200160405280878152602001336001600160a01b031681526020018b81526020018981526020018a815260200188151581526020016000815260200160008152602001428152509050613a6c81613e7e565b60055460405163170cc48160e21b81529192506001600160a01b031690635c33120490613a9d908490600401615e3a565b600060405180830381600087803b158015613ab757600080fd5b505af1158015613acb573d6000803e3d6000fd5b50505050613ad98389614ab6565b8615613ccf57613ae7614cc4565b6001600160a01b031663867904b433613afe611ed0565b6001600160a01b031663654a60ac8c87631cd554d160e21b6040518463ffffffff1660e01b8152600401613b3493929190615c30565b60206040518083038186803b158015613b4c57600080fd5b505afa158015613b60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613b8491908101906151f3565b6040518363ffffffff1660e01b8152600401613ba1929190615b90565b600060405180830381600087803b158015613bbb57600080fd5b505af1158015613bcf573d6000803e3d6000fd5b50505050613bdb614aa7565b6001600160a01b031663e31f27c1898b6040518363ffffffff1660e01b8152600401613c08929190615c02565b600060405180830381600087803b158015613c2257600080fd5b505af1158015613c36573d6000803e3d6000fd5b5050506000898152600960205260409020546001600160a01b0316159050613cca576000888152600960205260409081902054905163db454a5160e01b81526001600160a01b039091169063db454a5190613c979033908d90600401615b90565b600060405180830381600087803b158015613cb157600080fd5b505af1158015613cc5573d6000803e3d6000fd5b505050505b613dae565b600088815260086020526040902054613ce790614a26565b6001600160a01b031663867904b433846040518363ffffffff1660e01b8152600401613d14929190615b90565b600060405180830381600087803b158015613d2e57600080fd5b505af1158015613d42573d6000803e3d6000fd5b50505050613d4e614aa7565b6001600160a01b031663eb94bbde898b6040518363ffffffff1660e01b8152600401613d7b929190615c02565b600060405180830381600087803b158015613d9557600080fd5b505af1158015613da9573d6000803e3d6000fd5b505050505b336001600160a01b03167f604952b18be5fed608cbdd28101dc57bd667055c9678ec6d44fb1d8e4c7c172a878b8d8c88604051613def959493929190615e87565b60405180910390a25050505050949350505050565b6000611ecb6b53797374656d53746174757360a01b614a4a565b60008160e0015111613e425760405162461bcd60e51b815260040161086790615e1a565b42613e5d600e54836101000151611eeb90919063ffffffff16565b1115613e7b5760405162461bcd60e51b815260040161086790615cca565b50565b613e86614d53565b8190506000806000808560a00151613f2457613ea0614aa7565b6001600160a01b03166303f048b08760e001516040518263ffffffff1660e01b8152600401613ecf9190615be6565b60806040518083038186803b158015613ee757600080fd5b505afa158015613efb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613f1f9190810190615251565b613fb1565b613f2c614aa7565b6001600160a01b031663af07aa9d87606001518860e001516040518363ffffffff1660e01b8152600401613f61929190615c02565b60806040518083038186803b158015613f7957600080fd5b505afa158015613f8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613fb19190810190615251565b93509350935093506000808760a0015161404157613fcd614aa7565b6001600160a01b031663ba1c5e806040518163ffffffff1660e01b8152600401604080518083038186803b15801561400457600080fd5b505afa158015614018573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061403c9190810190615211565b6140d9565b614049614aa7565b606089015160009081526008602052604090819020549051630ee81f7960e41b81526001600160a01b03929092169163ee81f7909161408a91600401615be6565b604080518083038186803b1580156140a157600080fd5b505afa1580156140b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506140d99190810190615211565b9150915080156140fb5760405162461bcd60e51b815260040161086790615d6a565b6000614198731a60e2e2a8be0bc2b6381dd31fd3fd5f9a28de4c63907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561414457600080fd5b505af4158015614158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061417c91908101906151f3565b61418c428863ffffffff612a9216565b9063ffffffff612aba16565b905060006141bc6141af858463ffffffff611f1916565b889063ffffffff611eeb16565b905060008a60e001516000146141f5576141f06141df838b63ffffffff612a9216565b60808d01519063ffffffff611f1916565b6141f8565b60005b90508a60a0015161426d5761420b614aa7565b6001600160a01b031663f53037b6836040518263ffffffff1660e01b81526004016142369190615be6565b600060405180830381600087803b15801561425057600080fd5b505af1158015614264573d6000803e3d6000fd5b505050506142d9565b614275614aa7565b6001600160a01b031663246206398c60600151846040518363ffffffff1660e01b81526004016142a6929190615c02565b600060405180830381600087803b1580156142c057600080fd5b505af11580156142d4573d6000803e3d6000fd5b505050505b60c08b01516142ee908263ffffffff611eeb16565b60c08b015260e08a01869052600554604051631137390760e21b81526001600160a01b03909116906344dce41c9061432a908d90600401615e3a565b600060405180830381600087803b15801561434457600080fd5b505af1158015614358573d6000803e3d6000fd5b50505050505050505050505050919050565b600082815260086020526040902054819061438490614a26565b6001600160a01b03166370a08231856040518263ffffffff1660e01b81526004016143af9190615b31565b60206040518083038186803b1580156143c757600080fd5b505afa1580156143db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506143ff91908101906151f3565b10156112be5760405162461bcd60e51b815260040161086790615dea565b60008061443b8360c001518460800151611eeb90919063ffffffff16565b608084015160408501519350909150614452614a0f565b6001600160a01b031663d6f32e068686606001516040518363ffffffff1660e01b8152600401614483929190615bb9565b60206040518083038186803b15801561449b57600080fd5b505afa1580156144af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506144d3919081019061512a565b156144f05760405162461bcd60e51b815260040161086790615e2a565b606084015160009081526008602052604090205461450d90614a26565b6001600160a01b0316639dc29fac86846040518363ffffffff1660e01b815260040161453a929190615bb9565b600060405180830381600087803b15801561455457600080fd5b505af1158015614568573d6000803e3d6000fd5b505050508360a00151156146865761457e614aa7565b6001600160a01b0316635246f2b9856060015186608001516040518363ffffffff1660e01b81526004016145b3929190615c02565b600060405180830381600087803b1580156145cd57600080fd5b505af11580156145e1573d6000803e3d6000fd5b5050505060608401516000908152600960205260409020546001600160a01b03161561468157606084015160009081526009602052604090819020546080860151915163f3fef3a360e01b81526001600160a01b039091169163f3fef3a39161464e918a91600401615bb9565b600060405180830381600087803b15801561466857600080fd5b505af115801561467c573d6000803e3d6000fd5b505050505b6146f6565b61468e614aa7565b6001600160a01b031663e50a31b3856060015186608001516040518363ffffffff1660e01b81526004016146c3929190615c02565b600060405180830381600087803b1580156146dd57600080fd5b505af11580156146f1573d6000803e3d6000fd5b505050505b6147088460c001518560600151614ab6565b600060808501819052604080860182905260c0860182905260e0860191909152426101008601526005549051631137390760e21b81526001600160a01b03909116906344dce41c9061475e908790600401615e3a565b600060405180830381600087803b15801561477857600080fd5b505af115801561478c573d6000803e3d6000fd5b50505050846001600160a01b0316866001600160a01b03167f697721ed1b9d4866cb1aaa0692f62bb3abc1b01c2dafeaad053ffd4532aa7dbb866000015184876040516147db93929190615c30565b60405180910390a350509392505050565b6147f4614d53565b50818115801590614809575060008360c00151115b156148665760008360c0015183116148215782614827565b8360c001515b60c085015190915061483f908263ffffffff612a9216565b60c0830152614854838263ffffffff612a9216565b9250614864818560600151614ab6565b505b8115611f13576080830151614881908363ffffffff612a9216565b608082015260a08101511561499e57614898614aa7565b6001600160a01b0316635246f2b98260600151846040518363ffffffff1660e01b81526004016148c9929190615c02565b600060405180830381600087803b1580156148e357600080fd5b505af11580156148f7573d6000803e3d6000fd5b5050505060608101516000908152600960205260409020546001600160a01b0316156149995760608101516000908152600960209081526040918290205490830151915163f3fef3a360e01b81526001600160a01b039091169163f3fef3a39161496691908690600401615b90565b600060405180830381600087803b15801561498057600080fd5b505af1158015614994573d6000803e3d6000fd5b505050505b611f13565b6149a6614aa7565b6001600160a01b031663e50a31b38260600151846040518363ffffffff1660e01b81526004016149d7929190615c02565b600060405180830381600087803b1580156149f157600080fd5b505af1158015614a05573d6000803e3d6000fd5b5050505092915050565b6000611ecb6822bc31b430b733b2b960b91b614a4a565b6000611f1382614a4a565b6000611ecb6e466c657869626c6553746f7261676560881b5b60008181526003602090815260408083205490516001600160a01b039091169182151591614a7a91869101615b06565b60405160208183030381529060405290612bfa5760405162461bcd60e51b81526004016108679190615c59565b6006546001600160a01b031690565b8115610fdf57631cd554d160e21b8114614b5b57614ad2611ed0565b6001600160a01b031663654a60ac8284631cd554d160e21b6040518463ffffffff1660e01b8152600401614b0893929190615c30565b60206040518083038186803b158015614b2057600080fd5b505afa158015614b34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614b5891908101906151f3565b91505b614b63614cc4565b6001600160a01b031663867904b4614b79614cdb565b6001600160a01b031663eb1edd616040518163ffffffff1660e01b815260040160206040518083038186803b158015614bb157600080fd5b505afa158015614bc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614be99190810190614ff7565b846040518363ffffffff1660e01b8152600401614c07929190615bb9565b600060405180830381600087803b158015614c2157600080fd5b505af1158015614c35573d6000803e3d6000fd5b50505050614c41614cdb565b6001600160a01b03166322bf55ef836040518263ffffffff1660e01b8152600401614c6c9190615be6565b600060405180830381600087803b158015614c8657600080fd5b505af11580156105d8573d6000803e3d6000fd5b60006111218383670de0b6b3a7640000614cf0565b60006111218383670de0b6b3a7640000614d28565b6000611ecb6814de5b9d1a1cd554d160ba1b614a4a565b6000611ecb66119959541bdbdb60ca1b614a4a565b600080614d0a846135cf87600a870263ffffffff612aba16565b90506005600a825b0610614d1c57600a015b600a9004949350505050565b600080600a8304614d3f868663ffffffff612aba16565b81614d4657fe5b0490506005600a82614d12565b6040518061012001604052806000815260200160006001600160a01b031681526020016000815260200160008019168152602001600081526020016000151581526020016000815260200160008152602001600081525090565b8035611f1381615f76565b8051611f1381615f76565b60008083601f840112614dd557600080fd5b50813567ffffffffffffffff811115614ded57600080fd5b602083019150836020820283011115614e0557600080fd5b9250929050565b8035611f1381615f8a565b8051611f1381615f8a565b8035611f1381615f93565b8051611f1381615f93565b60006101208284031215614e4b57600080fd5b614e56610120615ed3565b90506000614e648484614e22565b8252506020614e7584848301614dad565b6020830152506040614e8984828501614e22565b6040830152506060614e9d84828501614e22565b6060830152506080614eb184828501614e22565b60808301525060a0614ec584828501614e0c565b60a08301525060c0614ed984828501614e22565b60c08301525060e0614eed84828501614e22565b60e083015250610100614f0284828501614e22565b6101008301525092915050565b60006101208284031215614f2257600080fd5b614f2d610120615ed3565b90506000614f3b8484614e2d565b8252506020614f4c84848301614db8565b6020830152506040614f6084828501614e2d565b6040830152506060614f7484828501614e2d565b6060830152506080614f8884828501614e2d565b60808301525060a0614f9c84828501614e17565b60a08301525060c0614fb084828501614e2d565b60c08301525060e0614fc484828501614e2d565b60e083015250610100614f0284828501614e2d565b600060208284031215614feb57600080fd5b60006107148484614dad565b60006020828403121561500957600080fd5b60006107148484614db8565b6000806040838503121561502857600080fd5b60006150348585614dad565b925050602061504585828601614e22565b9150509250929050565b60008060006060848603121561506457600080fd5b60006150708686614dad565b935050602061508186828701614e22565b925050604061509286828701614e22565b9150509250925092565b600080600080604085870312156150b257600080fd5b843567ffffffffffffffff8111156150c957600080fd5b6150d587828801614dc3565b9450945050602085013567ffffffffffffffff8111156150f457600080fd5b61510087828801614dc3565b95989497509550505050565b60006020828403121561511e57600080fd5b60006107148484614e0c565b60006020828403121561513c57600080fd5b60006107148484614e17565b6000806040838503121561515b57600080fd5b60006151678585614e17565b925050602061504585828601614e17565b60006020828403121561518a57600080fd5b60006107148484614e22565b600080604083850312156151a957600080fd5b60006150348585614e22565b600061012082840312156151c857600080fd5b60006107148484614e38565b600061012082840312156151e757600080fd5b60006107148484614f0f565b60006020828403121561520557600080fd5b60006107148484614e2d565b6000806040838503121561522457600080fd5b60006151678585614e2d565b60008060006060848603121561524557600080fd5b60006150708686614e22565b6000806000806080858703121561526757600080fd5b60006152738787614e2d565b945050602061528487828801614e2d565b935050604061529587828801614e2d565b92505060606152a687828801614e2d565b91505092959194509250565b60006152be8383615340565b505060200190565b6152cf81615f2e565b82525050565b6152cf81615f12565b60006152e982615f00565b6152f38185615f04565b93506152fe83615efa565b8060005b8381101561532c57815161531688826152b2565b975061532183615efa565b925050600101615302565b509495945050505050565b6152cf81615f1d565b6152cf81610c3a565b6152cf61535582610c3a565b610c3a565b6152cf81615f35565b600061536e82615f00565b6153788185615f04565b9350615388818560208601615f40565b61539181615f6c565b9093019392505050565b60006153a8601b83615f04565b7f496e707574206172726179206c656e677468206d69736d617463680000000000815260200192915050565b60006153e1601683615f04565b754d7573742062652067726561746572207468616e203160501b815260200192915050565b6000615413600e83615f04565b6d43726174696f20746f6f206c6f7760901b815260200192915050565b600061543d601583615f04565b74086c2dcdcdee840c8e4c2ee40e8d0d2e640daeac6d605b1b815260200192915050565b600061546e603583615f04565b7f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7581527402063616e20616363657074206f776e65727368697605c1b602082015260400192915050565b60006154c5601983615f04565b7f416c6c6f77616e6365206e6f74206869676820656e6f75676800000000000000815260200192915050565b60006154fe601d83615f04565b7f4c6f616e20726563656e746c7920696e74657261637465642077697468000000815260200192915050565b6000615537601e83615f04565b7f5061796d656e74206d7573742062652067726561746572207468616e20300000815260200192915050565b6000615570601b83615f04565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006155a9601283615f04565b7113585e081b1bd85b9cc8195e18d95959195960721b815260200192915050565b60006155d7601e83615f04565b7f43726174696f2061626f7665206c69717569646174696f6e20726174696f0000815260200192915050565b6000615610601d83615f04565b7f4e6f7420656e6f75676820636f6c6c61746572616c20746f206f70656e000000815260200192915050565b6000615649601883615f04565b7f43757272656e6379207261746520697320696e76616c69640000000000000000815260200192915050565b6000615682602083615f04565b7f57616974696e672073656373206f7220736574746c656d656e74206f77696e67815260200192915050565b60006156bb601e83615f04565b7f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815260200192915050565b60006156f4601a83615f04565b7f536166654d6174683a206469766973696f6e206279207a65726f000000000000815260200192915050565b600061572d601183615f0d565b70026b4b9b9b4b7339030b2323932b9b99d1607d1b815260110192915050565b600061575a601183615f04565b7014985d195cc8185c99481a5b9d985b1a59607a1b815260200192915050565b6000615787601383615f04565b7213dc195b9a5b99c81a5cc8191a5cd8589b1959606a1b815260200192915050565b60006157b6600a83615f04565b6926b0bc1018903437bab960b11b815260200192915050565b60006157dc601a83615f04565b7f436f6c6c61746572616c207261746520697320696e76616c6964000000000000815260200192915050565b6000615815601b83615f04565b7f45786365656473206d617820626f72726f77696e6720706f7765720000000000815260200192915050565b600061584e602f83615f04565b7f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726681526e37b936903a3434b99030b1ba34b7b760891b602082015260400192915050565b600061589f601f83615f04565b7f4e6f7420616c6c6f77656420746f20697373756520746869732073796e746800815260200192915050565b60006158d8602183615f04565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061591b601883615f04565b7f4e6f7420656e6f7567682073796e74682062616c616e63650000000000000000815260200192915050565b6000615954601e83615f04565b7f4465706f736974206d7573742062652067726561746572207468616e20300000815260200192915050565b600061598d601a83615f04565b7f44656274206c696d6974206f7220696e76616c69642072617465000000000000815260200192915050565b60006159c6601983615f0d565b7f5265736f6c766572206d697373696e67207461726765743a2000000000000000815260190192915050565b60006159ff601383615f04565b72131bd85b88191bd95cc81b9bdd08195e1a5cdd606a1b815260200192915050565b6000615a2e601b83615f04565b7f57616974696e67206f7220736574746c656d656e74206f77696e670000000000815260200192915050565b8051610120830190615a6c8482615340565b506020820151615a7f60208501826152d5565b506040820151615a926040850182615340565b506060820151615aa56060850182615340565b506080820151615ab86080850182615340565b5060a0820151615acb60a0850182615337565b5060c0820151615ade60c0850182615340565b5060e0820151615af160e0850182615340565b506101008201516107bf610100850182615340565b6000615b1182615720565b9150615b1d8284615349565b50602001919050565b6000615b11826159b9565b60208101611f1382846152d5565b60208101611f1382846152c6565b60408101615b5b82856152c6565b61112160208301846152d5565b60608101615b7682866152c6565b615b8360208301856152d5565b6107146040830184615340565b60408101615b9e82856152c6565b6111216020830184615340565b60408101615b5b82856152d5565b60408101615b9e82856152d5565b6020808252810161112181846152de565b60208101611f138284615337565b60208101611f138284615340565b60408101615b5b8285615340565b60408101615b9e8285615340565b60408101615c1e8285615340565b81810360208301526107148184615363565b60608101615c3e8286615340565b615b836020830185615340565b60208101611f13828461535a565b602080825281016111218184615363565b60208082528101611f138161539b565b60208082528101611f13816153d4565b60208082528101611f1381615406565b60208082528101611f1381615430565b60208082528101611f1381615461565b60208082528101611f13816154b8565b60208082528101611f13816154f1565b60208082528101611f138161552a565b60208082528101611f1381615563565b60208082528101611f138161559c565b60208082528101611f13816155ca565b60208082528101611f1381615603565b60208082528101611f138161563c565b60208082528101611f1381615675565b60208082528101611f13816156ae565b60208082528101611f13816156e7565b60208082528101611f138161574d565b60208082528101611f138161577a565b60208082528101611f13816157a9565b60208082528101611f13816157cf565b60208082528101611f1381615808565b60208082528101611f1381615841565b60208082528101611f1381615892565b60208082528101611f13816158cb565b60208082528101611f138161590e565b60208082528101611f1381615947565b60208082528101611f1381615980565b60208082528101611f13816159f2565b60208082528101611f1381615a21565b6101208101611f138284615a5a565b60808101615e578287615340565b615e6460208301866152c6565b615e716040830185615340565b615e7e6060830184615340565b95945050505050565b60a08101615e958288615340565b615ea26020830187615340565b615eaf6040830186615340565b615ebc6060830185615340565b615ec96080830184615340565b9695505050505050565b60405181810167ffffffffffffffff81118282101715615ef257600080fd5b604052919050565b60200190565b5190565b90815260200190565b919050565b6000611f1382615f22565b151590565b6001600160a01b031690565b6000611f13825b6000611f1382615f12565b60005b83811015615f5b578181015183820152602001615f43565b838111156107bf5750506000910152565b601f01601f191690565b615f7f81615f12565b8114613e7b57600080fd5b615f7f81615f1d565b615f7f81610c3a56fea365627a7a723158204578f876648ef8ba05b02ba66bfbd1ec2475768095dfe27bc32fd41939008dd06c6578706572696d656e74616cf564736f6c6343000510004000000000000000000000000004c3f6207be48de777967eb1886469e4e268fe07000000000000000000000000b64ff7a4a33acdf48d97dab0d764afd0f617688200000000000000000000000053bae964339e8a742b5b47f6c10bbfa8ff138f34000000000000000000000000242a3df52c375bee81b1c668741d7c63af68fdd27342544300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120a871cc002000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000009b2fe385cedea62d839e4de89b0a23ef4eacc7170000000000000000000000000000000000000000000000000000000000000008
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000004c3f6207be48de777967eb1886469e4e268fe07000000000000000000000000b64ff7a4a33acdf48d97dab0d764afd0f617688200000000000000000000000053bae964339e8a742b5b47f6c10bbfa8ff138f34000000000000000000000000242a3df52c375bee81b1c668741d7c63af68fdd27342544300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120a871cc002000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000009b2fe385cedea62d839e4de89b0a23ef4eacc7170000000000000000000000000000000000000000000000000000000000000008
-----Decoded View---------------
Arg [0] : _state (address): 0x04c3f6207be48de777967eb1886469e4e268fe07
Arg [1] : _owner (address): 0xb64ff7a4a33acdf48d97dab0d764afd0f6176882
Arg [2] : _manager (address): 0x53bae964339e8a742b5b47f6c10bbfa8ff138f34
Arg [3] : _resolver (address): 0x242a3df52c375bee81b1c668741d7c63af68fdd2
Arg [4] : _collateralKey (bytes32): 0x7342544300000000000000000000000000000000000000000000000000000000
Arg [5] : _minCratio (uint256): 1300000000000000000
Arg [6] : _minCollateral (uint256): 50000000000000000
Arg [7] : _underlyingContract (address): 0x9b2fe385cedea62d839e4de89b0a23ef4eacc717
Arg [8] : _underlyingDecimals (uint256): 8
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 00000000000000000000000004c3f6207be48de777967eb1886469e4e268fe07
Arg [1] : 000000000000000000000000b64ff7a4a33acdf48d97dab0d764afd0f6176882
Arg [2] : 00000000000000000000000053bae964339e8a742b5b47f6c10bbfa8ff138f34
Arg [3] : 000000000000000000000000242a3df52c375bee81b1c668741d7c63af68fdd2
Arg [4] : 7342544300000000000000000000000000000000000000000000000000000000
Arg [5] : 000000000000000000000000000000000000000000000000120a871cc0020000
Arg [6] : 00000000000000000000000000000000000000000000000000b1a2bc2ec50000
Arg [7] : 0000000000000000000000009b2fe385cedea62d839e4de89b0a23ef4eacc717
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000008
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.