Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00Token Holdings
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 74 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Settle Expired | 17113124 | 560 days ago | IN | 0 ETH | 0.00716621 | ||||
Expire | 13909831 | 1039 days ago | IN | 0 ETH | 0.04445731 | ||||
Redeem | 13698888 | 1072 days ago | IN | 0 ETH | 0.0133842 | ||||
Redeem | 13654380 | 1079 days ago | IN | 0 ETH | 0.01089435 | ||||
Redeem | 13517678 | 1101 days ago | IN | 0 ETH | 0.01590032 | ||||
Redeem | 13460454 | 1110 days ago | IN | 0 ETH | 0.00734269 | ||||
Redeem | 13459101 | 1110 days ago | IN | 0 ETH | 0.00935706 | ||||
Redeem | 13402242 | 1119 days ago | IN | 0 ETH | 0.01035153 | ||||
Redeem | 13401629 | 1119 days ago | IN | 0 ETH | 0.012398 | ||||
Redeem | 13401546 | 1119 days ago | IN | 0 ETH | 0.01484952 | ||||
Redeem | 13383649 | 1122 days ago | IN | 0 ETH | 0.01110256 | ||||
Redeem | 13377061 | 1123 days ago | IN | 0 ETH | 0.01438405 | ||||
Redeem | 13376871 | 1123 days ago | IN | 0 ETH | 0.01683752 | ||||
Redeem | 13376786 | 1123 days ago | IN | 0 ETH | 0.01565548 | ||||
Redeem | 13303206 | 1134 days ago | IN | 0 ETH | 0.00687317 | ||||
Redeem | 13302300 | 1135 days ago | IN | 0 ETH | 0.00811855 | ||||
Withdraw Passed ... | 13250150 | 1143 days ago | IN | 0 ETH | 0.00698454 | ||||
Request Withdraw... | 13248741 | 1143 days ago | IN | 0 ETH | 0.0035184 | ||||
Create | 13248662 | 1143 days ago | IN | 0 ETH | 0.00774916 | ||||
Redeem | 13206997 | 1149 days ago | IN | 0 ETH | 0.00780285 | ||||
Redeem | 13182665 | 1153 days ago | IN | 0 ETH | 0.01669039 | ||||
Redeem | 13085099 | 1168 days ago | IN | 0 ETH | 0.01044294 | ||||
Create | 13027109 | 1177 days ago | IN | 0 ETH | 0.00423771 | ||||
Withdraw Passed ... | 13025448 | 1177 days ago | IN | 0 ETH | 0.00514225 | ||||
Request Withdraw... | 13024815 | 1177 days ago | IN | 0 ETH | 0.0033828 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
12042160 | 1330 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x5A7f8F8B...70Db9516B The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
ExpiringMultiParty
Compiler Version
v0.6.12+commit.27d51765
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "./Liquidatable.sol"; /** * @title Expiring Multi Party. * @notice Convenient wrapper for Liquidatable. */ contract ExpiringMultiParty is Liquidatable { /** * @notice Constructs the ExpiringMultiParty contract. * @param params struct to define input parameters for construction of Liquidatable. Some params * are fed directly into the PricelessPositionManager's constructor within the inheritance tree. */ constructor(ConstructorParams memory params) public Liquidatable(params) // Note: since there is no logic here, there is no need to add a re-entrancy guard. { } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "@openzeppelin/contracts/access/Ownable.sol"; import "./Lockable.sol"; /** * @title A contract to track a whitelist of addresses. */ contract AddressWhitelist is Ownable, Lockable { enum Status { None, In, Out } mapping(address => Status) public whitelist; address[] public whitelistIndices; event AddedToWhitelist(address indexed addedAddress); event RemovedFromWhitelist(address indexed removedAddress); /** * @notice Adds an address to the whitelist. * @param newElement the new address to add. */ function addToWhitelist(address newElement) external nonReentrant() onlyOwner { // Ignore if address is already included if (whitelist[newElement] == Status.In) { return; } // Only append new addresses to the array, never a duplicate if (whitelist[newElement] == Status.None) { whitelistIndices.push(newElement); } whitelist[newElement] = Status.In; emit AddedToWhitelist(newElement); } /** * @notice Removes an address from the whitelist. * @param elementToRemove the existing address to remove. */ function removeFromWhitelist(address elementToRemove) external nonReentrant() onlyOwner { if (whitelist[elementToRemove] != Status.Out) { whitelist[elementToRemove] = Status.Out; emit RemovedFromWhitelist(elementToRemove); } } /** * @notice Checks whether an address is on the whitelist. * @param elementToCheck the address to check. * @return True if `elementToCheck` is on the whitelist, or False. */ function isOnWhitelist(address elementToCheck) external view nonReentrantView() returns (bool) { return whitelist[elementToCheck] == Status.In; } /** * @notice Gets all addresses that are currently included in the whitelist. * @dev Note: This method skips over, but still iterates through addresses. It is possible for this call to run out * of gas if a large number of addresses have been removed. To reduce the likelihood of this unlikely scenario, we * can modify the implementation so that when addresses are removed, the last addresses in the array is moved to * the empty index. * @return activeWhitelist the list of addresses on the whitelist. */ function getWhitelist() external view nonReentrantView() returns (address[] memory activeWhitelist) { // Determine size of whitelist first uint256 activeCount = 0; for (uint256 i = 0; i < whitelistIndices.length; i++) { if (whitelist[whitelistIndices[i]] == Status.In) { activeCount++; } } // Populate whitelist activeWhitelist = new address[](activeCount); activeCount = 0; for (uint256 i = 0; i < whitelistIndices.length; i++) { address addr = whitelistIndices[i]; if (whitelist[addr] == Status.In) { activeWhitelist[activeCount] = addr; activeCount++; } } } }
pragma solidity ^0.6.0; import "../GSN/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(_owner == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; /** * @title A contract that provides modifiers to prevent reentrancy to state-changing and view-only methods. This contract * is inspired by https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol * and https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol. */ contract Lockable { bool private _notEntered; constructor() internal { // Storing an initial non-zero value makes deployment a bit more // expensive, but in exchange the refund on every call to nonReentrant // will be lower in amount. Since refunds are capped to a percetange of // the total transaction's gas, it is best to keep them low in cases // like this one, to increase the likelihood of the full refund coming // into effect. _notEntered = true; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { _preEntranceCheck(); _preEntranceSet(); _; _postEntranceReset(); } /** * @dev Designed to prevent a view-only method from being re-entered during a call to a `nonReentrant()` state-changing method. */ modifier nonReentrantView() { _preEntranceCheck(); _; } // Internal methods are used to avoid copying the require statement's bytecode to every `nonReentrant()` method. // On entry into a function, `_preEntranceCheck()` should always be called to check if the function is being re-entered. // Then, if the function modifies state, it should call `_postEntranceSet()`, perform its logic, and then call `_postEntranceReset()`. // View-only methods can simply call `_preEntranceCheck()` to make sure that it is not being re-entered. function _preEntranceCheck() internal view { // On the first call to nonReentrant, _notEntered will be true require(_notEntered, "ReentrancyGuard: reentrant call"); } function _preEntranceSet() internal { // Any calls to nonReentrant after this point will fail _notEntered = false; } function _postEntranceReset() internal { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _notEntered = true; } }
pragma solidity ^0.6.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ contract Context { // Empty internal constructor, to prevent people from mistakenly deploying // an instance of this contract, which should be used via inheritance. constructor () internal { } function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "../interfaces/StoreInterface.sol"; import "../interfaces/OracleAncillaryInterface.sol"; import "../interfaces/FinderInterface.sol"; import "../interfaces/IdentifierWhitelistInterface.sol"; import "../interfaces/OptimisticOracleInterface.sol"; import "./Constants.sol"; import "../../common/implementation/Testable.sol"; import "../../common/implementation/Lockable.sol"; import "../../common/implementation/FixedPoint.sol"; import "../../common/implementation/AddressWhitelist.sol"; /** * @title Optimistic Requester. * @notice Optional interface that requesters can implement to receive callbacks. * @dev this contract does _not_ work with ERC777 collateral currencies or any others that call into the receiver on * transfer(). Using an ERC777 token would allow a user to maliciously grief other participants (while also losing * money themselves). */ interface OptimisticRequester { /** * @notice Callback for proposals. * @param identifier price identifier being requested. * @param timestamp timestamp of the price being requested. * @param ancillaryData ancillary data of the price being requested. */ function priceProposed( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) external; /** * @notice Callback for disputes. * @param identifier price identifier being requested. * @param timestamp timestamp of the price being requested. * @param ancillaryData ancillary data of the price being requested. * @param refund refund received in the case that refundOnDispute was enabled. */ function priceDisputed( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, uint256 refund ) external; /** * @notice Callback for settlement. * @param identifier price identifier being requested. * @param timestamp timestamp of the price being requested. * @param ancillaryData ancillary data of the price being requested. * @param price price that was resolved by the escalation process. */ function priceSettled( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, int256 price ) external; } /** * @title Optimistic Oracle. * @notice Pre-DVM escalation contract that allows faster settlement. */ contract OptimisticOracle is OptimisticOracleInterface, Testable, Lockable { using SafeMath for uint256; using SafeERC20 for IERC20; using Address for address; event RequestPrice( address indexed requester, bytes32 identifier, uint256 timestamp, bytes ancillaryData, address currency, uint256 reward, uint256 finalFee ); event ProposePrice( address indexed requester, address indexed proposer, bytes32 identifier, uint256 timestamp, bytes ancillaryData, int256 proposedPrice, uint256 expirationTimestamp, address currency ); event DisputePrice( address indexed requester, address indexed proposer, address indexed disputer, bytes32 identifier, uint256 timestamp, bytes ancillaryData, int256 proposedPrice ); event Settle( address indexed requester, address indexed proposer, address indexed disputer, bytes32 identifier, uint256 timestamp, bytes ancillaryData, int256 price, uint256 payout ); mapping(bytes32 => Request) public requests; // Finder to provide addresses for DVM contracts. FinderInterface public finder; // Default liveness value for all price requests. uint256 public defaultLiveness; /** * @notice Constructor. * @param _liveness default liveness applied to each price request. * @param _finderAddress finder to use to get addresses of DVM contracts. * @param _timerAddress address of the timer contract. Should be 0x0 in prod. */ constructor( uint256 _liveness, address _finderAddress, address _timerAddress ) public Testable(_timerAddress) { finder = FinderInterface(_finderAddress); _validateLiveness(_liveness); defaultLiveness = _liveness; } /** * @notice Requests a new price. * @param identifier price identifier being requested. * @param timestamp timestamp of the price being requested. * @param ancillaryData ancillary data representing additional args being passed with the price request. * @param currency ERC20 token used for payment of rewards and fees. Must be approved for use with the DVM. * @param reward reward offered to a successful proposer. Will be pulled from the caller. Note: this can be 0, * which could make sense if the contract requests and proposes the value in the same call or * provides its own reward system. * @return totalBond default bond (final fee) + final fee that the proposer and disputer will be required to pay. * This can be changed with a subsequent call to setBond(). */ function requestPrice( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, IERC20 currency, uint256 reward ) external override nonReentrant() returns (uint256 totalBond) { require(getState(msg.sender, identifier, timestamp, ancillaryData) == State.Invalid, "requestPrice: Invalid"); require(_getIdentifierWhitelist().isIdentifierSupported(identifier), "Unsupported identifier"); require(_getCollateralWhitelist().isOnWhitelist(address(currency)), "Unsupported currency"); require(timestamp <= getCurrentTime(), "Timestamp in future"); require(ancillaryData.length <= ancillaryBytesLimit, "Invalid ancillary data"); uint256 finalFee = _getStore().computeFinalFee(address(currency)).rawValue; requests[_getId(msg.sender, identifier, timestamp, ancillaryData)] = Request({ proposer: address(0), disputer: address(0), currency: currency, settled: false, refundOnDispute: false, proposedPrice: 0, resolvedPrice: 0, expirationTime: 0, reward: reward, finalFee: finalFee, bond: finalFee, customLiveness: 0 }); if (reward > 0) { currency.safeTransferFrom(msg.sender, address(this), reward); } emit RequestPrice(msg.sender, identifier, timestamp, ancillaryData, address(currency), reward, finalFee); // This function returns the initial proposal bond for this request, which can be customized by calling // setBond() with the same identifier and timestamp. return finalFee.mul(2); } /** * @notice Set the proposal bond associated with a price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @param bond custom bond amount to set. * @return totalBond new bond + final fee that the proposer and disputer will be required to pay. This can be * changed again with a subsequent call to setBond(). */ function setBond( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, uint256 bond ) external override nonReentrant() returns (uint256 totalBond) { require(getState(msg.sender, identifier, timestamp, ancillaryData) == State.Requested, "setBond: Requested"); Request storage request = _getRequest(msg.sender, identifier, timestamp, ancillaryData); request.bond = bond; // Total bond is the final fee + the newly set bond. return bond.add(request.finalFee); } /** * @notice Sets the request to refund the reward if the proposal is disputed. This can help to "hedge" the caller * in the event of a dispute-caused delay. Note: in the event of a dispute, the winner still receives the other's * bond, so there is still profit to be made even if the reward is refunded. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. */ function setRefundOnDispute( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) external override nonReentrant() { require( getState(msg.sender, identifier, timestamp, ancillaryData) == State.Requested, "setRefundOnDispute: Requested" ); _getRequest(msg.sender, identifier, timestamp, ancillaryData).refundOnDispute = true; } /** * @notice Sets a custom liveness value for the request. Liveness is the amount of time a proposal must wait before * being auto-resolved. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @param customLiveness new custom liveness. */ function setCustomLiveness( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, uint256 customLiveness ) external override nonReentrant() { require( getState(msg.sender, identifier, timestamp, ancillaryData) == State.Requested, "setCustomLiveness: Requested" ); _validateLiveness(customLiveness); _getRequest(msg.sender, identifier, timestamp, ancillaryData).customLiveness = customLiveness; } /** * @notice Proposes a price value on another address' behalf. Note: this address will receive any rewards that come * from this proposal. However, any bonds are pulled from the caller. * @param proposer address to set as the proposer. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @param proposedPrice price being proposed. * @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to * the proposer once settled if the proposal is correct. */ function proposePriceFor( address proposer, address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, int256 proposedPrice ) public override nonReentrant() returns (uint256 totalBond) { require(proposer != address(0), "proposer address must be non 0"); require( getState(requester, identifier, timestamp, ancillaryData) == State.Requested, "proposePriceFor: Requested" ); Request storage request = _getRequest(requester, identifier, timestamp, ancillaryData); request.proposer = proposer; request.proposedPrice = proposedPrice; // If a custom liveness has been set, use it instead of the default. request.expirationTime = getCurrentTime().add( request.customLiveness != 0 ? request.customLiveness : defaultLiveness ); totalBond = request.bond.add(request.finalFee); if (totalBond > 0) { request.currency.safeTransferFrom(msg.sender, address(this), totalBond); } emit ProposePrice( requester, proposer, identifier, timestamp, ancillaryData, proposedPrice, request.expirationTime, address(request.currency) ); // Callback. if (address(requester).isContract()) try OptimisticRequester(requester).priceProposed(identifier, timestamp, ancillaryData) {} catch {} } /** * @notice Proposes a price value for an existing price request. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @param proposedPrice price being proposed. * @return totalBond the amount that's pulled from the proposer's wallet as a bond. The bond will be returned to * the proposer once settled if the proposal is correct. */ function proposePrice( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, int256 proposedPrice ) external override returns (uint256 totalBond) { // Note: re-entrancy guard is done in the inner call. return proposePriceFor(msg.sender, requester, identifier, timestamp, ancillaryData, proposedPrice); } /** * @notice Disputes a price request with an active proposal on another address' behalf. Note: this address will * receive any rewards that come from this dispute. However, any bonds are pulled from the caller. * @param disputer address to set as the disputer. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to * the disputer once settled if the dispute was valid (the proposal was incorrect). */ function disputePriceFor( address disputer, address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) public override nonReentrant() returns (uint256 totalBond) { require(disputer != address(0), "disputer address must be non 0"); require( getState(requester, identifier, timestamp, ancillaryData) == State.Proposed, "disputePriceFor: Proposed" ); Request storage request = _getRequest(requester, identifier, timestamp, ancillaryData); request.disputer = disputer; uint256 finalFee = request.finalFee; uint256 bond = request.bond; totalBond = bond.add(finalFee); if (totalBond > 0) { request.currency.safeTransferFrom(msg.sender, address(this), totalBond); } StoreInterface store = _getStore(); // Avoids stack too deep compilation error. { // Along with the final fee, "burn" part of the loser's bond to ensure that a larger bond always makes it // proportionally more expensive to delay the resolution even if the proposer and disputer are the same // party. uint256 burnedBond = _computeBurnedBond(request); // The total fee is the burned bond and the final fee added together. uint256 totalFee = finalFee.add(burnedBond); if (totalFee > 0) { request.currency.safeIncreaseAllowance(address(store), totalFee); _getStore().payOracleFeesErc20(address(request.currency), FixedPoint.Unsigned(totalFee)); } } _getOracle().requestPrice(identifier, timestamp, _stampAncillaryData(ancillaryData, requester)); // Compute refund. uint256 refund = 0; if (request.reward > 0 && request.refundOnDispute) { refund = request.reward; request.reward = 0; request.currency.safeTransfer(requester, refund); } emit DisputePrice( requester, request.proposer, disputer, identifier, timestamp, ancillaryData, request.proposedPrice ); // Callback. if (address(requester).isContract()) try OptimisticRequester(requester).priceDisputed(identifier, timestamp, ancillaryData, refund) {} catch {} } /** * @notice Disputes a price value for an existing price request with an active proposal. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return totalBond the amount that's pulled from the disputer's wallet as a bond. The bond will be returned to * the disputer once settled if the dispute was valid (the proposal was incorrect). */ function disputePrice( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) external override returns (uint256 totalBond) { // Note: re-entrancy guard is done in the inner call. return disputePriceFor(msg.sender, requester, identifier, timestamp, ancillaryData); } /** * @notice Retrieves a price that was previously requested by a caller. Reverts if the request is not settled * or settleable. Note: this method is not view so that this call may actually settle the price request if it * hasn't been settled. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return resolved price. */ function settleAndGetPrice( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) external override nonReentrant() returns (int256) { if (getState(msg.sender, identifier, timestamp, ancillaryData) != State.Settled) { _settle(msg.sender, identifier, timestamp, ancillaryData); } return _getRequest(msg.sender, identifier, timestamp, ancillaryData).resolvedPrice; } /** * @notice Attempts to settle an outstanding price request. Will revert if it isn't settleable. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return payout the amount that the "winner" (proposer or disputer) receives on settlement. This amount includes * the returned bonds as well as additional rewards. */ function settle( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) external override nonReentrant() returns (uint256 payout) { return _settle(requester, identifier, timestamp, ancillaryData); } /** * @notice Gets the current data structure containing all information about a price request. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return the Request data structure. */ function getRequest( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) public view override returns (Request memory) { return _getRequest(requester, identifier, timestamp, ancillaryData); } /** * @notice Computes the current state of a price request. See the State enum for more details. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return the State. */ function getState( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) public view override returns (State) { Request storage request = _getRequest(requester, identifier, timestamp, ancillaryData); if (address(request.currency) == address(0)) { return State.Invalid; } if (request.proposer == address(0)) { return State.Requested; } if (request.settled) { return State.Settled; } if (request.disputer == address(0)) { return request.expirationTime <= getCurrentTime() ? State.Expired : State.Proposed; } return _getOracle().hasPrice(identifier, timestamp, _stampAncillaryData(ancillaryData, requester)) ? State.Resolved : State.Disputed; } /** * @notice Checks if a given request has resolved, expired or been settled (i.e the optimistic oracle has a price). * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return boolean indicating true if price exists and false if not. */ function hasPrice( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) public view override returns (bool) { State state = getState(requester, identifier, timestamp, ancillaryData); return state == State.Settled || state == State.Resolved || state == State.Expired; } /** * @notice Generates stamped ancillary data in the format that it would be used in the case of a price dispute. * @param ancillaryData ancillary data of the price being requested. * @param requester sender of the initial price request. * @return the stampped ancillary bytes. */ function stampAncillaryData(bytes memory ancillaryData, address requester) public pure returns (bytes memory) { return _stampAncillaryData(ancillaryData, requester); } function _getId( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) private pure returns (bytes32) { return keccak256(abi.encodePacked(requester, identifier, timestamp, ancillaryData)); } function _settle( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) private returns (uint256 payout) { State state = getState(requester, identifier, timestamp, ancillaryData); // Set it to settled so this function can never be entered again. Request storage request = _getRequest(requester, identifier, timestamp, ancillaryData); request.settled = true; if (state == State.Expired) { // In the expiry case, just pay back the proposer's bond and final fee along with the reward. request.resolvedPrice = request.proposedPrice; payout = request.bond.add(request.finalFee).add(request.reward); request.currency.safeTransfer(request.proposer, payout); } else if (state == State.Resolved) { // In the Resolved case, pay either the disputer or the proposer the entire payout (+ bond and reward). request.resolvedPrice = _getOracle().getPrice( identifier, timestamp, _stampAncillaryData(ancillaryData, requester) ); bool disputeSuccess = request.resolvedPrice != request.proposedPrice; uint256 bond = request.bond; // Unburned portion of the loser's bond = 1 - burned bond. uint256 unburnedBond = bond.sub(_computeBurnedBond(request)); // Winner gets: // - Their bond back. // - The unburned portion of the loser's bond. // - Their final fee back. // - The request reward (if not already refunded -- if refunded, it will be set to 0). payout = bond.add(unburnedBond).add(request.finalFee).add(request.reward); request.currency.safeTransfer(disputeSuccess ? request.disputer : request.proposer, payout); } else { revert("_settle: not settleable"); } emit Settle( requester, request.proposer, request.disputer, identifier, timestamp, ancillaryData, request.resolvedPrice, payout ); // Callback. if (address(requester).isContract()) try OptimisticRequester(requester).priceSettled(identifier, timestamp, ancillaryData, request.resolvedPrice) {} catch {} } function _getRequest( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) private view returns (Request storage) { return requests[_getId(requester, identifier, timestamp, ancillaryData)]; } function _computeBurnedBond(Request storage request) private view returns (uint256) { // burnedBond = floor(bond / 2) return request.bond.div(2); } function _validateLiveness(uint256 _liveness) private pure { require(_liveness < 5200 weeks, "Liveness too large"); require(_liveness > 0, "Liveness cannot be 0"); } function _getOracle() internal view returns (OracleAncillaryInterface) { return OracleAncillaryInterface(finder.getImplementationAddress(OracleInterfaces.Oracle)); } function _getCollateralWhitelist() internal view returns (AddressWhitelist) { return AddressWhitelist(finder.getImplementationAddress(OracleInterfaces.CollateralWhitelist)); } function _getStore() internal view returns (StoreInterface) { return StoreInterface(finder.getImplementationAddress(OracleInterfaces.Store)); } function _getIdentifierWhitelist() internal view returns (IdentifierWhitelistInterface) { return IdentifierWhitelistInterface(finder.getImplementationAddress(OracleInterfaces.IdentifierWhitelist)); } // Stamps the ancillary data blob with the optimistic oracle tag denoting what contract requested it. function _stampAncillaryData(bytes memory ancillaryData, address requester) internal pure returns (bytes memory) { return abi.encodePacked(ancillaryData, "OptimisticOracle", requester); } }
pragma solidity ^0.6.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
pragma solidity ^0.6.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ 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 { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length 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)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. // A Solidity high level call has three parts: // 1. The target address is checked to verify it contains contract code // 2. The call itself is made, and success asserted // 3. The return value is decoded, which in turn checks the size of the returned data. // solhint-disable-next-line max-line-length require(address(token).isContract(), "SafeERC20: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
pragma solidity ^0.6.0; /** * @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) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } }
pragma solidity ^0.6.2; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../../common/implementation/FixedPoint.sol"; /** * @title Interface that allows financial contracts to pay oracle fees for their use of the system. */ interface StoreInterface { /** * @notice Pays Oracle fees in ETH to the store. * @dev To be used by contracts whose margin currency is ETH. */ function payOracleFees() external payable; /** * @notice Pays oracle fees in the margin currency, erc20Address, to the store. * @dev To be used if the margin currency is an ERC20 token rather than ETH. * @param erc20Address address of the ERC20 token used to pay the fee. * @param amount number of tokens to transfer. An approval for at least this amount must exist. */ function payOracleFeesErc20(address erc20Address, FixedPoint.Unsigned calldata amount) external; /** * @notice Computes the regular oracle fees that a contract should pay for a period. * @param startTime defines the beginning time from which the fee is paid. * @param endTime end time until which the fee is paid. * @param pfc "profit from corruption", or the maximum amount of margin currency that a * token sponsor could extract from the contract through corrupting the price feed in their favor. * @return regularFee amount owed for the duration from start to end time for the given pfc. * @return latePenalty for paying the fee after the deadline. */ function computeRegularFee( uint256 startTime, uint256 endTime, FixedPoint.Unsigned calldata pfc ) external view returns (FixedPoint.Unsigned memory regularFee, FixedPoint.Unsigned memory latePenalty); /** * @notice Computes the final oracle fees that a contract should pay at settlement. * @param currency token used to pay the final fee. * @return finalFee amount due. */ function computeFinalFee(address currency) external view returns (FixedPoint.Unsigned memory); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; /** * @title Financial contract facing Oracle interface. * @dev Interface used by financial contracts to interact with the Oracle. Voters will use a different interface. */ abstract contract OracleAncillaryInterface { /** * @notice Enqueues a request (if a request isn't already present) for the given `identifier`, `time` pair. * @dev Time must be in the past and the identifier must be supported. * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. * @param ancillaryData arbitrary data appended to a price request to give the voters more info from the caller. * @param time unix timestamp for the price request. */ function requestPrice( bytes32 identifier, uint256 time, bytes memory ancillaryData ) public virtual; /** * @notice Whether the price for `identifier` and `time` is available. * @dev Time must be in the past and the identifier must be supported. * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. * @param time unix timestamp for the price request. * @param ancillaryData arbitrary data appended to a price request to give the voters more info from the caller. * @return bool if the DVM has resolved to a price for the given identifier and timestamp. */ function hasPrice( bytes32 identifier, uint256 time, bytes memory ancillaryData ) public view virtual returns (bool); /** * @notice Gets the price for `identifier` and `time` if it has already been requested and resolved. * @dev If the price is not available, the method reverts. * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. * @param time unix timestamp for the price request. * @param ancillaryData arbitrary data appended to a price request to give the voters more info from the caller. * @return int256 representing the resolved price for the given identifier and timestamp. */ function getPrice( bytes32 identifier, uint256 time, bytes memory ancillaryData ) public view virtual returns (int256); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; /** * @title Provides addresses of the live contracts implementing certain interfaces. * @dev Examples are the Oracle or Store interfaces. */ interface FinderInterface { /** * @notice Updates the address of the contract that implements `interfaceName`. * @param interfaceName bytes32 encoding of the interface name that is either changed or registered. * @param implementationAddress address of the deployed contract that implements the interface. */ function changeImplementationAddress(bytes32 interfaceName, address implementationAddress) external; /** * @notice Gets the address of the contract that implements the given `interfaceName`. * @param interfaceName queried interface. * @return implementationAddress address of the deployed contract that implements the interface. */ function getImplementationAddress(bytes32 interfaceName) external view returns (address); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; /** * @title Interface for whitelists of supported identifiers that the oracle can provide prices for. */ interface IdentifierWhitelistInterface { /** * @notice Adds the provided identifier as a supported identifier. * @dev Price requests using this identifier will succeed after this call. * @param identifier bytes32 encoding of the string identifier. Eg: BTC/USD. */ function addSupportedIdentifier(bytes32 identifier) external; /** * @notice Removes the identifier from the whitelist. * @dev Price requests using this identifier will no longer succeed after this call. * @param identifier bytes32 encoding of the string identifier. Eg: BTC/USD. */ function removeSupportedIdentifier(bytes32 identifier) external; /** * @notice Checks whether an identifier is on the whitelist. * @param identifier bytes32 encoding of the string identifier. Eg: BTC/USD. * @return bool if the identifier is supported (or not). */ function isIdentifierSupported(bytes32 identifier) external view returns (bool); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @title Financial contract facing Oracle interface. * @dev Interface used by financial contracts to interact with the Oracle. Voters will use a different interface. */ abstract contract OptimisticOracleInterface { // Struct representing the state of a price request. enum State { Invalid, // Never requested. Requested, // Requested, no other actions taken. Proposed, // Proposed, but not expired or disputed yet. Expired, // Proposed, not disputed, past liveness. Disputed, // Disputed, but no DVM price returned yet. Resolved, // Disputed and DVM price is available. Settled // Final price has been set in the contract (can get here from Expired or Resolved). } // Struct representing a price request. struct Request { address proposer; // Address of the proposer. address disputer; // Address of the disputer. IERC20 currency; // ERC20 token used to pay rewards and fees. bool settled; // True if the request is settled. bool refundOnDispute; // True if the requester should be refunded their reward on dispute. int256 proposedPrice; // Price that the proposer submitted. int256 resolvedPrice; // Price resolved once the request is settled. uint256 expirationTime; // Time at which the request auto-settles without a dispute. uint256 reward; // Amount of the currency to pay to the proposer on settlement. uint256 finalFee; // Final fee to pay to the Store upon request to the DVM. uint256 bond; // Bond that the proposer and disputer must pay on top of the final fee. uint256 customLiveness; // Custom liveness value set by the requester. } // This value must be <= the Voting contract's `ancillaryBytesLimit` value otherwise it is possible // that a price can be requested to this contract successfully, but cannot be disputed because the DVM refuses // to accept a price request made with ancillary data length of a certain size. uint256 public constant ancillaryBytesLimit = 8192; /** * @notice Requests a new price. * @param identifier price identifier being requested. * @param timestamp timestamp of the price being requested. * @param ancillaryData ancillary data representing additional args being passed with the price request. * @param currency ERC20 token used for payment of rewards and fees. Must be approved for use with the DVM. * @param reward reward offered to a successful proposer. Will be pulled from the caller. Note: this can be 0, * which could make sense if the contract requests and proposes the value in the same call or * provides its own reward system. * @return totalBond default bond (final fee) + final fee that the proposer and disputer will be required to pay. * This can be changed with a subsequent call to setBond(). */ function requestPrice( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, IERC20 currency, uint256 reward ) external virtual returns (uint256 totalBond); /** * @notice Set the proposal bond associated with a price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @param bond custom bond amount to set. * @return totalBond new bond + final fee that the proposer and disputer will be required to pay. This can be * changed again with a subsequent call to setBond(). */ function setBond( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, uint256 bond ) external virtual returns (uint256 totalBond); /** * @notice Sets the request to refund the reward if the proposal is disputed. This can help to "hedge" the caller * in the event of a dispute-caused delay. Note: in the event of a dispute, the winner still receives the other's * bond, so there is still profit to be made even if the reward is refunded. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. */ function setRefundOnDispute( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) external virtual; /** * @notice Sets a custom liveness value for the request. Liveness is the amount of time a proposal must wait before * being auto-resolved. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @param customLiveness new custom liveness. */ function setCustomLiveness( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, uint256 customLiveness ) external virtual; /** * @notice Proposes a price value on another address' behalf. Note: this address will receive any rewards that come * from this proposal. However, any bonds are pulled from the caller. * @param proposer address to set as the proposer. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @param proposedPrice price being proposed. * @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to * the proposer once settled if the proposal is correct. */ function proposePriceFor( address proposer, address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, int256 proposedPrice ) public virtual returns (uint256 totalBond); /** * @notice Proposes a price value for an existing price request. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @param proposedPrice price being proposed. * @return totalBond the amount that's pulled from the proposer's wallet as a bond. The bond will be returned to * the proposer once settled if the proposal is correct. */ function proposePrice( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData, int256 proposedPrice ) external virtual returns (uint256 totalBond); /** * @notice Disputes a price request with an active proposal on another address' behalf. Note: this address will * receive any rewards that come from this dispute. However, any bonds are pulled from the caller. * @param disputer address to set as the disputer. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to * the disputer once settled if the dispute was value (the proposal was incorrect). */ function disputePriceFor( address disputer, address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) public virtual returns (uint256 totalBond); /** * @notice Disputes a price value for an existing price request with an active proposal. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return totalBond the amount that's pulled from the disputer's wallet as a bond. The bond will be returned to * the disputer once settled if the dispute was valid (the proposal was incorrect). */ function disputePrice( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) external virtual returns (uint256 totalBond); /** * @notice Retrieves a price that was previously requested by a caller. Reverts if the request is not settled * or settleable. Note: this method is not view so that this call may actually settle the price request if it * hasn't been settled. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return resolved price. */ function settleAndGetPrice( bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) external virtual returns (int256); /** * @notice Attempts to settle an outstanding price request. Will revert if it isn't settleable. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return payout the amount that the "winner" (proposer or disputer) receives on settlement. This amount includes * the returned bonds as well as additional rewards. */ function settle( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) external virtual returns (uint256 payout); /** * @notice Gets the current data structure containing all information about a price request. * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return the Request data structure. */ function getRequest( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) public view virtual returns (Request memory); function getState( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) public view virtual returns (State); /** * @notice Checks if a given request has resolved or been settled (i.e the optimistic oracle has a price). * @param requester sender of the initial price request. * @param identifier price identifier to identify the existing request. * @param timestamp timestamp to identify the existing request. * @param ancillaryData ancillary data of the price being requested. * @return the State. */ function hasPrice( address requester, bytes32 identifier, uint256 timestamp, bytes memory ancillaryData ) public view virtual returns (bool); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; /** * @title Stores common interface names used throughout the DVM by registration in the Finder. */ library OracleInterfaces { bytes32 public constant Oracle = "Oracle"; bytes32 public constant IdentifierWhitelist = "IdentifierWhitelist"; bytes32 public constant Store = "Store"; bytes32 public constant FinancialContractsAdmin = "FinancialContractsAdmin"; bytes32 public constant Registry = "Registry"; bytes32 public constant CollateralWhitelist = "CollateralWhitelist"; bytes32 public constant OptimisticOracle = "OptimisticOracle"; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "./Timer.sol"; /** * @title Base class that provides time overrides, but only if being run in test mode. */ abstract contract Testable { // If the contract is being run on the test network, then `timerAddress` will be the 0x0 address. // Note: this variable should be set on construction and never modified. address public timerAddress; /** * @notice Constructs the Testable contract. Called by child contracts. * @param _timerAddress Contract that stores the current time in a testing environment. * Must be set to 0x0 for production environments that use live time. */ constructor(address _timerAddress) internal { timerAddress = _timerAddress; } /** * @notice Reverts if not running in test mode. */ modifier onlyIfTest { require(timerAddress != address(0x0)); _; } /** * @notice Sets the current time. * @dev Will revert if not running in test mode. * @param time timestamp to set current Testable time to. */ function setCurrentTime(uint256 time) external onlyIfTest { Timer(timerAddress).setCurrentTime(time); } /** * @notice Gets the current time. Will return the last time set in `setCurrentTime` if running in test mode. * Otherwise, it will return the block timestamp. * @return uint for the current Testable timestamp. */ function getCurrentTime() public view returns (uint256) { if (timerAddress != address(0x0)) { return Timer(timerAddress).getCurrentTime(); } else { return now; // solhint-disable-line not-rely-on-time } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/math/SignedSafeMath.sol"; /** * @title Library for fixed point arithmetic on uints */ library FixedPoint { using SafeMath for uint256; using SignedSafeMath for int256; // Supports 18 decimals. E.g., 1e18 represents "1", 5e17 represents "0.5". // For unsigned values: // This can represent a value up to (2^256 - 1)/10^18 = ~10^59. 10^59 will be stored internally as uint256 10^77. uint256 private constant FP_SCALING_FACTOR = 10**18; // --------------------------------------- UNSIGNED ----------------------------------------------------------------------------- struct Unsigned { uint256 rawValue; } /** * @notice Constructs an `Unsigned` from an unscaled uint, e.g., `b=5` gets stored internally as `5*(10**18)`. * @param a uint to convert into a FixedPoint. * @return the converted FixedPoint. */ function fromUnscaledUint(uint256 a) internal pure returns (Unsigned memory) { return Unsigned(a.mul(FP_SCALING_FACTOR)); } /** * @notice Whether `a` is equal to `b`. * @param a a FixedPoint. * @param b a uint256. * @return True if equal, or False. */ function isEqual(Unsigned memory a, uint256 b) internal pure returns (bool) { return a.rawValue == fromUnscaledUint(b).rawValue; } /** * @notice Whether `a` is equal to `b`. * @param a a FixedPoint. * @param b a FixedPoint. * @return True if equal, or False. */ function isEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) { return a.rawValue == b.rawValue; } /** * @notice Whether `a` is greater than `b`. * @param a a FixedPoint. * @param b a FixedPoint. * @return True if `a > b`, or False. */ function isGreaterThan(Unsigned memory a, Unsigned memory b) internal pure returns (bool) { return a.rawValue > b.rawValue; } /** * @notice Whether `a` is greater than `b`. * @param a a FixedPoint. * @param b a uint256. * @return True if `a > b`, or False. */ function isGreaterThan(Unsigned memory a, uint256 b) internal pure returns (bool) { return a.rawValue > fromUnscaledUint(b).rawValue; } /** * @notice Whether `a` is greater than `b`. * @param a a uint256. * @param b a FixedPoint. * @return True if `a > b`, or False. */ function isGreaterThan(uint256 a, Unsigned memory b) internal pure returns (bool) { return fromUnscaledUint(a).rawValue > b.rawValue; } /** * @notice Whether `a` is greater than or equal to `b`. * @param a a FixedPoint. * @param b a FixedPoint. * @return True if `a >= b`, or False. */ function isGreaterThanOrEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) { return a.rawValue >= b.rawValue; } /** * @notice Whether `a` is greater than or equal to `b`. * @param a a FixedPoint. * @param b a uint256. * @return True if `a >= b`, or False. */ function isGreaterThanOrEqual(Unsigned memory a, uint256 b) internal pure returns (bool) { return a.rawValue >= fromUnscaledUint(b).rawValue; } /** * @notice Whether `a` is greater than or equal to `b`. * @param a a uint256. * @param b a FixedPoint. * @return True if `a >= b`, or False. */ function isGreaterThanOrEqual(uint256 a, Unsigned memory b) internal pure returns (bool) { return fromUnscaledUint(a).rawValue >= b.rawValue; } /** * @notice Whether `a` is less than `b`. * @param a a FixedPoint. * @param b a FixedPoint. * @return True if `a < b`, or False. */ function isLessThan(Unsigned memory a, Unsigned memory b) internal pure returns (bool) { return a.rawValue < b.rawValue; } /** * @notice Whether `a` is less than `b`. * @param a a FixedPoint. * @param b a uint256. * @return True if `a < b`, or False. */ function isLessThan(Unsigned memory a, uint256 b) internal pure returns (bool) { return a.rawValue < fromUnscaledUint(b).rawValue; } /** * @notice Whether `a` is less than `b`. * @param a a uint256. * @param b a FixedPoint. * @return True if `a < b`, or False. */ function isLessThan(uint256 a, Unsigned memory b) internal pure returns (bool) { return fromUnscaledUint(a).rawValue < b.rawValue; } /** * @notice Whether `a` is less than or equal to `b`. * @param a a FixedPoint. * @param b a FixedPoint. * @return True if `a <= b`, or False. */ function isLessThanOrEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) { return a.rawValue <= b.rawValue; } /** * @notice Whether `a` is less than or equal to `b`. * @param a a FixedPoint. * @param b a uint256. * @return True if `a <= b`, or False. */ function isLessThanOrEqual(Unsigned memory a, uint256 b) internal pure returns (bool) { return a.rawValue <= fromUnscaledUint(b).rawValue; } /** * @notice Whether `a` is less than or equal to `b`. * @param a a uint256. * @param b a FixedPoint. * @return True if `a <= b`, or False. */ function isLessThanOrEqual(uint256 a, Unsigned memory b) internal pure returns (bool) { return fromUnscaledUint(a).rawValue <= b.rawValue; } /** * @notice The minimum of `a` and `b`. * @param a a FixedPoint. * @param b a FixedPoint. * @return the minimum of `a` and `b`. */ function min(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) { return a.rawValue < b.rawValue ? a : b; } /** * @notice The maximum of `a` and `b`. * @param a a FixedPoint. * @param b a FixedPoint. * @return the maximum of `a` and `b`. */ function max(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) { return a.rawValue > b.rawValue ? a : b; } /** * @notice Adds two `Unsigned`s, reverting on overflow. * @param a a FixedPoint. * @param b a FixedPoint. * @return the sum of `a` and `b`. */ function add(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) { return Unsigned(a.rawValue.add(b.rawValue)); } /** * @notice Adds an `Unsigned` to an unscaled uint, reverting on overflow. * @param a a FixedPoint. * @param b a uint256. * @return the sum of `a` and `b`. */ function add(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) { return add(a, fromUnscaledUint(b)); } /** * @notice Subtracts two `Unsigned`s, reverting on overflow. * @param a a FixedPoint. * @param b a FixedPoint. * @return the difference of `a` and `b`. */ function sub(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) { return Unsigned(a.rawValue.sub(b.rawValue)); } /** * @notice Subtracts an unscaled uint256 from an `Unsigned`, reverting on overflow. * @param a a FixedPoint. * @param b a uint256. * @return the difference of `a` and `b`. */ function sub(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) { return sub(a, fromUnscaledUint(b)); } /** * @notice Subtracts an `Unsigned` from an unscaled uint256, reverting on overflow. * @param a a uint256. * @param b a FixedPoint. * @return the difference of `a` and `b`. */ function sub(uint256 a, Unsigned memory b) internal pure returns (Unsigned memory) { return sub(fromUnscaledUint(a), b); } /** * @notice Multiplies two `Unsigned`s, reverting on overflow. * @dev This will "floor" the product. * @param a a FixedPoint. * @param b a FixedPoint. * @return the product of `a` and `b`. */ function mul(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) { // There are two caveats with this computation: // 1. Max output for the represented number is ~10^41, otherwise an intermediate value overflows. 10^41 is // stored internally as a uint256 ~10^59. // 2. Results that can't be represented exactly are truncated not rounded. E.g., 1.4 * 2e-18 = 2.8e-18, which // would round to 3, but this computation produces the result 2. // No need to use SafeMath because FP_SCALING_FACTOR != 0. return Unsigned(a.rawValue.mul(b.rawValue) / FP_SCALING_FACTOR); } /** * @notice Multiplies an `Unsigned` and an unscaled uint256, reverting on overflow. * @dev This will "floor" the product. * @param a a FixedPoint. * @param b a uint256. * @return the product of `a` and `b`. */ function mul(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) { return Unsigned(a.rawValue.mul(b)); } /** * @notice Multiplies two `Unsigned`s and "ceil's" the product, reverting on overflow. * @param a a FixedPoint. * @param b a FixedPoint. * @return the product of `a` and `b`. */ function mulCeil(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) { uint256 mulRaw = a.rawValue.mul(b.rawValue); uint256 mulFloor = mulRaw / FP_SCALING_FACTOR; uint256 mod = mulRaw.mod(FP_SCALING_FACTOR); if (mod != 0) { return Unsigned(mulFloor.add(1)); } else { return Unsigned(mulFloor); } } /** * @notice Multiplies an `Unsigned` and an unscaled uint256 and "ceil's" the product, reverting on overflow. * @param a a FixedPoint. * @param b a FixedPoint. * @return the product of `a` and `b`. */ function mulCeil(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) { // Since b is an int, there is no risk of truncation and we can just mul it normally return Unsigned(a.rawValue.mul(b)); } /** * @notice Divides one `Unsigned` by an `Unsigned`, reverting on overflow or division by 0. * @dev This will "floor" the quotient. * @param a a FixedPoint numerator. * @param b a FixedPoint denominator. * @return the quotient of `a` divided by `b`. */ function div(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) { // There are two caveats with this computation: // 1. Max value for the number dividend `a` represents is ~10^41, otherwise an intermediate value overflows. // 10^41 is stored internally as a uint256 10^59. // 2. Results that can't be represented exactly are truncated not rounded. E.g., 2 / 3 = 0.6 repeating, which // would round to 0.666666666666666667, but this computation produces the result 0.666666666666666666. return Unsigned(a.rawValue.mul(FP_SCALING_FACTOR).div(b.rawValue)); } /** * @notice Divides one `Unsigned` by an unscaled uint256, reverting on overflow or division by 0. * @dev This will "floor" the quotient. * @param a a FixedPoint numerator. * @param b a uint256 denominator. * @return the quotient of `a` divided by `b`. */ function div(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) { return Unsigned(a.rawValue.div(b)); } /** * @notice Divides one unscaled uint256 by an `Unsigned`, reverting on overflow or division by 0. * @dev This will "floor" the quotient. * @param a a uint256 numerator. * @param b a FixedPoint denominator. * @return the quotient of `a` divided by `b`. */ function div(uint256 a, Unsigned memory b) internal pure returns (Unsigned memory) { return div(fromUnscaledUint(a), b); } /** * @notice Divides one `Unsigned` by an `Unsigned` and "ceil's" the quotient, reverting on overflow or division by 0. * @param a a FixedPoint numerator. * @param b a FixedPoint denominator. * @return the quotient of `a` divided by `b`. */ function divCeil(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) { uint256 aScaled = a.rawValue.mul(FP_SCALING_FACTOR); uint256 divFloor = aScaled.div(b.rawValue); uint256 mod = aScaled.mod(b.rawValue); if (mod != 0) { return Unsigned(divFloor.add(1)); } else { return Unsigned(divFloor); } } /** * @notice Divides one `Unsigned` by an unscaled uint256 and "ceil's" the quotient, reverting on overflow or division by 0. * @param a a FixedPoint numerator. * @param b a uint256 denominator. * @return the quotient of `a` divided by `b`. */ function divCeil(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) { // Because it is possible that a quotient gets truncated, we can't just call "Unsigned(a.rawValue.div(b))" // similarly to mulCeil with a uint256 as the second parameter. Therefore we need to convert b into an Unsigned. // This creates the possibility of overflow if b is very large. return divCeil(a, fromUnscaledUint(b)); } /** * @notice Raises an `Unsigned` to the power of an unscaled uint256, reverting on overflow. E.g., `b=2` squares `a`. * @dev This will "floor" the result. * @param a a FixedPoint numerator. * @param b a uint256 denominator. * @return output is `a` to the power of `b`. */ function pow(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory output) { output = fromUnscaledUint(1); for (uint256 i = 0; i < b; i = i.add(1)) { output = mul(output, a); } } // ------------------------------------------------- SIGNED ------------------------------------------------------------- // Supports 18 decimals. E.g., 1e18 represents "1", 5e17 represents "0.5". // For signed values: // This can represent a value up (or down) to +-(2^255 - 1)/10^18 = ~10^58. 10^58 will be stored internally as int256 10^76. int256 private constant SFP_SCALING_FACTOR = 10**18; struct Signed { int256 rawValue; } function fromSigned(Signed memory a) internal pure returns (Unsigned memory) { require(a.rawValue >= 0, "Negative value provided"); return Unsigned(uint256(a.rawValue)); } function fromUnsigned(Unsigned memory a) internal pure returns (Signed memory) { require(a.rawValue <= uint256(type(int256).max), "Unsigned too large"); return Signed(int256(a.rawValue)); } /** * @notice Constructs a `Signed` from an unscaled int, e.g., `b=5` gets stored internally as `5*(10**18)`. * @param a int to convert into a FixedPoint.Signed. * @return the converted FixedPoint.Signed. */ function fromUnscaledInt(int256 a) internal pure returns (Signed memory) { return Signed(a.mul(SFP_SCALING_FACTOR)); } /** * @notice Whether `a` is equal to `b`. * @param a a FixedPoint.Signed. * @param b a int256. * @return True if equal, or False. */ function isEqual(Signed memory a, int256 b) internal pure returns (bool) { return a.rawValue == fromUnscaledInt(b).rawValue; } /** * @notice Whether `a` is equal to `b`. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return True if equal, or False. */ function isEqual(Signed memory a, Signed memory b) internal pure returns (bool) { return a.rawValue == b.rawValue; } /** * @notice Whether `a` is greater than `b`. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return True if `a > b`, or False. */ function isGreaterThan(Signed memory a, Signed memory b) internal pure returns (bool) { return a.rawValue > b.rawValue; } /** * @notice Whether `a` is greater than `b`. * @param a a FixedPoint.Signed. * @param b an int256. * @return True if `a > b`, or False. */ function isGreaterThan(Signed memory a, int256 b) internal pure returns (bool) { return a.rawValue > fromUnscaledInt(b).rawValue; } /** * @notice Whether `a` is greater than `b`. * @param a an int256. * @param b a FixedPoint.Signed. * @return True if `a > b`, or False. */ function isGreaterThan(int256 a, Signed memory b) internal pure returns (bool) { return fromUnscaledInt(a).rawValue > b.rawValue; } /** * @notice Whether `a` is greater than or equal to `b`. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return True if `a >= b`, or False. */ function isGreaterThanOrEqual(Signed memory a, Signed memory b) internal pure returns (bool) { return a.rawValue >= b.rawValue; } /** * @notice Whether `a` is greater than or equal to `b`. * @param a a FixedPoint.Signed. * @param b an int256. * @return True if `a >= b`, or False. */ function isGreaterThanOrEqual(Signed memory a, int256 b) internal pure returns (bool) { return a.rawValue >= fromUnscaledInt(b).rawValue; } /** * @notice Whether `a` is greater than or equal to `b`. * @param a an int256. * @param b a FixedPoint.Signed. * @return True if `a >= b`, or False. */ function isGreaterThanOrEqual(int256 a, Signed memory b) internal pure returns (bool) { return fromUnscaledInt(a).rawValue >= b.rawValue; } /** * @notice Whether `a` is less than `b`. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return True if `a < b`, or False. */ function isLessThan(Signed memory a, Signed memory b) internal pure returns (bool) { return a.rawValue < b.rawValue; } /** * @notice Whether `a` is less than `b`. * @param a a FixedPoint.Signed. * @param b an int256. * @return True if `a < b`, or False. */ function isLessThan(Signed memory a, int256 b) internal pure returns (bool) { return a.rawValue < fromUnscaledInt(b).rawValue; } /** * @notice Whether `a` is less than `b`. * @param a an int256. * @param b a FixedPoint.Signed. * @return True if `a < b`, or False. */ function isLessThan(int256 a, Signed memory b) internal pure returns (bool) { return fromUnscaledInt(a).rawValue < b.rawValue; } /** * @notice Whether `a` is less than or equal to `b`. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return True if `a <= b`, or False. */ function isLessThanOrEqual(Signed memory a, Signed memory b) internal pure returns (bool) { return a.rawValue <= b.rawValue; } /** * @notice Whether `a` is less than or equal to `b`. * @param a a FixedPoint.Signed. * @param b an int256. * @return True if `a <= b`, or False. */ function isLessThanOrEqual(Signed memory a, int256 b) internal pure returns (bool) { return a.rawValue <= fromUnscaledInt(b).rawValue; } /** * @notice Whether `a` is less than or equal to `b`. * @param a an int256. * @param b a FixedPoint.Signed. * @return True if `a <= b`, or False. */ function isLessThanOrEqual(int256 a, Signed memory b) internal pure returns (bool) { return fromUnscaledInt(a).rawValue <= b.rawValue; } /** * @notice The minimum of `a` and `b`. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return the minimum of `a` and `b`. */ function min(Signed memory a, Signed memory b) internal pure returns (Signed memory) { return a.rawValue < b.rawValue ? a : b; } /** * @notice The maximum of `a` and `b`. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return the maximum of `a` and `b`. */ function max(Signed memory a, Signed memory b) internal pure returns (Signed memory) { return a.rawValue > b.rawValue ? a : b; } /** * @notice Adds two `Signed`s, reverting on overflow. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return the sum of `a` and `b`. */ function add(Signed memory a, Signed memory b) internal pure returns (Signed memory) { return Signed(a.rawValue.add(b.rawValue)); } /** * @notice Adds an `Signed` to an unscaled int, reverting on overflow. * @param a a FixedPoint.Signed. * @param b an int256. * @return the sum of `a` and `b`. */ function add(Signed memory a, int256 b) internal pure returns (Signed memory) { return add(a, fromUnscaledInt(b)); } /** * @notice Subtracts two `Signed`s, reverting on overflow. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return the difference of `a` and `b`. */ function sub(Signed memory a, Signed memory b) internal pure returns (Signed memory) { return Signed(a.rawValue.sub(b.rawValue)); } /** * @notice Subtracts an unscaled int256 from an `Signed`, reverting on overflow. * @param a a FixedPoint.Signed. * @param b an int256. * @return the difference of `a` and `b`. */ function sub(Signed memory a, int256 b) internal pure returns (Signed memory) { return sub(a, fromUnscaledInt(b)); } /** * @notice Subtracts an `Signed` from an unscaled int256, reverting on overflow. * @param a an int256. * @param b a FixedPoint.Signed. * @return the difference of `a` and `b`. */ function sub(int256 a, Signed memory b) internal pure returns (Signed memory) { return sub(fromUnscaledInt(a), b); } /** * @notice Multiplies two `Signed`s, reverting on overflow. * @dev This will "floor" the product. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return the product of `a` and `b`. */ function mul(Signed memory a, Signed memory b) internal pure returns (Signed memory) { // There are two caveats with this computation: // 1. Max output for the represented number is ~10^41, otherwise an intermediate value overflows. 10^41 is // stored internally as an int256 ~10^59. // 2. Results that can't be represented exactly are truncated not rounded. E.g., 1.4 * 2e-18 = 2.8e-18, which // would round to 3, but this computation produces the result 2. // No need to use SafeMath because SFP_SCALING_FACTOR != 0. return Signed(a.rawValue.mul(b.rawValue) / SFP_SCALING_FACTOR); } /** * @notice Multiplies an `Signed` and an unscaled int256, reverting on overflow. * @dev This will "floor" the product. * @param a a FixedPoint.Signed. * @param b an int256. * @return the product of `a` and `b`. */ function mul(Signed memory a, int256 b) internal pure returns (Signed memory) { return Signed(a.rawValue.mul(b)); } /** * @notice Multiplies two `Signed`s and "ceil's" the product, reverting on overflow. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return the product of `a` and `b`. */ function mulAwayFromZero(Signed memory a, Signed memory b) internal pure returns (Signed memory) { int256 mulRaw = a.rawValue.mul(b.rawValue); int256 mulTowardsZero = mulRaw / SFP_SCALING_FACTOR; // Manual mod because SignedSafeMath doesn't support it. int256 mod = mulRaw % SFP_SCALING_FACTOR; if (mod != 0) { bool isResultPositive = isLessThan(a, 0) == isLessThan(b, 0); int256 valueToAdd = isResultPositive ? int256(1) : int256(-1); return Signed(mulTowardsZero.add(valueToAdd)); } else { return Signed(mulTowardsZero); } } /** * @notice Multiplies an `Signed` and an unscaled int256 and "ceil's" the product, reverting on overflow. * @param a a FixedPoint.Signed. * @param b a FixedPoint.Signed. * @return the product of `a` and `b`. */ function mulAwayFromZero(Signed memory a, int256 b) internal pure returns (Signed memory) { // Since b is an int, there is no risk of truncation and we can just mul it normally return Signed(a.rawValue.mul(b)); } /** * @notice Divides one `Signed` by an `Signed`, reverting on overflow or division by 0. * @dev This will "floor" the quotient. * @param a a FixedPoint numerator. * @param b a FixedPoint denominator. * @return the quotient of `a` divided by `b`. */ function div(Signed memory a, Signed memory b) internal pure returns (Signed memory) { // There are two caveats with this computation: // 1. Max value for the number dividend `a` represents is ~10^41, otherwise an intermediate value overflows. // 10^41 is stored internally as an int256 10^59. // 2. Results that can't be represented exactly are truncated not rounded. E.g., 2 / 3 = 0.6 repeating, which // would round to 0.666666666666666667, but this computation produces the result 0.666666666666666666. return Signed(a.rawValue.mul(SFP_SCALING_FACTOR).div(b.rawValue)); } /** * @notice Divides one `Signed` by an unscaled int256, reverting on overflow or division by 0. * @dev This will "floor" the quotient. * @param a a FixedPoint numerator. * @param b an int256 denominator. * @return the quotient of `a` divided by `b`. */ function div(Signed memory a, int256 b) internal pure returns (Signed memory) { return Signed(a.rawValue.div(b)); } /** * @notice Divides one unscaled int256 by an `Signed`, reverting on overflow or division by 0. * @dev This will "floor" the quotient. * @param a an int256 numerator. * @param b a FixedPoint denominator. * @return the quotient of `a` divided by `b`. */ function div(int256 a, Signed memory b) internal pure returns (Signed memory) { return div(fromUnscaledInt(a), b); } /** * @notice Divides one `Signed` by an `Signed` and "ceil's" the quotient, reverting on overflow or division by 0. * @param a a FixedPoint numerator. * @param b a FixedPoint denominator. * @return the quotient of `a` divided by `b`. */ function divAwayFromZero(Signed memory a, Signed memory b) internal pure returns (Signed memory) { int256 aScaled = a.rawValue.mul(SFP_SCALING_FACTOR); int256 divTowardsZero = aScaled.div(b.rawValue); // Manual mod because SignedSafeMath doesn't support it. int256 mod = aScaled % b.rawValue; if (mod != 0) { bool isResultPositive = isLessThan(a, 0) == isLessThan(b, 0); int256 valueToAdd = isResultPositive ? int256(1) : int256(-1); return Signed(divTowardsZero.add(valueToAdd)); } else { return Signed(divTowardsZero); } } /** * @notice Divides one `Signed` by an unscaled int256 and "ceil's" the quotient, reverting on overflow or division by 0. * @param a a FixedPoint numerator. * @param b an int256 denominator. * @return the quotient of `a` divided by `b`. */ function divAwayFromZero(Signed memory a, int256 b) internal pure returns (Signed memory) { // Because it is possible that a quotient gets truncated, we can't just call "Signed(a.rawValue.div(b))" // similarly to mulCeil with an int256 as the second parameter. Therefore we need to convert b into an Signed. // This creates the possibility of overflow if b is very large. return divAwayFromZero(a, fromUnscaledInt(b)); } /** * @notice Raises an `Signed` to the power of an unscaled uint256, reverting on overflow. E.g., `b=2` squares `a`. * @dev This will "floor" the result. * @param a a FixedPoint.Signed. * @param b a uint256 (negative exponents are not allowed). * @return output is `a` to the power of `b`. */ function pow(Signed memory a, uint256 b) internal pure returns (Signed memory output) { output = fromUnscaledInt(1); for (uint256 i = 0; i < b; i = i.add(1)) { output = mul(output, a); } } }
pragma solidity ^0.6.0; /** * @title SignedSafeMath * @dev Signed math operations with safety checks that revert on error. */ library SignedSafeMath { int256 constant private _INT256_MIN = -2**255; /** * @dev Multiplies two signed integers, reverts on overflow. */ function mul(int256 a, int256 b) internal pure returns (int256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow"); int256 c = a * b; require(c / a == b, "SignedSafeMath: multiplication overflow"); return c; } /** * @dev Integer division of two signed integers truncating the quotient, reverts on division by zero. */ function div(int256 a, int256 b) internal pure returns (int256) { require(b != 0, "SignedSafeMath: division by zero"); require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow"); int256 c = a / b; return c; } /** * @dev Subtracts two signed integers, reverts on overflow. */ function sub(int256 a, int256 b) internal pure returns (int256) { int256 c = a - b; require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); return c; } /** * @dev Adds two signed integers, reverts on overflow. */ function add(int256 a, int256 b) internal pure returns (int256) { int256 c = a + b; require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); return c; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; /** * @title Universal store of current contract time for testing environments. */ contract Timer { uint256 private currentTime; constructor() public { currentTime = now; // solhint-disable-line not-rely-on-time } /** * @notice Sets the current time. * @dev Will revert if not running in test mode. * @param time timestamp to set `currentTime` to. */ function setCurrentTime(uint256 time) external { currentTime = time; } /** * @notice Gets the current time. Will return the last time set in `setCurrentTime` if running in test mode. * Otherwise, it will return the block timestamp. * @return uint256 for the current Testable timestamp. */ function getCurrentTime() public view returns (uint256) { return currentTime; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../OptimisticOracle.sol"; // This is just a test contract to make requests to the optimistic oracle. contract OptimisticRequesterTest is OptimisticRequester { OptimisticOracle optimisticOracle; bool public shouldRevert = false; // State variables to track incoming calls. bytes32 public identifier; uint256 public timestamp; bytes public ancillaryData; uint256 public refund; int256 public price; constructor(OptimisticOracle _optimisticOracle) public { optimisticOracle = _optimisticOracle; } function requestPrice( bytes32 _identifier, uint256 _timestamp, bytes memory _ancillaryData, IERC20 currency, uint256 reward ) external { currency.approve(address(optimisticOracle), reward); optimisticOracle.requestPrice(_identifier, _timestamp, _ancillaryData, currency, reward); } function settleAndGetPrice( bytes32 _identifier, uint256 _timestamp, bytes memory _ancillaryData ) external returns (int256) { return optimisticOracle.settleAndGetPrice(_identifier, _timestamp, _ancillaryData); } function setBond( bytes32 _identifier, uint256 _timestamp, bytes memory _ancillaryData, uint256 bond ) external { optimisticOracle.setBond(_identifier, _timestamp, _ancillaryData, bond); } function setRefundOnDispute( bytes32 _identifier, uint256 _timestamp, bytes memory _ancillaryData ) external { optimisticOracle.setRefundOnDispute(_identifier, _timestamp, _ancillaryData); } function setCustomLiveness( bytes32 _identifier, uint256 _timestamp, bytes memory _ancillaryData, uint256 customLiveness ) external { optimisticOracle.setCustomLiveness(_identifier, _timestamp, _ancillaryData, customLiveness); } function setRevert(bool _shouldRevert) external { shouldRevert = _shouldRevert; } function clearState() external { delete identifier; delete timestamp; delete refund; delete price; } function priceProposed( bytes32 _identifier, uint256 _timestamp, bytes memory _ancillaryData ) external override { require(!shouldRevert); identifier = _identifier; timestamp = _timestamp; ancillaryData = _ancillaryData; } function priceDisputed( bytes32 _identifier, uint256 _timestamp, bytes memory _ancillaryData, uint256 _refund ) external override { require(!shouldRevert); identifier = _identifier; timestamp = _timestamp; ancillaryData = _ancillaryData; refund = _refund; } function priceSettled( bytes32 _identifier, uint256 _timestamp, bytes memory _ancillaryData, int256 _price ) external override { require(!shouldRevert); identifier = _identifier; timestamp = _timestamp; ancillaryData = _ancillaryData; price = _price; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../../common/implementation/FixedPoint.sol"; import "../../common/implementation/MultiRole.sol"; import "../../common/implementation/Withdrawable.sol"; import "../../common/implementation/Testable.sol"; import "../interfaces/StoreInterface.sol"; /** * @title An implementation of Store that can accept Oracle fees in ETH or any arbitrary ERC20 token. */ contract Store is StoreInterface, Withdrawable, Testable { using SafeMath for uint256; using FixedPoint for FixedPoint.Unsigned; using FixedPoint for uint256; using SafeERC20 for IERC20; /**************************************** * INTERNAL VARIABLES AND STORAGE * ****************************************/ enum Roles { Owner, Withdrawer } FixedPoint.Unsigned public fixedOracleFeePerSecondPerPfc; // Percentage of 1 E.g., .1 is 10% Oracle fee. FixedPoint.Unsigned public weeklyDelayFeePerSecondPerPfc; // Percentage of 1 E.g., .1 is 10% weekly delay fee. mapping(address => FixedPoint.Unsigned) public finalFees; uint256 public constant SECONDS_PER_WEEK = 604800; /**************************************** * EVENTS * ****************************************/ event NewFixedOracleFeePerSecondPerPfc(FixedPoint.Unsigned newOracleFee); event NewWeeklyDelayFeePerSecondPerPfc(FixedPoint.Unsigned newWeeklyDelayFeePerSecondPerPfc); event NewFinalFee(FixedPoint.Unsigned newFinalFee); /** * @notice Construct the Store contract. */ constructor( FixedPoint.Unsigned memory _fixedOracleFeePerSecondPerPfc, FixedPoint.Unsigned memory _weeklyDelayFeePerSecondPerPfc, address _timerAddress ) public Testable(_timerAddress) { _createExclusiveRole(uint256(Roles.Owner), uint256(Roles.Owner), msg.sender); _createWithdrawRole(uint256(Roles.Withdrawer), uint256(Roles.Owner), msg.sender); setFixedOracleFeePerSecondPerPfc(_fixedOracleFeePerSecondPerPfc); setWeeklyDelayFeePerSecondPerPfc(_weeklyDelayFeePerSecondPerPfc); } /**************************************** * ORACLE FEE CALCULATION AND PAYMENT * ****************************************/ /** * @notice Pays Oracle fees in ETH to the store. * @dev To be used by contracts whose margin currency is ETH. */ function payOracleFees() external payable override { require(msg.value > 0, "Value sent can't be zero"); } /** * @notice Pays oracle fees in the margin currency, erc20Address, to the store. * @dev To be used if the margin currency is an ERC20 token rather than ETH. * @param erc20Address address of the ERC20 token used to pay the fee. * @param amount number of tokens to transfer. An approval for at least this amount must exist. */ function payOracleFeesErc20(address erc20Address, FixedPoint.Unsigned calldata amount) external override { IERC20 erc20 = IERC20(erc20Address); require(amount.isGreaterThan(0), "Amount sent can't be zero"); erc20.safeTransferFrom(msg.sender, address(this), amount.rawValue); } /** * @notice Computes the regular oracle fees that a contract should pay for a period. * @dev The late penalty is similar to the regular fee in that is is charged per second over the period between * startTime and endTime. * * The late penalty percentage increases over time as follows: * * - 0-1 week since startTime: no late penalty * * - 1-2 weeks since startTime: 1x late penalty percentage is applied * * - 2-3 weeks since startTime: 2x late penalty percentage is applied * * - ... * * @param startTime defines the beginning time from which the fee is paid. * @param endTime end time until which the fee is paid. * @param pfc "profit from corruption", or the maximum amount of margin currency that a * token sponsor could extract from the contract through corrupting the price feed in their favor. * @return regularFee amount owed for the duration from start to end time for the given pfc. * @return latePenalty penalty percentage, if any, for paying the fee after the deadline. */ function computeRegularFee( uint256 startTime, uint256 endTime, FixedPoint.Unsigned calldata pfc ) external view override returns (FixedPoint.Unsigned memory regularFee, FixedPoint.Unsigned memory latePenalty) { uint256 timeDiff = endTime.sub(startTime); // Multiply by the unscaled `timeDiff` first, to get more accurate results. regularFee = pfc.mul(timeDiff).mul(fixedOracleFeePerSecondPerPfc); // Compute how long ago the start time was to compute the delay penalty. uint256 paymentDelay = getCurrentTime().sub(startTime); // Compute the additional percentage (per second) that will be charged because of the penalty. // Note: if less than a week has gone by since the startTime, paymentDelay / SECONDS_PER_WEEK will truncate to // 0, causing no penalty to be charged. FixedPoint.Unsigned memory penaltyPercentagePerSecond = weeklyDelayFeePerSecondPerPfc.mul(paymentDelay.div(SECONDS_PER_WEEK)); // Apply the penaltyPercentagePerSecond to the payment period. latePenalty = pfc.mul(timeDiff).mul(penaltyPercentagePerSecond); } /** * @notice Computes the final oracle fees that a contract should pay at settlement. * @param currency token used to pay the final fee. * @return finalFee amount due denominated in units of `currency`. */ function computeFinalFee(address currency) external view override returns (FixedPoint.Unsigned memory) { return finalFees[currency]; } /**************************************** * ADMIN STATE MODIFYING FUNCTIONS * ****************************************/ /** * @notice Sets a new oracle fee per second. * @param newFixedOracleFeePerSecondPerPfc new fee per second charged to use the oracle. */ function setFixedOracleFeePerSecondPerPfc(FixedPoint.Unsigned memory newFixedOracleFeePerSecondPerPfc) public onlyRoleHolder(uint256(Roles.Owner)) { // Oracle fees at or over 100% don't make sense. require(newFixedOracleFeePerSecondPerPfc.isLessThan(1), "Fee must be < 100% per second."); fixedOracleFeePerSecondPerPfc = newFixedOracleFeePerSecondPerPfc; emit NewFixedOracleFeePerSecondPerPfc(newFixedOracleFeePerSecondPerPfc); } /** * @notice Sets a new weekly delay fee. * @param newWeeklyDelayFeePerSecondPerPfc fee escalation per week of late fee payment. */ function setWeeklyDelayFeePerSecondPerPfc(FixedPoint.Unsigned memory newWeeklyDelayFeePerSecondPerPfc) public onlyRoleHolder(uint256(Roles.Owner)) { require(newWeeklyDelayFeePerSecondPerPfc.isLessThan(1), "weekly delay fee must be < 100%"); weeklyDelayFeePerSecondPerPfc = newWeeklyDelayFeePerSecondPerPfc; emit NewWeeklyDelayFeePerSecondPerPfc(newWeeklyDelayFeePerSecondPerPfc); } /** * @notice Sets a new final fee for a particular currency. * @param currency defines the token currency used to pay the final fee. * @param newFinalFee final fee amount. */ function setFinalFee(address currency, FixedPoint.Unsigned memory newFinalFee) public onlyRoleHolder(uint256(Roles.Owner)) { finalFees[currency] = newFinalFee; emit NewFinalFee(newFinalFee); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; library Exclusive { struct RoleMembership { address member; } function isMember(RoleMembership storage roleMembership, address memberToCheck) internal view returns (bool) { return roleMembership.member == memberToCheck; } function resetMember(RoleMembership storage roleMembership, address newMember) internal { require(newMember != address(0x0), "Cannot set an exclusive role to 0x0"); roleMembership.member = newMember; } function getMember(RoleMembership storage roleMembership) internal view returns (address) { return roleMembership.member; } function init(RoleMembership storage roleMembership, address initialMember) internal { resetMember(roleMembership, initialMember); } } library Shared { struct RoleMembership { mapping(address => bool) members; } function isMember(RoleMembership storage roleMembership, address memberToCheck) internal view returns (bool) { return roleMembership.members[memberToCheck]; } function addMember(RoleMembership storage roleMembership, address memberToAdd) internal { require(memberToAdd != address(0x0), "Cannot add 0x0 to a shared role"); roleMembership.members[memberToAdd] = true; } function removeMember(RoleMembership storage roleMembership, address memberToRemove) internal { roleMembership.members[memberToRemove] = false; } function init(RoleMembership storage roleMembership, address[] memory initialMembers) internal { for (uint256 i = 0; i < initialMembers.length; i++) { addMember(roleMembership, initialMembers[i]); } } } /** * @title Base class to manage permissions for the derived class. */ abstract contract MultiRole { using Exclusive for Exclusive.RoleMembership; using Shared for Shared.RoleMembership; enum RoleType { Invalid, Exclusive, Shared } struct Role { uint256 managingRole; RoleType roleType; Exclusive.RoleMembership exclusiveRoleMembership; Shared.RoleMembership sharedRoleMembership; } mapping(uint256 => Role) private roles; event ResetExclusiveMember(uint256 indexed roleId, address indexed newMember, address indexed manager); event AddedSharedMember(uint256 indexed roleId, address indexed newMember, address indexed manager); event RemovedSharedMember(uint256 indexed roleId, address indexed oldMember, address indexed manager); /** * @notice Reverts unless the caller is a member of the specified roleId. */ modifier onlyRoleHolder(uint256 roleId) { require(holdsRole(roleId, msg.sender), "Sender does not hold required role"); _; } /** * @notice Reverts unless the caller is a member of the manager role for the specified roleId. */ modifier onlyRoleManager(uint256 roleId) { require(holdsRole(roles[roleId].managingRole, msg.sender), "Can only be called by a role manager"); _; } /** * @notice Reverts unless the roleId represents an initialized, exclusive roleId. */ modifier onlyExclusive(uint256 roleId) { require(roles[roleId].roleType == RoleType.Exclusive, "Must be called on an initialized Exclusive role"); _; } /** * @notice Reverts unless the roleId represents an initialized, shared roleId. */ modifier onlyShared(uint256 roleId) { require(roles[roleId].roleType == RoleType.Shared, "Must be called on an initialized Shared role"); _; } /** * @notice Whether `memberToCheck` is a member of roleId. * @dev Reverts if roleId does not correspond to an initialized role. * @param roleId the Role to check. * @param memberToCheck the address to check. * @return True if `memberToCheck` is a member of `roleId`. */ function holdsRole(uint256 roleId, address memberToCheck) public view returns (bool) { Role storage role = roles[roleId]; if (role.roleType == RoleType.Exclusive) { return role.exclusiveRoleMembership.isMember(memberToCheck); } else if (role.roleType == RoleType.Shared) { return role.sharedRoleMembership.isMember(memberToCheck); } revert("Invalid roleId"); } /** * @notice Changes the exclusive role holder of `roleId` to `newMember`. * @dev Reverts if the caller is not a member of the managing role for `roleId` or if `roleId` is not an * initialized, ExclusiveRole. * @param roleId the ExclusiveRole membership to modify. * @param newMember the new ExclusiveRole member. */ function resetMember(uint256 roleId, address newMember) public onlyExclusive(roleId) onlyRoleManager(roleId) { roles[roleId].exclusiveRoleMembership.resetMember(newMember); emit ResetExclusiveMember(roleId, newMember, msg.sender); } /** * @notice Gets the current holder of the exclusive role, `roleId`. * @dev Reverts if `roleId` does not represent an initialized, exclusive role. * @param roleId the ExclusiveRole membership to check. * @return the address of the current ExclusiveRole member. */ function getMember(uint256 roleId) public view onlyExclusive(roleId) returns (address) { return roles[roleId].exclusiveRoleMembership.getMember(); } /** * @notice Adds `newMember` to the shared role, `roleId`. * @dev Reverts if `roleId` does not represent an initialized, SharedRole or if the caller is not a member of the * managing role for `roleId`. * @param roleId the SharedRole membership to modify. * @param newMember the new SharedRole member. */ function addMember(uint256 roleId, address newMember) public onlyShared(roleId) onlyRoleManager(roleId) { roles[roleId].sharedRoleMembership.addMember(newMember); emit AddedSharedMember(roleId, newMember, msg.sender); } /** * @notice Removes `memberToRemove` from the shared role, `roleId`. * @dev Reverts if `roleId` does not represent an initialized, SharedRole or if the caller is not a member of the * managing role for `roleId`. * @param roleId the SharedRole membership to modify. * @param memberToRemove the current SharedRole member to remove. */ function removeMember(uint256 roleId, address memberToRemove) public onlyShared(roleId) onlyRoleManager(roleId) { roles[roleId].sharedRoleMembership.removeMember(memberToRemove); emit RemovedSharedMember(roleId, memberToRemove, msg.sender); } /** * @notice Removes caller from the role, `roleId`. * @dev Reverts if the caller is not a member of the role for `roleId` or if `roleId` is not an * initialized, SharedRole. * @param roleId the SharedRole membership to modify. */ function renounceMembership(uint256 roleId) public onlyShared(roleId) onlyRoleHolder(roleId) { roles[roleId].sharedRoleMembership.removeMember(msg.sender); emit RemovedSharedMember(roleId, msg.sender, msg.sender); } /** * @notice Reverts if `roleId` is not initialized. */ modifier onlyValidRole(uint256 roleId) { require(roles[roleId].roleType != RoleType.Invalid, "Attempted to use an invalid roleId"); _; } /** * @notice Reverts if `roleId` is initialized. */ modifier onlyInvalidRole(uint256 roleId) { require(roles[roleId].roleType == RoleType.Invalid, "Cannot use a pre-existing role"); _; } /** * @notice Internal method to initialize a shared role, `roleId`, which will be managed by `managingRoleId`. * `initialMembers` will be immediately added to the role. * @dev Should be called by derived contracts, usually at construction time. Will revert if the role is already * initialized. */ function _createSharedRole( uint256 roleId, uint256 managingRoleId, address[] memory initialMembers ) internal onlyInvalidRole(roleId) { Role storage role = roles[roleId]; role.roleType = RoleType.Shared; role.managingRole = managingRoleId; role.sharedRoleMembership.init(initialMembers); require( roles[managingRoleId].roleType != RoleType.Invalid, "Attempted to use an invalid role to manage a shared role" ); } /** * @notice Internal method to initialize an exclusive role, `roleId`, which will be managed by `managingRoleId`. * `initialMember` will be immediately added to the role. * @dev Should be called by derived contracts, usually at construction time. Will revert if the role is already * initialized. */ function _createExclusiveRole( uint256 roleId, uint256 managingRoleId, address initialMember ) internal onlyInvalidRole(roleId) { Role storage role = roles[roleId]; role.roleType = RoleType.Exclusive; role.managingRole = managingRoleId; role.exclusiveRoleMembership.init(initialMember); require( roles[managingRoleId].roleType != RoleType.Invalid, "Attempted to use an invalid role to manage an exclusive role" ); } }
/** * Withdrawable contract. */ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./MultiRole.sol"; /** * @title Base contract that allows a specific role to withdraw any ETH and/or ERC20 tokens that the contract holds. */ abstract contract Withdrawable is MultiRole { using SafeERC20 for IERC20; uint256 private roleId; /** * @notice Withdraws ETH from the contract. */ function withdraw(uint256 amount) external onlyRoleHolder(roleId) { Address.sendValue(msg.sender, amount); } /** * @notice Withdraws ERC20 tokens from the contract. * @param erc20Address ERC20 token to withdraw. * @param amount amount of tokens to withdraw. */ function withdrawErc20(address erc20Address, uint256 amount) external onlyRoleHolder(roleId) { IERC20 erc20 = IERC20(erc20Address); erc20.safeTransfer(msg.sender, amount); } /** * @notice Internal method that allows derived contracts to create a role for withdrawal. * @dev Either this method or `_setWithdrawRole` must be called by the derived class for this contract to function * properly. * @param newRoleId ID corresponding to role whose members can withdraw. * @param managingRoleId ID corresponding to managing role who can modify the withdrawable role's membership. * @param withdrawerAddress new manager of withdrawable role. */ function _createWithdrawRole( uint256 newRoleId, uint256 managingRoleId, address withdrawerAddress ) internal { roleId = newRoleId; _createExclusiveRole(newRoleId, managingRoleId, withdrawerAddress); } /** * @notice Internal method that allows derived contracts to choose the role for withdrawal. * @dev The role `setRoleId` must exist. Either this method or `_createWithdrawRole` must be * called by the derived class for this contract to function properly. * @param setRoleId ID corresponding to role whose members can withdraw. */ function _setWithdrawRole(uint256 setRoleId) internal onlyValidRole(setRoleId) { roleId = setRoleId; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/FixedPoint.sol"; import "../../common/implementation/Testable.sol"; import "../interfaces/OracleInterface.sol"; import "../interfaces/VotingInterface.sol"; // A mock oracle used for testing. Exports the voting & oracle interfaces and events that contain no ancillary data. abstract contract VotingInterfaceTesting is OracleInterface, VotingInterface, Testable { using FixedPoint for FixedPoint.Unsigned; // Events, data structures and functions not exported in the base interfaces, used for testing. event VoteCommitted( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, bytes ancillaryData ); event EncryptedVote( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, bytes ancillaryData, bytes encryptedVote ); event VoteRevealed( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, int256 price, bytes ancillaryData, uint256 numTokens ); event RewardsRetrieved( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, bytes ancillaryData, uint256 numTokens ); event PriceRequestAdded(uint256 indexed roundId, bytes32 indexed identifier, uint256 time); event PriceResolved( uint256 indexed roundId, bytes32 indexed identifier, uint256 time, int256 price, bytes ancillaryData ); struct Round { uint256 snapshotId; // Voting token snapshot ID for this round. 0 if no snapshot has been taken. FixedPoint.Unsigned inflationRate; // Inflation rate set for this round. FixedPoint.Unsigned gatPercentage; // Gat rate set for this round. uint256 rewardsExpirationTime; // Time that rewards for this round can be claimed until. } // Represents the status a price request has. enum RequestStatus { NotRequested, // Was never requested. Active, // Is being voted on in the current round. Resolved, // Was resolved in a previous round. Future // Is scheduled to be voted on in a future round. } // Only used as a return value in view methods -- never stored in the contract. struct RequestState { RequestStatus status; uint256 lastVotingRound; } function rounds(uint256 roundId) public view virtual returns (Round memory); function getPriceRequestStatuses(VotingInterface.PendingRequest[] memory requests) public view virtual returns (RequestState[] memory); function getPendingPriceRequestsArray() external view virtual returns (bytes32[] memory); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; /** * @title Financial contract facing Oracle interface. * @dev Interface used by financial contracts to interact with the Oracle. Voters will use a different interface. */ abstract contract OracleInterface { /** * @notice Enqueues a request (if a request isn't already present) for the given `identifier`, `time` pair. * @dev Time must be in the past and the identifier must be supported. * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. * @param time unix timestamp for the price request. */ function requestPrice(bytes32 identifier, uint256 time) public virtual; /** * @notice Whether the price for `identifier` and `time` is available. * @dev Time must be in the past and the identifier must be supported. * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. * @param time unix timestamp for the price request. * @return bool if the DVM has resolved to a price for the given identifier and timestamp. */ function hasPrice(bytes32 identifier, uint256 time) public view virtual returns (bool); /** * @notice Gets the price for `identifier` and `time` if it has already been requested and resolved. * @dev If the price is not available, the method reverts. * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. * @param time unix timestamp for the price request. * @return int256 representing the resolved price for the given identifier and timestamp. */ function getPrice(bytes32 identifier, uint256 time) public view virtual returns (int256); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/FixedPoint.sol"; import "./VotingAncillaryInterface.sol"; /** * @title Interface that voters must use to Vote on price request resolutions. */ abstract contract VotingInterface { struct PendingRequest { bytes32 identifier; uint256 time; } // Captures the necessary data for making a commitment. // Used as a parameter when making batch commitments. // Not used as a data structure for storage. struct Commitment { bytes32 identifier; uint256 time; bytes32 hash; bytes encryptedVote; } // Captures the necessary data for revealing a vote. // Used as a parameter when making batch reveals. // Not used as a data structure for storage. struct Reveal { bytes32 identifier; uint256 time; int256 price; int256 salt; } /** * @notice Commit a vote for a price request for `identifier` at `time`. * @dev `identifier`, `time` must correspond to a price request that's currently in the commit phase. * Commits can be changed. * @dev Since transaction data is public, the salt will be revealed with the vote. While this is the system’s expected behavior, * voters should never reuse salts. If someone else is able to guess the voted price and knows that a salt will be reused, then * they can determine the vote pre-reveal. * @param identifier uniquely identifies the committed vote. EG BTC/USD price pair. * @param time unix timestamp of the price being voted on. * @param hash keccak256 hash of the `price`, `salt`, voter `address`, `time`, current `roundId`, and `identifier`. */ function commitVote( bytes32 identifier, uint256 time, bytes32 hash ) external virtual; /** * @notice Submit a batch of commits in a single transaction. * @dev Using `encryptedVote` is optional. If included then commitment is stored on chain. * Look at `project-root/common/Constants.js` for the tested maximum number of * commitments that can fit in one transaction. * @param commits array of structs that encapsulate an `identifier`, `time`, `hash` and optional `encryptedVote`. */ function batchCommit(Commitment[] memory commits) public virtual; /** * @notice commits a vote and logs an event with a data blob, typically an encrypted version of the vote * @dev An encrypted version of the vote is emitted in an event `EncryptedVote` to allow off-chain infrastructure to * retrieve the commit. The contents of `encryptedVote` are never used on chain: it is purely for convenience. * @param identifier unique price pair identifier. Eg: BTC/USD price pair. * @param time unix timestamp of for the price request. * @param hash keccak256 hash of the price you want to vote for and a `int256 salt`. * @param encryptedVote offchain encrypted blob containing the voters amount, time and salt. */ function commitAndEmitEncryptedVote( bytes32 identifier, uint256 time, bytes32 hash, bytes memory encryptedVote ) public virtual; /** * @notice snapshot the current round's token balances and lock in the inflation rate and GAT. * @dev This function can be called multiple times but each round will only every have one snapshot at the * time of calling `_freezeRoundVariables`. * @param signature signature required to prove caller is an EOA to prevent flash loans from being included in the * snapshot. */ function snapshotCurrentRound(bytes calldata signature) external virtual; /** * @notice Reveal a previously committed vote for `identifier` at `time`. * @dev The revealed `price`, `salt`, `address`, `time`, `roundId`, and `identifier`, must hash to the latest `hash` * that `commitVote()` was called with. Only the committer can reveal their vote. * @param identifier voted on in the commit phase. EG BTC/USD price pair. * @param time specifies the unix timestamp of the price is being voted on. * @param price voted on during the commit phase. * @param salt value used to hide the commitment price during the commit phase. */ function revealVote( bytes32 identifier, uint256 time, int256 price, int256 salt ) public virtual; /** * @notice Reveal multiple votes in a single transaction. * Look at `project-root/common/Constants.js` for the tested maximum number of reveals. * that can fit in one transaction. * @dev For more information on reveals, review the comment for `revealVote`. * @param reveals array of the Reveal struct which contains an identifier, time, price and salt. */ function batchReveal(Reveal[] memory reveals) public virtual; /** * @notice Gets the queries that are being voted on this round. * @return pendingRequests `PendingRequest` array containing identifiers * and timestamps for all pending requests. */ function getPendingRequests() external view virtual returns (VotingAncillaryInterface.PendingRequestAncillary[] memory); /** * @notice Returns the current voting phase, as a function of the current time. * @return Phase to indicate the current phase. Either { Commit, Reveal, NUM_PHASES_PLACEHOLDER }. */ function getVotePhase() external view virtual returns (VotingAncillaryInterface.Phase); /** * @notice Returns the current round ID, as a function of the current time. * @return uint256 representing the unique round ID. */ function getCurrentRoundId() external view virtual returns (uint256); /** * @notice Retrieves rewards owed for a set of resolved price requests. * @dev Can only retrieve rewards if calling for a valid round and if the * call is done within the timeout threshold (not expired). * @param voterAddress voter for which rewards will be retrieved. Does not have to be the caller. * @param roundId the round from which voting rewards will be retrieved from. * @param toRetrieve array of PendingRequests which rewards are retrieved from. * @return total amount of rewards returned to the voter. */ function retrieveRewards( address voterAddress, uint256 roundId, PendingRequest[] memory toRetrieve ) public virtual returns (FixedPoint.Unsigned memory); // Voting Owner functions. /** * @notice Disables this Voting contract in favor of the migrated one. * @dev Can only be called by the contract owner. * @param newVotingAddress the newly migrated contract address. */ function setMigrated(address newVotingAddress) external virtual; /** * @notice Resets the inflation rate. Note: this change only applies to rounds that have not yet begun. * @dev This method is public because calldata structs are not currently supported by solidity. * @param newInflationRate sets the next round's inflation rate. */ function setInflationRate(FixedPoint.Unsigned memory newInflationRate) public virtual; /** * @notice Resets the Gat percentage. Note: this change only applies to rounds that have not yet begun. * @dev This method is public because calldata structs are not currently supported by solidity. * @param newGatPercentage sets the next round's Gat percentage. */ function setGatPercentage(FixedPoint.Unsigned memory newGatPercentage) public virtual; /** * @notice Resets the rewards expiration timeout. * @dev This change only applies to rounds that have not yet begun. * @param NewRewardsExpirationTimeout how long a caller can wait before choosing to withdraw their rewards. */ function setRewardsExpirationTimeout(uint256 NewRewardsExpirationTimeout) public virtual; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/FixedPoint.sol"; /** * @title Interface that voters must use to Vote on price request resolutions. */ abstract contract VotingAncillaryInterface { struct PendingRequestAncillary { bytes32 identifier; uint256 time; bytes ancillaryData; } // Captures the necessary data for making a commitment. // Used as a parameter when making batch commitments. // Not used as a data structure for storage. struct CommitmentAncillary { bytes32 identifier; uint256 time; bytes ancillaryData; bytes32 hash; bytes encryptedVote; } // Captures the necessary data for revealing a vote. // Used as a parameter when making batch reveals. // Not used as a data structure for storage. struct RevealAncillary { bytes32 identifier; uint256 time; int256 price; bytes ancillaryData; int256 salt; } // Note: the phases must be in order. Meaning the first enum value must be the first phase, etc. // `NUM_PHASES_PLACEHOLDER` is to get the number of phases. It isn't an actual phase, and it should always be last. enum Phase { Commit, Reveal, NUM_PHASES_PLACEHOLDER } /** * @notice Commit a vote for a price request for `identifier` at `time`. * @dev `identifier`, `time` must correspond to a price request that's currently in the commit phase. * Commits can be changed. * @dev Since transaction data is public, the salt will be revealed with the vote. While this is the system’s expected behavior, * voters should never reuse salts. If someone else is able to guess the voted price and knows that a salt will be reused, then * they can determine the vote pre-reveal. * @param identifier uniquely identifies the committed vote. EG BTC/USD price pair. * @param time unix timestamp of the price being voted on. * @param hash keccak256 hash of the `price`, `salt`, voter `address`, `time`, current `roundId`, and `identifier`. */ function commitVote( bytes32 identifier, uint256 time, bytes memory ancillaryData, bytes32 hash ) public virtual; /** * @notice Submit a batch of commits in a single transaction. * @dev Using `encryptedVote` is optional. If included then commitment is stored on chain. * Look at `project-root/common/Constants.js` for the tested maximum number of * commitments that can fit in one transaction. * @param commits array of structs that encapsulate an `identifier`, `time`, `hash` and optional `encryptedVote`. */ function batchCommit(CommitmentAncillary[] memory commits) public virtual; /** * @notice commits a vote and logs an event with a data blob, typically an encrypted version of the vote * @dev An encrypted version of the vote is emitted in an event `EncryptedVote` to allow off-chain infrastructure to * retrieve the commit. The contents of `encryptedVote` are never used on chain: it is purely for convenience. * @param identifier unique price pair identifier. Eg: BTC/USD price pair. * @param time unix timestamp of for the price request. * @param hash keccak256 hash of the price you want to vote for and a `int256 salt`. * @param encryptedVote offchain encrypted blob containing the voters amount, time and salt. */ function commitAndEmitEncryptedVote( bytes32 identifier, uint256 time, bytes memory ancillaryData, bytes32 hash, bytes memory encryptedVote ) public virtual; /** * @notice snapshot the current round's token balances and lock in the inflation rate and GAT. * @dev This function can be called multiple times but each round will only every have one snapshot at the * time of calling `_freezeRoundVariables`. * @param signature signature required to prove caller is an EOA to prevent flash loans from being included in the * snapshot. */ function snapshotCurrentRound(bytes calldata signature) external virtual; /** * @notice Reveal a previously committed vote for `identifier` at `time`. * @dev The revealed `price`, `salt`, `address`, `time`, `roundId`, and `identifier`, must hash to the latest `hash` * that `commitVote()` was called with. Only the committer can reveal their vote. * @param identifier voted on in the commit phase. EG BTC/USD price pair. * @param time specifies the unix timestamp of the price is being voted on. * @param price voted on during the commit phase. * @param salt value used to hide the commitment price during the commit phase. */ function revealVote( bytes32 identifier, uint256 time, int256 price, bytes memory ancillaryData, int256 salt ) public virtual; /** * @notice Reveal multiple votes in a single transaction. * Look at `project-root/common/Constants.js` for the tested maximum number of reveals. * that can fit in one transaction. * @dev For more information on reveals, review the comment for `revealVote`. * @param reveals array of the Reveal struct which contains an identifier, time, price and salt. */ function batchReveal(RevealAncillary[] memory reveals) public virtual; /** * @notice Gets the queries that are being voted on this round. * @return pendingRequests `PendingRequest` array containing identifiers * and timestamps for all pending requests. */ function getPendingRequests() external view virtual returns (PendingRequestAncillary[] memory); /** * @notice Returns the current voting phase, as a function of the current time. * @return Phase to indicate the current phase. Either { Commit, Reveal, NUM_PHASES_PLACEHOLDER }. */ function getVotePhase() external view virtual returns (Phase); /** * @notice Returns the current round ID, as a function of the current time. * @return uint256 representing the unique round ID. */ function getCurrentRoundId() external view virtual returns (uint256); /** * @notice Retrieves rewards owed for a set of resolved price requests. * @dev Can only retrieve rewards if calling for a valid round and if the * call is done within the timeout threshold (not expired). * @param voterAddress voter for which rewards will be retrieved. Does not have to be the caller. * @param roundId the round from which voting rewards will be retrieved from. * @param toRetrieve array of PendingRequests which rewards are retrieved from. * @return total amount of rewards returned to the voter. */ function retrieveRewards( address voterAddress, uint256 roundId, PendingRequestAncillary[] memory toRetrieve ) public virtual returns (FixedPoint.Unsigned memory); // Voting Owner functions. /** * @notice Disables this Voting contract in favor of the migrated one. * @dev Can only be called by the contract owner. * @param newVotingAddress the newly migrated contract address. */ function setMigrated(address newVotingAddress) external virtual; /** * @notice Resets the inflation rate. Note: this change only applies to rounds that have not yet begun. * @dev This method is public because calldata structs are not currently supported by solidity. * @param newInflationRate sets the next round's inflation rate. */ function setInflationRate(FixedPoint.Unsigned memory newInflationRate) public virtual; /** * @notice Resets the Gat percentage. Note: this change only applies to rounds that have not yet begun. * @dev This method is public because calldata structs are not currently supported by solidity. * @param newGatPercentage sets the next round's Gat percentage. */ function setGatPercentage(FixedPoint.Unsigned memory newGatPercentage) public virtual; /** * @notice Resets the rewards expiration timeout. * @dev This change only applies to rounds that have not yet begun. * @param NewRewardsExpirationTimeout how long a caller can wait before choosing to withdraw their rewards. */ function setRewardsExpirationTimeout(uint256 NewRewardsExpirationTimeout) public virtual; }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/FixedPoint.sol"; import "../../common/implementation/Testable.sol"; import "../interfaces/FinderInterface.sol"; import "../interfaces/OracleInterface.sol"; import "../interfaces/OracleAncillaryInterface.sol"; import "../interfaces/VotingInterface.sol"; import "../interfaces/VotingAncillaryInterface.sol"; import "../interfaces/IdentifierWhitelistInterface.sol"; import "./Registry.sol"; import "./ResultComputation.sol"; import "./VoteTiming.sol"; import "./VotingToken.sol"; import "./Constants.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/cryptography/ECDSA.sol"; /** * @title Voting system for Oracle. * @dev Handles receiving and resolving price requests via a commit-reveal voting scheme. */ contract Voting is Testable, Ownable, OracleInterface, OracleAncillaryInterface, // Interface to support ancillary data with price requests. VotingInterface, VotingAncillaryInterface // Interface to support ancillary data with voting rounds. { using FixedPoint for FixedPoint.Unsigned; using SafeMath for uint256; using VoteTiming for VoteTiming.Data; using ResultComputation for ResultComputation.Data; /**************************************** * VOTING DATA STRUCTURES * ****************************************/ // Identifies a unique price request for which the Oracle will always return the same value. // Tracks ongoing votes as well as the result of the vote. struct PriceRequest { bytes32 identifier; uint256 time; // A map containing all votes for this price in various rounds. mapping(uint256 => VoteInstance) voteInstances; // If in the past, this was the voting round where this price was resolved. If current or the upcoming round, // this is the voting round where this price will be voted on, but not necessarily resolved. uint256 lastVotingRound; // The index in the `pendingPriceRequests` that references this PriceRequest. A value of UINT_MAX means that // this PriceRequest is resolved and has been cleaned up from `pendingPriceRequests`. uint256 index; bytes ancillaryData; } struct VoteInstance { // Maps (voterAddress) to their submission. mapping(address => VoteSubmission) voteSubmissions; // The data structure containing the computed voting results. ResultComputation.Data resultComputation; } struct VoteSubmission { // A bytes32 of `0` indicates no commit or a commit that was already revealed. bytes32 commit; // The hash of the value that was revealed. // Note: this is only used for computation of rewards. bytes32 revealHash; } struct Round { uint256 snapshotId; // Voting token snapshot ID for this round. 0 if no snapshot has been taken. FixedPoint.Unsigned inflationRate; // Inflation rate set for this round. FixedPoint.Unsigned gatPercentage; // Gat rate set for this round. uint256 rewardsExpirationTime; // Time that rewards for this round can be claimed until. } // Represents the status a price request has. enum RequestStatus { NotRequested, // Was never requested. Active, // Is being voted on in the current round. Resolved, // Was resolved in a previous round. Future // Is scheduled to be voted on in a future round. } // Only used as a return value in view methods -- never stored in the contract. struct RequestState { RequestStatus status; uint256 lastVotingRound; } /**************************************** * INTERNAL TRACKING * ****************************************/ // Maps round numbers to the rounds. mapping(uint256 => Round) public rounds; // Maps price request IDs to the PriceRequest struct. mapping(bytes32 => PriceRequest) private priceRequests; // Price request ids for price requests that haven't yet been marked as resolved. // These requests may be for future rounds. bytes32[] internal pendingPriceRequests; VoteTiming.Data public voteTiming; // Percentage of the total token supply that must be used in a vote to // create a valid price resolution. 1 == 100%. FixedPoint.Unsigned public gatPercentage; // Global setting for the rate of inflation per vote. This is the percentage of the snapshotted total supply that // should be split among the correct voters. // Note: this value is used to set per-round inflation at the beginning of each round. 1 = 100%. FixedPoint.Unsigned public inflationRate; // Time in seconds from the end of the round in which a price request is // resolved that voters can still claim their rewards. uint256 public rewardsExpirationTimeout; // Reference to the voting token. VotingToken public votingToken; // Reference to the Finder. FinderInterface private finder; // If non-zero, this contract has been migrated to this address. All voters and // financial contracts should query the new address only. address public migratedAddress; // Max value of an unsigned integer. uint256 private constant UINT_MAX = ~uint256(0); // Max length in bytes of ancillary data that can be appended to a price request. // As of December 2020, the current Ethereum gas limit is 12.5 million. This requestPrice function's gas primarily // comes from computing a Keccak-256 hash in _encodePriceRequest and writing a new PriceRequest to // storage. We have empirically determined an ancillary data limit of 8192 bytes that keeps this function // well within the gas limit at ~8 million gas. To learn more about the gas limit and EVM opcode costs go here: // - https://etherscan.io/chart/gaslimit // - https://github.com/djrtwo/evm-opcode-gas-costs uint256 public constant ancillaryBytesLimit = 8192; bytes32 public snapshotMessageHash = ECDSA.toEthSignedMessageHash(keccak256(bytes("Sign For Snapshot"))); /*************************************** * EVENTS * ****************************************/ event VoteCommitted( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, bytes ancillaryData ); event EncryptedVote( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, bytes ancillaryData, bytes encryptedVote ); event VoteRevealed( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, int256 price, bytes ancillaryData, uint256 numTokens ); event RewardsRetrieved( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, bytes ancillaryData, uint256 numTokens ); event PriceRequestAdded(uint256 indexed roundId, bytes32 indexed identifier, uint256 time); event PriceResolved( uint256 indexed roundId, bytes32 indexed identifier, uint256 time, int256 price, bytes ancillaryData ); /** * @notice Construct the Voting contract. * @param _phaseLength length of the commit and reveal phases in seconds. * @param _gatPercentage of the total token supply that must be used in a vote to create a valid price resolution. * @param _inflationRate percentage inflation per round used to increase token supply of correct voters. * @param _rewardsExpirationTimeout timeout, in seconds, within which rewards must be claimed. * @param _votingToken address of the UMA token contract used to commit votes. * @param _finder keeps track of all contracts within the system based on their interfaceName. * @param _timerAddress Contract that stores the current time in a testing environment. * Must be set to 0x0 for production environments that use live time. */ constructor( uint256 _phaseLength, FixedPoint.Unsigned memory _gatPercentage, FixedPoint.Unsigned memory _inflationRate, uint256 _rewardsExpirationTimeout, address _votingToken, address _finder, address _timerAddress ) public Testable(_timerAddress) { voteTiming.init(_phaseLength); require(_gatPercentage.isLessThanOrEqual(1), "GAT percentage must be <= 100%"); gatPercentage = _gatPercentage; inflationRate = _inflationRate; votingToken = VotingToken(_votingToken); finder = FinderInterface(_finder); rewardsExpirationTimeout = _rewardsExpirationTimeout; } /*************************************** MODIFIERS ****************************************/ modifier onlyRegisteredContract() { if (migratedAddress != address(0)) { require(msg.sender == migratedAddress, "Caller must be migrated address"); } else { Registry registry = Registry(finder.getImplementationAddress(OracleInterfaces.Registry)); require(registry.isContractRegistered(msg.sender), "Called must be registered"); } _; } modifier onlyIfNotMigrated() { require(migratedAddress == address(0), "Only call this if not migrated"); _; } /**************************************** * PRICE REQUEST AND ACCESS FUNCTIONS * ****************************************/ /** * @notice Enqueues a request (if a request isn't already present) for the given `identifier`, `time` pair. * @dev Time must be in the past and the identifier must be supported. The length of the ancillary data * is limited such that this method abides by the EVM transaction gas limit. * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. * @param time unix timestamp for the price request. * @param ancillaryData arbitrary data appended to a price request to give the voters more info from the caller. */ function requestPrice( bytes32 identifier, uint256 time, bytes memory ancillaryData ) public override onlyRegisteredContract() { uint256 blockTime = getCurrentTime(); require(time <= blockTime, "Can only request in past"); require(_getIdentifierWhitelist().isIdentifierSupported(identifier), "Unsupported identifier request"); require(ancillaryData.length <= ancillaryBytesLimit, "Invalid ancillary data"); bytes32 priceRequestId = _encodePriceRequest(identifier, time, ancillaryData); PriceRequest storage priceRequest = priceRequests[priceRequestId]; uint256 currentRoundId = voteTiming.computeCurrentRoundId(blockTime); RequestStatus requestStatus = _getRequestStatus(priceRequest, currentRoundId); if (requestStatus == RequestStatus.NotRequested) { // Price has never been requested. // Price requests always go in the next round, so add 1 to the computed current round. uint256 nextRoundId = currentRoundId.add(1); priceRequests[priceRequestId] = PriceRequest({ identifier: identifier, time: time, lastVotingRound: nextRoundId, index: pendingPriceRequests.length, ancillaryData: ancillaryData }); pendingPriceRequests.push(priceRequestId); emit PriceRequestAdded(nextRoundId, identifier, time); } } // Overloaded method to enable short term backwards compatibility. Will be deprecated in the next DVM version. function requestPrice(bytes32 identifier, uint256 time) public override { requestPrice(identifier, time, ""); } /** * @notice Whether the price for `identifier` and `time` is available. * @dev Time must be in the past and the identifier must be supported. * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. * @param time unix timestamp of for the price request. * @param ancillaryData arbitrary data appended to a price request to give the voters more info from the caller. * @return _hasPrice bool if the DVM has resolved to a price for the given identifier and timestamp. */ function hasPrice( bytes32 identifier, uint256 time, bytes memory ancillaryData ) public view override onlyRegisteredContract() returns (bool) { (bool _hasPrice, , ) = _getPriceOrError(identifier, time, ancillaryData); return _hasPrice; } // Overloaded method to enable short term backwards compatibility. Will be deprecated in the next DVM version. function hasPrice(bytes32 identifier, uint256 time) public view override returns (bool) { return hasPrice(identifier, time, ""); } /** * @notice Gets the price for `identifier` and `time` if it has already been requested and resolved. * @dev If the price is not available, the method reverts. * @param identifier uniquely identifies the price requested. eg BTC/USD (encoded as bytes32) could be requested. * @param time unix timestamp of for the price request. * @param ancillaryData arbitrary data appended to a price request to give the voters more info from the caller. * @return int256 representing the resolved price for the given identifier and timestamp. */ function getPrice( bytes32 identifier, uint256 time, bytes memory ancillaryData ) public view override onlyRegisteredContract() returns (int256) { (bool _hasPrice, int256 price, string memory message) = _getPriceOrError(identifier, time, ancillaryData); // If the price wasn't available, revert with the provided message. require(_hasPrice, message); return price; } // Overloaded method to enable short term backwards compatibility. Will be deprecated in the next DVM version. function getPrice(bytes32 identifier, uint256 time) public view override returns (int256) { return getPrice(identifier, time, ""); } /** * @notice Gets the status of a list of price requests, identified by their identifier and time. * @dev If the status for a particular request is NotRequested, the lastVotingRound will always be 0. * @param requests array of type PendingRequest which includes an identifier and timestamp for each request. * @return requestStates a list, in the same order as the input list, giving the status of each of the specified price requests. */ function getPriceRequestStatuses(PendingRequestAncillary[] memory requests) public view returns (RequestState[] memory) { RequestState[] memory requestStates = new RequestState[](requests.length); uint256 currentRoundId = voteTiming.computeCurrentRoundId(getCurrentTime()); for (uint256 i = 0; i < requests.length; i++) { PriceRequest storage priceRequest = _getPriceRequest(requests[i].identifier, requests[i].time, requests[i].ancillaryData); RequestStatus status = _getRequestStatus(priceRequest, currentRoundId); // If it's an active request, its true lastVotingRound is the current one, even if it hasn't been updated. if (status == RequestStatus.Active) { requestStates[i].lastVotingRound = currentRoundId; } else { requestStates[i].lastVotingRound = priceRequest.lastVotingRound; } requestStates[i].status = status; } return requestStates; } // Overloaded method to enable short term backwards compatibility. Will be deprecated in the next DVM version. function getPriceRequestStatuses(PendingRequest[] memory requests) public view returns (RequestState[] memory) { PendingRequestAncillary[] memory requestsAncillary = new PendingRequestAncillary[](requests.length); for (uint256 i = 0; i < requests.length; i++) { requestsAncillary[i].identifier = requests[i].identifier; requestsAncillary[i].time = requests[i].time; requestsAncillary[i].ancillaryData = ""; } return getPriceRequestStatuses(requestsAncillary); } /**************************************** * VOTING FUNCTIONS * ****************************************/ /** * @notice Commit a vote for a price request for `identifier` at `time`. * @dev `identifier`, `time` must correspond to a price request that's currently in the commit phase. * Commits can be changed. * @dev Since transaction data is public, the salt will be revealed with the vote. While this is the system’s expected behavior, * voters should never reuse salts. If someone else is able to guess the voted price and knows that a salt will be reused, then * they can determine the vote pre-reveal. * @param identifier uniquely identifies the committed vote. EG BTC/USD price pair. * @param time unix timestamp of the price being voted on. * @param ancillaryData arbitrary data appended to a price request to give the voters more info from the caller. * @param hash keccak256 hash of the `price`, `salt`, voter `address`, `time`, current `roundId`, and `identifier`. */ function commitVote( bytes32 identifier, uint256 time, bytes memory ancillaryData, bytes32 hash ) public override onlyIfNotMigrated() { require(hash != bytes32(0), "Invalid provided hash"); // Current time is required for all vote timing queries. uint256 blockTime = getCurrentTime(); require( voteTiming.computeCurrentPhase(blockTime) == VotingAncillaryInterface.Phase.Commit, "Cannot commit in reveal phase" ); // At this point, the computed and last updated round ID should be equal. uint256 currentRoundId = voteTiming.computeCurrentRoundId(blockTime); PriceRequest storage priceRequest = _getPriceRequest(identifier, time, ancillaryData); require( _getRequestStatus(priceRequest, currentRoundId) == RequestStatus.Active, "Cannot commit inactive request" ); priceRequest.lastVotingRound = currentRoundId; VoteInstance storage voteInstance = priceRequest.voteInstances[currentRoundId]; voteInstance.voteSubmissions[msg.sender].commit = hash; emit VoteCommitted(msg.sender, currentRoundId, identifier, time, ancillaryData); } // Overloaded method to enable short term backwards compatibility. Will be deprecated in the next DVM version. function commitVote( bytes32 identifier, uint256 time, bytes32 hash ) public override onlyIfNotMigrated() { commitVote(identifier, time, "", hash); } /** * @notice Snapshot the current round's token balances and lock in the inflation rate and GAT. * @dev This function can be called multiple times, but only the first call per round into this function or `revealVote` * will create the round snapshot. Any later calls will be a no-op. Will revert unless called during reveal period. * @param signature signature required to prove caller is an EOA to prevent flash loans from being included in the * snapshot. */ function snapshotCurrentRound(bytes calldata signature) external override(VotingInterface, VotingAncillaryInterface) onlyIfNotMigrated() { uint256 blockTime = getCurrentTime(); require(voteTiming.computeCurrentPhase(blockTime) == Phase.Reveal, "Only snapshot in reveal phase"); // Require public snapshot require signature to ensure caller is an EOA. require(ECDSA.recover(snapshotMessageHash, signature) == msg.sender, "Signature must match sender"); uint256 roundId = voteTiming.computeCurrentRoundId(blockTime); _freezeRoundVariables(roundId); } /** * @notice Reveal a previously committed vote for `identifier` at `time`. * @dev The revealed `price`, `salt`, `address`, `time`, `roundId`, and `identifier`, must hash to the latest `hash` * that `commitVote()` was called with. Only the committer can reveal their vote. * @param identifier voted on in the commit phase. EG BTC/USD price pair. * @param time specifies the unix timestamp of the price being voted on. * @param price voted on during the commit phase. * @param ancillaryData arbitrary data appended to a price request to give the voters more info from the caller. * @param salt value used to hide the commitment price during the commit phase. */ function revealVote( bytes32 identifier, uint256 time, int256 price, bytes memory ancillaryData, int256 salt ) public override onlyIfNotMigrated() { require(voteTiming.computeCurrentPhase(getCurrentTime()) == Phase.Reveal, "Cannot reveal in commit phase"); // Note: computing the current round is required to disallow people from revealing an old commit after the round is over. uint256 roundId = voteTiming.computeCurrentRoundId(getCurrentTime()); PriceRequest storage priceRequest = _getPriceRequest(identifier, time, ancillaryData); VoteInstance storage voteInstance = priceRequest.voteInstances[roundId]; VoteSubmission storage voteSubmission = voteInstance.voteSubmissions[msg.sender]; // Scoping to get rid of a stack too deep error. { // 0 hashes are disallowed in the commit phase, so they indicate a different error. // Cannot reveal an uncommitted or previously revealed hash require(voteSubmission.commit != bytes32(0), "Invalid hash reveal"); require( keccak256(abi.encodePacked(price, salt, msg.sender, time, ancillaryData, roundId, identifier)) == voteSubmission.commit, "Revealed data != commit hash" ); // To protect against flash loans, we require snapshot be validated as EOA. require(rounds[roundId].snapshotId != 0, "Round has no snapshot"); } // Get the frozen snapshotId uint256 snapshotId = rounds[roundId].snapshotId; delete voteSubmission.commit; // Get the voter's snapshotted balance. Since balances are returned pre-scaled by 10**18, we can directly // initialize the Unsigned value with the returned uint. FixedPoint.Unsigned memory balance = FixedPoint.Unsigned(votingToken.balanceOfAt(msg.sender, snapshotId)); // Set the voter's submission. voteSubmission.revealHash = keccak256(abi.encode(price)); // Add vote to the results. voteInstance.resultComputation.addVote(price, balance); emit VoteRevealed(msg.sender, roundId, identifier, time, price, ancillaryData, balance.rawValue); } // Overloaded method to enable short term backwards compatibility. Will be deprecated in the next DVM version. function revealVote( bytes32 identifier, uint256 time, int256 price, int256 salt ) public override { revealVote(identifier, time, price, "", salt); } /** * @notice commits a vote and logs an event with a data blob, typically an encrypted version of the vote * @dev An encrypted version of the vote is emitted in an event `EncryptedVote` to allow off-chain infrastructure to * retrieve the commit. The contents of `encryptedVote` are never used on chain: it is purely for convenience. * @param identifier unique price pair identifier. Eg: BTC/USD price pair. * @param time unix timestamp of for the price request. * @param ancillaryData arbitrary data appended to a price request to give the voters more info from the caller. * @param hash keccak256 hash of the price you want to vote for and a `int256 salt`. * @param encryptedVote offchain encrypted blob containing the voters amount, time and salt. */ function commitAndEmitEncryptedVote( bytes32 identifier, uint256 time, bytes memory ancillaryData, bytes32 hash, bytes memory encryptedVote ) public override { commitVote(identifier, time, ancillaryData, hash); uint256 roundId = voteTiming.computeCurrentRoundId(getCurrentTime()); emit EncryptedVote(msg.sender, roundId, identifier, time, ancillaryData, encryptedVote); } // Overloaded method to enable short term backwards compatibility. Will be deprecated in the next DVM version. function commitAndEmitEncryptedVote( bytes32 identifier, uint256 time, bytes32 hash, bytes memory encryptedVote ) public override { commitVote(identifier, time, "", hash); commitAndEmitEncryptedVote(identifier, time, "", hash, encryptedVote); } /** * @notice Submit a batch of commits in a single transaction. * @dev Using `encryptedVote` is optional. If included then commitment is emitted in an event. * Look at `project-root/common/Constants.js` for the tested maximum number of * commitments that can fit in one transaction. * @param commits struct to encapsulate an `identifier`, `time`, `hash` and optional `encryptedVote`. */ function batchCommit(CommitmentAncillary[] memory commits) public override { for (uint256 i = 0; i < commits.length; i++) { if (commits[i].encryptedVote.length == 0) { commitVote(commits[i].identifier, commits[i].time, commits[i].ancillaryData, commits[i].hash); } else { commitAndEmitEncryptedVote( commits[i].identifier, commits[i].time, commits[i].ancillaryData, commits[i].hash, commits[i].encryptedVote ); } } } // Overloaded method to enable short term backwards compatibility. Will be deprecated in the next DVM version. function batchCommit(Commitment[] memory commits) public override { CommitmentAncillary[] memory commitsAncillary = new CommitmentAncillary[](commits.length); for (uint256 i = 0; i < commits.length; i++) { commitsAncillary[i].identifier = commits[i].identifier; commitsAncillary[i].time = commits[i].time; commitsAncillary[i].ancillaryData = ""; commitsAncillary[i].hash = commits[i].hash; commitsAncillary[i].encryptedVote = commits[i].encryptedVote; } batchCommit(commitsAncillary); } /** * @notice Reveal multiple votes in a single transaction. * Look at `project-root/common/Constants.js` for the tested maximum number of reveals. * that can fit in one transaction. * @dev For more info on reveals, review the comment for `revealVote`. * @param reveals array of the Reveal struct which contains an identifier, time, price and salt. */ function batchReveal(RevealAncillary[] memory reveals) public override { for (uint256 i = 0; i < reveals.length; i++) { revealVote( reveals[i].identifier, reveals[i].time, reveals[i].price, reveals[i].ancillaryData, reveals[i].salt ); } } // Overloaded method to enable short term backwards compatibility. Will be deprecated in the next DVM version. function batchReveal(Reveal[] memory reveals) public override { RevealAncillary[] memory revealsAncillary = new RevealAncillary[](reveals.length); for (uint256 i = 0; i < reveals.length; i++) { revealsAncillary[i].identifier = reveals[i].identifier; revealsAncillary[i].time = reveals[i].time; revealsAncillary[i].price = reveals[i].price; revealsAncillary[i].ancillaryData = ""; revealsAncillary[i].salt = reveals[i].salt; } batchReveal(revealsAncillary); } /** * @notice Retrieves rewards owed for a set of resolved price requests. * @dev Can only retrieve rewards if calling for a valid round and if the call is done within the timeout threshold * (not expired). Note that a named return value is used here to avoid a stack to deep error. * @param voterAddress voter for which rewards will be retrieved. Does not have to be the caller. * @param roundId the round from which voting rewards will be retrieved from. * @param toRetrieve array of PendingRequests which rewards are retrieved from. * @return totalRewardToIssue total amount of rewards returned to the voter. */ function retrieveRewards( address voterAddress, uint256 roundId, PendingRequestAncillary[] memory toRetrieve ) public override returns (FixedPoint.Unsigned memory totalRewardToIssue) { if (migratedAddress != address(0)) { require(msg.sender == migratedAddress, "Can only call from migrated"); } require(roundId < voteTiming.computeCurrentRoundId(getCurrentTime()), "Invalid roundId"); Round storage round = rounds[roundId]; bool isExpired = getCurrentTime() > round.rewardsExpirationTime; FixedPoint.Unsigned memory snapshotBalance = FixedPoint.Unsigned(votingToken.balanceOfAt(voterAddress, round.snapshotId)); // Compute the total amount of reward that will be issued for each of the votes in the round. FixedPoint.Unsigned memory snapshotTotalSupply = FixedPoint.Unsigned(votingToken.totalSupplyAt(round.snapshotId)); FixedPoint.Unsigned memory totalRewardPerVote = round.inflationRate.mul(snapshotTotalSupply); // Keep track of the voter's accumulated token reward. totalRewardToIssue = FixedPoint.Unsigned(0); for (uint256 i = 0; i < toRetrieve.length; i++) { PriceRequest storage priceRequest = _getPriceRequest(toRetrieve[i].identifier, toRetrieve[i].time, toRetrieve[i].ancillaryData); VoteInstance storage voteInstance = priceRequest.voteInstances[priceRequest.lastVotingRound]; // Only retrieve rewards for votes resolved in same round require(priceRequest.lastVotingRound == roundId, "Retrieve for votes same round"); _resolvePriceRequest(priceRequest, voteInstance); if (voteInstance.voteSubmissions[voterAddress].revealHash == 0) { continue; } else if (isExpired) { // Emit a 0 token retrieval on expired rewards. emit RewardsRetrieved( voterAddress, roundId, toRetrieve[i].identifier, toRetrieve[i].time, toRetrieve[i].ancillaryData, 0 ); } else if ( voteInstance.resultComputation.wasVoteCorrect(voteInstance.voteSubmissions[voterAddress].revealHash) ) { // The price was successfully resolved during the voter's last voting round, the voter revealed // and was correct, so they are eligible for a reward. // Compute the reward and add to the cumulative reward. FixedPoint.Unsigned memory reward = snapshotBalance.mul(totalRewardPerVote).div( voteInstance.resultComputation.getTotalCorrectlyVotedTokens() ); totalRewardToIssue = totalRewardToIssue.add(reward); // Emit reward retrieval for this vote. emit RewardsRetrieved( voterAddress, roundId, toRetrieve[i].identifier, toRetrieve[i].time, toRetrieve[i].ancillaryData, reward.rawValue ); } else { // Emit a 0 token retrieval on incorrect votes. emit RewardsRetrieved( voterAddress, roundId, toRetrieve[i].identifier, toRetrieve[i].time, toRetrieve[i].ancillaryData, 0 ); } // Delete the submission to capture any refund and clean up storage. delete voteInstance.voteSubmissions[voterAddress].revealHash; } // Issue any accumulated rewards. if (totalRewardToIssue.isGreaterThan(0)) { require(votingToken.mint(voterAddress, totalRewardToIssue.rawValue), "Voting token issuance failed"); } } // Overloaded method to enable short term backwards compatibility. Will be deprecated in the next DVM version. function retrieveRewards( address voterAddress, uint256 roundId, PendingRequest[] memory toRetrieve ) public override returns (FixedPoint.Unsigned memory) { PendingRequestAncillary[] memory toRetrieveAncillary = new PendingRequestAncillary[](toRetrieve.length); for (uint256 i = 0; i < toRetrieve.length; i++) { toRetrieveAncillary[i].identifier = toRetrieve[i].identifier; toRetrieveAncillary[i].time = toRetrieve[i].time; toRetrieveAncillary[i].ancillaryData = ""; } return retrieveRewards(voterAddress, roundId, toRetrieveAncillary); } /**************************************** * VOTING GETTER FUNCTIONS * ****************************************/ /** * @notice Gets the queries that are being voted on this round. * @return pendingRequests array containing identifiers of type `PendingRequest`. * and timestamps for all pending requests. */ function getPendingRequests() external view override(VotingInterface, VotingAncillaryInterface) returns (PendingRequestAncillary[] memory) { uint256 blockTime = getCurrentTime(); uint256 currentRoundId = voteTiming.computeCurrentRoundId(blockTime); // Solidity memory arrays aren't resizable (and reading storage is expensive). Hence this hackery to filter // `pendingPriceRequests` only to those requests that have an Active RequestStatus. PendingRequestAncillary[] memory unresolved = new PendingRequestAncillary[](pendingPriceRequests.length); uint256 numUnresolved = 0; for (uint256 i = 0; i < pendingPriceRequests.length; i++) { PriceRequest storage priceRequest = priceRequests[pendingPriceRequests[i]]; if (_getRequestStatus(priceRequest, currentRoundId) == RequestStatus.Active) { unresolved[numUnresolved] = PendingRequestAncillary({ identifier: priceRequest.identifier, time: priceRequest.time, ancillaryData: priceRequest.ancillaryData }); numUnresolved++; } } PendingRequestAncillary[] memory pendingRequests = new PendingRequestAncillary[](numUnresolved); for (uint256 i = 0; i < numUnresolved; i++) { pendingRequests[i] = unresolved[i]; } return pendingRequests; } /** * @notice Returns the current voting phase, as a function of the current time. * @return Phase to indicate the current phase. Either { Commit, Reveal, NUM_PHASES_PLACEHOLDER }. */ function getVotePhase() external view override(VotingInterface, VotingAncillaryInterface) returns (Phase) { return voteTiming.computeCurrentPhase(getCurrentTime()); } /** * @notice Returns the current round ID, as a function of the current time. * @return uint256 representing the unique round ID. */ function getCurrentRoundId() external view override(VotingInterface, VotingAncillaryInterface) returns (uint256) { return voteTiming.computeCurrentRoundId(getCurrentTime()); } /**************************************** * OWNER ADMIN FUNCTIONS * ****************************************/ /** * @notice Disables this Voting contract in favor of the migrated one. * @dev Can only be called by the contract owner. * @param newVotingAddress the newly migrated contract address. */ function setMigrated(address newVotingAddress) external override(VotingInterface, VotingAncillaryInterface) onlyOwner { migratedAddress = newVotingAddress; } /** * @notice Resets the inflation rate. Note: this change only applies to rounds that have not yet begun. * @dev This method is public because calldata structs are not currently supported by solidity. * @param newInflationRate sets the next round's inflation rate. */ function setInflationRate(FixedPoint.Unsigned memory newInflationRate) public override(VotingInterface, VotingAncillaryInterface) onlyOwner { inflationRate = newInflationRate; } /** * @notice Resets the Gat percentage. Note: this change only applies to rounds that have not yet begun. * @dev This method is public because calldata structs are not currently supported by solidity. * @param newGatPercentage sets the next round's Gat percentage. */ function setGatPercentage(FixedPoint.Unsigned memory newGatPercentage) public override(VotingInterface, VotingAncillaryInterface) onlyOwner { require(newGatPercentage.isLessThan(1), "GAT percentage must be < 100%"); gatPercentage = newGatPercentage; } /** * @notice Resets the rewards expiration timeout. * @dev This change only applies to rounds that have not yet begun. * @param NewRewardsExpirationTimeout how long a caller can wait before choosing to withdraw their rewards. */ function setRewardsExpirationTimeout(uint256 NewRewardsExpirationTimeout) public override(VotingInterface, VotingAncillaryInterface) onlyOwner { rewardsExpirationTimeout = NewRewardsExpirationTimeout; } /**************************************** * PRIVATE AND INTERNAL FUNCTIONS * ****************************************/ // Returns the price for a given identifer. Three params are returns: bool if there was an error, int to represent // the resolved price and a string which is filled with an error message, if there was an error or "". function _getPriceOrError( bytes32 identifier, uint256 time, bytes memory ancillaryData ) private view returns ( bool, int256, string memory ) { PriceRequest storage priceRequest = _getPriceRequest(identifier, time, ancillaryData); uint256 currentRoundId = voteTiming.computeCurrentRoundId(getCurrentTime()); RequestStatus requestStatus = _getRequestStatus(priceRequest, currentRoundId); if (requestStatus == RequestStatus.Active) { return (false, 0, "Current voting round not ended"); } else if (requestStatus == RequestStatus.Resolved) { VoteInstance storage voteInstance = priceRequest.voteInstances[priceRequest.lastVotingRound]; (, int256 resolvedPrice) = voteInstance.resultComputation.getResolvedPrice(_computeGat(priceRequest.lastVotingRound)); return (true, resolvedPrice, ""); } else if (requestStatus == RequestStatus.Future) { return (false, 0, "Price is still to be voted on"); } else { return (false, 0, "Price was never requested"); } } function _getPriceRequest( bytes32 identifier, uint256 time, bytes memory ancillaryData ) private view returns (PriceRequest storage) { return priceRequests[_encodePriceRequest(identifier, time, ancillaryData)]; } function _encodePriceRequest( bytes32 identifier, uint256 time, bytes memory ancillaryData ) private pure returns (bytes32) { return keccak256(abi.encode(identifier, time, ancillaryData)); } function _freezeRoundVariables(uint256 roundId) private { Round storage round = rounds[roundId]; // Only on the first reveal should the snapshot be captured for that round. if (round.snapshotId == 0) { // There is no snapshot ID set, so create one. round.snapshotId = votingToken.snapshot(); // Set the round inflation rate to the current global inflation rate. rounds[roundId].inflationRate = inflationRate; // Set the round gat percentage to the current global gat rate. rounds[roundId].gatPercentage = gatPercentage; // Set the rewards expiration time based on end of time of this round and the current global timeout. rounds[roundId].rewardsExpirationTime = voteTiming.computeRoundEndTime(roundId).add( rewardsExpirationTimeout ); } } function _resolvePriceRequest(PriceRequest storage priceRequest, VoteInstance storage voteInstance) private { if (priceRequest.index == UINT_MAX) { return; } (bool isResolved, int256 resolvedPrice) = voteInstance.resultComputation.getResolvedPrice(_computeGat(priceRequest.lastVotingRound)); require(isResolved, "Can't resolve unresolved request"); // Delete the resolved price request from pendingPriceRequests. uint256 lastIndex = pendingPriceRequests.length - 1; PriceRequest storage lastPriceRequest = priceRequests[pendingPriceRequests[lastIndex]]; lastPriceRequest.index = priceRequest.index; pendingPriceRequests[priceRequest.index] = pendingPriceRequests[lastIndex]; pendingPriceRequests.pop(); priceRequest.index = UINT_MAX; emit PriceResolved( priceRequest.lastVotingRound, priceRequest.identifier, priceRequest.time, resolvedPrice, priceRequest.ancillaryData ); } function _computeGat(uint256 roundId) private view returns (FixedPoint.Unsigned memory) { uint256 snapshotId = rounds[roundId].snapshotId; if (snapshotId == 0) { // No snapshot - return max value to err on the side of caution. return FixedPoint.Unsigned(UINT_MAX); } // Grab the snapshotted supply from the voting token. It's already scaled by 10**18, so we can directly // initialize the Unsigned value with the returned uint. FixedPoint.Unsigned memory snapshottedSupply = FixedPoint.Unsigned(votingToken.totalSupplyAt(snapshotId)); // Multiply the total supply at the snapshot by the gatPercentage to get the GAT in number of tokens. return snapshottedSupply.mul(rounds[roundId].gatPercentage); } function _getRequestStatus(PriceRequest storage priceRequest, uint256 currentRoundId) private view returns (RequestStatus) { if (priceRequest.lastVotingRound == 0) { return RequestStatus.NotRequested; } else if (priceRequest.lastVotingRound < currentRoundId) { VoteInstance storage voteInstance = priceRequest.voteInstances[priceRequest.lastVotingRound]; (bool isResolved, ) = voteInstance.resultComputation.getResolvedPrice(_computeGat(priceRequest.lastVotingRound)); return isResolved ? RequestStatus.Resolved : RequestStatus.Active; } else if (priceRequest.lastVotingRound == currentRoundId) { return RequestStatus.Active; } else { // Means than priceRequest.lastVotingRound > currentRoundId return RequestStatus.Future; } } function _getIdentifierWhitelist() private view returns (IdentifierWhitelistInterface supportedIdentifiers) { return IdentifierWhitelistInterface(finder.getImplementationAddress(OracleInterfaces.IdentifierWhitelist)); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/MultiRole.sol"; import "../interfaces/RegistryInterface.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; /** * @title Registry for financial contracts and approved financial contract creators. * @dev Maintains a whitelist of financial contract creators that are allowed * to register new financial contracts and stores party members of a financial contract. */ contract Registry is RegistryInterface, MultiRole { using SafeMath for uint256; /**************************************** * INTERNAL VARIABLES AND STORAGE * ****************************************/ enum Roles { Owner, // The owner manages the set of ContractCreators. ContractCreator // Can register financial contracts. } // This enum is required because a `WasValid` state is required // to ensure that financial contracts cannot be re-registered. enum Validity { Invalid, Valid } // Local information about a contract. struct FinancialContract { Validity valid; uint128 index; } struct Party { address[] contracts; // Each financial contract address is stored in this array. // The address of each financial contract is mapped to its index for constant time look up and deletion. mapping(address => uint256) contractIndex; } // Array of all contracts that are approved to use the UMA Oracle. address[] public registeredContracts; // Map of financial contract contracts to the associated FinancialContract struct. mapping(address => FinancialContract) public contractMap; // Map each party member to their their associated Party struct. mapping(address => Party) private partyMap; /**************************************** * EVENTS * ****************************************/ event NewContractRegistered(address indexed contractAddress, address indexed creator, address[] parties); event PartyAdded(address indexed contractAddress, address indexed party); event PartyRemoved(address indexed contractAddress, address indexed party); /** * @notice Construct the Registry contract. */ constructor() public { _createExclusiveRole(uint256(Roles.Owner), uint256(Roles.Owner), msg.sender); // Start with no contract creators registered. _createSharedRole(uint256(Roles.ContractCreator), uint256(Roles.Owner), new address[](0)); } /**************************************** * REGISTRATION FUNCTIONS * ****************************************/ /** * @notice Registers a new financial contract. * @dev Only authorized contract creators can call this method. * @param parties array of addresses who become parties in the contract. * @param contractAddress address of the contract against which the parties are registered. */ function registerContract(address[] calldata parties, address contractAddress) external override onlyRoleHolder(uint256(Roles.ContractCreator)) { FinancialContract storage financialContract = contractMap[contractAddress]; require(contractMap[contractAddress].valid == Validity.Invalid, "Can only register once"); // Store contract address as a registered contract. registeredContracts.push(contractAddress); // No length check necessary because we should never hit (2^127 - 1) contracts. financialContract.index = uint128(registeredContracts.length.sub(1)); // For all parties in the array add them to the contract's parties. financialContract.valid = Validity.Valid; for (uint256 i = 0; i < parties.length; i = i.add(1)) { _addPartyToContract(parties[i], contractAddress); } emit NewContractRegistered(contractAddress, msg.sender, parties); } /** * @notice Adds a party member to the calling contract. * @dev msg.sender will be used to determine the contract that this party is added to. * @param party new party for the calling contract. */ function addPartyToContract(address party) external override { address contractAddress = msg.sender; require(contractMap[contractAddress].valid == Validity.Valid, "Can only add to valid contract"); _addPartyToContract(party, contractAddress); } /** * @notice Removes a party member from the calling contract. * @dev msg.sender will be used to determine the contract that this party is removed from. * @param partyAddress address to be removed from the calling contract. */ function removePartyFromContract(address partyAddress) external override { address contractAddress = msg.sender; Party storage party = partyMap[partyAddress]; uint256 numberOfContracts = party.contracts.length; require(numberOfContracts != 0, "Party has no contracts"); require(contractMap[contractAddress].valid == Validity.Valid, "Remove only from valid contract"); require(isPartyMemberOfContract(partyAddress, contractAddress), "Can only remove existing party"); // Index of the current location of the contract to remove. uint256 deleteIndex = party.contractIndex[contractAddress]; // Store the last contract's address to update the lookup map. address lastContractAddress = party.contracts[numberOfContracts - 1]; // Swap the contract to be removed with the last contract. party.contracts[deleteIndex] = lastContractAddress; // Update the lookup index with the new location. party.contractIndex[lastContractAddress] = deleteIndex; // Pop the last contract from the array and update the lookup map. party.contracts.pop(); delete party.contractIndex[contractAddress]; emit PartyRemoved(contractAddress, partyAddress); } /**************************************** * REGISTRY STATE GETTERS * ****************************************/ /** * @notice Returns whether the contract has been registered with the registry. * @dev If it is registered, it is an authorized participant in the UMA system. * @param contractAddress address of the financial contract. * @return bool indicates whether the contract is registered. */ function isContractRegistered(address contractAddress) external view override returns (bool) { return contractMap[contractAddress].valid == Validity.Valid; } /** * @notice Returns a list of all contracts that are associated with a particular party. * @param party address of the party. * @return an array of the contracts the party is registered to. */ function getRegisteredContracts(address party) external view override returns (address[] memory) { return partyMap[party].contracts; } /** * @notice Returns all registered contracts. * @return all registered contract addresses within the system. */ function getAllRegisteredContracts() external view override returns (address[] memory) { return registeredContracts; } /** * @notice checks if an address is a party of a contract. * @param party party to check. * @param contractAddress address to check against the party. * @return bool indicating if the address is a party of the contract. */ function isPartyMemberOfContract(address party, address contractAddress) public view override returns (bool) { uint256 index = partyMap[party].contractIndex[contractAddress]; return partyMap[party].contracts.length > index && partyMap[party].contracts[index] == contractAddress; } /**************************************** * INTERNAL FUNCTIONS * ****************************************/ function _addPartyToContract(address party, address contractAddress) internal { require(!isPartyMemberOfContract(party, contractAddress), "Can only register a party once"); uint256 contractIndex = partyMap[party].contracts.length; partyMap[party].contracts.push(contractAddress); partyMap[party].contractIndex[contractAddress] = contractIndex; emit PartyAdded(contractAddress, party); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../../common/implementation/FixedPoint.sol"; /** * @title Computes vote results. * @dev The result is the mode of the added votes. Otherwise, the vote is unresolved. */ library ResultComputation { using FixedPoint for FixedPoint.Unsigned; /**************************************** * INTERNAL LIBRARY DATA STRUCTURE * ****************************************/ struct Data { // Maps price to number of tokens that voted for that price. mapping(int256 => FixedPoint.Unsigned) voteFrequency; // The total votes that have been added. FixedPoint.Unsigned totalVotes; // The price that is the current mode, i.e., the price with the highest frequency in `voteFrequency`. int256 currentMode; } /**************************************** * VOTING FUNCTIONS * ****************************************/ /** * @notice Adds a new vote to be used when computing the result. * @param data contains information to which the vote is applied. * @param votePrice value specified in the vote for the given `numberTokens`. * @param numberTokens number of tokens that voted on the `votePrice`. */ function addVote( Data storage data, int256 votePrice, FixedPoint.Unsigned memory numberTokens ) internal { data.totalVotes = data.totalVotes.add(numberTokens); data.voteFrequency[votePrice] = data.voteFrequency[votePrice].add(numberTokens); if ( votePrice != data.currentMode && data.voteFrequency[votePrice].isGreaterThan(data.voteFrequency[data.currentMode]) ) { data.currentMode = votePrice; } } /**************************************** * VOTING STATE GETTERS * ****************************************/ /** * @notice Returns whether the result is resolved, and if so, what value it resolved to. * @dev `price` should be ignored if `isResolved` is false. * @param data contains information against which the `minVoteThreshold` is applied. * @param minVoteThreshold min (exclusive) number of tokens that must have voted for the result to be valid. Can be * used to enforce a minimum voter participation rate, regardless of how the votes are distributed. * @return isResolved indicates if the price has been resolved correctly. * @return price the price that the dvm resolved to. */ function getResolvedPrice(Data storage data, FixedPoint.Unsigned memory minVoteThreshold) internal view returns (bool isResolved, int256 price) { FixedPoint.Unsigned memory modeThreshold = FixedPoint.fromUnscaledUint(50).div(100); if ( data.totalVotes.isGreaterThan(minVoteThreshold) && data.voteFrequency[data.currentMode].div(data.totalVotes).isGreaterThan(modeThreshold) ) { // `modeThreshold` and `minVoteThreshold` are exceeded, so the current mode is the resolved price. isResolved = true; price = data.currentMode; } else { isResolved = false; } } /** * @notice Checks whether a `voteHash` is considered correct. * @dev Should only be called after a vote is resolved, i.e., via `getResolvedPrice`. * @param data contains information against which the `voteHash` is checked. * @param voteHash committed hash submitted by the voter. * @return bool true if the vote was correct. */ function wasVoteCorrect(Data storage data, bytes32 voteHash) internal view returns (bool) { return voteHash == keccak256(abi.encode(data.currentMode)); } /** * @notice Gets the total number of tokens whose votes are considered correct. * @dev Should only be called after a vote is resolved, i.e., via `getResolvedPrice`. * @param data contains all votes against which the correctly voted tokens are counted. * @return FixedPoint.Unsigned which indicates the frequency of the correctly voted tokens. */ function getTotalCorrectlyVotedTokens(Data storage data) internal view returns (FixedPoint.Unsigned memory) { return data.voteFrequency[data.currentMode]; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../interfaces/VotingInterface.sol"; /** * @title Library to compute rounds and phases for an equal length commit-reveal voting cycle. */ library VoteTiming { using SafeMath for uint256; struct Data { uint256 phaseLength; } /** * @notice Initializes the data object. Sets the phase length based on the input. */ function init(Data storage data, uint256 phaseLength) internal { // This should have a require message but this results in an internal Solidity error. require(phaseLength > 0); data.phaseLength = phaseLength; } /** * @notice Computes the roundID based off the current time as floor(timestamp/roundLength). * @dev The round ID depends on the global timestamp but not on the lifetime of the system. * The consequence is that the initial round ID starts at an arbitrary number (that increments, as expected, for subsequent rounds) instead of zero or one. * @param data input data object. * @param currentTime input unix timestamp used to compute the current roundId. * @return roundId defined as a function of the currentTime and `phaseLength` from `data`. */ function computeCurrentRoundId(Data storage data, uint256 currentTime) internal view returns (uint256) { uint256 roundLength = data.phaseLength.mul(uint256(VotingAncillaryInterface.Phase.NUM_PHASES_PLACEHOLDER)); return currentTime.div(roundLength); } /** * @notice compute the round end time as a function of the round Id. * @param data input data object. * @param roundId uniquely identifies the current round. * @return timestamp unix time of when the current round will end. */ function computeRoundEndTime(Data storage data, uint256 roundId) internal view returns (uint256) { uint256 roundLength = data.phaseLength.mul(uint256(VotingAncillaryInterface.Phase.NUM_PHASES_PLACEHOLDER)); return roundLength.mul(roundId.add(1)); } /** * @notice Computes the current phase based only on the current time. * @param data input data object. * @param currentTime input unix timestamp used to compute the current roundId. * @return current voting phase based on current time and vote phases configuration. */ function computeCurrentPhase(Data storage data, uint256 currentTime) internal view returns (VotingAncillaryInterface.Phase) { // This employs some hacky casting. We could make this an if-statement if we're worried about type safety. return VotingAncillaryInterface.Phase( currentTime.div(data.phaseLength).mod(uint256(VotingAncillaryInterface.Phase.NUM_PHASES_PLACEHOLDER)) ); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../../common/implementation/ExpandedERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20Snapshot.sol"; /** * @title Ownership of this token allows a voter to respond to price requests. * @dev Supports snapshotting and allows the Oracle to mint new tokens as rewards. */ contract VotingToken is ExpandedERC20, ERC20Snapshot { /** * @notice Constructs the VotingToken. */ constructor() public ExpandedERC20("UMA Voting Token v1", "UMA", 18) {} /** * @notice Creates a new snapshot ID. * @return uint256 Thew new snapshot ID. */ function snapshot() external returns (uint256) { return _snapshot(); } // _transfer, _mint and _burn are ERC20 internal methods that are overridden by ERC20Snapshot, // therefore the compiler will complain that VotingToken must override these methods // because the two base classes (ERC20 and ERC20Snapshot) both define the same functions function _transfer( address from, address to, uint256 value ) internal override(ERC20, ERC20Snapshot) { super._transfer(from, to, value); } function _mint(address account, uint256 value) internal override(ERC20, ERC20Snapshot) { super._mint(account, value); } function _burn(address account, uint256 value) internal override(ERC20, ERC20Snapshot) { super._burn(account, value); } }
pragma solidity ^0.6.0; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { // Check the signature length if (signature.length != 65) { revert("ECDSA: invalid signature length"); } // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { revert("ECDSA: invalid signature 's' value"); } if (v != 27 && v != 28) { revert("ECDSA: invalid signature 'v' value"); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); require(signer != address(0), "ECDSA: invalid signature"); return signer; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * replicates the behavior of the * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] * JSON-RPC method. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; /** * @title Interface for a registry of contracts and contract creators. */ interface RegistryInterface { /** * @notice Registers a new contract. * @dev Only authorized contract creators can call this method. * @param parties an array of addresses who become parties in the contract. * @param contractAddress defines the address of the deployed contract. */ function registerContract(address[] calldata parties, address contractAddress) external; /** * @notice Returns whether the contract has been registered with the registry. * @dev If it is registered, it is an authorized participant in the UMA system. * @param contractAddress address of the contract. * @return bool indicates whether the contract is registered. */ function isContractRegistered(address contractAddress) external view returns (bool); /** * @notice Returns a list of all contracts that are associated with a particular party. * @param party address of the party. * @return an array of the contracts the party is registered to. */ function getRegisteredContracts(address party) external view returns (address[] memory); /** * @notice Returns all registered contracts. * @return all registered contract addresses within the system. */ function getAllRegisteredContracts() external view returns (address[] memory); /** * @notice Adds a party to the calling contract. * @dev msg.sender must be the contract to which the party member is added. * @param party address to be added to the contract. */ function addPartyToContract(address party) external; /** * @notice Removes a party member to the calling contract. * @dev msg.sender must be the contract to which the party member is added. * @param party address to be removed from the contract. */ function removePartyFromContract(address party) external; /** * @notice checks if an address is a party in a contract. * @param party party to check. * @param contractAddress address to check against the party. * @return bool indicating if the address is a party of the contract. */ function isPartyMemberOfContract(address party, address contractAddress) external view returns (bool); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./MultiRole.sol"; import "../interfaces/ExpandedIERC20.sol"; /** * @title An ERC20 with permissioned burning and minting. The contract deployer will initially * be the owner who is capable of adding new roles. */ contract ExpandedERC20 is ExpandedIERC20, ERC20, MultiRole { enum Roles { // Can set the minter and burner. Owner, // Addresses that can mint new tokens. Minter, // Addresses that can burn tokens that address owns. Burner } /** * @notice Constructs the ExpandedERC20. * @param _tokenName The name which describes the new token. * @param _tokenSymbol The ticker abbreviation of the name. Ideally < 5 chars. * @param _tokenDecimals The number of decimals to define token precision. */ constructor( string memory _tokenName, string memory _tokenSymbol, uint8 _tokenDecimals ) public ERC20(_tokenName, _tokenSymbol) { _setupDecimals(_tokenDecimals); _createExclusiveRole(uint256(Roles.Owner), uint256(Roles.Owner), msg.sender); _createSharedRole(uint256(Roles.Minter), uint256(Roles.Owner), new address[](0)); _createSharedRole(uint256(Roles.Burner), uint256(Roles.Owner), new address[](0)); } /** * @dev Mints `value` tokens to `recipient`, returning true on success. * @param recipient address to mint to. * @param value amount of tokens to mint. * @return True if the mint succeeded, or False. */ function mint(address recipient, uint256 value) external override onlyRoleHolder(uint256(Roles.Minter)) returns (bool) { _mint(recipient, value); return true; } /** * @dev Burns `value` tokens owned by `msg.sender`. * @param value amount of tokens to burn. */ function burn(uint256 value) external override onlyRoleHolder(uint256(Roles.Burner)) { _burn(msg.sender, value); } /** * @notice Add Minter role to account. * @dev The caller must have the Owner role. * @param account The address to which the Minter role is added. */ function addMinter(address account) external virtual override { addMember(uint256(Roles.Minter), account); } /** * @notice Add Burner role to account. * @dev The caller must have the Owner role. * @param account The address to which the Burner role is added. */ function addBurner(address account) external virtual override { addMember(uint256(Roles.Burner), account); } /** * @notice Reset Owner role to account. * @dev The caller must have the Owner role. * @param account The new holder of the Owner role. */ function resetOwner(address account) external virtual override { resetMember(uint256(Roles.Owner), account); } }
pragma solidity ^0.6.0; import "../../math/SafeMath.sol"; import "../../utils/Arrays.sol"; import "../../utils/Counters.sol"; import "./ERC20.sol"; /** * @dev This contract extends an ERC20 token with a snapshot mechanism. When a snapshot is created, the balances and * total supply at the time are recorded for later access. * * This can be used to safely create mechanisms based on token balances such as trustless dividends or weighted voting. * In naive implementations it's possible to perform a "double spend" attack by reusing the same balance from different * accounts. By using snapshots to calculate dividends or voting power, those attacks no longer apply. It can also be * used to create an efficient ERC20 forking mechanism. * * Snapshots are created by the internal {_snapshot} function, which will emit the {Snapshot} event and return a * snapshot id. To get the total supply at the time of a snapshot, call the function {totalSupplyAt} with the snapshot * id. To get the balance of an account at the time of a snapshot, call the {balanceOfAt} function with the snapshot id * and the account address. * * ==== Gas Costs * * Snapshots are efficient. Snapshot creation is _O(1)_. Retrieval of balances or total supply from a snapshot is _O(log * n)_ in the number of snapshots that have been created, although _n_ for a specific account will generally be much * smaller since identical balances in subsequent snapshots are stored as a single entry. * * There is a constant overhead for normal ERC20 transfers due to the additional snapshot bookkeeping. This overhead is * only significant for the first transfer that immediately follows a snapshot for a particular account. Subsequent * transfers will have normal cost until the next snapshot, and so on. */ abstract contract ERC20Snapshot is ERC20 { // Inspired by Jordi Baylina's MiniMeToken to record historical balances: // https://github.com/Giveth/minimd/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol using SafeMath for uint256; using Arrays for uint256[]; using Counters for Counters.Counter; // Snapshotted values have arrays of ids and the value corresponding to that id. These could be an array of a // Snapshot struct, but that would impede usage of functions that work on an array. struct Snapshots { uint256[] ids; uint256[] values; } mapping (address => Snapshots) private _accountBalanceSnapshots; Snapshots private _totalSupplySnapshots; // Snapshot ids increase monotonically, with the first value being 1. An id of 0 is invalid. Counters.Counter private _currentSnapshotId; /** * @dev Emitted by {_snapshot} when a snapshot identified by `id` is created. */ event Snapshot(uint256 id); /** * @dev Creates a new snapshot and returns its snapshot id. * * Emits a {Snapshot} event that contains the same id. * * {_snapshot} is `internal` and you have to decide how to expose it externally. Its usage may be restricted to a * set of accounts, for example using {AccessControl}, or it may be open to the public. * * [WARNING] * ==== * While an open way of calling {_snapshot} is required for certain trust minimization mechanisms such as forking, * you must consider that it can potentially be used by attackers in two ways. * * First, it can be used to increase the cost of retrieval of values from snapshots, although it will grow * logarithmically thus rendering this attack ineffective in the long term. Second, it can be used to target * specific accounts and increase the cost of ERC20 transfers for them, in the ways specified in the Gas Costs * section above. * * We haven't measured the actual numbers; if this is something you're interested in please reach out to us. * ==== */ function _snapshot() internal virtual returns (uint256) { _currentSnapshotId.increment(); uint256 currentId = _currentSnapshotId.current(); emit Snapshot(currentId); return currentId; } /** * @dev Retrieves the balance of `account` at the time `snapshotId` was created. */ function balanceOfAt(address account, uint256 snapshotId) public view returns (uint256) { (bool snapshotted, uint256 value) = _valueAt(snapshotId, _accountBalanceSnapshots[account]); return snapshotted ? value : balanceOf(account); } /** * @dev Retrieves the total supply at the time `snapshotId` was created. */ function totalSupplyAt(uint256 snapshotId) public view returns(uint256) { (bool snapshotted, uint256 value) = _valueAt(snapshotId, _totalSupplySnapshots); return snapshotted ? value : totalSupply(); } // _transfer, _mint and _burn are the only functions where the balances are modified, so it is there that the // snapshots are updated. Note that the update happens _before_ the balance change, with the pre-modified value. // The same is true for the total supply and _mint and _burn. function _transfer(address from, address to, uint256 value) internal virtual override { _updateAccountSnapshot(from); _updateAccountSnapshot(to); super._transfer(from, to, value); } function _mint(address account, uint256 value) internal virtual override { _updateAccountSnapshot(account); _updateTotalSupplySnapshot(); super._mint(account, value); } function _burn(address account, uint256 value) internal virtual override { _updateAccountSnapshot(account); _updateTotalSupplySnapshot(); super._burn(account, value); } function _valueAt(uint256 snapshotId, Snapshots storage snapshots) private view returns (bool, uint256) { require(snapshotId > 0, "ERC20Snapshot: id is 0"); // solhint-disable-next-line max-line-length require(snapshotId <= _currentSnapshotId.current(), "ERC20Snapshot: nonexistent id"); // When a valid snapshot is queried, there are three possibilities: // a) The queried value was not modified after the snapshot was taken. Therefore, a snapshot entry was never // created for this id, and all stored snapshot ids are smaller than the requested one. The value that corresponds // to this id is the current one. // b) The queried value was modified after the snapshot was taken. Therefore, there will be an entry with the // requested id, and its value is the one to return. // c) More snapshots were created after the requested one, and the queried value was later modified. There will be // no entry for the requested id: the value that corresponds to it is that of the smallest snapshot id that is // larger than the requested one. // // In summary, we need to find an element in an array, returning the index of the smallest value that is larger if // it is not found, unless said value doesn't exist (e.g. when all values are smaller). Arrays.findUpperBound does // exactly this. uint256 index = snapshots.ids.findUpperBound(snapshotId); if (index == snapshots.ids.length) { return (false, 0); } else { return (true, snapshots.values[index]); } } function _updateAccountSnapshot(address account) private { _updateSnapshot(_accountBalanceSnapshots[account], balanceOf(account)); } function _updateTotalSupplySnapshot() private { _updateSnapshot(_totalSupplySnapshots, totalSupply()); } function _updateSnapshot(Snapshots storage snapshots, uint256 currentValue) private { uint256 currentId = _currentSnapshotId.current(); if (_lastSnapshotId(snapshots.ids) < currentId) { snapshots.ids.push(currentId); snapshots.values.push(currentValue); } } function _lastSnapshotId(uint256[] storage ids) private view returns (uint256) { if (ids.length == 0) { return 0; } else { return ids[ids.length - 1]; } } }
pragma solidity ^0.6.0; import "../../GSN/Context.sol"; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20MinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; using Address for address; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name, string memory symbol) public { _name = name; _symbol = symbol; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}; * * Requirements: * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. * * This is internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @title ERC20 interface that includes burn and mint methods. */ abstract contract ExpandedIERC20 is IERC20 { /** * @notice Burns a specific amount of the caller's tokens. * @dev Only burns the caller's tokens, so it is safe to leave this method permissionless. */ function burn(uint256 value) external virtual; /** * @notice Mints tokens and adds them to the balance of the `to` address. * @dev This method should be permissioned to only allow designated parties to mint tokens. */ function mint(address to, uint256 value) external virtual returns (bool); function addMinter(address account) external virtual; function addBurner(address account) external virtual; function resetOwner(address account) external virtual; }
pragma solidity ^0.6.0; import "../math/Math.sol"; /** * @dev Collection of functions related to array types. */ library Arrays { /** * @dev Searches a sorted `array` and returns the first index that contains * a value greater or equal to `element`. If no such index exists (i.e. all * values in the array are strictly less than `element`), the array length is * returned. Time complexity O(log n). * * `array` is expected to be sorted in ascending order, and to contain no * repeated elements. */ function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) { if (array.length == 0) { return 0; } uint256 low = 0; uint256 high = array.length; while (low < high) { uint256 mid = Math.average(low, high); // Note that mid will always be strictly less than high (i.e. it will be a valid array index) // because Math.average rounds down (it does integer division with truncation). if (array[mid] > element) { high = mid; } else { low = mid + 1; } } // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound. if (low > 0 && array[low - 1] == element) { return low - 1; } else { return low; } } }
pragma solidity ^0.6.0; import "../math/SafeMath.sol"; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath} * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never * directly accessed. */ library Counters { using SafeMath for uint256; struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { // The {SafeMath} overflow check can be skipped here, see the comment at the top counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value.sub(1); } }
pragma solidity ^0.6.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../oracle/implementation/Finder.sol"; import "../oracle/implementation/Constants.sol"; import "../oracle/implementation/Voting.sol"; /** * @title A contract that executes a short series of upgrade calls that must be performed atomically as a part of the * upgrade process for Voting.sol. * @dev Note: the complete upgrade process requires more than just the transactions in this contract. These are only * the ones that need to be performed atomically. */ contract VotingUpgrader { // Existing governor is the only one who can initiate the upgrade. address public governor; // Existing Voting contract needs to be informed of the address of the new Voting contract. Voting public existingVoting; // New governor will be the new owner of the finder. // Finder contract to push upgrades to. Finder public finder; // Addresses to upgrade. address public newVoting; // Address to call setMigrated on the old voting contract. address public setMigratedAddress; /** * @notice Removes an address from the whitelist. * @param _governor the Governor contract address. * @param _existingVoting the current/existing Voting contract address. * @param _newVoting the new Voting deployment address. * @param _finder the Finder contract address. * @param _setMigratedAddress the address to set migrated. This address will be able to continue making calls to * old voting contract (used to claim rewards on others' behalf). Note: this address * can always be changed by the voters. */ constructor( address _governor, address _existingVoting, address _newVoting, address _finder, address _setMigratedAddress ) public { governor = _governor; existingVoting = Voting(_existingVoting); newVoting = _newVoting; finder = Finder(_finder); setMigratedAddress = _setMigratedAddress; } /** * @notice Performs the atomic portion of the upgrade process. * @dev This method updates the Voting address in the finder, sets the old voting contract to migrated state, and * returns ownership of the existing Voting contract and Finder back to the Governor. */ function upgrade() external { require(msg.sender == governor, "Upgrade can only be initiated by the existing governor."); // Change the addresses in the Finder. finder.changeImplementationAddress(OracleInterfaces.Oracle, newVoting); // Set the preset "migrated" address to allow this address to claim rewards on voters' behalf. // This also effectively shuts down the existing voting contract so new votes cannot be triggered. existingVoting.setMigrated(setMigratedAddress); // Transfer back ownership of old voting contract and the finder to the governor. existingVoting.transferOwnership(governor); finder.transferOwnership(governor); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "@openzeppelin/contracts/access/Ownable.sol"; import "../interfaces/FinderInterface.sol"; /** * @title Provides addresses of the live contracts implementing certain interfaces. * @dev Examples of interfaces with implementations that Finder locates are the Oracle and Store interfaces. */ contract Finder is FinderInterface, Ownable { mapping(bytes32 => address) public interfacesImplemented; event InterfaceImplementationChanged(bytes32 indexed interfaceName, address indexed newImplementationAddress); /** * @notice Updates the address of the contract that implements `interfaceName`. * @param interfaceName bytes32 of the interface name that is either changed or registered. * @param implementationAddress address of the implementation contract. */ function changeImplementationAddress(bytes32 interfaceName, address implementationAddress) external override onlyOwner { interfacesImplemented[interfaceName] = implementationAddress; emit InterfaceImplementationChanged(interfaceName, implementationAddress); } /** * @notice Gets the address of the contract that implements the given `interfaceName`. * @param interfaceName queried interface. * @return implementationAddress address of the defined interface. */ function getImplementationAddress(bytes32 interfaceName) external view override returns (address) { address implementationAddress = interfacesImplemented[interfaceName]; require(implementationAddress != address(0x0), "Implementation not found"); return implementationAddress; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../oracle/implementation/Finder.sol"; import "../oracle/implementation/Constants.sol"; import "../oracle/implementation/Voting.sol"; /** * @title A contract to track a whitelist of addresses. */ contract Umip3Upgrader { // Existing governor is the only one who can initiate the upgrade. address public existingGovernor; // Existing Voting contract needs to be informed of the address of the new Voting contract. Voting public existingVoting; // New governor will be the new owner of the finder. address public newGovernor; // Finder contract to push upgrades to. Finder public finder; // Addresses to upgrade. address public voting; address public identifierWhitelist; address public store; address public financialContractsAdmin; address public registry; constructor( address _existingGovernor, address _existingVoting, address _finder, address _voting, address _identifierWhitelist, address _store, address _financialContractsAdmin, address _registry, address _newGovernor ) public { existingGovernor = _existingGovernor; existingVoting = Voting(_existingVoting); finder = Finder(_finder); voting = _voting; identifierWhitelist = _identifierWhitelist; store = _store; financialContractsAdmin = _financialContractsAdmin; registry = _registry; newGovernor = _newGovernor; } function upgrade() external { require(msg.sender == existingGovernor, "Upgrade can only be initiated by the existing governor."); // Change the addresses in the Finder. finder.changeImplementationAddress(OracleInterfaces.Oracle, voting); finder.changeImplementationAddress(OracleInterfaces.IdentifierWhitelist, identifierWhitelist); finder.changeImplementationAddress(OracleInterfaces.Store, store); finder.changeImplementationAddress(OracleInterfaces.FinancialContractsAdmin, financialContractsAdmin); finder.changeImplementationAddress(OracleInterfaces.Registry, registry); // Transfer the ownership of the Finder to the new Governor now that all the addresses have been updated. finder.transferOwnership(newGovernor); // Inform the existing Voting contract of the address of the new Voting contract and transfer its // ownership to the new governor to allow for any future changes to the migrated contract. existingVoting.setMigrated(voting); existingVoting.transferOwnership(newGovernor); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/Testable.sol"; import "../interfaces/OracleAncillaryInterface.sol"; import "../interfaces/IdentifierWhitelistInterface.sol"; import "../interfaces/FinderInterface.sol"; import "../implementation/Constants.sol"; // A mock oracle used for testing. contract MockOracleAncillary is OracleAncillaryInterface, Testable { // Represents an available price. Have to keep a separate bool to allow for price=0. struct Price { bool isAvailable; int256 price; // Time the verified price became available. uint256 verifiedTime; } // The two structs below are used in an array and mapping to keep track of prices that have been requested but are // not yet available. struct QueryIndex { bool isValid; uint256 index; } // Represents a (identifier, time) point that has been queried. struct QueryPoint { bytes32 identifier; uint256 time; bytes ancillaryData; } // Reference to the Finder. FinderInterface private finder; // Conceptually we want a (time, identifier) -> price map. mapping(bytes32 => mapping(uint256 => mapping(bytes => Price))) private verifiedPrices; // The mapping and array allow retrieving all the elements in a mapping and finding/deleting elements. // Can we generalize this data structure? mapping(bytes32 => mapping(uint256 => mapping(bytes => QueryIndex))) private queryIndices; QueryPoint[] private requestedPrices; constructor(address _finderAddress, address _timerAddress) public Testable(_timerAddress) { finder = FinderInterface(_finderAddress); } // Enqueues a request (if a request isn't already present) for the given (identifier, time) pair. function requestPrice( bytes32 identifier, uint256 time, bytes memory ancillaryData ) public override { require(_getIdentifierWhitelist().isIdentifierSupported(identifier)); Price storage lookup = verifiedPrices[identifier][time][ancillaryData]; if (!lookup.isAvailable && !queryIndices[identifier][time][ancillaryData].isValid) { // New query, enqueue it for review. queryIndices[identifier][time][ancillaryData] = QueryIndex(true, requestedPrices.length); requestedPrices.push(QueryPoint(identifier, time, ancillaryData)); } } // Pushes the verified price for a requested query. function pushPrice( bytes32 identifier, uint256 time, bytes memory ancillaryData, int256 price ) external { verifiedPrices[identifier][time][ancillaryData] = Price(true, price, getCurrentTime()); QueryIndex storage queryIndex = queryIndices[identifier][time][ancillaryData]; require(queryIndex.isValid, "Can't push prices that haven't been requested"); // Delete from the array. Instead of shifting the queries over, replace the contents of `indexToReplace` with // the contents of the last index (unless it is the last index). uint256 indexToReplace = queryIndex.index; delete queryIndices[identifier][time][ancillaryData]; uint256 lastIndex = requestedPrices.length - 1; if (lastIndex != indexToReplace) { QueryPoint storage queryToCopy = requestedPrices[lastIndex]; queryIndices[queryToCopy.identifier][queryToCopy.time][queryToCopy.ancillaryData].index = indexToReplace; requestedPrices[indexToReplace] = queryToCopy; } } // Checks whether a price has been resolved. function hasPrice( bytes32 identifier, uint256 time, bytes memory ancillaryData ) public view override returns (bool) { require(_getIdentifierWhitelist().isIdentifierSupported(identifier)); Price storage lookup = verifiedPrices[identifier][time][ancillaryData]; return lookup.isAvailable; } // Gets a price that has already been resolved. function getPrice( bytes32 identifier, uint256 time, bytes memory ancillaryData ) public view override returns (int256) { require(_getIdentifierWhitelist().isIdentifierSupported(identifier)); Price storage lookup = verifiedPrices[identifier][time][ancillaryData]; require(lookup.isAvailable); return lookup.price; } // Gets the queries that still need verified prices. function getPendingQueries() external view returns (QueryPoint[] memory) { return requestedPrices; } function _getIdentifierWhitelist() private view returns (IdentifierWhitelistInterface supportedIdentifiers) { return IdentifierWhitelistInterface(finder.getImplementationAddress(OracleInterfaces.IdentifierWhitelist)); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/Testable.sol"; import "../interfaces/OracleInterface.sol"; import "../interfaces/IdentifierWhitelistInterface.sol"; import "../interfaces/FinderInterface.sol"; import "../implementation/Constants.sol"; // A mock oracle used for testing. contract MockOracle is OracleInterface, Testable { // Represents an available price. Have to keep a separate bool to allow for price=0. struct Price { bool isAvailable; int256 price; // Time the verified price became available. uint256 verifiedTime; } // The two structs below are used in an array and mapping to keep track of prices that have been requested but are // not yet available. struct QueryIndex { bool isValid; uint256 index; } // Represents a (identifier, time) point that has been queried. struct QueryPoint { bytes32 identifier; uint256 time; } // Reference to the Finder. FinderInterface private finder; // Conceptually we want a (time, identifier) -> price map. mapping(bytes32 => mapping(uint256 => Price)) private verifiedPrices; // The mapping and array allow retrieving all the elements in a mapping and finding/deleting elements. // Can we generalize this data structure? mapping(bytes32 => mapping(uint256 => QueryIndex)) private queryIndices; QueryPoint[] private requestedPrices; constructor(address _finderAddress, address _timerAddress) public Testable(_timerAddress) { finder = FinderInterface(_finderAddress); } // Enqueues a request (if a request isn't already present) for the given (identifier, time) pair. function requestPrice(bytes32 identifier, uint256 time) public override { require(_getIdentifierWhitelist().isIdentifierSupported(identifier)); Price storage lookup = verifiedPrices[identifier][time]; if (!lookup.isAvailable && !queryIndices[identifier][time].isValid) { // New query, enqueue it for review. queryIndices[identifier][time] = QueryIndex(true, requestedPrices.length); requestedPrices.push(QueryPoint(identifier, time)); } } // Pushes the verified price for a requested query. function pushPrice( bytes32 identifier, uint256 time, int256 price ) external { verifiedPrices[identifier][time] = Price(true, price, getCurrentTime()); QueryIndex storage queryIndex = queryIndices[identifier][time]; require(queryIndex.isValid, "Can't push prices that haven't been requested"); // Delete from the array. Instead of shifting the queries over, replace the contents of `indexToReplace` with // the contents of the last index (unless it is the last index). uint256 indexToReplace = queryIndex.index; delete queryIndices[identifier][time]; uint256 lastIndex = requestedPrices.length - 1; if (lastIndex != indexToReplace) { QueryPoint storage queryToCopy = requestedPrices[lastIndex]; queryIndices[queryToCopy.identifier][queryToCopy.time].index = indexToReplace; requestedPrices[indexToReplace] = queryToCopy; } } // Checks whether a price has been resolved. function hasPrice(bytes32 identifier, uint256 time) public view override returns (bool) { require(_getIdentifierWhitelist().isIdentifierSupported(identifier)); Price storage lookup = verifiedPrices[identifier][time]; return lookup.isAvailable; } // Gets a price that has already been resolved. function getPrice(bytes32 identifier, uint256 time) public view override returns (int256) { require(_getIdentifierWhitelist().isIdentifierSupported(identifier)); Price storage lookup = verifiedPrices[identifier][time]; require(lookup.isAvailable); return lookup.price; } // Gets the queries that still need verified prices. function getPendingQueries() external view returns (QueryPoint[] memory) { return requestedPrices; } function _getIdentifierWhitelist() private view returns (IdentifierWhitelistInterface supportedIdentifiers) { return IdentifierWhitelistInterface(finder.getImplementationAddress(OracleInterfaces.IdentifierWhitelist)); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/MultiRole.sol"; import "../../common/implementation/FixedPoint.sol"; import "../../common/implementation/Testable.sol"; import "../interfaces/FinderInterface.sol"; import "../interfaces/IdentifierWhitelistInterface.sol"; import "../interfaces/OracleInterface.sol"; import "./Constants.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/utils/Address.sol"; /** * @title Takes proposals for certain governance actions and allows UMA token holders to vote on them. */ contract Governor is MultiRole, Testable { using SafeMath for uint256; using Address for address; /**************************************** * INTERNAL VARIABLES AND STORAGE * ****************************************/ enum Roles { Owner, // Can set the proposer. Proposer // Address that can make proposals. } struct Transaction { address to; uint256 value; bytes data; } struct Proposal { Transaction[] transactions; uint256 requestTime; } FinderInterface private finder; Proposal[] public proposals; /**************************************** * EVENTS * ****************************************/ // Emitted when a new proposal is created. event NewProposal(uint256 indexed id, Transaction[] transactions); // Emitted when an existing proposal is executed. event ProposalExecuted(uint256 indexed id, uint256 transactionIndex); /** * @notice Construct the Governor contract. * @param _finderAddress keeps track of all contracts within the system based on their interfaceName. * @param _startingId the initial proposal id that the contract will begin incrementing from. * @param _timerAddress Contract that stores the current time in a testing environment. * Must be set to 0x0 for production environments that use live time. */ constructor( address _finderAddress, uint256 _startingId, address _timerAddress ) public Testable(_timerAddress) { finder = FinderInterface(_finderAddress); _createExclusiveRole(uint256(Roles.Owner), uint256(Roles.Owner), msg.sender); _createExclusiveRole(uint256(Roles.Proposer), uint256(Roles.Owner), msg.sender); // Ensure the startingId is not set unreasonably high to avoid it being set such that new proposals overwrite // other storage slots in the contract. uint256 maxStartingId = 10**18; require(_startingId <= maxStartingId, "Cannot set startingId larger than 10^18"); // This just sets the initial length of the array to the startingId since modifying length directly has been // disallowed in solidity 0.6. assembly { sstore(proposals_slot, _startingId) } } /**************************************** * PROPOSAL ACTIONS * ****************************************/ /** * @notice Proposes a new governance action. Can only be called by the holder of the Proposer role. * @param transactions list of transactions that are being proposed. * @dev You can create the data portion of each transaction by doing the following: * ``` * const truffleContractInstance = await TruffleContract.deployed() * const data = truffleContractInstance.methods.methodToCall(arg1, arg2).encodeABI() * ``` * Note: this method must be public because of a solidity limitation that * disallows structs arrays to be passed to external functions. */ function propose(Transaction[] memory transactions) public onlyRoleHolder(uint256(Roles.Proposer)) { uint256 id = proposals.length; uint256 time = getCurrentTime(); // Note: doing all of this array manipulation manually is necessary because directly setting an array of // structs in storage to an an array of structs in memory is currently not implemented in solidity :/. // Add a zero-initialized element to the proposals array. proposals.push(); // Initialize the new proposal. Proposal storage proposal = proposals[id]; proposal.requestTime = time; // Initialize the transaction array. for (uint256 i = 0; i < transactions.length; i++) { require(transactions[i].to != address(0), "The `to` address cannot be 0x0"); // If the transaction has any data with it the recipient must be a contract, not an EOA. if (transactions[i].data.length > 0) { require(transactions[i].to.isContract(), "EOA can't accept tx with data"); } proposal.transactions.push(transactions[i]); } bytes32 identifier = _constructIdentifier(id); // Request a vote on this proposal in the DVM. OracleInterface oracle = _getOracle(); IdentifierWhitelistInterface supportedIdentifiers = _getIdentifierWhitelist(); supportedIdentifiers.addSupportedIdentifier(identifier); oracle.requestPrice(identifier, time); supportedIdentifiers.removeSupportedIdentifier(identifier); emit NewProposal(id, transactions); } /** * @notice Executes a proposed governance action that has been approved by voters. * @dev This can be called by any address. Caller is expected to send enough ETH to execute payable transactions. * @param id unique id for the executed proposal. * @param transactionIndex unique transaction index for the executed proposal. */ function executeProposal(uint256 id, uint256 transactionIndex) external payable { Proposal storage proposal = proposals[id]; int256 price = _getOracle().getPrice(_constructIdentifier(id), proposal.requestTime); Transaction memory transaction = proposal.transactions[transactionIndex]; require( transactionIndex == 0 || proposal.transactions[transactionIndex.sub(1)].to == address(0), "Previous tx not yet executed" ); require(transaction.to != address(0), "Tx already executed"); require(price != 0, "Proposal was rejected"); require(msg.value == transaction.value, "Must send exact amount of ETH"); // Delete the transaction before execution to avoid any potential re-entrancy issues. delete proposal.transactions[transactionIndex]; require(_executeCall(transaction.to, transaction.value, transaction.data), "Tx execution failed"); emit ProposalExecuted(id, transactionIndex); } /**************************************** * GOVERNOR STATE GETTERS * ****************************************/ /** * @notice Gets the total number of proposals (includes executed and non-executed). * @return uint256 representing the current number of proposals. */ function numProposals() external view returns (uint256) { return proposals.length; } /** * @notice Gets the proposal data for a particular id. * @dev after a proposal is executed, its data will be zeroed out, except for the request time. * @param id uniquely identify the identity of the proposal. * @return proposal struct containing transactions[] and requestTime. */ function getProposal(uint256 id) external view returns (Proposal memory) { return proposals[id]; } /**************************************** * PRIVATE GETTERS AND FUNCTIONS * ****************************************/ function _executeCall( address to, uint256 value, bytes memory data ) private returns (bool) { // Mostly copied from: // solhint-disable-next-line max-line-length // https://github.com/gnosis/safe-contracts/blob/59cfdaebcd8b87a0a32f87b50fead092c10d3a05/contracts/base/Executor.sol#L23-L31 // solhint-disable-next-line no-inline-assembly bool success; assembly { let inputData := add(data, 0x20) let inputDataSize := mload(data) success := call(gas(), to, value, inputData, inputDataSize, 0, 0) } return success; } function _getOracle() private view returns (OracleInterface) { return OracleInterface(finder.getImplementationAddress(OracleInterfaces.Oracle)); } function _getIdentifierWhitelist() private view returns (IdentifierWhitelistInterface supportedIdentifiers) { return IdentifierWhitelistInterface(finder.getImplementationAddress(OracleInterfaces.IdentifierWhitelist)); } // Returns a UTF-8 identifier representing a particular admin proposal. // The identifier is of the form "Admin n", where n is the proposal id provided. function _constructIdentifier(uint256 id) internal pure returns (bytes32) { bytes32 bytesId = _uintToUtf8(id); return _addPrefix(bytesId, "Admin ", 6); } // This method converts the integer `v` into a base-10, UTF-8 representation stored in a `bytes32` type. // If the input cannot be represented by 32 base-10 digits, it returns only the highest 32 digits. // This method is based off of this code: https://ethereum.stackexchange.com/a/6613/47801. function _uintToUtf8(uint256 v) internal pure returns (bytes32) { bytes32 ret; if (v == 0) { // Handle 0 case explicitly. ret = "0"; } else { // Constants. uint256 bitsPerByte = 8; uint256 base = 10; // Note: the output should be base-10. The below implementation will not work for bases > 10. uint256 utf8NumberOffset = 48; while (v > 0) { // Downshift the entire bytes32 to allow the new digit to be added at the "front" of the bytes32, which // translates to the beginning of the UTF-8 representation. ret = ret >> bitsPerByte; // Separate the last digit that remains in v by modding by the base of desired output representation. uint256 leastSignificantDigit = v % base; // Digits 0-9 are represented by 48-57 in UTF-8, so an offset must be added to create the character. bytes32 utf8Digit = bytes32(leastSignificantDigit + utf8NumberOffset); // The top byte of ret has already been cleared to make room for the new digit. // Upshift by 31 bytes to put it in position, and OR it with ret to leave the other characters untouched. ret |= utf8Digit << (31 * bitsPerByte); // Divide v by the base to remove the digit that was just added. v /= base; } } return ret; } // This method takes two UTF-8 strings represented as bytes32 and outputs one as a prefixed by the other. // `input` is the UTF-8 that should have the prefix prepended. // `prefix` is the UTF-8 that should be prepended onto input. // `prefixLength` is number of UTF-8 characters represented by `prefix`. // Notes: // 1. If the resulting UTF-8 is larger than 32 characters, then only the first 32 characters will be represented // by the bytes32 output. // 2. If `prefix` has more characters than `prefixLength`, the function will produce an invalid result. function _addPrefix( bytes32 input, bytes32 prefix, uint256 prefixLength ) internal pure returns (bytes32) { // Downshift `input` to open space at the "front" of the bytes32 bytes32 shiftedInput = input >> (prefixLength * 8); return shiftedInput | prefix; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../Governor.sol"; // GovernorTest exposes internal methods in the Governor for testing. contract GovernorTest is Governor { constructor(address _timerAddress) public Governor(address(0), 0, _timerAddress) {} function addPrefix( bytes32 input, bytes32 prefix, uint256 prefixLength ) external pure returns (bytes32) { return _addPrefix(input, prefix, prefixLength); } function uintToUtf8(uint256 v) external pure returns (bytes32 ret) { return _uintToUtf8(v); } function constructIdentifier(uint256 id) external pure returns (bytes32 identifier) { return _constructIdentifier(id); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "../../common/implementation/FixedPoint.sol"; import "../../common/interfaces/ExpandedIERC20.sol"; import "../../common/interfaces/IERC20Standard.sol"; import "../../oracle/interfaces/OracleInterface.sol"; import "../../oracle/interfaces/OptimisticOracleInterface.sol"; import "../../oracle/interfaces/IdentifierWhitelistInterface.sol"; import "../../oracle/implementation/Constants.sol"; import "../common/FeePayer.sol"; import "../common/financial-product-libraries/FinancialProductLibrary.sol"; /** * @title Financial contract with priceless position management. * @notice Handles positions for multiple sponsors in an optimistic (i.e., priceless) way without relying * on a price feed. On construction, deploys a new ERC20, managed by this contract, that is the synthetic token. */ contract PricelessPositionManager is FeePayer { using SafeMath for uint256; using FixedPoint for FixedPoint.Unsigned; using SafeERC20 for IERC20; using SafeERC20 for ExpandedIERC20; using Address for address; /**************************************** * PRICELESS POSITION DATA STRUCTURES * ****************************************/ // Stores the state of the PricelessPositionManager. Set on expiration, emergency shutdown, or settlement. enum ContractState { Open, ExpiredPriceRequested, ExpiredPriceReceived } ContractState public contractState; // Represents a single sponsor's position. All collateral is held by this contract. // This struct acts as bookkeeping for how much of that collateral is allocated to each sponsor. struct PositionData { FixedPoint.Unsigned tokensOutstanding; // Tracks pending withdrawal requests. A withdrawal request is pending if `withdrawalRequestPassTimestamp != 0`. uint256 withdrawalRequestPassTimestamp; FixedPoint.Unsigned withdrawalRequestAmount; // Raw collateral value. This value should never be accessed directly -- always use _getFeeAdjustedCollateral(). // To add or remove collateral, use _addCollateral() and _removeCollateral(). FixedPoint.Unsigned rawCollateral; // Tracks pending transfer position requests. A transfer position request is pending if `transferPositionRequestPassTimestamp != 0`. uint256 transferPositionRequestPassTimestamp; } // Maps sponsor addresses to their positions. Each sponsor can have only one position. mapping(address => PositionData) public positions; // Keep track of the total collateral and tokens across all positions to enable calculating the // global collateralization ratio without iterating over all positions. FixedPoint.Unsigned public totalTokensOutstanding; // Similar to the rawCollateral in PositionData, this value should not be used directly. // _getFeeAdjustedCollateral(), _addCollateral() and _removeCollateral() must be used to access and adjust. FixedPoint.Unsigned public rawTotalPositionCollateral; // Synthetic token created by this contract. ExpandedIERC20 public tokenCurrency; // Unique identifier for DVM price feed ticker. bytes32 public priceIdentifier; // Time that this contract expires. Should not change post-construction unless an emergency shutdown occurs. uint256 public expirationTimestamp; // Time that has to elapse for a withdrawal request to be considered passed, if no liquidations occur. // !!Note: The lower the withdrawal liveness value, the more risk incurred by the contract. // Extremely low liveness values increase the chance that opportunistic invalid withdrawal requests // expire without liquidation, thereby increasing the insolvency risk for the contract as a whole. An insolvent // contract is extremely risky for any sponsor or synthetic token holder for the contract. uint256 public withdrawalLiveness; // Minimum number of tokens in a sponsor's position. FixedPoint.Unsigned public minSponsorTokens; // The expiry price pulled from the DVM. FixedPoint.Unsigned public expiryPrice; // Instance of FinancialProductLibrary to provide custom price and collateral requirement transformations to extend // the functionality of the EMP to support a wider range of financial products. FinancialProductLibrary public financialProductLibrary; /**************************************** * EVENTS * ****************************************/ event RequestTransferPosition(address indexed oldSponsor); event RequestTransferPositionExecuted(address indexed oldSponsor, address indexed newSponsor); event RequestTransferPositionCanceled(address indexed oldSponsor); event Deposit(address indexed sponsor, uint256 indexed collateralAmount); event Withdrawal(address indexed sponsor, uint256 indexed collateralAmount); event RequestWithdrawal(address indexed sponsor, uint256 indexed collateralAmount); event RequestWithdrawalExecuted(address indexed sponsor, uint256 indexed collateralAmount); event RequestWithdrawalCanceled(address indexed sponsor, uint256 indexed collateralAmount); event PositionCreated(address indexed sponsor, uint256 indexed collateralAmount, uint256 indexed tokenAmount); event NewSponsor(address indexed sponsor); event EndedSponsorPosition(address indexed sponsor); event Repay(address indexed sponsor, uint256 indexed numTokensRepaid, uint256 indexed newTokenCount); event Redeem(address indexed sponsor, uint256 indexed collateralAmount, uint256 indexed tokenAmount); event ContractExpired(address indexed caller); event SettleExpiredPosition( address indexed caller, uint256 indexed collateralReturned, uint256 indexed tokensBurned ); event EmergencyShutdown(address indexed caller, uint256 originalExpirationTimestamp, uint256 shutdownTimestamp); /**************************************** * MODIFIERS * ****************************************/ modifier onlyPreExpiration() { _onlyPreExpiration(); _; } modifier onlyPostExpiration() { _onlyPostExpiration(); _; } modifier onlyCollateralizedPosition(address sponsor) { _onlyCollateralizedPosition(sponsor); _; } // Check that the current state of the pricelessPositionManager is Open. // This prevents multiple calls to `expire` and `EmergencyShutdown` post expiration. modifier onlyOpenState() { _onlyOpenState(); _; } modifier noPendingWithdrawal(address sponsor) { _positionHasNoPendingWithdrawal(sponsor); _; } /** * @notice Construct the PricelessPositionManager * @dev Deployer of this contract should consider carefully which parties have ability to mint and burn * the synthetic tokens referenced by `_tokenAddress`. This contract's security assumes that no external accounts * can mint new tokens, which could be used to steal all of this contract's locked collateral. * We recommend to only use synthetic token contracts whose sole Owner role (the role capable of adding & removing roles) * is assigned to this contract, whose sole Minter role is assigned to this contract, and whose * total supply is 0 prior to construction of this contract. * @param _expirationTimestamp unix timestamp of when the contract will expire. * @param _withdrawalLiveness liveness delay, in seconds, for pending withdrawals. * @param _collateralAddress ERC20 token used as collateral for all positions. * @param _tokenAddress ERC20 token used as synthetic token. * @param _finderAddress UMA protocol Finder used to discover other protocol contracts. * @param _priceIdentifier registered in the DVM for the synthetic. * @param _minSponsorTokens minimum amount of collateral that must exist at any time in a position. * @param _timerAddress Contract that stores the current time in a testing environment. * Must be set to 0x0 for production environments that use live time. * @param _financialProductLibraryAddress Contract providing contract state transformations. */ constructor( uint256 _expirationTimestamp, uint256 _withdrawalLiveness, address _collateralAddress, address _tokenAddress, address _finderAddress, bytes32 _priceIdentifier, FixedPoint.Unsigned memory _minSponsorTokens, address _timerAddress, address _financialProductLibraryAddress ) public FeePayer(_collateralAddress, _finderAddress, _timerAddress) nonReentrant() { require(_expirationTimestamp > getCurrentTime()); require(_getIdentifierWhitelist().isIdentifierSupported(_priceIdentifier)); expirationTimestamp = _expirationTimestamp; withdrawalLiveness = _withdrawalLiveness; tokenCurrency = ExpandedIERC20(_tokenAddress); minSponsorTokens = _minSponsorTokens; priceIdentifier = _priceIdentifier; // Initialize the financialProductLibrary at the provided address. financialProductLibrary = FinancialProductLibrary(_financialProductLibraryAddress); } /**************************************** * POSITION FUNCTIONS * ****************************************/ /** * @notice Requests to transfer ownership of the caller's current position to a new sponsor address. * Once the request liveness is passed, the sponsor can execute the transfer and specify the new sponsor. * @dev The liveness length is the same as the withdrawal liveness. */ function requestTransferPosition() public onlyPreExpiration() nonReentrant() { PositionData storage positionData = _getPositionData(msg.sender); require(positionData.transferPositionRequestPassTimestamp == 0); // Make sure the proposed expiration of this request is not post-expiry. uint256 requestPassTime = getCurrentTime().add(withdrawalLiveness); require(requestPassTime < expirationTimestamp); // Update the position object for the user. positionData.transferPositionRequestPassTimestamp = requestPassTime; emit RequestTransferPosition(msg.sender); } /** * @notice After a passed transfer position request (i.e., by a call to `requestTransferPosition` and waiting * `withdrawalLiveness`), transfers ownership of the caller's current position to `newSponsorAddress`. * @dev Transferring positions can only occur if the recipient does not already have a position. * @param newSponsorAddress is the address to which the position will be transferred. */ function transferPositionPassedRequest(address newSponsorAddress) public onlyPreExpiration() noPendingWithdrawal(msg.sender) nonReentrant() { require( _getFeeAdjustedCollateral(positions[newSponsorAddress].rawCollateral).isEqual( FixedPoint.fromUnscaledUint(0) ) ); PositionData storage positionData = _getPositionData(msg.sender); require( positionData.transferPositionRequestPassTimestamp != 0 && positionData.transferPositionRequestPassTimestamp <= getCurrentTime() ); // Reset transfer request. positionData.transferPositionRequestPassTimestamp = 0; positions[newSponsorAddress] = positionData; delete positions[msg.sender]; emit RequestTransferPositionExecuted(msg.sender, newSponsorAddress); emit NewSponsor(newSponsorAddress); emit EndedSponsorPosition(msg.sender); } /** * @notice Cancels a pending transfer position request. */ function cancelTransferPosition() external onlyPreExpiration() nonReentrant() { PositionData storage positionData = _getPositionData(msg.sender); require(positionData.transferPositionRequestPassTimestamp != 0); emit RequestTransferPositionCanceled(msg.sender); // Reset withdrawal request. positionData.transferPositionRequestPassTimestamp = 0; } /** * @notice Transfers `collateralAmount` of `collateralCurrency` into the specified sponsor's position. * @dev Increases the collateralization level of a position after creation. This contract must be approved to spend * at least `collateralAmount` of `collateralCurrency`. * @param sponsor the sponsor to credit the deposit to. * @param collateralAmount total amount of collateral tokens to be sent to the sponsor's position. */ function depositTo(address sponsor, FixedPoint.Unsigned memory collateralAmount) public onlyPreExpiration() noPendingWithdrawal(sponsor) fees() nonReentrant() { require(collateralAmount.isGreaterThan(0)); PositionData storage positionData = _getPositionData(sponsor); // Increase the position and global collateral balance by collateral amount. _incrementCollateralBalances(positionData, collateralAmount); emit Deposit(sponsor, collateralAmount.rawValue); // Move collateral currency from sender to contract. collateralCurrency.safeTransferFrom(msg.sender, address(this), collateralAmount.rawValue); } /** * @notice Transfers `collateralAmount` of `collateralCurrency` into the caller's position. * @dev Increases the collateralization level of a position after creation. This contract must be approved to spend * at least `collateralAmount` of `collateralCurrency`. * @param collateralAmount total amount of collateral tokens to be sent to the sponsor's position. */ function deposit(FixedPoint.Unsigned memory collateralAmount) public { // This is just a thin wrapper over depositTo that specified the sender as the sponsor. depositTo(msg.sender, collateralAmount); } /** * @notice Transfers `collateralAmount` of `collateralCurrency` from the sponsor's position to the sponsor. * @dev Reverts if the withdrawal puts this position's collateralization ratio below the global collateralization * ratio. In that case, use `requestWithdrawal`. Might not withdraw the full requested amount to account for precision loss. * @param collateralAmount is the amount of collateral to withdraw. * @return amountWithdrawn The actual amount of collateral withdrawn. */ function withdraw(FixedPoint.Unsigned memory collateralAmount) public onlyPreExpiration() noPendingWithdrawal(msg.sender) fees() nonReentrant() returns (FixedPoint.Unsigned memory amountWithdrawn) { require(collateralAmount.isGreaterThan(0)); PositionData storage positionData = _getPositionData(msg.sender); // Decrement the sponsor's collateral and global collateral amounts. Check the GCR between decrement to ensure // position remains above the GCR within the withdrawal. If this is not the case the caller must submit a request. amountWithdrawn = _decrementCollateralBalancesCheckGCR(positionData, collateralAmount); emit Withdrawal(msg.sender, amountWithdrawn.rawValue); // Move collateral currency from contract to sender. // Note: that we move the amount of collateral that is decreased from rawCollateral (inclusive of fees) // instead of the user requested amount. This eliminates precision loss that could occur // where the user withdraws more collateral than rawCollateral is decremented by. collateralCurrency.safeTransfer(msg.sender, amountWithdrawn.rawValue); } /** * @notice Starts a withdrawal request that, if passed, allows the sponsor to withdraw` from their position. * @dev The request will be pending for `withdrawalLiveness`, during which the position can be liquidated. * @param collateralAmount the amount of collateral requested to withdraw */ function requestWithdrawal(FixedPoint.Unsigned memory collateralAmount) public onlyPreExpiration() noPendingWithdrawal(msg.sender) nonReentrant() { PositionData storage positionData = _getPositionData(msg.sender); require( collateralAmount.isGreaterThan(0) && collateralAmount.isLessThanOrEqual(_getFeeAdjustedCollateral(positionData.rawCollateral)) ); // Make sure the proposed expiration of this request is not post-expiry. uint256 requestPassTime = getCurrentTime().add(withdrawalLiveness); require(requestPassTime < expirationTimestamp); // Update the position object for the user. positionData.withdrawalRequestPassTimestamp = requestPassTime; positionData.withdrawalRequestAmount = collateralAmount; emit RequestWithdrawal(msg.sender, collateralAmount.rawValue); } /** * @notice After a passed withdrawal request (i.e., by a call to `requestWithdrawal` and waiting * `withdrawalLiveness`), withdraws `positionData.withdrawalRequestAmount` of collateral currency. * @dev Might not withdraw the full requested amount in order to account for precision loss or if the full requested * amount exceeds the collateral in the position (due to paying fees). * @return amountWithdrawn The actual amount of collateral withdrawn. */ function withdrawPassedRequest() external onlyPreExpiration() fees() nonReentrant() returns (FixedPoint.Unsigned memory amountWithdrawn) { PositionData storage positionData = _getPositionData(msg.sender); require( positionData.withdrawalRequestPassTimestamp != 0 && positionData.withdrawalRequestPassTimestamp <= getCurrentTime() ); // If withdrawal request amount is > position collateral, then withdraw the full collateral amount. // This situation is possible due to fees charged since the withdrawal was originally requested. FixedPoint.Unsigned memory amountToWithdraw = positionData.withdrawalRequestAmount; if (positionData.withdrawalRequestAmount.isGreaterThan(_getFeeAdjustedCollateral(positionData.rawCollateral))) { amountToWithdraw = _getFeeAdjustedCollateral(positionData.rawCollateral); } // Decrement the sponsor's collateral and global collateral amounts. amountWithdrawn = _decrementCollateralBalances(positionData, amountToWithdraw); // Reset withdrawal request by setting withdrawal amount and withdrawal timestamp to 0. _resetWithdrawalRequest(positionData); // Transfer approved withdrawal amount from the contract to the caller. collateralCurrency.safeTransfer(msg.sender, amountWithdrawn.rawValue); emit RequestWithdrawalExecuted(msg.sender, amountWithdrawn.rawValue); } /** * @notice Cancels a pending withdrawal request. */ function cancelWithdrawal() external nonReentrant() { PositionData storage positionData = _getPositionData(msg.sender); require(positionData.withdrawalRequestPassTimestamp != 0); emit RequestWithdrawalCanceled(msg.sender, positionData.withdrawalRequestAmount.rawValue); // Reset withdrawal request by setting withdrawal amount and withdrawal timestamp to 0. _resetWithdrawalRequest(positionData); } /** * @notice Creates tokens by creating a new position or by augmenting an existing position. Pulls `collateralAmount` into the sponsor's position and mints `numTokens` of `tokenCurrency`. * @dev Reverts if minting these tokens would put the position's collateralization ratio below the * global collateralization ratio. This contract must be approved to spend at least `collateralAmount` of * `collateralCurrency`. * @dev This contract must have the Minter role for the `tokenCurrency`. * @param collateralAmount is the number of collateral tokens to collateralize the position with * @param numTokens is the number of tokens to mint from the position. */ function create(FixedPoint.Unsigned memory collateralAmount, FixedPoint.Unsigned memory numTokens) public onlyPreExpiration() fees() nonReentrant() { PositionData storage positionData = positions[msg.sender]; // Either the new create ratio or the resultant position CR must be above the current GCR. require( (_checkCollateralization( _getFeeAdjustedCollateral(positionData.rawCollateral).add(collateralAmount), positionData.tokensOutstanding.add(numTokens) ) || _checkCollateralization(collateralAmount, numTokens)), "Insufficient collateral" ); require(positionData.withdrawalRequestPassTimestamp == 0, "Pending withdrawal"); if (positionData.tokensOutstanding.isEqual(0)) { require(numTokens.isGreaterThanOrEqual(minSponsorTokens), "Below minimum sponsor position"); emit NewSponsor(msg.sender); } // Increase the position and global collateral balance by collateral amount. _incrementCollateralBalances(positionData, collateralAmount); // Add the number of tokens created to the position's outstanding tokens. positionData.tokensOutstanding = positionData.tokensOutstanding.add(numTokens); totalTokensOutstanding = totalTokensOutstanding.add(numTokens); emit PositionCreated(msg.sender, collateralAmount.rawValue, numTokens.rawValue); // Transfer tokens into the contract from caller and mint corresponding synthetic tokens to the caller's address. collateralCurrency.safeTransferFrom(msg.sender, address(this), collateralAmount.rawValue); require(tokenCurrency.mint(msg.sender, numTokens.rawValue)); } /** * @notice Burns `numTokens` of `tokenCurrency` to decrease sponsors position size, without sending back `collateralCurrency`. * This is done by a sponsor to increase position CR. Resulting size is bounded by minSponsorTokens. * @dev Can only be called by token sponsor. This contract must be approved to spend `numTokens` of `tokenCurrency`. * @dev This contract must have the Burner role for the `tokenCurrency`. * @param numTokens is the number of tokens to be burnt from the sponsor's debt position. */ function repay(FixedPoint.Unsigned memory numTokens) public onlyPreExpiration() noPendingWithdrawal(msg.sender) fees() nonReentrant() { PositionData storage positionData = _getPositionData(msg.sender); require(numTokens.isLessThanOrEqual(positionData.tokensOutstanding)); // Decrease the sponsors position tokens size. Ensure it is above the min sponsor size. FixedPoint.Unsigned memory newTokenCount = positionData.tokensOutstanding.sub(numTokens); require(newTokenCount.isGreaterThanOrEqual(minSponsorTokens)); positionData.tokensOutstanding = newTokenCount; // Update the totalTokensOutstanding after redemption. totalTokensOutstanding = totalTokensOutstanding.sub(numTokens); emit Repay(msg.sender, numTokens.rawValue, newTokenCount.rawValue); // Transfer the tokens back from the sponsor and burn them. tokenCurrency.safeTransferFrom(msg.sender, address(this), numTokens.rawValue); tokenCurrency.burn(numTokens.rawValue); } /** * @notice Burns `numTokens` of `tokenCurrency` and sends back the proportional amount of `collateralCurrency`. * @dev Can only be called by a token sponsor. Might not redeem the full proportional amount of collateral * in order to account for precision loss. This contract must be approved to spend at least `numTokens` of * `tokenCurrency`. * @dev This contract must have the Burner role for the `tokenCurrency`. * @param numTokens is the number of tokens to be burnt for a commensurate amount of collateral. * @return amountWithdrawn The actual amount of collateral withdrawn. */ function redeem(FixedPoint.Unsigned memory numTokens) public noPendingWithdrawal(msg.sender) fees() nonReentrant() returns (FixedPoint.Unsigned memory amountWithdrawn) { PositionData storage positionData = _getPositionData(msg.sender); require(!numTokens.isGreaterThan(positionData.tokensOutstanding)); FixedPoint.Unsigned memory fractionRedeemed = numTokens.div(positionData.tokensOutstanding); FixedPoint.Unsigned memory collateralRedeemed = fractionRedeemed.mul(_getFeeAdjustedCollateral(positionData.rawCollateral)); // If redemption returns all tokens the sponsor has then we can delete their position. Else, downsize. if (positionData.tokensOutstanding.isEqual(numTokens)) { amountWithdrawn = _deleteSponsorPosition(msg.sender); } else { // Decrement the sponsor's collateral and global collateral amounts. amountWithdrawn = _decrementCollateralBalances(positionData, collateralRedeemed); // Decrease the sponsors position tokens size. Ensure it is above the min sponsor size. FixedPoint.Unsigned memory newTokenCount = positionData.tokensOutstanding.sub(numTokens); require(newTokenCount.isGreaterThanOrEqual(minSponsorTokens), "Below minimum sponsor position"); positionData.tokensOutstanding = newTokenCount; // Update the totalTokensOutstanding after redemption. totalTokensOutstanding = totalTokensOutstanding.sub(numTokens); } emit Redeem(msg.sender, amountWithdrawn.rawValue, numTokens.rawValue); // Transfer collateral from contract to caller and burn callers synthetic tokens. collateralCurrency.safeTransfer(msg.sender, amountWithdrawn.rawValue); tokenCurrency.safeTransferFrom(msg.sender, address(this), numTokens.rawValue); tokenCurrency.burn(numTokens.rawValue); } /** * @notice After a contract has passed expiry all token holders can redeem their tokens for underlying at the * prevailing price defined by the DVM from the `expire` function. * @dev This burns all tokens from the caller of `tokenCurrency` and sends back the proportional amount of * `collateralCurrency`. Might not redeem the full proportional amount of collateral in order to account for * precision loss. This contract must be approved to spend `tokenCurrency` at least up to the caller's full balance. * @dev This contract must have the Burner role for the `tokenCurrency`. * @return amountWithdrawn The actual amount of collateral withdrawn. */ function settleExpired() external onlyPostExpiration() fees() nonReentrant() returns (FixedPoint.Unsigned memory amountWithdrawn) { // If the contract state is open and onlyPostExpiration passed then `expire()` has not yet been called. require(contractState != ContractState.Open, "Unexpired position"); // Get the current settlement price and store it. If it is not resolved will revert. if (contractState != ContractState.ExpiredPriceReceived) { expiryPrice = _getOraclePriceExpiration(expirationTimestamp); contractState = ContractState.ExpiredPriceReceived; } // Get caller's tokens balance and calculate amount of underlying entitled to them. FixedPoint.Unsigned memory tokensToRedeem = FixedPoint.Unsigned(tokenCurrency.balanceOf(msg.sender)); FixedPoint.Unsigned memory totalRedeemableCollateral = tokensToRedeem.mul(expiryPrice); // If the caller is a sponsor with outstanding collateral they are also entitled to their excess collateral after their debt. PositionData storage positionData = positions[msg.sender]; if (_getFeeAdjustedCollateral(positionData.rawCollateral).isGreaterThan(0)) { // Calculate the underlying entitled to a token sponsor. This is collateral - debt in underlying. FixedPoint.Unsigned memory tokenDebtValueInCollateral = positionData.tokensOutstanding.mul(expiryPrice); FixedPoint.Unsigned memory positionCollateral = _getFeeAdjustedCollateral(positionData.rawCollateral); // If the debt is greater than the remaining collateral, they cannot redeem anything. FixedPoint.Unsigned memory positionRedeemableCollateral = tokenDebtValueInCollateral.isLessThan(positionCollateral) ? positionCollateral.sub(tokenDebtValueInCollateral) : FixedPoint.Unsigned(0); // Add the number of redeemable tokens for the sponsor to their total redeemable collateral. totalRedeemableCollateral = totalRedeemableCollateral.add(positionRedeemableCollateral); // Reset the position state as all the value has been removed after settlement. delete positions[msg.sender]; emit EndedSponsorPosition(msg.sender); } // Take the min of the remaining collateral and the collateral "owed". If the contract is undercapitalized, // the caller will get as much collateral as the contract can pay out. FixedPoint.Unsigned memory payout = FixedPoint.min(_getFeeAdjustedCollateral(rawTotalPositionCollateral), totalRedeemableCollateral); // Decrement total contract collateral and outstanding debt. amountWithdrawn = _removeCollateral(rawTotalPositionCollateral, payout); totalTokensOutstanding = totalTokensOutstanding.sub(tokensToRedeem); emit SettleExpiredPosition(msg.sender, amountWithdrawn.rawValue, tokensToRedeem.rawValue); // Transfer tokens & collateral and burn the redeemed tokens. collateralCurrency.safeTransfer(msg.sender, amountWithdrawn.rawValue); tokenCurrency.safeTransferFrom(msg.sender, address(this), tokensToRedeem.rawValue); tokenCurrency.burn(tokensToRedeem.rawValue); } /**************************************** * GLOBAL STATE FUNCTIONS * ****************************************/ /** * @notice Locks contract state in expired and requests oracle price. * @dev this function can only be called once the contract is expired and can't be re-called. */ function expire() external onlyPostExpiration() onlyOpenState() fees() nonReentrant() { contractState = ContractState.ExpiredPriceRequested; // Final fees do not need to be paid when sending a request to the optimistic oracle. _requestOraclePriceExpiration(expirationTimestamp); emit ContractExpired(msg.sender); } /** * @notice Premature contract settlement under emergency circumstances. * @dev Only the governor can call this function as they are permissioned within the `FinancialContractAdmin`. * Upon emergency shutdown, the contract settlement time is set to the shutdown time. This enables withdrawal * to occur via the standard `settleExpired` function. Contract state is set to `ExpiredPriceRequested` * which prevents re-entry into this function or the `expire` function. No fees are paid when calling * `emergencyShutdown` as the governor who would call the function would also receive the fees. */ function emergencyShutdown() external override onlyPreExpiration() onlyOpenState() nonReentrant() { require(msg.sender == _getFinancialContractsAdminAddress()); contractState = ContractState.ExpiredPriceRequested; // Expiratory time now becomes the current time (emergency shutdown time). // Price requested at this time stamp. `settleExpired` can now withdraw at this timestamp. uint256 oldExpirationTimestamp = expirationTimestamp; expirationTimestamp = getCurrentTime(); _requestOraclePriceExpiration(expirationTimestamp); emit EmergencyShutdown(msg.sender, oldExpirationTimestamp, expirationTimestamp); } /** * @notice Theoretically supposed to pay fees and move money between margin accounts to make sure they * reflect the NAV of the contract. However, this functionality doesn't apply to this contract. * @dev This is supposed to be implemented by any contract that inherits `AdministrateeInterface` and callable * only by the Governor contract. This method is therefore minimally implemented in this contract and does nothing. */ function remargin() external override onlyPreExpiration() nonReentrant() { return; } /** * @notice Accessor method for a sponsor's collateral. * @dev This is necessary because the struct returned by the positions() method shows * rawCollateral, which isn't a user-readable value. * @dev This method accounts for pending regular fees that have not yet been withdrawn from this contract, for * example if the `lastPaymentTime != currentTime`. * @param sponsor address whose collateral amount is retrieved. * @return collateralAmount amount of collateral within a sponsors position. */ function getCollateral(address sponsor) external view nonReentrantView() returns (FixedPoint.Unsigned memory) { // Note: do a direct access to avoid the validity check. return _getPendingRegularFeeAdjustedCollateral(_getFeeAdjustedCollateral(positions[sponsor].rawCollateral)); } /** * @notice Accessor method for the total collateral stored within the PricelessPositionManager. * @return totalCollateral amount of all collateral within the Expiring Multi Party Contract. * @dev This method accounts for pending regular fees that have not yet been withdrawn from this contract, for * example if the `lastPaymentTime != currentTime`. */ function totalPositionCollateral() external view nonReentrantView() returns (FixedPoint.Unsigned memory) { return _getPendingRegularFeeAdjustedCollateral(_getFeeAdjustedCollateral(rawTotalPositionCollateral)); } /** * @notice Accessor method to compute a transformed price using the finanicalProductLibrary specified at contract * deployment. If no library was provided then no modification to the price is done. * @param price input price to be transformed. * @param requestTime timestamp the oraclePrice was requested at. * @return transformedPrice price with the transformation function applied to it. * @dev This method should never revert. */ function transformPrice(FixedPoint.Unsigned memory price, uint256 requestTime) public view nonReentrantView() returns (FixedPoint.Unsigned memory) { return _transformPrice(price, requestTime); } /** * @notice Accessor method to compute a transformed price identifier using the finanicalProductLibrary specified * at contract deployment. If no library was provided then no modification to the identifier is done. * @param requestTime timestamp the identifier is to be used at. * @return transformedPrice price with the transformation function applied to it. * @dev This method should never revert. */ function transformPriceIdentifier(uint256 requestTime) public view nonReentrantView() returns (bytes32) { return _transformPriceIdentifier(requestTime); } /**************************************** * INTERNAL FUNCTIONS * ****************************************/ // Reduces a sponsor's position and global counters by the specified parameters. Handles deleting the entire // position if the entire position is being removed. Does not make any external transfers. function _reduceSponsorPosition( address sponsor, FixedPoint.Unsigned memory tokensToRemove, FixedPoint.Unsigned memory collateralToRemove, FixedPoint.Unsigned memory withdrawalAmountToRemove ) internal { PositionData storage positionData = _getPositionData(sponsor); // If the entire position is being removed, delete it instead. if ( tokensToRemove.isEqual(positionData.tokensOutstanding) && _getFeeAdjustedCollateral(positionData.rawCollateral).isEqual(collateralToRemove) ) { _deleteSponsorPosition(sponsor); return; } // Decrement the sponsor's collateral and global collateral amounts. _decrementCollateralBalances(positionData, collateralToRemove); // Ensure that the sponsor will meet the min position size after the reduction. FixedPoint.Unsigned memory newTokenCount = positionData.tokensOutstanding.sub(tokensToRemove); require(newTokenCount.isGreaterThanOrEqual(minSponsorTokens), "Below minimum sponsor position"); positionData.tokensOutstanding = newTokenCount; // Decrement the position's withdrawal amount. positionData.withdrawalRequestAmount = positionData.withdrawalRequestAmount.sub(withdrawalAmountToRemove); // Decrement the total outstanding tokens in the overall contract. totalTokensOutstanding = totalTokensOutstanding.sub(tokensToRemove); } // Deletes a sponsor's position and updates global counters. Does not make any external transfers. function _deleteSponsorPosition(address sponsor) internal returns (FixedPoint.Unsigned memory) { PositionData storage positionToLiquidate = _getPositionData(sponsor); FixedPoint.Unsigned memory startingGlobalCollateral = _getFeeAdjustedCollateral(rawTotalPositionCollateral); // Remove the collateral and outstanding from the overall total position. FixedPoint.Unsigned memory remainingRawCollateral = positionToLiquidate.rawCollateral; rawTotalPositionCollateral = rawTotalPositionCollateral.sub(remainingRawCollateral); totalTokensOutstanding = totalTokensOutstanding.sub(positionToLiquidate.tokensOutstanding); // Reset the sponsors position to have zero outstanding and collateral. delete positions[sponsor]; emit EndedSponsorPosition(sponsor); // Return fee-adjusted amount of collateral deleted from position. return startingGlobalCollateral.sub(_getFeeAdjustedCollateral(rawTotalPositionCollateral)); } function _pfc() internal view virtual override returns (FixedPoint.Unsigned memory) { return _getFeeAdjustedCollateral(rawTotalPositionCollateral); } function _getPositionData(address sponsor) internal view onlyCollateralizedPosition(sponsor) returns (PositionData storage) { return positions[sponsor]; } function _getIdentifierWhitelist() internal view returns (IdentifierWhitelistInterface) { return IdentifierWhitelistInterface(finder.getImplementationAddress(OracleInterfaces.IdentifierWhitelist)); } function _getOracle() internal view returns (OracleInterface) { return OracleInterface(finder.getImplementationAddress(OracleInterfaces.Oracle)); } function _getOptimisticOracle() internal view returns (OptimisticOracleInterface) { return OptimisticOracleInterface(finder.getImplementationAddress(OracleInterfaces.OptimisticOracle)); } function _getFinancialContractsAdminAddress() internal view returns (address) { return finder.getImplementationAddress(OracleInterfaces.FinancialContractsAdmin); } // Requests a price for transformed `priceIdentifier` at `requestedTime` from the Oracle. function _requestOraclePriceExpiration(uint256 requestedTime) internal { OptimisticOracleInterface optimisticOracle = _getOptimisticOracle(); // Increase token allowance to enable the optimistic oracle reward transfer. FixedPoint.Unsigned memory reward = _computeFinalFees(); collateralCurrency.safeIncreaseAllowance(address(optimisticOracle), reward.rawValue); optimisticOracle.requestPrice( _transformPriceIdentifier(requestedTime), requestedTime, _getAncillaryData(), collateralCurrency, reward.rawValue // Reward is equal to the final fee ); // Apply haircut to all sponsors by decrementing the cumlativeFeeMultiplier by the amount lost from the final fee. _adjustCumulativeFeeMultiplier(reward, _pfc()); } // Fetches a resolved Oracle price from the Oracle. Reverts if the Oracle hasn't resolved for this request. function _getOraclePriceExpiration(uint256 requestedTime) internal returns (FixedPoint.Unsigned memory) { // Create an instance of the oracle and get the price. If the price is not resolved revert. OptimisticOracleInterface optimisticOracle = _getOptimisticOracle(); require( optimisticOracle.hasPrice( address(this), _transformPriceIdentifier(requestedTime), requestedTime, _getAncillaryData() ) ); int256 optimisticOraclePrice = optimisticOracle.settleAndGetPrice( _transformPriceIdentifier(requestedTime), requestedTime, _getAncillaryData() ); // For now we don't want to deal with negative prices in positions. if (optimisticOraclePrice < 0) { optimisticOraclePrice = 0; } return _transformPrice(FixedPoint.Unsigned(uint256(optimisticOraclePrice)), requestedTime); } // Requests a price for transformed `priceIdentifier` at `requestedTime` from the Oracle. function _requestOraclePriceLiquidation(uint256 requestedTime) internal { OracleInterface oracle = _getOracle(); oracle.requestPrice(_transformPriceIdentifier(requestedTime), requestedTime); } // Fetches a resolved Oracle price from the Oracle. Reverts if the Oracle hasn't resolved for this request. function _getOraclePriceLiquidation(uint256 requestedTime) internal view returns (FixedPoint.Unsigned memory) { // Create an instance of the oracle and get the price. If the price is not resolved revert. OracleInterface oracle = _getOracle(); require(oracle.hasPrice(_transformPriceIdentifier(requestedTime), requestedTime), "Unresolved oracle price"); int256 oraclePrice = oracle.getPrice(_transformPriceIdentifier(requestedTime), requestedTime); // For now we don't want to deal with negative prices in positions. if (oraclePrice < 0) { oraclePrice = 0; } return _transformPrice(FixedPoint.Unsigned(uint256(oraclePrice)), requestedTime); } // Reset withdrawal request by setting the withdrawal request and withdrawal timestamp to 0. function _resetWithdrawalRequest(PositionData storage positionData) internal { positionData.withdrawalRequestAmount = FixedPoint.fromUnscaledUint(0); positionData.withdrawalRequestPassTimestamp = 0; } // Ensure individual and global consistency when increasing collateral balances. Returns the change to the position. function _incrementCollateralBalances( PositionData storage positionData, FixedPoint.Unsigned memory collateralAmount ) internal returns (FixedPoint.Unsigned memory) { _addCollateral(positionData.rawCollateral, collateralAmount); return _addCollateral(rawTotalPositionCollateral, collateralAmount); } // Ensure individual and global consistency when decrementing collateral balances. Returns the change to the // position. We elect to return the amount that the global collateral is decreased by, rather than the individual // position's collateral, because we need to maintain the invariant that the global collateral is always // <= the collateral owned by the contract to avoid reverts on withdrawals. The amount returned = amount withdrawn. function _decrementCollateralBalances( PositionData storage positionData, FixedPoint.Unsigned memory collateralAmount ) internal returns (FixedPoint.Unsigned memory) { _removeCollateral(positionData.rawCollateral, collateralAmount); return _removeCollateral(rawTotalPositionCollateral, collateralAmount); } // Ensure individual and global consistency when decrementing collateral balances. Returns the change to the position. // This function is similar to the _decrementCollateralBalances function except this function checks position GCR // between the decrements. This ensures that collateral removal will not leave the position undercollateralized. function _decrementCollateralBalancesCheckGCR( PositionData storage positionData, FixedPoint.Unsigned memory collateralAmount ) internal returns (FixedPoint.Unsigned memory) { _removeCollateral(positionData.rawCollateral, collateralAmount); require(_checkPositionCollateralization(positionData), "CR below GCR"); return _removeCollateral(rawTotalPositionCollateral, collateralAmount); } // These internal functions are supposed to act identically to modifiers, but re-used modifiers // unnecessarily increase contract bytecode size. // source: https://blog.polymath.network/solidity-tips-and-tricks-to-save-gas-and-reduce-bytecode-size-c44580b218e6 function _onlyOpenState() internal view { require(contractState == ContractState.Open, "Contract state is not OPEN"); } function _onlyPreExpiration() internal view { require(getCurrentTime() < expirationTimestamp, "Only callable pre-expiry"); } function _onlyPostExpiration() internal view { require(getCurrentTime() >= expirationTimestamp, "Only callable post-expiry"); } function _onlyCollateralizedPosition(address sponsor) internal view { require( _getFeeAdjustedCollateral(positions[sponsor].rawCollateral).isGreaterThan(0), "Position has no collateral" ); } // Note: This checks whether an already existing position has a pending withdrawal. This cannot be used on the // `create` method because it is possible that `create` is called on a new position (i.e. one without any collateral // or tokens outstanding) which would fail the `onlyCollateralizedPosition` modifier on `_getPositionData`. function _positionHasNoPendingWithdrawal(address sponsor) internal view { require(_getPositionData(sponsor).withdrawalRequestPassTimestamp == 0, "Pending withdrawal"); } /**************************************** * PRIVATE FUNCTIONS * ****************************************/ function _checkPositionCollateralization(PositionData storage positionData) private view returns (bool) { return _checkCollateralization( _getFeeAdjustedCollateral(positionData.rawCollateral), positionData.tokensOutstanding ); } // Checks whether the provided `collateral` and `numTokens` have a collateralization ratio above the global // collateralization ratio. function _checkCollateralization(FixedPoint.Unsigned memory collateral, FixedPoint.Unsigned memory numTokens) private view returns (bool) { FixedPoint.Unsigned memory global = _getCollateralizationRatio(_getFeeAdjustedCollateral(rawTotalPositionCollateral), totalTokensOutstanding); FixedPoint.Unsigned memory thisChange = _getCollateralizationRatio(collateral, numTokens); return !global.isGreaterThan(thisChange); } function _getCollateralizationRatio(FixedPoint.Unsigned memory collateral, FixedPoint.Unsigned memory numTokens) private pure returns (FixedPoint.Unsigned memory ratio) { if (!numTokens.isGreaterThan(0)) { return FixedPoint.fromUnscaledUint(0); } else { return collateral.div(numTokens); } } // IERC20Standard.decimals() will revert if the collateral contract has not implemented the decimals() method, // which is possible since the method is only an OPTIONAL method in the ERC20 standard: // https://eips.ethereum.org/EIPS/eip-20#methods. function _getSyntheticDecimals(address _collateralAddress) public view returns (uint8 decimals) { try IERC20Standard(_collateralAddress).decimals() returns (uint8 _decimals) { return _decimals; } catch { return 18; } } function _transformPrice(FixedPoint.Unsigned memory price, uint256 requestTime) internal view returns (FixedPoint.Unsigned memory) { if (!address(financialProductLibrary).isContract()) return price; try financialProductLibrary.transformPrice(price, requestTime) returns ( FixedPoint.Unsigned memory transformedPrice ) { return transformedPrice; } catch { return price; } } function _transformPriceIdentifier(uint256 requestTime) internal view returns (bytes32) { if (!address(financialProductLibrary).isContract()) return priceIdentifier; try financialProductLibrary.transformPriceIdentifier(priceIdentifier, requestTime) returns ( bytes32 transformedIdentifier ) { return transformedIdentifier; } catch { return priceIdentifier; } } function _getAncillaryData() internal view returns (bytes memory) { // Note: when ancillary data is passed to the optimistic oracle, it should be tagged with the token address // whose funding rate it's trying to get. return abi.encodePacked(address(tokenCurrency)); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @title ERC20 interface that includes the decimals read only method. */ interface IERC20Standard is IERC20 { /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should be displayed to a user as `5,05` * (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between Ether and Wei. This is the value * {ERC20} uses, unless {_setupDecimals} is called. * * NOTE: This information is only used for _display_ purposes: it in no way affects any of the arithmetic * of the contract, including {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../../common/implementation/Lockable.sol"; import "../../common/implementation/FixedPoint.sol"; import "../../common/implementation/Testable.sol"; import "../../oracle/interfaces/StoreInterface.sol"; import "../../oracle/interfaces/FinderInterface.sol"; import "../../oracle/interfaces/AdministrateeInterface.sol"; import "../../oracle/implementation/Constants.sol"; /** * @title FeePayer contract. * @notice Provides fee payment functionality for the ExpiringMultiParty contract. * contract is abstract as each derived contract that inherits `FeePayer` must implement `pfc()`. */ abstract contract FeePayer is AdministrateeInterface, Testable, Lockable { using SafeMath for uint256; using FixedPoint for FixedPoint.Unsigned; using SafeERC20 for IERC20; /**************************************** * FEE PAYER DATA STRUCTURES * ****************************************/ // The collateral currency used to back the positions in this contract. IERC20 public collateralCurrency; // Finder contract used to look up addresses for UMA system contracts. FinderInterface public finder; // Tracks the last block time when the fees were paid. uint256 private lastPaymentTime; // Tracks the cumulative fees that have been paid by the contract for use by derived contracts. // The multiplier starts at 1, and is updated by computing cumulativeFeeMultiplier * (1 - effectiveFee). // Put another way, the cumulativeFeeMultiplier is (1 - effectiveFee1) * (1 - effectiveFee2) ... // For example: // The cumulativeFeeMultiplier should start at 1. // If a 1% fee is charged, the multiplier should update to .99. // If another 1% fee is charged, the multiplier should be 0.99^2 (0.9801). FixedPoint.Unsigned public cumulativeFeeMultiplier; /**************************************** * EVENTS * ****************************************/ event RegularFeesPaid(uint256 indexed regularFee, uint256 indexed lateFee); event FinalFeesPaid(uint256 indexed amount); /**************************************** * MODIFIERS * ****************************************/ // modifier that calls payRegularFees(). modifier fees virtual { // Note: the regular fee is applied on every fee-accruing transaction, where the total change is simply the // regular fee applied linearly since the last update. This implies that the compounding rate depends on the // frequency of update transactions that have this modifier, and it never reaches the ideal of continuous // compounding. This approximate-compounding pattern is common in the Ethereum ecosystem because of the // complexity of compounding data on-chain. payRegularFees(); _; } /** * @notice Constructs the FeePayer contract. Called by child contracts. * @param _collateralAddress ERC20 token that is used as the underlying collateral for the synthetic. * @param _finderAddress UMA protocol Finder used to discover other protocol contracts. * @param _timerAddress Contract that stores the current time in a testing environment. * Must be set to 0x0 for production environments that use live time. */ constructor( address _collateralAddress, address _finderAddress, address _timerAddress ) public Testable(_timerAddress) { collateralCurrency = IERC20(_collateralAddress); finder = FinderInterface(_finderAddress); lastPaymentTime = getCurrentTime(); cumulativeFeeMultiplier = FixedPoint.fromUnscaledUint(1); } /**************************************** * FEE PAYMENT FUNCTIONS * ****************************************/ /** * @notice Pays UMA DVM regular fees (as a % of the collateral pool) to the Store contract. * @dev These must be paid periodically for the life of the contract. If the contract has not paid its regular fee * in a week or more then a late penalty is applied which is sent to the caller. If the amount of * fees owed are greater than the pfc, then this will pay as much as possible from the available collateral. * An event is only fired if the fees charged are greater than 0. * @return totalPaid Amount of collateral that the contract paid (sum of the amount paid to the Store and caller). * This returns 0 and exit early if there is no pfc, fees were already paid during the current block, or the fee rate is 0. */ function payRegularFees() public nonReentrant() returns (FixedPoint.Unsigned memory) { uint256 time = getCurrentTime(); FixedPoint.Unsigned memory collateralPool = _pfc(); // Fetch the regular fees, late penalty and the max possible to pay given the current collateral within the contract. ( FixedPoint.Unsigned memory regularFee, FixedPoint.Unsigned memory latePenalty, FixedPoint.Unsigned memory totalPaid ) = getOutstandingRegularFees(time); lastPaymentTime = time; // If there are no fees to pay then exit early. if (totalPaid.isEqual(0)) { return totalPaid; } emit RegularFeesPaid(regularFee.rawValue, latePenalty.rawValue); _adjustCumulativeFeeMultiplier(totalPaid, collateralPool); if (regularFee.isGreaterThan(0)) { StoreInterface store = _getStore(); collateralCurrency.safeIncreaseAllowance(address(store), regularFee.rawValue); store.payOracleFeesErc20(address(collateralCurrency), regularFee); } if (latePenalty.isGreaterThan(0)) { collateralCurrency.safeTransfer(msg.sender, latePenalty.rawValue); } return totalPaid; } /** * @notice Fetch any regular fees that the contract has pending but has not yet paid. If the fees to be paid are more * than the total collateral within the contract then the totalPaid returned is full contract collateral amount. * @dev This returns 0 and exit early if there is no pfc, fees were already paid during the current block, or the fee rate is 0. * @return regularFee outstanding unpaid regular fee. * @return latePenalty outstanding unpaid late fee for being late in previous fee payments. * @return totalPaid Amount of collateral that the contract paid (sum of the amount paid to the Store and caller). */ function getOutstandingRegularFees(uint256 time) public view returns ( FixedPoint.Unsigned memory regularFee, FixedPoint.Unsigned memory latePenalty, FixedPoint.Unsigned memory totalPaid ) { StoreInterface store = _getStore(); FixedPoint.Unsigned memory collateralPool = _pfc(); // Exit early if there is no collateral or if fees were already paid during this block. if (collateralPool.isEqual(0) || lastPaymentTime == time) { return (regularFee, latePenalty, totalPaid); } (regularFee, latePenalty) = store.computeRegularFee(lastPaymentTime, time, collateralPool); totalPaid = regularFee.add(latePenalty); if (totalPaid.isEqual(0)) { return (regularFee, latePenalty, totalPaid); } // If the effective fees paid as a % of the pfc is > 100%, then we need to reduce it and make the contract pay // as much of the fee that it can (up to 100% of its pfc). We'll reduce the late penalty first and then the // regular fee, which has the effect of paying the store first, followed by the caller if there is any fee remaining. if (totalPaid.isGreaterThan(collateralPool)) { FixedPoint.Unsigned memory deficit = totalPaid.sub(collateralPool); FixedPoint.Unsigned memory latePenaltyReduction = FixedPoint.min(latePenalty, deficit); latePenalty = latePenalty.sub(latePenaltyReduction); deficit = deficit.sub(latePenaltyReduction); regularFee = regularFee.sub(FixedPoint.min(regularFee, deficit)); totalPaid = collateralPool; } } /** * @notice Gets the current profit from corruption for this contract in terms of the collateral currency. * @dev This is equivalent to the collateral pool available from which to pay fees. Therefore, derived contracts are * expected to implement this so that pay-fee methods can correctly compute the owed fees as a % of PfC. * @return pfc value for equal to the current profit from corruption denominated in collateral currency. */ function pfc() external view override nonReentrantView() returns (FixedPoint.Unsigned memory) { return _pfc(); } /** * @notice Removes excess collateral balance not counted in the PfC by distributing it out pro-rata to all sponsors. * @dev Multiplying the `cumulativeFeeMultiplier` by the ratio of non-PfC-collateral :: PfC-collateral effectively * pays all sponsors a pro-rata portion of the excess collateral. * @dev This will revert if PfC is 0 and this contract's collateral balance > 0. */ function gulp() external nonReentrant() { _gulp(); } /**************************************** * INTERNAL FUNCTIONS * ****************************************/ // Pays UMA Oracle final fees of `amount` in `collateralCurrency` to the Store contract. Final fee is a flat fee // charged for each price request. If payer is the contract, adjusts internal bookkeeping variables. If payer is not // the contract, pulls in `amount` of collateral currency. function _payFinalFees(address payer, FixedPoint.Unsigned memory amount) internal { if (amount.isEqual(0)) { return; } if (payer != address(this)) { // If the payer is not the contract pull the collateral from the payer. collateralCurrency.safeTransferFrom(payer, address(this), amount.rawValue); } else { // If the payer is the contract, adjust the cumulativeFeeMultiplier to compensate. FixedPoint.Unsigned memory collateralPool = _pfc(); // The final fee must be < available collateral or the fee will be larger than 100%. // Note: revert reason removed to save bytecode. require(collateralPool.isGreaterThan(amount)); _adjustCumulativeFeeMultiplier(amount, collateralPool); } emit FinalFeesPaid(amount.rawValue); StoreInterface store = _getStore(); collateralCurrency.safeIncreaseAllowance(address(store), amount.rawValue); store.payOracleFeesErc20(address(collateralCurrency), amount); } function _gulp() internal { FixedPoint.Unsigned memory currentPfc = _pfc(); FixedPoint.Unsigned memory currentBalance = FixedPoint.Unsigned(collateralCurrency.balanceOf(address(this))); if (currentPfc.isLessThan(currentBalance)) { cumulativeFeeMultiplier = cumulativeFeeMultiplier.mul(currentBalance.div(currentPfc)); } } function _pfc() internal view virtual returns (FixedPoint.Unsigned memory); function _getStore() internal view returns (StoreInterface) { return StoreInterface(finder.getImplementationAddress(OracleInterfaces.Store)); } function _computeFinalFees() internal view returns (FixedPoint.Unsigned memory finalFees) { StoreInterface store = _getStore(); return store.computeFinalFee(address(collateralCurrency)); } // Returns the user's collateral minus any fees that have been subtracted since it was originally // deposited into the contract. Note: if the contract has paid fees since it was deployed, the raw // value should be larger than the returned value. function _getFeeAdjustedCollateral(FixedPoint.Unsigned memory rawCollateral) internal view returns (FixedPoint.Unsigned memory collateral) { return rawCollateral.mul(cumulativeFeeMultiplier); } // Returns the user's collateral minus any pending fees that have yet to be subtracted. function _getPendingRegularFeeAdjustedCollateral(FixedPoint.Unsigned memory rawCollateral) internal view returns (FixedPoint.Unsigned memory) { (, , FixedPoint.Unsigned memory currentTotalOutstandingRegularFees) = getOutstandingRegularFees(getCurrentTime()); if (currentTotalOutstandingRegularFees.isEqual(FixedPoint.fromUnscaledUint(0))) return rawCollateral; // Calculate the total outstanding regular fee as a fraction of the total contract PFC. FixedPoint.Unsigned memory effectiveOutstandingFee = currentTotalOutstandingRegularFees.divCeil(_pfc()); // Scale as rawCollateral* (1 - effectiveOutstandingFee) to apply the pro-rata amount to the regular fee. return rawCollateral.mul(FixedPoint.fromUnscaledUint(1).sub(effectiveOutstandingFee)); } // Converts a user-readable collateral value into a raw value that accounts for already-assessed fees. If any fees // have been taken from this contract in the past, then the raw value will be larger than the user-readable value. function _convertToRawCollateral(FixedPoint.Unsigned memory collateral) internal view returns (FixedPoint.Unsigned memory rawCollateral) { return collateral.div(cumulativeFeeMultiplier); } // Decrease rawCollateral by a fee-adjusted collateralToRemove amount. Fee adjustment scales up collateralToRemove // by dividing it by cumulativeFeeMultiplier. There is potential for this quotient to be floored, therefore // rawCollateral is decreased by less than expected. Because this method is usually called in conjunction with an // actual removal of collateral from this contract, return the fee-adjusted amount that the rawCollateral is // decreased by so that the caller can minimize error between collateral removed and rawCollateral debited. function _removeCollateral(FixedPoint.Unsigned storage rawCollateral, FixedPoint.Unsigned memory collateralToRemove) internal returns (FixedPoint.Unsigned memory removedCollateral) { FixedPoint.Unsigned memory initialBalance = _getFeeAdjustedCollateral(rawCollateral); FixedPoint.Unsigned memory adjustedCollateral = _convertToRawCollateral(collateralToRemove); rawCollateral.rawValue = rawCollateral.sub(adjustedCollateral).rawValue; removedCollateral = initialBalance.sub(_getFeeAdjustedCollateral(rawCollateral)); } // Increase rawCollateral by a fee-adjusted collateralToAdd amount. Fee adjustment scales up collateralToAdd // by dividing it by cumulativeFeeMultiplier. There is potential for this quotient to be floored, therefore // rawCollateral is increased by less than expected. Because this method is usually called in conjunction with an // actual addition of collateral to this contract, return the fee-adjusted amount that the rawCollateral is // increased by so that the caller can minimize error between collateral added and rawCollateral credited. // NOTE: This return value exists only for the sake of symmetry with _removeCollateral. We don't actually use it // because we are OK if more collateral is stored in the contract than is represented by rawTotalPositionCollateral. function _addCollateral(FixedPoint.Unsigned storage rawCollateral, FixedPoint.Unsigned memory collateralToAdd) internal returns (FixedPoint.Unsigned memory addedCollateral) { FixedPoint.Unsigned memory initialBalance = _getFeeAdjustedCollateral(rawCollateral); FixedPoint.Unsigned memory adjustedCollateral = _convertToRawCollateral(collateralToAdd); rawCollateral.rawValue = rawCollateral.add(adjustedCollateral).rawValue; addedCollateral = _getFeeAdjustedCollateral(rawCollateral).sub(initialBalance); } // Scale the cumulativeFeeMultiplier by the ratio of fees paid to the current available collateral. function _adjustCumulativeFeeMultiplier(FixedPoint.Unsigned memory amount, FixedPoint.Unsigned memory currentPfc) internal { FixedPoint.Unsigned memory effectiveFee = amount.divCeil(currentPfc); cumulativeFeeMultiplier = cumulativeFeeMultiplier.mul(FixedPoint.fromUnscaledUint(1).sub(effectiveFee)); } }
pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../../common/implementation/FixedPoint.sol"; interface ExpiringContractInterface { function expirationTimestamp() external view returns (uint256); } /** * @title Financial product library contract * @notice Provides price and collateral requirement transformation interfaces that can be overridden by custom * Financial product library implementations. */ abstract contract FinancialProductLibrary { using FixedPoint for FixedPoint.Unsigned; /** * @notice Transforms a given oracle price using the financial product libraries transformation logic. * @param oraclePrice input price returned by the DVM to be transformed. * @param requestTime timestamp the oraclePrice was requested at. * @return transformedOraclePrice input oraclePrice with the transformation function applied. */ function transformPrice(FixedPoint.Unsigned memory oraclePrice, uint256 requestTime) public view virtual returns (FixedPoint.Unsigned memory) { return oraclePrice; } /** * @notice Transforms a given collateral requirement using the financial product libraries transformation logic. * @param oraclePrice input price returned by DVM used to transform the collateral requirement. * @param collateralRequirement input collateral requirement to be transformed. * @return transformedCollateralRequirement input collateral requirement with the transformation function applied. */ function transformCollateralRequirement( FixedPoint.Unsigned memory oraclePrice, FixedPoint.Unsigned memory collateralRequirement ) public view virtual returns (FixedPoint.Unsigned memory) { return collateralRequirement; } /** * @notice Transforms a given price identifier using the financial product libraries transformation logic. * @param priceIdentifier input price identifier defined for the financial contract. * @param requestTime timestamp the identifier is to be used at. EG the time that a price request would be sent using this identifier. * @return transformedPriceIdentifier input price identifier with the transformation function applied. */ function transformPriceIdentifier(bytes32 priceIdentifier, uint256 requestTime) public view virtual returns (bytes32) { return priceIdentifier; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/FixedPoint.sol"; /** * @title Interface that all financial contracts expose to the admin. */ interface AdministrateeInterface { /** * @notice Initiates the shutdown process, in case of an emergency. */ function emergencyShutdown() external; /** * @notice A core contract method called independently or as a part of other financial contract transactions. * @dev It pays fees and moves money between margin accounts to make sure they reflect the NAV of the contract. */ function remargin() external; /** * @notice Gets the current profit from corruption for this contract in terms of the collateral currency. * @dev This is equivalent to the collateral pool available from which to pay fees. Therefore, derived contracts are * expected to implement this so that pay-fee methods can correctly compute the owed fees as a % of PfC. * @return pfc value for equal to the current profit from corruption denominated in collateral currency. */ function pfc() external view returns (FixedPoint.Unsigned memory); }
pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../common/financial-product-libraries/FinancialProductLibrary.sol"; // Implements a simple FinancialProductLibrary to test price and collateral requirement transoformations. contract FinancialProductLibraryTest is FinancialProductLibrary { FixedPoint.Unsigned public priceTransformationScalar; FixedPoint.Unsigned public collateralRequirementTransformationScalar; bytes32 public transformedPriceIdentifier; bool public shouldRevert; constructor( FixedPoint.Unsigned memory _priceTransformationScalar, FixedPoint.Unsigned memory _collateralRequirementTransformationScalar, bytes32 _transformedPriceIdentifier ) public { priceTransformationScalar = _priceTransformationScalar; collateralRequirementTransformationScalar = _collateralRequirementTransformationScalar; transformedPriceIdentifier = _transformedPriceIdentifier; } // Set the mocked methods to revert to test failed library computation. function setShouldRevert(bool _shouldRevert) public { shouldRevert = _shouldRevert; } // Create a simple price transformation function that scales the input price by the scalar for testing. function transformPrice(FixedPoint.Unsigned memory oraclePrice, uint256 requestTime) public view override returns (FixedPoint.Unsigned memory) { require(!shouldRevert, "set to always reverts"); return oraclePrice.mul(priceTransformationScalar); } // Create a simple collateral requirement transformation that doubles the input collateralRequirement. function transformCollateralRequirement( FixedPoint.Unsigned memory price, FixedPoint.Unsigned memory collateralRequirement ) public view override returns (FixedPoint.Unsigned memory) { require(!shouldRevert, "set to always reverts"); return collateralRequirement.mul(collateralRequirementTransformationScalar); } // Create a simple transformPriceIdentifier function that returns the transformed price identifier. function transformPriceIdentifier(bytes32 priceIdentifier, uint256 requestTime) public view override returns (bytes32) { require(!shouldRevert, "set to always reverts"); return transformedPriceIdentifier; } }
pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/Testable.sol"; import "../../common/implementation/FixedPoint.sol"; import "../common/financial-product-libraries/FinancialProductLibrary.sol"; contract ExpiringMultiPartyMock is Testable { using FixedPoint for FixedPoint.Unsigned; FinancialProductLibrary public financialProductLibrary; uint256 public expirationTimestamp; FixedPoint.Unsigned public collateralRequirement; bytes32 public priceIdentifier; constructor( address _financialProductLibraryAddress, uint256 _expirationTimestamp, FixedPoint.Unsigned memory _collateralRequirement, bytes32 _priceIdentifier, address _timerAddress ) public Testable(_timerAddress) { expirationTimestamp = _expirationTimestamp; collateralRequirement = _collateralRequirement; financialProductLibrary = FinancialProductLibrary(_financialProductLibraryAddress); priceIdentifier = _priceIdentifier; } function transformPrice(FixedPoint.Unsigned memory price, uint256 requestTime) public view returns (FixedPoint.Unsigned memory) { if (address(financialProductLibrary) == address(0)) return price; try financialProductLibrary.transformPrice(price, requestTime) returns ( FixedPoint.Unsigned memory transformedPrice ) { return transformedPrice; } catch { return price; } } function transformCollateralRequirement(FixedPoint.Unsigned memory price) public view returns (FixedPoint.Unsigned memory) { if (address(financialProductLibrary) == address(0)) return collateralRequirement; try financialProductLibrary.transformCollateralRequirement(price, collateralRequirement) returns ( FixedPoint.Unsigned memory transformedCollateralRequirement ) { return transformedCollateralRequirement; } catch { return collateralRequirement; } } function transformPriceIdentifier(uint256 requestTime) public view returns (bytes32) { if (address(financialProductLibrary) == address(0)) return priceIdentifier; try financialProductLibrary.transformPriceIdentifier(priceIdentifier, requestTime) returns ( bytes32 transformedIdentifier ) { return transformedIdentifier; } catch { return priceIdentifier; } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/FixedPoint.sol"; import "../../common/implementation/Testable.sol"; import "../interfaces/OracleAncillaryInterface.sol"; import "../interfaces/VotingAncillaryInterface.sol"; // A mock oracle used for testing. Exports the voting & oracle interfaces and events that contain ancillary data. abstract contract VotingAncillaryInterfaceTesting is OracleAncillaryInterface, VotingAncillaryInterface, Testable { using FixedPoint for FixedPoint.Unsigned; // Events, data structures and functions not exported in the base interfaces, used for testing. event VoteCommitted( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, bytes ancillaryData ); event EncryptedVote( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, bytes ancillaryData, bytes encryptedVote ); event VoteRevealed( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, int256 price, bytes ancillaryData, uint256 numTokens ); event RewardsRetrieved( address indexed voter, uint256 indexed roundId, bytes32 indexed identifier, uint256 time, uint256 numTokens ); event PriceRequestAdded(uint256 indexed roundId, bytes32 indexed identifier, uint256 time); event PriceResolved( uint256 indexed roundId, bytes32 indexed identifier, uint256 time, int256 price, bytes ancillaryData ); struct Round { uint256 snapshotId; // Voting token snapshot ID for this round. 0 if no snapshot has been taken. FixedPoint.Unsigned inflationRate; // Inflation rate set for this round. FixedPoint.Unsigned gatPercentage; // Gat rate set for this round. uint256 rewardsExpirationTime; // Time that rewards for this round can be claimed until. } // Represents the status a price request has. enum RequestStatus { NotRequested, // Was never requested. Active, // Is being voted on in the current round. Resolved, // Was resolved in a previous round. Future // Is scheduled to be voted on in a future round. } // Only used as a return value in view methods -- never stored in the contract. struct RequestState { RequestStatus status; uint256 lastVotingRound; } function rounds(uint256 roundId) public view virtual returns (Round memory); function getPriceRequestStatuses(VotingAncillaryInterface.PendingRequestAncillary[] memory requests) public view virtual returns (RequestState[] memory); function getPendingPriceRequestsArray() external view virtual returns (bytes32[] memory); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/MultiRole.sol"; import "../../common/implementation/Withdrawable.sol"; import "../interfaces/VotingAncillaryInterface.sol"; import "../interfaces/FinderInterface.sol"; import "./Constants.sol"; /** * @title Proxy to allow voting from another address. * @dev Allows a UMA token holder to designate another address to vote on their behalf. * Each voter must deploy their own instance of this contract. */ contract DesignatedVoting is Withdrawable { /**************************************** * INTERNAL VARIABLES AND STORAGE * ****************************************/ enum Roles { Owner, // Can set the Voter role. Is also permanently permissioned as the minter role. Voter // Can vote through this contract. } // Reference to the UMA Finder contract, allowing Voting upgrades to be performed // without requiring any calls to this contract. FinderInterface private finder; /** * @notice Construct the DesignatedVoting contract. * @param finderAddress keeps track of all contracts within the system based on their interfaceName. * @param ownerAddress address of the owner of the DesignatedVoting contract. * @param voterAddress address to which the owner has delegated their voting power. */ constructor( address finderAddress, address ownerAddress, address voterAddress ) public { _createExclusiveRole(uint256(Roles.Owner), uint256(Roles.Owner), ownerAddress); _createExclusiveRole(uint256(Roles.Voter), uint256(Roles.Owner), voterAddress); _setWithdrawRole(uint256(Roles.Owner)); finder = FinderInterface(finderAddress); } /**************************************** * VOTING AND REWARD FUNCTIONALITY * ****************************************/ /** * @notice Forwards a commit to Voting. * @param identifier uniquely identifies the feed for this vote. EG BTC/USD price pair. * @param time specifies the unix timestamp of the price being voted on. * @param hash the keccak256 hash of the price you want to vote for and a random integer salt value. */ function commitVote( bytes32 identifier, uint256 time, bytes memory ancillaryData, bytes32 hash ) external onlyRoleHolder(uint256(Roles.Voter)) { _getVotingAddress().commitVote(identifier, time, ancillaryData, hash); } /** * @notice Forwards a batch commit to Voting. * @param commits struct to encapsulate an `identifier`, `time`, `hash` and optional `encryptedVote`. */ function batchCommit(VotingAncillaryInterface.CommitmentAncillary[] calldata commits) external onlyRoleHolder(uint256(Roles.Voter)) { _getVotingAddress().batchCommit(commits); } /** * @notice Forwards a reveal to Voting. * @param identifier voted on in the commit phase. EG BTC/USD price pair. * @param time specifies the unix timestamp of the price being voted on. * @param price used along with the `salt` to produce the `hash` during the commit phase. * @param salt used along with the `price` to produce the `hash` during the commit phase. */ function revealVote( bytes32 identifier, uint256 time, int256 price, bytes memory ancillaryData, int256 salt ) external onlyRoleHolder(uint256(Roles.Voter)) { _getVotingAddress().revealVote(identifier, time, price, ancillaryData, salt); } /** * @notice Forwards a batch reveal to Voting. * @param reveals is an array of the Reveal struct which contains an identifier, time, price and salt. */ function batchReveal(VotingAncillaryInterface.RevealAncillary[] calldata reveals) external onlyRoleHolder(uint256(Roles.Voter)) { _getVotingAddress().batchReveal(reveals); } /** * @notice Forwards a reward retrieval to Voting. * @dev Rewards are added to the tokens already held by this contract. * @param roundId defines the round from which voting rewards will be retrieved from. * @param toRetrieve an array of PendingRequests which rewards are retrieved from. * @return amount of rewards that the user should receive. */ function retrieveRewards(uint256 roundId, VotingAncillaryInterface.PendingRequestAncillary[] memory toRetrieve) public onlyRoleHolder(uint256(Roles.Voter)) returns (FixedPoint.Unsigned memory) { return _getVotingAddress().retrieveRewards(address(this), roundId, toRetrieve); } function _getVotingAddress() private view returns (VotingAncillaryInterface) { return VotingAncillaryInterface(finder.getImplementationAddress(OracleInterfaces.Oracle)); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/Withdrawable.sol"; import "./DesignatedVoting.sol"; /** * @title Factory to deploy new instances of DesignatedVoting and look up previously deployed instances. * @dev Allows off-chain infrastructure to look up a hot wallet's deployed DesignatedVoting contract. */ contract DesignatedVotingFactory is Withdrawable { /**************************************** * INTERNAL VARIABLES AND STORAGE * ****************************************/ enum Roles { Withdrawer // Can withdraw any ETH or ERC20 sent accidentally to this contract. } address private finder; mapping(address => DesignatedVoting) public designatedVotingContracts; /** * @notice Construct the DesignatedVotingFactory contract. * @param finderAddress keeps track of all contracts within the system based on their interfaceName. */ constructor(address finderAddress) public { finder = finderAddress; _createWithdrawRole(uint256(Roles.Withdrawer), uint256(Roles.Withdrawer), msg.sender); } /** * @notice Deploys a new `DesignatedVoting` contract. * @param ownerAddress defines who will own the deployed instance of the designatedVoting contract. * @return designatedVoting a new DesignatedVoting contract. */ function newDesignatedVoting(address ownerAddress) external returns (DesignatedVoting) { require(address(designatedVotingContracts[msg.sender]) == address(0), "Duplicate hot key not permitted"); DesignatedVoting designatedVoting = new DesignatedVoting(finder, ownerAddress, msg.sender); designatedVotingContracts[msg.sender] = designatedVoting; return designatedVoting; } /** * @notice Associates a `DesignatedVoting` instance with `msg.sender`. * @param designatedVotingAddress address to designate voting to. * @dev This is generally only used if the owner of a `DesignatedVoting` contract changes their `voter` * address and wants that reflected here. */ function setDesignatedVoting(address designatedVotingAddress) external { require(address(designatedVotingContracts[msg.sender]) == address(0), "Duplicate hot key not permitted"); designatedVotingContracts[msg.sender] = DesignatedVoting(designatedVotingAddress); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../implementation/Withdrawable.sol"; // WithdrawableTest is derived from the abstract contract Withdrawable for testing purposes. contract WithdrawableTest is Withdrawable { enum Roles { Governance, Withdraw } // solhint-disable-next-line no-empty-blocks constructor() public { _createExclusiveRole(uint256(Roles.Governance), uint256(Roles.Governance), msg.sender); _createWithdrawRole(uint256(Roles.Withdraw), uint256(Roles.Governance), msg.sender); } function pay() external payable { require(msg.value > 0); } function setInternalWithdrawRole(uint256 setRoleId) public { _setWithdrawRole(setRoleId); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; /** * @title An implementation of ERC20 with the same interface as the Compound project's testnet tokens (mainly DAI) * @dev This contract can be deployed or the interface can be used to communicate with Compound's ERC20 tokens. Note: * this token should never be used to store real value since it allows permissionless minting. */ contract TestnetERC20 is ERC20 { /** * @notice Constructs the TestnetERC20. * @param _name The name which describes the new token. * @param _symbol The ticker abbreviation of the name. Ideally < 5 chars. * @param _decimals The number of decimals to define token precision. */ constructor( string memory _name, string memory _symbol, uint8 _decimals ) public ERC20(_name, _symbol) { _setupDecimals(_decimals); } // Sample token information. /** * @notice Mints value tokens to the owner address. * @param ownerAddress the address to mint to. * @param value the amount of tokens to mint. */ function allocateTo(address ownerAddress, uint256 value) external { _mint(ownerAddress, value); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../interfaces/IdentifierWhitelistInterface.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; /** * @title Stores a whitelist of supported identifiers that the oracle can provide prices for. */ contract IdentifierWhitelist is IdentifierWhitelistInterface, Ownable { /**************************************** * INTERNAL VARIABLES AND STORAGE * ****************************************/ mapping(bytes32 => bool) private supportedIdentifiers; /**************************************** * EVENTS * ****************************************/ event SupportedIdentifierAdded(bytes32 indexed identifier); event SupportedIdentifierRemoved(bytes32 indexed identifier); /**************************************** * ADMIN STATE MODIFYING FUNCTIONS * ****************************************/ /** * @notice Adds the provided identifier as a supported identifier. * @dev Price requests using this identifier will succeed after this call. * @param identifier unique UTF-8 representation for the feed being added. Eg: BTC/USD. */ function addSupportedIdentifier(bytes32 identifier) external override onlyOwner { if (!supportedIdentifiers[identifier]) { supportedIdentifiers[identifier] = true; emit SupportedIdentifierAdded(identifier); } } /** * @notice Removes the identifier from the whitelist. * @dev Price requests using this identifier will no longer succeed after this call. * @param identifier unique UTF-8 representation for the feed being removed. Eg: BTC/USD. */ function removeSupportedIdentifier(bytes32 identifier) external override onlyOwner { if (supportedIdentifiers[identifier]) { supportedIdentifiers[identifier] = false; emit SupportedIdentifierRemoved(identifier); } } /**************************************** * WHITELIST GETTERS FUNCTIONS * ****************************************/ /** * @notice Checks whether an identifier is on the whitelist. * @param identifier unique UTF-8 representation for the feed being queried. Eg: BTC/USD. * @return bool if the identifier is supported (or not). */ function isIdentifierSupported(bytes32 identifier) external view override returns (bool) { return supportedIdentifiers[identifier]; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../interfaces/AdministrateeInterface.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; /** * @title Admin for financial contracts in the UMA system. * @dev Allows appropriately permissioned admin roles to interact with financial contracts. */ contract FinancialContractsAdmin is Ownable { /** * @notice Calls emergency shutdown on the provided financial contract. * @param financialContract address of the FinancialContract to be shut down. */ function callEmergencyShutdown(address financialContract) external onlyOwner { AdministrateeInterface administratee = AdministrateeInterface(financialContract); administratee.emergencyShutdown(); } /** * @notice Calls remargin on the provided financial contract. * @param financialContract address of the FinancialContract to be remargined. */ function callRemargin(address financialContract) external onlyOwner { AdministrateeInterface administratee = AdministrateeInterface(financialContract); administratee.remargin(); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../interfaces/AdministrateeInterface.sol"; // A mock implementation of AdministrateeInterface, taking the place of a financial contract. contract MockAdministratee is AdministrateeInterface { uint256 public timesRemargined; uint256 public timesEmergencyShutdown; function remargin() external override { timesRemargined++; } function emergencyShutdown() external override { timesEmergencyShutdown++; } function pfc() external view override returns (FixedPoint.Unsigned memory) { return FixedPoint.fromUnscaledUint(0); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../common/FeePayer.sol"; import "../../common/implementation/FixedPoint.sol"; import "../../oracle/interfaces/IdentifierWhitelistInterface.sol"; import "../../oracle/interfaces/OracleInterface.sol"; import "../../oracle/implementation/ContractCreator.sol"; /** * @title Token Deposit Box * @notice This is a minimal example of a financial template that depends on price requests from the DVM. * This contract should be thought of as a "Deposit Box" into which the user deposits some ERC20 collateral. * The main feature of this box is that the user can withdraw their ERC20 corresponding to a desired USD amount. * When the user wants to make a withdrawal, a price request is enqueued with the UMA DVM. * For simplicty, the user is constrained to have one outstanding withdrawal request at any given time. * Regular fees are charged on the collateral in the deposit box throughout the lifetime of the deposit box, * and final fees are charged on each price request. * * This example is intended to accompany a technical tutorial for how to integrate the DVM into a project. * The main feature this demo serves to showcase is how to build a financial product on-chain that "pulls" price * requests from the DVM on-demand, which is an implementation of the "priceless" oracle framework. * * The typical user flow would be: * - User sets up a deposit box for the (wETH - USD) price-identifier. The "collateral currency" in this deposit * box is therefore wETH. * The user can subsequently make withdrawal requests for USD-denominated amounts of wETH. * - User deposits 10 wETH into their deposit box. * - User later requests to withdraw $100 USD of wETH. * - DepositBox asks DVM for latest wETH/USD exchange rate. * - DVM resolves the exchange rate at: 1 wETH is worth 200 USD. * - DepositBox transfers 0.5 wETH to user. */ contract DepositBox is FeePayer, ContractCreator { using SafeMath for uint256; using FixedPoint for FixedPoint.Unsigned; using SafeERC20 for IERC20; // Represents a single caller's deposit box. All collateral is held by this contract. struct DepositBoxData { // Requested amount of collateral, denominated in quote asset of the price identifier. // Example: If the price identifier is wETH-USD, and the `withdrawalRequestAmount = 100`, then // this represents a withdrawal request for 100 USD worth of wETH. FixedPoint.Unsigned withdrawalRequestAmount; // Timestamp of the latest withdrawal request. A withdrawal request is pending if `requestPassTimestamp != 0`. uint256 requestPassTimestamp; // Raw collateral value. This value should never be accessed directly -- always use _getFeeAdjustedCollateral(). // To add or remove collateral, use _addCollateral() and _removeCollateral(). FixedPoint.Unsigned rawCollateral; } // Maps addresses to their deposit boxes. Each address can have only one position. mapping(address => DepositBoxData) private depositBoxes; // Unique identifier for DVM price feed ticker. bytes32 private priceIdentifier; // Similar to the rawCollateral in DepositBoxData, this value should not be used directly. // _getFeeAdjustedCollateral(), _addCollateral() and _removeCollateral() must be used to access and adjust. FixedPoint.Unsigned private rawTotalDepositBoxCollateral; // This blocks every public state-modifying method until it flips to true, via the `initialize()` method. bool private initialized; /**************************************** * EVENTS * ****************************************/ event NewDepositBox(address indexed user); event EndedDepositBox(address indexed user); event Deposit(address indexed user, uint256 indexed collateralAmount); event RequestWithdrawal(address indexed user, uint256 indexed collateralAmount, uint256 requestPassTimestamp); event RequestWithdrawalExecuted( address indexed user, uint256 indexed collateralAmount, uint256 exchangeRate, uint256 requestPassTimestamp ); event RequestWithdrawalCanceled( address indexed user, uint256 indexed collateralAmount, uint256 requestPassTimestamp ); /**************************************** * MODIFIERS * ****************************************/ modifier noPendingWithdrawal(address user) { _depositBoxHasNoPendingWithdrawal(user); _; } modifier isInitialized() { _isInitialized(); _; } /**************************************** * PUBLIC FUNCTIONS * ****************************************/ /** * @notice Construct the DepositBox. * @param _collateralAddress ERC20 token to be deposited. * @param _finderAddress UMA protocol Finder used to discover other protocol contracts. * @param _priceIdentifier registered in the DVM, used to price the ERC20 deposited. * The price identifier consists of a "base" asset and a "quote" asset. The "base" asset corresponds to the collateral ERC20 * currency deposited into this account, and it is denominated in the "quote" asset on withdrawals. * An example price identifier would be "ETH-USD" which will resolve and return the USD price of ETH. * @param _timerAddress Contract that stores the current time in a testing environment. * Must be set to 0x0 for production environments that use live time. */ constructor( address _collateralAddress, address _finderAddress, bytes32 _priceIdentifier, address _timerAddress ) public ContractCreator(_finderAddress) FeePayer(_collateralAddress, _finderAddress, _timerAddress) nonReentrant() { require(_getIdentifierWhitelist().isIdentifierSupported(_priceIdentifier), "Unsupported price identifier"); priceIdentifier = _priceIdentifier; } /** * @notice This should be called after construction of the DepositBox and handles registration with the Registry, which is required * to make price requests in production environments. * @dev This contract must hold the `ContractCreator` role with the Registry in order to register itself as a financial-template with the DVM. * Note that `_registerContract` cannot be called from the constructor because this contract first needs to be given the `ContractCreator` role * in order to register with the `Registry`. But, its address is not known until after deployment. */ function initialize() public nonReentrant() { initialized = true; _registerContract(new address[](0), address(this)); } /** * @notice Transfers `collateralAmount` of `collateralCurrency` into caller's deposit box. * @dev This contract must be approved to spend at least `collateralAmount` of `collateralCurrency`. * @param collateralAmount total amount of collateral tokens to be sent to the sponsor's position. */ function deposit(FixedPoint.Unsigned memory collateralAmount) public isInitialized() fees() nonReentrant() { require(collateralAmount.isGreaterThan(0), "Invalid collateral amount"); DepositBoxData storage depositBoxData = depositBoxes[msg.sender]; if (_getFeeAdjustedCollateral(depositBoxData.rawCollateral).isEqual(0)) { emit NewDepositBox(msg.sender); } // Increase the individual deposit box and global collateral balance by collateral amount. _incrementCollateralBalances(depositBoxData, collateralAmount); emit Deposit(msg.sender, collateralAmount.rawValue); // Move collateral currency from sender to contract. collateralCurrency.safeTransferFrom(msg.sender, address(this), collateralAmount.rawValue); } /** * @notice Starts a withdrawal request that allows the sponsor to withdraw `denominatedCollateralAmount` * from their position denominated in the quote asset of the price identifier, following a DVM price resolution. * @dev The request will be pending for the duration of the DVM vote and can be cancelled at any time. * Only one withdrawal request can exist for the user. * @param denominatedCollateralAmount the quote-asset denominated amount of collateral requested to withdraw. */ function requestWithdrawal(FixedPoint.Unsigned memory denominatedCollateralAmount) public isInitialized() noPendingWithdrawal(msg.sender) nonReentrant() { DepositBoxData storage depositBoxData = depositBoxes[msg.sender]; require(denominatedCollateralAmount.isGreaterThan(0), "Invalid collateral amount"); // Update the position object for the user. depositBoxData.withdrawalRequestAmount = denominatedCollateralAmount; depositBoxData.requestPassTimestamp = getCurrentTime(); emit RequestWithdrawal(msg.sender, denominatedCollateralAmount.rawValue, depositBoxData.requestPassTimestamp); // Every price request costs a fixed fee. Check that this user has enough deposited to cover the final fee. FixedPoint.Unsigned memory finalFee = _computeFinalFees(); require( _getFeeAdjustedCollateral(depositBoxData.rawCollateral).isGreaterThanOrEqual(finalFee), "Cannot pay final fee" ); _payFinalFees(address(this), finalFee); // A price request is sent for the current timestamp. _requestOraclePrice(depositBoxData.requestPassTimestamp); } /** * @notice After a passed withdrawal request (i.e., by a call to `requestWithdrawal` and subsequent DVM price resolution), * withdraws `depositBoxData.withdrawalRequestAmount` of collateral currency denominated in the quote asset. * @dev Might not withdraw the full requested amount in order to account for precision loss or if the full requested * amount exceeds the collateral in the position (due to paying fees). * @return amountWithdrawn The actual amount of collateral withdrawn. */ function executeWithdrawal() external isInitialized() fees() nonReentrant() returns (FixedPoint.Unsigned memory amountWithdrawn) { DepositBoxData storage depositBoxData = depositBoxes[msg.sender]; require( depositBoxData.requestPassTimestamp != 0 && depositBoxData.requestPassTimestamp <= getCurrentTime(), "Invalid withdraw request" ); // Get the resolved price or revert. FixedPoint.Unsigned memory exchangeRate = _getOraclePrice(depositBoxData.requestPassTimestamp); // Calculate denomated amount of collateral based on resolved exchange rate. // Example 1: User wants to withdraw $100 of ETH, exchange rate is $200/ETH, therefore user to receive 0.5 ETH. // Example 2: User wants to withdraw $250 of ETH, exchange rate is $200/ETH, therefore user to receive 1.25 ETH. FixedPoint.Unsigned memory denominatedAmountToWithdraw = depositBoxData.withdrawalRequestAmount.div(exchangeRate); // If withdrawal request amount is > collateral, then withdraw the full collateral amount and delete the deposit box data. if (denominatedAmountToWithdraw.isGreaterThan(_getFeeAdjustedCollateral(depositBoxData.rawCollateral))) { denominatedAmountToWithdraw = _getFeeAdjustedCollateral(depositBoxData.rawCollateral); // Reset the position state as all the value has been removed after settlement. emit EndedDepositBox(msg.sender); } // Decrease the individual deposit box and global collateral balance. amountWithdrawn = _decrementCollateralBalances(depositBoxData, denominatedAmountToWithdraw); emit RequestWithdrawalExecuted( msg.sender, amountWithdrawn.rawValue, exchangeRate.rawValue, depositBoxData.requestPassTimestamp ); // Reset withdrawal request by setting withdrawal request timestamp to 0. _resetWithdrawalRequest(depositBoxData); // Transfer approved withdrawal amount from the contract to the caller. collateralCurrency.safeTransfer(msg.sender, amountWithdrawn.rawValue); } /** * @notice Cancels a pending withdrawal request. */ function cancelWithdrawal() external isInitialized() nonReentrant() { DepositBoxData storage depositBoxData = depositBoxes[msg.sender]; require(depositBoxData.requestPassTimestamp != 0, "No pending withdrawal"); emit RequestWithdrawalCanceled( msg.sender, depositBoxData.withdrawalRequestAmount.rawValue, depositBoxData.requestPassTimestamp ); // Reset withdrawal request by setting withdrawal request timestamp to 0. _resetWithdrawalRequest(depositBoxData); } /** * @notice `emergencyShutdown` and `remargin` are required to be implemented by all financial contracts and exposed to the DVM, but * because this is a minimal demo they will simply exit silently. */ function emergencyShutdown() external override isInitialized() nonReentrant() { return; } /** * @notice Same comment as `emergencyShutdown`. For the sake of simplicity, this will simply exit silently. */ function remargin() external override isInitialized() nonReentrant() { return; } /** * @notice Accessor method for a user's collateral. * @dev This is necessary because the struct returned by the depositBoxes() method shows * rawCollateral, which isn't a user-readable value. * @param user address whose collateral amount is retrieved. * @return the fee-adjusted collateral amount in the deposit box (i.e. available for withdrawal). */ function getCollateral(address user) external view nonReentrantView() returns (FixedPoint.Unsigned memory) { return _getFeeAdjustedCollateral(depositBoxes[user].rawCollateral); } /** * @notice Accessor method for the total collateral stored within the entire contract. * @return the total fee-adjusted collateral amount in the contract (i.e. across all users). */ function totalDepositBoxCollateral() external view nonReentrantView() returns (FixedPoint.Unsigned memory) { return _getFeeAdjustedCollateral(rawTotalDepositBoxCollateral); } /**************************************** * INTERNAL FUNCTIONS * ****************************************/ // Requests a price for `priceIdentifier` at `requestedTime` from the Oracle. function _requestOraclePrice(uint256 requestedTime) internal { OracleInterface oracle = _getOracle(); oracle.requestPrice(priceIdentifier, requestedTime); } // Ensure individual and global consistency when increasing collateral balances. Returns the change to the position. function _incrementCollateralBalances( DepositBoxData storage depositBoxData, FixedPoint.Unsigned memory collateralAmount ) internal returns (FixedPoint.Unsigned memory) { _addCollateral(depositBoxData.rawCollateral, collateralAmount); return _addCollateral(rawTotalDepositBoxCollateral, collateralAmount); } // Ensure individual and global consistency when decrementing collateral balances. Returns the change to the // position. We elect to return the amount that the global collateral is decreased by, rather than the individual // position's collateral, because we need to maintain the invariant that the global collateral is always // <= the collateral owned by the contract to avoid reverts on withdrawals. The amount returned = amount withdrawn. function _decrementCollateralBalances( DepositBoxData storage depositBoxData, FixedPoint.Unsigned memory collateralAmount ) internal returns (FixedPoint.Unsigned memory) { _removeCollateral(depositBoxData.rawCollateral, collateralAmount); return _removeCollateral(rawTotalDepositBoxCollateral, collateralAmount); } function _resetWithdrawalRequest(DepositBoxData storage depositBoxData) internal { depositBoxData.withdrawalRequestAmount = FixedPoint.fromUnscaledUint(0); depositBoxData.requestPassTimestamp = 0; } function _depositBoxHasNoPendingWithdrawal(address user) internal view { require(depositBoxes[user].requestPassTimestamp == 0, "Pending withdrawal"); } function _isInitialized() internal view { require(initialized, "Uninitialized contract"); } function _getIdentifierWhitelist() internal view returns (IdentifierWhitelistInterface) { return IdentifierWhitelistInterface(finder.getImplementationAddress(OracleInterfaces.IdentifierWhitelist)); } function _getOracle() internal view returns (OracleInterface) { return OracleInterface(finder.getImplementationAddress(OracleInterfaces.Oracle)); } // Fetches a resolved Oracle price from the Oracle. Reverts if the Oracle hasn't resolved for this request. function _getOraclePrice(uint256 requestedTime) internal view returns (FixedPoint.Unsigned memory) { OracleInterface oracle = _getOracle(); require(oracle.hasPrice(priceIdentifier, requestedTime), "Unresolved oracle price"); int256 oraclePrice = oracle.getPrice(priceIdentifier, requestedTime); // For simplicity we don't want to deal with negative prices. if (oraclePrice < 0) { oraclePrice = 0; } return FixedPoint.Unsigned(uint256(oraclePrice)); } // `_pfc()` is inherited from FeePayer and must be implemented to return the available pool of collateral from // which fees can be charged. For this contract, the available fee pool is simply all of the collateral locked up in the // contract. function _pfc() internal view virtual override returns (FixedPoint.Unsigned memory) { return _getFeeAdjustedCollateral(rawTotalDepositBoxCollateral); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../interfaces/FinderInterface.sol"; import "../../common/implementation/AddressWhitelist.sol"; import "./Registry.sol"; import "./Constants.sol"; /** * @title Base contract for all financial contract creators */ abstract contract ContractCreator { address internal finderAddress; constructor(address _finderAddress) public { finderAddress = _finderAddress; } function _requireWhitelistedCollateral(address collateralAddress) internal view { FinderInterface finder = FinderInterface(finderAddress); AddressWhitelist collateralWhitelist = AddressWhitelist(finder.getImplementationAddress(OracleInterfaces.CollateralWhitelist)); require(collateralWhitelist.isOnWhitelist(collateralAddress), "Collateral not whitelisted"); } function _registerContract(address[] memory parties, address contractToRegister) internal { FinderInterface finder = FinderInterface(finderAddress); Registry registry = Registry(finder.getImplementationAddress(OracleInterfaces.Registry)); registry.registerContract(parties, contractToRegister); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/interfaces/ExpandedIERC20.sol"; import "../../common/interfaces/IERC20Standard.sol"; import "../../oracle/implementation/ContractCreator.sol"; import "../../common/implementation/Testable.sol"; import "../../common/implementation/AddressWhitelist.sol"; import "../../common/implementation/Lockable.sol"; import "../common/TokenFactory.sol"; import "../common/SyntheticToken.sol"; import "./PerpetualLib.sol"; import "./ConfigStore.sol"; /** * @title Perpetual Contract creator. * @notice Factory contract to create and register new instances of perpetual contracts. * Responsible for constraining the parameters used to construct a new perpetual. This creator contains a number of constraints * that are applied to newly created contract. These constraints can evolve over time and are * initially constrained to conservative values in this first iteration. Technically there is nothing in the * Perpetual contract requiring these constraints. However, because `createPerpetual()` is intended * to be the only way to create valid financial contracts that are registered with the DVM (via _registerContract), we can enforce deployment configurations here. */ contract PerpetualCreator is ContractCreator, Testable, Lockable { using FixedPoint for FixedPoint.Unsigned; /**************************************** * PERP CREATOR DATA STRUCTURES * ****************************************/ // Immutable params for perpetual contract. struct Params { address collateralAddress; bytes32 priceFeedIdentifier; bytes32 fundingRateIdentifier; string syntheticName; string syntheticSymbol; FixedPoint.Unsigned collateralRequirement; FixedPoint.Unsigned disputeBondPercentage; FixedPoint.Unsigned sponsorDisputeRewardPercentage; FixedPoint.Unsigned disputerDisputeRewardPercentage; FixedPoint.Unsigned minSponsorTokens; FixedPoint.Unsigned tokenScaling; uint256 withdrawalLiveness; uint256 liquidationLiveness; } // Address of TokenFactory used to create a new synthetic token. address public tokenFactoryAddress; event CreatedPerpetual(address indexed perpetualAddress, address indexed deployerAddress); event CreatedConfigStore(address indexed configStoreAddress, address indexed ownerAddress); /** * @notice Constructs the Perpetual contract. * @param _finderAddress UMA protocol Finder used to discover other protocol contracts. * @param _tokenFactoryAddress ERC20 token factory used to deploy synthetic token instances. * @param _timerAddress Contract that stores the current time in a testing environment. */ constructor( address _finderAddress, address _tokenFactoryAddress, address _timerAddress ) public ContractCreator(_finderAddress) Testable(_timerAddress) nonReentrant() { tokenFactoryAddress = _tokenFactoryAddress; } /** * @notice Creates an instance of perpetual and registers it within the registry. * @param params is a `ConstructorParams` object from Perpetual. * @return address of the deployed contract. */ function createPerpetual(Params memory params, ConfigStore.ConfigSettings memory configSettings) public nonReentrant() returns (address) { require(bytes(params.syntheticName).length != 0, "Missing synthetic name"); require(bytes(params.syntheticSymbol).length != 0, "Missing synthetic symbol"); // Create new config settings store for this contract and reset ownership to the deployer. ConfigStore configStore = new ConfigStore(configSettings, timerAddress); configStore.transferOwnership(msg.sender); emit CreatedConfigStore(address(configStore), configStore.owner()); // Create a new synthetic token using the params. TokenFactory tf = TokenFactory(tokenFactoryAddress); // If the collateral token does not have a `decimals()` method, // then a default precision of 18 will be applied to the newly created synthetic token. uint8 syntheticDecimals = _getSyntheticDecimals(params.collateralAddress); ExpandedIERC20 tokenCurrency = tf.createToken(params.syntheticName, params.syntheticSymbol, syntheticDecimals); address derivative = PerpetualLib.deploy(_convertParams(params, tokenCurrency, address(configStore))); // Give permissions to new derivative contract and then hand over ownership. tokenCurrency.addMinter(derivative); tokenCurrency.addBurner(derivative); tokenCurrency.resetOwner(derivative); _registerContract(new address[](0), derivative); emit CreatedPerpetual(derivative, msg.sender); return derivative; } /**************************************** * PRIVATE FUNCTIONS * ****************************************/ // Converts createPerpetual params to Perpetual constructor params. function _convertParams( Params memory params, ExpandedIERC20 newTokenCurrency, address configStore ) private view returns (Perpetual.ConstructorParams memory constructorParams) { // Known from creator deployment. constructorParams.finderAddress = finderAddress; constructorParams.timerAddress = timerAddress; // Enforce configuration constraints. require(params.withdrawalLiveness != 0, "Withdrawal liveness cannot be 0"); require(params.liquidationLiveness != 0, "Liquidation liveness cannot be 0"); _requireWhitelistedCollateral(params.collateralAddress); // We don't want perpetual deployers to be able to intentionally or unintentionally set // liveness periods that could induce arithmetic overflow, but we also don't want // to be opinionated about what livenesses are "correct", so we will somewhat // arbitrarily set the liveness upper bound to 100 years (5200 weeks). In practice, liveness // periods even greater than a few days would make the perpetual unusable for most users. require(params.withdrawalLiveness < 5200 weeks, "Withdrawal liveness too large"); require(params.liquidationLiveness < 5200 weeks, "Liquidation liveness too large"); // To avoid precision loss or overflows, prevent the token scaling from being too large or too small. FixedPoint.Unsigned memory minScaling = FixedPoint.Unsigned(1e8); // 1e-10 FixedPoint.Unsigned memory maxScaling = FixedPoint.Unsigned(1e28); // 1e10 require( params.tokenScaling.isGreaterThan(minScaling) && params.tokenScaling.isLessThan(maxScaling), "Invalid tokenScaling" ); // Input from function call. constructorParams.configStoreAddress = configStore; constructorParams.tokenAddress = address(newTokenCurrency); constructorParams.collateralAddress = params.collateralAddress; constructorParams.priceFeedIdentifier = params.priceFeedIdentifier; constructorParams.fundingRateIdentifier = params.fundingRateIdentifier; constructorParams.collateralRequirement = params.collateralRequirement; constructorParams.disputeBondPercentage = params.disputeBondPercentage; constructorParams.sponsorDisputeRewardPercentage = params.sponsorDisputeRewardPercentage; constructorParams.disputerDisputeRewardPercentage = params.disputerDisputeRewardPercentage; constructorParams.minSponsorTokens = params.minSponsorTokens; constructorParams.withdrawalLiveness = params.withdrawalLiveness; constructorParams.liquidationLiveness = params.liquidationLiveness; constructorParams.tokenScaling = params.tokenScaling; } // IERC20Standard.decimals() will revert if the collateral contract has not implemented the decimals() method, // which is possible since the method is only an OPTIONAL method in the ERC20 standard: // https://eips.ethereum.org/EIPS/eip-20#methods. function _getSyntheticDecimals(address _collateralAddress) public view returns (uint8 decimals) { try IERC20Standard(_collateralAddress).decimals() returns (uint8 _decimals) { return _decimals; } catch { return 18; } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "./SyntheticToken.sol"; import "../../common/interfaces/ExpandedIERC20.sol"; import "../../common/implementation/Lockable.sol"; /** * @title Factory for creating new mintable and burnable tokens. */ contract TokenFactory is Lockable { /** * @notice Create a new token and return it to the caller. * @dev The caller will become the only minter and burner and the new owner capable of assigning the roles. * @param tokenName used to describe the new token. * @param tokenSymbol short ticker abbreviation of the name. Ideally < 5 chars. * @param tokenDecimals used to define the precision used in the token's numerical representation. * @return newToken an instance of the newly created token interface. */ function createToken( string calldata tokenName, string calldata tokenSymbol, uint8 tokenDecimals ) external nonReentrant() returns (ExpandedIERC20 newToken) { SyntheticToken mintableToken = new SyntheticToken(tokenName, tokenSymbol, tokenDecimals); mintableToken.addMinter(msg.sender); mintableToken.addBurner(msg.sender); mintableToken.resetOwner(msg.sender); newToken = ExpandedIERC20(address(mintableToken)); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../../common/implementation/ExpandedERC20.sol"; import "../../common/implementation/Lockable.sol"; /** * @title Burnable and mintable ERC20. * @dev The contract deployer will initially be the only minter, burner and owner capable of adding new roles. */ contract SyntheticToken is ExpandedERC20, Lockable { /** * @notice Constructs the SyntheticToken. * @param tokenName The name which describes the new token. * @param tokenSymbol The ticker abbreviation of the name. Ideally < 5 chars. * @param tokenDecimals The number of decimals to define token precision. */ constructor( string memory tokenName, string memory tokenSymbol, uint8 tokenDecimals ) public ExpandedERC20(tokenName, tokenSymbol, tokenDecimals) nonReentrant() {} /** * @notice Add Minter role to account. * @dev The caller must have the Owner role. * @param account The address to which the Minter role is added. */ function addMinter(address account) external override nonReentrant() { addMember(uint256(Roles.Minter), account); } /** * @notice Remove Minter role from account. * @dev The caller must have the Owner role. * @param account The address from which the Minter role is removed. */ function removeMinter(address account) external nonReentrant() { removeMember(uint256(Roles.Minter), account); } /** * @notice Add Burner role to account. * @dev The caller must have the Owner role. * @param account The address to which the Burner role is added. */ function addBurner(address account) external override nonReentrant() { addMember(uint256(Roles.Burner), account); } /** * @notice Removes Burner role from account. * @dev The caller must have the Owner role. * @param account The address from which the Burner role is removed. */ function removeBurner(address account) external nonReentrant() { removeMember(uint256(Roles.Burner), account); } /** * @notice Reset Owner role to account. * @dev The caller must have the Owner role. * @param account The new holder of the Owner role. */ function resetOwner(address account) external override nonReentrant() { resetMember(uint256(Roles.Owner), account); } /** * @notice Checks if a given account holds the Minter role. * @param account The address which is checked for the Minter role. * @return bool True if the provided account is a Minter. */ function isMinter(address account) public view nonReentrantView() returns (bool) { return holdsRole(uint256(Roles.Minter), account); } /** * @notice Checks if a given account holds the Burner role. * @param account The address which is checked for the Burner role. * @return bool True if the provided account is a Burner. */ function isBurner(address account) public view nonReentrantView() returns (bool) { return holdsRole(uint256(Roles.Burner), account); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "./Perpetual.sol"; /** * @title Provides convenient Perpetual Multi Party contract utilities. * @dev Using this library to deploy Perpetuals allows calling contracts to avoid importing the full bytecode. */ library PerpetualLib { /** * @notice Returns address of new Perpetual deployed with given `params` configuration. * @dev Caller will need to register new Perpetual with the Registry to begin requesting prices. Caller is also * responsible for enforcing constraints on `params`. * @param params is a `ConstructorParams` object from Perpetual. * @return address of the deployed Perpetual contract */ function deploy(Perpetual.ConstructorParams memory params) public returns (address) { Perpetual derivative = new Perpetual(params); return address(derivative); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "./ConfigStoreInterface.sol"; import "../../common/implementation/Testable.sol"; import "../../common/implementation/Lockable.sol"; import "../../common/implementation/FixedPoint.sol"; /** * @notice ConfigStore stores configuration settings for a perpetual contract and provides an interface for it * to query settings such as reward rates, proposal bond sizes, etc. The configuration settings can be upgraded * by a privileged account and the upgraded changes are timelocked. */ contract ConfigStore is ConfigStoreInterface, Testable, Lockable, Ownable { using SafeMath for uint256; using FixedPoint for FixedPoint.Unsigned; /**************************************** * STORE DATA STRUCTURES * ****************************************/ // Make currentConfig private to force user to call getCurrentConfig, which returns the pendingConfig // if its liveness has expired. ConfigStoreInterface.ConfigSettings private currentConfig; // Beginning on `pendingPassedTimestamp`, the `pendingConfig` can be published as the current config. ConfigStoreInterface.ConfigSettings public pendingConfig; uint256 public pendingPassedTimestamp; /**************************************** * EVENTS * ****************************************/ event ProposedNewConfigSettings( address indexed proposer, uint256 rewardRatePerSecond, uint256 proposerBondPercentage, uint256 timelockLiveness, int256 maxFundingRate, int256 minFundingRate, uint256 proposalTimePastLimit, uint256 proposalPassedTimestamp ); event ChangedConfigSettings( uint256 rewardRatePerSecond, uint256 proposerBondPercentage, uint256 timelockLiveness, int256 maxFundingRate, int256 minFundingRate, uint256 proposalTimePastLimit ); /**************************************** * MODIFIERS * ****************************************/ // Update config settings if possible. modifier updateConfig() { _updateConfig(); _; } /** * @notice Construct the Config Store. An initial configuration is provided and set on construction. * @param _initialConfig Configuration settings to initialize `currentConfig` with. * @param _timerAddress Address of testable Timer contract. */ constructor(ConfigSettings memory _initialConfig, address _timerAddress) public Testable(_timerAddress) { _validateConfig(_initialConfig); currentConfig = _initialConfig; } /** * @notice Returns current config or pending config if pending liveness has expired. * @return ConfigSettings config settings that calling financial contract should view as "live". */ function updateAndGetCurrentConfig() external override updateConfig() nonReentrant() returns (ConfigStoreInterface.ConfigSettings memory) { return currentConfig; } /** * @notice Propose new configuration settings. New settings go into effect after a liveness period passes. * @param newConfig Configuration settings to publish after `currentConfig.timelockLiveness` passes from now. * @dev Callable only by owner. Calling this while there is already a pending proposal will overwrite the pending proposal. */ function proposeNewConfig(ConfigSettings memory newConfig) external onlyOwner() nonReentrant() updateConfig() { _validateConfig(newConfig); // Warning: This overwrites a pending proposal! pendingConfig = newConfig; // Use current config's liveness period to timelock this proposal. pendingPassedTimestamp = getCurrentTime().add(currentConfig.timelockLiveness); emit ProposedNewConfigSettings( msg.sender, newConfig.rewardRatePerSecond.rawValue, newConfig.proposerBondPercentage.rawValue, newConfig.timelockLiveness, newConfig.maxFundingRate.rawValue, newConfig.minFundingRate.rawValue, newConfig.proposalTimePastLimit, pendingPassedTimestamp ); } /** * @notice Publish any pending configuration settings if there is a pending proposal that has passed liveness. */ function publishPendingConfig() external nonReentrant() updateConfig() {} /**************************************** * INTERNAL FUNCTIONS * ****************************************/ // Check if pending proposal can overwrite the current config. function _updateConfig() internal { // If liveness has passed, publish proposed configuration settings. if (_pendingProposalPassed()) { currentConfig = pendingConfig; _deletePendingConfig(); emit ChangedConfigSettings( currentConfig.rewardRatePerSecond.rawValue, currentConfig.proposerBondPercentage.rawValue, currentConfig.timelockLiveness, currentConfig.maxFundingRate.rawValue, currentConfig.minFundingRate.rawValue, currentConfig.proposalTimePastLimit ); } } function _deletePendingConfig() internal { delete pendingConfig; pendingPassedTimestamp = 0; } function _pendingProposalPassed() internal view returns (bool) { return (pendingPassedTimestamp != 0 && pendingPassedTimestamp <= getCurrentTime()); } // Use this method to constrain values with which you can set ConfigSettings. function _validateConfig(ConfigStoreInterface.ConfigSettings memory config) internal pure { // We don't set limits on proposal timestamps because there are already natural limits: // - Future: price requests to the OptimisticOracle must be in the past---we can't add further constraints. // - Past: proposal times must always be after the last update time, and a reasonable past limit would be 30 // mins, meaning that no proposal timestamp can be more than 30 minutes behind the current time. // Make sure timelockLiveness is not too long, otherwise contract might not be able to fix itself // before a vulnerability drains its collateral. require(config.timelockLiveness <= 7 days && config.timelockLiveness >= 1 days, "Invalid timelockLiveness"); // The reward rate should be modified as needed to incentivize honest proposers appropriately. // Additionally, the rate should be less than 100% a year => 100% / 360 days / 24 hours / 60 mins / 60 secs // = 0.0000033 FixedPoint.Unsigned memory maxRewardRatePerSecond = FixedPoint.fromUnscaledUint(33).div(1e7); require(config.rewardRatePerSecond.isLessThan(maxRewardRatePerSecond), "Invalid rewardRatePerSecond"); // We don't set a limit on the proposer bond because it is a defense against dishonest proposers. If a proposer // were to successfully propose a very high or low funding rate, then their PfC would be very high. The proposer // could theoretically keep their "evil" funding rate alive indefinitely by continuously disputing honest // proposers, so we would want to be able to set the proposal bond (equal to the dispute bond) higher than their // PfC for each proposal liveness window. The downside of not limiting this is that the config store owner // can set it arbitrarily high and preclude a new funding rate from ever coming in. We suggest setting the // proposal bond based on the configuration's funding rate range like in this discussion: // https://github.com/UMAprotocol/protocol/issues/2039#issuecomment-719734383 // We also don't set a limit on the funding rate max/min because we might need to allow very high magnitude // funding rates in extraordinarily volatile market situations. Note, that even though we do not bound // the max/min, we still recommend that the deployer of this contract set the funding rate max/min values // to bound the PfC of a dishonest proposer. A reasonable range might be the equivalent of [+200%/year, -200%/year]. } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "./PerpetualLiquidatable.sol"; /** * @title Perpetual Multiparty Contract. * @notice Convenient wrapper for Liquidatable. */ contract Perpetual is PerpetualLiquidatable { /** * @notice Constructs the Perpetual contract. * @param params struct to define input parameters for construction of Liquidatable. Some params * are fed directly into the PositionManager's constructor within the inheritance tree. */ constructor(ConstructorParams memory params) public PerpetualLiquidatable(params) // Note: since there is no logic here, there is no need to add a re-entrancy guard. { } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./PerpetualPositionManager.sol"; import "../../common/implementation/FixedPoint.sol"; /** * @title PerpetualLiquidatable * @notice Adds logic to a position-managing contract that enables callers to liquidate an undercollateralized position. * @dev The liquidation has a liveness period before expiring successfully, during which someone can "dispute" the * liquidation, which sends a price request to the relevant Oracle to settle the final collateralization ratio based on * a DVM price. The contract enforces dispute rewards in order to incentivize disputers to correctly dispute false * liquidations and compensate position sponsors who had their position incorrectly liquidated. Importantly, a * prospective disputer must deposit a dispute bond that they can lose in the case of an unsuccessful dispute. * NOTE: this contract does _not_ work with ERC777 collateral currencies or any others that call into the receiver on * transfer(). Using an ERC777 token would allow a user to maliciously grief other participants (while also losing * money themselves). */ contract PerpetualLiquidatable is PerpetualPositionManager { using FixedPoint for FixedPoint.Unsigned; using SafeMath for uint256; using SafeERC20 for IERC20; /**************************************** * LIQUIDATION DATA STRUCTURES * ****************************************/ // Because of the check in withdrawable(), the order of these enum values should not change. enum Status { Uninitialized, NotDisputed, Disputed, DisputeSucceeded, DisputeFailed } struct LiquidationData { // Following variables set upon creation of liquidation: address sponsor; // Address of the liquidated position's sponsor address liquidator; // Address who created this liquidation Status state; // Liquidated (and expired or not), Pending a Dispute, or Dispute has resolved uint256 liquidationTime; // Time when liquidation is initiated, needed to get price from Oracle // Following variables determined by the position that is being liquidated: FixedPoint.Unsigned tokensOutstanding; // Synthetic tokens required to be burned by liquidator to initiate dispute FixedPoint.Unsigned lockedCollateral; // Collateral locked by contract and released upon expiry or post-dispute // Amount of collateral being liquidated, which could be different from // lockedCollateral if there were pending withdrawals at the time of liquidation FixedPoint.Unsigned liquidatedCollateral; // Unit value (starts at 1) that is used to track the fees per unit of collateral over the course of the liquidation. FixedPoint.Unsigned rawUnitCollateral; // Following variable set upon initiation of a dispute: address disputer; // Person who is disputing a liquidation // Following variable set upon a resolution of a dispute: FixedPoint.Unsigned settlementPrice; // Final price as determined by an Oracle following a dispute FixedPoint.Unsigned finalFee; } // Define the contract's constructor parameters as a struct to enable more variables to be specified. // This is required to enable more params, over and above Solidity's limits. struct ConstructorParams { // Params for PerpetualPositionManager only. uint256 withdrawalLiveness; address configStoreAddress; address collateralAddress; address tokenAddress; address finderAddress; address timerAddress; bytes32 priceFeedIdentifier; bytes32 fundingRateIdentifier; FixedPoint.Unsigned minSponsorTokens; FixedPoint.Unsigned tokenScaling; // Params specifically for PerpetualLiquidatable. uint256 liquidationLiveness; FixedPoint.Unsigned collateralRequirement; FixedPoint.Unsigned disputeBondPercentage; FixedPoint.Unsigned sponsorDisputeRewardPercentage; FixedPoint.Unsigned disputerDisputeRewardPercentage; } // This struct is used in the `withdrawLiquidation` method that disperses liquidation and dispute rewards. // `payToX` stores the total collateral to withdraw from the contract to pay X. This value might differ // from `paidToX` due to precision loss between accounting for the `rawCollateral` versus the // fee-adjusted collateral. These variables are stored within a struct to avoid the stack too deep error. struct RewardsData { FixedPoint.Unsigned payToSponsor; FixedPoint.Unsigned payToLiquidator; FixedPoint.Unsigned payToDisputer; FixedPoint.Unsigned paidToSponsor; FixedPoint.Unsigned paidToLiquidator; FixedPoint.Unsigned paidToDisputer; } // Liquidations are unique by ID per sponsor mapping(address => LiquidationData[]) public liquidations; // Total collateral in liquidation. FixedPoint.Unsigned public rawLiquidationCollateral; // Immutable contract parameters: // Amount of time for pending liquidation before expiry. // !!Note: The lower the liquidation liveness value, the more risk incurred by sponsors. // Extremely low liveness values increase the chance that opportunistic invalid liquidations // expire without dispute, thereby decreasing the usability for sponsors and increasing the risk // for the contract as a whole. An insolvent contract is extremely risky for any sponsor or synthetic // token holder for the contract. uint256 public liquidationLiveness; // Required collateral:TRV ratio for a position to be considered sufficiently collateralized. FixedPoint.Unsigned public collateralRequirement; // Percent of a Liquidation/Position's lockedCollateral to be deposited by a potential disputer // Represented as a multiplier, for example 1.5e18 = "150%" and 0.05e18 = "5%" FixedPoint.Unsigned public disputeBondPercentage; // Percent of oraclePrice paid to sponsor in the Disputed state (i.e. following a successful dispute) // Represented as a multiplier, see above. FixedPoint.Unsigned public sponsorDisputeRewardPercentage; // Percent of oraclePrice paid to disputer in the Disputed state (i.e. following a successful dispute) // Represented as a multiplier, see above. FixedPoint.Unsigned public disputerDisputeRewardPercentage; /**************************************** * EVENTS * ****************************************/ event LiquidationCreated( address indexed sponsor, address indexed liquidator, uint256 indexed liquidationId, uint256 tokensOutstanding, uint256 lockedCollateral, uint256 liquidatedCollateral, uint256 liquidationTime ); event LiquidationDisputed( address indexed sponsor, address indexed liquidator, address indexed disputer, uint256 liquidationId, uint256 disputeBondAmount ); event DisputeSettled( address indexed caller, address indexed sponsor, address indexed liquidator, address disputer, uint256 liquidationId, bool disputeSucceeded ); event LiquidationWithdrawn( address indexed caller, uint256 paidToLiquidator, uint256 paidToDisputer, uint256 paidToSponsor, Status indexed liquidationStatus, uint256 settlementPrice ); /**************************************** * MODIFIERS * ****************************************/ modifier disputable(uint256 liquidationId, address sponsor) { _disputable(liquidationId, sponsor); _; } modifier withdrawable(uint256 liquidationId, address sponsor) { _withdrawable(liquidationId, sponsor); _; } /** * @notice Constructs the liquidatable contract. * @param params struct to define input parameters for construction of Liquidatable. Some params * are fed directly into the PositionManager's constructor within the inheritance tree. */ constructor(ConstructorParams memory params) public PerpetualPositionManager( params.withdrawalLiveness, params.collateralAddress, params.tokenAddress, params.finderAddress, params.priceFeedIdentifier, params.fundingRateIdentifier, params.minSponsorTokens, params.configStoreAddress, params.tokenScaling, params.timerAddress ) { require(params.collateralRequirement.isGreaterThan(1)); require(params.sponsorDisputeRewardPercentage.add(params.disputerDisputeRewardPercentage).isLessThan(1)); // Set liquidatable specific variables. liquidationLiveness = params.liquidationLiveness; collateralRequirement = params.collateralRequirement; disputeBondPercentage = params.disputeBondPercentage; sponsorDisputeRewardPercentage = params.sponsorDisputeRewardPercentage; disputerDisputeRewardPercentage = params.disputerDisputeRewardPercentage; } /**************************************** * LIQUIDATION FUNCTIONS * ****************************************/ /** * @notice Liquidates the sponsor's position if the caller has enough * synthetic tokens to retire the position's outstanding tokens. Liquidations above * a minimum size also reset an ongoing "slow withdrawal"'s liveness. * @dev This method generates an ID that will uniquely identify liquidation for the sponsor. This contract must be * approved to spend at least `tokensLiquidated` of `tokenCurrency` and at least `finalFeeBond` of `collateralCurrency`. * @dev This contract must have the Burner role for the `tokenCurrency`. * @param sponsor address of the sponsor to liquidate. * @param minCollateralPerToken abort the liquidation if the position's collateral per token is below this value. * @param maxCollateralPerToken abort the liquidation if the position's collateral per token exceeds this value. * @param maxTokensToLiquidate max number of tokens to liquidate. * @param deadline abort the liquidation if the transaction is mined after this timestamp. * @return liquidationId ID of the newly created liquidation. * @return tokensLiquidated amount of synthetic tokens removed and liquidated from the `sponsor`'s position. * @return finalFeeBond amount of collateral to be posted by liquidator and returned if not disputed successfully. */ function createLiquidation( address sponsor, FixedPoint.Unsigned calldata minCollateralPerToken, FixedPoint.Unsigned calldata maxCollateralPerToken, FixedPoint.Unsigned calldata maxTokensToLiquidate, uint256 deadline ) external notEmergencyShutdown() fees() nonReentrant() returns ( uint256 liquidationId, FixedPoint.Unsigned memory tokensLiquidated, FixedPoint.Unsigned memory finalFeeBond ) { // Check that this transaction was mined pre-deadline. require(getCurrentTime() <= deadline, "Mined after deadline"); // Retrieve Position data for sponsor PositionData storage positionToLiquidate = _getPositionData(sponsor); tokensLiquidated = FixedPoint.min(maxTokensToLiquidate, positionToLiquidate.tokensOutstanding); require(tokensLiquidated.isGreaterThan(0)); // Starting values for the Position being liquidated. If withdrawal request amount is > position's collateral, // then set this to 0, otherwise set it to (startCollateral - withdrawal request amount). FixedPoint.Unsigned memory startCollateral = _getFeeAdjustedCollateral(positionToLiquidate.rawCollateral); FixedPoint.Unsigned memory startCollateralNetOfWithdrawal = FixedPoint.fromUnscaledUint(0); if (positionToLiquidate.withdrawalRequestAmount.isLessThanOrEqual(startCollateral)) { startCollateralNetOfWithdrawal = startCollateral.sub(positionToLiquidate.withdrawalRequestAmount); } // Scoping to get rid of a stack too deep error. { FixedPoint.Unsigned memory startTokens = positionToLiquidate.tokensOutstanding; // The Position's collateralization ratio must be between [minCollateralPerToken, maxCollateralPerToken]. require( maxCollateralPerToken.mul(startTokens).isGreaterThanOrEqual(startCollateralNetOfWithdrawal), "CR is more than max liq. price" ); // minCollateralPerToken >= startCollateralNetOfWithdrawal / startTokens. require( minCollateralPerToken.mul(startTokens).isLessThanOrEqual(startCollateralNetOfWithdrawal), "CR is less than min liq. price" ); } // Compute final fee at time of liquidation. finalFeeBond = _computeFinalFees(); // These will be populated within the scope below. FixedPoint.Unsigned memory lockedCollateral; FixedPoint.Unsigned memory liquidatedCollateral; // Scoping to get rid of a stack too deep error. The amount of tokens to remove from the position // are not funding-rate adjusted because the multiplier only affects their redemption value, not their // notional. { FixedPoint.Unsigned memory ratio = tokensLiquidated.div(positionToLiquidate.tokensOutstanding); // The actual amount of collateral that gets moved to the liquidation. lockedCollateral = startCollateral.mul(ratio); // For purposes of disputes, it's actually this liquidatedCollateral value that's used. This value is net of // withdrawal requests. liquidatedCollateral = startCollateralNetOfWithdrawal.mul(ratio); // Part of the withdrawal request is also removed. Ideally: // liquidatedCollateral + withdrawalAmountToRemove = lockedCollateral. FixedPoint.Unsigned memory withdrawalAmountToRemove = positionToLiquidate.withdrawalRequestAmount.mul(ratio); _reduceSponsorPosition(sponsor, tokensLiquidated, lockedCollateral, withdrawalAmountToRemove); } // Add to the global liquidation collateral count. _addCollateral(rawLiquidationCollateral, lockedCollateral.add(finalFeeBond)); // Construct liquidation object. // Note: All dispute-related values are zeroed out until a dispute occurs. liquidationId is the index of the new // LiquidationData that is pushed into the array, which is equal to the current length of the array pre-push. liquidationId = liquidations[sponsor].length; liquidations[sponsor].push( LiquidationData({ sponsor: sponsor, liquidator: msg.sender, state: Status.NotDisputed, liquidationTime: getCurrentTime(), tokensOutstanding: _getFundingRateAppliedTokenDebt(tokensLiquidated), lockedCollateral: lockedCollateral, liquidatedCollateral: liquidatedCollateral, rawUnitCollateral: _convertToRawCollateral(FixedPoint.fromUnscaledUint(1)), disputer: address(0), settlementPrice: FixedPoint.fromUnscaledUint(0), finalFee: finalFeeBond }) ); // If this liquidation is a subsequent liquidation on the position, and the liquidation size is larger than // some "griefing threshold", then re-set the liveness. This enables a liquidation against a withdraw request to be // "dragged out" if the position is very large and liquidators need time to gather funds. The griefing threshold // is enforced so that liquidations for trivially small # of tokens cannot drag out an honest sponsor's slow withdrawal. // We arbitrarily set the "griefing threshold" to `minSponsorTokens` because it is the only parameter // denominated in token currency units and we can avoid adding another parameter. FixedPoint.Unsigned memory griefingThreshold = minSponsorTokens; if ( positionToLiquidate.withdrawalRequestPassTimestamp > 0 && // The position is undergoing a slow withdrawal. positionToLiquidate.withdrawalRequestPassTimestamp > getCurrentTime() && // The slow withdrawal has not yet expired. tokensLiquidated.isGreaterThanOrEqual(griefingThreshold) // The liquidated token count is above a "griefing threshold". ) { positionToLiquidate.withdrawalRequestPassTimestamp = getCurrentTime().add(withdrawalLiveness); } emit LiquidationCreated( sponsor, msg.sender, liquidationId, _getFundingRateAppliedTokenDebt(tokensLiquidated).rawValue, lockedCollateral.rawValue, liquidatedCollateral.rawValue, getCurrentTime() ); // Destroy tokens tokenCurrency.safeTransferFrom(msg.sender, address(this), tokensLiquidated.rawValue); tokenCurrency.burn(tokensLiquidated.rawValue); // Pull final fee from liquidator. collateralCurrency.safeTransferFrom(msg.sender, address(this), finalFeeBond.rawValue); } /** * @notice Disputes a liquidation, if the caller has enough collateral to post a dispute bond and pay a fixed final * fee charged on each price request. * @dev Can only dispute a liquidation before the liquidation expires and if there are no other pending disputes. * This contract must be approved to spend at least the dispute bond amount of `collateralCurrency`. This dispute * bond amount is calculated from `disputeBondPercentage` times the collateral in the liquidation. * @param liquidationId of the disputed liquidation. * @param sponsor the address of the sponsor whose liquidation is being disputed. * @return totalPaid amount of collateral charged to disputer (i.e. final fee bond + dispute bond). */ function dispute(uint256 liquidationId, address sponsor) external disputable(liquidationId, sponsor) fees() nonReentrant() returns (FixedPoint.Unsigned memory totalPaid) { LiquidationData storage disputedLiquidation = _getLiquidationData(sponsor, liquidationId); // Multiply by the unit collateral so the dispute bond is a percentage of the locked collateral after fees. FixedPoint.Unsigned memory disputeBondAmount = disputedLiquidation.lockedCollateral.mul(disputeBondPercentage).mul( _getFeeAdjustedCollateral(disputedLiquidation.rawUnitCollateral) ); _addCollateral(rawLiquidationCollateral, disputeBondAmount); // Request a price from DVM. Liquidation is pending dispute until DVM returns a price. disputedLiquidation.state = Status.Disputed; disputedLiquidation.disputer = msg.sender; // Enqueue a request with the DVM. _requestOraclePrice(disputedLiquidation.liquidationTime); emit LiquidationDisputed( sponsor, disputedLiquidation.liquidator, msg.sender, liquidationId, disputeBondAmount.rawValue ); totalPaid = disputeBondAmount.add(disputedLiquidation.finalFee); // Pay the final fee for requesting price from the DVM. _payFinalFees(msg.sender, disputedLiquidation.finalFee); // Transfer the dispute bond amount from the caller to this contract. collateralCurrency.safeTransferFrom(msg.sender, address(this), disputeBondAmount.rawValue); } /** * @notice After a dispute has settled or after a non-disputed liquidation has expired, * anyone can call this method to disperse payments to the sponsor, liquidator, and disputer. * @dev If the dispute SUCCEEDED: the sponsor, liquidator, and disputer are eligible for payment. * If the dispute FAILED: only the liquidator receives payment. This method deletes the liquidation data. * This method will revert if rewards have already been dispersed. * @param liquidationId uniquely identifies the sponsor's liquidation. * @param sponsor address of the sponsor associated with the liquidation. * @return data about rewards paid out. */ function withdrawLiquidation(uint256 liquidationId, address sponsor) public withdrawable(liquidationId, sponsor) fees() nonReentrant() returns (RewardsData memory) { LiquidationData storage liquidation = _getLiquidationData(sponsor, liquidationId); // Settles the liquidation if necessary. This call will revert if the price has not resolved yet. _settle(liquidationId, sponsor); // Calculate rewards as a function of the TRV. // Note1: all payouts are scaled by the unit collateral value so all payouts are charged the fees pro rata. // Note2: the tokenRedemptionValue uses the tokensOutstanding which was calculated using the funding rate at // liquidation time from _getFundingRateAppliedTokenDebt. Therefore the TRV considers the full debt value at that time. FixedPoint.Unsigned memory feeAttenuation = _getFeeAdjustedCollateral(liquidation.rawUnitCollateral); FixedPoint.Unsigned memory settlementPrice = liquidation.settlementPrice; FixedPoint.Unsigned memory tokenRedemptionValue = liquidation.tokensOutstanding.mul(settlementPrice).mul(feeAttenuation); FixedPoint.Unsigned memory collateral = liquidation.lockedCollateral.mul(feeAttenuation); FixedPoint.Unsigned memory disputerDisputeReward = disputerDisputeRewardPercentage.mul(tokenRedemptionValue); FixedPoint.Unsigned memory sponsorDisputeReward = sponsorDisputeRewardPercentage.mul(tokenRedemptionValue); FixedPoint.Unsigned memory disputeBondAmount = collateral.mul(disputeBondPercentage); FixedPoint.Unsigned memory finalFee = liquidation.finalFee.mul(feeAttenuation); // There are three main outcome states: either the dispute succeeded, failed or was not updated. // Based on the state, different parties of a liquidation receive different amounts. // After assigning rewards based on the liquidation status, decrease the total collateral held in this contract // by the amount to pay each party. The actual amounts withdrawn might differ if _removeCollateral causes // precision loss. RewardsData memory rewards; if (liquidation.state == Status.DisputeSucceeded) { // If the dispute is successful then all three users should receive rewards: // Pay DISPUTER: disputer reward + dispute bond + returned final fee rewards.payToDisputer = disputerDisputeReward.add(disputeBondAmount).add(finalFee); // Pay SPONSOR: remaining collateral (collateral - TRV) + sponsor reward rewards.payToSponsor = sponsorDisputeReward.add(collateral.sub(tokenRedemptionValue)); // Pay LIQUIDATOR: TRV - dispute reward - sponsor reward // If TRV > Collateral, then subtract rewards from collateral // NOTE: This should never be below zero since we prevent (sponsorDisputePercentage+disputerDisputePercentage) >= 0 in // the constructor when these params are set. rewards.payToLiquidator = tokenRedemptionValue.sub(sponsorDisputeReward).sub(disputerDisputeReward); // Transfer rewards and debit collateral rewards.paidToLiquidator = _removeCollateral(rawLiquidationCollateral, rewards.payToLiquidator); rewards.paidToSponsor = _removeCollateral(rawLiquidationCollateral, rewards.payToSponsor); rewards.paidToDisputer = _removeCollateral(rawLiquidationCollateral, rewards.payToDisputer); collateralCurrency.safeTransfer(liquidation.disputer, rewards.paidToDisputer.rawValue); collateralCurrency.safeTransfer(liquidation.liquidator, rewards.paidToLiquidator.rawValue); collateralCurrency.safeTransfer(liquidation.sponsor, rewards.paidToSponsor.rawValue); // In the case of a failed dispute only the liquidator can withdraw. } else if (liquidation.state == Status.DisputeFailed) { // Pay LIQUIDATOR: collateral + dispute bond + returned final fee rewards.payToLiquidator = collateral.add(disputeBondAmount).add(finalFee); // Transfer rewards and debit collateral rewards.paidToLiquidator = _removeCollateral(rawLiquidationCollateral, rewards.payToLiquidator); collateralCurrency.safeTransfer(liquidation.liquidator, rewards.paidToLiquidator.rawValue); // If the state is pre-dispute but time has passed liveness then there was no dispute. We represent this // state as a dispute failed and the liquidator can withdraw. } else if (liquidation.state == Status.NotDisputed) { // Pay LIQUIDATOR: collateral + returned final fee rewards.payToLiquidator = collateral.add(finalFee); // Transfer rewards and debit collateral rewards.paidToLiquidator = _removeCollateral(rawLiquidationCollateral, rewards.payToLiquidator); collateralCurrency.safeTransfer(liquidation.liquidator, rewards.paidToLiquidator.rawValue); } emit LiquidationWithdrawn( msg.sender, rewards.paidToLiquidator.rawValue, rewards.paidToDisputer.rawValue, rewards.paidToSponsor.rawValue, liquidation.state, settlementPrice.rawValue ); // Free up space after collateral is withdrawn by removing the liquidation object from the array. delete liquidations[sponsor][liquidationId]; return rewards; } /** * @notice Gets all liquidation information for a given sponsor address. * @param sponsor address of the position sponsor. * @return liquidationData array of all liquidation information for the given sponsor address. */ function getLiquidations(address sponsor) external view nonReentrantView() returns (LiquidationData[] memory liquidationData) { return liquidations[sponsor]; } /**************************************** * INTERNAL FUNCTIONS * ****************************************/ // This settles a liquidation if it is in the Disputed state. If not, it will immediately return. // If the liquidation is in the Disputed state, but a price is not available, this will revert. function _settle(uint256 liquidationId, address sponsor) internal { LiquidationData storage liquidation = _getLiquidationData(sponsor, liquidationId); // Settlement only happens when state == Disputed and will only happen once per liquidation. // If this liquidation is not ready to be settled, this method should return immediately. if (liquidation.state != Status.Disputed) { return; } // Get the returned price from the oracle. If this has not yet resolved will revert. liquidation.settlementPrice = _getOraclePrice(liquidation.liquidationTime); // Find the value of the tokens in the underlying collateral. FixedPoint.Unsigned memory tokenRedemptionValue = liquidation.tokensOutstanding.mul(liquidation.settlementPrice); // The required collateral is the value of the tokens in underlying * required collateral ratio. FixedPoint.Unsigned memory requiredCollateral = tokenRedemptionValue.mul(collateralRequirement); // If the position has more than the required collateral it is solvent and the dispute is valid (liquidation is invalid) // Note that this check uses the liquidatedCollateral not the lockedCollateral as this considers withdrawals. bool disputeSucceeded = liquidation.liquidatedCollateral.isGreaterThanOrEqual(requiredCollateral); liquidation.state = disputeSucceeded ? Status.DisputeSucceeded : Status.DisputeFailed; emit DisputeSettled( msg.sender, sponsor, liquidation.liquidator, liquidation.disputer, liquidationId, disputeSucceeded ); } function _pfc() internal view override returns (FixedPoint.Unsigned memory) { return super._pfc().add(_getFeeAdjustedCollateral(rawLiquidationCollateral)); } function _getLiquidationData(address sponsor, uint256 liquidationId) internal view returns (LiquidationData storage liquidation) { LiquidationData[] storage liquidationArray = liquidations[sponsor]; // Revert if the caller is attempting to access an invalid liquidation // (one that has never been created or one has never been initialized). require( liquidationId < liquidationArray.length && liquidationArray[liquidationId].state != Status.Uninitialized ); return liquidationArray[liquidationId]; } function _getLiquidationExpiry(LiquidationData storage liquidation) internal view returns (uint256) { return liquidation.liquidationTime.add(liquidationLiveness); } // These internal functions are supposed to act identically to modifiers, but re-used modifiers // unnecessarily increase contract bytecode size. // source: https://blog.polymath.network/solidity-tips-and-tricks-to-save-gas-and-reduce-bytecode-size-c44580b218e6 function _disputable(uint256 liquidationId, address sponsor) internal view { LiquidationData storage liquidation = _getLiquidationData(sponsor, liquidationId); require( (getCurrentTime() < _getLiquidationExpiry(liquidation)) && (liquidation.state == Status.NotDisputed), "Liquidation not disputable" ); } function _withdrawable(uint256 liquidationId, address sponsor) internal view { LiquidationData storage liquidation = _getLiquidationData(sponsor, liquidationId); Status state = liquidation.state; // Must be disputed or the liquidation has passed expiry. require( (state > Status.NotDisputed) || ((_getLiquidationExpiry(liquidation) <= getCurrentTime()) && (state == Status.NotDisputed)) ); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../../common/implementation/FixedPoint.sol"; import "../../common/interfaces/ExpandedIERC20.sol"; import "../../oracle/interfaces/OracleInterface.sol"; import "../../oracle/interfaces/IdentifierWhitelistInterface.sol"; import "../../oracle/implementation/Constants.sol"; import "../common/FundingRateApplier.sol"; /** * @title Financial contract with priceless position management. * @notice Handles positions for multiple sponsors in an optimistic (i.e., priceless) way without relying * on a price feed. On construction, deploys a new ERC20, managed by this contract, that is the synthetic token. */ contract PerpetualPositionManager is FundingRateApplier { using SafeMath for uint256; using FixedPoint for FixedPoint.Unsigned; using SafeERC20 for IERC20; using SafeERC20 for ExpandedIERC20; /**************************************** * PRICELESS POSITION DATA STRUCTURES * ****************************************/ // Represents a single sponsor's position. All collateral is held by this contract. // This struct acts as bookkeeping for how much of that collateral is allocated to each sponsor. struct PositionData { FixedPoint.Unsigned tokensOutstanding; // Tracks pending withdrawal requests. A withdrawal request is pending if `withdrawalRequestPassTimestamp != 0`. uint256 withdrawalRequestPassTimestamp; FixedPoint.Unsigned withdrawalRequestAmount; // Raw collateral value. This value should never be accessed directly -- always use _getFeeAdjustedCollateral(). // To add or remove collateral, use _addCollateral() and _removeCollateral(). FixedPoint.Unsigned rawCollateral; } // Maps sponsor addresses to their positions. Each sponsor can have only one position. mapping(address => PositionData) public positions; // Keep track of the total collateral and tokens across all positions to enable calculating the // global collateralization ratio without iterating over all positions. FixedPoint.Unsigned public totalTokensOutstanding; // Similar to the rawCollateral in PositionData, this value should not be used directly. // _getFeeAdjustedCollateral(), _addCollateral() and _removeCollateral() must be used to access and adjust. FixedPoint.Unsigned public rawTotalPositionCollateral; // Synthetic token created by this contract. ExpandedIERC20 public tokenCurrency; // Unique identifier for DVM price feed ticker. bytes32 public priceIdentifier; // Time that has to elapse for a withdrawal request to be considered passed, if no liquidations occur. // !!Note: The lower the withdrawal liveness value, the more risk incurred by the contract. // Extremely low liveness values increase the chance that opportunistic invalid withdrawal requests // expire without liquidation, thereby increasing the insolvency risk for the contract as a whole. An insolvent // contract is extremely risky for any sponsor or synthetic token holder for the contract. uint256 public withdrawalLiveness; // Minimum number of tokens in a sponsor's position. FixedPoint.Unsigned public minSponsorTokens; // Expiry price pulled from the DVM in the case of an emergency shutdown. FixedPoint.Unsigned public emergencyShutdownPrice; /**************************************** * EVENTS * ****************************************/ event Deposit(address indexed sponsor, uint256 indexed collateralAmount); event Withdrawal(address indexed sponsor, uint256 indexed collateralAmount); event RequestWithdrawal(address indexed sponsor, uint256 indexed collateralAmount); event RequestWithdrawalExecuted(address indexed sponsor, uint256 indexed collateralAmount); event RequestWithdrawalCanceled(address indexed sponsor, uint256 indexed collateralAmount); event PositionCreated(address indexed sponsor, uint256 indexed collateralAmount, uint256 indexed tokenAmount); event NewSponsor(address indexed sponsor); event EndedSponsorPosition(address indexed sponsor); event Redeem(address indexed sponsor, uint256 indexed collateralAmount, uint256 indexed tokenAmount); event Repay(address indexed sponsor, uint256 indexed numTokensRepaid, uint256 indexed newTokenCount); event EmergencyShutdown(address indexed caller, uint256 shutdownTimestamp); event SettleEmergencyShutdown( address indexed caller, uint256 indexed collateralReturned, uint256 indexed tokensBurned ); /**************************************** * MODIFIERS * ****************************************/ modifier onlyCollateralizedPosition(address sponsor) { _onlyCollateralizedPosition(sponsor); _; } modifier noPendingWithdrawal(address sponsor) { _positionHasNoPendingWithdrawal(sponsor); _; } /** * @notice Construct the PerpetualPositionManager. * @dev Deployer of this contract should consider carefully which parties have ability to mint and burn * the synthetic tokens referenced by `_tokenAddress`. This contract's security assumes that no external accounts * can mint new tokens, which could be used to steal all of this contract's locked collateral. * We recommend to only use synthetic token contracts whose sole Owner role (the role capable of adding & removing roles) * is assigned to this contract, whose sole Minter role is assigned to this contract, and whose * total supply is 0 prior to construction of this contract. * @param _withdrawalLiveness liveness delay, in seconds, for pending withdrawals. * @param _collateralAddress ERC20 token used as collateral for all positions. * @param _tokenAddress ERC20 token used as synthetic token. * @param _finderAddress UMA protocol Finder used to discover other protocol contracts. * @param _priceIdentifier registered in the DVM for the synthetic. * @param _fundingRateIdentifier Unique identifier for DVM price feed ticker for child financial contract. * @param _minSponsorTokens minimum amount of collateral that must exist at any time in a position. * @param _tokenScaling initial scaling to apply to the token value (i.e. scales the tracking index). * @param _timerAddress Contract that stores the current time in a testing environment. Set to 0x0 for production. */ constructor( uint256 _withdrawalLiveness, address _collateralAddress, address _tokenAddress, address _finderAddress, bytes32 _priceIdentifier, bytes32 _fundingRateIdentifier, FixedPoint.Unsigned memory _minSponsorTokens, address _configStoreAddress, FixedPoint.Unsigned memory _tokenScaling, address _timerAddress ) public FundingRateApplier( _fundingRateIdentifier, _collateralAddress, _finderAddress, _configStoreAddress, _tokenScaling, _timerAddress ) { require(_getIdentifierWhitelist().isIdentifierSupported(_priceIdentifier)); withdrawalLiveness = _withdrawalLiveness; tokenCurrency = ExpandedIERC20(_tokenAddress); minSponsorTokens = _minSponsorTokens; priceIdentifier = _priceIdentifier; } /**************************************** * POSITION FUNCTIONS * ****************************************/ /** * @notice Transfers `collateralAmount` of `collateralCurrency` into the specified sponsor's position. * @dev Increases the collateralization level of a position after creation. This contract must be approved to spend * at least `collateralAmount` of `collateralCurrency`. * @param sponsor the sponsor to credit the deposit to. * @param collateralAmount total amount of collateral tokens to be sent to the sponsor's position. */ function depositTo(address sponsor, FixedPoint.Unsigned memory collateralAmount) public notEmergencyShutdown() noPendingWithdrawal(sponsor) fees() nonReentrant() { require(collateralAmount.isGreaterThan(0)); PositionData storage positionData = _getPositionData(sponsor); // Increase the position and global collateral balance by collateral amount. _incrementCollateralBalances(positionData, collateralAmount); emit Deposit(sponsor, collateralAmount.rawValue); // Move collateral currency from sender to contract. collateralCurrency.safeTransferFrom(msg.sender, address(this), collateralAmount.rawValue); } /** * @notice Transfers `collateralAmount` of `collateralCurrency` into the caller's position. * @dev Increases the collateralization level of a position after creation. This contract must be approved to spend * at least `collateralAmount` of `collateralCurrency`. * @param collateralAmount total amount of collateral tokens to be sent to the sponsor's position. */ function deposit(FixedPoint.Unsigned memory collateralAmount) public { // This is just a thin wrapper over depositTo that specified the sender as the sponsor. depositTo(msg.sender, collateralAmount); } /** * @notice Transfers `collateralAmount` of `collateralCurrency` from the sponsor's position to the sponsor. * @dev Reverts if the withdrawal puts this position's collateralization ratio below the global collateralization * ratio. In that case, use `requestWithdrawal`. Might not withdraw the full requested amount to account for precision loss. * @param collateralAmount is the amount of collateral to withdraw. * @return amountWithdrawn The actual amount of collateral withdrawn. */ function withdraw(FixedPoint.Unsigned memory collateralAmount) public notEmergencyShutdown() noPendingWithdrawal(msg.sender) fees() nonReentrant() returns (FixedPoint.Unsigned memory amountWithdrawn) { require(collateralAmount.isGreaterThan(0)); PositionData storage positionData = _getPositionData(msg.sender); // Decrement the sponsor's collateral and global collateral amounts. Check the GCR between decrement to ensure // position remains above the GCR within the withdrawal. If this is not the case the caller must submit a request. amountWithdrawn = _decrementCollateralBalancesCheckGCR(positionData, collateralAmount); emit Withdrawal(msg.sender, amountWithdrawn.rawValue); // Move collateral currency from contract to sender. // Note: that we move the amount of collateral that is decreased from rawCollateral (inclusive of fees) // instead of the user requested amount. This eliminates precision loss that could occur // where the user withdraws more collateral than rawCollateral is decremented by. collateralCurrency.safeTransfer(msg.sender, amountWithdrawn.rawValue); } /** * @notice Starts a withdrawal request that, if passed, allows the sponsor to withdraw from their position. * @dev The request will be pending for `withdrawalLiveness`, during which the position can be liquidated. * @param collateralAmount the amount of collateral requested to withdraw */ function requestWithdrawal(FixedPoint.Unsigned memory collateralAmount) public notEmergencyShutdown() noPendingWithdrawal(msg.sender) nonReentrant() { PositionData storage positionData = _getPositionData(msg.sender); require( collateralAmount.isGreaterThan(0) && collateralAmount.isLessThanOrEqual(_getFeeAdjustedCollateral(positionData.rawCollateral)) ); // Update the position object for the user. positionData.withdrawalRequestPassTimestamp = getCurrentTime().add(withdrawalLiveness); positionData.withdrawalRequestAmount = collateralAmount; emit RequestWithdrawal(msg.sender, collateralAmount.rawValue); } /** * @notice After a passed withdrawal request (i.e., by a call to `requestWithdrawal` and waiting * `withdrawalLiveness`), withdraws `positionData.withdrawalRequestAmount` of collateral currency. * @dev Might not withdraw the full requested amount in order to account for precision loss or if the full requested * amount exceeds the collateral in the position (due to paying fees). * @return amountWithdrawn The actual amount of collateral withdrawn. */ function withdrawPassedRequest() external notEmergencyShutdown() fees() nonReentrant() returns (FixedPoint.Unsigned memory amountWithdrawn) { PositionData storage positionData = _getPositionData(msg.sender); require( positionData.withdrawalRequestPassTimestamp != 0 && positionData.withdrawalRequestPassTimestamp <= getCurrentTime() ); // If withdrawal request amount is > position collateral, then withdraw the full collateral amount. // This situation is possible due to fees charged since the withdrawal was originally requested. FixedPoint.Unsigned memory amountToWithdraw = positionData.withdrawalRequestAmount; if (positionData.withdrawalRequestAmount.isGreaterThan(_getFeeAdjustedCollateral(positionData.rawCollateral))) { amountToWithdraw = _getFeeAdjustedCollateral(positionData.rawCollateral); } // Decrement the sponsor's collateral and global collateral amounts. amountWithdrawn = _decrementCollateralBalances(positionData, amountToWithdraw); // Reset withdrawal request by setting withdrawal amount and withdrawal timestamp to 0. _resetWithdrawalRequest(positionData); // Transfer approved withdrawal amount from the contract to the caller. collateralCurrency.safeTransfer(msg.sender, amountWithdrawn.rawValue); emit RequestWithdrawalExecuted(msg.sender, amountWithdrawn.rawValue); } /** * @notice Cancels a pending withdrawal request. */ function cancelWithdrawal() external notEmergencyShutdown() nonReentrant() { PositionData storage positionData = _getPositionData(msg.sender); // No pending withdrawal require message removed to save bytecode. require(positionData.withdrawalRequestPassTimestamp != 0); emit RequestWithdrawalCanceled(msg.sender, positionData.withdrawalRequestAmount.rawValue); // Reset withdrawal request by setting withdrawal amount and withdrawal timestamp to 0. _resetWithdrawalRequest(positionData); } /** * @notice Creates tokens by creating a new position or by augmenting an existing position. Pulls `collateralAmount * ` into the sponsor's position and mints `numTokens` of `tokenCurrency`. * @dev This contract must have the Minter role for the `tokenCurrency`. * @dev Reverts if minting these tokens would put the position's collateralization ratio below the * global collateralization ratio. This contract must be approved to spend at least `collateralAmount` of * `collateralCurrency`. * @param collateralAmount is the number of collateral tokens to collateralize the position with * @param numTokens is the number of tokens to mint from the position. */ function create(FixedPoint.Unsigned memory collateralAmount, FixedPoint.Unsigned memory numTokens) public notEmergencyShutdown() fees() nonReentrant() { PositionData storage positionData = positions[msg.sender]; // Either the new create ratio or the resultant position CR must be above the current GCR. require( (_checkCollateralization( _getFeeAdjustedCollateral(positionData.rawCollateral).add(collateralAmount), positionData.tokensOutstanding.add(numTokens) ) || _checkCollateralization(collateralAmount, numTokens)), "Insufficient collateral" ); require(positionData.withdrawalRequestPassTimestamp == 0); if (positionData.tokensOutstanding.isEqual(0)) { require(numTokens.isGreaterThanOrEqual(minSponsorTokens)); emit NewSponsor(msg.sender); } // Increase the position and global collateral balance by collateral amount. _incrementCollateralBalances(positionData, collateralAmount); // Add the number of tokens created to the position's outstanding tokens. positionData.tokensOutstanding = positionData.tokensOutstanding.add(numTokens); totalTokensOutstanding = totalTokensOutstanding.add(numTokens); emit PositionCreated(msg.sender, collateralAmount.rawValue, numTokens.rawValue); // Transfer tokens into the contract from caller and mint corresponding synthetic tokens to the caller's address. collateralCurrency.safeTransferFrom(msg.sender, address(this), collateralAmount.rawValue); // Note: revert reason removed to save bytecode. require(tokenCurrency.mint(msg.sender, numTokens.rawValue)); } /** * @notice Burns `numTokens` of `tokenCurrency` and sends back the proportional amount of `collateralCurrency`. * @dev Can only be called by a token sponsor. Might not redeem the full proportional amount of collateral * in order to account for precision loss. This contract must be approved to spend at least `numTokens` of * `tokenCurrency`. * @dev This contract must have the Burner role for the `tokenCurrency`. * @param numTokens is the number of tokens to be burnt for a commensurate amount of collateral. * @return amountWithdrawn The actual amount of collateral withdrawn. */ function redeem(FixedPoint.Unsigned memory numTokens) public notEmergencyShutdown() noPendingWithdrawal(msg.sender) fees() nonReentrant() returns (FixedPoint.Unsigned memory amountWithdrawn) { PositionData storage positionData = _getPositionData(msg.sender); require(numTokens.isLessThanOrEqual(positionData.tokensOutstanding)); FixedPoint.Unsigned memory fractionRedeemed = numTokens.div(positionData.tokensOutstanding); FixedPoint.Unsigned memory collateralRedeemed = fractionRedeemed.mul(_getFeeAdjustedCollateral(positionData.rawCollateral)); // If redemption returns all tokens the sponsor has then we can delete their position. Else, downsize. if (positionData.tokensOutstanding.isEqual(numTokens)) { amountWithdrawn = _deleteSponsorPosition(msg.sender); } else { // Decrement the sponsor's collateral and global collateral amounts. amountWithdrawn = _decrementCollateralBalances(positionData, collateralRedeemed); // Decrease the sponsors position tokens size. Ensure it is above the min sponsor size. FixedPoint.Unsigned memory newTokenCount = positionData.tokensOutstanding.sub(numTokens); require(newTokenCount.isGreaterThanOrEqual(minSponsorTokens)); positionData.tokensOutstanding = newTokenCount; // Update the totalTokensOutstanding after redemption. totalTokensOutstanding = totalTokensOutstanding.sub(numTokens); } emit Redeem(msg.sender, amountWithdrawn.rawValue, numTokens.rawValue); // Transfer collateral from contract to caller and burn callers synthetic tokens. collateralCurrency.safeTransfer(msg.sender, amountWithdrawn.rawValue); tokenCurrency.safeTransferFrom(msg.sender, address(this), numTokens.rawValue); tokenCurrency.burn(numTokens.rawValue); } /** * @notice Burns `numTokens` of `tokenCurrency` to decrease sponsors position size, without sending back `collateralCurrency`. * This is done by a sponsor to increase position CR. Resulting size is bounded by minSponsorTokens. * @dev Can only be called by token sponsor. This contract must be approved to spend `numTokens` of `tokenCurrency`. * @dev This contract must have the Burner role for the `tokenCurrency`. * @param numTokens is the number of tokens to be burnt from the sponsor's debt position. */ function repay(FixedPoint.Unsigned memory numTokens) public notEmergencyShutdown() noPendingWithdrawal(msg.sender) fees() nonReentrant() { PositionData storage positionData = _getPositionData(msg.sender); require(numTokens.isLessThanOrEqual(positionData.tokensOutstanding)); // Decrease the sponsors position tokens size. Ensure it is above the min sponsor size. FixedPoint.Unsigned memory newTokenCount = positionData.tokensOutstanding.sub(numTokens); require(newTokenCount.isGreaterThanOrEqual(minSponsorTokens)); positionData.tokensOutstanding = newTokenCount; // Update the totalTokensOutstanding after redemption. totalTokensOutstanding = totalTokensOutstanding.sub(numTokens); emit Repay(msg.sender, numTokens.rawValue, newTokenCount.rawValue); // Transfer the tokens back from the sponsor and burn them. tokenCurrency.safeTransferFrom(msg.sender, address(this), numTokens.rawValue); tokenCurrency.burn(numTokens.rawValue); } /** * @notice If the contract is emergency shutdown then all token holders and sponsors can redeem their tokens or * remaining collateral for underlying at the prevailing price defined by a DVM vote. * @dev This burns all tokens from the caller of `tokenCurrency` and sends back the resolved settlement value of * `collateralCurrency`. Might not redeem the full proportional amount of collateral in order to account for * precision loss. This contract must be approved to spend `tokenCurrency` at least up to the caller's full balance. * @dev This contract must have the Burner role for the `tokenCurrency`. * @dev Note that this function does not call the updateFundingRate modifier to update the funding rate as this * function is only called after an emergency shutdown & there should be no funding rate updates after the shutdown. * @return amountWithdrawn The actual amount of collateral withdrawn. */ function settleEmergencyShutdown() external isEmergencyShutdown() fees() nonReentrant() returns (FixedPoint.Unsigned memory amountWithdrawn) { // Set the emergency shutdown price as resolved from the DVM. If DVM has not resolved will revert. if (emergencyShutdownPrice.isEqual(FixedPoint.fromUnscaledUint(0))) { emergencyShutdownPrice = _getOracleEmergencyShutdownPrice(); } // Get caller's tokens balance and calculate amount of underlying entitled to them. FixedPoint.Unsigned memory tokensToRedeem = FixedPoint.Unsigned(tokenCurrency.balanceOf(msg.sender)); FixedPoint.Unsigned memory totalRedeemableCollateral = _getFundingRateAppliedTokenDebt(tokensToRedeem).mul(emergencyShutdownPrice); // If the caller is a sponsor with outstanding collateral they are also entitled to their excess collateral after their debt. PositionData storage positionData = positions[msg.sender]; if (_getFeeAdjustedCollateral(positionData.rawCollateral).isGreaterThan(0)) { // Calculate the underlying entitled to a token sponsor. This is collateral - debt in underlying with // the funding rate applied to the outstanding token debt. FixedPoint.Unsigned memory tokenDebtValueInCollateral = _getFundingRateAppliedTokenDebt(positionData.tokensOutstanding).mul(emergencyShutdownPrice); FixedPoint.Unsigned memory positionCollateral = _getFeeAdjustedCollateral(positionData.rawCollateral); // If the debt is greater than the remaining collateral, they cannot redeem anything. FixedPoint.Unsigned memory positionRedeemableCollateral = tokenDebtValueInCollateral.isLessThan(positionCollateral) ? positionCollateral.sub(tokenDebtValueInCollateral) : FixedPoint.Unsigned(0); // Add the number of redeemable tokens for the sponsor to their total redeemable collateral. totalRedeemableCollateral = totalRedeemableCollateral.add(positionRedeemableCollateral); // Reset the position state as all the value has been removed after settlement. delete positions[msg.sender]; emit EndedSponsorPosition(msg.sender); } // Take the min of the remaining collateral and the collateral "owed". If the contract is undercapitalized, // the caller will get as much collateral as the contract can pay out. FixedPoint.Unsigned memory payout = FixedPoint.min(_getFeeAdjustedCollateral(rawTotalPositionCollateral), totalRedeemableCollateral); // Decrement total contract collateral and outstanding debt. amountWithdrawn = _removeCollateral(rawTotalPositionCollateral, payout); totalTokensOutstanding = totalTokensOutstanding.sub(tokensToRedeem); emit SettleEmergencyShutdown(msg.sender, amountWithdrawn.rawValue, tokensToRedeem.rawValue); // Transfer tokens & collateral and burn the redeemed tokens. collateralCurrency.safeTransfer(msg.sender, amountWithdrawn.rawValue); tokenCurrency.safeTransferFrom(msg.sender, address(this), tokensToRedeem.rawValue); tokenCurrency.burn(tokensToRedeem.rawValue); } /**************************************** * GLOBAL STATE FUNCTIONS * ****************************************/ /** * @notice Premature contract settlement under emergency circumstances. * @dev Only the governor can call this function as they are permissioned within the `FinancialContractAdmin`. * Upon emergency shutdown, the contract settlement time is set to the shutdown time. This enables withdrawal * to occur via the `settleEmergencyShutdown` function. */ function emergencyShutdown() external override notEmergencyShutdown() fees() nonReentrant() { // Note: revert reason removed to save bytecode. require(msg.sender == _getFinancialContractsAdminAddress()); emergencyShutdownTimestamp = getCurrentTime(); _requestOraclePrice(emergencyShutdownTimestamp); emit EmergencyShutdown(msg.sender, emergencyShutdownTimestamp); } /** * @notice Theoretically supposed to pay fees and move money between margin accounts to make sure they * reflect the NAV of the contract. However, this functionality doesn't apply to this contract. * @dev This is supposed to be implemented by any contract that inherits `AdministrateeInterface` and callable * only by the Governor contract. This method is therefore minimally implemented in this contract and does nothing. */ function remargin() external override { return; } /** * @notice Accessor method for a sponsor's collateral. * @dev This is necessary because the struct returned by the positions() method shows * rawCollateral, which isn't a user-readable value. * @dev This method accounts for pending regular fees that have not yet been withdrawn from this contract, for * example if the `lastPaymentTime != currentTime`. * @param sponsor address whose collateral amount is retrieved. * @return collateralAmount amount of collateral within a sponsors position. */ function getCollateral(address sponsor) external view nonReentrantView() returns (FixedPoint.Unsigned memory collateralAmount) { // Note: do a direct access to avoid the validity check. return _getPendingRegularFeeAdjustedCollateral(_getFeeAdjustedCollateral(positions[sponsor].rawCollateral)); } /** * @notice Accessor method for the total collateral stored within the PerpetualPositionManager. * @return totalCollateral amount of all collateral within the position manager. */ function totalPositionCollateral() external view nonReentrantView() returns (FixedPoint.Unsigned memory totalCollateral) { return _getPendingRegularFeeAdjustedCollateral(_getFeeAdjustedCollateral(rawTotalPositionCollateral)); } function getFundingRateAppliedTokenDebt(FixedPoint.Unsigned memory rawTokenDebt) external view nonReentrantView() returns (FixedPoint.Unsigned memory totalCollateral) { return _getFundingRateAppliedTokenDebt(rawTokenDebt); } /**************************************** * INTERNAL FUNCTIONS * ****************************************/ // Reduces a sponsor's position and global counters by the specified parameters. Handles deleting the entire // position if the entire position is being removed. Does not make any external transfers. function _reduceSponsorPosition( address sponsor, FixedPoint.Unsigned memory tokensToRemove, FixedPoint.Unsigned memory collateralToRemove, FixedPoint.Unsigned memory withdrawalAmountToRemove ) internal { PositionData storage positionData = _getPositionData(sponsor); // If the entire position is being removed, delete it instead. if ( tokensToRemove.isEqual(positionData.tokensOutstanding) && _getFeeAdjustedCollateral(positionData.rawCollateral).isEqual(collateralToRemove) ) { _deleteSponsorPosition(sponsor); return; } // Decrement the sponsor's collateral and global collateral amounts. _decrementCollateralBalances(positionData, collateralToRemove); // Ensure that the sponsor will meet the min position size after the reduction. positionData.tokensOutstanding = positionData.tokensOutstanding.sub(tokensToRemove); require(positionData.tokensOutstanding.isGreaterThanOrEqual(minSponsorTokens)); // Decrement the position's withdrawal amount. positionData.withdrawalRequestAmount = positionData.withdrawalRequestAmount.sub(withdrawalAmountToRemove); // Decrement the total outstanding tokens in the overall contract. totalTokensOutstanding = totalTokensOutstanding.sub(tokensToRemove); } // Deletes a sponsor's position and updates global counters. Does not make any external transfers. function _deleteSponsorPosition(address sponsor) internal returns (FixedPoint.Unsigned memory) { PositionData storage positionToLiquidate = _getPositionData(sponsor); FixedPoint.Unsigned memory startingGlobalCollateral = _getFeeAdjustedCollateral(rawTotalPositionCollateral); // Remove the collateral and outstanding from the overall total position. rawTotalPositionCollateral = rawTotalPositionCollateral.sub(positionToLiquidate.rawCollateral); totalTokensOutstanding = totalTokensOutstanding.sub(positionToLiquidate.tokensOutstanding); // Reset the sponsors position to have zero outstanding and collateral. delete positions[sponsor]; emit EndedSponsorPosition(sponsor); // Return fee-adjusted amount of collateral deleted from position. return startingGlobalCollateral.sub(_getFeeAdjustedCollateral(rawTotalPositionCollateral)); } function _pfc() internal view virtual override returns (FixedPoint.Unsigned memory) { return _getFeeAdjustedCollateral(rawTotalPositionCollateral); } function _getPositionData(address sponsor) internal view onlyCollateralizedPosition(sponsor) returns (PositionData storage) { return positions[sponsor]; } function _getIdentifierWhitelist() internal view returns (IdentifierWhitelistInterface) { return IdentifierWhitelistInterface(finder.getImplementationAddress(OracleInterfaces.IdentifierWhitelist)); } function _getOracle() internal view returns (OracleInterface) { return OracleInterface(finder.getImplementationAddress(OracleInterfaces.Oracle)); } function _getFinancialContractsAdminAddress() internal view returns (address) { return finder.getImplementationAddress(OracleInterfaces.FinancialContractsAdmin); } // Requests a price for `priceIdentifier` at `requestedTime` from the Oracle. function _requestOraclePrice(uint256 requestedTime) internal { _getOracle().requestPrice(priceIdentifier, requestedTime); } // Fetches a resolved Oracle price from the Oracle. Reverts if the Oracle hasn't resolved for this request. function _getOraclePrice(uint256 requestedTime) internal view returns (FixedPoint.Unsigned memory price) { // Create an instance of the oracle and get the price. If the price is not resolved revert. int256 oraclePrice = _getOracle().getPrice(priceIdentifier, requestedTime); // For now we don't want to deal with negative prices in positions. if (oraclePrice < 0) { oraclePrice = 0; } return FixedPoint.Unsigned(uint256(oraclePrice)); } // Fetches a resolved Oracle price from the Oracle. Reverts if the Oracle hasn't resolved for this request. function _getOracleEmergencyShutdownPrice() internal view returns (FixedPoint.Unsigned memory) { return _getOraclePrice(emergencyShutdownTimestamp); } // Reset withdrawal request by setting the withdrawal request and withdrawal timestamp to 0. function _resetWithdrawalRequest(PositionData storage positionData) internal { positionData.withdrawalRequestAmount = FixedPoint.fromUnscaledUint(0); positionData.withdrawalRequestPassTimestamp = 0; } // Ensure individual and global consistency when increasing collateral balances. Returns the change to the position. function _incrementCollateralBalances( PositionData storage positionData, FixedPoint.Unsigned memory collateralAmount ) internal returns (FixedPoint.Unsigned memory) { _addCollateral(positionData.rawCollateral, collateralAmount); return _addCollateral(rawTotalPositionCollateral, collateralAmount); } // Ensure individual and global consistency when decrementing collateral balances. Returns the change to the // position. We elect to return the amount that the global collateral is decreased by, rather than the individual // position's collateral, because we need to maintain the invariant that the global collateral is always // <= the collateral owned by the contract to avoid reverts on withdrawals. The amount returned = amount withdrawn. function _decrementCollateralBalances( PositionData storage positionData, FixedPoint.Unsigned memory collateralAmount ) internal returns (FixedPoint.Unsigned memory) { _removeCollateral(positionData.rawCollateral, collateralAmount); return _removeCollateral(rawTotalPositionCollateral, collateralAmount); } // Ensure individual and global consistency when decrementing collateral balances. Returns the change to the position. // This function is similar to the _decrementCollateralBalances function except this function checks position GCR // between the decrements. This ensures that collateral removal will not leave the position undercollateralized. function _decrementCollateralBalancesCheckGCR( PositionData storage positionData, FixedPoint.Unsigned memory collateralAmount ) internal returns (FixedPoint.Unsigned memory) { _removeCollateral(positionData.rawCollateral, collateralAmount); require(_checkPositionCollateralization(positionData), "CR below GCR"); return _removeCollateral(rawTotalPositionCollateral, collateralAmount); } // These internal functions are supposed to act identically to modifiers, but re-used modifiers // unnecessarily increase contract bytecode size. // source: https://blog.polymath.network/solidity-tips-and-tricks-to-save-gas-and-reduce-bytecode-size-c44580b218e6 function _onlyCollateralizedPosition(address sponsor) internal view { require(_getFeeAdjustedCollateral(positions[sponsor].rawCollateral).isGreaterThan(0)); } // Note: This checks whether an already existing position has a pending withdrawal. This cannot be used on the // `create` method because it is possible that `create` is called on a new position (i.e. one without any collateral // or tokens outstanding) which would fail the `onlyCollateralizedPosition` modifier on `_getPositionData`. function _positionHasNoPendingWithdrawal(address sponsor) internal view { require(_getPositionData(sponsor).withdrawalRequestPassTimestamp == 0); } /**************************************** * PRIVATE FUNCTIONS * ****************************************/ function _checkPositionCollateralization(PositionData storage positionData) private view returns (bool) { return _checkCollateralization( _getFeeAdjustedCollateral(positionData.rawCollateral), positionData.tokensOutstanding ); } // Checks whether the provided `collateral` and `numTokens` have a collateralization ratio above the global // collateralization ratio. function _checkCollateralization(FixedPoint.Unsigned memory collateral, FixedPoint.Unsigned memory numTokens) private view returns (bool) { FixedPoint.Unsigned memory global = _getCollateralizationRatio(_getFeeAdjustedCollateral(rawTotalPositionCollateral), totalTokensOutstanding); FixedPoint.Unsigned memory thisChange = _getCollateralizationRatio(collateral, numTokens); return !global.isGreaterThan(thisChange); } function _getCollateralizationRatio(FixedPoint.Unsigned memory collateral, FixedPoint.Unsigned memory numTokens) private pure returns (FixedPoint.Unsigned memory ratio) { return numTokens.isLessThanOrEqual(0) ? FixedPoint.fromUnscaledUint(0) : collateral.div(numTokens); } function _getTokenAddress() internal view override returns (address) { return address(tokenCurrency); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/utils/SafeCast.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../../common/implementation/Lockable.sol"; import "../../common/implementation/FixedPoint.sol"; import "../../common/implementation/Testable.sol"; import "../../oracle/implementation/Constants.sol"; import "../../oracle/interfaces/OptimisticOracleInterface.sol"; import "../perpetual-multiparty/ConfigStoreInterface.sol"; import "./EmergencyShutdownable.sol"; import "./FeePayer.sol"; /** * @title FundingRateApplier contract. * @notice Provides funding rate payment functionality for the Perpetual contract. */ abstract contract FundingRateApplier is EmergencyShutdownable, FeePayer { using FixedPoint for FixedPoint.Unsigned; using FixedPoint for FixedPoint.Signed; using SafeERC20 for IERC20; using SafeMath for uint256; /**************************************** * FUNDING RATE APPLIER DATA STRUCTURES * ****************************************/ struct FundingRate { // Current funding rate value. FixedPoint.Signed rate; // Identifier to retrieve the funding rate. bytes32 identifier; // Tracks the cumulative funding payments that have been paid to the sponsors. // The multiplier starts at 1, and is updated by computing cumulativeFundingRateMultiplier * (1 + effectivePayment). // Put another way, the cumulativeFeeMultiplier is (1 + effectivePayment1) * (1 + effectivePayment2) ... // For example: // The cumulativeFundingRateMultiplier should start at 1. // If a 1% funding payment is paid to sponsors, the multiplier should update to 1.01. // If another 1% fee is charged, the multiplier should be 1.01^2 (1.0201). FixedPoint.Unsigned cumulativeMultiplier; // Most recent time that the funding rate was updated. uint256 updateTime; // Most recent time that the funding rate was applied and changed the cumulative multiplier. uint256 applicationTime; // The time for the active (if it exists) funding rate proposal. 0 otherwise. uint256 proposalTime; } FundingRate public fundingRate; // Remote config store managed an owner. ConfigStoreInterface public configStore; /**************************************** * EVENTS * ****************************************/ event FundingRateUpdated(int256 newFundingRate, uint256 indexed updateTime, uint256 reward); /**************************************** * MODIFIERS * ****************************************/ // This is overridden to both pay fees (which is done by applyFundingRate()) and apply the funding rate. modifier fees override { // Note: the funding rate is applied on every fee-accruing transaction, where the total change is simply the // rate applied linearly since the last update. This implies that the compounding rate depends on the frequency // of update transactions that have this modifier, and it never reaches the ideal of continuous compounding. // This approximate-compounding pattern is common in the Ethereum ecosystem because of the complexity of // compounding data on-chain. applyFundingRate(); _; } // Note: this modifier is intended to be used if the caller intends to _only_ pay regular fees. modifier paysRegularFees { payRegularFees(); _; } /** * @notice Constructs the FundingRateApplier contract. Called by child contracts. * @param _fundingRateIdentifier identifier that tracks the funding rate of this contract. * @param _collateralAddress address of the collateral token. * @param _finderAddress Finder used to discover financial-product-related contracts. * @param _configStoreAddress address of the remote configuration store managed by an external owner. * @param _tokenScaling initial scaling to apply to the token value (i.e. scales the tracking index). * @param _timerAddress address of the timer contract in test envs, otherwise 0x0. */ constructor( bytes32 _fundingRateIdentifier, address _collateralAddress, address _finderAddress, address _configStoreAddress, FixedPoint.Unsigned memory _tokenScaling, address _timerAddress ) public FeePayer(_collateralAddress, _finderAddress, _timerAddress) EmergencyShutdownable() { uint256 currentTime = getCurrentTime(); fundingRate.updateTime = currentTime; fundingRate.applicationTime = currentTime; // Seed the cumulative multiplier with the token scaling, from which it will be scaled as funding rates are // applied over time. fundingRate.cumulativeMultiplier = _tokenScaling; fundingRate.identifier = _fundingRateIdentifier; configStore = ConfigStoreInterface(_configStoreAddress); } /** * @notice This method takes 3 distinct actions: * 1. Pays out regular fees. * 2. If possible, resolves the outstanding funding rate proposal, pulling the result in and paying out the rewards. * 3. Applies the prevailing funding rate over the most recent period. */ function applyFundingRate() public paysRegularFees() nonReentrant() { _applyEffectiveFundingRate(); } /** * @notice Proposes a new funding rate. Proposer receives a reward if correct. * @param rate funding rate being proposed. * @param timestamp time at which the funding rate was computed. */ function proposeFundingRate(FixedPoint.Signed memory rate, uint256 timestamp) external fees() nonReentrant() returns (FixedPoint.Unsigned memory totalBond) { require(fundingRate.proposalTime == 0, "Proposal in progress"); _validateFundingRate(rate); // Timestamp must be after the last funding rate update time, within the last 30 minutes. uint256 currentTime = getCurrentTime(); uint256 updateTime = fundingRate.updateTime; require( timestamp > updateTime && timestamp >= currentTime.sub(_getConfig().proposalTimePastLimit), "Invalid proposal time" ); // Set the proposal time in order to allow this contract to track this request. fundingRate.proposalTime = timestamp; OptimisticOracleInterface optimisticOracle = _getOptimisticOracle(); // Set up optimistic oracle. bytes32 identifier = fundingRate.identifier; bytes memory ancillaryData = _getAncillaryData(); // Note: requestPrice will revert if `timestamp` is less than the current block timestamp. optimisticOracle.requestPrice(identifier, timestamp, ancillaryData, collateralCurrency, 0); totalBond = FixedPoint.Unsigned( optimisticOracle.setBond( identifier, timestamp, ancillaryData, _pfc().mul(_getConfig().proposerBondPercentage).rawValue ) ); // Pull bond from caller and send to optimistic oracle. if (totalBond.isGreaterThan(0)) { collateralCurrency.safeTransferFrom(msg.sender, address(this), totalBond.rawValue); collateralCurrency.safeIncreaseAllowance(address(optimisticOracle), totalBond.rawValue); } optimisticOracle.proposePriceFor( msg.sender, address(this), identifier, timestamp, ancillaryData, rate.rawValue ); } // Returns a token amount scaled by the current funding rate multiplier. // Note: if the contract has paid fees since it was deployed, the raw value should be larger than the returned value. function _getFundingRateAppliedTokenDebt(FixedPoint.Unsigned memory rawTokenDebt) internal view returns (FixedPoint.Unsigned memory tokenDebt) { return rawTokenDebt.mul(fundingRate.cumulativeMultiplier); } function _getOptimisticOracle() internal view returns (OptimisticOracleInterface) { return OptimisticOracleInterface(finder.getImplementationAddress(OracleInterfaces.OptimisticOracle)); } function _getConfig() internal returns (ConfigStoreInterface.ConfigSettings memory) { return configStore.updateAndGetCurrentConfig(); } function _updateFundingRate() internal { uint256 proposalTime = fundingRate.proposalTime; // If there is no pending proposal then do nothing. Otherwise check to see if we can update the funding rate. if (proposalTime != 0) { // Attempt to update the funding rate. OptimisticOracleInterface optimisticOracle = _getOptimisticOracle(); bytes32 identifier = fundingRate.identifier; bytes memory ancillaryData = _getAncillaryData(); // Try to get the price from the optimistic oracle. This call will revert if the request has not resolved // yet. If the request has not resolved yet, then we need to do additional checks to see if we should // "forget" the pending proposal and allow new proposals to update the funding rate. try optimisticOracle.settleAndGetPrice(identifier, proposalTime, ancillaryData) returns (int256 price) { // If successful, determine if the funding rate state needs to be updated. // If the request is more recent than the last update then we should update it. uint256 lastUpdateTime = fundingRate.updateTime; if (proposalTime >= lastUpdateTime) { // Update funding rates fundingRate.rate = FixedPoint.Signed(price); fundingRate.updateTime = proposalTime; // If there was no dispute, send a reward. FixedPoint.Unsigned memory reward = FixedPoint.fromUnscaledUint(0); OptimisticOracleInterface.Request memory request = optimisticOracle.getRequest(address(this), identifier, proposalTime, ancillaryData); if (request.disputer == address(0)) { reward = _pfc().mul(_getConfig().rewardRatePerSecond).mul(proposalTime.sub(lastUpdateTime)); if (reward.isGreaterThan(0)) { _adjustCumulativeFeeMultiplier(reward, _pfc()); collateralCurrency.safeTransfer(request.proposer, reward.rawValue); } } // This event will only be emitted after the fundingRate struct's "updateTime" has been set // to the latest proposal's proposalTime, indicating that the proposal has been published. // So, it suffices to just emit fundingRate.updateTime here. emit FundingRateUpdated(fundingRate.rate.rawValue, fundingRate.updateTime, reward.rawValue); } // Set proposal time to 0 since this proposal has now been resolved. fundingRate.proposalTime = 0; } catch { // Stop tracking and allow other proposals to come in if: // - The requester address is empty, indicating that the Oracle does not know about this funding rate // request. This is possible if the Oracle is replaced while the price request is still pending. // - The request has been disputed. OptimisticOracleInterface.Request memory request = optimisticOracle.getRequest(address(this), identifier, proposalTime, ancillaryData); if (request.disputer != address(0) || request.proposer == address(0)) { fundingRate.proposalTime = 0; } } } } // Constraining the range of funding rates limits the PfC for any dishonest proposer and enhances the // perpetual's security. For example, let's examine the case where the max and min funding rates // are equivalent to +/- 500%/year. This 1000% funding rate range allows a 8.6% profit from corruption for a // proposer who can deter honest proposers for 74 hours: // 1000%/year / 360 days / 24 hours * 74 hours max attack time = ~ 8.6%. // How would attack work? Imagine that the market is very volatile currently and that the "true" funding // rate for the next 74 hours is -500%, but a dishonest proposer successfully proposes a rate of +500% // (after a two hour liveness) and disputes honest proposers for the next 72 hours. This results in a funding // rate error of 1000% for 74 hours, until the DVM can set the funding rate back to its correct value. function _validateFundingRate(FixedPoint.Signed memory rate) internal { require( rate.isLessThanOrEqual(_getConfig().maxFundingRate) && rate.isGreaterThanOrEqual(_getConfig().minFundingRate) ); } // Fetches a funding rate from the Store, determines the period over which to compute an effective fee, // and multiplies the current multiplier by the effective fee. // A funding rate < 1 will reduce the multiplier, and a funding rate of > 1 will increase the multiplier. // Note: 1 is set as the neutral rate because there are no negative numbers in FixedPoint, so we decide to treat // values < 1 as "negative". function _applyEffectiveFundingRate() internal { // If contract is emergency shutdown, then the funding rate multiplier should no longer change. if (emergencyShutdownTimestamp != 0) { return; } uint256 currentTime = getCurrentTime(); uint256 paymentPeriod = currentTime.sub(fundingRate.applicationTime); _updateFundingRate(); // Update the funding rate if there is a resolved proposal. fundingRate.cumulativeMultiplier = _calculateEffectiveFundingRate( paymentPeriod, fundingRate.rate, fundingRate.cumulativeMultiplier ); fundingRate.applicationTime = currentTime; } function _calculateEffectiveFundingRate( uint256 paymentPeriodSeconds, FixedPoint.Signed memory fundingRatePerSecond, FixedPoint.Unsigned memory currentCumulativeFundingRateMultiplier ) internal pure returns (FixedPoint.Unsigned memory newCumulativeFundingRateMultiplier) { // Note: this method uses named return variables to save a little bytecode. // The overall formula that this function is performing: // newCumulativeFundingRateMultiplier = // (1 + (fundingRatePerSecond * paymentPeriodSeconds)) * currentCumulativeFundingRateMultiplier. FixedPoint.Signed memory ONE = FixedPoint.fromUnscaledInt(1); // Multiply the per-second rate over the number of seconds that have elapsed to get the period rate. FixedPoint.Signed memory periodRate = fundingRatePerSecond.mul(SafeCast.toInt256(paymentPeriodSeconds)); // Add one to create the multiplier to scale the existing fee multiplier. FixedPoint.Signed memory signedPeriodMultiplier = ONE.add(periodRate); // Max with 0 to ensure the multiplier isn't negative, then cast to an Unsigned. FixedPoint.Unsigned memory unsignedPeriodMultiplier = FixedPoint.fromSigned(FixedPoint.max(signedPeriodMultiplier, FixedPoint.fromUnscaledInt(0))); // Multiply the existing cumulative funding rate multiplier by the computed period multiplier to get the new // cumulative funding rate multiplier. newCumulativeFundingRateMultiplier = currentCumulativeFundingRateMultiplier.mul(unsignedPeriodMultiplier); } function _getAncillaryData() internal view returns (bytes memory) { // Note: when ancillary data is passed to the optimistic oracle, it should be tagged with the token address // whose funding rate it's trying to get. return abi.encodePacked(_getTokenAddress()); } function _getTokenAddress() internal view virtual returns (address); }
pragma solidity ^0.6.0; /** * @dev Wrappers over Solidity's uintXX casting operators with added overflow * checks. * * Downcasting from uint256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such 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. * * Can be combined with {SafeMath} to extend it to smaller types, by performing * all math on `uint256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits. */ function toUint8(uint256 value) internal pure returns (uint8) { require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { require(value < 2**255, "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/FixedPoint.sol"; interface ConfigStoreInterface { // All of the configuration settings available for querying by a perpetual. struct ConfigSettings { // Liveness period (in seconds) for an update to currentConfig to become official. uint256 timelockLiveness; // Reward rate paid to successful proposers. Percentage of 1 E.g., .1 is 10%. FixedPoint.Unsigned rewardRatePerSecond; // Bond % (of given contract's PfC) that must be staked by proposers. Percentage of 1, e.g. 0.0005 is 0.05%. FixedPoint.Unsigned proposerBondPercentage; // Maximum funding rate % per second that can be proposed. FixedPoint.Signed maxFundingRate; // Minimum funding rate % per second that can be proposed. FixedPoint.Signed minFundingRate; // Funding rate proposal timestamp cannot be more than this amount of seconds in the past from the latest // update time. uint256 proposalTimePastLimit; } function updateAndGetCurrentConfig() external returns (ConfigSettings memory); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/math/SafeMath.sol"; /** * @title EmergencyShutdownable contract. * @notice Any contract that inherits this contract will have an emergency shutdown timestamp state variable. * This contract provides modifiers that can be used by children contracts to determine if the contract is * in the shutdown state. The child contract is expected to implement the logic that happens * once a shutdown occurs. */ abstract contract EmergencyShutdownable { using SafeMath for uint256; /**************************************** * EMERGENCY SHUTDOWN DATA STRUCTURES * ****************************************/ // Timestamp used in case of emergency shutdown. 0 if no shutdown has been triggered. uint256 public emergencyShutdownTimestamp; /**************************************** * MODIFIERS * ****************************************/ modifier notEmergencyShutdown() { _notEmergencyShutdown(); _; } modifier isEmergencyShutdown() { _isEmergencyShutdown(); _; } /**************************************** * EXTERNAL FUNCTIONS * ****************************************/ constructor() public { emergencyShutdownTimestamp = 0; } /**************************************** * INTERNAL FUNCTIONS * ****************************************/ function _notEmergencyShutdown() internal view { // Note: removed require string to save bytecode. require(emergencyShutdownTimestamp == 0); } function _isEmergencyShutdown() internal view { // Note: removed require string to save bytecode. require(emergencyShutdownTimestamp != 0); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/interfaces/ExpandedIERC20.sol"; import "../../common/interfaces/IERC20Standard.sol"; import "../../oracle/implementation/ContractCreator.sol"; import "../../common/implementation/Testable.sol"; import "../../common/implementation/AddressWhitelist.sol"; import "../../common/implementation/Lockable.sol"; import "../common/TokenFactory.sol"; import "../common/SyntheticToken.sol"; import "./ExpiringMultiPartyLib.sol"; /** * @title Expiring Multi Party Contract creator. * @notice Factory contract to create and register new instances of expiring multiparty contracts. * Responsible for constraining the parameters used to construct a new EMP. This creator contains a number of constraints * that are applied to newly created expiring multi party contract. These constraints can evolve over time and are * initially constrained to conservative values in this first iteration. Technically there is nothing in the * ExpiringMultiParty contract requiring these constraints. However, because `createExpiringMultiParty()` is intended * to be the only way to create valid financial contracts that are registered with the DVM (via _registerContract), we can enforce deployment configurations here. */ contract ExpiringMultiPartyCreator is ContractCreator, Testable, Lockable { using FixedPoint for FixedPoint.Unsigned; /**************************************** * EMP CREATOR DATA STRUCTURES * ****************************************/ struct Params { uint256 expirationTimestamp; address collateralAddress; bytes32 priceFeedIdentifier; string syntheticName; string syntheticSymbol; FixedPoint.Unsigned collateralRequirement; FixedPoint.Unsigned disputeBondPercentage; FixedPoint.Unsigned sponsorDisputeRewardPercentage; FixedPoint.Unsigned disputerDisputeRewardPercentage; FixedPoint.Unsigned minSponsorTokens; uint256 withdrawalLiveness; uint256 liquidationLiveness; address financialProductLibraryAddress; } // Address of TokenFactory used to create a new synthetic token. address public tokenFactoryAddress; event CreatedExpiringMultiParty(address indexed expiringMultiPartyAddress, address indexed deployerAddress); /** * @notice Constructs the ExpiringMultiPartyCreator contract. * @param _finderAddress UMA protocol Finder used to discover other protocol contracts. * @param _tokenFactoryAddress ERC20 token factory used to deploy synthetic token instances. * @param _timerAddress Contract that stores the current time in a testing environment. */ constructor( address _finderAddress, address _tokenFactoryAddress, address _timerAddress ) public ContractCreator(_finderAddress) Testable(_timerAddress) nonReentrant() { tokenFactoryAddress = _tokenFactoryAddress; } /** * @notice Creates an instance of expiring multi party and registers it within the registry. * @param params is a `ConstructorParams` object from ExpiringMultiParty. * @return address of the deployed ExpiringMultiParty contract. */ function createExpiringMultiParty(Params memory params) public nonReentrant() returns (address) { // Create a new synthetic token using the params. require(bytes(params.syntheticName).length != 0, "Missing synthetic name"); require(bytes(params.syntheticSymbol).length != 0, "Missing synthetic symbol"); TokenFactory tf = TokenFactory(tokenFactoryAddress); // If the collateral token does not have a `decimals()` method, then a default precision of 18 will be // applied to the newly created synthetic token. uint8 syntheticDecimals = _getSyntheticDecimals(params.collateralAddress); ExpandedIERC20 tokenCurrency = tf.createToken(params.syntheticName, params.syntheticSymbol, syntheticDecimals); address derivative = ExpiringMultiPartyLib.deploy(_convertParams(params, tokenCurrency)); // Give permissions to new derivative contract and then hand over ownership. tokenCurrency.addMinter(derivative); tokenCurrency.addBurner(derivative); tokenCurrency.resetOwner(derivative); _registerContract(new address[](0), derivative); emit CreatedExpiringMultiParty(derivative, msg.sender); return derivative; } /**************************************** * PRIVATE FUNCTIONS * ****************************************/ // Converts createExpiringMultiParty params to ExpiringMultiParty constructor params. function _convertParams(Params memory params, ExpandedIERC20 newTokenCurrency) private view returns (ExpiringMultiParty.ConstructorParams memory constructorParams) { // Known from creator deployment. constructorParams.finderAddress = finderAddress; constructorParams.timerAddress = timerAddress; // Enforce configuration constraints. require(params.withdrawalLiveness != 0, "Withdrawal liveness cannot be 0"); require(params.liquidationLiveness != 0, "Liquidation liveness cannot be 0"); require(params.expirationTimestamp > now, "Invalid expiration time"); _requireWhitelistedCollateral(params.collateralAddress); // We don't want EMP deployers to be able to intentionally or unintentionally set // liveness periods that could induce arithmetic overflow, but we also don't want // to be opinionated about what livenesses are "correct", so we will somewhat // arbitrarily set the liveness upper bound to 100 years (5200 weeks). In practice, liveness // periods even greater than a few days would make the EMP unusable for most users. require(params.withdrawalLiveness < 5200 weeks, "Withdrawal liveness too large"); require(params.liquidationLiveness < 5200 weeks, "Liquidation liveness too large"); // Input from function call. constructorParams.tokenAddress = address(newTokenCurrency); constructorParams.expirationTimestamp = params.expirationTimestamp; constructorParams.collateralAddress = params.collateralAddress; constructorParams.priceFeedIdentifier = params.priceFeedIdentifier; constructorParams.collateralRequirement = params.collateralRequirement; constructorParams.disputeBondPercentage = params.disputeBondPercentage; constructorParams.sponsorDisputeRewardPercentage = params.sponsorDisputeRewardPercentage; constructorParams.disputerDisputeRewardPercentage = params.disputerDisputeRewardPercentage; constructorParams.minSponsorTokens = params.minSponsorTokens; constructorParams.withdrawalLiveness = params.withdrawalLiveness; constructorParams.liquidationLiveness = params.liquidationLiveness; constructorParams.financialProductLibraryAddress = params.financialProductLibraryAddress; } // IERC20Standard.decimals() will revert if the collateral contract has not implemented the decimals() method, // which is possible since the method is only an OPTIONAL method in the ERC20 standard: // https://eips.ethereum.org/EIPS/eip-20#methods. function _getSyntheticDecimals(address _collateralAddress) public view returns (uint8 decimals) { try IERC20Standard(_collateralAddress).decimals() returns (uint8 _decimals) { return _decimals; } catch { return 18; } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "./ExpiringMultiParty.sol"; /** * @title Provides convenient Expiring Multi Party contract utilities. * @dev Using this library to deploy EMP's allows calling contracts to avoid importing the full EMP bytecode. */ library ExpiringMultiPartyLib { /** * @notice Returns address of new EMP deployed with given `params` configuration. * @dev Caller will need to register new EMP with the Registry to begin requesting prices. Caller is also * responsible for enforcing constraints on `params`. * @param params is a `ConstructorParams` object from ExpiringMultiParty. * @return address of the deployed ExpiringMultiParty contract */ function deploy(ExpiringMultiParty.ConstructorParams memory params) public returns (address) { ExpiringMultiParty derivative = new ExpiringMultiParty(params); return address(derivative); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "./PricelessPositionManager.sol"; import "../../common/implementation/FixedPoint.sol"; /** * @title Liquidatable * @notice Adds logic to a position-managing contract that enables callers to liquidate an undercollateralized position. * @dev The liquidation has a liveness period before expiring successfully, during which someone can "dispute" the * liquidation, which sends a price request to the relevant Oracle to settle the final collateralization ratio based on * a DVM price. The contract enforces dispute rewards in order to incentivize disputers to correctly dispute false * liquidations and compensate position sponsors who had their position incorrectly liquidated. Importantly, a * prospective disputer must deposit a dispute bond that they can lose in the case of an unsuccessful dispute. * NOTE: this contract does _not_ work with ERC777 collateral currencies or any others that call into the receiver on * transfer(). Using an ERC777 token would allow a user to maliciously grief other participants (while also losing * money themselves). */ contract Liquidatable is PricelessPositionManager { using FixedPoint for FixedPoint.Unsigned; using SafeMath for uint256; using SafeERC20 for IERC20; using Address for address; /**************************************** * LIQUIDATION DATA STRUCTURES * ****************************************/ // Because of the check in withdrawable(), the order of these enum values should not change. enum Status { Uninitialized, NotDisputed, Disputed, DisputeSucceeded, DisputeFailed } struct LiquidationData { // Following variables set upon creation of liquidation: address sponsor; // Address of the liquidated position's sponsor address liquidator; // Address who created this liquidation Status state; // Liquidated (and expired or not), Pending a Dispute, or Dispute has resolved uint256 liquidationTime; // Time when liquidation is initiated, needed to get price from Oracle // Following variables determined by the position that is being liquidated: FixedPoint.Unsigned tokensOutstanding; // Synthetic tokens required to be burned by liquidator to initiate dispute FixedPoint.Unsigned lockedCollateral; // Collateral locked by contract and released upon expiry or post-dispute // Amount of collateral being liquidated, which could be different from // lockedCollateral if there were pending withdrawals at the time of liquidation FixedPoint.Unsigned liquidatedCollateral; // Unit value (starts at 1) that is used to track the fees per unit of collateral over the course of the liquidation. FixedPoint.Unsigned rawUnitCollateral; // Following variable set upon initiation of a dispute: address disputer; // Person who is disputing a liquidation // Following variable set upon a resolution of a dispute: FixedPoint.Unsigned settlementPrice; // Final price as determined by an Oracle following a dispute FixedPoint.Unsigned finalFee; } // Define the contract's constructor parameters as a struct to enable more variables to be specified. // This is required to enable more params, over and above Solidity's limits. struct ConstructorParams { // Params for PricelessPositionManager only. uint256 expirationTimestamp; uint256 withdrawalLiveness; address collateralAddress; address tokenAddress; address finderAddress; address timerAddress; address financialProductLibraryAddress; bytes32 priceFeedIdentifier; FixedPoint.Unsigned minSponsorTokens; // Params specifically for Liquidatable. uint256 liquidationLiveness; FixedPoint.Unsigned collateralRequirement; FixedPoint.Unsigned disputeBondPercentage; FixedPoint.Unsigned sponsorDisputeRewardPercentage; FixedPoint.Unsigned disputerDisputeRewardPercentage; } // This struct is used in the `withdrawLiquidation` method that disperses liquidation and dispute rewards. // `payToX` stores the total collateral to withdraw from the contract to pay X. This value might differ // from `paidToX` due to precision loss between accounting for the `rawCollateral` versus the // fee-adjusted collateral. These variables are stored within a struct to avoid the stack too deep error. struct RewardsData { FixedPoint.Unsigned payToSponsor; FixedPoint.Unsigned payToLiquidator; FixedPoint.Unsigned payToDisputer; FixedPoint.Unsigned paidToSponsor; FixedPoint.Unsigned paidToLiquidator; FixedPoint.Unsigned paidToDisputer; } // Liquidations are unique by ID per sponsor mapping(address => LiquidationData[]) public liquidations; // Total collateral in liquidation. FixedPoint.Unsigned public rawLiquidationCollateral; // Immutable contract parameters: // Amount of time for pending liquidation before expiry. // !!Note: The lower the liquidation liveness value, the more risk incurred by sponsors. // Extremely low liveness values increase the chance that opportunistic invalid liquidations // expire without dispute, thereby decreasing the usability for sponsors and increasing the risk // for the contract as a whole. An insolvent contract is extremely risky for any sponsor or synthetic // token holder for the contract. uint256 public liquidationLiveness; // Required collateral:TRV ratio for a position to be considered sufficiently collateralized. FixedPoint.Unsigned public collateralRequirement; // Percent of a Liquidation/Position's lockedCollateral to be deposited by a potential disputer // Represented as a multiplier, for example 1.5e18 = "150%" and 0.05e18 = "5%" FixedPoint.Unsigned public disputeBondPercentage; // Percent of oraclePrice paid to sponsor in the Disputed state (i.e. following a successful dispute) // Represented as a multiplier, see above. FixedPoint.Unsigned public sponsorDisputeRewardPercentage; // Percent of oraclePrice paid to disputer in the Disputed state (i.e. following a successful dispute) // Represented as a multiplier, see above. FixedPoint.Unsigned public disputerDisputeRewardPercentage; /**************************************** * EVENTS * ****************************************/ event LiquidationCreated( address indexed sponsor, address indexed liquidator, uint256 indexed liquidationId, uint256 tokensOutstanding, uint256 lockedCollateral, uint256 liquidatedCollateral, uint256 liquidationTime ); event LiquidationDisputed( address indexed sponsor, address indexed liquidator, address indexed disputer, uint256 liquidationId, uint256 disputeBondAmount ); event DisputeSettled( address indexed caller, address indexed sponsor, address indexed liquidator, address disputer, uint256 liquidationId, bool disputeSucceeded ); event LiquidationWithdrawn( address indexed caller, uint256 paidToLiquidator, uint256 paidToDisputer, uint256 paidToSponsor, Status indexed liquidationStatus, uint256 settlementPrice ); /**************************************** * MODIFIERS * ****************************************/ modifier disputable(uint256 liquidationId, address sponsor) { _disputable(liquidationId, sponsor); _; } modifier withdrawable(uint256 liquidationId, address sponsor) { _withdrawable(liquidationId, sponsor); _; } /** * @notice Constructs the liquidatable contract. * @param params struct to define input parameters for construction of Liquidatable. Some params * are fed directly into the PricelessPositionManager's constructor within the inheritance tree. */ constructor(ConstructorParams memory params) public PricelessPositionManager( params.expirationTimestamp, params.withdrawalLiveness, params.collateralAddress, params.tokenAddress, params.finderAddress, params.priceFeedIdentifier, params.minSponsorTokens, params.timerAddress, params.financialProductLibraryAddress ) nonReentrant() { require(params.collateralRequirement.isGreaterThan(1)); require(params.sponsorDisputeRewardPercentage.add(params.disputerDisputeRewardPercentage).isLessThan(1)); // Set liquidatable specific variables. liquidationLiveness = params.liquidationLiveness; collateralRequirement = params.collateralRequirement; disputeBondPercentage = params.disputeBondPercentage; sponsorDisputeRewardPercentage = params.sponsorDisputeRewardPercentage; disputerDisputeRewardPercentage = params.disputerDisputeRewardPercentage; } /**************************************** * LIQUIDATION FUNCTIONS * ****************************************/ /** * @notice Liquidates the sponsor's position if the caller has enough * synthetic tokens to retire the position's outstanding tokens. Liquidations above * a minimum size also reset an ongoing "slow withdrawal"'s liveness. * @dev This method generates an ID that will uniquely identify liquidation for the sponsor. This contract must be * approved to spend at least `tokensLiquidated` of `tokenCurrency` and at least `finalFeeBond` of `collateralCurrency`. * @dev This contract must have the Burner role for the `tokenCurrency`. * @param sponsor address of the sponsor to liquidate. * @param minCollateralPerToken abort the liquidation if the position's collateral per token is below this value. * @param maxCollateralPerToken abort the liquidation if the position's collateral per token exceeds this value. * @param maxTokensToLiquidate max number of tokens to liquidate. * @param deadline abort the liquidation if the transaction is mined after this timestamp. * @return liquidationId ID of the newly created liquidation. * @return tokensLiquidated amount of synthetic tokens removed and liquidated from the `sponsor`'s position. * @return finalFeeBond amount of collateral to be posted by liquidator and returned if not disputed successfully. */ function createLiquidation( address sponsor, FixedPoint.Unsigned calldata minCollateralPerToken, FixedPoint.Unsigned calldata maxCollateralPerToken, FixedPoint.Unsigned calldata maxTokensToLiquidate, uint256 deadline ) external fees() onlyPreExpiration() nonReentrant() returns ( uint256 liquidationId, FixedPoint.Unsigned memory tokensLiquidated, FixedPoint.Unsigned memory finalFeeBond ) { // Check that this transaction was mined pre-deadline. require(getCurrentTime() <= deadline, "Mined after deadline"); // Retrieve Position data for sponsor PositionData storage positionToLiquidate = _getPositionData(sponsor); tokensLiquidated = FixedPoint.min(maxTokensToLiquidate, positionToLiquidate.tokensOutstanding); require(tokensLiquidated.isGreaterThan(0)); // Starting values for the Position being liquidated. If withdrawal request amount is > position's collateral, // then set this to 0, otherwise set it to (startCollateral - withdrawal request amount). FixedPoint.Unsigned memory startCollateral = _getFeeAdjustedCollateral(positionToLiquidate.rawCollateral); FixedPoint.Unsigned memory startCollateralNetOfWithdrawal = FixedPoint.fromUnscaledUint(0); if (positionToLiquidate.withdrawalRequestAmount.isLessThanOrEqual(startCollateral)) { startCollateralNetOfWithdrawal = startCollateral.sub(positionToLiquidate.withdrawalRequestAmount); } // Scoping to get rid of a stack too deep error. { FixedPoint.Unsigned memory startTokens = positionToLiquidate.tokensOutstanding; // The Position's collateralization ratio must be between [minCollateralPerToken, maxCollateralPerToken]. // maxCollateralPerToken >= startCollateralNetOfWithdrawal / startTokens. require( maxCollateralPerToken.mul(startTokens).isGreaterThanOrEqual(startCollateralNetOfWithdrawal), "CR is more than max liq. price" ); // minCollateralPerToken >= startCollateralNetOfWithdrawal / startTokens. require( minCollateralPerToken.mul(startTokens).isLessThanOrEqual(startCollateralNetOfWithdrawal), "CR is less than min liq. price" ); } // Compute final fee at time of liquidation. finalFeeBond = _computeFinalFees(); // These will be populated within the scope below. FixedPoint.Unsigned memory lockedCollateral; FixedPoint.Unsigned memory liquidatedCollateral; // Scoping to get rid of a stack too deep error. { FixedPoint.Unsigned memory ratio = tokensLiquidated.div(positionToLiquidate.tokensOutstanding); // The actual amount of collateral that gets moved to the liquidation. lockedCollateral = startCollateral.mul(ratio); // For purposes of disputes, it's actually this liquidatedCollateral value that's used. This value is net of // withdrawal requests. liquidatedCollateral = startCollateralNetOfWithdrawal.mul(ratio); // Part of the withdrawal request is also removed. Ideally: // liquidatedCollateral + withdrawalAmountToRemove = lockedCollateral. FixedPoint.Unsigned memory withdrawalAmountToRemove = positionToLiquidate.withdrawalRequestAmount.mul(ratio); _reduceSponsorPosition(sponsor, tokensLiquidated, lockedCollateral, withdrawalAmountToRemove); } // Add to the global liquidation collateral count. _addCollateral(rawLiquidationCollateral, lockedCollateral.add(finalFeeBond)); // Construct liquidation object. // Note: All dispute-related values are zeroed out until a dispute occurs. liquidationId is the index of the new // LiquidationData that is pushed into the array, which is equal to the current length of the array pre-push. liquidationId = liquidations[sponsor].length; liquidations[sponsor].push( LiquidationData({ sponsor: sponsor, liquidator: msg.sender, state: Status.NotDisputed, liquidationTime: getCurrentTime(), tokensOutstanding: tokensLiquidated, lockedCollateral: lockedCollateral, liquidatedCollateral: liquidatedCollateral, rawUnitCollateral: _convertToRawCollateral(FixedPoint.fromUnscaledUint(1)), disputer: address(0), settlementPrice: FixedPoint.fromUnscaledUint(0), finalFee: finalFeeBond }) ); // If this liquidation is a subsequent liquidation on the position, and the liquidation size is larger than // some "griefing threshold", then re-set the liveness. This enables a liquidation against a withdraw request to be // "dragged out" if the position is very large and liquidators need time to gather funds. The griefing threshold // is enforced so that liquidations for trivially small # of tokens cannot drag out an honest sponsor's slow withdrawal. // We arbitrarily set the "griefing threshold" to `minSponsorTokens` because it is the only parameter // denominated in token currency units and we can avoid adding another parameter. FixedPoint.Unsigned memory griefingThreshold = minSponsorTokens; if ( positionToLiquidate.withdrawalRequestPassTimestamp > 0 && // The position is undergoing a slow withdrawal. positionToLiquidate.withdrawalRequestPassTimestamp > getCurrentTime() && // The slow withdrawal has not yet expired. tokensLiquidated.isGreaterThanOrEqual(griefingThreshold) // The liquidated token count is above a "griefing threshold". ) { positionToLiquidate.withdrawalRequestPassTimestamp = getCurrentTime().add(withdrawalLiveness); } emit LiquidationCreated( sponsor, msg.sender, liquidationId, tokensLiquidated.rawValue, lockedCollateral.rawValue, liquidatedCollateral.rawValue, getCurrentTime() ); // Destroy tokens tokenCurrency.safeTransferFrom(msg.sender, address(this), tokensLiquidated.rawValue); tokenCurrency.burn(tokensLiquidated.rawValue); // Pull final fee from liquidator. collateralCurrency.safeTransferFrom(msg.sender, address(this), finalFeeBond.rawValue); } /** * @notice Disputes a liquidation, if the caller has enough collateral to post a dispute bond * and pay a fixed final fee charged on each price request. * @dev Can only dispute a liquidation before the liquidation expires and if there are no other pending disputes. * This contract must be approved to spend at least the dispute bond amount of `collateralCurrency`. This dispute * bond amount is calculated from `disputeBondPercentage` times the collateral in the liquidation. * @param liquidationId of the disputed liquidation. * @param sponsor the address of the sponsor whose liquidation is being disputed. * @return totalPaid amount of collateral charged to disputer (i.e. final fee bond + dispute bond). */ function dispute(uint256 liquidationId, address sponsor) external disputable(liquidationId, sponsor) fees() nonReentrant() returns (FixedPoint.Unsigned memory totalPaid) { LiquidationData storage disputedLiquidation = _getLiquidationData(sponsor, liquidationId); // Multiply by the unit collateral so the dispute bond is a percentage of the locked collateral after fees. FixedPoint.Unsigned memory disputeBondAmount = disputedLiquidation.lockedCollateral.mul(disputeBondPercentage).mul( _getFeeAdjustedCollateral(disputedLiquidation.rawUnitCollateral) ); _addCollateral(rawLiquidationCollateral, disputeBondAmount); // Request a price from DVM. Liquidation is pending dispute until DVM returns a price. disputedLiquidation.state = Status.Disputed; disputedLiquidation.disputer = msg.sender; // Enqueue a request with the DVM. _requestOraclePriceLiquidation(disputedLiquidation.liquidationTime); emit LiquidationDisputed( sponsor, disputedLiquidation.liquidator, msg.sender, liquidationId, disputeBondAmount.rawValue ); totalPaid = disputeBondAmount.add(disputedLiquidation.finalFee); // Pay the final fee for requesting price from the DVM. _payFinalFees(msg.sender, disputedLiquidation.finalFee); // Transfer the dispute bond amount from the caller to this contract. collateralCurrency.safeTransferFrom(msg.sender, address(this), disputeBondAmount.rawValue); } /** * @notice After a dispute has settled or after a non-disputed liquidation has expired, * anyone can call this method to disperse payments to the sponsor, liquidator, and disdputer. * @dev If the dispute SUCCEEDED: the sponsor, liquidator, and disputer are eligible for payment. * If the dispute FAILED: only the liquidator can receive payment. * This method will revert if rewards have already been dispersed. * @param liquidationId uniquely identifies the sponsor's liquidation. * @param sponsor address of the sponsor associated with the liquidation. * @return data about rewards paid out. */ function withdrawLiquidation(uint256 liquidationId, address sponsor) public withdrawable(liquidationId, sponsor) fees() nonReentrant() returns (RewardsData memory) { LiquidationData storage liquidation = _getLiquidationData(sponsor, liquidationId); // Settles the liquidation if necessary. This call will revert if the price has not resolved yet. _settle(liquidationId, sponsor); // Calculate rewards as a function of the TRV. // Note: all payouts are scaled by the unit collateral value so all payouts are charged the fees pro rata. FixedPoint.Unsigned memory feeAttenuation = _getFeeAdjustedCollateral(liquidation.rawUnitCollateral); FixedPoint.Unsigned memory settlementPrice = liquidation.settlementPrice; FixedPoint.Unsigned memory tokenRedemptionValue = liquidation.tokensOutstanding.mul(settlementPrice).mul(feeAttenuation); FixedPoint.Unsigned memory collateral = liquidation.lockedCollateral.mul(feeAttenuation); FixedPoint.Unsigned memory disputerDisputeReward = disputerDisputeRewardPercentage.mul(tokenRedemptionValue); FixedPoint.Unsigned memory sponsorDisputeReward = sponsorDisputeRewardPercentage.mul(tokenRedemptionValue); FixedPoint.Unsigned memory disputeBondAmount = collateral.mul(disputeBondPercentage); FixedPoint.Unsigned memory finalFee = liquidation.finalFee.mul(feeAttenuation); // There are three main outcome states: either the dispute succeeded, failed or was not updated. // Based on the state, different parties of a liquidation receive different amounts. // After assigning rewards based on the liquidation status, decrease the total collateral held in this contract // by the amount to pay each party. The actual amounts withdrawn might differ if _removeCollateral causes // precision loss. RewardsData memory rewards; if (liquidation.state == Status.DisputeSucceeded) { // If the dispute is successful then all three users should receive rewards: // Pay DISPUTER: disputer reward + dispute bond + returned final fee rewards.payToDisputer = disputerDisputeReward.add(disputeBondAmount).add(finalFee); // Pay SPONSOR: remaining collateral (collateral - TRV) + sponsor reward rewards.payToSponsor = sponsorDisputeReward.add(collateral.sub(tokenRedemptionValue)); // Pay LIQUIDATOR: TRV - dispute reward - sponsor reward // If TRV > Collateral, then subtract rewards from collateral // NOTE: This should never be below zero since we prevent (sponsorDisputePct+disputerDisputePct) >= 0 in // the constructor when these params are set. rewards.payToLiquidator = tokenRedemptionValue.sub(sponsorDisputeReward).sub(disputerDisputeReward); // Transfer rewards and debit collateral rewards.paidToLiquidator = _removeCollateral(rawLiquidationCollateral, rewards.payToLiquidator); rewards.paidToSponsor = _removeCollateral(rawLiquidationCollateral, rewards.payToSponsor); rewards.paidToDisputer = _removeCollateral(rawLiquidationCollateral, rewards.payToDisputer); collateralCurrency.safeTransfer(liquidation.disputer, rewards.paidToDisputer.rawValue); collateralCurrency.safeTransfer(liquidation.liquidator, rewards.paidToLiquidator.rawValue); collateralCurrency.safeTransfer(liquidation.sponsor, rewards.paidToSponsor.rawValue); // In the case of a failed dispute only the liquidator can withdraw. } else if (liquidation.state == Status.DisputeFailed) { // Pay LIQUIDATOR: collateral + dispute bond + returned final fee rewards.payToLiquidator = collateral.add(disputeBondAmount).add(finalFee); // Transfer rewards and debit collateral rewards.paidToLiquidator = _removeCollateral(rawLiquidationCollateral, rewards.payToLiquidator); collateralCurrency.safeTransfer(liquidation.liquidator, rewards.paidToLiquidator.rawValue); // If the state is pre-dispute but time has passed liveness then there was no dispute. We represent this // state as a dispute failed and the liquidator can withdraw. } else if (liquidation.state == Status.NotDisputed) { // Pay LIQUIDATOR: collateral + returned final fee rewards.payToLiquidator = collateral.add(finalFee); // Transfer rewards and debit collateral rewards.paidToLiquidator = _removeCollateral(rawLiquidationCollateral, rewards.payToLiquidator); collateralCurrency.safeTransfer(liquidation.liquidator, rewards.paidToLiquidator.rawValue); } emit LiquidationWithdrawn( msg.sender, rewards.paidToLiquidator.rawValue, rewards.paidToDisputer.rawValue, rewards.paidToSponsor.rawValue, liquidation.state, settlementPrice.rawValue ); // Free up space after collateral is withdrawn by removing the liquidation object from the array. delete liquidations[sponsor][liquidationId]; return rewards; } /** * @notice Gets all liquidation information for a given sponsor address. * @param sponsor address of the position sponsor. * @return liquidationData array of all liquidation information for the given sponsor address. */ function getLiquidations(address sponsor) external view nonReentrantView() returns (LiquidationData[] memory liquidationData) { return liquidations[sponsor]; } /** * @notice Accessor method to calculate a transformed collateral requirement using the finanical product library specified during contract deployment. If no library was provided then no modification to the collateral requirement is done. * @param price input price used as an input to transform the collateral requirement. * @return transformedCollateralRequirement collateral requirement with transformation applied to it. * @dev This method should never revert. */ function transformCollateralRequirement(FixedPoint.Unsigned memory price) public view nonReentrantView() returns (FixedPoint.Unsigned memory) { return _transformCollateralRequirement(price); } /**************************************** * INTERNAL FUNCTIONS * ****************************************/ // This settles a liquidation if it is in the Disputed state. If not, it will immediately return. // If the liquidation is in the Disputed state, but a price is not available, this will revert. function _settle(uint256 liquidationId, address sponsor) internal { LiquidationData storage liquidation = _getLiquidationData(sponsor, liquidationId); // Settlement only happens when state == Disputed and will only happen once per liquidation. // If this liquidation is not ready to be settled, this method should return immediately. if (liquidation.state != Status.Disputed) { return; } // Get the returned price from the oracle. If this has not yet resolved will revert. liquidation.settlementPrice = _getOraclePriceLiquidation(liquidation.liquidationTime); // Find the value of the tokens in the underlying collateral. FixedPoint.Unsigned memory tokenRedemptionValue = liquidation.tokensOutstanding.mul(liquidation.settlementPrice); // The required collateral is the value of the tokens in underlying * required collateral ratio. The Transform // Collateral requirement method applies a from the financial Product library to change the scaled the collateral // requirement based on the settlement price. If no library was specified when deploying the emp then this makes no change. FixedPoint.Unsigned memory requiredCollateral = tokenRedemptionValue.mul(_transformCollateralRequirement(liquidation.settlementPrice)); // If the position has more than the required collateral it is solvent and the dispute is valid(liquidation is invalid) // Note that this check uses the liquidatedCollateral not the lockedCollateral as this considers withdrawals. bool disputeSucceeded = liquidation.liquidatedCollateral.isGreaterThanOrEqual(requiredCollateral); liquidation.state = disputeSucceeded ? Status.DisputeSucceeded : Status.DisputeFailed; emit DisputeSettled( msg.sender, sponsor, liquidation.liquidator, liquidation.disputer, liquidationId, disputeSucceeded ); } function _pfc() internal view override returns (FixedPoint.Unsigned memory) { return super._pfc().add(_getFeeAdjustedCollateral(rawLiquidationCollateral)); } function _getLiquidationData(address sponsor, uint256 liquidationId) internal view returns (LiquidationData storage liquidation) { LiquidationData[] storage liquidationArray = liquidations[sponsor]; // Revert if the caller is attempting to access an invalid liquidation // (one that has never been created or one has never been initialized). require( liquidationId < liquidationArray.length && liquidationArray[liquidationId].state != Status.Uninitialized, "Invalid liquidation ID" ); return liquidationArray[liquidationId]; } function _getLiquidationExpiry(LiquidationData storage liquidation) internal view returns (uint256) { return liquidation.liquidationTime.add(liquidationLiveness); } // These internal functions are supposed to act identically to modifiers, but re-used modifiers // unnecessarily increase contract bytecode size. // source: https://blog.polymath.network/solidity-tips-and-tricks-to-save-gas-and-reduce-bytecode-size-c44580b218e6 function _disputable(uint256 liquidationId, address sponsor) internal view { LiquidationData storage liquidation = _getLiquidationData(sponsor, liquidationId); require( (getCurrentTime() < _getLiquidationExpiry(liquidation)) && (liquidation.state == Status.NotDisputed), "Liquidation not disputable" ); } function _withdrawable(uint256 liquidationId, address sponsor) internal view { LiquidationData storage liquidation = _getLiquidationData(sponsor, liquidationId); Status state = liquidation.state; // Must be disputed or the liquidation has passed expiry. require( (state > Status.NotDisputed) || ((_getLiquidationExpiry(liquidation) <= getCurrentTime()) && (state == Status.NotDisputed)), "Liquidation not withdrawable" ); } function _transformCollateralRequirement(FixedPoint.Unsigned memory price) internal view returns (FixedPoint.Unsigned memory) { if (!address(financialProductLibrary).isContract()) return collateralRequirement; try financialProductLibrary.transformCollateralRequirement(price, collateralRequirement) returns ( FixedPoint.Unsigned memory transformedCollateralRequirement ) { return transformedCollateralRequirement; } catch { return collateralRequirement; } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../common/FundingRateApplier.sol"; import "../../common/implementation/FixedPoint.sol"; // Implements FundingRateApplier internal methods to enable unit testing. contract FundingRateApplierTest is FundingRateApplier { constructor( bytes32 _fundingRateIdentifier, address _collateralAddress, address _finderAddress, address _configStoreAddress, FixedPoint.Unsigned memory _tokenScaling, address _timerAddress ) public FundingRateApplier( _fundingRateIdentifier, _collateralAddress, _finderAddress, _configStoreAddress, _tokenScaling, _timerAddress ) {} function calculateEffectiveFundingRate( uint256 paymentPeriodSeconds, FixedPoint.Signed memory fundingRatePerSecond, FixedPoint.Unsigned memory currentCumulativeFundingRateMultiplier ) public pure returns (FixedPoint.Unsigned memory) { return _calculateEffectiveFundingRate( paymentPeriodSeconds, fundingRatePerSecond, currentCumulativeFundingRateMultiplier ); } // Required overrides. function _pfc() internal view virtual override returns (FixedPoint.Unsigned memory currentPfc) { return FixedPoint.Unsigned(collateralCurrency.balanceOf(address(this))); } function emergencyShutdown() external override {} function remargin() external override {} function _getTokenAddress() internal view override returns (address) { return address(collateralCurrency); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../../common/implementation/FixedPoint.sol"; import "../../common/interfaces/ExpandedIERC20.sol"; import "./VotingToken.sol"; /** * @title Migration contract for VotingTokens. * @dev Handles migrating token holders from one token to the next. */ contract TokenMigrator { using FixedPoint for FixedPoint.Unsigned; /**************************************** * INTERNAL VARIABLES AND STORAGE * ****************************************/ VotingToken public oldToken; ExpandedIERC20 public newToken; uint256 public snapshotId; FixedPoint.Unsigned public rate; mapping(address => bool) public hasMigrated; /** * @notice Construct the TokenMigrator contract. * @dev This function triggers the snapshot upon which all migrations will be based. * @param _rate the number of old tokens it takes to generate one new token. * @param _oldToken address of the token being migrated from. * @param _newToken address of the token being migrated to. */ constructor( FixedPoint.Unsigned memory _rate, address _oldToken, address _newToken ) public { // Prevents division by 0 in migrateTokens(). // Also it doesn’t make sense to have “0 old tokens equate to 1 new token”. require(_rate.isGreaterThan(0), "Rate can't be 0"); rate = _rate; newToken = ExpandedIERC20(_newToken); oldToken = VotingToken(_oldToken); snapshotId = oldToken.snapshot(); } /** * @notice Migrates the tokenHolder's old tokens to new tokens. * @dev This function can only be called once per `tokenHolder`. Anyone can call this method * on behalf of any other token holder since there is no disadvantage to receiving the tokens earlier. * @param tokenHolder address of the token holder to migrate. */ function migrateTokens(address tokenHolder) external { require(!hasMigrated[tokenHolder], "Already migrated tokens"); hasMigrated[tokenHolder] = true; FixedPoint.Unsigned memory oldBalance = FixedPoint.Unsigned(oldToken.balanceOfAt(tokenHolder, snapshotId)); if (!oldBalance.isGreaterThan(0)) { return; } FixedPoint.Unsigned memory newBalance = oldBalance.div(rate); require(newToken.mint(tokenHolder, newBalance.rawValue), "Mint failed"); } }
pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "./FinancialProductLibrary.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "../../../common/implementation/Lockable.sol"; /** * @title Structured Note Financial Product Library * @notice Adds custom price transformation logic to modify the behavior of the expiring multi party contract. The * contract holds say 1 WETH in collateral and pays out that 1 WETH if, at expiry, ETHUSD is below a set strike. If * ETHUSD is above that strike, the contract pays out a given dollar amount of ETH. * Example: expiry is DEC 31. Strike is $400. Each token is backed by 1 WETH * If ETHUSD < $400 at expiry, token is redeemed for 1 ETH. * If ETHUSD >= $400 at expiry, token is redeemed for $400 worth of ETH, as determined by the DVM. */ contract StructuredNoteFinancialProductLibrary is FinancialProductLibrary, Ownable, Lockable { mapping(address => FixedPoint.Unsigned) financialProductStrikes; /** * @notice Enables the deployer of the library to set the strike price for an associated financial product. * @param financialProduct address of the financial product. * @param strikePrice the strike price for the structured note to be applied to the financial product. * @dev Note: a) Only the owner (deployer) of this library can set new strike prices b) A strike price cannot be 0. * c) A strike price can only be set once to prevent the deployer from changing the strike after the fact. * d) financialProduct must exposes an expirationTimestamp method. */ function setFinancialProductStrike(address financialProduct, FixedPoint.Unsigned memory strikePrice) public onlyOwner nonReentrant() { require(strikePrice.isGreaterThan(0), "Cant set 0 strike"); require(financialProductStrikes[financialProduct].isEqual(0), "Strike already set"); require(ExpiringContractInterface(financialProduct).expirationTimestamp() != 0, "Invalid EMP contract"); financialProductStrikes[financialProduct] = strikePrice; } /** * @notice Returns the strike price associated with a given financial product address. * @param financialProduct address of the financial product. * @return strikePrice for the associated financial product. */ function getStrikeForFinancialProduct(address financialProduct) public view nonReentrantView() returns (FixedPoint.Unsigned memory) { return financialProductStrikes[financialProduct]; } /** * @notice Returns a transformed price by applying the structured note payout structure. * @param oraclePrice price from the oracle to be transformed. * @param requestTime timestamp the oraclePrice was requested at. * @return transformedPrice the input oracle price with the price transformation logic applied to it. */ function transformPrice(FixedPoint.Unsigned memory oraclePrice, uint256 requestTime) public view override nonReentrantView() returns (FixedPoint.Unsigned memory) { FixedPoint.Unsigned memory strike = financialProductStrikes[msg.sender]; require(strike.isGreaterThan(0), "Caller has no strike"); // If price request is made before expiry, return 1. Thus we can keep the contract 100% collateralized with // each token backed 1:1 by collateral currency. if (requestTime < ExpiringContractInterface(msg.sender).expirationTimestamp()) { return FixedPoint.fromUnscaledUint(1); } if (oraclePrice.isLessThan(strike)) { return FixedPoint.fromUnscaledUint(1); } else { // Token expires to be worth strike $ worth of collateral. // eg if ETHUSD is $500 and strike is $400, token is redeemable for 400/500 = 0.8 WETH. return strike.div(oraclePrice); } } /** * @notice Returns a transformed collateral requirement by applying the structured note payout structure. If the price * of the structured note is greater than the strike then the collateral requirement scales down accordingly. * @param oraclePrice price from the oracle to transform the collateral requirement. * @param collateralRequirement financial products collateral requirement to be scaled according to price and strike. * @return transformedCollateralRequirement the input collateral requirement with the transformation logic applied to it. */ function transformCollateralRequirement( FixedPoint.Unsigned memory oraclePrice, FixedPoint.Unsigned memory collateralRequirement ) public view override nonReentrantView() returns (FixedPoint.Unsigned memory) { FixedPoint.Unsigned memory strike = financialProductStrikes[msg.sender]; require(strike.isGreaterThan(0), "Caller has no strike"); // If the price is less than the strike than the original collateral requirement is used. if (oraclePrice.isLessThan(strike)) { return collateralRequirement; } else { // If the price is more than the strike then the collateral requirement is scaled by the strike. For example // a strike of $400 and a CR of 1.2 would yield: // ETHUSD = $350, payout is 1 WETH. CR is multiplied by 1. resulting CR = 1.2 // ETHUSD = $400, payout is 1 WETH. CR is multiplied by 1. resulting CR = 1.2 // ETHUSD = $425, payout is 0.941 WETH (worth $400). CR is multiplied by 0.941. resulting CR = 1.1292 // ETHUSD = $500, payout is 0.8 WETH (worth $400). CR multiplied by 0.8. resulting CR = 0.96 return collateralRequirement.mul(strike.div(oraclePrice)); } } }
pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "./FinancialProductLibrary.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "../../../common/implementation/Lockable.sol"; /** * @title Pre-Expiration Identifier Transformation Financial Product Library * @notice Adds custom identifier transformation to enable a financial contract to use two different identifiers, depending * on when a price request is made. If the request is made before expiration then a transformation is made to the identifier * & if it is at or after expiration then the original identifier is returned. This library enables self referential * TWAP identifier to be used on synthetics pre-expiration, in conjunction with a separate identifier at expiration. */ contract PreExpirationIdentifierTransformationFinancialProductLibrary is FinancialProductLibrary, Ownable, Lockable { mapping(address => bytes32) financialProductTransformedIdentifiers; /** * @notice Enables the deployer of the library to set the transformed identifier for an associated financial product. * @param financialProduct address of the financial product. * @param transformedIdentifier the identifier for the financial product to be used if the contract is post expiration. * @dev Note: a) Only the owner (deployer) of this library can set identifier transformations b) The identifier can't * be set to blank. c) A transformed price can only be set once to prevent the deployer from changing it after the fact. * d) financialProduct must expose an expirationTimestamp method. */ function setFinancialProductTransformedIdentifier(address financialProduct, bytes32 transformedIdentifier) public onlyOwner nonReentrant() { require(transformedIdentifier != "", "Cant set to empty transformation"); require(financialProductTransformedIdentifiers[financialProduct] == "", "Transformation already set"); require(ExpiringContractInterface(financialProduct).expirationTimestamp() != 0, "Invalid EMP contract"); financialProductTransformedIdentifiers[financialProduct] = transformedIdentifier; } /** * @notice Returns the transformed identifier associated with a given financial product address. * @param financialProduct address of the financial product. * @return transformed identifier for the associated financial product. */ function getTransformedIdentifierForFinancialProduct(address financialProduct) public view nonReentrantView() returns (bytes32) { return financialProductTransformedIdentifiers[financialProduct]; } /** * @notice Returns a transformed price identifier if the contract is pre-expiration and no transformation if post. * @param identifier input price identifier to be transformed. * @param requestTime timestamp the identifier is to be used at. * @return transformedPriceIdentifier the input price identifier with the transformation logic applied to it. */ function transformPriceIdentifier(bytes32 identifier, uint256 requestTime) public view override nonReentrantView() returns (bytes32) { require(financialProductTransformedIdentifiers[msg.sender] != "", "Caller has no transformation"); // If the request time is before contract expiration then return the transformed identifier. Else, return the // original price identifier. if (requestTime < ExpiringContractInterface(msg.sender).expirationTimestamp()) { return financialProductTransformedIdentifiers[msg.sender]; } else { return identifier; } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../implementation/Lockable.sol"; import "./ReentrancyAttack.sol"; // Tests reentrancy guards defined in Lockable.sol. // Extends https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.1/contracts/mocks/ReentrancyMock.sol. contract ReentrancyMock is Lockable { uint256 public counter; constructor() public { counter = 0; } function callback() external nonReentrant { _count(); } function countAndSend(ReentrancyAttack attacker) external nonReentrant { _count(); bytes4 func = bytes4(keccak256("callback()")); attacker.callSender(func); } function countAndCall(ReentrancyAttack attacker) external nonReentrant { _count(); bytes4 func = bytes4(keccak256("getCount()")); attacker.callSender(func); } function countLocalRecursive(uint256 n) public nonReentrant { if (n > 0) { _count(); countLocalRecursive(n - 1); } } function countThisRecursive(uint256 n) public nonReentrant { if (n > 0) { _count(); // solhint-disable-next-line avoid-low-level-calls (bool success, ) = address(this).call(abi.encodeWithSignature("countThisRecursive(uint256)", n - 1)); require(success, "ReentrancyMock: failed call"); } } function countLocalCall() public nonReentrant { getCount(); } function countThisCall() public nonReentrant { // solhint-disable-next-line avoid-low-level-calls (bool success, ) = address(this).call(abi.encodeWithSignature("getCount()")); require(success, "ReentrancyMock: failed call"); } function getCount() public view nonReentrantView returns (uint256) { return counter; } function _count() private { counter += 1; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; // Tests reentrancy guards defined in Lockable.sol. // Copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.1/contracts/mocks/ReentrancyAttack.sol. contract ReentrancyAttack { function callSender(bytes4 data) public { // solhint-disable-next-line avoid-low-level-calls (bool success, ) = msg.sender.call(abi.encodeWithSelector(data)); require(success, "ReentrancyAttack: failed call"); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../../interfaces/VotingInterface.sol"; import "../VoteTiming.sol"; // Wraps the library VoteTiming for testing purposes. contract VoteTimingTest { using VoteTiming for VoteTiming.Data; VoteTiming.Data public voteTiming; constructor(uint256 phaseLength) public { wrapInit(phaseLength); } function wrapComputeCurrentRoundId(uint256 currentTime) external view returns (uint256) { return voteTiming.computeCurrentRoundId(currentTime); } function wrapComputeCurrentPhase(uint256 currentTime) external view returns (VotingAncillaryInterface.Phase) { return voteTiming.computeCurrentPhase(currentTime); } function wrapInit(uint256 phaseLength) public { voteTiming.init(phaseLength); } }
/* MultiRoleTest contract. */ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../implementation/MultiRole.sol"; // The purpose of this contract is to make the MultiRole creation methods externally callable for testing purposes. contract MultiRoleTest is MultiRole { function createSharedRole( uint256 roleId, uint256 managingRoleId, address[] calldata initialMembers ) external { _createSharedRole(roleId, managingRoleId, initialMembers); } function createExclusiveRole( uint256 roleId, uint256 managingRoleId, address initialMember ) external { _createExclusiveRole(roleId, managingRoleId, initialMember); } // solhint-disable-next-line no-empty-blocks function revertIfNotHoldingRole(uint256 roleId) external view onlyRoleHolder(roleId) {} }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../implementation/Testable.sol"; // TestableTest is derived from the abstract contract Testable for testing purposes. contract TestableTest is Testable { // solhint-disable-next-line no-empty-blocks constructor(address _timerAddress) public Testable(_timerAddress) {} function getTestableTimeAndBlockTime() external view returns (uint256 testableTime, uint256 blockTime) { // solhint-disable-next-line not-rely-on-time return (getCurrentTime(), now); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../ResultComputation.sol"; import "../../../common/implementation/FixedPoint.sol"; // Wraps the library ResultComputation for testing purposes. contract ResultComputationTest { using ResultComputation for ResultComputation.Data; ResultComputation.Data public data; function wrapAddVote(int256 votePrice, uint256 numberTokens) external { data.addVote(votePrice, FixedPoint.Unsigned(numberTokens)); } function wrapGetResolvedPrice(uint256 minVoteThreshold) external view returns (bool isResolved, int256 price) { return data.getResolvedPrice(FixedPoint.Unsigned(minVoteThreshold)); } function wrapWasVoteCorrect(bytes32 revealHash) external view returns (bool) { return data.wasVoteCorrect(revealHash); } function wrapGetTotalCorrectlyVotedTokens() external view returns (uint256) { return data.getTotalCorrectlyVotedTokens().rawValue; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "../Voting.sol"; import "../../../common/implementation/FixedPoint.sol"; // Test contract used to access internal variables in the Voting contract. contract VotingTest is Voting { constructor( uint256 _phaseLength, FixedPoint.Unsigned memory _gatPercentage, FixedPoint.Unsigned memory _inflationRate, uint256 _rewardsExpirationTimeout, address _votingToken, address _finder, address _timerAddress ) public Voting( _phaseLength, _gatPercentage, _inflationRate, _rewardsExpirationTimeout, _votingToken, _finder, _timerAddress ) {} function getPendingPriceRequestsArray() external view returns (bytes32[] memory) { return pendingPriceRequests; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../implementation/FixedPoint.sol"; // Wraps the FixedPoint library for testing purposes. contract UnsignedFixedPointTest { using FixedPoint for FixedPoint.Unsigned; using FixedPoint for uint256; using SafeMath for uint256; function wrapFromUnscaledUint(uint256 a) external pure returns (uint256) { return FixedPoint.fromUnscaledUint(a).rawValue; } function wrapIsEqual(uint256 a, uint256 b) external pure returns (bool) { return FixedPoint.Unsigned(a).isEqual(FixedPoint.Unsigned(b)); } function wrapMixedIsEqual(uint256 a, uint256 b) external pure returns (bool) { return FixedPoint.Unsigned(a).isEqual(b); } function wrapIsGreaterThan(uint256 a, uint256 b) external pure returns (bool) { return FixedPoint.Unsigned(a).isGreaterThan(FixedPoint.Unsigned(b)); } function wrapIsGreaterThanOrEqual(uint256 a, uint256 b) external pure returns (bool) { return FixedPoint.Unsigned(a).isGreaterThanOrEqual(FixedPoint.Unsigned(b)); } function wrapMixedIsGreaterThan(uint256 a, uint256 b) external pure returns (bool) { return FixedPoint.Unsigned(a).isGreaterThan(b); } function wrapMixedIsGreaterThanOrEqual(uint256 a, uint256 b) external pure returns (bool) { return FixedPoint.Unsigned(a).isGreaterThanOrEqual(b); } function wrapMixedIsGreaterThanOpposite(uint256 a, uint256 b) external pure returns (bool) { return a.isGreaterThan(FixedPoint.Unsigned(b)); } function wrapMixedIsGreaterThanOrEqualOpposite(uint256 a, uint256 b) external pure returns (bool) { return a.isGreaterThanOrEqual(FixedPoint.Unsigned(b)); } function wrapIsLessThan(uint256 a, uint256 b) external pure returns (bool) { return FixedPoint.Unsigned(a).isLessThan(FixedPoint.Unsigned(b)); } function wrapIsLessThanOrEqual(uint256 a, uint256 b) external pure returns (bool) { return FixedPoint.Unsigned(a).isLessThanOrEqual(FixedPoint.Unsigned(b)); } function wrapMixedIsLessThan(uint256 a, uint256 b) external pure returns (bool) { return FixedPoint.Unsigned(a).isLessThan(b); } function wrapMixedIsLessThanOrEqual(uint256 a, uint256 b) external pure returns (bool) { return FixedPoint.Unsigned(a).isLessThanOrEqual(b); } function wrapMixedIsLessThanOpposite(uint256 a, uint256 b) external pure returns (bool) { return a.isLessThan(FixedPoint.Unsigned(b)); } function wrapMixedIsLessThanOrEqualOpposite(uint256 a, uint256 b) external pure returns (bool) { return a.isLessThanOrEqual(FixedPoint.Unsigned(b)); } function wrapMin(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).min(FixedPoint.Unsigned(b)).rawValue; } function wrapMax(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).max(FixedPoint.Unsigned(b)).rawValue; } function wrapAdd(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).add(FixedPoint.Unsigned(b)).rawValue; } // The first uint256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedAdd(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).add(b).rawValue; } function wrapSub(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).sub(FixedPoint.Unsigned(b)).rawValue; } // The first uint256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedSub(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).sub(b).rawValue; } // The second uint256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedSubOpposite(uint256 a, uint256 b) external pure returns (uint256) { return a.sub(FixedPoint.Unsigned(b)).rawValue; } function wrapMul(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).mul(FixedPoint.Unsigned(b)).rawValue; } function wrapMulCeil(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).mulCeil(FixedPoint.Unsigned(b)).rawValue; } // The first uint256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedMul(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).mul(b).rawValue; } function wrapMixedMulCeil(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).mulCeil(b).rawValue; } function wrapDiv(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).div(FixedPoint.Unsigned(b)).rawValue; } function wrapDivCeil(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).divCeil(FixedPoint.Unsigned(b)).rawValue; } // The first uint256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedDiv(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).div(b).rawValue; } function wrapMixedDivCeil(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).divCeil(b).rawValue; } // The second uint256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedDivOpposite(uint256 a, uint256 b) external pure returns (uint256) { return a.div(FixedPoint.Unsigned(b)).rawValue; } // The first uint256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapPow(uint256 a, uint256 b) external pure returns (uint256) { return FixedPoint.Unsigned(a).pow(b).rawValue; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../implementation/FixedPoint.sol"; // Wraps the FixedPoint library for testing purposes. contract SignedFixedPointTest { using FixedPoint for FixedPoint.Signed; using FixedPoint for int256; using SafeMath for int256; function wrapFromSigned(int256 a) external pure returns (uint256) { return FixedPoint.fromSigned(FixedPoint.Signed(a)).rawValue; } function wrapFromUnsigned(uint256 a) external pure returns (int256) { return FixedPoint.fromUnsigned(FixedPoint.Unsigned(a)).rawValue; } function wrapFromUnscaledInt(int256 a) external pure returns (int256) { return FixedPoint.fromUnscaledInt(a).rawValue; } function wrapIsEqual(int256 a, int256 b) external pure returns (bool) { return FixedPoint.Signed(a).isEqual(FixedPoint.Signed(b)); } function wrapMixedIsEqual(int256 a, int256 b) external pure returns (bool) { return FixedPoint.Signed(a).isEqual(b); } function wrapIsGreaterThan(int256 a, int256 b) external pure returns (bool) { return FixedPoint.Signed(a).isGreaterThan(FixedPoint.Signed(b)); } function wrapIsGreaterThanOrEqual(int256 a, int256 b) external pure returns (bool) { return FixedPoint.Signed(a).isGreaterThanOrEqual(FixedPoint.Signed(b)); } function wrapMixedIsGreaterThan(int256 a, int256 b) external pure returns (bool) { return FixedPoint.Signed(a).isGreaterThan(b); } function wrapMixedIsGreaterThanOrEqual(int256 a, int256 b) external pure returns (bool) { return FixedPoint.Signed(a).isGreaterThanOrEqual(b); } function wrapMixedIsGreaterThanOpposite(int256 a, int256 b) external pure returns (bool) { return a.isGreaterThan(FixedPoint.Signed(b)); } function wrapMixedIsGreaterThanOrEqualOpposite(int256 a, int256 b) external pure returns (bool) { return a.isGreaterThanOrEqual(FixedPoint.Signed(b)); } function wrapIsLessThan(int256 a, int256 b) external pure returns (bool) { return FixedPoint.Signed(a).isLessThan(FixedPoint.Signed(b)); } function wrapIsLessThanOrEqual(int256 a, int256 b) external pure returns (bool) { return FixedPoint.Signed(a).isLessThanOrEqual(FixedPoint.Signed(b)); } function wrapMixedIsLessThan(int256 a, int256 b) external pure returns (bool) { return FixedPoint.Signed(a).isLessThan(b); } function wrapMixedIsLessThanOrEqual(int256 a, int256 b) external pure returns (bool) { return FixedPoint.Signed(a).isLessThanOrEqual(b); } function wrapMixedIsLessThanOpposite(int256 a, int256 b) external pure returns (bool) { return a.isLessThan(FixedPoint.Signed(b)); } function wrapMixedIsLessThanOrEqualOpposite(int256 a, int256 b) external pure returns (bool) { return a.isLessThanOrEqual(FixedPoint.Signed(b)); } function wrapMin(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).min(FixedPoint.Signed(b)).rawValue; } function wrapMax(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).max(FixedPoint.Signed(b)).rawValue; } function wrapAdd(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).add(FixedPoint.Signed(b)).rawValue; } // The first int256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedAdd(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).add(b).rawValue; } function wrapSub(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).sub(FixedPoint.Signed(b)).rawValue; } // The first int256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedSub(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).sub(b).rawValue; } // The second int256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedSubOpposite(int256 a, int256 b) external pure returns (int256) { return a.sub(FixedPoint.Signed(b)).rawValue; } function wrapMul(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).mul(FixedPoint.Signed(b)).rawValue; } function wrapMulAwayFromZero(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).mulAwayFromZero(FixedPoint.Signed(b)).rawValue; } // The first int256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedMul(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).mul(b).rawValue; } function wrapMixedMulAwayFromZero(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).mulAwayFromZero(b).rawValue; } function wrapDiv(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).div(FixedPoint.Signed(b)).rawValue; } function wrapDivAwayFromZero(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).divAwayFromZero(FixedPoint.Signed(b)).rawValue; } // The first int256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedDiv(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).div(b).rawValue; } function wrapMixedDivAwayFromZero(int256 a, int256 b) external pure returns (int256) { return FixedPoint.Signed(a).divAwayFromZero(b).rawValue; } // The second int256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapMixedDivOpposite(int256 a, int256 b) external pure returns (int256) { return a.div(FixedPoint.Signed(b)).rawValue; } // The first int256 is interpreted with a scaling factor and is converted to an `Unsigned` directly. function wrapPow(int256 a, uint256 b) external pure returns (int256) { return FixedPoint.Signed(a).pow(b).rawValue; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "../interfaces/OneSplit.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @title OneSplit Mock that allows manual price injection. */ contract OneSplitMock is OneSplit { address constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; mapping(bytes32 => uint256) prices; receive() external payable {} // Sets price of 1 FROM = <PRICE> TO function setPrice( address from, address to, uint256 price ) external { prices[keccak256(abi.encodePacked(from, to))] = price; } function getExpectedReturn( address fromToken, address destToken, uint256 amount, uint256 parts, uint256 flags // See constants in IOneSplit.sol ) public view override returns (uint256 returnAmount, uint256[] memory distribution) { returnAmount = prices[keccak256(abi.encodePacked(fromToken, destToken))] * amount; return (returnAmount, distribution); } function swap( address fromToken, address destToken, uint256 amount, uint256 minReturn, uint256[] memory distribution, uint256 flags ) public payable override returns (uint256 returnAmount) { uint256 amountReturn = prices[keccak256(abi.encodePacked(fromToken, destToken))] * amount; require(amountReturn >= minReturn, "Min Amount not reached"); if (destToken == ETH_ADDRESS) { msg.sender.transfer(amountReturn); } else { require(IERC20(destToken).transfer(msg.sender, amountReturn), "erc20-send-failed"); } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; abstract contract OneSplit { function getExpectedReturn( address fromToken, address destToken, uint256 amount, uint256 parts, uint256 flags // See constants in IOneSplit.sol ) public view virtual returns (uint256 returnAmount, uint256[] memory distribution); function swap( address fromToken, address destToken, uint256 amount, uint256 minReturn, uint256[] memory distribution, uint256 flags ) public payable virtual returns (uint256 returnAmount); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.6.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @title Implements only the required ERC20 methods. This contract is used * test how contracts handle ERC20 contracts that have not implemented `decimals()` * @dev Mostly copied from Consensys EIP-20 implementation: * https://github.com/ConsenSys/Tokens/blob/fdf687c69d998266a95f15216b1955a4965a0a6d/contracts/eip20/EIP20.sol */ contract BasicERC20 is IERC20 { uint256 private constant MAX_UINT256 = 2**256 - 1; mapping(address => uint256) public balances; mapping(address => mapping(address => uint256)) public allowed; uint256 private _totalSupply; constructor(uint256 _initialAmount) public { balances[msg.sender] = _initialAmount; _totalSupply = _initialAmount; } function totalSupply() public view override returns (uint256) { return _totalSupply; } function transfer(address _to, uint256 _value) public override returns (bool success) { require(balances[msg.sender] >= _value); balances[msg.sender] -= _value; balances[_to] += _value; emit Transfer(msg.sender, _to, _value); return true; } function transferFrom( address _from, address _to, uint256 _value ) public override returns (bool success) { uint256 allowance = allowed[_from][msg.sender]; require(balances[_from] >= _value && allowance >= _value); balances[_to] += _value; balances[_from] -= _value; if (allowance < MAX_UINT256) { allowed[_from][msg.sender] -= _value; } emit Transfer(_from, _to, _value); //solhint-disable-line indent, no-unused-vars return true; } function balanceOf(address _owner) public view override returns (uint256 balance) { return balances[_owner]; } function approve(address _spender, uint256 _value) public override returns (bool success) { allowed[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); //solhint-disable-line indent, no-unused-vars return true; } function allowance(address _owner, address _spender) public view override returns (uint256 remaining) { return allowed[_owner][_spender]; } }
{ "optimizer": { "enabled": true, "runs": 199 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"components":[{"internalType":"uint256","name":"expirationTimestamp","type":"uint256"},{"internalType":"uint256","name":"withdrawalLiveness","type":"uint256"},{"internalType":"address","name":"collateralAddress","type":"address"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"finderAddress","type":"address"},{"internalType":"address","name":"timerAddress","type":"address"},{"internalType":"address","name":"financialProductLibraryAddress","type":"address"},{"internalType":"bytes32","name":"priceFeedIdentifier","type":"bytes32"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"minSponsorTokens","type":"tuple"},{"internalType":"uint256","name":"liquidationLiveness","type":"uint256"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"collateralRequirement","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"disputeBondPercentage","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"sponsorDisputeRewardPercentage","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"disputerDisputeRewardPercentage","type":"tuple"}],"internalType":"struct Liquidatable.ConstructorParams","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"ContractExpired","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"},{"indexed":true,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"sponsor","type":"address"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"address","name":"disputer","type":"address"},{"indexed":false,"internalType":"uint256","name":"liquidationId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"disputeSucceeded","type":"bool"}],"name":"DisputeSettled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"originalExpirationTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shutdownTimestamp","type":"uint256"}],"name":"EmergencyShutdown","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"}],"name":"EndedSponsorPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FinalFeesPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":true,"internalType":"uint256","name":"liquidationId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensOutstanding","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockedCollateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidatedCollateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidationTime","type":"uint256"}],"name":"LiquidationCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":true,"internalType":"address","name":"disputer","type":"address"},{"indexed":false,"internalType":"uint256","name":"liquidationId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"disputeBondAmount","type":"uint256"}],"name":"LiquidationDisputed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"paidToLiquidator","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paidToDisputer","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paidToSponsor","type":"uint256"},{"indexed":true,"internalType":"enum Liquidatable.Status","name":"liquidationStatus","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"settlementPrice","type":"uint256"}],"name":"LiquidationWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"}],"name":"NewSponsor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"},{"indexed":true,"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"PositionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"},{"indexed":true,"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"regularFee","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"lateFee","type":"uint256"}],"name":"RegularFeesPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"},{"indexed":true,"internalType":"uint256","name":"numTokensRepaid","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"newTokenCount","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldSponsor","type":"address"}],"name":"RequestTransferPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldSponsor","type":"address"}],"name":"RequestTransferPositionCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldSponsor","type":"address"},{"indexed":true,"internalType":"address","name":"newSponsor","type":"address"}],"name":"RequestTransferPositionExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"},{"indexed":true,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"RequestWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"},{"indexed":true,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"RequestWithdrawalCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"},{"indexed":true,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"RequestWithdrawalExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"uint256","name":"collateralReturned","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokensBurned","type":"uint256"}],"name":"SettleExpiredPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sponsor","type":"address"},{"indexed":true,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"Withdrawal","type":"event"},{"inputs":[{"internalType":"address","name":"_collateralAddress","type":"address"}],"name":"_getSyntheticDecimals","outputs":[{"internalType":"uint8","name":"decimals","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelTransferPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateralCurrency","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralRequirement","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractState","outputs":[{"internalType":"enum PricelessPositionManager.ContractState","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"collateralAmount","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"numTokens","type":"tuple"}],"name":"create","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sponsor","type":"address"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"minCollateralPerToken","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"maxCollateralPerToken","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"maxTokensToLiquidate","type":"tuple"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"createLiquidation","outputs":[{"internalType":"uint256","name":"liquidationId","type":"uint256"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"tokensLiquidated","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"finalFeeBond","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cumulativeFeeMultiplier","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"collateralAmount","type":"tuple"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sponsor","type":"address"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"collateralAmount","type":"tuple"}],"name":"depositTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidationId","type":"uint256"},{"internalType":"address","name":"sponsor","type":"address"}],"name":"dispute","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"totalPaid","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disputeBondPercentage","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"disputerDisputeRewardPercentage","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyShutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"expirationTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"expire","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"expiryPrice","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"financialProductLibrary","outputs":[{"internalType":"contract FinancialProductLibrary","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finder","outputs":[{"internalType":"contract FinderInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sponsor","type":"address"}],"name":"getCollateral","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sponsor","type":"address"}],"name":"getLiquidations","outputs":[{"components":[{"internalType":"address","name":"sponsor","type":"address"},{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"enum Liquidatable.Status","name":"state","type":"uint8"},{"internalType":"uint256","name":"liquidationTime","type":"uint256"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"tokensOutstanding","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"lockedCollateral","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"liquidatedCollateral","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"rawUnitCollateral","type":"tuple"},{"internalType":"address","name":"disputer","type":"address"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"settlementPrice","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"finalFee","type":"tuple"}],"internalType":"struct Liquidatable.LiquidationData[]","name":"liquidationData","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"time","type":"uint256"}],"name":"getOutstandingRegularFees","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"regularFee","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"latePenalty","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"totalPaid","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gulp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidationLiveness","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"liquidations","outputs":[{"internalType":"address","name":"sponsor","type":"address"},{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"enum Liquidatable.Status","name":"state","type":"uint8"},{"internalType":"uint256","name":"liquidationTime","type":"uint256"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"tokensOutstanding","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"lockedCollateral","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"liquidatedCollateral","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"rawUnitCollateral","type":"tuple"},{"internalType":"address","name":"disputer","type":"address"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"settlementPrice","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"finalFee","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minSponsorTokens","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"payRegularFees","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pfc","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"positions","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"tokensOutstanding","type":"tuple"},{"internalType":"uint256","name":"withdrawalRequestPassTimestamp","type":"uint256"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"withdrawalRequestAmount","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"rawCollateral","type":"tuple"},{"internalType":"uint256","name":"transferPositionRequestPassTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceIdentifier","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rawLiquidationCollateral","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rawTotalPositionCollateral","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"numTokens","type":"tuple"}],"name":"redeem","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"amountWithdrawn","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"remargin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"numTokens","type":"tuple"}],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestTransferPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"collateralAmount","type":"tuple"}],"name":"requestWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"time","type":"uint256"}],"name":"setCurrentTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"settleExpired","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"amountWithdrawn","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sponsorDisputeRewardPercentage","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenCurrency","outputs":[{"internalType":"contract ExpandedIERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPositionCollateral","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTokensOutstanding","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newSponsorAddress","type":"address"}],"name":"transferPositionPassedRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"price","type":"tuple"}],"name":"transformCollateralRequirement","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"price","type":"tuple"},{"internalType":"uint256","name":"requestTime","type":"uint256"}],"name":"transformPrice","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestTime","type":"uint256"}],"name":"transformPriceIdentifier","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"collateralAmount","type":"tuple"}],"name":"withdraw","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"amountWithdrawn","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidationId","type":"uint256"},{"internalType":"address","name":"sponsor","type":"address"}],"name":"withdrawLiquidation","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"payToSponsor","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"payToLiquidator","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"payToDisputer","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"paidToSponsor","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"paidToLiquidator","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"paidToDisputer","type":"tuple"}],"internalType":"struct Liquidatable.RewardsData","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawPassedRequest","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"amountWithdrawn","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalLiveness","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103425760003560e01c80635f1af1ca116101b85780639b56d6c911610104578063b9a3c84c116100a2578063d1e92c111161007c578063d1e92c111461065f578063dd0eef3d14610672578063edfa9a9b1461067a578063fcccedc71461068257610342565b8063b9a3c84c1461063c578063bc12163014610644578063bda02e771461065757610342565b80639ff4dea8116100de5780639ff4dea8146105f9578063a1c4d1e714610601578063a765fbea14610621578063b795f0d41461063457610342565b80639b56d6c9146105bc5780639e4efaa0146105cf5780639f43ddd2146105f157610342565b806381a10ae11161017157806392120aec1161014b57806392120aec1461059c5780639375f0e9146105a457806394909e62146105ac57806397523661146105b457610342565b806381a10ae11461057757806385209ee01461057f5780638c382eb21461059457610342565b80635f1af1ca1461052657806362b5f7f5146105395780636ba2f9921461054c5780637048594b1461055f57806379599f96146105675780637e398c221461056f57610342565b806333a46ca21161029257806348e30c3f1161023057806350f498461161020a57806350f49846146104d457806355f57510146104dc5780635617151c146105005780635aa266c91461051357610342565b806348e30c3f146104825780634ead6e511461048a5780634f8c4847146104aa57610342565b806336980f581161026c57806336980f58146104575780633cb6ce831461045f5780633ee7a5ce1461046757806343e4771b1461047a57610342565b806333a46ca2146104275780633403c2fc1461042f578063360598e11461043757610342565b80631c39c38d116102ff57806325ed4dd8116102d957806325ed4dd8146103ed57806329cb924d1461040f5780632d5436cf146104175780632e154f2e1461041f57610342565b80631c39c38d146103ca57806322611280146103d257806322f8e566146103da57610342565b8063081b314e146103475780630c9229ca146103655780630de15fd91461036d5780630ff49b901461038257806318928a0c146103a2578063197f7848146103b7575b600080fd5b61034f61068a565b60405161035c9190614cca565b60405180910390f35b61034f610690565b610375610696565b60405161035c9190614a6c565b610395610390366004614963565b6106a5565b60405161035c919061523f565b6103b56103b036600461484e565b6106c8565b005b6103956103c53660046148e6565b610781565b6103756107a2565b6103b56107b1565b6103b56103e836600461497f565b610824565b6104006103fb3660046147ee565b61089e565b60405161035c939291906152a3565b61034f610dfd565b61034f610ea0565b61034f610ea6565b610395610eac565b6103b5610fe6565b61044a610445366004614997565b611097565b60405161035c91906151f5565b6103b5611515565b6103956115a7565b6103956104753660046148e6565b611737565b6103956117f1565b61034f611828565b61049d6104983660046147b6565b61182e565b60405161035c91906152eb565b6104bd6104b8366004614883565b6118ac565b60405161035c9b9a99989796959493929190614ab3565b61034f611970565b6104ef6104ea3660046147b6565b611976565b60405161035c95949392919061527f565b6103b561050e3660046147b6565b6119c3565b6103b56105213660046148e6565b611b71565b6103956105343660046148e6565b611cf0565b61034f61054736600461497f565b611f31565b6103b561055a36600461491c565b611f44565b6103756121b1565b6103b56121c0565b61034f612235565b61039561223b565b610587612253565b60405161035c9190614d3c565b61034f61225c565b61034f612262565b610375612268565b6103b5612277565b61034f612297565b6103956105ca3660046147b6565b61229d565b6105e26105dd36600461497f565b6122e6565b60405161035c93929190615249565b61034f61245d565b61034f612463565b61061461060f3660046147b6565b612469565b60405161035c9190614bbb565b61039561062f366004614997565b6125c7565b6103b561274e565b6103756127c0565b6103b56106523660046148e6565b6127cf565b6103b56128a4565b6103b561066d3660046148e6565b6128bc565b61034f6128c9565b61034f6128cf565b6103956128d5565b60155481565b60075481565b6001546001600160a01b031681565b6106ad6146ef565b6106b5612d61565b6106bf8383612d8a565b90505b92915050565b6106d0612e3d565b816106da81612e65565b6106e26115a7565b506106eb612d61565b6106f3612e8f565b6106fe826000612ca8565b61070757600080fd5b600061071284612e9e565b905061071e8184612ec6565b5082516040516001600160a01b038616907fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c90600090a38251600154610773916001600160a01b039091169033903090612ee7565b5061077c612f45565b505050565b6107896146ef565b610791612d61565b61079a82612f5a565b90505b919050565b6000546001600160a01b031681565b6107b9612d61565b6107c1612e8f565b60006107cc33612e9e565b90508060010154600014156107e057600080fd5b600281015460405133907f74d8a3658feb89d1a5c335229bbbfc3bbcfaf492769feb7aa4cd2d92efeaf69190600090a361081981613026565b50610822612f45565b565b6000546001600160a01b031661083957600080fd5b60005460405163117c72b360e11b81526001600160a01b03909116906322f8e56690610869908490600401614cca565b600060405180830381600087803b15801561088357600080fd5b505af1158015610897573d6000803e3d6000fd5b5050505050565b60006108a86146ef565b6108b06146ef565b6108b86115a7565b506108c1612e3d565b6108c9612d61565b6108d1612e8f565b836108da610dfd565b11156109015760405162461bcd60e51b81526004016108f890614dd1565b60405180910390fd5b600061090c89612e9e565b9050610934610920368890038801886148e6565b604080516020810190915283548152613040565b9250610941836000612ca8565b61094a57600080fd5b6109526146ef565b60408051602081019091526003830154815261096d9061305e565b90506109776146ef565b6109816000612c7a565b6040805160208101909152600285015481529091506109a09083613081565b156109c5576040805160208101909152600284015481526109c2908390613089565b90505b6109cd6146ef565b50604080516020810190915283548152610a00826109fa836109f4368f90038f018f6148e6565b906130ac565b906130e9565b610a1c5760405162461bcd60e51b81526004016108f890614fde565b610a3b82610a35838e8036038101906109f491906148e6565b90613081565b610a575760405162461bcd60e51b81526004016108f8906150da565b50610a606130f1565b9350610a6a6146ef565b610a726146ef565b610a7a6146ef565b604080516020810190915286548152610a9490899061318c565b9050610aa085826130ac565b9250610aac84826130ac565b9150610ab66146ef565b604080516020810190915260028801548152610ad290836130ac565b9050610ae08f8a86846131c1565b50610af790506011610af28489612cbf565b6132d8565b506001600160a01b038d1660008181526010602090815260409182902080548351610160810185529485523392850192909252909a509190810160018152602001610b40610dfd565b8152602001898152602001848152602001838152602001610b69610b646001612c7a565b613359565b815260200160006001600160a01b03168152602001610b886000612c7a565b815260209081018990528254600180820185556000948552938290208351600a9092020180546001600160a01b03199081166001600160a01b0393841617825592840151948101805490931694909116939093178082556040830151929392919060ff60a01b1916600160a01b836004811115610c0157fe5b021790555060608201516002820155608082015151600382015560a082015151600482015560c082015151600582015560e08201515160068201556101008201516007820180546001600160a01b0319166001600160a01b039092169190911790556101208201515160088201556101409091015151600990910155610c856146ef565b506040805160208101909152600d548152600186015415801590610cb35750610cac610dfd565b8660010154115b8015610cc45750610cc488826130e9565b15610ce357610cdd600c54610cd7610dfd565b90612d3c565b60018701555b88336001600160a01b03168f6001600160a01b03167f39b4371645b4132767fd76a1aad3108ff95c20d7b687b24d171555f5459a75978b6000015187600001518760000151610d30610dfd565b604051610d4094939291906152d0565b60405180910390a48751600954610d66916001600160a01b039091169033903090612ee7565b6009548851604051630852cd8d60e31b81526001600160a01b03909216916342966c6891610d9691600401614cca565b600060405180830381600087803b158015610db057600080fd5b505af1158015610dc4573d6000803e3d6000fd5b50508851600154610de493506001600160a01b0316915033903090612ee7565b505050505050610df2612f45565b955095509592505050565b600080546001600160a01b031615610e9a5760008054906101000a90046001600160a01b03166001600160a01b03166329cb924d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610e5b57600080fd5b505afa158015610e6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9391906148ce565b9050610e9d565b50425b90565b60125481565b60145481565b610eb46146ef565b610ebc612e3d565b610ec46115a7565b50610ecd612d61565b610ed5612e8f565b6000610ee033612e9e565b90508060010154600014158015610f025750610efa610dfd565b816001015411155b610f0b57600080fd5b610f136146ef565b5060408051602080820183526002840154825282519081019092526003830154825290610f5b90610f439061305e565b6040805160208101909152600285015481529061337c565b15610f7e57604080516020810190915260038301548152610f7b9061305e565b90505b610f888282613383565b9250610f9382613026565b8251600154610faf916001600160a01b039091169033906133a4565b825160405133907fc86c3298cb79f486674dca87d9247e88b76146160e7d412cc59b26b14c358a6890600090a35050610e9d612f45565b610fee612e3d565b610ff66133c3565b610ffe612d61565b611006612e8f565b61100e6133f3565b6001600160a01b0316336001600160a01b03161461102b57600080fd5b6005805460ff19166001179055600b54611043610dfd565b600b8190556110519061348e565b600b5460405133917fd39eeb7157d9c446579a0893ecf9ecd87d1f466cdb270c6a189cf38ca1e30f4891611086918591614cd3565b60405180910390a250610822612f45565b61109f614702565b82826110ab828261357f565b6110b36115a7565b506110bc612d61565b6110c4612e8f565b60006110d085876135f9565b90506110dc8686613696565b6110e46146ef565b6040805160208101909152600683015481526110ff9061305e565b90506111096146ef565b506040805160208101909152600883015481526111246146ef565b6040805160208101909152600385015481526111469084906109f490856130ac565b90506111506146ef565b60408051602081019091526004860154815261116c90856130ac565b90506111766146ef565b6040805160208101909152601654815261119090846130ac565b905061119a6146ef565b604080516020810190915260155481526111b490856130ac565b90506111be6146ef565b604080516020810190915260145481526111d99085906130ac565b90506111e36146ef565b604080516020810190915260098a015481526111ff90896130ac565b9050611209614702565b600360018b0154600160a01b900460ff16600481111561122557fe5b14156113235761123f826112398786612cbf565b90612cbf565b60408201526112586112518789613089565b8590612cbf565b815261126e856112688987613089565b90613089565b60208201819052611281906011906137f0565b60808201528051611294906011906137f0565b606082015260408101516112aa906011906137f0565b60a0820181905260078b015490516001546112d3926001600160a01b03918216929116906133a4565b6001808b015460808301515191546112fb926001600160a01b039182169291909116906133a4565b895460608201515160015461131e926001600160a01b03918216929116906133a4565b6113f6565b600460018b0154600160a01b900460ff16600481111561133f57fe5b141561138f57611353826112398886612cbf565b60208201819052611366906011906137f0565b608082018190526001808c01549151905461131e926001600160a01b03918216929116906133a4565b6001808b0154600160a01b900460ff1660048111156113aa57fe5b14156113f6576113ba8683612cbf565b602082018190526113cd906011906137f0565b608082018190526001808c0154915190546113f6926001600160a01b03918216929116906133a4565b60018a0154600160a01b900460ff16600481111561141057fe5b60808201515160a0830151516060840151518b5160405133947fb479588a37dc7f6bac1c91587fcfc539cac4949cf26bb536ad9c8d061f00f50d9461145894919390926152d0565b60405180910390a3601060008f6001600160a01b03166001600160a01b031681526020019081526020016000208f8154811061149057fe5b600091825260208220600a9091020180546001600160a01b031990811682556001820180546001600160a81b03191690556002820183905560038201839055600482018390556005820183905560068201839055600782018054909116905560088101829055600901559b5050505050505050505061150d612f45565b505092915050565b61151d612e3d565b611525612d61565b61152d612e8f565b600061153833612e9e565b9050806004015460001461154b57600080fd5b600061155b600c54610cd7610dfd565b9050600b54811061156b57600080fd5b6004820181905560405133907fbf457c80c8bf299d5c48272c4c1168bf87b33d83b13f0ab9aac332ce1161ed1e90600090a25050610822612f45565b6115af6146ef565b6115b7612d61565b6115bf612e8f565b60006115c9610dfd565b90506115d36146ef565b6115db61386d565b90506115e56146ef565b6115ed6146ef565b6115f56146ef565b6115fe856122e6565b60038890559194509250905061161581600061389a565b1561162657945061172f9350505050565b815183516040517f19b92e73d08d517d71ec46136266e4f5d526a8cd4f8501d73713cebfe4f335ef90600090a361165d81856138b1565b611668836000612ca8565b156116fc5760006116776138f5565b8451600154919250611694916001600160a01b031690839061392e565b60015460405163432ce91960e11b81526001600160a01b0383811692638659d232926116c892909116908890600401614b7e565b600060405180830381600087803b1580156116e257600080fd5b505af11580156116f6573d6000803e3d6000fd5b50505050505b611707826000612ca8565b15611728578151600154611728916001600160a01b039091169033906133a4565b9450505050505b610e9d612f45565b61173f6146ef565b611747612e3d565b3361175181612e65565b6117596115a7565b50611762612d61565b61176a612e8f565b611775836000612ca8565b61177e57600080fd5b600061178933612e9e565b905061179581856139d3565b80516040519194509033907f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b6590600090a382516001546117e2916001600160a01b039091169033906133a4565b506117eb612f45565b50919050565b6117f96146ef565b611801612d61565b604080516020810190915260085481526118239061181e9061305e565b613a19565b905090565b60135481565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561186957600080fd5b505afa925050508015611899575060408051601f3d908101601f19168201909252611896918101906149c6565b60015b6118a55750601261079d565b905061079d565b601060205281600052604060002081815481106118c557fe5b6000918252602091829020600a919091020180546001820154600283015460408051808701825260038601548152815180880183526004870154815282518089018452600588015481528351808a0185526006890154815260078901548551808c01875260088b0154815286519b8c019096526009909901548a526001600160a01b039788169b508787169a50600160a01b90960460ff16989497929691959094909391909216918b565b60115481565b600660209081526000918252604091829020825180830184528154815260018201548451808501865260028401548152855194850190955260038301548452600490920154909391929085565b6119cb612e3d565b336119d581612e65565b6119dd612d61565b6119e5612e8f565b611a2d6119f26000612c7a565b6001600160a01b03841660009081526006602090815260409182902082519182019092526003909101548152611a279061305e565b90613a96565b611a3657600080fd5b6000611a4133612e9e565b90508060040154600014158015611a635750611a5b610dfd565b816004015411155b611a6c57600080fd5b600060048083018281556001600160a01b038616808452600660205260408085208654815560018088015481830155600280890154818401556003808a01548185015595549287019290925533808852838820888155918201889055918101879055938401869055929093018490559151919290917ff1a2dcf23621f1a96185c79d39a5776b5ba3dadbea70c5aa86d84c17c7e9418e9190a36040516001600160a01b038416907ff60993fa76f94c9e0a803526ee6e1314814ed4d2b0d223febf1436b36897fb3790600090a260405133907fcad20625296d189a6fc6e5b39d0d544e5bd99dbda0c8f2f0ecffef3e0fbcc28290600090a250611b6d612f45565b5050565b611b79612e3d565b33611b8381612e65565b611b8b6115a7565b50611b94612d61565b611b9c612e8f565b6000611ba733612e9e565b604080516020810190915281548152909150611bc4908490613081565b611bcd57600080fd5b611bd56146ef565b604080516020810190915282548152611bee9085613089565b6040805160208101909152600d548152909150611c0c9082906130e9565b611c1557600080fd5b8051825560408051602081019091526007548152611c339085613089565b516007558051845160405133907f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d090600090a48351600954611c84916001600160a01b039091169033903090612ee7565b6009548451604051630852cd8d60e31b81526001600160a01b03909216916342966c6891611cb491600401614cca565b600060405180830381600087803b158015611cce57600080fd5b505af1158015611ce2573d6000803e3d6000fd5b505050505050611b6d612f45565b611cf86146ef565b33611d0281612e65565b611d0a6115a7565b50611d13612d61565b611d1b612e8f565b6000611d2633612e9e565b604080516020810190915281548152909150611d4390859061337c565b15611d4d57600080fd5b611d556146ef565b604080516020810190915282548152611d6f90869061318c565b9050611d796146ef565b604080516020810190915260038401548152611d9f90611d989061305e565b83906130ac565b604080516020810190915284548152909150611dbb9087613a96565b15611dd057611dc933613a9d565b9450611e5b565b611dda8382613383565b9450611de46146ef565b604080516020810190915284548152611dfd9088613089565b6040805160208101909152600d548152909150611e1b9082906130e9565b611e375760405162461bcd60e51b81526004016108f890614e9c565b8051845560408051602081019091526007548152611e559088613089565b51600755505b8551855160405133907fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a92990600090a48451600154611ea6916001600160a01b039091169033906133a4565b8551600954611ec4916001600160a01b039091169033903090612ee7565b6009548651604051630852cd8d60e31b81526001600160a01b03909216916342966c6891611ef491600401614cca565b600060405180830381600087803b158015611f0e57600080fd5b505af1158015611f22573d6000803e3d6000fd5b505050505050506117eb612f45565b6000611f3b612d61565b61079a82613baf565b611f4c612e3d565b611f546115a7565b50611f5d612d61565b611f65612e8f565b33600090815260066020908152604091829020825191820190925260038201548152611fb890611f9a9085906112399061305e565b604080516020810190915283548152611fb39085612cbf565b613c5c565b80611fc85750611fc88383613c5c565b611fe45760405162461bcd60e51b81526004016108f89061503b565b6001810154156120065760405162461bcd60e51b81526004016108f890614fb2565b60408051602081019091528154815261202090600061389a565b15612088576040805160208101909152600d5481526120409083906130e9565b61205c5760405162461bcd60e51b81526004016108f890614e9c565b60405133907ff60993fa76f94c9e0a803526ee6e1314814ed4d2b0d223febf1436b36897fb3790600090a25b6120928184612ec6565b506040805160208101909152815481526120ac9083612cbf565b518155604080516020810190915260075481526120c99083612cbf565b516007558151835160405133907f4b82aa16e071a61de1a6b9aeec9edab0356331f8122c78683b469ac8e685dabc90600090a4825160015461211a916001600160a01b039091169033903090612ee7565b60095482516040516340c10f1960e01b81526001600160a01b03909216916340c10f199161214d91339190600401614a80565b602060405180830381600087803b15801561216757600080fd5b505af115801561217b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061219f91906148ae565b6121a857600080fd5b50611b6d612f45565b6009546001600160a01b031681565b6121c8613cc2565b6121d06133c3565b6121d86115a7565b506121e1612d61565b6121e9612e8f565b6005805460ff19166001179055600b546122029061348e565b60405133907f18600820405d6cf356e3556301762ca32395e72d8c81494fa344835c9da3633d90600090a2610822612f45565b60165481565b6122436146ef565b61224b612d61565b61182361386d565b60055460ff1681565b60085481565b600d5481565b600f546001600160a01b031681565b61227f612d61565b612287612e8f565b61228f613ceb565b610822612f45565b600a5481565b6122a56146ef565b6122ad612d61565b6001600160a01b0382166000908152600660209081526040918290208251918201909252600390910154815261079a9061181e9061305e565b6122ee6146ef565b6122f66146ef565b6122fe6146ef565b60006123086138f5565b90506123126146ef565b61231a61386d565b905061232781600061389a565b80612333575085600354145b1561233f575050612456565b6003546040516374201feb60e01b81526001600160a01b038416916374201feb9161237191908a9086906004016152b9565b604080518083038186803b15801561238857600080fd5b505afa15801561239c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123c09190614938565b90955093506123cf8585612cbf565b92506123dc83600061389a565b156123e8575050612456565b6123f2838261337c565b15612453576123ff6146ef565b6124098483613089565b90506124136146ef565b61241d8683613040565b90506124298682613089565b95506124358282613089565b915061244b6124448884613040565b8890613089565b965082945050505b50505b9193909250565b600b5481565b600c5481565b6060612473612d61565b6001600160a01b038216600090815260106020908152604080832080548251818502810185019093528083529193909284015b828210156125bc5760008481526020908190206040805161016081018252600a860290920180546001600160a01b039081168452600182015490811694840194909452919290830190600160a01b900460ff16600481111561250457fe5b600481111561250f57fe5b8152600282015460208083019190915260408051808301825260038501548152818401528051808301825260048501548152606084015280518083018252600585015481526080840152805180830182526006850154815260a084015260078401546001600160a01b031660c0840152805180830182526008850154815260e0840152805180830190915260099093015483526101009091019190915290825260019290920191016124a6565b505050509050919050565b6125cf6146ef565b82826125db8282613db5565b6125e36115a7565b506125ec612d61565b6125f4612e8f565b600061260085876135f9565b905061260a6146ef565b604080516020810190915260068301548152612652906126299061305e565b604080516020808201835260145482528251908101909252600486015482526109f491906130ac565b905061265f6011826132d8565b5060018201805460ff60a01b1916600160a11b1790556007820180546001600160a01b03191633179055600282015461269790613e15565b6001820154815160405133926001600160a01b0390811692908a16917fcaca181ccad7979cf36ed4fc921e496001ab5264608f0fac7007ae1b43d36102916126e1918d9190614cd3565b60405180910390a4604080516020810190915260098301548152612706908290612cbf565b604080516020810190915260098401548152909550612726903390613e8d565b8051600154612744916001600160a01b039091169033903090612ee7565b505061150d612f45565b612756612e3d565b61275e612d61565b612766612e8f565b600061277133612e9e565b905080600401546000141561278557600080fd5b60405133907f2e5702420c76e041698ad7ba57a9ff5cadccf647ea8d96e6007a40b5b2662f5690600090a26000600490910155610822612f45565b6002546001600160a01b031681565b6127d7612e3d565b336127e181612e65565b6127e9612d61565b6127f1612e8f565b60006127fc33612e9e565b9050612809836000612ca8565b801561283657506040805160208101909152600382015481526128369061282f9061305e565b8490613081565b61283f57600080fd5b600061284f600c54610cd7610dfd565b9050600b54811061285f57600080fd5b6001820181905583516002830181905560405133907fd33b726e11d2c5d38e6702b16613df0160a07f7ba5185455ee3c45d0494fab1190600090a35050611b6d612f45565b6128ac612e3d565b6128b4612d61565b61228f612e8f565b6128c633826106c8565b50565b60045481565b600e5481565b6128dd6146ef565b6128e5613cc2565b6128ed6115a7565b506128f6612d61565b6128fe612e8f565b600060055460ff16600281111561291157fe5b141561292f5760405162461bcd60e51b81526004016108f890615192565b600260055460ff16600281111561294257fe5b1461296457612952600b54613fc3565b51600e556005805460ff191660021790555b61296c6146ef565b6040805160208101918290526009546370a0823160e01b9092529081906001600160a01b03166370a082316129a43360248501614a6c565b60206040518083038186803b1580156129bc57600080fd5b505afa1580156129d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129f491906148ce565b90529050612a006146ef565b6040805160208101909152600e548152612a1b9083906130ac565b336000908152600660209081526040808320815192830190915260038101548252929350612a539190612a4d9061305e565b90612ca8565b15612b4a57612a606146ef565b6040805160208082018352600e548252825190810190925283548252612a8691906130ac565b9050612a906146ef565b604080516020810190915260038401548152612aab9061305e565b9050612ab56146ef565b612abf838361412b565b612ad85760405180602001604052806000815250612ae2565b612ae28284613089565b9050612aee8582612cbf565b336000818152600660205260408082208281556001810183905560028101839055600381018390556004018290555192975090917fcad20625296d189a6fc6e5b39d0d544e5bd99dbda0c8f2f0ecffef3e0fbcc2829190a25050505b612b526146ef565b60408051602081019091526008548152612b7590612b6f9061305e565b84613040565b9050612b826008826137f0565b60408051602081019091526007548152909550612b9f9085613089565b516007558351855160405133907f9d349c102bec959fb7f20f9a3621e015819d3ae4ed6e9afd1f56a69d5845600690600090a48451600154612bee916001600160a01b039091169033906133a4565b8351600954612c0c916001600160a01b039091169033903090612ee7565b6009548451604051630852cd8d60e31b81526001600160a01b03909216916342966c6891612c3c91600401614cca565b600060405180830381600087803b158015612c5657600080fd5b505af1158015612c6a573d6000803e3d6000fd5b5050505050505050610e9d612f45565b612c826146ef565b604080516020810190915280612ca084670de0b6b3a7640000612d02565b905292915050565b6000612cb382612c7a565b51835111905092915050565b612cc76146ef565b6040805160208101909152825184518291612ce29190612d3c565b90529392505050565b6000612cf682612c7a565b51835110905092915050565b600082612d11575060006106c2565b82820282848281612d1e57fe5b04146106bf5760405162461bcd60e51b81526004016108f890614f71565b6000828201838110156106bf5760405162461bcd60e51b81526004016108f890614d9a565b600054600160a01b900460ff166108225760405162461bcd60e51b81526004016108f89061515b565b612d926146ef565b600f54612da7906001600160a01b0316614132565b612db25750816106c2565b600f5460405162ff49b960e41b81526001600160a01b0390911690630ff49b9090612de39086908690600401615270565b60206040518083038186803b158015612dfb57600080fd5b505afa925050508015612e2b575060408051601f3d908101601f19168201909252612e2891810190614901565b60015b612e365750816106c2565b90506106c2565b600b54612e48610dfd565b106108225760405162461bcd60e51b81526004016108f89061506c565b612e6e81612e9e565b60010154156128c65760405162461bcd60e51b81526004016108f890614fb2565b6000805460ff60a01b19169055565b600081612eaa8161416b565b50506001600160a01b0316600090815260066020526040902090565b612ece6146ef565b612edb83600301836132d8565b506106bf6008836132d8565b612f3f846323b872dd60e01b858585604051602401612f0893929190614b23565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526141be565b50505050565b6000805460ff60a01b1916600160a01b179055565b612f626146ef565b600f54612f77906001600160a01b0316614132565b612f9157506040805160208101909152601354815261079d565b600f54604051632a31263760e01b81526001600160a01b0390911690632a31263790612fc4908590601390600401615260565b60206040518083038186803b158015612fdc57600080fd5b505afa92505050801561300c575060408051601f3d908101601f1916820190925261300991810190614901565b60015b6118a557506040805160208101909152601354815261079d565b6130306000612c7a565b5160028201556000600190910155565b6130486146ef565b815183511061305757816106bf565b5090919050565b6130666146ef565b6040805160208101909152600454815261079a9083906130ac565b519051111590565b6130916146ef565b6040805160208101909152825184518291612ce291906142a3565b6130b46146ef565b6040805160208101909152825184518291670de0b6b3a7640000916130d891612d02565b816130df57fe5b0490529392505050565b519051101590565b6130f96146ef565b60006131036138f5565b600154604051635b97aadd60e01b81529192506001600160a01b0380841692635b97aadd92613136921690600401614a6c565b60206040518083038186803b15801561314e57600080fd5b505afa158015613162573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131869190614901565b91505090565b6131946146ef565b6040805160208101909152825184518291612ce2916131bb90670de0b6b3a7640000612d02565b906142e5565b60006131cc85612e9e565b6040805160208101909152815481529091506131e9908590613a96565b80156132115750604080516020810190915260038201548152613211908490611a279061305e565b156132265761321f85613a9d565b5050612f3f565b6132308184613383565b506132396146ef565b6040805160208101909152825481526132529086613089565b6040805160208101909152600d5481529091506132709082906130e9565b61328c5760405162461bcd60e51b81526004016108f890614e9c565b805182556040805160208101909152600283015481526132ac9084613089565b516002830155604080516020810190915260075481526132cc9086613089565b51600755505050505050565b6132e06146ef565b6132e86146ef565b6040805160208101909152845481526133009061305e565b905061330a6146ef565b61331384613359565b60408051602081019091528654815290915061332f9082612cbf565b5180865560408051602081019091529081526133509083906112689061305e565b95945050505050565b6133616146ef565b6040805160208101909152600454815261079a90839061318c565b5190511190565b61338b6146ef565b61339883600301836137f0565b506106bf6008836137f0565b61077c8363a9059cbb60e01b8484604051602401612f08929190614a80565b600060055460ff1660028111156133d657fe5b146108225760405162461bcd60e51b81526004016108f890614e34565b6002546040516302abf57960e61b81526000916001600160a01b03169063aafd5e409061343e90762334b730b731b4b0b621b7b73a3930b1ba39a0b236b4b760491b90600401614cca565b60206040518083038186803b15801561345657600080fd5b505afa15801561346a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061182391906147d2565b6000613498614327565b90506134a26146ef565b6134aa6130f1565b80516001549192506134c7916001600160a01b031690849061392e565b816001600160a01b03166311df92f16134df85613baf565b856134e861436b565b60015486516040516001600160e01b031960e088901b16815261351b959493926001600160a01b03169190600401614d00565b602060405180830381600087803b15801561353557600080fd5b505af1158015613549573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061356d91906148ce565b5061077c8161357a61386d565b6138b1565b600061358b82846135f9565b600181810154919250600160a01b90910460ff16908160048111156135ac57fe5b11806135dd57506135bb610dfd565b6135c4836143a0565b111580156135dd575060018160048111156135db57fe5b145b612f3f5760405162461bcd60e51b81526004016108f890614d63565b6001600160a01b03821660009081526010602052604081208054831080156136575750600081848154811061362a57fe5b90600052602060002090600a020160010160149054906101000a900460ff16600481111561365457fe5b14155b6136735760405162461bcd60e51b81526004016108f890614ed3565b80838154811061367f57fe5b90600052602060002090600a020191505092915050565b60006136a282846135f9565b905060026001820154600160a01b900460ff1660048111156136c057fe5b146136cb5750611b6d565b6136d881600201546143bb565b5160088201556136e66146ef565b60408051602080820183526008850154825282519081019092526003840154825261371191906130ac565b905061371b6146ef565b60408051602081019091526008840154815261373a90611d9890612f5a565b60408051602081019091526005850154815290915060009061375c90836130e9565b90508061376a57600461376d565b60035b60018501805460ff60a01b1916600160a01b83600481111561378b57fe5b0217905550600184015460078501546040516001600160a01b03928316928881169233927f6c5582199868fabbe697f9ea10abe481bacf53ac78c02a965b34dff82fd20e3b926137e09216908c908890614b98565b60405180910390a4505050505050565b6137f86146ef565b6138006146ef565b6040805160208101909152845481526138189061305e565b90506138226146ef565b61382b84613359565b6040805160208101909152865481529091506138479082613089565b518086556040805160208101909152908152613350906138669061305e565b8390613089565b6138756146ef565b60408051602081019091526011548152611823906138929061305e565b6112396144d5565b60006138a582612c7a565b51835114905092915050565b6138b96146ef565b6138c383836144f6565b90506138ec6138d6826112686001612c7a565b60408051602081019091526004548152906130ac565b51600455505050565b6002546040516302abf57960e61b81526000916001600160a01b03169063aafd5e409061343e906453746f726560d81b90600401614cca565b60006139b282856001600160a01b031663dd62ed3e30876040518363ffffffff1660e01b8152600401613962929190614a99565b60206040518083038186803b15801561397a57600080fd5b505afa15801561398e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cd791906148ce565b9050612f3f8463095ea7b360e01b8584604051602401612f08929190614a80565b6139db6146ef565b6139e883600301836137f0565b506139f283614580565b613a0e5760405162461bcd60e51b81526004016108f890615015565b6106bf6008836137f0565b613a216146ef565b613a296146ef565b613a346105dd610dfd565b92505050613a4c613a456000612c7a565b8290613a96565b15613a5a578291505061079d565b613a626146ef565b613a74613a6d61386d565b83906144f6565b9050613a8e613a87826112686001612c7a565b85906130ac565b949350505050565b5190511490565b613aa56146ef565b6000613ab083612e9e565b9050613aba6146ef565b60408051602081019091526008548152613ad39061305e565b9050613add6146ef565b506040805160208082018352600385015482528251908101909252600854825290613b089082613089565b5160085560408051602080820183528554825282519081019092526007548252613b329190613089565b516007556001600160a01b038516600081815260066020526040808220828155600181018390556002810183905560038101839055600401829055517fcad20625296d189a6fc6e5b39d0d544e5bd99dbda0c8f2f0ecffef3e0fbcc2829190a260408051602081019091526008548152613350906138669061305e565b600f54600090613bc7906001600160a01b0316614132565b613bd45750600a5461079d565b600f54600a5460405163f19371b760e01b81526001600160a01b039092169163f19371b791613c07918690600401614cd3565b60206040518083038186803b158015613c1f57600080fd5b505afa925050508015613c4f575060408051601f3d908101601f19168201909252613c4c918101906148ce565b60015b6118a55750600a5461079d565b6000613c666146ef565b60408051602081019091526008548152613c9890613c839061305e565b604080516020810190915260075481526145b6565b9050613ca26146ef565b613cac85856145b6565b9050613cb8828261337c565b1595945050505050565b600b54613ccd610dfd565b10156108225760405162461bcd60e51b81526004016108f8906150a3565b613cf36146ef565b613cfb61386d565b9050613d056146ef565b6040805160208101918290526001546370a0823160e01b9092529081906001600160a01b03166370a08231613d3d3060248501614a6c565b60206040518083038186803b158015613d5557600080fd5b505afa158015613d69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d8d91906148ce565b90529050613d9b828261412b565b15611b6d57613dad6138d6828461318c565b516004555050565b6000613dc182846135f9565b9050613dcc816143a0565b613dd4610dfd565b108015613df95750600180820154600160a01b900460ff166004811115613df757fe5b145b61077c5760405162461bcd60e51b81526004016108f890614f3a565b6000613e1f6145e1565b9050806001600160a01b03166368ad8ae3613e3984613baf565b846040518363ffffffff1660e01b8152600401613e57929190614cd3565b600060405180830381600087803b158015613e7157600080fd5b505af1158015613e85573d6000803e3d6000fd5b505050505050565b613e9881600061389a565b15613ea257611b6d565b6001600160a01b0382163014613ed5578051600154613ed0916001600160a01b039091169084903090612ee7565b613f06565b613edd6146ef565b613ee561386d565b9050613ef1818361337c565b613efa57600080fd5b613f0482826138b1565b505b80516040517f4f9bf7e8cd0f2456f9c43d2597bedcf1446c9c64544053f1ece6423ae9a07e5290600090a26000613f3b6138f5565b8251600154919250613f58916001600160a01b031690839061392e565b60015460405163432ce91960e11b81526001600160a01b0383811692638659d23292613f8c92909116908690600401614b7e565b600060405180830381600087803b158015613fa657600080fd5b505af1158015613fba573d6000803e3d6000fd5b50505050505050565b613fcb6146ef565b6000613fd5614327565b9050806001600160a01b031663bc58ccaa30613ff086613baf565b86613ff961436b565b6040518563ffffffff1660e01b81526004016140189493929190614b47565b60206040518083038186803b15801561403057600080fd5b505afa158015614044573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061406891906148ae565b61407157600080fd5b6000816001600160a01b03166353b5923961408b86613baf565b8661409461436b565b6040518463ffffffff1660e01b81526004016140b293929190614ce1565b602060405180830381600087803b1580156140cc57600080fd5b505af11580156140e0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061410491906148ce565b90506000811215614113575060005b613a8e60405180602001604052808381525085612d8a565b5190511090565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590613a8e575050151592915050565b6001600160a01b038116600090815260066020908152604080832081519283019091526003015481526141a29190612a4d9061305e565b6128c65760405162461bcd60e51b81526004016108f890614f03565b6141d0826001600160a01b0316614132565b6141ec5760405162461bcd60e51b81526004016108f8906151be565b60006060836001600160a01b0316836040516142089190614a50565b6000604051808303816000865af19150503d8060008114614245576040519150601f19603f3d011682016040523d82523d6000602084013e61424a565b606091505b50915091508161426c5760405162461bcd60e51b81526004016108f890614dff565b805115612f3f578080602001905181019061428791906148ae565b612f3f5760405162461bcd60e51b81526004016108f890615111565b60006106bf83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061461b565b60006106bf83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614647565b6002546040516302abf57960e61b81526000916001600160a01b03169063aafd5e409061343e906f4f7074696d69737469634f7261636c6560801b90600401614cca565b60095460405160609161438c916001600160a01b0390911690602001614a33565b604051602081830303815290604052905090565b600061079a6012548360020154612d3c90919063ffffffff16565b6143c36146ef565b60006143cd6145e1565b9050806001600160a01b031663a03e881a6143e785613baf565b856040518363ffffffff1660e01b8152600401614405929190614cd3565b60206040518083038186803b15801561441d57600080fd5b505afa158015614431573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061445591906148ae565b6144715760405162461bcd60e51b81526004016108f890614e6b565b6000816001600160a01b031663c9280f0661448b86613baf565b866040518363ffffffff1660e01b81526004016144a9929190614cd3565b60206040518083038186803b1580156144c157600080fd5b505afa1580156140e0573d6000803e3d6000fd5b6144dd6146ef565b604080516020810190915260085481526118239061305e565b6144fe6146ef565b825160009061451590670de0b6b3a7640000612d02565b83519091506000906145289083906142e5565b845190915060009061453b90849061467e565b905080156145675760408051602081019091528061455a846001612d3c565b81525093505050506106c2565b60405180602001604052808381525093505050506106c2565b60408051602081019091526003820154815260009061079a906145a29061305e565b604080516020810190915284548152613c5c565b6145be6146ef565b6145c9826000612ca8565b6145d757612e366000612c7a565b612e36838361318c565b6002546040516302abf57960e61b81526000916001600160a01b03169063aafd5e409061343e90654f7261636c6560d01b90600401614cca565b6000818484111561463f5760405162461bcd60e51b81526004016108f89190614d50565b505050900390565b600081836146685760405162461bcd60e51b81526004016108f89190614d50565b50600083858161467457fe5b0495945050505050565b60006106bf83836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f0000000000000000815250600081836146dc5760405162461bcd60e51b81526004016108f89190614d50565b508284816146e657fe5b06949350505050565b6040518060200160405280600081525090565b6040518060c001604052806147156146ef565b81526020016147226146ef565b815260200161472f6146ef565b815260200161473c6146ef565b81526020016147496146ef565b81526020016147566146ef565b905290565b6000602082840312156117eb578081fd5b60006020828403121561477d578081fd5b61478760206152f9565b9135825250919050565b6000602082840312156147a2578081fd5b6147ac60206152f9565b9151825250919050565b6000602082840312156147c7578081fd5b81356106bf8161534c565b6000602082840312156147e3578081fd5b81516106bf8161534c565b600080600080600060a08688031215614805578081fd5b85356148108161534c565b945061481f876020880161475b565b935061482e876040880161475b565b925061483d876060880161475b565b949793965091946080013592915050565b60008060408385031215614860578182fd5b823561486b8161534c565b915061487a846020850161476c565b90509250929050565b60008060408385031215614895578182fd5b82356148a08161534c565b946020939093013593505050565b6000602082840312156148bf578081fd5b815180151581146106bf578182fd5b6000602082840312156148df578081fd5b5051919050565b6000602082840312156148f7578081fd5b6106bf838361476c565b600060208284031215614912578081fd5b6106bf8383614791565b6000806040838503121561492e578182fd5b61486b848461476c565b6000806040838503121561494a578182fd5b6149548484614791565b915061487a8460208501614791565b60008060408385031215614975578182fd5b6148a0848461476c565b600060208284031215614990578081fd5b5035919050565b600080604083850312156149a9578182fd5b8235915060208301356149bb8161534c565b809150509250929050565b6000602082840312156149d7578081fd5b815160ff811681146106bf578182fd5b6001600160a01b03169052565b60008151808452614a0c816020860160208601615320565b601f01601f19169290920160200192915050565b60058110614a2a57fe5b9052565b519052565b60609190911b6bffffffffffffffffffffffff1916815260140190565b60008251614a62818460208701615320565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b038c811682528b81166020830152610160820190614adb604084018d614a20565b606083019a909a5297516080820152955160a0870152935160c0860152915160e085015290941661010083015292516101208201529151610140909201919091529392505050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600060018060a01b038616825284602083015283604083015260806060830152614b7460808301846149f4565b9695505050505050565b6001600160a01b0392909216825251602082015260400190565b6001600160a01b0393909316835260208301919091521515604082015260600190565b602080825282518282018190526000919060409081850190868401855b82811015614cbd578151614bed8582516149e7565b86810151614bfd888701826149e7565b5085810151614c0e87870182614a20565b5060608181015190860152608080820151614c2b82880182614a2e565b505060a080820151614c3f82880182614a2e565b505060c080820151614c5382880182614a2e565b505060e080820151614c6782880182614a2e565b505061010080820151614c7c828801826149e7565b505061012080820151614c9182880182614a2e565b50506101409081015190614ca786820183614a2e565b5050610160939093019290850190600101614bd8565b5091979650505050505050565b90815260200190565b918252602082015260400190565b60008482528360208301526060604083015261335060608301846149f4565b600086825285602083015260a06040830152614d1f60a08301866149f4565b6001600160a01b0394909416606083015250608001529392505050565b6020810160038310614d4a57fe5b91905290565b6000602082526106bf60208301846149f4565b6020808252601c908201527f4c69717569646174696f6e206e6f7420776974686472617761626c6500000000604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252601490820152734d696e656420616674657220646561646c696e6560601b604082015260600190565b6020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b6020808252601a908201527f436f6e7472616374207374617465206973206e6f74204f50454e000000000000604082015260600190565b602080825260179082015276556e7265736f6c766564206f7261636c6520707269636560481b604082015260600190565b6020808252601e908201527f42656c6f77206d696e696d756d2073706f6e736f7220706f736974696f6e0000604082015260600190565b602080825260169082015275125b9d985b1a59081b1a5c5d5a59185d1a5bdb88125160521b604082015260600190565b6020808252601a908201527f506f736974696f6e20686173206e6f20636f6c6c61746572616c000000000000604082015260600190565b6020808252601a908201527f4c69717569646174696f6e206e6f742064697370757461626c65000000000000604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b60208082526012908201527114195b991a5b99c81dda5d1a191c985dd85b60721b604082015260600190565b6020808252601e908201527f4352206973206d6f7265207468616e206d6178206c69712e2070726963650000604082015260600190565b6020808252600c908201526b21a9103132b637bb9023a1a960a11b604082015260600190565b602080825260179082015276125b9cdd59999a58da595b9d0818dbdb1b185d195c985b604a1b604082015260600190565b60208082526018908201527f4f6e6c792063616c6c61626c65207072652d6578706972790000000000000000604082015260600190565b60208082526019908201527f4f6e6c792063616c6c61626c6520706f73742d65787069727900000000000000604082015260600190565b6020808252601e908201527f4352206973206c657373207468616e206d696e206c69712e2070726963650000604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6020808252601290820152712ab732bc3834b932b2103837b9b4ba34b7b760711b604082015260600190565b6020808252601f908201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604082015260600190565b600060c0820190508251518252602083015151602083015260408301515160408301526060830151516060830152608083015151608083015260a08301515160a083015292915050565b9051815260200190565b925183529051602083015251604082015260600190565b9151825254602082015260400190565b91518252602082015260400190565b94518552602085019390935290516040840152516060830152608082015260a00190565b9283529051602083015251604082015260600190565b928352602083019190915251604082015260600190565b93845260208401929092526040830152606082015260800190565b60ff91909116815260200190565b60405181810167ffffffffffffffff8111828210171561531857600080fd5b604052919050565b60005b8381101561533b578181015183820152602001615323565b83811115612f3f5750506000910152565b6001600160a01b03811681146128c657600080fdfea2646970667358221220d730fd0020b72c4d1d90b7f9a77b9ed5a64fb3680c693525ebaaccb811e6741b64736f6c634300060c0033
Deployed Bytecode Sourcemap
219:518:44:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6365:57:47;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;2986:49:48;;;:::i;1221:32:36:-;;;:::i;:::-;;;;;;;:::i;35122:242:48:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;13013:709::-;;;;;;:::i;:::-;;:::i;:::-;;26947:240:47;;;;;;:::i;:::-;;:::i;397:27:19:-;;;:::i;19418:444:48:-;;;:::i;1112:115:19:-;;;;;;:::i;:::-;;:::i;10943:6682:47:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;:::i;1472:256:19:-;;;:::i;5783:34:47:-;;;:::i;6158:48::-;;;:::i;17837:1506:48:-;;;:::i;31941:678::-;;;:::i;20691:5285:47:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;10026:626:48:-;;;:::i;4831:1266:36:-;;;:::i;14871:1225:48:-;;;;;;:::i;:::-;;:::i;34417:223::-;;;:::i;5921:48:47:-;;;:::i;48889:270:48:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;5063:57:47:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;5167:51::-;;;:::i;2754:49:48:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;:::i;11086:981::-;;;;;;:::i;:::-;;:::i;22893:1076::-;;;;;;:::i;:::-;;:::i;24605:1961::-;;;;;;:::i;:::-;;:::i;35809:166::-;;;;;;:::i;:::-;;:::i;20570:1773::-;;;;;;:::i;:::-;;:::i;3356:35::-;;;:::i;30948:352::-;;;:::i;6582:58:47:-;;;:::i;8939:124:36:-;;;:::i;1686:34:48:-;;;:::i;:::-;;;;;;;:::i;3247:53::-;;;:::i;4272:43::-;;;:::i;4616:54::-;;;:::i;9480:64:36:-;;;:::i;3450:30:48:-;;;:::i;33727:299::-;;;;;;:::i;:::-;;:::i;6764:1704:36:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;:::i;3599:34:48:-;;;:::i;4175:33::-;;;:::i;26229:207:47:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;18395:1642::-;;;;;;:::i;:::-;;:::i;12149:392:48:-;;;:::i;1335:29:36:-;;;:::i;16420:921:48:-;;;;;;:::i;:::-;;:::i;33083:96::-;;;:::i;14123:221::-;;;;;;:::i;:::-;;:::i;1999:50:36:-;;;:::i;4367:38:48:-;;;:::i;27269:3345::-;;;:::i;6365:57:47:-;;;;:::o;2986:49:48:-;;;;:::o;1221:32:36:-;;;-1:-1:-1;;;;;1221:32:36;;:::o;35122:242:48:-;35273:26;;:::i;:::-;1625:19:17;:17;:19::i;:::-;35322:35:48::1;35338:5;35345:11;35322:15;:35::i;:::-;35315:42;;1654:1:17;35122:242:48::0;;;;:::o;13013:709::-;6421:20;:18;:20::i;:::-;13165:7:::1;6970:40;7002:7;6970:31;:40::i;:::-;3054:16:36::2;:14;:16::i;:::-;;1339:19:17::3;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;13234:33:48::4;:16:::0;13265:1:::4;13234:30;:33::i;:::-;13226:42;;;::::0;::::4;;13278:33;13314:25;13331:7;13314:16;:25::i;:::-;13278:61;;13435:60;13464:12;13478:16;13435:28;:60::i;:::-;-1:-1:-1::0;13528:25:48;;13511:43:::4;::::0;-1:-1:-1;;;;;13511:43:48;::::4;::::0;::::4;::::0;13528:25:::4;::::0;13511:43:::4;13689:25:::0;;13626:18:::4;::::0;:89:::4;::::0;-1:-1:-1;;;;;13626:18:48;;::::4;::::0;13662:10:::4;::::0;13682:4:::4;::::0;13626:35:::4;:89::i;:::-;1395:1:17;1406:20:::3;:18;:20::i;:::-;6451:1:48::1;13013:709:::0;;:::o;26947:240:47:-;27093:26;;:::i;:::-;1625:19:17;:17;:19::i;:::-;27142:38:47::1;27174:5;27142:31;:38::i;:::-;27135:45;;1654:1:17;26947:240:47::0;;;:::o;397:27:19:-;;;-1:-1:-1;;;;;397:27:19;;:::o;19418:444:48:-;1339:19:17;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;19480:33:48::1;19516:28;19533:10;19516:16;:28::i;:::-;19480:64;;19562:12;:43;;;19609:1;19562:48;;19554:57;;;::::0;::::1;;19665:36;::::0;::::1;:45:::0;19627:84:::1;::::0;19653:10:::1;::::0;19627:84:::1;::::0;19665:45:::1;::::0;19627:84:::1;19818:37;19842:12;19818:23;:37::i;:::-;1395:1:17;1406:20:::0;:18;:20::i;:::-;19418:444:48:o;1112:115:19:-;914:3;890:12;-1:-1:-1;;;;;890:12:19;882:37;;;;;;1186:12:::1;::::0;1180:40:::1;::::0;-1:-1:-1;;;1180:40:19;;-1:-1:-1;;;;;1186:12:19;;::::1;::::0;1180:34:::1;::::0;:40:::1;::::0;1215:4;;1180:40:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;1112:115:::0;:::o;10943:6682:47:-;11319:21;11354:43;;:::i;:::-;11411:39;;:::i;:::-;3054:16:36;:14;:16::i;:::-;;6421:20:48::1;:18;:20::i;:::-;1339:19:17::2;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;11566:8:47::3;11546:16;:14;:16::i;:::-;:28;;11538:61;;;::::0;-1:-1:-1;;;11538:61:47;;::::3;::::0;::::3;;;:::i;:::-;;;;;;;;;11656:40;11699:25;11716:7;11699:16;:25::i;:::-;11656:68:::0;-1:-1:-1;11754:75:47::3;;;::::0;;::::3;::::0;::::3;11769:20:::0;11754:75:::3;:::i;:::-;;::::0;;::::3;::::0;::::3;::::0;;;;;;;:14:::3;:75::i;:::-;11735:94:::0;-1:-1:-1;11847:33:47::3;11735:94:::0;11878:1:::3;11847:30;:33::i;:::-;11839:42;;;::::0;::::3;;12109;;:::i;:::-;12154:60;::::0;;::::3;::::0;::::3;::::0;;;12180:33:::3;::::0;::::3;12154:60:::0;;;::::3;::::0;:25:::3;:60::i;:::-;12109:105;;12224:57;;:::i;:::-;12284:30;12312:1;12284:27;:30::i;:::-;12328:61;::::0;;::::3;::::0;::::3;::::0;;;:43:::3;::::0;::::3;:61:::0;;;12224:90;;-1:-1:-1;12328:78:47::3;::::0;12390:15;12328:61:::3;:78::i;:::-;12324:206;;;12455:64;::::0;;::::3;::::0;::::3;::::0;;;12475:43:::3;::::0;::::3;12455:64:::0;;;::::3;::::0;:15;;:19:::3;:64::i;:::-;12422:97;;12324:206;12611:38;;:::i;:::-;-1:-1:-1::0;12611:78:47::3;::::0;;::::3;::::0;::::3;::::0;;;;;;;12933:91:::3;12993:30:::0;12933:38:::3;12611:78:::0;12933:25:::3;;::::0;;::::3;::::0;::::3;:21:::0;:25:::3;:::i;:::-;::::0;::::3;:38::i;:::-;:59:::0;::::3;:91::i;:::-;12908:180;;;::::0;-1:-1:-1;;;12908:180:47;;::::3;::::0;::::3;;;:::i;:::-;13213:88;13270:30;13213:38;13239:11;13213:21;:25;;;;;;;;;;:::i;:38::-;:56:::0;::::3;:88::i;:::-;13188:177;;;::::0;-1:-1:-1;;;13188:177:47;;::::3;::::0;::::3;;;:::i;:::-;1395:1:17;13454:19:47;:17;:19::i;:::-;13439:34;;13543:43;;:::i;:::-;13596:47;;:::i;:::-;13725:32;;:::i;:::-;13760:59;::::0;;::::3;::::0;::::3;::::0;;;;;;;::::3;::::0;:16;;:20:::3;:59::i;:::-;13725:94:::0;-1:-1:-1;13936:26:47::3;:15:::0;13725:94;13936:19:::3;:26::i;:::-;13917:45:::0;-1:-1:-1;14157:41:47::3;:30:::0;14192:5;14157:34:::3;:41::i;:::-;14134:64;;14368:51;;:::i;:::-;14438:47;::::0;;::::3;::::0;::::3;::::0;;;:43:::3;::::0;::::3;:47:::0;;;:54:::3;::::0;14486:5;14438:47:::3;:54::i;:::-;14368:124;;14506:93;14529:7;14538:16;14556;14574:24;14506:22;:93::i;:::-;-1:-1:-1::0;14679:76:47::3;::::0;-1:-1:-1;14694:24:47::3;14720:34;:16:::0;14741:12;14720:20:::3;:34::i;:::-;14679:14;:76::i;:::-;-1:-1:-1::0;;;;;;15062:21:47;::::3;;::::0;;;:12:::3;:21;::::0;;;;;;;;:28;;15140:599;;::::3;::::0;::::3;::::0;;;;;15220:10:::3;15140:599:::0;;::::3;::::0;;;;-1:-1:-1;15140:599:47;;;;;;;15062:28;-1:-1:-1;15062:21:47;15140:599;;;15308:16:::3;:14;:16::i;:::-;15140:599;;;;15361:16;15140:599;;;;15413:16;15140:599;;;;15469:20;15140:599;;;;15526:55;15550:30;15578:1;15550:27;:30::i;:::-;15526:23;:55::i;:::-;15140:599:::0;;15617:1:::3;15140:599;::::0;::::3;::::0;;;;;;;;15654:30:::3;::::0;:27:::3;:30::i;:::-;15140:599:::0;;::::3;::::0;;::::3;::::0;;;15100:649;;::::3;::::0;;::::3;::::0;;-1:-1:-1;15100:649:47;;;;;;;;;::::3;::::0;;::::3;;::::0;;-1:-1:-1;;;;;;15100:649:47;;::::3;-1:-1:-1::0;;;;;15100:649:47;;::::3;;::::0;;;;::::3;::::0;;;::::3;::::0;;;;::::3;::::0;;;::::3;::::0;;;::::3;::::0;;;::::3;::::0;::::3;::::0;;;;;;-1:-1:-1;;;;15100:649:47::3;-1:-1:-1::0;;;15100:649:47;::::3;::::0;::::3;;;;;;;;::::0;;-1:-1:-1;15100:649:47::3;::::0;::::3;::::0;::::3;::::0;::::3;::::0;::::3;::::0;::::3;::::0;;::::3;::::0;::::3;::::0;::::3;::::0;::::3;::::0;;::::3;::::0;::::3;::::0;::::3;::::0;::::3;::::0;;::::3;::::0;::::3;::::0;::::3;::::0;::::3;::::0;;::::3;::::0;::::3;::::0;::::3;::::0;::::3;::::0;::::3;::::0;::::3;::::0;;-1:-1:-1;;;;;;15100:649:47::3;-1:-1:-1::0;;;;;15100:649:47;;::::3;::::0;;;::::3;::::0;;::::3;::::0;::::3;::::0;;::::3;::::0;::::3;::::0;::::3;::::0;;::::3;::::0;;::::3;::::0;;::::3;::::0;16451:44:::3;;:::i;:::-;-1:-1:-1::0;16451:63:47::3;::::0;;::::3;::::0;::::3;::::0;;;16498:16:::3;16451:63:::0;;;16541:50:::3;::::0;::::3;::::0;:54;;;;:188:::3;;;16713:16;:14;:16::i;:::-;16660:19;:50;;;:69;16541:188;:304;;;;-1:-1:-1::0;16789:56:47::3;:16:::0;16827:17;16789:37:::3;:56::i;:::-;16524:513;;;16986:40;17007:18;;16986:16;:14;:16::i;:::-;:20:::0;::::3;:40::i;:::-;16933:50;::::0;::::3;:93:::0;16524:513:::3;17129:13;17105:10;-1:-1:-1::0;;;;;17052:251:47::3;17084:7;-1:-1:-1::0;;;;;17052:251:47::3;;17156:16;:25;;;17195:16;:25;;;17234:20;:29;;;17277:16;:14;:16::i;:::-;17052:251;;;;;;;;;:::i;:::-;;;;;;;;17398:25:::0;;17340:13:::3;::::0;:84:::3;::::0;-1:-1:-1;;;;;17340:13:47;;::::3;::::0;17371:10:::3;::::0;17391:4:::3;::::0;17340:30:::3;:84::i;:::-;17434:13;::::0;17453:25;;17434:45:::3;::::0;-1:-1:-1;;;17434:45:47;;-1:-1:-1;;;;;17434:13:47;;::::3;::::0;:18:::3;::::0;:45:::3;::::0;::::3;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;-1:-1:-1::0;;17596:21:47;;17533:18:::3;::::0;:85:::3;::::0;-1:-1:-1;;;;;;17533:18:47::3;::::0;-1:-1:-1;17569:10:47::3;::::0;17589:4:::3;::::0;17533:35:::3;:85::i;:::-;1395:1:17;;;;;;1406:20:::2;:18;:20::i;:::-;10943:6682:47::0;;;;;;;;;:::o;1472:256:19:-;1519:7;1542:12;;-1:-1:-1;;;;;1542:12:19;:28;1538:184;;1599:12;;1593:36;;;-1:-1:-1;;;1593:36:19;;;;-1:-1:-1;;;;;1599:12:19;;;;1593:34;;:36;;;;;;;;;;;;;;;1599:12;1593:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1586:43;;;;1538:184;-1:-1:-1;1667:3:19;1538:184;1472:256;:::o;5783:34:47:-;;;;:::o;6158:48::-;;;;:::o;17837:1506:48:-;17970:42;;:::i;:::-;6421:20;:18;:20::i;:::-;3054:16:36::1;:14;:16::i;:::-;;1339:19:17::2;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;18028:33:48::3;18064:28;18081:10;18064:16;:28::i;:::-;18028:64;;18123:12;:43;;;18170:1;18123:48;;:131;;;;;18238:16;:14;:16::i;:::-;18191:12;:43;;;:63;;18123:131;18102:162;;;::::0;::::3;;18488:43;;:::i;:::-;-1:-1:-1::0;18488:82:48::3;::::0;;::::3;::::0;;::::3;::::0;;18534:36:::3;::::0;::::3;18488:82:::0;;;18635:53;;;;::::3;::::0;;;18661:26:::3;::::0;::::3;18635:53:::0;;;18488:82;18584:105:::3;::::0;18635:53:::3;::::0;:25:::3;:53::i;:::-;18584:50;::::0;;::::3;::::0;::::3;::::0;;;:36:::3;::::0;::::3;:50:::0;;;;::::3;:105::i;:::-;18580:208;;;18724:53;::::0;;::::3;::::0;::::3;::::0;;;18750:26:::3;::::0;::::3;18724:53:::0;;;::::3;::::0;:25:::3;:53::i;:::-;18705:72;;18580:208;18893:60;18922:12;18936:16;18893:28;:60::i;:::-;18875:78;;19060:37;19084:12;19060:23;:37::i;:::-;19232:24:::0;;19188:18:::3;::::0;:69:::3;::::0;-1:-1:-1;;;;;19188:18:48;;::::3;::::0;19220:10:::3;::::0;19188:31:::3;:69::i;:::-;19311:24:::0;;19273:63:::3;::::0;19299:10:::3;::::0;19273:63:::3;::::0;19311:24:::3;::::0;19273:63:::3;1395:1:17;;1406:20:::2;:18;:20::i;31941:678:48:-:0;6421:20;:18;:20::i;:::-;6874:16:::1;:14;:16::i;:::-;1339:19:17::2;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;32071:36:48::3;:34;:36::i;:::-;-1:-1:-1::0;;;;;32057:50:48::3;:10;:50;32049:59;;;::::0;::::3;;32119:13;:51:::0;;-1:-1:-1;;32119:51:48::3;32135:35;32119:51;::::0;;32395:19:::3;::::0;32446:16:::3;:14;:16::i;:::-;32424:19;:38:::0;;;32472:50:::3;::::0;:29:::3;:50::i;:::-;32592:19;::::0;32538:74:::3;::::0;32556:10:::3;::::0;32538:74:::3;::::0;::::3;::::0;32568:22;;32538:74:::3;:::i;:::-;;;;;;;;1395:1:17;1406:20:::2;:18;:20::i;20691:5285:47:-:0;20875:18;;:::i;:::-;20796:13;20811:7;8082:37;8096:13;8111:7;8082:13;:37::i;:::-;3054:16:36::1;:14;:16::i;:::-;;1339:19:17::2;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;20909:35:47::3;20947:43;20967:7;20976:13;20947:19;:43::i;:::-;20909:81;;21107:31;21115:13;21130:7;21107;:31::i;:::-;21319:41;;:::i;:::-;21363:56;::::0;;::::3;::::0;::::3;::::0;;;21389:29:::3;::::0;::::3;21363:56:::0;;;::::3;::::0;:25:::3;:56::i;:::-;21319:100;;21429:42;;:::i;:::-;-1:-1:-1::0;21429:72:47::3;::::0;;::::3;::::0;::::3;::::0;;;21474:27:::3;::::0;::::3;21429:72:::0;;;21511:47:::3;;:::i;:::-;21573:33;::::0;;::::3;::::0;::::3;::::0;;;:29:::3;::::0;::::3;:33:::0;;;:70:::3;::::0;21628:14;;21573:50:::3;::::0;21607:15;21573:33:::3;:50::i;:70::-;21511:132;;21653:37;;:::i;:::-;21693:32;::::0;;::::3;::::0;::::3;::::0;;;:28:::3;::::0;::::3;:32:::0;;;:48:::3;::::0;21726:14;21693:32:::3;:48::i;:::-;21653:88;;21751:48;;:::i;:::-;21802:35;::::0;;::::3;::::0;::::3;::::0;;;:31:::3;:35:::0;;;:57:::3;::::0;21838:20;21802:35:::3;:57::i;:::-;21751:108;;21869:47;;:::i;:::-;21919:34;::::0;;::::3;::::0;::::3;::::0;;;:30:::3;:34:::0;;;:56:::3;::::0;21954:20;21919:34:::3;:56::i;:::-;21869:106;;21985:44;;:::i;:::-;22032:37;::::0;;::::3;::::0;::::3;::::0;;;22047:21:::3;22032:37:::0;;;::::3;::::0;:10;;:14:::3;:37::i;:::-;21985:84;;22079:35;;:::i;:::-;22117:24;::::0;;::::3;::::0;::::3;::::0;;;:20:::3;::::0;::::3;:24:::0;;;:40:::3;::::0;22142:14;22117:24:::3;:40::i;:::-;22079:78;;22627:26;;:::i;:::-;22688:23;22667:17;::::0;::::3;::::0;-1:-1:-1;;;22667:17:47;::::3;;;:44;::::0;::::3;;;;;;;22663:2847;;;22922:58;22971:8:::0;22922:44:::3;:21:::0;22948:17;22922:25:::3;:44::i;:::-;:48:::0;::::3;:58::i;:::-;22898:21;::::0;::::3;:82:::0;23103:62:::3;23128:36;:10:::0;23143:20;23128:14:::3;:36::i;:::-;23103:20:::0;;:24:::3;:62::i;:::-;23080:85:::0;;23524:73:::3;23575:21:::0;23524:46:::3;:20:::0;23549;23524:24:::3;:46::i;:::-;:50:::0;::::3;:73::i;:::-;23498:23;::::0;::::3;:99:::0;;;23692:68:::3;::::0;23710:24:::3;::::0;23692:17:::3;:68::i;:::-;23665:24;::::0;::::3;:95:::0;23842:20;;23798:65:::3;::::0;23816:24:::3;::::0;23798:17:::3;:65::i;:::-;23774:21;::::0;::::3;:89:::0;23946:21:::3;::::0;::::3;::::0;23902:66:::3;::::0;23920:24:::3;::::0;23902:17:::3;:66::i;:::-;23877:22;::::0;::::3;:91:::0;;;24015:20:::3;::::0;::::3;::::0;24037:31;;24015:20;23983:18;:86:::3;::::0;-1:-1:-1;;;;;23983:18:47;;::::3;::::0;24015:20;::::3;::::0;23983:31:::3;:86::i;:::-;24115:22;::::0;;::::3;::::0;24139:24:::3;::::0;::::3;::::0;:33;24083:18;;:90:::3;::::0;-1:-1:-1;;;;;24083:18:47;;::::3;::::0;24115:22;;;::::3;::::0;24083:31:::3;:90::i;:::-;24219:19:::0;;24240:21:::3;::::0;::::3;::::0;:30;24219:19;24187:18;:84:::3;::::0;-1:-1:-1;;;;;24187:18:47;;::::3;::::0;24219:19;::::3;::::0;24187:31:::3;:84::i;:::-;22663:2847;;;24395:20;24374:17;::::0;::::3;::::0;-1:-1:-1;;;24374:17:47;::::3;;;:41;::::0;::::3;;;;;;;24370:1140;;;24535:47;24573:8:::0;24535:33:::3;:10:::0;24550:17;24535:14:::3;:33::i;:47::-;24509:23;::::0;::::3;:73:::0;;;24677:68:::3;::::0;24695:24:::3;::::0;24677:17:::3;:68::i;:::-;24650:24;::::0;::::3;:95:::0;;;24792:22:::3;::::0;;::::3;::::0;24816:33;;24760:18;;:90:::3;::::0;-1:-1:-1;;;;;24760:18:47;;::::3;::::0;24792:22;::::3;::::0;24760:31:::3;:90::i;24370:1140::-;25084:18;25063:17:::0;;::::3;::::0;-1:-1:-1;;;25063:17:47;::::3;;;:39;::::0;::::3;;;;;;;25059:451;;;25207:24;:10:::0;25222:8;25207:14:::3;:24::i;:::-;25181:23;::::0;::::3;:50:::0;;;25326:68:::3;::::0;25344:24:::3;::::0;25326:17:::3;:68::i;:::-;25299:24;::::0;::::3;:95:::0;;;25441:22:::3;::::0;;::::3;::::0;25465:33;;25409:18;;:90:::3;::::0;-1:-1:-1;;;;;25409:18:47;;::::3;::::0;25441:22;::::3;::::0;25409:31:::3;:90::i;:::-;25719:17;::::0;::::3;::::0;-1:-1:-1;;;25719:17:47;::::3;;;25525:259;::::0;::::3;;;;;;25583:24;::::0;::::3;::::0;:33;25630:22:::3;::::0;::::3;::::0;:31;25675:21:::3;::::0;::::3;::::0;:30;25750:24;;25525:259:::3;::::0;25559:10:::3;::::0;25525:259:::3;::::0;::::3;::::0;25583:33;;25630:31;;25525:259:::3;:::i;:::-;;;;;;;;25908:12;:21;25921:7;-1:-1:-1::0;;;;;25908:21:47::3;-1:-1:-1::0;;;;;25908:21:47::3;;;;;;;;;;;;25930:13;25908:36;;;;;;;;;::::0;;;::::3;::::0;;::::3;::::0;;;::::3;;25901:43:::0;;-1:-1:-1;;;;;;25901:43:47;;::::3;::::0;;-1:-1:-1;25901:43:47;::::3;::::0;;-1:-1:-1;;;;;;25901:43:47;;;::::3;::::0;::::3;::::0;;;::::3;::::0;::::3;::::0;;;::::3;::::0;::::3;::::0;;;::::3;::::0;::::3;::::0;;;::::3;::::0;::::3;::::0;;;::::3;::::0;::::3;::::0;;;;::::3;::::0;;::::3;::::0;::::3;::::0;;;::::3;;::::0;25962:7;-1:-1:-1;1406:20:17::2;::::0;-1:-1:-1;1406:18:17::2;::::0;-1:-1:-1;;;;;;;;1406:20:17:i:2;:::-;20691:5285:47::0;;;;;;:::o;10026:626:48:-;6421:20;:18;:20::i;:::-;1339:19:17::1;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;10113:33:48::2;10149:28;10166:10;10149:16;:28::i;:::-;10113:64;;10195:12;:49;;;10248:1;10195:54;10187:63;;;::::0;::::2;;10342:23;10368:40;10389:18;;10368:16;:14;:16::i;:40::-;10342:66;;10444:19;;10426:15;:37;10418:46;;;::::0;::::2;;10527:49;::::0;::::2;:67:::0;;;10610:35:::2;::::0;10634:10:::2;::::0;10610:35:::2;::::0;;;::::2;1395:1:17;;1406:20:::1;:18;:20::i;4831:1266:36:-:0;4888:26;;:::i;:::-;1339:19:17;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;4926:12:36::1;4941:16;:14;:16::i;:::-;4926:31;;4967:41;;:::i;:::-;5011:6;:4;:6::i;:::-;4967:50;;5168:37;;:::i;:::-;5219:38;;:::i;:::-;5271:36;;:::i;:::-;5320:31;5346:4;5320:25;:31::i;:::-;5361:15;:22:::0;;;5154:197;;-1:-1:-1;5154:197:36;-1:-1:-1;5154:197:36;-1:-1:-1;5454:20:36::1;5154:197:::0;5472:1:::1;5454:17;:20::i;:::-;5450:67;;;5497:9:::0;-1:-1:-1;5490:16:36::1;::::0;-1:-1:-1;;;;5490:16:36::1;5450:67;5569:20:::0;;5548:19;;5532:58:::1;::::0;::::1;::::0;5569:20:::1;::::0;5532:58:::1;5601:57;5632:9;5643:14;5601:30;:57::i;:::-;5673:27;:10:::0;5698:1:::1;5673:24;:27::i;:::-;5669:262;;;5716:20;5739:11;:9;:11::i;:::-;5821:19:::0;;5764:18:::1;::::0;5716:34;;-1:-1:-1;5764:77:36::1;::::0;-1:-1:-1;;;;;5764:18:36::1;::::0;5716:34;;5764:40:::1;:77::i;:::-;5888:18;::::0;5855:65:::1;::::0;-1:-1:-1;;;5855:65:36;;-1:-1:-1;;;;;5855:24:36;;::::1;::::0;::::1;::::0;:65:::1;::::0;5888:18;;::::1;::::0;5909:10;;5855:65:::1;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;5669:262;;5945:28;:11:::0;5971:1:::1;5945:25;:28::i;:::-;5941:124;;;6033:20:::0;;5989:18:::1;::::0;:65:::1;::::0;-1:-1:-1;;;;;5989:18:36;;::::1;::::0;6021:10:::1;::::0;5989:31:::1;:65::i;:::-;6081:9:::0;-1:-1:-1;;;;;1395:1:17::1;1406:20:::0;:18;:20::i;14871:1225:48:-;15072:42;;:::i;:::-;6421:20;:18;:20::i;:::-;15005:10:::1;6970:40;7002:7;6970:31;:40::i;:::-;3054:16:36::2;:14;:16::i;:::-;;1339:19:17::3;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;15138:33:48::4;:16:::0;15169:1:::4;15138:30;:33::i;:::-;15130:42;;;::::0;::::4;;15182:33;15218:28;15235:10;15218:16;:28::i;:::-;15182:64;;15517:68;15554:12;15568:16;15517:36;:68::i;:::-;15624:24:::0;;15601:48:::4;::::0;15499:86;;-1:-1:-1;15624:24:48;15612:10:::4;::::0;15601:48:::4;::::0;15624:24:::4;::::0;15601:48:::4;16064:24:::0;;16020:18:::4;::::0;:69:::4;::::0;-1:-1:-1;;;;;16020:18:48;;::::4;::::0;16052:10:::4;::::0;16020:31:::4;:69::i;:::-;1395:1:17;1406:20:::3;:18;:20::i;:::-;6451:1:48::1;14871:1225:::0;;;:::o;34417:223::-;34494:26;;:::i;:::-;1625:19:17;:17;:19::i;:::-;34579:53:48::1;::::0;;::::1;::::0;::::1;::::0;;;34605:26:::1;34579:53:::0;;;34539:94:::1;::::0;34579:53:::1;::::0;:25:::1;:53::i;:::-;34539:39;:94::i;:::-;34532:101;;34417:223:::0;:::o;5921:48:47:-;;;;:::o;48889:270:48:-;48969:14;49014:18;-1:-1:-1;;;;;48999:43:48;;:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;48999:45:48;;;;;;;;-1:-1:-1;;48999:45:48;;;;;;;;;;;;:::i;:::-;;;48995:158;;-1:-1:-1;49140:2:48;49133:9;;48995:158;49092:9;-1:-1:-1;49085:16:48;;5063:57:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5063:57:47;;;;-1:-1:-1;5063:57:47;;;;-1:-1:-1;;;;5063:57:47;;;;;;;;;;;;;;;;;;;;;;:::o;5167:51::-;;;;:::o;2754:49:48:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;11086:981::-;6421:20;:18;:20::i;:::-;11223:10:::1;6970:40;7002:7;6970:31;:40::i;:::-;1339:19:17::2;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;11293:139:48::3;11388:30;11416:1;11388:27;:30::i;:::-;-1:-1:-1::0;;;;;11319:28:48;::::3;;::::0;;;:9:::3;:28;::::0;;;;;;;;11293:69;;;;::::3;::::0;;;11319:42:::3;::::0;;::::3;11293:69:::0;;;::::3;::::0;:25:::3;:69::i;:::-;:77:::0;::::3;:139::i;:::-;11272:170;;;::::0;::::3;;11452:33;11488:28;11505:10;11488:16;:28::i;:::-;11452:64;;11547:12;:49;;;11600:1;11547:54;;:143;;;;;11674:16;:14;:16::i;:::-;11621:12;:49;;;:69;;11547:143;11526:174;;;::::0;::::3;;11798:1;11746:49;::::0;;::::3;:53:::0;;;-1:-1:-1;;;;;11810:28:48;::::3;::::0;;;:9:::3;:28;::::0;;;;;:43;;;;-1:-1:-1;11810:43:48;;::::3;::::0;;;::::3;::::0;::::3;::::0;;::::3;::::0;;;::::3;::::0;::::3;::::0;;::::3;::::0;;;::::3;::::0;;;;;::::3;::::0;;;;11880:10:::3;11870:21:::0;;;;;;11863:28;;;;;::::3;::::0;;;;;::::3;::::0;;;;;::::3;::::0;;;;;;::::3;::::0;;;11907:62;;11810:28;;11880:10;;11907:62:::3;::::0;11798:1;11907:62:::3;11984:29;::::0;-1:-1:-1;;;;;11984:29:48;::::3;::::0;::::3;::::0;;;::::3;12028:32;::::0;12049:10:::3;::::0;12028:32:::3;::::0;;;::::3;1395:1:17;1406:20:::2;:18;:20::i;:::-;6451:1:48::1;11086:981:::0;:::o;22893:1076::-;6421:20;:18;:20::i;:::-;23017:10:::1;6970:40;7002:7;6970:31;:40::i;:::-;3054:16:36::2;:14;:16::i;:::-;;1339:19:17::3;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;23081:33:48::4;23117:28;23134:10;23117:16;:28::i;:::-;23163:59;::::0;;::::4;::::0;::::4;::::0;;;;;;;23081:64;;-1:-1:-1;23163:59:48::4;::::0;:9;;:27:::4;:59::i;:::-;23155:68;;;::::0;::::4;;23330:40;;:::i;:::-;23373:34;::::0;;::::4;::::0;::::4;::::0;;;;;;;:45:::4;::::0;23408:9;23373:34:::4;:45::i;:::-;23436:52;::::0;;::::4;::::0;::::4;::::0;;;23471:16:::4;23436:52:::0;;;23330:88;;-1:-1:-1;23436:52:48::4;::::0;23330:88;;23436:34:::4;:52::i;:::-;23428:61;;;::::0;::::4;;23499:46:::0;;;;23644:26:::4;::::0;;::::4;::::0;::::4;::::0;;;:22:::4;:26:::0;;;:37:::4;::::0;23671:9;23644:26:::4;:37::i;:::-;23619:62:::0;:22:::4;:62:::0;23735:22;;23715:18;;23697:61:::4;::::0;23703:10:::4;::::0;23697:61:::4;::::0;23619:62:::4;::::0;23697:61:::4;23895:18:::0;;23837:13:::4;::::0;:77:::4;::::0;-1:-1:-1;;;;;23837:13:48;;::::4;::::0;23868:10:::4;::::0;23888:4:::4;::::0;23837:30:::4;:77::i;:::-;23924:13;::::0;23943:18;;23924:38:::4;::::0;-1:-1:-1;;;23924:38:48;;-1:-1:-1;;;;;23924:13:48;;::::4;::::0;:18:::4;::::0;:38:::4;::::0;::::4;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::4;;;;;;;;;;;;::::0;::::4;;;;;;;;;1395:1:17;;1406:20:::3;:18;:20::i;24605:1961:48:-:0;24769:42;;:::i;:::-;24702:10;6970:40;7002:7;6970:31;:40::i;:::-;3054:16:36::1;:14;:16::i;:::-;;1339:19:17::2;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;24827:33:48::3;24863:28;24880:10;24863:16;:28::i;:::-;24910:55;::::0;;::::3;::::0;::::3;::::0;;;;;;;24827:64;;-1:-1:-1;24910:55:48::3;::::0;:9;;:23:::3;:55::i;:::-;24909:56;24901:65;;;::::0;::::3;;24977:43;;:::i;:::-;25023:45;::::0;;::::3;::::0;::::3;::::0;;;;;;;::::3;::::0;:9;;:13:::3;:45::i;:::-;24977:91;;25078:45;;:::i;:::-;25159:53;::::0;;::::3;::::0;::::3;::::0;;;25185:26:::3;::::0;::::3;25159:53:::0;;;25138:75:::3;::::0;25159:53:::3;::::0;:25:::3;:53::i;:::-;25138:16:::0;;:20:::3;:75::i;:::-;25339:38;::::0;;::::3;::::0;::::3;::::0;;;;;;;25078:135;;-1:-1:-1;25339:49:48::3;::::0;25378:9;25339:38:::3;:49::i;:::-;25335:840;;;25422:34;25445:10;25422:22;:34::i;:::-;25404:52;;25335:840;;;25586:62;25615:12;25629:18;25586:28;:62::i;:::-;25568:80;;25763:40;;:::i;:::-;25806:34;::::0;;::::3;::::0;::::3;::::0;;;;;;;:45:::3;::::0;25841:9;25806:34:::3;:45::i;:::-;25873:52;::::0;;::::3;::::0;::::3;::::0;;;25908:16:::3;25873:52:::0;;;25763:88;;-1:-1:-1;25873:52:48::3;::::0;25763:88;;25873:34:::3;:52::i;:::-;25865:95;;;::::0;-1:-1:-1;;;25865:95:48;;::::3;::::0;::::3;;;:::i;:::-;25974:46:::0;;;;26127:26:::3;::::0;;::::3;::::0;::::3;::::0;;;:22:::3;:26:::0;;;:37:::3;::::0;26154:9;26127:26:::3;:37::i;:::-;26102:62:::0;:22:::3;:62:::0;-1:-1:-1;25335:840:48::3;26235:18:::0;;26209:24;;26190:64:::3;::::0;26197:10:::3;::::0;26190:64:::3;::::0;26235:18:::3;::::0;26190:64:::3;26399:24:::0;;26355:18:::3;::::0;:69:::3;::::0;-1:-1:-1;;;;;26355:18:48;;::::3;::::0;26387:10:::3;::::0;26355:31:::3;:69::i;:::-;26492:18:::0;;26434:13:::3;::::0;:77:::3;::::0;-1:-1:-1;;;;;26434:13:48;;::::3;::::0;26465:10:::3;::::0;26485:4:::3;::::0;26434:30:::3;:77::i;:::-;26521:13;::::0;26540:18;;26521:38:::3;::::0;-1:-1:-1;;;26521:38:48;;-1:-1:-1;;;;;26521:13:48;;::::3;::::0;:18:::3;::::0;:38:::3;::::0;::::3;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;1395:1:17;;;1406:20:::2;:18;:20::i;35809:166:48:-:0;35904:7;1625:19:17;:17;:19::i;:::-;35930:38:48::1;35956:11;35930:25;:38::i;20570:1773::-:0;6421:20;:18;:20::i;:::-;3054:16:36::1;:14;:16::i;:::-;;1339:19:17::2;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;20810:10:48::3;20764:33;20800:21:::0;;;:9:::3;:21;::::0;;;;;;;;20994:53;;;;::::3;::::0;;;21020:26:::3;::::0;::::3;20994:53:::0;;;20953:193:::3;::::0;20994:75:::3;::::0;21052:16;;20994:53:::3;::::0;:25:::3;:53::i;:75::-;21087:34;::::0;;::::3;::::0;::::3;::::0;;;;;;;:45:::3;::::0;21122:9;21087:34:::3;:45::i;:::-;20953:23;:193::i;:::-;:249;;;;21150:52;21174:16;21192:9;21150:23;:52::i;:::-;20931:321;;;::::0;-1:-1:-1;;;20931:321:48;;::::3;::::0;::::3;;;:::i;:::-;21271:43;::::0;::::3;::::0;:48;21263:79:::3;;;::::0;-1:-1:-1;;;21263:79:48;;::::3;::::0;::::3;;;:::i;:::-;21356:38;::::0;;::::3;::::0;::::3;::::0;;;;;;;:41:::3;::::0;21395:1:::3;21356:38;:41::i;:::-;21352:204;;;21421:48;::::0;;::::3;::::0;::::3;::::0;;;21452:16:::3;21421:48:::0;;;::::3;::::0;:9;;:30:::3;:48::i;:::-;21413:91;;;::::0;-1:-1:-1;;;21413:91:48;;::::3;::::0;::::3;;;:::i;:::-;21523:22;::::0;21534:10:::3;::::0;21523:22:::3;::::0;;;::::3;21352:204;21651:60;21680:12;21694:16;21651:28;:60::i;:::-;-1:-1:-1::0;21837:34:48::3;::::0;;::::3;::::0;::::3;::::0;;;;;;;:45:::3;::::0;21872:9;21837:34:::3;:45::i;:::-;21804:78:::0;;;21918:26:::3;::::0;;::::3;::::0;::::3;::::0;;;:22:::3;:26:::0;;;:37:::3;::::0;21945:9;21918:26:::3;:37::i;:::-;21893:62:::0;:22:::3;:62:::0;22026:18;;21999:25;;21971:74:::3;::::0;21987:10:::3;::::0;21971:74:::3;::::0;21893:62:::3;::::0;21971:74:::3;22241:25:::0;;22178:18:::3;::::0;:89:::3;::::0;-1:-1:-1;;;;;22178:18:48;;::::3;::::0;22214:10:::3;::::0;22234:4:::3;::::0;22178:35:::3;:89::i;:::-;22285:13;::::0;22316:18;;22285:50:::3;::::0;-1:-1:-1;;;22285:50:48;;-1:-1:-1;;;;;22285:13:48;;::::3;::::0;-1:-1:-1;;22285:50:48::3;::::0;22304:10:::3;::::0;22316:18;22285:50:::3;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;22277:59;;;::::0;::::3;;1395:1:17;1406:20:::2;:18;:20::i;3356:35:48:-:0;;;-1:-1:-1;;;;;3356:35:48;;:::o;30948:352::-;6505:21;:19;:21::i;:::-;6874:16:::1;:14;:16::i;:::-;3054::36::2;:14;:16::i;:::-;;1339:19:17::3;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;31044:13:48::4;:51:::0;;-1:-1:-1;;31044:51:48::4;31060:35;31044:51;::::0;;31230:19:::4;::::0;31200:50:::4;::::0;:29:::4;:50::i;:::-;31266:27;::::0;31282:10:::4;::::0;31266:27:::4;::::0;;;::::4;1406:20:17::3;:18;:20::i;6582:58:47:-:0;;;;:::o;8939:124:36:-;9005:26;;:::i;:::-;1625:19:17;:17;:19::i;:::-;9050:6:36::1;:4;:6::i;1686:34:48:-:0;;;;;;:::o;3247:53::-;;;;:::o;4272:43::-;;;;:::o;4616:54::-;;;-1:-1:-1;;;;;4616:54:48;;:::o;9480:64:36:-;1339:19:17;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;9530:7:36::1;:5;:7::i;:::-;1406:20:17::0;:18;:20::i;3450:30:48:-;;;;:::o;33727:299::-;33809:26;;:::i;:::-;1625:19:17;:17;:19::i;:::-;-1:-1:-1;;;;;33985:18:48;::::1;;::::0;;;:9:::1;:18;::::0;;;;;;;;33959:59;;;;::::1;::::0;;;33985:32:::1;::::0;;::::1;33959:59:::0;;;33919:100:::1;::::0;33959:59:::1;::::0;:25:::1;:59::i;6764:1704:36:-:0;6871:37;;:::i;:::-;6922:38;;:::i;:::-;6974:36;;:::i;:::-;7035:20;7058:11;:9;:11::i;:::-;7035:34;;7079:41;;:::i;:::-;7123:6;:4;:6::i;:::-;7079:50;-1:-1:-1;7240:25:36;7079:50;7263:1;7240:22;:25::i;:::-;:52;;;;7288:4;7269:15;;:23;7240:52;7236:126;;;7308:43;;;;7236:126;7424:15;;7400:62;;-1:-1:-1;;;7400:62:36;;-1:-1:-1;;;;;7400:23:36;;;-1:-1:-1;;7400:62:36;;7424:15;7441:4;;7447:14;;7400:62;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7372:90;;-1:-1:-1;7372:90:36;-1:-1:-1;7485:27:36;7372:90;;7485:14;:27::i;:::-;7473:39;-1:-1:-1;7526:20:36;7473:39;7544:1;7526:17;:20::i;:::-;7522:94;;;7562:43;;;;7522:94;7990:39;:9;8014:14;7990:23;:39::i;:::-;7986:476;;;8045:34;;:::i;:::-;8082:29;:9;8096:14;8082:13;:29::i;:::-;8045:66;;8125:47;;:::i;:::-;8175:36;8190:11;8203:7;8175:14;:36::i;:::-;8125:86;-1:-1:-1;8239:37:36;:11;8125:86;8239:15;:37::i;:::-;8225:51;-1:-1:-1;8300:33:36;:7;8312:20;8300:11;:33::i;:::-;8290:43;;8360:51;8375:35;8390:10;8402:7;8375:14;:35::i;:::-;8360:10;;:14;:51::i;:::-;8347:64;;8437:14;8425:26;;7986:476;;;6764:1704;;;;;;;;:::o;3599:34:48:-;;;;:::o;4175:33::-;;;;:::o;26229:207:47:-;26345:40;1625:19:17;:17;:19::i;:::-;-1:-1:-1;;;;;26408:21:47;::::1;;::::0;;;:12:::1;:21;::::0;;;;;;;26401:28;;;;;;::::1;::::0;;;;;;;;;;;;26408:21;;26401:28;::::1;;;;;;;;;::::0;;;::::1;::::0;;;;::::1;::::0;;::::1;::::0;::::1;::::0;;::::1;::::0;::::1;::::0;;::::1;::::0;;-1:-1:-1;;;;;26401:28:47;;::::1;::::0;;-1:-1:-1;26401:28:47;::::1;::::0;;;::::1;::::0;;::::1;::::0;;;;;;;;;;-1:-1:-1;;;26401:28:47;::::1;;;;::::0;::::1;;;;;;;;;;;;;;::::0;;::::1;::::0;::::1;::::0;::::1;::::0;;::::1;::::0;;;;;;;;;::::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;;;;::::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;;;;::::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;;;;::::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;::::1;::::0;::::1;::::0;-1:-1:-1;;;;;26401:28:47::1;::::0;;;;;;;;::::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;;;;::::1;::::0;;;::::1;::::0;;::::1;::::0;;;::::1;::::0;;;;;;;;;;-1:-1:-1;26401:28:47;;;::::1;::::0;::::1;;;;;;;;;;26229:207:::0;;;:::o;18395:1642::-;18567:36;;:::i;:::-;18488:13;18503:7;7951:35;7963:13;7978:7;7951:11;:35::i;:::-;3054:16:36::1;:14;:16::i;:::-;;1339:19:17::2;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;18619:43:47::3;18665;18685:7;18694:13;18665:19;:43::i;:::-;18619:89;;18835:44;;:::i;:::-;18979:64;::::0;;::::3;::::0;::::3;::::0;;;19005:37:::3;::::0;::::3;18979:64:::0;;;18894:163:::3;::::0;18979:64:::3;::::0;:25:::3;:64::i;:::-;18894:63;::::0;;::::3;::::0;;::::3;::::0;;18935:21:::3;18894:63:::0;;;:40;;;;::::3;::::0;;;:36:::3;::::0;::::3;:40:::0;;;:63:::3;::::0;:40;::::3;:63::i;:163::-;18835:222;;19067:59;19082:24;19108:17;19067:14;:59::i;:::-;-1:-1:-1::0;19232:25:47::3;::::0;::::3;:43:::0;;-1:-1:-1;;;;19232:43:47::3;-1:-1:-1::0;;;19232:43:47::3;::::0;;19285:28:::3;::::0;::::3;:41:::0;;-1:-1:-1;;;;;;19285:41:47::3;19316:10;19285:41;::::0;;19260:15:::3;19411:35:::0;::::3;::::0;19380:67:::3;::::0;:30:::3;:67::i;:::-;19517:30;::::0;::::3;::::0;19612:26;;19463:185:::3;::::0;19561:10:::3;::::0;-1:-1:-1;;;;;19517:30:47;;::::3;::::0;19463:185;;::::3;::::0;::::3;::::0;::::3;::::0;19585:13;;19612:26;19463:185:::3;:::i;:::-;;;;;;;;19670:51;::::0;;::::3;::::0;::::3;::::0;;;19692:28:::3;::::0;::::3;19670:51:::0;;;::::3;::::0;:17;;:21:::3;:51::i;:::-;19796:55;::::0;;::::3;::::0;::::3;::::0;;;19822:28:::3;::::0;::::3;19796:55:::0;;;19658:63;;-1:-1:-1;19796:55:47::3;::::0;19810:10:::3;::::0;19796:13:::3;:55::i;:::-;20003:26:::0;;19940:18:::3;::::0;:90:::3;::::0;-1:-1:-1;;;;;19940:18:47;;::::3;::::0;19976:10:::3;::::0;19996:4:::3;::::0;19940:35:::3;:90::i;:::-;1395:1:17;;1406:20:::2;:18;:20::i;12149:392:48:-:0;6421:20;:18;:20::i;:::-;1339:19:17::1;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;12237:33:48::2;12273:28;12290:10;12273:16;:28::i;:::-;12237:64;;12319:12;:49;;;12372:1;12319:54;;12311:63;;;::::0;::::2;;12390:43;::::0;12422:10:::2;::::0;12390:43:::2;::::0;;;::::2;12533:1;12481:49;::::0;;::::2;:53:::0;1406:20:17::1;:18;:20::i;1335:29:36:-:0;;;-1:-1:-1;;;;;1335:29:36;;:::o;16420:921:48:-;6421:20;:18;:20::i;:::-;16563:10:::1;6970:40;7002:7;6970:31;:40::i;:::-;1339:19:17::2;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;16612:33:48::3;16648:28;16665:10;16648:16;:28::i;:::-;16612:64:::0;-1:-1:-1;16707:33:48::3;:16:::0;16738:1:::3;16707:30;:33::i;:::-;:142;;;;-1:-1:-1::0;16795:53:48::3;::::0;;::::3;::::0;::::3;::::0;;;16821:26:::3;::::0;::::3;16795:53:::0;;;16760:89:::3;::::0;16795:53:::3;::::0;:25:::3;:53::i;:::-;16760:16:::0;;:34:::3;:89::i;:::-;16686:173;;;::::0;::::3;;16951:23;16977:40;16998:18;;16977:16;:14;:16::i;:40::-;16951:66;;17053:19;;17035:15;:37;17027:46;;;::::0;::::3;;17136:43;::::0;::::3;:61:::0;;;17207:55;;:36:::3;::::0;::::3;:55:::0;;;17278:56:::3;::::0;17296:10:::3;::::0;17278:56:::3;::::0;17207:55:::3;::::0;17278:56:::3;1395:1:17;;1406:20:::2;:18;:20::i;33083:96:48:-:0;6421:20;:18;:20::i;:::-;1339:19:17::1;:17;:19::i;:::-;1368:17;:15;:17::i;14123:221:48:-:0;14298:39;14308:10;14320:16;14298:9;:39::i;:::-;14123:221;:::o;1999:50:36:-;;;;:::o;4367:38:48:-;;;;:::o;27269:3345::-;27395:42;;:::i;:::-;6505:21;:19;:21::i;:::-;3054:16:36::1;:14;:16::i;:::-;;1339:19:17::2;:17;:19::i;:::-;1368:17;:15;:17::i;:::-;27590:18:48::3;27573:13;::::0;::::3;;:35;::::0;::::3;;;;;;;;27565:66;;;::::0;-1:-1:-1;;;27565:66:48;;::::3;::::0;::::3;;;:::i;:::-;27756:34;27739:13;::::0;::::3;;:51;::::0;::::3;;;;;;;27735:206;;27820:46;27846:19;;27820:25;:46::i;:::-;27806:60:::0;:11:::3;:60:::0;27880:13:::3;:50:::0;;-1:-1:-1;;27880:50:48::3;27896:34;27880:50;::::0;;27735:206:::3;28043:41;;:::i;:::-;28087:56;::::0;;::::3;::::0;::::3;::::0;;;;28107:13:::3;::::0;-1:-1:-1;;;28107:35:48;;;28087:56;;;-1:-1:-1;;;;;28107:13:48::3;-1:-1:-1::0;28107:35:48::3;28131:10;28107:35:::0;;;::::3;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;28087:56:::0;;28043:100;-1:-1:-1;28153:52:48::3;;:::i;:::-;28208:31;::::0;;::::3;::::0;::::3;::::0;;;28227:11:::3;28208:31:::0;;;::::3;::::0;:14;;:18:::3;:31::i;:::-;28430:10;28384:33;28420:21:::0;;;:9:::3;:21;::::0;;;;;;;28455:53;;;;::::3;::::0;;;28481:26:::3;::::0;::::3;28455:53:::0;;;28153:86;;-1:-1:-1;28455:70:48::3;::::0;28384:33;28455:53:::3;::::0;:25:::3;:53::i;:::-;:67:::0;::::3;:70::i;:::-;28451:1184;;;28651:53;;:::i;:::-;28707:47;::::0;;::::3;::::0;;::::3;::::0;;28742:11:::3;28707:47:::0;;;:34;;;;::::3;::::0;;;;;;;:47:::3;::::0;:34;::::3;:47::i;:::-;28651:103;;28768:45;;:::i;:::-;28816:53;::::0;;::::3;::::0;::::3;::::0;;;28842:26:::3;::::0;::::3;28816:53:::0;;;::::3;::::0;:25:::3;:53::i;:::-;28768:101;;28982:55;;:::i;:::-;29056:57;:26:::0;29094:18;29056:37:::3;:57::i;:::-;:175;;29209:22;;;;;;;;29229:1;29209:22;;::::0;29056:175:::3;;;29136:50;:18:::0;29159:26;29136:22:::3;:50::i;:::-;28982:249:::0;-1:-1:-1;29379:59:48::3;:25:::0;28982:249;29379:29:::3;:59::i;:::-;29562:10;29552:21;::::0;;;:9:::3;:21;::::0;;;;;29545:28;;;::::3;::::0;::::3;::::0;;;::::3;::::0;::::3;::::0;;;::::3;::::0;::::3;::::0;;;::::3;;::::0;;;29592:32;29351:87;;-1:-1:-1;29562:10:48;;29592:32:::3;::::0;29552:21;29592:32:::3;28451:1184;;;;29840:33;;:::i;:::-;29903:53;::::0;;::::3;::::0;::::3;::::0;;;29929:26:::3;29903:53:::0;;;29888:96:::3;::::0;29903:53:::3;::::0;:25:::3;:53::i;:::-;29958:25;29888:14;:96::i;:::-;29840:144;;30082:53;30100:26;30128:6;30082:17;:53::i;:::-;30170:26;::::0;;::::3;::::0;::::3;::::0;;;:22:::3;:26:::0;;;30064:71;;-1:-1:-1;30170:42:48::3;::::0;30197:14;30170:26:::3;:42::i;:::-;30145:67:::0;:22:::3;:67:::0;30288:23;;30262:24;;30228:84:::3;::::0;30250:10:::3;::::0;30228:84:::3;::::0;30145:67:::3;::::0;30228:84:::3;30437:24:::0;;30393:18:::3;::::0;:69:::3;::::0;-1:-1:-1;;;;;30393:18:48;;::::3;::::0;30425:10:::3;::::0;30393:31:::3;:69::i;:::-;30530:23:::0;;30472:13:::3;::::0;:82:::3;::::0;-1:-1:-1;;;;;30472:13:48;;::::3;::::0;30503:10:::3;::::0;30523:4:::3;::::0;30472:30:::3;:82::i;:::-;30564:13;::::0;30583:23;;30564:43:::3;::::0;-1:-1:-1;;;30564:43:48;;-1:-1:-1;;;;;30564:13:48;;::::3;::::0;:18:::3;::::0;:43:::3;::::0;::::3;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;1395:1:17;;;;1406:20:::2;:18;:20::i;1032:135:16:-:0;1092:15;;:::i;:::-;1126:34;;;;;;;;;;1135:24;:1;607:6;1135:5;:24::i;:::-;1126:34;;1119:41;1032:135;-1:-1:-1;;1032:135:16:o;2248:147::-;2324:4;2360:19;2377:1;2360:16;:19::i;:::-;:28;2347:10;;:41;;-1:-1:-1;2248:147:16;;;;:::o;6433:151::-;6507:15;;:::i;:::-;6541:36;;;;;;;;;6565:10;;6550;;6541:36;;6550:26;;:10;:14;:26::i;:::-;6541:36;;6534:43;6433:151;-1:-1:-1;;;6433:151:16:o;4184:144::-;4257:4;4293:19;4310:1;4293:16;:19::i;:::-;:28;4280:10;;:41;;-1:-1:-1;4184:144:16;;;;:::o;2119:459:4:-;2177:7;2418:6;2414:45;;-1:-1:-1;2447:1:4;2440:8;;2414:45;2481:5;;;2485:1;2481;:5;:1;2504:5;;;;;:10;2496:56;;;;-1:-1:-1;;;2496:56:4;;;;;;;:::i;834:176::-;892:7;923:5;;;946:6;;;;938:46;;;;-1:-1:-1;;;938:46:4;;;;;;;:::i;2158:186:17:-;2290:11;;-1:-1:-1;;;2290:11:17;;;;2282:55;;;;-1:-1:-1;;;2282:55:17;;;;;;;:::i;49165:480:48:-;49292:26;;:::i;:::-;49347:23;;49339:45;;-1:-1:-1;;;;;49347:23:48;49339:43;:45::i;:::-;49334:64;;-1:-1:-1;49393:5:48;49386:12;;49334:64;49412:23;;:58;;-1:-1:-1;;;49412:58:48;;-1:-1:-1;;;;;49412:23:48;;;;:38;;:58;;49451:5;;49458:11;;49412:58;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;49412:58:48;;;;;;;;-1:-1:-1;;49412:58:48;;;;;;;;;;;;:::i;:::-;;;49408:231;;-1:-1:-1;49623:5:48;49616:12;;49408:231;49568:16;-1:-1:-1;49561:23:48;;46113:136;46194:19;;46175:16;:14;:16::i;:::-;:38;46167:75;;;;-1:-1:-1;;;46167:75:48;;;;;;;:::i;46988:181::-;47078:25;47095:7;47078:16;:25::i;:::-;:56;;;:61;47070:92;;;;-1:-1:-1;;;47070:92:48;;;;;;;:::i;2350:136:17:-;2474:5;2460:19;;-1:-1:-1;;;;2460:19:17;;;2350:136::o;39097:202:48:-;39231:20;39205:7;6613:36;6641:7;6613:27;:36::i;:::-;-1:-1:-1;;;;;;;39274:18:48::1;;::::0;;;:9:::1;:18;::::0;;;;;39097:202::o;43746:340::-;43904:26;;:::i;:::-;43942:60;43957:12;:26;;43985:16;43942:14;:60::i;:::-;;44019;44034:26;44062:16;44019:14;:60::i;843:203:9:-;943:96;963:5;993:27;;;1022:4;1028:2;1032:5;970:68;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;970:68:9;;;;;;;;;;;;;;-1:-1:-1;;;;;970:68:9;-1:-1:-1;;;;;;970:68:9;;;;;;;;;;;943:19;:96::i;:::-;843:203;;;;:::o;2492:206:17:-;2673:11;:18;;-1:-1:-1;;;;2673:18:17;-1:-1:-1;;;2673:18:17;;;2492:206::o;31716:565:47:-;31838:26;;:::i;:::-;31893:23;;31885:45;;-1:-1:-1;;;;;31893:23:47;31885:43;:45::i;:::-;31880:80;;-1:-1:-1;31932:28:47;;;;;;;;;31939:21;31932:28;;;;;31880:80;31974:23;;:84;;-1:-1:-1;;;31974:84:47;;-1:-1:-1;;;;;31974:23:47;;;;-1:-1:-1;;31974:84:47;;32029:5;;32036:21;;31974:84;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;31974:84:47;;;;;;;;-1:-1:-1;;31974:84:47;;;;;;;;;;;;:::i;:::-;;;31970:305;;-1:-1:-1;32236:28:47;;;;;;;;;32243:21;32236:28;;;;;43399:220:48;43525:30;43553:1;43525:27;:30::i;:::-;43486:69;:36;;;:69;;43565:43;;;;:47;43399:220::o;5792:146:16:-;5866:15;;:::i;:::-;5913:10;;5900;;:23;:31;;5930:1;5900:31;;;-1:-1:-1;5926:1:16;;5792:146;-1:-1:-1;5792:146:16:o;12169:233:36:-;12293:37;;:::i;:::-;12353:42;;;;;;;;;12371:23;12353:42;;;;;:13;;:17;:42::i;4820:142:16:-;4945:10;4931;;:24;;;4820:142::o;7107:151::-;7181:15;;:::i;:::-;7215:36;;;;;;;;;7239:10;;7224;;7215:36;;7224:26;;:10;:14;:26::i;8186:650::-;8260:15;;:::i;:::-;8773:56;;;;;;;;;8797:10;;8782;;8773:56;;607:6;;8782:26;;:14;:26::i;:::-;:46;;;;;;8773:56;;8766:63;8186:650;-1:-1:-1;;;8186:650:16:o;2896:145::-;3024:10;3010;;:24;;;2896:145::o;11695:208:36:-;11747:36;;:::i;:::-;11795:20;11818:11;:9;:11::i;:::-;11876:18;;11846:50;;-1:-1:-1;;;11846:50:36;;11795:34;;-1:-1:-1;;;;;;11846:21:36;;;;-1:-1:-1;;11846:50:36;;11876:18;;11846:50;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;11839:57;;;11695:208;:::o;10602:634:16:-;10676:15;;:::i;:::-;11170:59;;;;;;;;;11217:10;;11179;;11170:59;;11179:49;;:33;;607:6;11179:14;:33::i;:::-;:37;;:49::i;36329:1480:48:-;36578:33;36614:25;36631:7;36614:16;:25::i;:::-;36738:54;;;;;;;;;;;;;36578:61;;-1:-1:-1;36738:54:48;;:14;;:22;:54::i;:::-;:151;;;;-1:-1:-1;36808:53:48;;;;;;;;;36834:26;;;36808:53;;;:81;;36870:18;;36808:53;;:25;:53::i;:81::-;36721:255;;;36914:31;36937:7;36914:22;:31::i;:::-;;36959:7;;;36721:255;37063:62;37092:12;37106:18;37063:28;:62::i;:::-;;37224:40;;:::i;:::-;37267:34;;;;;;;;;;;;;:50;;37302:14;37267:34;:50::i;:::-;37335:52;;;;;;;;;37370:16;37335:52;;;37224:93;;-1:-1:-1;37335:52:48;;37224:93;;37335:34;:52::i;:::-;37327:95;;;;-1:-1:-1;;;37327:95:48;;;;;;;:::i;:::-;37432:46;;;;37583:40;;;;;;;;;:36;;;:40;;;:66;;37624:24;37583:40;:66::i;:::-;37544:105;:36;;;:105;37760:26;;;;;;;;;:22;:26;;;:42;;37787:14;37760:26;:42::i;:::-;37735:67;:22;:67;-1:-1:-1;;36329:1480:48;;;;:::o;15778:561:36:-;15923:42;;:::i;:::-;15981:41;;:::i;:::-;16025:40;;;;;;;;;;;;;;;:25;:40::i;:::-;15981:84;;16075:45;;:::i;:::-;16123:40;16147:15;16123:23;:40::i;:::-;16198:17;;;;;;;;;;;;;16075:88;;-1:-1:-1;16198:37:36;;16075:88;16198:17;:37::i;:::-;:46;16173:71;;;16272:40;;;;;;;;;;;;:60;;16317:14;;16272:40;;:25;:40::i;:60::-;16254:78;15778:561;-1:-1:-1;;;;;15778:561:36:o;13586:228::-;13705:40;;:::i;:::-;13768:39;;;;;;;;;13783:23;13768:39;;;;;:10;;:14;:39::i;1942:137:16:-;2062:10;2049;;:23;;1942:137::o;44552:346:48:-;44710:26;;:::i;:::-;44748:63;44766:12;:26;;44794:16;44748:17;:63::i;:::-;;44828;44846:26;44874:16;44828:17;:63::i;662:175:9:-;744:86;764:5;794:23;;;819:2;823:5;771:58;;;;;;;;;:::i;45976:131:48:-;46051:18;46034:13;;;;:35;;;;;;;;;46026:74;;;;-1:-1:-1;;;46026:74:48;;;;;;;:::i;39892:175::-;39987:6;;:73;;-1:-1:-1;;;39987:73:48;;39961:7;;-1:-1:-1;;;;;39987:6:48;;:31;;:73;;-1:-1:-1;;;;39987:73:48;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;40167:840::-;40248:42;40293:22;:20;:22::i;:::-;40248:67;;40411:33;;:::i;:::-;40447:19;:17;:19::i;:::-;40544:15;;40476:18;;40544:15;;-1:-1:-1;40476:84:48;;-1:-1:-1;;;;;40476:18:48;;40525:16;;40476:40;:84::i;:::-;-1:-1:-1;;;;;40570:29:48;;;40613:40;40639:13;40613:25;:40::i;:::-;40667:13;40694:19;:17;:19::i;:::-;40727:18;;40759:15;;40570:250;;-1:-1:-1;40570:250:48;;;-1:-1:-1;;;;;;40570:250:48;;;;;;;;-1:-1:-1;;;;;40727:18:48;;40759:15;40570:250;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;40954:46;40985:6;40993;:4;:6::i;:::-;40954:30;:46::i;31202:508:47:-;31289:35;31327:43;31347:7;31356:13;31327:19;:43::i;:::-;31395:17;;;;;;;-1:-1:-1;;;;31395:17:47;;;;;;31511:5;:26;;;;;;;;;31510:139;;;;31598:16;:14;:16::i;:::-;31560:34;31582:11;31560:21;:34::i;:::-;:54;;31559:89;;;;-1:-1:-1;31629:18:47;31620:5;:27;;;;;;;;;31559:89;31489:214;;;;-1:-1:-1;;;31489:214:47;;;;;;;:::i;29747:631::-;-1:-1:-1;;;;;29959:21:47;;29863:35;29959:21;;;:12;:21;;;;;30187:23;;30171:39;;:104;;;;-1:-1:-1;30255:20:47;30214:16;30231:13;30214:31;;;;;;;;;;;;;;;;;;:37;;;;;;;;;;;;:61;;;;;;;;;;30171:104;30150:173;;;;-1:-1:-1;;;30150:173:47;;;;;;;:::i;:::-;30340:16;30357:13;30340:31;;;;;;;;;;;;;;;;;;30333:38;;;29747:631;;;;:::o;27535:2031::-;27611:35;27649:43;27669:7;27678:13;27649:19;:43::i;:::-;27611:81;-1:-1:-1;27927:15:47;27906:17;;;;-1:-1:-1;;;27906:17:47;;;;:36;;;;;;;;;27902:73;;27958:7;;;27902:73;28108:55;28135:11;:27;;;28108:26;:55::i;:::-;28078:85;:27;;;:85;28244:47;;:::i;:::-;28306:62;;;;;;;;;28340:27;;;28306:62;;;:33;;;;;;;;:29;;;:33;;;:62;;:33;;:62::i;:::-;28244:124;;28752:45;;:::i;:::-;28837:60;;;;;;;;;28869:27;;;28837:60;;;28812:86;;28837:60;;:31;:60::i;28812:86::-;29179:53;;;;;;;;;:32;;;:53;;;28752:146;;-1:-1:-1;29155:21:47;;29179:73;;28752:146;29179:53;:73::i;:::-;29155:97;;29282:16;:65;;29327:20;29282:65;;;29301:23;29282:65;29262:17;;;:85;;-1:-1:-1;;;;29262:85:47;-1:-1:-1;;;29262:85:47;;;;;;;;;;;;;-1:-1:-1;29436:22:47;;;;29472:20;;;;29363:196;;-1:-1:-1;;;;;29436:22:47;;;;29363:196;;;;29391:10;;29363:196;;;;29472:20;;29506:13;;29533:16;;29363:196;:::i;:::-;;;;;;;;27535:2031;;;;;;:::o;14394:574:36:-;14545:44;;:::i;:::-;14605:41;;:::i;:::-;14649:40;;;;;;;;;;;;;;;:25;:40::i;:::-;14605:84;;14699:45;;:::i;:::-;14747:43;14771:18;14747:23;:43::i;:::-;14825:17;;;;;;;;;;;;;14699:91;;-1:-1:-1;14825:37:36;;14699:91;14825:17;:37::i;:::-;:46;14800:71;;;14920:40;;;;;;;;;;;;14901:60;;14920:40;;:25;:40::i;:::-;14901:14;;:18;:60::i;29572:169:47:-;29620:26;;:::i;:::-;29682:51;;;;;;;;;29708:24;29682:51;;;29665:69;;29682:51;;:25;:51::i;:::-;29665:12;:10;:12::i;1330:142:16:-;1400:4;1437:19;1454:1;1437:16;:19::i;:::-;:28;1423:10;;:42;;-1:-1:-1;1330:142:16;;;;:::o;16449:333:36:-;16594:39;;:::i;:::-;16636:26;:6;16651:10;16636:14;:26::i;:::-;16594:68;;16698:77;16726:48;16761:12;16726:30;16754:1;16726:27;:30::i;:48::-;16698:27;;;;;;;;;:23;:27;;;;;:77::i;:::-;16672:103;:23;:103;-1:-1:-1;;;16449:333:36:o;11534:155::-;11626:6;;:55;;-1:-1:-1;;;11626:55:36;;11578:14;;-1:-1:-1;;;;;11626:6:36;;:31;;:55;;-1:-1:-1;;;;11626:55:36;;;:::i;1671:283:9:-;1790:39;;-1:-1:-1;;;1790:39:9;;1767:20;;1790:50;;1834:5;;-1:-1:-1;;;;;1790:15:9;;;;;:39;;1814:4;;1821:7;;1790:39;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:50::-;1767:73;;1850:97;1870:5;1900:22;;;1924:7;1933:12;1877:69;;;;;;;;;:::i;45262:434:48:-;45428:26;;:::i;:::-;45466:63;45484:12;:26;;45512:16;45466:17;:63::i;:::-;;45547:45;45579:12;45547:31;:45::i;:::-;45539:70;;;;-1:-1:-1;;;45539:70:48;;;;;;;:::i;:::-;45626:63;45644:26;45672:16;45626:17;:63::i;12500:842:36:-;12638:26;;:::i;:::-;12685:61;;:::i;:::-;12762:43;12788:16;:14;:16::i;12762:43::-;12680:125;;;;12819:74;12862:30;12890:1;12862:27;:30::i;:::-;12819:34;;:42;:74::i;:::-;12815:100;;;12902:13;12895:20;;;;;12815:100;13022:50;;:::i;:::-;13075;13118:6;:4;:6::i;:::-;13075:34;;:42;:50::i;:::-;13022:103;;13257:78;13275:59;13310:23;13275:30;13303:1;13275:27;:30::i;:59::-;13257:13;;:17;:78::i;:::-;13250:85;12500:842;-1:-1:-1;;;;12500:842:36:o;1638:132:16:-;1753:10;1739;;:24;;1638:132::o;37918:1006:48:-;37985:26;;:::i;:::-;38023:40;38066:25;38083:7;38066:16;:25::i;:::-;38023:68;;38102:51;;:::i;:::-;38156:53;;;;;;;;;38182:26;38156:53;;;;;:25;:53::i;:::-;38102:107;;38302:49;;:::i;:::-;-1:-1:-1;38302:85:48;;;;;;;;;38354:33;;;38302:85;;;38426:30;;;;;;;;:26;:30;;;38302:85;38426:54;;38302:85;38426:30;:54::i;:::-;38397:83;:26;:83;38515:65;;;;;;;;;;;;;:26;;;;;;;;:22;:26;;;:65;;:26;;:65::i;:::-;38490:90;:22;:90;-1:-1:-1;;;;;38678:18:48;;38490:90;38678:18;;;:9;:18;;;;;;38671:25;;;-1:-1:-1;38671:25:48;;;;;;;;;;;;;;;;;;;;;;38712:29;;;38490:90;38712:29;38863:53;;;;;;;;;38889:26;38863:53;;;38834:83;;38863:53;;:25;:53::i;49651:440::-;49762:23;;49730:7;;49754:45;;-1:-1:-1;;;;;49762:23:48;49754:43;:45::i;:::-;49749:74;;-1:-1:-1;49808:15:48;;49801:22;;49749:74;49837:23;;49886:15;;49837:78;;-1:-1:-1;;;49837:78:48;;-1:-1:-1;;;;;49837:23:48;;;;-1:-1:-1;;49837:78:48;;49903:11;;49837:78;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;49837:78:48;;;;;;;;-1:-1:-1;;49837:78:48;;;;;;;;;;;;:::i;:::-;;;49833:252;;-1:-1:-1;50059:15:48;;50052:22;;47760:485;47916:4;47936:33;;:::i;:::-;48011:53;;;;;;;;;48037:26;48011:53;;;47984:105;;48011:53;;:25;:53::i;:::-;47984:105;;;;;;;;;48066:22;47984:105;;;:26;:105::i;:::-;47936:153;;48099:37;;:::i;:::-;48139:49;48166:10;48178:9;48139:26;:49::i;:::-;48099:89;-1:-1:-1;48206:32:48;:6;48099:89;48206:20;:32::i;:::-;48205:33;;47760:485;-1:-1:-1;;;;;47760:485:48:o;46255:139::-;46338:19;;46318:16;:14;:16::i;:::-;:39;;46310:77;;;;-1:-1:-1;;;46310:77:48;;;;;;;:::i;11078:369:36:-;11114:37;;:::i;:::-;11154:6;:4;:6::i;:::-;11114:46;;11170:41;;:::i;:::-;11214:64;;;;;;;;;;11234:18;;-1:-1:-1;;;11234:43:36;;;11214:64;;;-1:-1:-1;;;;;11234:18:36;-1:-1:-1;11234:43:36;11271:4;11234:43;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;11214:64;;11170:108;-1:-1:-1;11292:37:36;:10;11170:108;11292:21;:37::i;:::-;11288:153;;;11371:59;11399:30;:14;11418:10;11399:18;:30::i;11371:59::-;11345:85;:23;:85;11078:369;;:::o;30840:356:47:-;30925:35;30963:43;30983:7;30992:13;30963:19;:43::i;:::-;30925:81;;31057:34;31079:11;31057:21;:34::i;:::-;31038:16;:14;:16::i;:::-;:53;31037:100;;;;-1:-1:-1;31118:18:47;31097:17;;;;-1:-1:-1;;;31097:17:47;;;;:39;;;;;;;;;31037:100;31016:173;;;;-1:-1:-1;;;31016:173:47;;;;;;;:::i;42244:212:48:-;42326:22;42351:12;:10;:12::i;:::-;42326:37;-1:-1:-1;;;;;;42373:19:48;;;42393:40;42419:13;42393:25;:40::i;:::-;42435:13;42373:76;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;42244:212;;:::o;9991:1081:36:-;10087:17;:6;10102:1;10087:14;:17::i;:::-;10083:54;;;10120:7;;10083:54;10168:4;-1:-1:-1;;;;;10151:22:36;;;10147:674;;10331:15;;10273:18;;:74;;-1:-1:-1;;;;;10273:18:36;;;;10309:5;;10324:4;;10273:35;:74::i;:::-;10147:674;;;10473:41;;:::i;:::-;10517:6;:4;:6::i;:::-;10473:50;-1:-1:-1;10704:36:36;10473:50;10733:6;10704:28;:36::i;:::-;10696:45;;;;;;10756:54;10787:6;10795:14;10756:30;:54::i;:::-;10147:674;;10850:15;;10836:30;;;;10850:15;;10836:30;10877:20;10900:11;:9;:11::i;:::-;10978:15;;10921:18;;10877:34;;-1:-1:-1;10921:73:36;;-1:-1:-1;;;;;10921:18:36;;10877:34;;10921:40;:73::i;:::-;11037:18;;11004:61;;-1:-1:-1;;;11004:61:36;;-1:-1:-1;;;;;11004:24:36;;;;;;:61;;11037:18;;;;11058:6;;11004:61;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9991:1081;;;:::o;41125:1019:48:-;41201:26;;:::i;:::-;41339:42;41384:22;:20;:22::i;:::-;41339:67;-1:-1:-1;;;;;;41437:25:48;;;41488:4;41511:40;41537:13;41511:25;:40::i;:::-;41569:13;41600:19;:17;:19::i;:::-;41437:196;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;41416:227;;;;;;41653:28;-1:-1:-1;;;;;41696:34:48;;;41748:40;41774:13;41748:25;:40::i;:::-;41806:13;41837:19;:17;:19::i;:::-;41696:174;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;41653:217;;41985:1;41961:21;:25;41957:81;;;-1:-1:-1;42026:1:48;41957:81;42054:83;42070:51;;;;;;;;42098:21;42070:51;;;42123:13;42054:15;:83::i;3884:134:16:-;4001:10;3988;;:23;;3884:134::o;685:610:10:-;745:4;1206:20;;1051:66;1245:23;;;;;;:42;;-1:-1:-1;;1272:15:10;;;685:610;-1:-1:-1;;685:610:10:o;46400:234:48:-;-1:-1:-1;;;;;46525:18:48;;46573:1;46525:18;;;:9;:18;;;;;;;;46499:59;;;;;;;;46525:32;;46499:59;;;:76;;46573:1;46499:59;;:25;:59::i;:76::-;46478:149;;;;-1:-1:-1;;;46478:149:48;;;;;;;:::i;2671:1096:9:-;3267:27;-1:-1:-1;;;;;3267:25:9;;;:27::i;:::-;3259:71;;;;-1:-1:-1;;;3259:71:9;;;;;;;:::i;:::-;3442:25;;3401:12;;3415:23;;-1:-1:-1;;;;;3442:19:9;;;:25;;3462:4;;3442:25;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3400:67;;;;3485:7;3477:52;;;;-1:-1:-1;;;3477:52:9;;;;;;;:::i;:::-;3544:17;;:21;3540:221;;3684:10;3673:30;;;;;;;;;;;;:::i;:::-;3665:85;;;;-1:-1:-1;;;3665:85:9;;;;;;;:::i;1274:134:4:-;1332:7;1358:43;1362:1;1365;1358:43;;;;;;;;;;;;;;;;;:3;:43::i;3033:130::-;3091:7;3117:39;3121:1;3124;3117:39;;;;;;;;;;;;;;;;;:3;:39::i;39687:199:48:-;39812:6;;:66;;-1:-1:-1;;;39812:66:48;;39742:25;;-1:-1:-1;;;;;39812:6:48;;:31;;:66;;-1:-1:-1;;;;39812:66:48;;;:::i;50097:296::-;50371:13;;50346:40;;50149:12;;50346:40;;-1:-1:-1;;;;;50371:13:48;;;;50346:40;;;:::i;:::-;;;;;;;;;;;;;50339:47;;50097:296;:::o;30384:176:47:-;30475:7;30501:52;30533:19;;30501:11;:27;;;:31;;:52;;;;:::i;42574:722:48:-;42656:26;;:::i;:::-;42794:22;42819:12;:10;:12::i;:::-;42794:37;-1:-1:-1;;;;;;42849:15:48;;;42865:40;42891:13;42865:25;:40::i;:::-;42907:13;42849:72;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;42841:108;;;;-1:-1:-1;;;42841:108:48;;;;;;;:::i;:::-;42959:18;-1:-1:-1;;;;;42980:15:48;;;42996:40;43022:13;42996:25;:40::i;:::-;43038:13;42980:72;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;38930:161;38986:26;;:::i;:::-;39031:53;;;;;;;;;39057:26;39031:53;;;;;:25;:53::i;12377:398:16:-;12455:15;;:::i;:::-;12500:10;;12482:15;;12500:33;;607:6;12500:14;:33::i;:::-;12574:10;;12482:51;;-1:-1:-1;12543:16:16;;12562:23;;12482:51;;12562:11;:23::i;:::-;12621:10;;12543:42;;-1:-1:-1;12595:11:16;;12609:23;;:7;;:11;:23::i;:::-;12595:37;-1:-1:-1;12646:8:16;;12642:127;;12677:25;;;;;;;;;;12686:15;:8;12699:1;12686:12;:15::i;:::-;12677:25;;;12670:32;;;;;;;12642:127;12740:18;;;;;;;;12749:8;12740:18;;;12733:25;;;;;;;47314:296:48;47488:53;;;;;;;;;47514:26;;;47488:53;;;47412:4;;47447:156;;47488:53;;:25;:53::i;:::-;47447:156;;;;;;;;;;;;;:23;:156::i;48251:371::-;48410:32;;:::i;:::-;48463:26;:9;48487:1;48463:23;:26::i;:::-;48458:158;;48512:30;48540:1;48512:27;:30::i;48458:158::-;48580:25;:10;48595:9;48580:14;:25::i;39522:159::-;39617:6;;:56;;-1:-1:-1;;;39617:56:48;;39567:15;;-1:-1:-1;;;;;39617:6:48;;:31;;:56;;-1:-1:-1;;;;39617:56:48;;;:::i;1692:187:4:-;1778:7;1813:12;1805:6;;;;1797:29;;;;-1:-1:-1;;;1797:29:4;;;;;;;;:::i;:::-;-1:-1:-1;;;1848:5:4;;;1692:187::o;3638:338::-;3724:7;3824:12;3817:5;3809:28;;;;-1:-1:-1;;;3809:28:4;;;;;;;;:::i;:::-;;3847:9;3863:1;3859;:5;;;;;;;3638:338;-1:-1:-1;;;;;3638:338:4:o;4420:128::-;4478:7;4504:37;4508:1;4511;4504:37;;;;;;;;;;;;;;;;;5098:7;5133:12;5125:6;5117:29;;;;-1:-1:-1;;;5117:29:4;;;;;;;;:::i;:::-;;5167:1;5163;:5;;;;;;;5012:163;-1:-1:-1;;;;5012:163:4:o;-1:-1:-1:-;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;:::o;733:159::-;;845:2;836:6;831:3;827:16;823:25;820:2;;;-1:-1;;851:12;932:328;;1047:4;1035:9;1030:3;1026:19;1022:30;1019:2;;;-1:-1;;1055:12;1019:2;1083:20;1047:4;1083:20;:::i;:::-;1724;;1164:75;;-1:-1;1074:29;1013:247;-1:-1;1013:247::o;1300:350::-;;1426:4;1414:9;1409:3;1405:19;1401:30;1398:2;;;-1:-1;;1434:12;1398:2;1462:20;1426:4;1462:20;:::i;:::-;1872:13;;1543:86;;-1:-1;1453:29;1392:258;-1:-1;1392:258::o;2072:241::-;;2176:2;2164:9;2155:7;2151:23;2147:32;2144:2;;;-1:-1;;2182:12;2144:2;85:6;72:20;97:33;124:5;97:33;:::i;2320:263::-;;2435:2;2423:9;2414:7;2410:23;2406:32;2403:2;;;-1:-1;;2441:12;2403:2;226:6;220:13;238:33;265:5;238:33;:::i;2590:911::-;;;;;;2846:3;2834:9;2825:7;2821:23;2817:33;2814:2;;;-1:-1;;2853:12;2814:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;2905:63;-1:-1;3023:81;3096:7;3005:2;3072:22;;3023:81;:::i;:::-;3013:91;;3159:81;3232:7;3141:2;3212:9;3208:22;3159:81;:::i;:::-;3149:91;;3295:81;3368:7;3277:2;3348:9;3344:22;3295:81;:::i;:::-;2808:693;;;;-1:-1;2808:693;;3413:3;3453:22;1724:20;;2808:693;-1:-1;;2808:693::o;3508:418::-;;;3655:2;3643:9;3634:7;3630:23;3626:32;3623:2;;;-1:-1;;3661:12;3623:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;3713:63;-1:-1;3831:79;3902:7;3813:2;3878:22;;3831:79;:::i;:::-;3821:89;;3617:309;;;;;:::o;3933:366::-;;;4054:2;4042:9;4033:7;4029:23;4025:32;4022:2;;;-1:-1;;4060:12;4022:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;4112:63;4212:2;4251:22;;;;1724:20;;-1:-1;;;4016:283::o;4306:257::-;;4418:2;4406:9;4397:7;4393:23;4389:32;4386:2;;;-1:-1;;4424:12;4386:2;364:6;358:13;57198:5;53116:13;53109:21;57176:5;57173:32;57163:2;;-1:-1;;57209:12;4570:263;;4685:2;4673:9;4664:7;4660:23;4656:32;4653:2;;;-1:-1;;4691:12;4653:2;-1:-1;496:13;;4647:186;-1:-1;4647:186::o;5108:293::-;;5238:2;5226:9;5217:7;5213:23;5209:32;5206:2;;;-1:-1;;5244:12;5206:2;5306:79;5377:7;5353:22;5306:79;:::i;5408:315::-;;5549:2;5537:9;5528:7;5524:23;5520:32;5517:2;;;-1:-1;;5555:12;5517:2;5617:90;5699:7;5675:22;5617:90;:::i;5730:470::-;;;5903:2;5891:9;5882:7;5878:23;5874:32;5871:2;;;-1:-1;;5909:12;5871:2;5971:79;6042:7;6018:22;5971:79;:::i;6207:503::-;;;6391:2;6379:9;6370:7;6366:23;6362:32;6359:2;;;-1:-1;;6397:12;6359:2;6459:90;6541:7;6517:22;6459:90;:::i;:::-;6449:100;;6604:90;6686:7;6586:2;6666:9;6662:22;6604:90;:::i;6717:418::-;;;6864:2;6852:9;6843:7;6839:23;6835:32;6832:2;;;-1:-1;;6870:12;6832:2;6932:79;7003:7;6979:22;6932:79;:::i;7142:241::-;;7246:2;7234:9;7225:7;7221:23;7217:32;7214:2;;;-1:-1;;7252:12;7214:2;-1:-1;1724:20;;7208:175;-1:-1;7208:175::o;7660:366::-;;;7781:2;7769:9;7760:7;7756:23;7752:32;7749:2;;;-1:-1;;7787:12;7749:2;1737:6;1724:20;7839:63;;7939:2;7982:9;7978:22;72:20;97:33;124:5;97:33;:::i;:::-;7947:63;;;;7743:283;;;;;:::o;8033:259::-;;8146:2;8134:9;8125:7;8121:23;8117:32;8114:2;;;-1:-1;;8152:12;8114:2;2017:6;2011:13;53865:4;57688:5;53854:16;57665:5;57662:33;57652:2;;-1:-1;;57699:12;8768:103;-1:-1;;;;;53649:54;8829:37;;8823:48::o;10445:343::-;;10587:5;51696:12;52308:6;52303:3;52296:19;10680:52;10725:6;52345:4;52340:3;52336:14;52345:4;10706:5;10702:16;10680:52;:::i;:::-;56533:7;56517:14;-1:-1;;56513:28;10744:39;;;;52345:4;10744:39;;10535:253;-1:-1;;10535:253::o;12047:136::-;56964:1;56957:5;56954:12;56944:2;;56970:9;56944:2;12118:60;;12112:71::o;24150:318::-;24355:23;10396:37;;24258:210::o;25698:253::-;56628:2;56624:14;;;;-1:-1;;56624:14;9087:58;;25923:2;25914:12;;25814:137::o;25958:271::-;;10955:5;51696:12;11066:52;11111:6;11106:3;11099:4;11092:5;11088:16;11066:52;:::i;:::-;11130:16;;;;;26092:137;-1:-1;;26092:137::o;26236:222::-;-1:-1;;;;;53649:54;;;;8829:37;;26363:2;26348:18;;26334:124::o;26710:349::-;-1:-1;;;;;53649:54;;;;8698:58;;27045:2;27030:18;;10396:37;26873:2;26858:18;;26844:215::o;27066:333::-;-1:-1;;;;;53649:54;;;8829:37;;53649:54;;27385:2;27370:18;;8829:37;27221:2;27206:18;;27192:207::o;27406:1986::-;-1:-1;;;;;53649:54;;;8829:37;;53649:54;;;28301:2;28286:18;;8829:37;28136:3;28121:19;;;28316:82;28394:2;28379:18;;28370:6;28316:82;:::i;:::-;28477:2;28462:18;;10396:37;;;;24355:23;;28612:3;28597:19;;10396:37;24355:23;;28748:3;28733:19;;10396:37;24355:23;;28884:3;28869:19;;10396:37;24355:23;;29020:3;29005:19;;10396:37;53649:54;;;29104:3;29089:19;;8829:37;24355:23;;29240:3;29225:19;;10396:37;24355:23;;29377:3;29362:19;;;10396:37;;;;28107:1285;;-1:-1;;;28107:1285::o;29399:444::-;-1:-1;;;;;53649:54;;;8829:37;;53649:54;;;;29746:2;29731:18;;8829:37;29829:2;29814:18;;10396:37;;;;29582:2;29567:18;;29553:290::o;29850:640::-;;53660:42;;;;;53032:5;53649:54;8836:3;8829:37;10426:5;30244:2;30233:9;30229:18;10396:37;10426:5;30327:2;30316:9;30312:18;10396:37;30079:3;30364:2;30353:9;30349:18;30342:48;30404:76;30079:3;30068:9;30064:19;30466:6;30404:76;:::i;:::-;30396:84;30050:440;-1:-1;;;;;;30050:440::o;30497:437::-;-1:-1;;;;;53649:54;;;;8829:37;;24355:23;30920:2;30905:18;;10396:37;30704:2;30689:18;;30675:259::o;31281:432::-;-1:-1;;;;;53649:54;;;;8829:37;;31622:2;31607:18;;10396:37;;;;53116:13;53109:21;31699:2;31684:18;;10279:34;31458:2;31443:18;;31429:284::o;31720:506::-;31965:2;31979:47;;;51696:12;;31950:18;;;52296:19;;;31720:506;;31965:2;52336:14;;;;;;51516;;;31720:506;9822:362;9847:6;9844:1;9841:13;9822:362;;;9914:6;9908:13;20478:63;20526:14;20455:16;20449:23;20478:63;:::i;:::-;31965:2;20619:5;20615:16;20609:23;20638:63;31965:2;20690:3;20686:14;20672:12;20638:63;:::i;:::-;;52336:14;20774:5;20770:16;20764:23;20793:73;52336:14;20855:3;20851:14;20837:12;20793:73;:::i;:::-;-1:-1;20956:4;20945:16;;;20939:23;21016:14;;;10396:37;21123:4;21112:16;;;21106:23;21135:115;21235:14;;;21106:23;21135:115;:::i;:::-;;;21341:4;;21334:5;21330:16;21324:23;21353:115;21341:4;21457:3;21453:14;21439:12;21353:115;:::i;:::-;;;21563:4;;21556:5;21552:16;21546:23;21575:115;21563:4;21679:3;21675:14;21661:12;21575:115;:::i;:::-;;;21782:4;;21775:5;21771:16;21765:23;21794:115;21782:4;21898:3;21894:14;21880:12;21794:115;:::i;:::-;;;21992:6;;21985:5;21981:18;21975:25;22006:65;21992:6;22058:3;22054:16;22040:12;22006:65;:::i;:::-;;;22161:6;;22154:5;22150:18;22144:25;22175:117;22161:6;22279:3;22275:16;22261:12;22175:117;:::i;:::-;-1:-1;;22375:6;22364:18;;;22358:25;;22389:117;22489:16;;;22358:25;22389:117;:::i;:::-;-1:-1;;8598:6;8589:16;;;;;52117:14;;;;9869:1;9862:9;9822:362;;;-1:-1;32032:184;;31936:290;-1:-1;;;;;;;31936:290::o;32233:222::-;10396:37;;;32360:2;32345:18;;32331:124::o;32462:333::-;10396:37;;;32781:2;32766:18;;10396:37;32617:2;32602:18;;32588:207::o;32802:528::-;;10426:5;10403:3;10396:37;10426:5;33167:2;33156:9;33152:18;10396:37;33003:2;33204;33193:9;33189:18;33182:48;33244:76;33003:2;32992:9;32988:18;33306:6;33244:76;:::i;33337:782::-;;10426:5;10403:3;10396:37;10426:5;33774:2;33763:9;33759:18;10396:37;33609:3;33811:2;33800:9;33796:18;33789:48;33851:76;33609:3;33598:9;33594:19;33913:6;33851:76;:::i;:::-;-1:-1;;;;;53649:54;;;;34021:2;34006:18;;11252:73;-1:-1;34104:3;34089:19;10396:37;53649:54;33843:84;-1:-1;;;33580:539::o;35232:256::-;35376:2;35361:18;;56854:1;56844:12;;56834:2;;56860:9;56834:2;11968:67;;;35347:141;:::o;35495:310::-;;35642:2;35663:17;35656:47;35717:78;35642:2;35631:9;35627:18;35781:6;35717:78;:::i;35812:416::-;36012:2;36026:47;;;12922:2;35997:18;;;52296:19;12958:30;52336:14;;;12938:51;13008:12;;;35983:245::o;36235:416::-;36435:2;36449:47;;;13259:2;36420:18;;;52296:19;13295:29;52336:14;;;13275:50;13344:12;;;36406:245::o;36658:416::-;36858:2;36872:47;;;13595:2;36843:18;;;52296:19;-1:-1;;;52336:14;;;13611:43;-1:-1;13673:12;;36829:245::o;37081:416::-;37281:2;37295:47;;;37266:18;;;52296:19;13960:34;52336:14;;;13940:55;14014:12;;;37252:245::o;37504:416::-;37704:2;37718:47;;;14265:2;37689:18;;;52296:19;14301:28;52336:14;;;14281:49;14349:12;;;37675:245::o;37927:416::-;38127:2;38141:47;;;14600:2;38112:18;;;52296:19;-1:-1;;;52336:14;;;14616:46;14681:12;;;38098:245::o;38350:416::-;38550:2;38564:47;;;14932:2;38535:18;;;52296:19;14968:32;52336:14;;;14948:53;15020:12;;;38521:245::o;38773:416::-;38973:2;38987:47;;;15271:2;38958:18;;;52296:19;-1:-1;;;52336:14;;;15287:45;15351:12;;;38944:245::o;39196:416::-;39396:2;39410:47;;;15602:2;39381:18;;;52296:19;15638:28;52336:14;;;15618:49;15686:12;;;39367:245::o;39619:416::-;39819:2;39833:47;;;15937:2;39804:18;;;52296:19;15973:28;52336:14;;;15953:49;16021:12;;;39790:245::o;40042:416::-;40242:2;40256:47;;;16272:2;40227:18;;;52296:19;16308:34;52336:14;;;16288:55;-1:-1;;;16363:12;;;16356:25;16400:12;;;40213:245::o;40465:416::-;40665:2;40679:47;;;16651:2;40650:18;;;52296:19;-1:-1;;;52336:14;;;16667:41;16727:12;;;40636:245::o;40888:416::-;41088:2;41102:47;;;16978:2;41073:18;;;52296:19;17014:32;52336:14;;;16994:53;17066:12;;;41059:245::o;41311:416::-;41511:2;41525:47;;;17317:2;41496:18;;;52296:19;-1:-1;;;52336:14;;;17333:35;17387:12;;;41482:245::o;41734:416::-;41934:2;41948:47;;;17638:2;41919:18;;;52296:19;-1:-1;;;52336:14;;;17654:46;17719:12;;;41905:245::o;42157:416::-;42357:2;42371:47;;;17970:2;42342:18;;;52296:19;18006:26;52336:14;;;17986:47;18052:12;;;42328:245::o;42580:416::-;42780:2;42794:47;;;18303:2;42765:18;;;52296:19;18339:27;52336:14;;;18319:48;18386:12;;;42751:245::o;43003:416::-;43203:2;43217:47;;;18637:2;43188:18;;;52296:19;18673:32;52336:14;;;18653:53;18725:12;;;43174:245::o;43426:416::-;43626:2;43640:47;;;18976:2;43611:18;;;52296:19;19012:34;52336:14;;;18992:55;-1:-1;;;19067:12;;;19060:34;19113:12;;;43597:245::o;43849:416::-;44049:2;44063:47;;;19364:2;44034:18;;;52296:19;19400:33;52336:14;;;19380:54;19453:12;;;44020:245::o;44272:416::-;44472:2;44486:47;;;19704:2;44457:18;;;52296:19;-1:-1;;;52336:14;;;19720:41;19780:12;;;44443:245::o;44695:416::-;44895:2;44909:47;;;20031:2;44880:18;;;52296:19;20067:33;52336:14;;;20047:54;20120:12;;;44866:245::o;45118:343::-;;45305:3;45294:9;45290:19;45282:27;;22840:16;22834:23;24355;10403:3;10396:37;23068:4;23061:5;23057:16;23051:23;24355;23068:4;23184:3;23180:14;10396:37;23283:4;23276:5;23272:16;23266:23;24355;23283:4;23399:3;23395:14;10396:37;23498:4;23491:5;23487:16;23481:23;24355;23498:4;23614:3;23610:14;10396:37;23716:4;23709:5;23705:16;23699:23;24355;23716:4;23832:3;23828:14;10396:37;23932:4;23925:5;23921:16;23915:23;24355;23932:4;24048:3;24044:14;10396:37;45276:185;;;;:::o;45468:326::-;24355:23;;10396:37;;45647:2;45632:18;;45618:176::o;45801:756::-;24355:23;;10396:37;;24355:23;;46408:2;46393:18;;10396:37;24355:23;46543:2;46528:18;;10396:37;46140:2;46125:18;;46111:446::o;46564:535::-;24355:23;;10396:37;;25153:23;47085:2;47070:18;;10396:37;46820:2;46805:18;;46791:308::o;47106:437::-;24355:23;;10396:37;;47529:2;47514:18;;10396:37;47313:2;47298:18;;47284:259::o;47550:980::-;24355:23;;10396:37;;48162:2;48147:18;;10396:37;;;;24355:23;;48297:2;48282:18;;10396:37;24355:23;48432:2;48417:18;;10396:37;48515:3;48500:19;;10396:37;47945:3;47930:19;;47916:614::o;48766:652::-;10396:37;;;24355:23;;49269:2;49254:18;;10396:37;24355:23;49404:2;49389:18;;10396:37;49053:2;49038:18;;49024:394::o;49765:548::-;10396:37;;;50164:2;50149:18;;10396:37;;;;24355:23;50299:2;50284:18;;10396:37;50000:2;49985:18;;49971:342::o;50320:556::-;10396:37;;;50696:2;50681:18;;10396:37;;;;50779:2;50764:18;;10396:37;50862:2;50847:18;;10396:37;50531:3;50516:19;;50502:374::o;50883:214::-;53865:4;53854:16;;;;25651:35;;51006:2;50991:18;;50977:120::o;51104:256::-;51166:2;51160:9;51192:17;;;51267:18;51252:34;;51288:22;;;51249:62;51246:2;;;51324:1;;51314:12;51246:2;51166;51333:22;51144:216;;-1:-1;51144:216::o;55807:268::-;55872:1;55879:101;55893:6;55890:1;55887:13;55879:101;;;55960:11;;;55954:18;55941:11;;;55934:39;55915:2;55908:10;55879:101;;;55995:6;55992:1;55989:13;55986:2;;;-1:-1;;55872:1;56042:16;;56035:27;55856:219::o;56993:117::-;-1:-1;;;;;53649:54;;57052:35;;57042:2;;57101:1;;57091:12
Swarm Source
ipfs://d730fd0020b72c4d1d90b7f9a77b9ed5a64fb3680c693525ebaaccb811e6741b
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $2,423.32 | 2.7558 | $6,678.3 |
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.