Transaction Hash:
Block:
11514569 at Dec-24-2020 06:04:11 AM +UTC
Transaction Fee:
0.00218859732 ETH
$5.35
Gas Used:
29,692 Gas / 73.71 Gwei
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x00192Fb1...d1BF599E8
Miner
| (2Miners: PPLNS) | 238.257000358544314272 Eth | 238.259188955864314272 Eth | 0.00218859732 | |
0x62f7A1F9...7d452565B |
7.91780534 Eth
Nonce: 1
|
7.91561674268 Eth
Nonce: 2
| 0.00218859732 |
Execution Trace
CollateralErc20.open( collateral=100000000, amount=500000000000000000000, currency=7355534400000000000000000000000000000000000000000000000000000000 )
RenBTC.dd62ed3e( )
-
RenERC20LogicV1.allowance( owner=0x62f7A1F94aba23eD2dD108F8D23Aa3e7d452565B, spender=0x3B3812BB9f6151bEb6fa10783F1ae848a77a0d46 ) => ( 0 )
-
open[CollateralErc20 (ln:2307)]
allowance[CollateralErc20 (ln:2312)]
transferFrom[CollateralErc20 (ln:2315)]
scaleUpCollateral[CollateralErc20 (ln:2318)]
openInternal[CollateralErc20 (ln:2320)]
File 1 of 3: CollateralErc20
File 2 of 3: RenBTC
File 3 of 3: RenERC20LogicV1
/* ____ __ __ __ _ / __/__ __ ___ / /_ / / ___ / /_ (_)__ __ _\ \ / // // _ \/ __// _ \/ -_)/ __// / \ \ / /___/ \_, //_//_/\__//_//_/\__/ \__//_/ /_\_\ /___/ * 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) 2020 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_MESSAGE_GAS_LIMIT = "crossDomainMessageGasLimit"; bytes32 internal constant CONTRACT_FLEXIBLESTORAGE = "FlexibleStorage"; 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 getCrossDomainMessageGasLimit() internal view returns (uint) { return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_CROSS_DOMAIN_MESSAGE_GAS_LIMIT); } 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 = loan.amount.add(loan.accruedInterest).multiplyDecimal( _exchangeRates().rateForCurrency(loan.currency) ); uint collateralValue = loan.collateral.multiplyDecimal(_exchangeRates().rateForCurrency(collateralKey)); uint unit = SafeDecimalMath.unit(); uint dividend = debtValue.sub(collateralValue.divideDecimal(minCratio)); uint divisor = unit.sub(unit.add(liquidationPenalty).divideDecimal(minCratio)); return dividend.divideDecimal(divisor); } // 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); } }
File 2 of 3: RenBTC
/** Deployed by Ren Project, https://renproject.io Commit hash: 9068f80 Repository: https://github.com/renproject/darknode-sol Issues: https://github.com/renproject/darknode-sol/issues Licenses @openzeppelin/contracts: (MIT) https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/LICENSE darknode-sol: (GNU GPL V3) https://github.com/renproject/darknode-sol/blob/master/LICENSE */ pragma solidity 0.5.16; contract Initializable { bool private initialized; bool private initializing; modifier initializer() { require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized"); bool isTopLevelCall = !initializing; if (isTopLevelCall) { initializing = true; initialized = true; } _; if (isTopLevelCall) { initializing = false; } } function isConstructor() private view returns (bool) { address self = address(this); uint256 cs; assembly { cs := extcodesize(self) } return cs == 0; } uint256[50] private ______gap; } contract Context is Initializable { constructor () internal { } function _msgSender() internal view returns (address payable) { return msg.sender; } function _msgData() internal view returns (bytes memory) { this; return msg.data; } } contract Ownable is Initializable, Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function initialize(address sender) public initializer { _owner = sender; emit OwnershipTransferred(address(0), _owner); } function owner() public view returns (address) { return _owner; } modifier onlyOwner() { require(isOwner(), "Ownable: caller is not the owner"); _; } function isOwner() public view returns (bool) { return _msgSender() == _owner; } function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } function _transferOwnership(address newOwner) internal { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } uint256[50] private ______gap; } contract Proxy { function () payable external { _fallback(); } function _implementation() internal view returns (address); function _delegate(address implementation) internal { assembly { calldatacopy(0, 0, calldatasize) let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) returndatacopy(0, 0, returndatasize) switch result case 0 { revert(0, returndatasize) } default { return(0, returndatasize) } } } function _willFallback() internal { } function _fallback() internal { _willFallback(); _delegate(_implementation()); } } library OpenZeppelinUpgradesAddress { function isContract(address account) internal view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 0; } } contract BaseUpgradeabilityProxy is Proxy { event Upgraded(address indexed implementation); bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } function _setImplementation(address newImplementation) internal { require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); bytes32 slot = IMPLEMENTATION_SLOT; assembly { sstore(slot, newImplementation) } } } contract UpgradeabilityProxy is BaseUpgradeabilityProxy { constructor(address _logic, bytes memory _data) public payable { assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); _setImplementation(_logic); if(_data.length > 0) { (bool success,) = _logic.delegatecall(_data); require(success); } } } contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy { event AdminChanged(address previousAdmin, address newAdmin); bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; modifier ifAdmin() { if (msg.sender == _admin()) { _; } else { _fallback(); } } function admin() external ifAdmin returns (address) { return _admin(); } function implementation() external ifAdmin returns (address) { return _implementation(); } function changeAdmin(address newAdmin) external ifAdmin { require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); emit AdminChanged(_admin(), newAdmin); _setAdmin(newAdmin); } function upgradeTo(address newImplementation) external ifAdmin { _upgradeTo(newImplementation); } function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin { _upgradeTo(newImplementation); (bool success,) = newImplementation.delegatecall(data); require(success); } function _admin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; assembly { adm := sload(slot) } } function _setAdmin(address newAdmin) internal { bytes32 slot = ADMIN_SLOT; assembly { sstore(slot, newAdmin) } } function _willFallback() internal { require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin"); super._willFallback(); } } contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy { function initialize(address _logic, bytes memory _data) public payable { require(_implementation() == address(0)); assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); _setImplementation(_logic); if(_data.length > 0) { (bool success,) = _logic.delegatecall(_data); require(success); } } } contract InitializableAdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy { function initialize(address _logic, address _admin, bytes memory _data) public payable { require(_implementation() == address(0)); InitializableUpgradeabilityProxy.initialize(_logic, _data); assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)); _setAdmin(_admin); } } interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; return c; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } contract ERC20 is Initializable, Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; function totalSupply() public view returns (uint256) { return _totalSupply; } function balanceOf(address account) public view returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) public returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } function allowance(address owner, address spender) public view returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) public returns (bool) { _approve(_msgSender(), spender, amount); return true; } function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } function _transfer(address sender, address recipient, uint256 amount) internal { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } function _mint(address account, uint256 amount) internal { require(account != address(0), "ERC20: mint to the zero address"); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } function _burn(address account, uint256 amount) internal { require(account != address(0), "ERC20: burn from the zero address"); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } function _approve(address owner, address spender, uint256 amount) internal { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } function _burnFrom(address account, uint256 amount) internal { _burn(account, amount); _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")); } uint256[50] private ______gap; } contract ERC20Detailed is Initializable, IERC20 { string private _name; string private _symbol; uint8 private _decimals; function initialize(string memory name, string memory symbol, uint8 decimals) public initializer { _name = name; _symbol = symbol; _decimals = decimals; } function name() public view returns (string memory) { return _name; } function symbol() public view returns (string memory) { return _symbol; } function decimals() public view returns (uint8) { return _decimals; } uint256[50] private ______gap; } contract Claimable is Initializable, Ownable { address public pendingOwner; function initialize(address _nextOwner) public initializer { Ownable.initialize(_nextOwner); } modifier onlyPendingOwner() { require( _msgSender() == pendingOwner, "Claimable: caller is not the pending owner" ); _; } function transferOwnership(address newOwner) public onlyOwner { require( newOwner != owner() && newOwner != pendingOwner, "Claimable: invalid new owner" ); pendingOwner = newOwner; } function claimOwnership() public onlyPendingOwner { _transferOwnership(pendingOwner); delete pendingOwner; } } library Address { function isContract(address account) internal view returns (bool) { bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; assembly { codehash := extcodehash(account) } return (codehash != 0x0 && codehash != accountHash); } function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call.value(amount)(""); require(success, "Address: unable to send value, recipient may have reverted"); } } library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove(IERC20 token, address spender, uint256 value) internal { require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function callOptionalReturn(IERC20 token, bytes memory data) private { require(address(token).isContract(), "SafeERC20: call to non-contract"); (bool success, bytes memory returndata) = address(token).call(data); require(success, "SafeERC20: low-level call failed"); if (returndata.length > 0) { require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } contract CanReclaimTokens is Claimable { using SafeERC20 for ERC20; mapping(address => bool) private recoverableTokensBlacklist; function initialize(address _nextOwner) public initializer { Claimable.initialize(_nextOwner); } function blacklistRecoverableToken(address _token) public onlyOwner { recoverableTokensBlacklist[_token] = true; } function recoverTokens(address _token) external onlyOwner { require( !recoverableTokensBlacklist[_token], "CanReclaimTokens: token is not recoverable" ); if (_token == address(0x0)) { msg.sender.transfer(address(this).balance); } else { ERC20(_token).safeTransfer( msg.sender, ERC20(_token).balanceOf(address(this)) ); } } } contract ERC20WithRate is Initializable, Ownable, ERC20 { using SafeMath for uint256; uint256 public constant _rateScale = 1e18; uint256 internal _rate; event LogRateChanged(uint256 indexed _rate); function initialize(address _nextOwner, uint256 _initialRate) public initializer { Ownable.initialize(_nextOwner); _setRate(_initialRate); } function setExchangeRate(uint256 _nextRate) public onlyOwner { _setRate(_nextRate); } function exchangeRateCurrent() public view returns (uint256) { require(_rate != 0, "ERC20WithRate: rate has not been initialized"); return _rate; } function _setRate(uint256 _nextRate) internal { require(_nextRate > 0, "ERC20WithRate: rate must be greater than zero"); _rate = _nextRate; } function balanceOfUnderlying(address _account) public view returns (uint256) { return toUnderlying(balanceOf(_account)); } function toUnderlying(uint256 _amount) public view returns (uint256) { return _amount.mul(_rate).div(_rateScale); } function fromUnderlying(uint256 _amountUnderlying) public view returns (uint256) { return _amountUnderlying.mul(_rateScale).div(_rate); } } contract ERC20WithPermit is Initializable, ERC20, ERC20Detailed { using SafeMath for uint256; mapping(address => uint256) public nonces; string public version; bytes32 public DOMAIN_SEPARATOR; bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb; function initialize( uint256 _chainId, string memory _version, string memory _name, string memory _symbol, uint8 _decimals ) public initializer { ERC20Detailed.initialize(_name, _symbol, _decimals); version = _version; DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256(bytes(name())), keccak256(bytes(version)), _chainId, address(this) ) ); } function permit( address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s ) external { bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256( abi.encode( PERMIT_TYPEHASH, holder, spender, nonce, expiry, allowed ) ) ) ); require(holder != address(0), "ERC20WithRate: address must not be 0x0"); require( holder == ecrecover(digest, v, r, s), "ERC20WithRate: invalid signature" ); require( expiry == 0 || now <= expiry, "ERC20WithRate: permit has expired" ); require(nonce == nonces[holder]++, "ERC20WithRate: invalid nonce"); uint256 amount = allowed ? uint256(-1) : 0; _approve(holder, spender, amount); } } contract RenERC20LogicV1 is Initializable, ERC20, ERC20Detailed, ERC20WithRate, ERC20WithPermit, Claimable, CanReclaimTokens { function initialize( uint256 _chainId, address _nextOwner, uint256 _initialRate, string memory _version, string memory _name, string memory _symbol, uint8 _decimals ) public initializer { ERC20Detailed.initialize(_name, _symbol, _decimals); ERC20WithRate.initialize(_nextOwner, _initialRate); ERC20WithPermit.initialize( _chainId, _version, _name, _symbol, _decimals ); Claimable.initialize(_nextOwner); CanReclaimTokens.initialize(_nextOwner); } function mint(address _to, uint256 _amount) public onlyOwner { _mint(_to, _amount); } function burn(address _from, uint256 _amount) public onlyOwner { _burn(_from, _amount); } function transfer(address recipient, uint256 amount) public returns (bool) { require( recipient != address(this), "RenERC20: can't transfer to token address" ); return super.transfer(recipient, amount); } function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { require( recipient != address(this), "RenERC20: can't transfer to token address" ); return super.transferFrom(sender, recipient, amount); } } contract RenBTC is InitializableAdminUpgradeabilityProxy {} contract RenZEC is InitializableAdminUpgradeabilityProxy {} contract RenBCH is InitializableAdminUpgradeabilityProxy {}
File 3 of 3: RenERC20LogicV1
/** Deployed by Ren Project, https://renproject.io Commit hash: 9068f80 Repository: https://github.com/renproject/darknode-sol Issues: https://github.com/renproject/darknode-sol/issues Licenses @openzeppelin/contracts: (MIT) https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/LICENSE darknode-sol: (GNU GPL V3) https://github.com/renproject/darknode-sol/blob/master/LICENSE */ pragma solidity 0.5.16; contract Initializable { bool private initialized; bool private initializing; modifier initializer() { require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized"); bool isTopLevelCall = !initializing; if (isTopLevelCall) { initializing = true; initialized = true; } _; if (isTopLevelCall) { initializing = false; } } function isConstructor() private view returns (bool) { address self = address(this); uint256 cs; assembly { cs := extcodesize(self) } return cs == 0; } uint256[50] private ______gap; } contract Context is Initializable { constructor () internal { } function _msgSender() internal view returns (address payable) { return msg.sender; } function _msgData() internal view returns (bytes memory) { this; return msg.data; } } contract Ownable is Initializable, Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function initialize(address sender) public initializer { _owner = sender; emit OwnershipTransferred(address(0), _owner); } function owner() public view returns (address) { return _owner; } modifier onlyOwner() { require(isOwner(), "Ownable: caller is not the owner"); _; } function isOwner() public view returns (bool) { return _msgSender() == _owner; } function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } function _transferOwnership(address newOwner) internal { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } uint256[50] private ______gap; } contract Proxy { function () payable external { _fallback(); } function _implementation() internal view returns (address); function _delegate(address implementation) internal { assembly { calldatacopy(0, 0, calldatasize) let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) returndatacopy(0, 0, returndatasize) switch result case 0 { revert(0, returndatasize) } default { return(0, returndatasize) } } } function _willFallback() internal { } function _fallback() internal { _willFallback(); _delegate(_implementation()); } } library OpenZeppelinUpgradesAddress { function isContract(address account) internal view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 0; } } contract BaseUpgradeabilityProxy is Proxy { event Upgraded(address indexed implementation); bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } function _setImplementation(address newImplementation) internal { require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); bytes32 slot = IMPLEMENTATION_SLOT; assembly { sstore(slot, newImplementation) } } } contract UpgradeabilityProxy is BaseUpgradeabilityProxy { constructor(address _logic, bytes memory _data) public payable { assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); _setImplementation(_logic); if(_data.length > 0) { (bool success,) = _logic.delegatecall(_data); require(success); } } } contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy { event AdminChanged(address previousAdmin, address newAdmin); bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; modifier ifAdmin() { if (msg.sender == _admin()) { _; } else { _fallback(); } } function admin() external ifAdmin returns (address) { return _admin(); } function implementation() external ifAdmin returns (address) { return _implementation(); } function changeAdmin(address newAdmin) external ifAdmin { require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); emit AdminChanged(_admin(), newAdmin); _setAdmin(newAdmin); } function upgradeTo(address newImplementation) external ifAdmin { _upgradeTo(newImplementation); } function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin { _upgradeTo(newImplementation); (bool success,) = newImplementation.delegatecall(data); require(success); } function _admin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; assembly { adm := sload(slot) } } function _setAdmin(address newAdmin) internal { bytes32 slot = ADMIN_SLOT; assembly { sstore(slot, newAdmin) } } function _willFallback() internal { require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin"); super._willFallback(); } } contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy { function initialize(address _logic, bytes memory _data) public payable { require(_implementation() == address(0)); assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); _setImplementation(_logic); if(_data.length > 0) { (bool success,) = _logic.delegatecall(_data); require(success); } } } contract InitializableAdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy { function initialize(address _logic, address _admin, bytes memory _data) public payable { require(_implementation() == address(0)); InitializableUpgradeabilityProxy.initialize(_logic, _data); assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)); _setAdmin(_admin); } } interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; return c; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } contract ERC20 is Initializable, Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; function totalSupply() public view returns (uint256) { return _totalSupply; } function balanceOf(address account) public view returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) public returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } function allowance(address owner, address spender) public view returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) public returns (bool) { _approve(_msgSender(), spender, amount); return true; } function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } function _transfer(address sender, address recipient, uint256 amount) internal { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } function _mint(address account, uint256 amount) internal { require(account != address(0), "ERC20: mint to the zero address"); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } function _burn(address account, uint256 amount) internal { require(account != address(0), "ERC20: burn from the zero address"); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } function _approve(address owner, address spender, uint256 amount) internal { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } function _burnFrom(address account, uint256 amount) internal { _burn(account, amount); _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")); } uint256[50] private ______gap; } contract ERC20Detailed is Initializable, IERC20 { string private _name; string private _symbol; uint8 private _decimals; function initialize(string memory name, string memory symbol, uint8 decimals) public initializer { _name = name; _symbol = symbol; _decimals = decimals; } function name() public view returns (string memory) { return _name; } function symbol() public view returns (string memory) { return _symbol; } function decimals() public view returns (uint8) { return _decimals; } uint256[50] private ______gap; } contract Claimable is Initializable, Ownable { address public pendingOwner; function initialize(address _nextOwner) public initializer { Ownable.initialize(_nextOwner); } modifier onlyPendingOwner() { require( _msgSender() == pendingOwner, "Claimable: caller is not the pending owner" ); _; } function transferOwnership(address newOwner) public onlyOwner { require( newOwner != owner() && newOwner != pendingOwner, "Claimable: invalid new owner" ); pendingOwner = newOwner; } function claimOwnership() public onlyPendingOwner { _transferOwnership(pendingOwner); delete pendingOwner; } } library Address { function isContract(address account) internal view returns (bool) { bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; assembly { codehash := extcodehash(account) } return (codehash != 0x0 && codehash != accountHash); } function toPayable(address account) internal pure returns (address payable) { return address(uint160(account)); } function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call.value(amount)(""); require(success, "Address: unable to send value, recipient may have reverted"); } } library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove(IERC20 token, address spender, uint256 value) internal { require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function callOptionalReturn(IERC20 token, bytes memory data) private { require(address(token).isContract(), "SafeERC20: call to non-contract"); (bool success, bytes memory returndata) = address(token).call(data); require(success, "SafeERC20: low-level call failed"); if (returndata.length > 0) { require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } contract CanReclaimTokens is Claimable { using SafeERC20 for ERC20; mapping(address => bool) private recoverableTokensBlacklist; function initialize(address _nextOwner) public initializer { Claimable.initialize(_nextOwner); } function blacklistRecoverableToken(address _token) public onlyOwner { recoverableTokensBlacklist[_token] = true; } function recoverTokens(address _token) external onlyOwner { require( !recoverableTokensBlacklist[_token], "CanReclaimTokens: token is not recoverable" ); if (_token == address(0x0)) { msg.sender.transfer(address(this).balance); } else { ERC20(_token).safeTransfer( msg.sender, ERC20(_token).balanceOf(address(this)) ); } } } contract ERC20WithRate is Initializable, Ownable, ERC20 { using SafeMath for uint256; uint256 public constant _rateScale = 1e18; uint256 internal _rate; event LogRateChanged(uint256 indexed _rate); function initialize(address _nextOwner, uint256 _initialRate) public initializer { Ownable.initialize(_nextOwner); _setRate(_initialRate); } function setExchangeRate(uint256 _nextRate) public onlyOwner { _setRate(_nextRate); } function exchangeRateCurrent() public view returns (uint256) { require(_rate != 0, "ERC20WithRate: rate has not been initialized"); return _rate; } function _setRate(uint256 _nextRate) internal { require(_nextRate > 0, "ERC20WithRate: rate must be greater than zero"); _rate = _nextRate; } function balanceOfUnderlying(address _account) public view returns (uint256) { return toUnderlying(balanceOf(_account)); } function toUnderlying(uint256 _amount) public view returns (uint256) { return _amount.mul(_rate).div(_rateScale); } function fromUnderlying(uint256 _amountUnderlying) public view returns (uint256) { return _amountUnderlying.mul(_rateScale).div(_rate); } } contract ERC20WithPermit is Initializable, ERC20, ERC20Detailed { using SafeMath for uint256; mapping(address => uint256) public nonces; string public version; bytes32 public DOMAIN_SEPARATOR; bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb; function initialize( uint256 _chainId, string memory _version, string memory _name, string memory _symbol, uint8 _decimals ) public initializer { ERC20Detailed.initialize(_name, _symbol, _decimals); version = _version; DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256(bytes(name())), keccak256(bytes(version)), _chainId, address(this) ) ); } function permit( address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s ) external { bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256( abi.encode( PERMIT_TYPEHASH, holder, spender, nonce, expiry, allowed ) ) ) ); require(holder != address(0), "ERC20WithRate: address must not be 0x0"); require( holder == ecrecover(digest, v, r, s), "ERC20WithRate: invalid signature" ); require( expiry == 0 || now <= expiry, "ERC20WithRate: permit has expired" ); require(nonce == nonces[holder]++, "ERC20WithRate: invalid nonce"); uint256 amount = allowed ? uint256(-1) : 0; _approve(holder, spender, amount); } } contract RenERC20LogicV1 is Initializable, ERC20, ERC20Detailed, ERC20WithRate, ERC20WithPermit, Claimable, CanReclaimTokens { function initialize( uint256 _chainId, address _nextOwner, uint256 _initialRate, string memory _version, string memory _name, string memory _symbol, uint8 _decimals ) public initializer { ERC20Detailed.initialize(_name, _symbol, _decimals); ERC20WithRate.initialize(_nextOwner, _initialRate); ERC20WithPermit.initialize( _chainId, _version, _name, _symbol, _decimals ); Claimable.initialize(_nextOwner); CanReclaimTokens.initialize(_nextOwner); } function mint(address _to, uint256 _amount) public onlyOwner { _mint(_to, _amount); } function burn(address _from, uint256 _amount) public onlyOwner { _burn(_from, _amount); } function transfer(address recipient, uint256 amount) public returns (bool) { require( recipient != address(this), "RenERC20: can't transfer to token address" ); return super.transfer(recipient, amount); } function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { require( recipient != address(this), "RenERC20: can't transfer to token address" ); return super.transferFrom(sender, recipient, amount); } } contract RenBTC is InitializableAdminUpgradeabilityProxy {} contract RenZEC is InitializableAdminUpgradeabilityProxy {} contract RenBCH is InitializableAdminUpgradeabilityProxy {}