Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 11,181 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Retrieve Rewards | 16741128 | 706 days ago | IN | 0 ETH | 0.00438931 | ||||
Retrieve Rewards | 16740579 | 706 days ago | IN | 0 ETH | 0.00300081 | ||||
Retrieve Rewards | 16739454 | 706 days ago | IN | 0 ETH | 0.00341393 | ||||
Retrieve Rewards | 16738694 | 706 days ago | IN | 0 ETH | 0.00399435 | ||||
Retrieve Rewards | 16738679 | 706 days ago | IN | 0 ETH | 0.00373222 | ||||
Retrieve Rewards | 16737880 | 706 days ago | IN | 0 ETH | 0.00354762 | ||||
Retrieve Rewards | 16737836 | 706 days ago | IN | 0 ETH | 0.00610772 | ||||
Retrieve Rewards | 16737749 | 706 days ago | IN | 0 ETH | 0.00349638 | ||||
Retrieve Rewards | 16737718 | 706 days ago | IN | 0 ETH | 0.00373946 | ||||
Retrieve Rewards | 16737602 | 706 days ago | IN | 0 ETH | 0.00394369 | ||||
Retrieve Rewards | 16737209 | 706 days ago | IN | 0 ETH | 0.00474337 | ||||
Retrieve Rewards | 16737206 | 706 days ago | IN | 0 ETH | 0.00682975 | ||||
Batch Reveal | 16736854 | 706 days ago | IN | 0 ETH | 0.00271091 | ||||
Batch Reveal | 16736643 | 706 days ago | IN | 0 ETH | 0.0031592 | ||||
Batch Reveal | 16736482 | 706 days ago | IN | 0 ETH | 0.00258488 | ||||
Batch Reveal | 16735377 | 707 days ago | IN | 0 ETH | 0.00547422 | ||||
Retrieve Rewards | 16735371 | 707 days ago | IN | 0 ETH | 0.01304326 | ||||
Batch Reveal | 16735341 | 707 days ago | IN | 0 ETH | 0.00748274 | ||||
Batch Reveal | 16735287 | 707 days ago | IN | 0 ETH | 0.00558714 | ||||
Batch Reveal | 16734871 | 707 days ago | IN | 0 ETH | 0.00825118 | ||||
Batch Reveal | 16734656 | 707 days ago | IN | 0 ETH | 0.00651165 | ||||
Batch Reveal | 16734558 | 707 days ago | IN | 0 ETH | 0.00449224 | ||||
Batch Reveal | 16734289 | 707 days ago | IN | 0 ETH | 0.00269312 | ||||
Batch Reveal | 16734021 | 707 days ago | IN | 0 ETH | 0.00229393 | ||||
Batch Reveal | 16733762 | 707 days ago | IN | 0 ETH | 0.00276065 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Voting
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 "../../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; 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/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; /** * @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. */ constructor( address _governor, address _existingVoting, address _newVoting, address _finder ) public { governor = _governor; existingVoting = Voting(_existingVoting); newVoting = _newVoting; finder = Finder(_finder); } /** * @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 current Voting contract to migrated. existingVoting.setMigrated(newVoting); // 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 "./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; 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":[{"internalType":"uint256","name":"_phaseLength","type":"uint256"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"_gatPercentage","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"_inflationRate","type":"tuple"},{"internalType":"uint256","name":"_rewardsExpirationTimeout","type":"uint256"},{"internalType":"address","name":"_votingToken","type":"address"},{"internalType":"address","name":"_finder","type":"address"},{"internalType":"address","name":"_timerAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"ancillaryData","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"encryptedVote","type":"bytes"}],"name":"EncryptedVote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"}],"name":"PriceRequestAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"},{"indexed":false,"internalType":"int256","name":"price","type":"int256"},{"indexed":false,"internalType":"bytes","name":"ancillaryData","type":"bytes"}],"name":"PriceResolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"ancillaryData","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"numTokens","type":"uint256"}],"name":"RewardsRetrieved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"ancillaryData","type":"bytes"}],"name":"VoteCommitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"},{"indexed":false,"internalType":"int256","name":"price","type":"int256"},{"indexed":false,"internalType":"bytes","name":"ancillaryData","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"numTokens","type":"uint256"}],"name":"VoteRevealed","type":"event"},{"inputs":[],"name":"ancillaryBytesLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes","name":"ancillaryData","type":"bytes"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"encryptedVote","type":"bytes"}],"internalType":"struct VotingAncillaryInterface.CommitmentAncillary[]","name":"commits","type":"tuple[]"}],"name":"batchCommit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"encryptedVote","type":"bytes"}],"internalType":"struct VotingInterface.Commitment[]","name":"commits","type":"tuple[]"}],"name":"batchCommit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"int256","name":"price","type":"int256"},{"internalType":"int256","name":"salt","type":"int256"}],"internalType":"struct VotingInterface.Reveal[]","name":"reveals","type":"tuple[]"}],"name":"batchReveal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"int256","name":"price","type":"int256"},{"internalType":"bytes","name":"ancillaryData","type":"bytes"},{"internalType":"int256","name":"salt","type":"int256"}],"internalType":"struct VotingAncillaryInterface.RevealAncillary[]","name":"reveals","type":"tuple[]"}],"name":"batchReveal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes","name":"ancillaryData","type":"bytes"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"encryptedVote","type":"bytes"}],"name":"commitAndEmitEncryptedVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"encryptedVote","type":"bytes"}],"name":"commitAndEmitEncryptedVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes","name":"ancillaryData","type":"bytes"},{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"commitVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"commitVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gatPercentage","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentRoundId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingRequests","outputs":[{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes","name":"ancillaryData","type":"bytes"}],"internalType":"struct VotingAncillaryInterface.PendingRequestAncillary[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes","name":"ancillaryData","type":"bytes"}],"name":"getPrice","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"}],"name":"getPrice","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes","name":"ancillaryData","type":"bytes"}],"internalType":"struct VotingAncillaryInterface.PendingRequestAncillary[]","name":"requests","type":"tuple[]"}],"name":"getPriceRequestStatuses","outputs":[{"components":[{"internalType":"enum Voting.RequestStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"lastVotingRound","type":"uint256"}],"internalType":"struct Voting.RequestState[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"}],"internalType":"struct VotingInterface.PendingRequest[]","name":"requests","type":"tuple[]"}],"name":"getPriceRequestStatuses","outputs":[{"components":[{"internalType":"enum Voting.RequestStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"lastVotingRound","type":"uint256"}],"internalType":"struct Voting.RequestState[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVotePhase","outputs":[{"internalType":"enum VotingAncillaryInterface.Phase","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"}],"name":"hasPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes","name":"ancillaryData","type":"bytes"}],"name":"hasPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inflationRate","outputs":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"migratedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes","name":"ancillaryData","type":"bytes"}],"name":"requestPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"}],"name":"requestPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"voterAddress","type":"address"},{"internalType":"uint256","name":"roundId","type":"uint256"},{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes","name":"ancillaryData","type":"bytes"}],"internalType":"struct VotingAncillaryInterface.PendingRequestAncillary[]","name":"toRetrieve","type":"tuple[]"}],"name":"retrieveRewards","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"totalRewardToIssue","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"voterAddress","type":"address"},{"internalType":"uint256","name":"roundId","type":"uint256"},{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"}],"internalType":"struct VotingInterface.PendingRequest[]","name":"toRetrieve","type":"tuple[]"}],"name":"retrieveRewards","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"int256","name":"price","type":"int256"},{"internalType":"int256","name":"salt","type":"int256"}],"name":"revealVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"int256","name":"price","type":"int256"},{"internalType":"bytes","name":"ancillaryData","type":"bytes"},{"internalType":"int256","name":"salt","type":"int256"}],"name":"revealVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardsExpirationTimeout","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rounds","outputs":[{"internalType":"uint256","name":"snapshotId","type":"uint256"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"inflationRate","type":"tuple"},{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"gatPercentage","type":"tuple"},{"internalType":"uint256","name":"rewardsExpirationTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"time","type":"uint256"}],"name":"setCurrentTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"newGatPercentage","type":"tuple"}],"name":"setGatPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"newInflationRate","type":"tuple"}],"name":"setInflationRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newVotingAddress","type":"address"}],"name":"setMigrated","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"NewRewardsExpirationTimeout","type":"uint256"}],"name":"setRewardsExpirationTimeout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"snapshotCurrentRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"snapshotMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"voteTiming","outputs":[{"internalType":"uint256","name":"phaseLength","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votingToken","outputs":[{"internalType":"contract VotingToken","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60c060405260116080527014da59db88119bdc8814db985c1cda1bdd607a1b60a052620000587f05ae8dd7e03a2a2e45343dbd50fe048482c1d0e0e9e034857ff13dd19f476b7e620001a0602090811b620026ed17901c565b600c553480156200006857600080fd5b5060405162004cc838038062004cc88339810160408190526200008b91620002da565b600080546001600160a01b0319166001600160a01b038316178155620000b0620001d2565b600180546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35062000119876005620001d660201b6200271d1790919060201c565b62000134600187620001e860201b6200272e1790919060201c565b6200015c5760405162461bcd60e51b8152600401620001539062000399565b60405180910390fd5b5093516006559151600755600980546001600160a01b039384166001600160a01b031991821617909155600a8054949093169316929092179055600855506200042a565b600081604051602001620001b5919062000368565b604051602081830303815290604052805190602001209050919050565b3390565b60008111620001e457600080fd5b9055565b6000620001f58262000203565b518351111590505b92915050565b6200020d62000289565b60405180602001604052806200023a670de0b6b3a7640000856200024260201b620027461790919060201c565b905292915050565b6000826200025357506000620001fd565b828202828482816200026157fe5b0414620002825760405162461bcd60e51b81526004016200015390620003d0565b9392505050565b6040518060200160405280600081525090565b600060208284031215620002ae578081fd5b604051602081016001600160401b0381118282101715620002cd578283fd5b6040529151825250919050565b600080600080600080600060e0888a031215620002f5578283fd5b87519650620003088960208a016200029c565b9550620003198960408a016200029c565b9450606088015193506080880151620003328162000411565b60a0890151909350620003458162000411565b60c0890151909250620003588162000411565b8091505092959891949750929550565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b6020808252601e908201527f4741542070657263656e74616765206d757374206265203c3d20313030250000604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6001600160a01b03811681146200042757600080fd5b50565b61488e806200043a6000396000f3fe608060405234801561001057600080fd5b50600436106102695760003560e01c806370a0cf2c116101515780638da5cb5b116100c3578063b551cd5011610087578063b551cd5014610514578063b90fd48014610527578063c371dda71461052f578063c9280f0614610537578063d8651ad01461054a578063f2fde38b1461055d57610269565b80638da5cb5b146104be578063a03e881a146104c6578063a65d1591146104e6578063add2ccb4146104f9578063b03401231461050c57610269565b806380a1f7121161011557806380a1f7121461043a57806383c6aaca1461044f5780638558d4f3146104625780638876e8a014610475578063894fcecc146104885780638c65c81f1461049b57610269565b806370a0cf2c146103ef578063715018a614610402578063719c6d561461040a57806371b7db531461041d57806374dd278c1461042557610269565b80632960b5af116101ea578063498ec3c9116101ae578063498ec3c91461037b5780634c7a26031461039b5780635727e25d146103ae5780636852eea0146103b657806368ad8ae3146103c95780636ec1fd5d146103dc57610269565b80632960b5af1461033d57806329cb924d1461035057806331f9e35b146103585780634000851f146103605780634666cb0c1461036857610269565b80631c39c38d116102315780631c39c38d146102e557806320b373a2146102fa578063216666a41461030f57806322f8e5661461032257806326af73bf1461033557610269565b8063017e4c031461026e5780630d434e7e14610297578063110d559a146102aa57806313e56d6a146102bf57806316d32474146102d2575b600080fd5b61028161027c366004613669565b610570565b60405161028e9190614663565b60405180910390f35b6102816102a53660046136bf565b610b44565b6102bd6102b8366004613d49565b610c5e565b005b6102bd6102cd366004613db5565b610d58565b6102bd6102e0366004613c3c565b610dba565b6102ed610e24565b60405161028e9190613ec0565b610302610e33565b60405161028e9190613fbf565b6102bd61031d366004613ba4565b610e39565b6102bd610330366004613dda565b6111da565b610302611254565b6102bd61034b366004613631565b61125a565b6103026112b1565b610302611354565b6102ed61135a565b6102bd610376366004613b4d565b611369565b61038e6103893660046138dd565b6113a0565b60405161028e9190613f5b565b6102bd6103a9366004613d18565b611504565b610302611520565b6102bd6103c4366004613a17565b611532565b6102bd6103d7366004613b01565b6116a4565b6102bd6103ea36600461370b565b6116be565b6102bd6103fd36600461380e565b6117e0565b6102bd61194e565b610302610418366004613ba4565b6119cd565b610302611b7b565b61042d611b81565b60405161028e9190614005565b610442611b96565b60405161028e9190613eed565b6102bd61045d366004613db5565b611df4565b61038e61047036600461390f565b611e2f565b6102bd610483366004613dda565b611f3e565b6102bd610496366004613be7565b611f78565b6104ae6104a9366004613dda565b6120c0565b60405161028e94939291906147a8565b6102ed6120fa565b6104d96104d4366004613b01565b612109565b60405161028e9190613fb4565b6102bd6104f4366004613941565b61212e565b6102bd610507366004613cb9565b6121c2565b6102ed61243d565b6104d9610522366004613ba4565b61244c565b6103026125c5565b6103026125cb565b610302610545366004613b01565b6125d1565b6102bd610558366004613b22565b6125ed565b6102bd61056b366004613631565b612636565b610578613347565b600b546001600160a01b0316156105bc57600b546001600160a01b031633146105bc5760405162461bcd60e51b81526004016105b3906141c7565b60405180910390fd5b6105cf6105c76112b1565b600590612780565b83106105ed5760405162461bcd60e51b81526004016105b3906141fe565b6000838152600260205260408120600381015490919061060b6112b1565b119050610616613347565b604080516020810191829052600954855463277166bf60e11b909352909182916001600160a01b031690634ee2cd7e90610655908c9060248601613ed4565b60206040518083038186803b15801561066d57600080fd5b505afa158015610681573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a59190613df2565b905290506106b1613347565b6040805160208101918290526009548654630981b24d60e41b909352909182916001600160a01b03169063981b24d0906106ee9060248501613fbf565b60206040518083038186803b15801561070657600080fd5b505afa15801561071a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061073e9190613df2565b9052905061074a613347565b60408051602081019091526001860154815261076690836127a5565b905060405180602001604052806000815250955060005b8751811015610a865760006107d889838151811061079757fe5b6020026020010151600001518a84815181106107af57fe5b6020026020010151602001518b85815181106107c757fe5b6020026020010151604001516127e2565b6003810154600081815260028301602052604090209192508b1461080e5760405162461bcd60e51b81526004016105b3906145be565b610818828261280a565b6001600160a01b038c1660009081526020829052604090206001015461083f575050610a7e565b86156108d75789838151811061085157fe5b6020026020010151600001518b8d6001600160a01b03167f6fb9765a6e4b0dd2aaedad44f9b165a2a64a53ce67a6ec812075faa9220d41bc8d878151811061089557fe5b6020026020010151602001518e88815181106108ad57fe5b60200260200101516040015160006040516108ca939291906146b1565b60405180910390a4610a5f565b6001600160a01b038c16600090815260208290526040902060019081015461090191830190612950565b156109d15761090e613347565b61092d61091d83600101612987565b61092789886127a5565b906129b3565b90506109398a826129f1565b99508a848151811061094757fe5b6020026020010151600001518c8e6001600160a01b03167f6fb9765a6e4b0dd2aaedad44f9b165a2a64a53ce67a6ec812075faa9220d41bc8e888151811061098b57fe5b6020026020010151602001518f89815181106109a357fe5b60200260200101516040015186600001516040516109c3939291906146b1565b60405180910390a450610a5f565b8983815181106109dd57fe5b6020026020010151600001518b8d6001600160a01b03167f6fb9765a6e4b0dd2aaedad44f9b165a2a64a53ce67a6ec812075faa9220d41bc8d8781518110610a2157fe5b6020026020010151602001518e8881518110610a3957fe5b6020026020010151604001516000604051610a56939291906146b1565b60405180910390a45b6001600160a01b038c1660009081526020919091526040812060010155505b60010161077d565b50610a92866000612a14565b15610b385760095486516040516340c10f1960e01b81526001600160a01b03909216916340c10f1991610aca918d9190600401613ed4565b602060405180830381600087803b158015610ae457600080fd5b505af1158015610af8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b1c9190613ae1565b610b385760405162461bcd60e51b81526004016105b3906143b4565b50505050509392505050565b610b4c613347565b606082516001600160401b0381118015610b6557600080fd5b50604051908082528060200260200182016040528015610b9f57816020015b610b8c61335a565b815260200190600190039081610b845790505b50905060005b8351811015610c4957838181518110610bba57fe5b602002602001015160000151828281518110610bd257fe5b60200260200101516000018181525050838181518110610bee57fe5b602002602001015160200151828281518110610c0657fe5b6020026020010151602001818152505060405180602001604052806000815250828281518110610c3257fe5b602090810291909101015160400152600101610ba5565b50610c55858583610570565b95945050505050565b600b546001600160a01b031615610c875760405162461bcd60e51b81526004016105b390614558565b6000610c916112b1565b90506001610ca0600583612a2b565b6002811115610cab57fe5b14610cc85760405162461bcd60e51b81526004016105b3906145f5565b336001600160a01b0316610d14600c5485858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612a5092505050565b6001600160a01b031614610d3a5760405162461bcd60e51b81526004016105b39061437d565b6000610d47600583612780565b9050610d5281612b7e565b50505050565b610d60612c69565b6001546001600160a01b03908116911614610d8d5760405162461bcd60e51b81526004016105b390614311565b610d98816001612c6d565b610db45760405162461bcd60e51b81526004016105b39061447e565b51600655565b610dc685858585611f78565b6000610dd36105c76112b1565b90508581336001600160a01b03167f0296c44e55ad4a025c9701a71c746d4275d63dfe301e390a7429551010a8fea1888887604051610e1493929190614686565b60405180910390a4505050505050565b6000546001600160a01b031681565b600c5481565b600b546001600160a01b031615610e7957600b546001600160a01b03163314610e745760405162461bcd60e51b81526004016105b39061462c565b610fa2565b600a546040516302abf57960e61b81526000916001600160a01b03169063aafd5e4090610eb59067526567697374727960c01b90600401613fbf565b60206040518083038186803b158015610ecd57600080fd5b505afa158015610ee1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f05919061364d565b60405163f9f6b49b60e01b81529091506001600160a01b0382169063f9f6b49b90610f34903390600401613ec0565b60206040518083038186803b158015610f4c57600080fd5b505afa158015610f60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f849190613ae1565b610fa05760405162461bcd60e51b81526004016105b3906144ea565b505b6000610fac6112b1565b905080831115610fce5760405162461bcd60e51b81526004016105b390614447565b610fd6612c84565b6001600160a01b03166390978d1b856040518263ffffffff1660e01b81526004016110019190613fbf565b60206040518083038186803b15801561101957600080fd5b505afa15801561102d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110519190613ae1565b61106d5760405162461bcd60e51b81526004016105b390614299565b612000825111156110905760405162461bcd60e51b81526004016105b390614269565b600061109d858585612d1b565b60008181526003602052604081209192506110b9600585612780565b905060006110c78383612d51565b905060008160038111156110d757fe5b14156111d05760006110ea836001612dd8565b6040805160a0810182528b815260208082018c81528284018581526004805460608601908152608086018f815260008e81526003808852989020875181559451600186015592519684019690965594519482019490945592518051949550919361115a9260058501920190613379565b5050600480546001810182556000919091527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0186905550604051899082907f5d80f93c41e95cacea0b9ce9bb825092d709fa503a70bb26ea3f536bf16946bd906111c6908c90613fbf565b60405180910390a3505b5050505050505050565b6000546001600160a01b03166111ef57600080fd5b60005460405163117c72b360e11b81526001600160a01b03909116906322f8e5669061121f908490600401613fbf565b600060405180830381600087803b15801561123957600080fd5b505af115801561124d573d6000803e3d6000fd5b5050505050565b60085481565b611262612c69565b6001546001600160a01b0390811691161461128f5760405162461bcd60e51b81526004016105b390614311565b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b03161561134e5760008054906101000a90046001600160a01b03166001600160a01b03166329cb924d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561130f57600080fd5b505afa158015611323573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113479190613df2565b9050611351565b50425b90565b60075481565b600b546001600160a01b031681565b61138484846040518060200160405280600081525085611f78565b610d528484604051806020016040528060008152508585610dba565b60608082516001600160401b03811180156113ba57600080fd5b506040519080825280602002602001820160405280156113f457816020015b6113e16133f7565b8152602001906001900390816113d95790505b50905060006114046105c76112b1565b905060005b84518110156114f957600061145386838151811061142357fe5b60200260200101516000015187848151811061143b57fe5b6020026020010151602001518885815181106107c757fe5b905060006114618285612d51565b9050600181600381111561147157fe5b1415611499578385848151811061148457fe5b602002602001015160200181815250506114bb565b81600301548584815181106114aa57fe5b602002602001015160200181815250505b808584815181106114c857fe5b60200260200101516000019060038111156114df57fe5b908160038111156114ec57fe5b9052505050600101611409565b50909150505b919050565b610d5284848460405180602001604052806000815250856121c2565b600061152d6105c76112b1565b905090565b606081516001600160401b038111801561154b57600080fd5b5060405190808252806020026020018201604052801561158557816020015b61157261340e565b81526020019060019003908161156a5790505b50905060005b8251811015611696578281815181106115a057fe5b6020026020010151600001518282815181106115b857fe5b602002602001015160000181815250508281815181106115d457fe5b6020026020010151602001518282815181106115ec57fe5b6020026020010151602001818152505082818151811061160857fe5b60200260200101516040015182828151811061162057fe5b602002602001015160400181815250506040518060200160405280600081525082828151811061164c57fe5b60200260200101516060018190525082818151811061166757fe5b60200260200101516060015182828151811061167f57fe5b60209081029190910101516080015260010161158b565b506116a08161212e565b5050565b6116a0828260405180602001604052806000815250610e39565b60005b81518110156116a0578181815181106116d657fe5b6020026020010151608001515160001415611758576117538282815181106116fa57fe5b60200260200101516000015183838151811061171257fe5b60200260200101516020015184848151811061172a57fe5b60200260200101516040015185858151811061174257fe5b602002602001015160600151611f78565b6117d8565b6117d882828151811061176757fe5b60200260200101516000015183838151811061177f57fe5b60200260200101516020015184848151811061179757fe5b6020026020010151604001518585815181106117af57fe5b6020026020010151606001518686815181106117c757fe5b602002602001015160800151610dba565b6001016116c1565b606081516001600160401b03811180156117f957600080fd5b5060405190808252806020026020018201604052801561183357816020015b611820613440565b8152602001906001900390816118185790505b50905060005b82518110156119445782818151811061184e57fe5b60200260200101516000015182828151811061186657fe5b6020026020010151600001818152505082818151811061188257fe5b60200260200101516020015182828151811061189a57fe5b60200260200101516020018181525050604051806020016040528060008152508282815181106118c657fe5b6020026020010151604001819052508281815181106118e157fe5b6020026020010151604001518282815181106118f957fe5b6020026020010151606001818152505082818151811061191557fe5b60200260200101516060015182828151811061192d57fe5b602090810291909101015160800152600101611839565b506116a0816116be565b611956612c69565b6001546001600160a01b039081169116146119835760405162461bcd60e51b81526004016105b390614311565b6001546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b600b546000906001600160a01b031615611a1057600b546001600160a01b03163314611a0b5760405162461bcd60e51b81526004016105b39061462c565b611b39565b600a546040516302abf57960e61b81526000916001600160a01b03169063aafd5e4090611a4c9067526567697374727960c01b90600401613fbf565b60206040518083038186803b158015611a6457600080fd5b505afa158015611a78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a9c919061364d565b60405163f9f6b49b60e01b81529091506001600160a01b0382169063f9f6b49b90611acb903390600401613ec0565b60206040518083038186803b158015611ae357600080fd5b505afa158015611af7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b1b9190613ae1565b611b375760405162461bcd60e51b81526004016105b3906144ea565b505b6000806060611b49878787612dfd565b925092509250828190611b6f5760405162461bcd60e51b81526004016105b39190614019565b50909695505050505050565b60055481565b600061152d611b8e6112b1565b600590612a2b565b60606000611ba26112b1565b90506000611bb1600583612780565b6004549091506060906001600160401b0381118015611bcf57600080fd5b50604051908082528060200260200182016040528015611c0957816020015b611bf661335a565b815260200190600190039081611bee5790505b5090506000805b600454811015611d565760006003600060048481548110611c2d57fe5b90600052602060002001548152602001908152602001600020905060016003811115611c5557fe5b611c5f8287612d51565b6003811115611c6a57fe5b1415611d4d5760405180606001604052808260000154815260200182600101548152602001826005018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611d275780601f10611cfc57610100808354040283529160200191611d27565b820191906000526020600020905b815481529060010190602001808311611d0a57829003601f168201915b5050505050815250848481518110611d3b57fe5b60209081029190910101526001909201915b50600101611c10565b506060816001600160401b0381118015611d6f57600080fd5b50604051908082528060200260200182016040528015611da957816020015b611d9661335a565b815260200190600190039081611d8e5790505b50905060005b82811015611dea57838181518110611dc357fe5b6020026020010151828281518110611dd757fe5b6020908102919091010152600101611daf565b5094505050505090565b611dfc612c69565b6001546001600160a01b03908116911614611e295760405162461bcd60e51b81526004016105b390614311565b51600755565b60608082516001600160401b0381118015611e4957600080fd5b50604051908082528060200260200182016040528015611e8357816020015b611e7061335a565b815260200190600190039081611e685790505b50905060005b8351811015611f2d57838181518110611e9e57fe5b602002602001015160000151828281518110611eb657fe5b60200260200101516000018181525050838181518110611ed257fe5b602002602001015160200151828281518110611eea57fe5b6020026020010151602001818152505060405180602001604052806000815250828281518110611f1657fe5b602090810291909101015160400152600101611e89565b50611f37816113a0565b9392505050565b611f46612c69565b6001546001600160a01b03908116911614611f735760405162461bcd60e51b81526004016105b390614311565b600855565b600b546001600160a01b031615611fa15760405162461bcd60e51b81526004016105b390614558565b80611fbe5760405162461bcd60e51b81526004016105b3906143eb565b6000611fc86112b1565b90506000611fd7600583612a2b565b6002811115611fe257fe5b14611fff5760405162461bcd60e51b81526004016105b390614521565b600061200c600583612780565b9050600061201b8787876127e2565b905060016120298284612d51565b600381111561203457fe5b146120515760405162461bcd60e51b81526004016105b390614346565b600381018290556000828152600282016020908152604080832033808552928190529281902087905551899185917f6beca723245953d9ed92ae4d320d4772838e841161bfff12c78ae4268df525eb906120ae908c908c9061466d565b60405180910390a45050505050505050565b6002602081815260009283526040928390208054845180840186526001830154815285519384019095529281015482526003015491929184565b6001546001600160a01b031690565b600061212583836040518060200160405280600081525061244c565b90505b92915050565b60005b81518110156116a0576121ba82828151811061214957fe5b60200260200101516000015183838151811061216157fe5b60200260200101516020015184848151811061217957fe5b60200260200101516040015185858151811061219157fe5b6020026020010151606001518686815181106121a957fe5b6020026020010151608001516121c2565b600101612131565b600b546001600160a01b0316156121eb5760405162461bcd60e51b81526004016105b390614558565b60016121f8611b8e6112b1565b600281111561220357fe5b146122205760405162461bcd60e51b81526004016105b39061414e565b600061222d6105c76112b1565b9050600061223c8787866127e2565b60008381526002820160209081526040808320338452918290529091208054929350909161227c5760405162461bcd60e51b81526004016105b39061441a565b80600001548786338b8a898f60405160200161229e9796959493929190613e36565b60405160208183030381529060405280519060200120146122d15760405162461bcd60e51b81526004016105b39061409a565b6000848152600260205260409020546122fc5760405162461bcd60e51b81526004016105b39061458f565b600084815260026020526040812054908255612316613347565b60408051602081019182905260095463277166bf60e11b9092529081906001600160a01b0316634ee2cd7e61234f338760248601613ed4565b60206040518083038186803b15801561236757600080fd5b505afa15801561237b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061239f9190613df2565b8152509050886040516020016123b59190613fbf565b60408051601f1981840301815291905280516020909101206001808501919091556123e39085018a83612f8b565b8a86336001600160a01b03167f3fad5d37ee1be2f58ff1735699121a8ead73c3b70d02fc06d07b0db29854d3b48d8d8d876000015160405161242894939291906146da565b60405180910390a45050505050505050505050565b6009546001600160a01b031681565b600b546000906001600160a01b03161561248f57600b546001600160a01b0316331461248a5760405162461bcd60e51b81526004016105b39061462c565b6125b8565b600a546040516302abf57960e61b81526000916001600160a01b03169063aafd5e40906124cb9067526567697374727960c01b90600401613fbf565b60206040518083038186803b1580156124e357600080fd5b505afa1580156124f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061251b919061364d565b60405163f9f6b49b60e01b81529091506001600160a01b0382169063f9f6b49b9061254a903390600401613ec0565b60206040518083038186803b15801561256257600080fd5b505afa158015612576573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061259a9190613ae1565b6125b65760405162461bcd60e51b81526004016105b3906144ea565b505b6000611b6f858585612dfd565b60065481565b61200081565b60006121258383604051806020016040528060008152506119cd565b600b546001600160a01b0316156126165760405162461bcd60e51b81526004016105b390614558565b61263183836040518060200160405280600081525084611f78565b505050565b61263e612c69565b6001546001600160a01b0390811691161461266b5760405162461bcd60e51b81526004016105b390614311565b6001600160a01b0381166126915760405162461bcd60e51b81526004016105b3906140d1565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000816040516020016127009190613e8f565b604051602081830303815290604052805190602001209050919050565b6000811161272a57600080fd5b9055565b600061273982613040565b5183511115905092915050565b60008261275557506000612128565b8282028284828161276257fe5b04146121255760405162461bcd60e51b81526004016105b3906142d0565b60008061279160025b855490612746565b905061279d838261306e565b949350505050565b6127ad613347565b6040805160208101909152825184518291670de0b6b3a7640000916127d191612746565b816127d857fe5b0490529392505050565b6000600360006127f3868686612d1b565b815260200190815260200160002090509392505050565b6004820154600019141561281d576116a0565b60008061283a61283085600301546130b0565b60018501906131a2565b915091508161285b5760405162461bcd60e51b81526004016105b3906144b5565b6004805460001981019160009160039183918590811061287757fe5b90600052602060002001548152602001908152602001600020905085600401548160040181905550600482815481106128ac57fe5b906000526020600020015460048760040154815481106128c857fe5b60009182526020909120015560048054806128df57fe5b60008281526020812082016000199081019190915590810190915560048701558554600387015460018801546040517fb1f1bf5aec084730c2c09f66ae2099185eaf6f951ddc113a19aa886a9f5e71b79161294091889060058d019061470a565b60405180910390a3505050505050565b600082600201546040516020016129679190613fbf565b604051602081830303815290604052805190602001208214905092915050565b61298f613347565b50600281015460009081526020918252604090819020815192830190915254815290565b6129bb613347565b60408051602081019091528251845182916129e8916129e290670de0b6b3a7640000612746565b9061306e565b90529392505050565b6129f9613347565b60408051602081019091528251845182916129e89190612dd8565b6000612a1f82613040565b51835111905092915050565b6000612a4560028454612a3f90859061306e565b9061324e565b600281111561212557fe5b60008151604114612a735760405162461bcd60e51b81526004016105b390614063565b60208201516040830151606084015160001a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115612ac55760405162461bcd60e51b81526004016105b390614185565b8060ff16601b14158015612add57508060ff16601c14155b15612afa5760405162461bcd60e51b81526004016105b390614227565b600060018783868660405160008152602001604052604051612b1f9493929190613fe7565b6020604051602081039080840390855afa158015612b41573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116612b745760405162461bcd60e51b81526004016105b39061402c565b9695505050505050565b600081815260026020526040902080546116a057600960009054906101000a90046001600160a01b03166001600160a01b0316639711715a6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612be257600080fd5b505af1158015612bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c1a9190613df2565b815560008281526002602081905260409091206007546001820155600654910155600854612c5390612c4d600585613290565b90612dd8565b6000838152600260205260409020600301555050565b3390565b6000612c7882613040565b51835110905092915050565b600a546040516302abf57960e61b81526000916001600160a01b03169063aafd5e4090612ccb90721259195b9d1a599a595c95da1a5d195b1a5cdd606a1b90600401613fbf565b60206040518083038186803b158015612ce357600080fd5b505afa158015612cf7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061152d919061364d565b6000838383604051602001612d3293929190613fc8565b6040516020818303038152906040528051906020012090509392505050565b6000826003015460001415612d6857506000612128565b8183600301541015612dbc5760038301546000818152600285016020526040812091612da190612d97906130b0565b60018401906131a2565b50905080612db0576001612db3565b60025b92505050612128565b8183600301541415612dd057506001612128565b506003612128565b6000828201838110156121255760405162461bcd60e51b81526004016105b390614117565b60008060606000612e0f8787876127e2565b90506000612e1e6105c76112b1565b90506000612e2c8383612d51565b90506001816003811115612e3c57fe5b1415612e89576000806040518060400160405280601e81526020017f43757272656e7420766f74696e6720726f756e64206e6f7420656e6465640000815250955095509550505050612f82565b6002816003811115612e9757fe5b1415612ee45760038301546000818152600285016020526040812091612ec090612d97906130b0565b60408051602081019091526000815260019a509098509650612f8295505050505050565b6003816003811115612ef257fe5b1415612f3f576000806040518060400160405280601d81526020017f5072696365206973207374696c6c20746f20626520766f746564206f6e000000815250955095509550505050612f82565b6000806040518060400160405280601981526020017f507269636520776173206e6576657220726571756573746564000000000000008152509550955095505050505b93509350939050565b604080516020810190915260018401548152612fa790826129f1565b51600184015560008281526020848152604091829020825191820190925290548152612fd390826129f1565b60008381526020859052604090209051905560028301548214801590613031575060028301546000908152602084815260408083208151808401835290548152858452868352928190208151928301909152548152613031916132b4565b15612631575060029190910155565b613048613347565b60408051602081019091528061306684670de0b6b3a7640000612746565b905292915050565b600061212583836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506132bb565b6130b8613347565b600082815260026020526040902054806130e3575050604080516020810190915260001981526114ff565b6130eb613347565b604080516020810191829052600954630981b24d60e41b9092529081906001600160a01b031663981b24d06131238660248501613fbf565b60206040518083038186803b15801561313b57600080fd5b505afa15801561314f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131739190613df2565b90526000858152600260208181526040928390208351918201909352910154815290915061279d9082906127a5565b6000806131ad613347565b6131c260646131bc6032613040565b906132f2565b6040805160208101909152600187015481529091506131e190856132b4565b801561322c57506040805160208082018352600188015482526002880154600090815288825283902083519182019093529154825261322c91839161322691906129b3565b906132b4565b15613241576001925084600201549150613246565b600092505b509250929050565b600061212583836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f0000000000000000815250613313565b60008061329d6002612789565b905061279d6132ad846001612dd8565b8290612746565b5190511190565b600081836132dc5760405162461bcd60e51b81526004016105b39190614019565b5060008385816132e857fe5b0495945050505050565b6132fa613347565b6040805160208101909152835181906129e8908561306e565b600081836133345760405162461bcd60e51b81526004016105b39190614019565b5082848161333e57fe5b06949350505050565b6040518060200160405280600081525090565b6040805160608082018352600080835260208301529181019190915290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106133ba57805160ff19168380011785556133e7565b828001600101855582156133e7579182015b828111156133e75782518255916020019190600101906133cc565b506133f392915061346d565b5090565b604080518082019091526000808252602082015290565b6040518060a0016040528060008019168152602001600081526020016000815260200160608152602001600081525090565b6040805160a081018252600080825260208201819052606092820183905282820152608081019190915290565b5b808211156133f3576000815560010161346e565b600082601f830112613492578081fd5b81356134a56134a0826147e9565b6147c3565b818152915060208083019084810160005b8481101561352f5781358701606080601f19838c030112156134d757600080fd5b6134e0816147c3565b8286013581526040808401358288015291830135916001600160401b0383111561350957600080fd5b6135178c88858701016135c9565b908201528652505092820192908201906001016134b6565b505050505092915050565b600082601f83011261354a578081fd5b81356135586134a0826147e9565b818152915060208083019084810160408085028701830188101561357b57600080fd5b6000805b868110156135bc5782848b031215613595578182fd5b61359e836147c3565b8435815285850135868201528652948401949282019260010161357f565b5050505050505092915050565b600082601f8301126135d9578081fd5b81356001600160401b038111156135ee578182fd5b613601601f8201601f19166020016147c3565b915080825283602082850101111561361857600080fd5b8060208401602084013760009082016020015292915050565b600060208284031215613642578081fd5b813561212581614840565b60006020828403121561365e578081fd5b815161212581614840565b60008060006060848603121561367d578182fd5b833561368881614840565b92506020840135915060408401356001600160401b038111156136a9578182fd5b6136b586828701613482565b9150509250925092565b6000806000606084860312156136d3578081fd5b83356136de81614840565b92506020840135915060408401356001600160401b038111156136ff578182fd5b6136b58682870161353a565b6000602080838503121561371d578182fd5b82356001600160401b0380821115613733578384fd5b818501915085601f830112613746578384fd5b81356137546134a0826147e9565b81815284810190848601875b848110156137ff578135870160a080601f19838f03011215613780578a8bfd5b613789816147c3565b8a830135815260408301358b8201526060830135898111156137a9578c8dfd5b6137b78f8d838701016135c9565b604083015250608083013560608201529082013590888211156137d8578b8cfd5b6137e68e8c848601016135c9565b6080820152865250509287019290870190600101613760565b50909998505050505050505050565b60006020808385031215613820578182fd5b82356001600160401b0380821115613836578384fd5b818501915085601f830112613849578384fd5b81356138576134a0826147e9565b81815284810190848601875b848110156137ff5781358701608080601f19838f03011215613883578a8bfd5b61388c816147c3565b828b01358152604080840135828d01526060840135908201529082013590888211156138b6578b8cfd5b6138c48e8c848601016135c9565b6060820152865250509287019290870190600101613863565b6000602082840312156138ee578081fd5b81356001600160401b03811115613903578182fd5b61279d84828501613482565b600060208284031215613920578081fd5b81356001600160401b03811115613935578182fd5b61279d8482850161353a565b60006020808385031215613953578182fd5b82356001600160401b0380821115613969578384fd5b818501915085601f83011261397c578384fd5b813561398a6134a0826147e9565b81815284810190848601875b848110156137ff578135870160a080601f19838f030112156139b6578a8bfd5b6139bf816147c3565b828b01358152604080840135828d01526060840135908201526080830135898111156139e9578c8dfd5b6139f78f8d838701016135c9565b606083015250910135608082015284529287019290870190600101613996565b60006020808385031215613a29578182fd5b82356001600160401b03811115613a3e578283fd5b8301601f81018513613a4e578283fd5b8035613a5c6134a0826147e9565b818152838101908385016080808502860187018a1015613a7a578788fd5b8795505b84861015613ad35780828b031215613a94578788fd5b613a9d816147c3565b82358152878301358882015260408084013590820152606080840135908201528452600195909501949286019290810190613a7e565b509098975050505050505050565b600060208284031215613af2578081fd5b81518015158114612125578182fd5b60008060408385031215613b13578182fd5b50508035926020909101359150565b600080600060608486031215613b36578081fd5b505081359360208301359350604090920135919050565b60008060008060808587031215613b62578182fd5b84359350602085013592506040850135915060608501356001600160401b03811115613b8c578182fd5b613b98878288016135c9565b91505092959194509250565b600080600060608486031215613bb8578081fd5b833592506020840135915060408401356001600160401b03811115613bdb578182fd5b6136b5868287016135c9565b60008060008060808587031215613bfc578182fd5b843593506020850135925060408501356001600160401b03811115613c1f578283fd5b613c2b878288016135c9565b949793965093946060013593505050565b600080600080600060a08688031215613c53578283fd5b853594506020860135935060408601356001600160401b0380821115613c77578485fd5b613c8389838a016135c9565b9450606088013593506080880135915080821115613c9f578283fd5b50613cac888289016135c9565b9150509295509295909350565b600080600080600060a08688031215613cd0578283fd5b85359450602086013593506040860135925060608601356001600160401b03811115613cfa578182fd5b613d06888289016135c9565b95989497509295608001359392505050565b60008060008060808587031215613d2d578182fd5b5050823594602084013594506040840135936060013592509050565b60008060208385031215613d5b578182fd5b82356001600160401b0380821115613d71578384fd5b818501915085601f830112613d84578384fd5b813581811115613d92578485fd5b866020828501011115613da3578485fd5b60209290920196919550909350505050565b600060208284031215613dc6578081fd5b613dd060206147c3565b9135825250919050565b600060208284031215613deb578081fd5b5035919050565b600060208284031215613e03578081fd5b5051919050565b60008151808452613e22816020860160208601614814565b601f01601f19169290920160200192915050565b60008882528760208301526bffffffffffffffffffffffff198760601b1660408301528560548301528451613e72816074850160208901614814565b909101607481019390935250609482015260b40195945050505050565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b60208082528251828201819052600091906040908185019080840286018301878501865b83811015613ad357888303603f1901855281518051845287810151888501528601516060878501819052613f4781860183613e0a565b968901969450505090860190600101613f11565b602080825282518282018190526000919060409081850190868401855b82811015613fa7578151805160048110613f8e57fe5b8552860151868501529284019290850190600101613f78565b5091979650505050505050565b901515815260200190565b90815260200190565b600084825283602083015260606040830152610c556060830184613e0a565b93845260ff9290921660208401526040830152606082015260800190565b602081016003831061401357fe5b91905290565b6000602082526121256020830184613e0a565b60208082526018908201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604082015260600190565b6020808252601f908201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604082015260600190565b6020808252601c908201527f52657665616c6564206461746120213d20636f6d6d6974206861736800000000604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252601d908201527f43616e6e6f742072657665616c20696e20636f6d6d6974207068617365000000604082015260600190565b60208082526022908201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604082015261756560f01b606082015260800190565b6020808252601b908201527f43616e206f6e6c792063616c6c2066726f6d206d696772617465640000000000604082015260600190565b6020808252600f908201526e125b9d985b1a59081c9bdd5b991259608a1b604082015260600190565b60208082526022908201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604082015261756560f01b606082015260800190565b602080825260169082015275496e76616c696420616e63696c6c617279206461746160501b604082015260600190565b6020808252601e908201527f556e737570706f72746564206964656e74696669657220726571756573740000604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252601e908201527f43616e6e6f7420636f6d6d697420696e61637469766520726571756573740000604082015260600190565b6020808252601b908201527f5369676e6174757265206d757374206d617463682073656e6465720000000000604082015260600190565b6020808252601c908201527f566f74696e6720746f6b656e2069737375616e6365206661696c656400000000604082015260600190565b602080825260159082015274092dcecc2d8d2c840e0e4deecd2c8cac840d0c2e6d605b1b604082015260600190565b602080825260139082015272125b9d985b1a59081a185cda081c995d99585b606a1b604082015260600190565b60208082526018908201527f43616e206f6e6c79207265717565737420696e20706173740000000000000000604082015260600190565b6020808252601d908201527f4741542070657263656e74616765206d757374206265203c2031303025000000604082015260600190565b6020808252818101527f43616e2774207265736f6c766520756e7265736f6c7665642072657175657374604082015260600190565b60208082526019908201527f43616c6c6564206d757374206265207265676973746572656400000000000000604082015260600190565b6020808252601d908201527f43616e6e6f7420636f6d6d697420696e2072657665616c207068617365000000604082015260600190565b6020808252601e908201527f4f6e6c792063616c6c2074686973206966206e6f74206d696772617465640000604082015260600190565b602080825260159082015274149bdd5b99081a185cc81b9bc81cdb985c1cda1bdd605a1b604082015260600190565b6020808252601d908201527f526574726965766520666f7220766f7465732073616d6520726f756e64000000604082015260600190565b6020808252601d908201527f4f6e6c7920736e617073686f7420696e2072657665616c207068617365000000604082015260600190565b6020808252601f908201527f43616c6c6572206d757374206265206d69677261746564206164647265737300604082015260600190565b9051815260200190565b60008382526040602083015261279d6040830184613e0a565b60008482526060602083015261469f6060830185613e0a565b8281036040840152612b748185613e0a565b6000848252606060208301526146ca6060830185613e0a565b9050826040830152949350505050565b6000858252846020830152608060408301526146f96080830185613e0a565b905082606083015295945050505050565b600060608201858352602085818501526060604085015282855460018082166000811461473e576001811461475e57614799565b61474e607f600285041687613fbf565b60ff198416815285019350614799565b6002830461476c8188613fbf565b6147758b614808565b895b8381101561479057815483820152908501908801614777565b91909101955050505b50919998505050505050505050565b93845291516020840152516040830152606082015260800190565b6040518181016001600160401b03811182821017156147e157600080fd5b604052919050565b60006001600160401b038211156147fe578081fd5b5060209081020190565b60009081526020902090565b60005b8381101561482f578181015183820152602001614817565b83811115610d525750506000910152565b6001600160a01b038116811461485557600080fd5b5056fea264697066735822122022f7fc478ff7feaaea6add2866b0175f606be8ef9f88a726e829e809654fef6b64736f6c634300060c0033000000000000000000000000000000000000000000000000000000000001518000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000001c6bf526340000000000000000000000000000000000000000000000000000000000757b12c0000000000000000000000000004fa0d235c4abf4bcf4787af4cf447de572ef82800000000000000000000000040f941e48a552bf496b154af6bf55725f18d77c30000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102695760003560e01c806370a0cf2c116101515780638da5cb5b116100c3578063b551cd5011610087578063b551cd5014610514578063b90fd48014610527578063c371dda71461052f578063c9280f0614610537578063d8651ad01461054a578063f2fde38b1461055d57610269565b80638da5cb5b146104be578063a03e881a146104c6578063a65d1591146104e6578063add2ccb4146104f9578063b03401231461050c57610269565b806380a1f7121161011557806380a1f7121461043a57806383c6aaca1461044f5780638558d4f3146104625780638876e8a014610475578063894fcecc146104885780638c65c81f1461049b57610269565b806370a0cf2c146103ef578063715018a614610402578063719c6d561461040a57806371b7db531461041d57806374dd278c1461042557610269565b80632960b5af116101ea578063498ec3c9116101ae578063498ec3c91461037b5780634c7a26031461039b5780635727e25d146103ae5780636852eea0146103b657806368ad8ae3146103c95780636ec1fd5d146103dc57610269565b80632960b5af1461033d57806329cb924d1461035057806331f9e35b146103585780634000851f146103605780634666cb0c1461036857610269565b80631c39c38d116102315780631c39c38d146102e557806320b373a2146102fa578063216666a41461030f57806322f8e5661461032257806326af73bf1461033557610269565b8063017e4c031461026e5780630d434e7e14610297578063110d559a146102aa57806313e56d6a146102bf57806316d32474146102d2575b600080fd5b61028161027c366004613669565b610570565b60405161028e9190614663565b60405180910390f35b6102816102a53660046136bf565b610b44565b6102bd6102b8366004613d49565b610c5e565b005b6102bd6102cd366004613db5565b610d58565b6102bd6102e0366004613c3c565b610dba565b6102ed610e24565b60405161028e9190613ec0565b610302610e33565b60405161028e9190613fbf565b6102bd61031d366004613ba4565b610e39565b6102bd610330366004613dda565b6111da565b610302611254565b6102bd61034b366004613631565b61125a565b6103026112b1565b610302611354565b6102ed61135a565b6102bd610376366004613b4d565b611369565b61038e6103893660046138dd565b6113a0565b60405161028e9190613f5b565b6102bd6103a9366004613d18565b611504565b610302611520565b6102bd6103c4366004613a17565b611532565b6102bd6103d7366004613b01565b6116a4565b6102bd6103ea36600461370b565b6116be565b6102bd6103fd36600461380e565b6117e0565b6102bd61194e565b610302610418366004613ba4565b6119cd565b610302611b7b565b61042d611b81565b60405161028e9190614005565b610442611b96565b60405161028e9190613eed565b6102bd61045d366004613db5565b611df4565b61038e61047036600461390f565b611e2f565b6102bd610483366004613dda565b611f3e565b6102bd610496366004613be7565b611f78565b6104ae6104a9366004613dda565b6120c0565b60405161028e94939291906147a8565b6102ed6120fa565b6104d96104d4366004613b01565b612109565b60405161028e9190613fb4565b6102bd6104f4366004613941565b61212e565b6102bd610507366004613cb9565b6121c2565b6102ed61243d565b6104d9610522366004613ba4565b61244c565b6103026125c5565b6103026125cb565b610302610545366004613b01565b6125d1565b6102bd610558366004613b22565b6125ed565b6102bd61056b366004613631565b612636565b610578613347565b600b546001600160a01b0316156105bc57600b546001600160a01b031633146105bc5760405162461bcd60e51b81526004016105b3906141c7565b60405180910390fd5b6105cf6105c76112b1565b600590612780565b83106105ed5760405162461bcd60e51b81526004016105b3906141fe565b6000838152600260205260408120600381015490919061060b6112b1565b119050610616613347565b604080516020810191829052600954855463277166bf60e11b909352909182916001600160a01b031690634ee2cd7e90610655908c9060248601613ed4565b60206040518083038186803b15801561066d57600080fd5b505afa158015610681573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a59190613df2565b905290506106b1613347565b6040805160208101918290526009548654630981b24d60e41b909352909182916001600160a01b03169063981b24d0906106ee9060248501613fbf565b60206040518083038186803b15801561070657600080fd5b505afa15801561071a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061073e9190613df2565b9052905061074a613347565b60408051602081019091526001860154815261076690836127a5565b905060405180602001604052806000815250955060005b8751811015610a865760006107d889838151811061079757fe5b6020026020010151600001518a84815181106107af57fe5b6020026020010151602001518b85815181106107c757fe5b6020026020010151604001516127e2565b6003810154600081815260028301602052604090209192508b1461080e5760405162461bcd60e51b81526004016105b3906145be565b610818828261280a565b6001600160a01b038c1660009081526020829052604090206001015461083f575050610a7e565b86156108d75789838151811061085157fe5b6020026020010151600001518b8d6001600160a01b03167f6fb9765a6e4b0dd2aaedad44f9b165a2a64a53ce67a6ec812075faa9220d41bc8d878151811061089557fe5b6020026020010151602001518e88815181106108ad57fe5b60200260200101516040015160006040516108ca939291906146b1565b60405180910390a4610a5f565b6001600160a01b038c16600090815260208290526040902060019081015461090191830190612950565b156109d15761090e613347565b61092d61091d83600101612987565b61092789886127a5565b906129b3565b90506109398a826129f1565b99508a848151811061094757fe5b6020026020010151600001518c8e6001600160a01b03167f6fb9765a6e4b0dd2aaedad44f9b165a2a64a53ce67a6ec812075faa9220d41bc8e888151811061098b57fe5b6020026020010151602001518f89815181106109a357fe5b60200260200101516040015186600001516040516109c3939291906146b1565b60405180910390a450610a5f565b8983815181106109dd57fe5b6020026020010151600001518b8d6001600160a01b03167f6fb9765a6e4b0dd2aaedad44f9b165a2a64a53ce67a6ec812075faa9220d41bc8d8781518110610a2157fe5b6020026020010151602001518e8881518110610a3957fe5b6020026020010151604001516000604051610a56939291906146b1565b60405180910390a45b6001600160a01b038c1660009081526020919091526040812060010155505b60010161077d565b50610a92866000612a14565b15610b385760095486516040516340c10f1960e01b81526001600160a01b03909216916340c10f1991610aca918d9190600401613ed4565b602060405180830381600087803b158015610ae457600080fd5b505af1158015610af8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b1c9190613ae1565b610b385760405162461bcd60e51b81526004016105b3906143b4565b50505050509392505050565b610b4c613347565b606082516001600160401b0381118015610b6557600080fd5b50604051908082528060200260200182016040528015610b9f57816020015b610b8c61335a565b815260200190600190039081610b845790505b50905060005b8351811015610c4957838181518110610bba57fe5b602002602001015160000151828281518110610bd257fe5b60200260200101516000018181525050838181518110610bee57fe5b602002602001015160200151828281518110610c0657fe5b6020026020010151602001818152505060405180602001604052806000815250828281518110610c3257fe5b602090810291909101015160400152600101610ba5565b50610c55858583610570565b95945050505050565b600b546001600160a01b031615610c875760405162461bcd60e51b81526004016105b390614558565b6000610c916112b1565b90506001610ca0600583612a2b565b6002811115610cab57fe5b14610cc85760405162461bcd60e51b81526004016105b3906145f5565b336001600160a01b0316610d14600c5485858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612a5092505050565b6001600160a01b031614610d3a5760405162461bcd60e51b81526004016105b39061437d565b6000610d47600583612780565b9050610d5281612b7e565b50505050565b610d60612c69565b6001546001600160a01b03908116911614610d8d5760405162461bcd60e51b81526004016105b390614311565b610d98816001612c6d565b610db45760405162461bcd60e51b81526004016105b39061447e565b51600655565b610dc685858585611f78565b6000610dd36105c76112b1565b90508581336001600160a01b03167f0296c44e55ad4a025c9701a71c746d4275d63dfe301e390a7429551010a8fea1888887604051610e1493929190614686565b60405180910390a4505050505050565b6000546001600160a01b031681565b600c5481565b600b546001600160a01b031615610e7957600b546001600160a01b03163314610e745760405162461bcd60e51b81526004016105b39061462c565b610fa2565b600a546040516302abf57960e61b81526000916001600160a01b03169063aafd5e4090610eb59067526567697374727960c01b90600401613fbf565b60206040518083038186803b158015610ecd57600080fd5b505afa158015610ee1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f05919061364d565b60405163f9f6b49b60e01b81529091506001600160a01b0382169063f9f6b49b90610f34903390600401613ec0565b60206040518083038186803b158015610f4c57600080fd5b505afa158015610f60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f849190613ae1565b610fa05760405162461bcd60e51b81526004016105b3906144ea565b505b6000610fac6112b1565b905080831115610fce5760405162461bcd60e51b81526004016105b390614447565b610fd6612c84565b6001600160a01b03166390978d1b856040518263ffffffff1660e01b81526004016110019190613fbf565b60206040518083038186803b15801561101957600080fd5b505afa15801561102d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110519190613ae1565b61106d5760405162461bcd60e51b81526004016105b390614299565b612000825111156110905760405162461bcd60e51b81526004016105b390614269565b600061109d858585612d1b565b60008181526003602052604081209192506110b9600585612780565b905060006110c78383612d51565b905060008160038111156110d757fe5b14156111d05760006110ea836001612dd8565b6040805160a0810182528b815260208082018c81528284018581526004805460608601908152608086018f815260008e81526003808852989020875181559451600186015592519684019690965594519482019490945592518051949550919361115a9260058501920190613379565b5050600480546001810182556000919091527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0186905550604051899082907f5d80f93c41e95cacea0b9ce9bb825092d709fa503a70bb26ea3f536bf16946bd906111c6908c90613fbf565b60405180910390a3505b5050505050505050565b6000546001600160a01b03166111ef57600080fd5b60005460405163117c72b360e11b81526001600160a01b03909116906322f8e5669061121f908490600401613fbf565b600060405180830381600087803b15801561123957600080fd5b505af115801561124d573d6000803e3d6000fd5b5050505050565b60085481565b611262612c69565b6001546001600160a01b0390811691161461128f5760405162461bcd60e51b81526004016105b390614311565b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b03161561134e5760008054906101000a90046001600160a01b03166001600160a01b03166329cb924d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561130f57600080fd5b505afa158015611323573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113479190613df2565b9050611351565b50425b90565b60075481565b600b546001600160a01b031681565b61138484846040518060200160405280600081525085611f78565b610d528484604051806020016040528060008152508585610dba565b60608082516001600160401b03811180156113ba57600080fd5b506040519080825280602002602001820160405280156113f457816020015b6113e16133f7565b8152602001906001900390816113d95790505b50905060006114046105c76112b1565b905060005b84518110156114f957600061145386838151811061142357fe5b60200260200101516000015187848151811061143b57fe5b6020026020010151602001518885815181106107c757fe5b905060006114618285612d51565b9050600181600381111561147157fe5b1415611499578385848151811061148457fe5b602002602001015160200181815250506114bb565b81600301548584815181106114aa57fe5b602002602001015160200181815250505b808584815181106114c857fe5b60200260200101516000019060038111156114df57fe5b908160038111156114ec57fe5b9052505050600101611409565b50909150505b919050565b610d5284848460405180602001604052806000815250856121c2565b600061152d6105c76112b1565b905090565b606081516001600160401b038111801561154b57600080fd5b5060405190808252806020026020018201604052801561158557816020015b61157261340e565b81526020019060019003908161156a5790505b50905060005b8251811015611696578281815181106115a057fe5b6020026020010151600001518282815181106115b857fe5b602002602001015160000181815250508281815181106115d457fe5b6020026020010151602001518282815181106115ec57fe5b6020026020010151602001818152505082818151811061160857fe5b60200260200101516040015182828151811061162057fe5b602002602001015160400181815250506040518060200160405280600081525082828151811061164c57fe5b60200260200101516060018190525082818151811061166757fe5b60200260200101516060015182828151811061167f57fe5b60209081029190910101516080015260010161158b565b506116a08161212e565b5050565b6116a0828260405180602001604052806000815250610e39565b60005b81518110156116a0578181815181106116d657fe5b6020026020010151608001515160001415611758576117538282815181106116fa57fe5b60200260200101516000015183838151811061171257fe5b60200260200101516020015184848151811061172a57fe5b60200260200101516040015185858151811061174257fe5b602002602001015160600151611f78565b6117d8565b6117d882828151811061176757fe5b60200260200101516000015183838151811061177f57fe5b60200260200101516020015184848151811061179757fe5b6020026020010151604001518585815181106117af57fe5b6020026020010151606001518686815181106117c757fe5b602002602001015160800151610dba565b6001016116c1565b606081516001600160401b03811180156117f957600080fd5b5060405190808252806020026020018201604052801561183357816020015b611820613440565b8152602001906001900390816118185790505b50905060005b82518110156119445782818151811061184e57fe5b60200260200101516000015182828151811061186657fe5b6020026020010151600001818152505082818151811061188257fe5b60200260200101516020015182828151811061189a57fe5b60200260200101516020018181525050604051806020016040528060008152508282815181106118c657fe5b6020026020010151604001819052508281815181106118e157fe5b6020026020010151604001518282815181106118f957fe5b6020026020010151606001818152505082818151811061191557fe5b60200260200101516060015182828151811061192d57fe5b602090810291909101015160800152600101611839565b506116a0816116be565b611956612c69565b6001546001600160a01b039081169116146119835760405162461bcd60e51b81526004016105b390614311565b6001546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b600b546000906001600160a01b031615611a1057600b546001600160a01b03163314611a0b5760405162461bcd60e51b81526004016105b39061462c565b611b39565b600a546040516302abf57960e61b81526000916001600160a01b03169063aafd5e4090611a4c9067526567697374727960c01b90600401613fbf565b60206040518083038186803b158015611a6457600080fd5b505afa158015611a78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a9c919061364d565b60405163f9f6b49b60e01b81529091506001600160a01b0382169063f9f6b49b90611acb903390600401613ec0565b60206040518083038186803b158015611ae357600080fd5b505afa158015611af7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b1b9190613ae1565b611b375760405162461bcd60e51b81526004016105b3906144ea565b505b6000806060611b49878787612dfd565b925092509250828190611b6f5760405162461bcd60e51b81526004016105b39190614019565b50909695505050505050565b60055481565b600061152d611b8e6112b1565b600590612a2b565b60606000611ba26112b1565b90506000611bb1600583612780565b6004549091506060906001600160401b0381118015611bcf57600080fd5b50604051908082528060200260200182016040528015611c0957816020015b611bf661335a565b815260200190600190039081611bee5790505b5090506000805b600454811015611d565760006003600060048481548110611c2d57fe5b90600052602060002001548152602001908152602001600020905060016003811115611c5557fe5b611c5f8287612d51565b6003811115611c6a57fe5b1415611d4d5760405180606001604052808260000154815260200182600101548152602001826005018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611d275780601f10611cfc57610100808354040283529160200191611d27565b820191906000526020600020905b815481529060010190602001808311611d0a57829003601f168201915b5050505050815250848481518110611d3b57fe5b60209081029190910101526001909201915b50600101611c10565b506060816001600160401b0381118015611d6f57600080fd5b50604051908082528060200260200182016040528015611da957816020015b611d9661335a565b815260200190600190039081611d8e5790505b50905060005b82811015611dea57838181518110611dc357fe5b6020026020010151828281518110611dd757fe5b6020908102919091010152600101611daf565b5094505050505090565b611dfc612c69565b6001546001600160a01b03908116911614611e295760405162461bcd60e51b81526004016105b390614311565b51600755565b60608082516001600160401b0381118015611e4957600080fd5b50604051908082528060200260200182016040528015611e8357816020015b611e7061335a565b815260200190600190039081611e685790505b50905060005b8351811015611f2d57838181518110611e9e57fe5b602002602001015160000151828281518110611eb657fe5b60200260200101516000018181525050838181518110611ed257fe5b602002602001015160200151828281518110611eea57fe5b6020026020010151602001818152505060405180602001604052806000815250828281518110611f1657fe5b602090810291909101015160400152600101611e89565b50611f37816113a0565b9392505050565b611f46612c69565b6001546001600160a01b03908116911614611f735760405162461bcd60e51b81526004016105b390614311565b600855565b600b546001600160a01b031615611fa15760405162461bcd60e51b81526004016105b390614558565b80611fbe5760405162461bcd60e51b81526004016105b3906143eb565b6000611fc86112b1565b90506000611fd7600583612a2b565b6002811115611fe257fe5b14611fff5760405162461bcd60e51b81526004016105b390614521565b600061200c600583612780565b9050600061201b8787876127e2565b905060016120298284612d51565b600381111561203457fe5b146120515760405162461bcd60e51b81526004016105b390614346565b600381018290556000828152600282016020908152604080832033808552928190529281902087905551899185917f6beca723245953d9ed92ae4d320d4772838e841161bfff12c78ae4268df525eb906120ae908c908c9061466d565b60405180910390a45050505050505050565b6002602081815260009283526040928390208054845180840186526001830154815285519384019095529281015482526003015491929184565b6001546001600160a01b031690565b600061212583836040518060200160405280600081525061244c565b90505b92915050565b60005b81518110156116a0576121ba82828151811061214957fe5b60200260200101516000015183838151811061216157fe5b60200260200101516020015184848151811061217957fe5b60200260200101516040015185858151811061219157fe5b6020026020010151606001518686815181106121a957fe5b6020026020010151608001516121c2565b600101612131565b600b546001600160a01b0316156121eb5760405162461bcd60e51b81526004016105b390614558565b60016121f8611b8e6112b1565b600281111561220357fe5b146122205760405162461bcd60e51b81526004016105b39061414e565b600061222d6105c76112b1565b9050600061223c8787866127e2565b60008381526002820160209081526040808320338452918290529091208054929350909161227c5760405162461bcd60e51b81526004016105b39061441a565b80600001548786338b8a898f60405160200161229e9796959493929190613e36565b60405160208183030381529060405280519060200120146122d15760405162461bcd60e51b81526004016105b39061409a565b6000848152600260205260409020546122fc5760405162461bcd60e51b81526004016105b39061458f565b600084815260026020526040812054908255612316613347565b60408051602081019182905260095463277166bf60e11b9092529081906001600160a01b0316634ee2cd7e61234f338760248601613ed4565b60206040518083038186803b15801561236757600080fd5b505afa15801561237b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061239f9190613df2565b8152509050886040516020016123b59190613fbf565b60408051601f1981840301815291905280516020909101206001808501919091556123e39085018a83612f8b565b8a86336001600160a01b03167f3fad5d37ee1be2f58ff1735699121a8ead73c3b70d02fc06d07b0db29854d3b48d8d8d876000015160405161242894939291906146da565b60405180910390a45050505050505050505050565b6009546001600160a01b031681565b600b546000906001600160a01b03161561248f57600b546001600160a01b0316331461248a5760405162461bcd60e51b81526004016105b39061462c565b6125b8565b600a546040516302abf57960e61b81526000916001600160a01b03169063aafd5e40906124cb9067526567697374727960c01b90600401613fbf565b60206040518083038186803b1580156124e357600080fd5b505afa1580156124f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061251b919061364d565b60405163f9f6b49b60e01b81529091506001600160a01b0382169063f9f6b49b9061254a903390600401613ec0565b60206040518083038186803b15801561256257600080fd5b505afa158015612576573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061259a9190613ae1565b6125b65760405162461bcd60e51b81526004016105b3906144ea565b505b6000611b6f858585612dfd565b60065481565b61200081565b60006121258383604051806020016040528060008152506119cd565b600b546001600160a01b0316156126165760405162461bcd60e51b81526004016105b390614558565b61263183836040518060200160405280600081525084611f78565b505050565b61263e612c69565b6001546001600160a01b0390811691161461266b5760405162461bcd60e51b81526004016105b390614311565b6001600160a01b0381166126915760405162461bcd60e51b81526004016105b3906140d1565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000816040516020016127009190613e8f565b604051602081830303815290604052805190602001209050919050565b6000811161272a57600080fd5b9055565b600061273982613040565b5183511115905092915050565b60008261275557506000612128565b8282028284828161276257fe5b04146121255760405162461bcd60e51b81526004016105b3906142d0565b60008061279160025b855490612746565b905061279d838261306e565b949350505050565b6127ad613347565b6040805160208101909152825184518291670de0b6b3a7640000916127d191612746565b816127d857fe5b0490529392505050565b6000600360006127f3868686612d1b565b815260200190815260200160002090509392505050565b6004820154600019141561281d576116a0565b60008061283a61283085600301546130b0565b60018501906131a2565b915091508161285b5760405162461bcd60e51b81526004016105b3906144b5565b6004805460001981019160009160039183918590811061287757fe5b90600052602060002001548152602001908152602001600020905085600401548160040181905550600482815481106128ac57fe5b906000526020600020015460048760040154815481106128c857fe5b60009182526020909120015560048054806128df57fe5b60008281526020812082016000199081019190915590810190915560048701558554600387015460018801546040517fb1f1bf5aec084730c2c09f66ae2099185eaf6f951ddc113a19aa886a9f5e71b79161294091889060058d019061470a565b60405180910390a3505050505050565b600082600201546040516020016129679190613fbf565b604051602081830303815290604052805190602001208214905092915050565b61298f613347565b50600281015460009081526020918252604090819020815192830190915254815290565b6129bb613347565b60408051602081019091528251845182916129e8916129e290670de0b6b3a7640000612746565b9061306e565b90529392505050565b6129f9613347565b60408051602081019091528251845182916129e89190612dd8565b6000612a1f82613040565b51835111905092915050565b6000612a4560028454612a3f90859061306e565b9061324e565b600281111561212557fe5b60008151604114612a735760405162461bcd60e51b81526004016105b390614063565b60208201516040830151606084015160001a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115612ac55760405162461bcd60e51b81526004016105b390614185565b8060ff16601b14158015612add57508060ff16601c14155b15612afa5760405162461bcd60e51b81526004016105b390614227565b600060018783868660405160008152602001604052604051612b1f9493929190613fe7565b6020604051602081039080840390855afa158015612b41573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116612b745760405162461bcd60e51b81526004016105b39061402c565b9695505050505050565b600081815260026020526040902080546116a057600960009054906101000a90046001600160a01b03166001600160a01b0316639711715a6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612be257600080fd5b505af1158015612bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c1a9190613df2565b815560008281526002602081905260409091206007546001820155600654910155600854612c5390612c4d600585613290565b90612dd8565b6000838152600260205260409020600301555050565b3390565b6000612c7882613040565b51835110905092915050565b600a546040516302abf57960e61b81526000916001600160a01b03169063aafd5e4090612ccb90721259195b9d1a599a595c95da1a5d195b1a5cdd606a1b90600401613fbf565b60206040518083038186803b158015612ce357600080fd5b505afa158015612cf7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061152d919061364d565b6000838383604051602001612d3293929190613fc8565b6040516020818303038152906040528051906020012090509392505050565b6000826003015460001415612d6857506000612128565b8183600301541015612dbc5760038301546000818152600285016020526040812091612da190612d97906130b0565b60018401906131a2565b50905080612db0576001612db3565b60025b92505050612128565b8183600301541415612dd057506001612128565b506003612128565b6000828201838110156121255760405162461bcd60e51b81526004016105b390614117565b60008060606000612e0f8787876127e2565b90506000612e1e6105c76112b1565b90506000612e2c8383612d51565b90506001816003811115612e3c57fe5b1415612e89576000806040518060400160405280601e81526020017f43757272656e7420766f74696e6720726f756e64206e6f7420656e6465640000815250955095509550505050612f82565b6002816003811115612e9757fe5b1415612ee45760038301546000818152600285016020526040812091612ec090612d97906130b0565b60408051602081019091526000815260019a509098509650612f8295505050505050565b6003816003811115612ef257fe5b1415612f3f576000806040518060400160405280601d81526020017f5072696365206973207374696c6c20746f20626520766f746564206f6e000000815250955095509550505050612f82565b6000806040518060400160405280601981526020017f507269636520776173206e6576657220726571756573746564000000000000008152509550955095505050505b93509350939050565b604080516020810190915260018401548152612fa790826129f1565b51600184015560008281526020848152604091829020825191820190925290548152612fd390826129f1565b60008381526020859052604090209051905560028301548214801590613031575060028301546000908152602084815260408083208151808401835290548152858452868352928190208151928301909152548152613031916132b4565b15612631575060029190910155565b613048613347565b60408051602081019091528061306684670de0b6b3a7640000612746565b905292915050565b600061212583836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506132bb565b6130b8613347565b600082815260026020526040902054806130e3575050604080516020810190915260001981526114ff565b6130eb613347565b604080516020810191829052600954630981b24d60e41b9092529081906001600160a01b031663981b24d06131238660248501613fbf565b60206040518083038186803b15801561313b57600080fd5b505afa15801561314f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131739190613df2565b90526000858152600260208181526040928390208351918201909352910154815290915061279d9082906127a5565b6000806131ad613347565b6131c260646131bc6032613040565b906132f2565b6040805160208101909152600187015481529091506131e190856132b4565b801561322c57506040805160208082018352600188015482526002880154600090815288825283902083519182019093529154825261322c91839161322691906129b3565b906132b4565b15613241576001925084600201549150613246565b600092505b509250929050565b600061212583836040518060400160405280601881526020017f536166654d6174683a206d6f64756c6f206279207a65726f0000000000000000815250613313565b60008061329d6002612789565b905061279d6132ad846001612dd8565b8290612746565b5190511190565b600081836132dc5760405162461bcd60e51b81526004016105b39190614019565b5060008385816132e857fe5b0495945050505050565b6132fa613347565b6040805160208101909152835181906129e8908561306e565b600081836133345760405162461bcd60e51b81526004016105b39190614019565b5082848161333e57fe5b06949350505050565b6040518060200160405280600081525090565b6040805160608082018352600080835260208301529181019190915290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106133ba57805160ff19168380011785556133e7565b828001600101855582156133e7579182015b828111156133e75782518255916020019190600101906133cc565b506133f392915061346d565b5090565b604080518082019091526000808252602082015290565b6040518060a0016040528060008019168152602001600081526020016000815260200160608152602001600081525090565b6040805160a081018252600080825260208201819052606092820183905282820152608081019190915290565b5b808211156133f3576000815560010161346e565b600082601f830112613492578081fd5b81356134a56134a0826147e9565b6147c3565b818152915060208083019084810160005b8481101561352f5781358701606080601f19838c030112156134d757600080fd5b6134e0816147c3565b8286013581526040808401358288015291830135916001600160401b0383111561350957600080fd5b6135178c88858701016135c9565b908201528652505092820192908201906001016134b6565b505050505092915050565b600082601f83011261354a578081fd5b81356135586134a0826147e9565b818152915060208083019084810160408085028701830188101561357b57600080fd5b6000805b868110156135bc5782848b031215613595578182fd5b61359e836147c3565b8435815285850135868201528652948401949282019260010161357f565b5050505050505092915050565b600082601f8301126135d9578081fd5b81356001600160401b038111156135ee578182fd5b613601601f8201601f19166020016147c3565b915080825283602082850101111561361857600080fd5b8060208401602084013760009082016020015292915050565b600060208284031215613642578081fd5b813561212581614840565b60006020828403121561365e578081fd5b815161212581614840565b60008060006060848603121561367d578182fd5b833561368881614840565b92506020840135915060408401356001600160401b038111156136a9578182fd5b6136b586828701613482565b9150509250925092565b6000806000606084860312156136d3578081fd5b83356136de81614840565b92506020840135915060408401356001600160401b038111156136ff578182fd5b6136b58682870161353a565b6000602080838503121561371d578182fd5b82356001600160401b0380821115613733578384fd5b818501915085601f830112613746578384fd5b81356137546134a0826147e9565b81815284810190848601875b848110156137ff578135870160a080601f19838f03011215613780578a8bfd5b613789816147c3565b8a830135815260408301358b8201526060830135898111156137a9578c8dfd5b6137b78f8d838701016135c9565b604083015250608083013560608201529082013590888211156137d8578b8cfd5b6137e68e8c848601016135c9565b6080820152865250509287019290870190600101613760565b50909998505050505050505050565b60006020808385031215613820578182fd5b82356001600160401b0380821115613836578384fd5b818501915085601f830112613849578384fd5b81356138576134a0826147e9565b81815284810190848601875b848110156137ff5781358701608080601f19838f03011215613883578a8bfd5b61388c816147c3565b828b01358152604080840135828d01526060840135908201529082013590888211156138b6578b8cfd5b6138c48e8c848601016135c9565b6060820152865250509287019290870190600101613863565b6000602082840312156138ee578081fd5b81356001600160401b03811115613903578182fd5b61279d84828501613482565b600060208284031215613920578081fd5b81356001600160401b03811115613935578182fd5b61279d8482850161353a565b60006020808385031215613953578182fd5b82356001600160401b0380821115613969578384fd5b818501915085601f83011261397c578384fd5b813561398a6134a0826147e9565b81815284810190848601875b848110156137ff578135870160a080601f19838f030112156139b6578a8bfd5b6139bf816147c3565b828b01358152604080840135828d01526060840135908201526080830135898111156139e9578c8dfd5b6139f78f8d838701016135c9565b606083015250910135608082015284529287019290870190600101613996565b60006020808385031215613a29578182fd5b82356001600160401b03811115613a3e578283fd5b8301601f81018513613a4e578283fd5b8035613a5c6134a0826147e9565b818152838101908385016080808502860187018a1015613a7a578788fd5b8795505b84861015613ad35780828b031215613a94578788fd5b613a9d816147c3565b82358152878301358882015260408084013590820152606080840135908201528452600195909501949286019290810190613a7e565b509098975050505050505050565b600060208284031215613af2578081fd5b81518015158114612125578182fd5b60008060408385031215613b13578182fd5b50508035926020909101359150565b600080600060608486031215613b36578081fd5b505081359360208301359350604090920135919050565b60008060008060808587031215613b62578182fd5b84359350602085013592506040850135915060608501356001600160401b03811115613b8c578182fd5b613b98878288016135c9565b91505092959194509250565b600080600060608486031215613bb8578081fd5b833592506020840135915060408401356001600160401b03811115613bdb578182fd5b6136b5868287016135c9565b60008060008060808587031215613bfc578182fd5b843593506020850135925060408501356001600160401b03811115613c1f578283fd5b613c2b878288016135c9565b949793965093946060013593505050565b600080600080600060a08688031215613c53578283fd5b853594506020860135935060408601356001600160401b0380821115613c77578485fd5b613c8389838a016135c9565b9450606088013593506080880135915080821115613c9f578283fd5b50613cac888289016135c9565b9150509295509295909350565b600080600080600060a08688031215613cd0578283fd5b85359450602086013593506040860135925060608601356001600160401b03811115613cfa578182fd5b613d06888289016135c9565b95989497509295608001359392505050565b60008060008060808587031215613d2d578182fd5b5050823594602084013594506040840135936060013592509050565b60008060208385031215613d5b578182fd5b82356001600160401b0380821115613d71578384fd5b818501915085601f830112613d84578384fd5b813581811115613d92578485fd5b866020828501011115613da3578485fd5b60209290920196919550909350505050565b600060208284031215613dc6578081fd5b613dd060206147c3565b9135825250919050565b600060208284031215613deb578081fd5b5035919050565b600060208284031215613e03578081fd5b5051919050565b60008151808452613e22816020860160208601614814565b601f01601f19169290920160200192915050565b60008882528760208301526bffffffffffffffffffffffff198760601b1660408301528560548301528451613e72816074850160208901614814565b909101607481019390935250609482015260b40195945050505050565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b60208082528251828201819052600091906040908185019080840286018301878501865b83811015613ad357888303603f1901855281518051845287810151888501528601516060878501819052613f4781860183613e0a565b968901969450505090860190600101613f11565b602080825282518282018190526000919060409081850190868401855b82811015613fa7578151805160048110613f8e57fe5b8552860151868501529284019290850190600101613f78565b5091979650505050505050565b901515815260200190565b90815260200190565b600084825283602083015260606040830152610c556060830184613e0a565b93845260ff9290921660208401526040830152606082015260800190565b602081016003831061401357fe5b91905290565b6000602082526121256020830184613e0a565b60208082526018908201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604082015260600190565b6020808252601f908201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604082015260600190565b6020808252601c908201527f52657665616c6564206461746120213d20636f6d6d6974206861736800000000604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252601d908201527f43616e6e6f742072657665616c20696e20636f6d6d6974207068617365000000604082015260600190565b60208082526022908201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604082015261756560f01b606082015260800190565b6020808252601b908201527f43616e206f6e6c792063616c6c2066726f6d206d696772617465640000000000604082015260600190565b6020808252600f908201526e125b9d985b1a59081c9bdd5b991259608a1b604082015260600190565b60208082526022908201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604082015261756560f01b606082015260800190565b602080825260169082015275496e76616c696420616e63696c6c617279206461746160501b604082015260600190565b6020808252601e908201527f556e737570706f72746564206964656e74696669657220726571756573740000604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252601e908201527f43616e6e6f7420636f6d6d697420696e61637469766520726571756573740000604082015260600190565b6020808252601b908201527f5369676e6174757265206d757374206d617463682073656e6465720000000000604082015260600190565b6020808252601c908201527f566f74696e6720746f6b656e2069737375616e6365206661696c656400000000604082015260600190565b602080825260159082015274092dcecc2d8d2c840e0e4deecd2c8cac840d0c2e6d605b1b604082015260600190565b602080825260139082015272125b9d985b1a59081a185cda081c995d99585b606a1b604082015260600190565b60208082526018908201527f43616e206f6e6c79207265717565737420696e20706173740000000000000000604082015260600190565b6020808252601d908201527f4741542070657263656e74616765206d757374206265203c2031303025000000604082015260600190565b6020808252818101527f43616e2774207265736f6c766520756e7265736f6c7665642072657175657374604082015260600190565b60208082526019908201527f43616c6c6564206d757374206265207265676973746572656400000000000000604082015260600190565b6020808252601d908201527f43616e6e6f7420636f6d6d697420696e2072657665616c207068617365000000604082015260600190565b6020808252601e908201527f4f6e6c792063616c6c2074686973206966206e6f74206d696772617465640000604082015260600190565b602080825260159082015274149bdd5b99081a185cc81b9bc81cdb985c1cda1bdd605a1b604082015260600190565b6020808252601d908201527f526574726965766520666f7220766f7465732073616d6520726f756e64000000604082015260600190565b6020808252601d908201527f4f6e6c7920736e617073686f7420696e2072657665616c207068617365000000604082015260600190565b6020808252601f908201527f43616c6c6572206d757374206265206d69677261746564206164647265737300604082015260600190565b9051815260200190565b60008382526040602083015261279d6040830184613e0a565b60008482526060602083015261469f6060830185613e0a565b8281036040840152612b748185613e0a565b6000848252606060208301526146ca6060830185613e0a565b9050826040830152949350505050565b6000858252846020830152608060408301526146f96080830185613e0a565b905082606083015295945050505050565b600060608201858352602085818501526060604085015282855460018082166000811461473e576001811461475e57614799565b61474e607f600285041687613fbf565b60ff198416815285019350614799565b6002830461476c8188613fbf565b6147758b614808565b895b8381101561479057815483820152908501908801614777565b91909101955050505b50919998505050505050505050565b93845291516020840152516040830152606082015260800190565b6040518181016001600160401b03811182821017156147e157600080fd5b604052919050565b60006001600160401b038211156147fe578081fd5b5060209081020190565b60009081526020902090565b60005b8381101561482f578181015183820152602001614817565b83811115610d525750506000910152565b6001600160a01b038116811461485557600080fd5b5056fea264697066735822122022f7fc478ff7feaaea6add2866b0175f606be8ef9f88a726e829e809654fef6b64736f6c634300060c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000001518000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000001c6bf526340000000000000000000000000000000000000000000000000000000000757b12c0000000000000000000000000004fa0d235c4abf4bcf4787af4cf447de572ef82800000000000000000000000040f941e48a552bf496b154af6bf55725f18d77c30000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _phaseLength (uint256): 86400
Arg [1] : _gatPercentage (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [2] : _inflationRate (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [3] : _rewardsExpirationTimeout (uint256): 31536000000
Arg [4] : _votingToken (address): 0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828
Arg [5] : _finder (address): 0x40f941E48A552bF496B154Af6bf55725f18D77c3
Arg [6] : _timerAddress (address): 0x0000000000000000000000000000000000000000
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [1] : 00000000000000000000000000000000000000000000000000b1a2bc2ec50000
Arg [2] : 0000000000000000000000000000000000000000000000000001c6bf52634000
Arg [3] : 0000000000000000000000000000000000000000000000000000000757b12c00
Arg [4] : 00000000000000000000000004fa0d235c4abf4bcf4787af4cf447de572ef828
Arg [5] : 00000000000000000000000040f941e48a552bf496b154af6bf55725f18d77c3
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode Sourcemap
938:43936:73:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;29346:4026;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;33493:640;;;;;;:::i;:::-;;:::i;19895:628::-;;;;;;:::i;:::-;;:::i;:::-;;38074:299;;;;;;:::i;:::-;;:::i;24633:444::-;;;;;;:::i;:::-;;:::i;397:27:19:-;;;:::i;:::-;;;;;;;:::i;6189:104:73:-;;;:::i;:::-;;;;;;;:::i;10466:1483::-;;;;;;:::i;:::-;;:::i;1112:115:19:-;;;;;;:::i;:::-;;:::i;5029:39:73:-;;;:::i;37062:197::-;;;;;;:::i;:::-;;:::i;1472:256:19:-;;;:::i;4846:40:73:-;;;:::i;5365:30::-;;;:::i;25198:301::-;;;;;;:::i;:::-;;:::i;15064:1054::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;23627:197::-;;;;;;:::i;:::-;;:::i;36516:187::-;;;:::i;28126:555::-;;;;;;:::i;:::-;;:::i;12070:123::-;;;;;;:::i;:::-;;:::i;25927:620::-;;;;;;:::i;:::-;;:::i;26668:583::-;;;;;;:::i;:::-;;:::i;1656:145:1:-;;;:::i;13893:431:73:-;;;;;;:::i;:::-;;:::i;4365:33::-;;;:::i;36179:178::-;;;:::i;:::-;;;;;;;:::i;34497:1473::-;;;:::i;:::-;;;;;;;:::i;37558:217::-;;;;;;:::i;:::-;;:::i;16239:534::-;;;;;;:::i;:::-;;:::i;38633:242::-;;;;;;:::i;:::-;;:::i;17851:1231::-;;;;;;:::i;:::-;;:::i;4020:39::-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;:::i;1033:77:1:-;;;:::i;13172:142:73:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;27643:362::-;;;;;;:::i;:::-;;:::i;21240:2266::-;;;;;;:::i;:::-;;:::i;5113:30::-;;;:::i;12765:286::-;;;;;;:::i;:::-;;:::i;4531:40::-;;;:::i;6132:50::-;;;:::i;14445:144::-;;;;;;:::i;:::-;;:::i;19203:189::-;;;;;;:::i;:::-;;:::i;1950:240:1:-;;;;;;:::i;:::-;;:::i;29346:4026:73:-;29510:45;;:::i;:::-;29571:15;;-1:-1:-1;;;;;29571:15:73;:29;29567:129;;29638:15;;-1:-1:-1;;;;;29638:15:73;29624:10;:29;29616:69;;;;-1:-1:-1;;;29616:69:73;;;;;;;:::i;:::-;;;;;;;;;29723:50;29756:16;:14;:16::i;:::-;29723:10;;:32;:50::i;:::-;29713:7;:60;29705:88;;;;-1:-1:-1;;;29705:88:73;;;;;;;:::i;:::-;29804:19;29826:15;;;:6;:15;;;;;29887:27;;;;29826:15;;29804:19;29868:16;:14;:16::i;:::-;:46;29851:63;;29924:42;;:::i;:::-;29981:76;;;;;;;;;;30001:11;;30039:16;;-1:-1:-1;;;30001:55:73;;;29981:76;;;;-1:-1:-1;;;;;30001:11:73;;:23;;:55;;30025:12;;30001:55;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;29981:76;;29924:133;-1:-1:-1;30170:46:73;;:::i;:::-;30231:64;;;;;;;;;;30251:11;;30277:16;;-1:-1:-1;;;30251:43:73;;;30231:64;;;;-1:-1:-1;;;;;30251:11:73;;:25;;:43;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;30231:64;;30170:125;-1:-1:-1;30305:45:73;;:::i;:::-;30353:23;;;;;;;;;:19;;;:23;;;:44;;30377:19;30353:23;:44::i;:::-;30305:92;;30492:22;;;;;;;;30512:1;30492:22;;;30471:43;;30530:9;30525:2623;30549:10;:17;30545:1;:21;30525:2623;;;30587:33;30639:91;30656:10;30667:1;30656:13;;;;;;;;;;;;;;:24;;;30682:10;30693:1;30682:13;;;;;;;;;;;;;;:18;;;30702:10;30713:1;30702:13;;;;;;;;;;;;;;:27;;;30639:16;:91::i;:::-;30807:28;;;;30744:33;30780:56;;;:26;;;:56;;;;;30587:143;;-1:-1:-1;30928:39:73;;30920:81;;;;-1:-1:-1;;;30920:81:73;;;;;;;:::i;:::-;31016:48;31037:12;31051;31016:20;:48::i;:::-;-1:-1:-1;;;;;31083:42:73;;:28;:42;;;;;;;;;;:53;;;31079:1903;;31161:8;;;;31079:1903;31194:9;31190:1792;;;31393:10;31404:1;31393:13;;;;;;;;;;;;;;:24;;;31364:7;31330:12;-1:-1:-1;;;;;31292:255:73;;31439:10;31450:1;31439:13;;;;;;;;;;;;;;:18;;;31479:10;31490:1;31479:13;;;;;;;;;;;;;;:27;;;31528:1;31292:255;;;;;;;;:::i;:::-;;;;;;;;31190:1792;;;-1:-1:-1;;;;;31635:42:73;;:28;:42;;;;;;;;;;:53;;;;;31589:100;;:30;;;:45;:100::i;:::-;31568:1414;;;31978:33;;:::i;:::-;32034:152;32103:61;:12;:30;;:59;:61::i;:::-;32034:39;:15;32054:18;32034:19;:39::i;:::-;:43;;:152::i;:::-;31978:208;-1:-1:-1;32225:30:73;:18;31978:208;32225:22;:30::i;:::-;32204:51;;32436:10;32447:1;32436:13;;;;;;;;;;;;;;:24;;;32407:7;32373:12;-1:-1:-1;;;;;32335:269:73;;32482:10;32493:1;32482:13;;;;;;;;;;;;;;:18;;;32522:10;32533:1;32522:13;;;;;;;;;;;;;;:27;;;32571:6;:15;;;32335:269;;;;;;;;:::i;:::-;;;;;;;;31568:1414;;;;32813:10;32824:1;32813:13;;;;;;;;;;;;;;:24;;;32784:7;32750:12;-1:-1:-1;;;;;32712:255:73;;32859:10;32870:1;32859:13;;;;;;;;;;;;;;:18;;;32899:10;32910:1;32899:13;;;;;;;;;;;;;;:27;;;32948:1;32712:255;;;;;;;;:::i;:::-;;;;;;;;31568:1414;-1:-1:-1;;;;;33084:42:73;;:28;:42;;;;;;;;;;;:53;;33077:60;-1:-1:-1;30525:2623:73;30568:3;;30525:2623;;;-1:-1:-1;33204:35:73;:18;33237:1;33204:32;:35::i;:::-;33200:166;;;33263:11;;33294:27;;33263:59;;-1:-1:-1;;;33263:59:73;;-1:-1:-1;;;;;33263:11:73;;;;:16;;:59;;33280:12;;33294:27;33263:59;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;33255:100;;;;-1:-1:-1;;;33255:100:73;;;;;;;:::i;:::-;29346:4026;;;;;;;;;;:::o;33493:640::-;33648:26;;:::i;:::-;33686:52;33771:10;:17;-1:-1:-1;;;;;33741:48:73;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;33686:103;;33805:9;33800:250;33824:10;:17;33820:1;:21;33800:250;;;33898:10;33909:1;33898:13;;;;;;;;;;;;;;:24;;;33862:19;33882:1;33862:22;;;;;;;;;;;;;;:33;;:60;;;;;33966:10;33977:1;33966:13;;;;;;;;;;;;;;:18;;;33936:19;33956:1;33936:22;;;;;;;;;;;;;;:27;;:48;;;;;33998:41;;;;;;;;;;;;:19;34018:1;33998:22;;;;;;;;;;;;;;;;;;:36;;:41;33843:3;;33800:250;;;;34067:59;34083:12;34097:7;34106:19;34067:15;:59::i;:::-;34060:66;33493:640;-1:-1:-1;;;;;33493:640:73:o;19895:628::-;9629:15;;-1:-1:-1;;;;;9629:15:73;:29;9621:72;;;;-1:-1:-1;;;9621:72:73;;;;;;;:::i;:::-;20070:17:::1;20090:16;:14;:16::i;:::-;20070:36:::0;-1:-1:-1;20169:12:73::1;20124:41;:10;20155:9:::0;20124:30:::1;:41::i;:::-;:57;;;;;;;;;20116:99;;;;-1:-1:-1::0;;;20116:99:73::1;;;;;;;:::i;:::-;20363:10;-1:-1:-1::0;;;;;20314:59:73::1;:45;20328:19;;20349:9;;20314:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;::::0;;;;-1:-1:-1;20314:13:73::1;::::0;-1:-1:-1;;;20314:45:73:i:1;:::-;-1:-1:-1::0;;;;;20314:59:73::1;;20306:99;;;;-1:-1:-1::0;;;20306:99:73::1;;;;;;;:::i;:::-;20415:15;20433:43;:10;20466:9:::0;20433:32:::1;:43::i;:::-;20415:61;;20486:30;20508:7;20486:21;:30::i;:::-;9703:1;;19895:628:::0;;:::o;38074:299::-;1247:12:1;:10;:12::i;:::-;1237:6;;-1:-1:-1;;;;;1237:6:1;;;:22;;;1229:67;;;;-1:-1:-1;;;1229:67:1;;;;;;;:::i;:::-;38260:30:73::1;:16:::0;38288:1:::1;38260:27;:30::i;:::-;38252:72;;;;-1:-1:-1::0;;;38252:72:73::1;;;;;;;:::i;:::-;38334:32:::0;:13:::1;:32:::0;38074:299::o;24633:444::-;24845:49;24856:10;24868:4;24874:13;24889:4;24845:10;:49::i;:::-;24905:15;24923:50;24956:16;:14;:16::i;24923:50::-;24905:68;;25023:10;25014:7;25002:10;-1:-1:-1;;;;;24988:82:73;;25035:4;25041:13;25056;24988:82;;;;;;;;:::i;:::-;;;;;;;;24633:444;;;;;;:::o;397:27:19:-;;;-1:-1:-1;;;;;397:27:19;;:::o;6189:104:73:-;;;;:::o;10466:1483::-;9218:15;;-1:-1:-1;;;;;9218:15:73;:29;9214:345;;9285:15;;-1:-1:-1;;;;;9285:15:73;9271:10;:29;9263:73;;;;-1:-1:-1;;;9263:73:73;;;;;;;:::i;:::-;9214:345;;;9396:6;;:58;;-1:-1:-1;;;9396:58:73;;9367:17;;-1:-1:-1;;;;;9396:6:73;;:31;;:58;;-1:-1:-1;;;9428:25:73;9396:58;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9477:41;;-1:-1:-1;;;9477:41:73;;9367:88;;-1:-1:-1;;;;;;9477:29:73;;;;;:41;;9507:10;;9477:41;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9469:79;;;;-1:-1:-1;;;9469:79:73;;;;;;;:::i;:::-;9214:345;;10631:17:::1;10651:16;:14;:16::i;:::-;10631:36;;10693:9;10685:4;:17;;10677:54;;;;-1:-1:-1::0;;;10677:54:73::1;;;;;;;:::i;:::-;10749:25;:23;:25::i;:::-;-1:-1:-1::0;;;;;10749:47:73::1;;10797:10;10749:59;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;10741:102;;;;-1:-1:-1::0;;;10741:102:73::1;;;;;;;:::i;:::-;6178:4;10861:13;:20;:43;;10853:78;;;;-1:-1:-1::0;;;10853:78:73::1;;;;;;;:::i;:::-;10942:22;10967:52;10987:10;10999:4;11005:13;10967:19;:52::i;:::-;11029:33;11065:29:::0;;;:13:::1;:29;::::0;;;;10942:77;;-1:-1:-1;11129:43:73::1;:10;11162:9:::0;11129:32:::1;:43::i;:::-;11104:68;;11183:27;11213:47;11231:12;11245:14;11213:17;:47::i;:::-;11183:77:::0;-1:-1:-1;11292:26:73::1;11275:13;:43;;;;;;;;;11271:672;;;11480:19;11502:21;:14:::0;11521:1:::1;11502:18;:21::i;:::-;11570:240;::::0;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;;::::1;::::0;;;;;;;;;11722:20:::1;:27:::0;;11570:240;;;;;;;;;;;;-1:-1:-1;11538:29:73;;;:13:::1;:29:::0;;;;;;:272;;;;;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;;;;;;;;::::1;::::0;;;;;;;;11480:43;;-1:-1:-1;11570:240:73;;11538:272:::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;::::1;:::i;:::-;-1:-1:-1::0;;11824:20:73::1;:41:::0;;::::1;::::0;::::1;::::0;;-1:-1:-1;11824:41:73;;;;;::::1;::::0;;;-1:-1:-1;11884:48:73::1;::::0;11915:10;;11902:11;;11884:48:::1;::::0;::::1;::::0;11927:4;;11884:48:::1;:::i;:::-;;;;;;;;11271:672;;9568:1;;;;;10466:1483:::0;;;:::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;5029:39:73:-;;;;:::o;37062:197::-;1247:12:1;:10;:12::i;:::-;1237:6;;-1:-1:-1;;;;;1237:6:1;;;:22;;;1229:67;;;;-1:-1:-1;;;1229:67:1;;;;;;;:::i;:::-;37218:15:73::1;:34:::0;;-1:-1:-1;;;;;;37218:34:73::1;-1:-1:-1::0;;;;;37218:34:73;;;::::1;::::0;;;::::1;::::0;;37062:197::o;1472:256:19:-;1519:7;1542:12;;-1:-1:-1;;;;;1542:12:19;:28;1538:184;;1599:12;;;;;;;;-1:-1:-1;;;;;1599:12:19;-1:-1:-1;;;;;1593:34:19;;:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1586:43;;;;1538:184;-1:-1:-1;1667:3:19;1538:184;1472:256;:::o;4846:40:73:-;;;;:::o;5365:30::-;;;-1:-1:-1;;;;;5365:30:73;;:::o;25198:301::-;25374:38;25385:10;25397:4;25374:38;;;;;;;;;;;;25407:4;25374:10;:38::i;:::-;25423:69;25450:10;25462:4;25423:69;;;;;;;;;;;;25472:4;25478:13;25423:26;:69::i;15064:1054::-;15185:21;15222:35;15279:8;:15;-1:-1:-1;;;;;15260:35:73;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;15222:73;;15305:22;15330:50;15363:16;:14;:16::i;15330:50::-;15305:75;;15395:9;15390:692;15414:8;:15;15410:1;:19;15390:692;;;15450:33;15502:85;15519:8;15528:1;15519:11;;;;;;;;;;;;;;:22;;;15543:8;15552:1;15543:11;;;;;;;;;;;;;;:16;;;15561:8;15570:1;15561:11;;;;;;;15502:85;15450:137;;15602:20;15625:47;15643:12;15657:14;15625:17;:47::i;:::-;15602:70;-1:-1:-1;15820:20:73;15810:6;:30;;;;;;;;;15806:220;;;15895:14;15860:13;15874:1;15860:16;;;;;;;;;;;;;;:32;;:49;;;;;15806:220;;;15983:12;:28;;;15948:13;15962:1;15948:16;;;;;;;;;;;;;;:32;;:63;;;;;15806:220;16065:6;16039:13;16053:1;16039:16;;;;;;;;;;;;;;:23;;:32;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;15431:3:73;;15390:692;;;-1:-1:-1;16098:13:73;;-1:-1:-1;;15064:1054:73;;;;:::o;23627:197::-;23772:45;23783:10;23795:4;23801:5;23772:45;;;;;;;;;;;;23812:4;23772:10;:45::i;36516:187::-;36620:7;36646:50;36679:16;:14;:16::i;36646:50::-;36639:57;;36516:187;:::o;28126:555::-;28198:41;28264:7;:14;-1:-1:-1;;;;;28242:37:73;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;28198:81;;28295:9;28290:346;28314:7;:14;28310:1;:18;28290:346;;;28382:7;28390:1;28382:10;;;;;;;;;;;;;;:21;;;28349:16;28366:1;28349:19;;;;;;;;;;;;;;:30;;:54;;;;;28444:7;28452:1;28444:10;;;;;;;;;;;;;;:15;;;28417:16;28434:1;28417:19;;;;;;;;;;;;;;:24;;:42;;;;;28501:7;28509:1;28501:10;;;;;;;;;;;;;;:16;;;28473;28490:1;28473:19;;;;;;;;;;;;;;:25;;:44;;;;;28531:38;;;;;;;;;;;;:16;28548:1;28531:19;;;;;;;;;;;;;;:33;;:38;;;;28610:7;28618:1;28610:10;;;;;;;;;;;;;;:15;;;28583:16;28600:1;28583:19;;;;;;;;;;;;;;;;;;:24;;:42;28330:3;;28290:346;;;;28645:29;28657:16;28645:11;:29::i;:::-;28126:555;;:::o;12070:123::-;12152:34;12165:10;12177:4;12152:34;;;;;;;;;;;;:12;:34::i;25927:620::-;26017:9;26012:529;26036:7;:14;26032:1;:18;26012:529;;;26075:7;26083:1;26075:10;;;;;;;;;;;;;;:24;;;:31;26110:1;26075:36;26071:460;;;26131:93;26142:7;26150:1;26142:10;;;;;;;;;;;;;;:21;;;26165:7;26173:1;26165:10;;;;;;;;;;;;;;:15;;;26182:7;26190:1;26182:10;;;;;;;;;;;;;;:24;;;26208:7;26216:1;26208:10;;;;;;;;;;;;;;:15;;;26131:10;:93::i;:::-;26071:460;;;26263:253;26311:7;26319:1;26311:10;;;;;;;;;;;;;;:21;;;26354:7;26362:1;26354:10;;;;;;;;;;;;;;:15;;;26391:7;26399:1;26391:10;;;;;;;;;;;;;;:24;;;26437:7;26445:1;26437:10;;;;;;;;;;;;;;:15;;;26474:7;26482:1;26474:10;;;;;;;;;;;;;;:24;;;26263:26;:253::i;:::-;26052:3;;26012:529;;26668:583;26744:45;26818:7;:14;-1:-1:-1;;;;;26792:41:73;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;26744:89;;26849:9;26844:362;26868:7;:14;26864:1;:18;26844:362;;;26936:7;26944:1;26936:10;;;;;;;;;;;;;;:21;;;26903:16;26920:1;26903:19;;;;;;;;;;;;;;:30;;:54;;;;;26998:7;27006:1;26998:10;;;;;;;;;;;;;;:15;;;26971:16;26988:1;26971:19;;;;;;;;;;;;;;:24;;:42;;;;;27027:38;;;;;;;;;;;;:16;27044:1;27027:19;;;;;;;;;;;;;;:33;;:38;;;;27106:7;27114:1;27106:10;;;;;;;;;;;;;;:15;;;27079:16;27096:1;27079:19;;;;;;;;;;;;;;:24;;:42;;;;;27171:7;27179:1;27171:10;;;;;;;;;;;;;;:24;;;27135:16;27152:1;27135:19;;;;;;;;;;;;;;;;;;:33;;:60;26884:3;;26844:362;;;;27215:29;27227:16;27215:11;:29::i;1656:145:1:-;1247:12;:10;:12::i;:::-;1237:6;;-1:-1:-1;;;;;1237:6:1;;;:22;;;1229:67;;;;-1:-1:-1;;;1229:67:1;;;;;;;:::i;:::-;1746:6:::1;::::0;1725:40:::1;::::0;1762:1:::1;::::0;-1:-1:-1;;;;;1746:6:1::1;::::0;1725:40:::1;::::0;1762:1;;1725:40:::1;1775:6;:19:::0;;-1:-1:-1;;;;;;1775:19:1::1;::::0;;1656:145::o;13893:431:73:-;9218:15;;14058:6;;-1:-1:-1;;;;;9218:15:73;:29;9214:345;;9285:15;;-1:-1:-1;;;;;9285:15:73;9271:10;:29;9263:73;;;;-1:-1:-1;;;9263:73:73;;;;;;;:::i;:::-;9214:345;;;9396:6;;:58;;-1:-1:-1;;;9396:58:73;;9367:17;;-1:-1:-1;;;;;9396:6:73;;:31;;:58;;-1:-1:-1;;;9428:25:73;9396:58;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9477:41;;-1:-1:-1;;;9477:41:73;;9367:88;;-1:-1:-1;;;;;;9477:29:73;;;;;:41;;9507:10;;9477:41;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9469:79;;;;-1:-1:-1;;;9469:79:73;;;;;;;:::i;:::-;9214:345;;14077:14:::1;14093:12:::0;14107:21:::1;14132:49;14149:10;14161:4;14167:13;14132:16;:49::i;:::-;14076:105;;;;;;14276:9;14287:7;14268:27;;;;;-1:-1:-1::0;;;14268:27:73::1;;;;;;;;:::i;:::-;-1:-1:-1::0;14312:5:73;;13893:431;-1:-1:-1;;;;;;13893:431:73:o;4365:33::-;;;;:::o;36179:178::-;36278:5;36302:48;36333:16;:14;:16::i;:::-;36302:10;;:30;:48::i;34497:1473::-;34634:32;34682:17;34702:16;:14;:16::i;:::-;34682:36;-1:-1:-1;34728:22:73;34753:43;:10;34682:36;34753:32;:43::i;:::-;35091:20;:27;34728:68;;-1:-1:-1;35015:43:73;;-1:-1:-1;;;;;35061:58:73;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;35015:104;;35129:21;35170:9;35165:549;35189:20;:27;35185:31;;35165:549;;;35237:33;35273:13;:38;35287:20;35308:1;35287:23;;;;;;;;;;;;;;;;35273:38;;;;;;;;;;;35237:74;;35380:20;35329:71;;;;;;;;:47;35347:12;35361:14;35329:17;:47::i;:::-;:71;;;;;;;;;35325:379;;;35448:208;;;;;;;;35506:12;:23;;;35448:208;;;;35557:12;:17;;;35448:208;;;;35611:12;:26;;35448:208;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;35420:10;35431:13;35420:25;;;;;;;;;;;;;;;;;:236;35674:15;;;;;35325:379;-1:-1:-1;35218:3:73;;35165:549;;;;35724:48;35805:13;-1:-1:-1;;;;;35775:44:73;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;35724:95;;35834:9;35829:103;35853:13;35849:1;:17;35829:103;;;35908:10;35919:1;35908:13;;;;;;;;;;;;;;35887:15;35903:1;35887:18;;;;;;;;;;;;;;;;;:34;35868:3;;35829:103;;;-1:-1:-1;35948:15:73;-1:-1:-1;;;;;34497:1473:73;:::o;37558:217::-;1247:12:1;:10;:12::i;:::-;1237:6;;-1:-1:-1;;;;;1237:6:1;;;:22;;;1229:67;;;;-1:-1:-1;;;1229:67:1;;;;;;;:::i;:::-;37736:32:73;:13:::1;:32:::0;37558:217::o;16239:534::-;16327:21;16360:50;16443:8;:15;-1:-1:-1;;;;;16413:46:73;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;16360:99;;16475:9;16470:238;16494:8;:15;16490:1;:19;16470:238;;;16564:8;16573:1;16564:11;;;;;;;;;;;;;;:22;;;16530:17;16548:1;16530:20;;;;;;;;;;;;;;:31;;:56;;;;;16628:8;16637:1;16628:11;;;;;;;;;;;;;;:16;;;16600:17;16618:1;16600:20;;;;;;;;;;;;;;:25;;:44;;;;;16658:39;;;;;;;;;;;;:17;16676:1;16658:20;;;;;;;;;;;;;;;;;;:34;;:39;16511:3;;16470:238;;;;16724:42;16748:17;16724:23;:42::i;:::-;16717:49;16239:534;-1:-1:-1;;;16239:534:73:o;38633:242::-;1247:12:1;:10;:12::i;:::-;1237:6;;-1:-1:-1;;;;;1237:6:1;;;:22;;;1229:67;;;;-1:-1:-1;;;1229:67:1;;;;;;;:::i;:::-;38814:24:73::1;:54:::0;38633:242::o;17851:1231::-;9629:15;;-1:-1:-1;;;;;9629:15:73;:29;9621:72;;;;-1:-1:-1;;;9621:72:73;;;;;;;:::i;:::-;18039:18;18031:52:::1;;;;-1:-1:-1::0;;;18031:52:73::1;;;;;;;:::i;:::-;18158:17;18178:16;:14;:16::i;:::-;18158:36:::0;-1:-1:-1;18270:37:73::1;18225:41;:10;18256:9:::0;18225:30:::1;:41::i;:::-;:82;;;;;;;;;18204:158;;;;-1:-1:-1::0;;;18204:158:73::1;;;;;;;:::i;:::-;18455:22;18480:43;:10;18513:9:::0;18480:32:::1;:43::i;:::-;18455:68;;18534:33;18570:49;18587:10;18599:4;18605:13;18570:16;:49::i;:::-;18534:85:::0;-1:-1:-1;18701:20:73::1;18650:47;18668:12;18682:14;18650:17;:47::i;:::-;:71;;;;;;;;;18629:148;;;;-1:-1:-1::0;;;18629:148:73::1;;;;;;;:::i;:::-;18788:28;::::0;::::1;:45:::0;;;18843:33:::1;18879:42:::0;;;:26:::1;::::0;::::1;:42;::::0;;;;;;;18960:10:::1;18931:40:::0;;;;;;;;;;;:54;;;19001:74;19043:10;;18819:14;;19001:74:::1;::::0;::::1;::::0;19055:4;;19061:13;;19001:74:::1;:::i;:::-;;;;;;;;9703:1;;;;17851:1231:::0;;;;:::o;4020:39::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1033:77:1:-;1097:6;;-1:-1:-1;;;;;1097:6:1;1033:77;:::o;13172:142:73:-;13254:4;13277:30;13286:10;13298:4;13277:30;;;;;;;;;;;;:8;:30::i;:::-;13270:37;;13172:142;;;;;:::o;27643:362::-;27729:9;27724:275;27748:7;:14;27744:1;:18;27724:275;;;27783:205;27811:7;27819:1;27811:10;;;;;;;;;;;;;;:21;;;27850:7;27858:1;27850:10;;;;;;;;;;;;;;:15;;;27883:7;27891:1;27883:10;;;;;;;;;;;;;;:16;;;27917:7;27925:1;27917:10;;;;;;;;;;;;;;:24;;;27959:7;27967:1;27959:10;;;;;;;;;;;;;;:15;;;27783:10;:205::i;:::-;27764:3;;27724:275;;21240:2266;9629:15;;-1:-1:-1;;;;;9629:15:73;:29;9621:72;;;;-1:-1:-1;;;9621:72:73;;;;;;;:::i;:::-;21501:12:::1;21449:48;21480:16;:14;:16::i;21449:48::-;:64;;;;;;;;;21441:106;;;;-1:-1:-1::0;;;21441:106:73::1;;;;;;;:::i;:::-;21687:15;21705:50;21738:16;:14;:16::i;21705:50::-;21687:68;;21766:33;21802:49;21819:10;21831:4;21837:13;21802:16;:49::i;:::-;21861:33;21897:35:::0;;;:26:::1;::::0;::::1;:35;::::0;;;;;;;22011:10:::1;21982:40:::0;;;;;;;;;22280:21;;21766:85;;-1:-1:-1;21897:35:73;;22272:67:::1;;;;-1:-1:-1::0;;;22272:67:73::1;;;;;;;:::i;:::-;22496:14;:21;;;22405:5;22412:4;22418:10;22430:4;22436:13;22451:7;22460:10;22388:83;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;22378:94;;;;;;:139;22353:226;;;;-1:-1:-1::0;;;22353:226:73::1;;;;;;;:::i;:::-;22689:15;::::0;;;:6:::1;:15;::::0;;;;:26;22681:65:::1;;;;-1:-1:-1::0;;;22681:65:73::1;;;;;;;:::i;:::-;22804:18;22825:15:::0;;;:6:::1;:15;::::0;;;;:26;22862:28;;;23080:34:::1;;:::i;:::-;23117:68;::::0;;::::1;::::0;::::1;::::0;;;;23137:11:::1;::::0;-1:-1:-1;;;23137:47:73;;;23117:68;;;-1:-1:-1;;;;;23137:11:73::1;:23;:47;23161:10;23173::::0;23137:47;;;::::1;:::i;:::-;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;23117:68;;::::0;23080:105:::1;;23284:5;23273:17;;;;;;;;:::i;:::-;;::::0;;-1:-1:-1;;23273:17:73;;::::1;::::0;;;;;;23263:28;;23273:17:::1;23263:28:::0;;::::1;::::0;23235:25:::1;::::0;;::::1;:56:::0;;;;23338:54:::1;::::0;:30;::::1;23377:5:::0;23384:7;23338:38:::1;:54::i;:::-;23442:10;23433:7;23421:10;-1:-1:-1::0;;;;;23408:91:73::1;;23454:4;23460:5;23467:13;23482:7;:16;;;23408:91;;;;;;;;;:::i;:::-;;;;;;;;9703:1;;;;;;21240:2266:::0;;;;;:::o;5113:30::-;;;-1:-1:-1;;;;;5113:30:73;;:::o;12765:286::-;9218:15;;12930:4;;-1:-1:-1;;;;;9218:15:73;:29;9214:345;;9285:15;;-1:-1:-1;;;;;9285:15:73;9271:10;:29;9263:73;;;;-1:-1:-1;;;9263:73:73;;;;;;;:::i;:::-;9214:345;;;9396:6;;:58;;-1:-1:-1;;;9396:58:73;;9367:17;;-1:-1:-1;;;;;9396:6:73;;:31;;:58;;-1:-1:-1;;;9428:25:73;9396:58;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9477:41;;-1:-1:-1;;;9477:41:73;;9367:88;;-1:-1:-1;;;;;;9477:29:73;;;;;:41;;9507:10;;9477:41;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9469:79;;;;-1:-1:-1;;;9469:79:73;;;;;;;:::i;:::-;9214:345;;12947:14:::1;12969:49;12986:10;12998:4;13004:13;12969:16;:49::i;4531:40::-:0;;;;:::o;6132:50::-;6178:4;6132:50;:::o;14445:144::-;14527:6;14552:30;14561:10;14573:4;14552:30;;;;;;;;;;;;:8;:30::i;19203:189::-;9629:15;;-1:-1:-1;;;;;9629:15:73;:29;9621:72;;;;-1:-1:-1;;;9621:72:73;;;;;;;:::i;:::-;19347:38:::1;19358:10;19370:4;19347:38;;;;;;;;;;;::::0;19380:4:::1;19347:10;:38::i;:::-;19203:189:::0;;;:::o;1950:240:1:-;1247:12;:10;:12::i;:::-;1237:6;;-1:-1:-1;;;;;1237:6:1;;;:22;;;1229:67;;;;-1:-1:-1;;;1229:67:1;;;;;;;:::i;:::-;-1:-1:-1;;;;;2038:22:1;::::1;2030:73;;;;-1:-1:-1::0;;;2030:73:1::1;;;;;;;:::i;:::-;2139:6;::::0;2118:38:::1;::::0;-1:-1:-1;;;;;2118:38:1;;::::1;::::0;2139:6:::1;::::0;2118:38:::1;::::0;2139:6:::1;::::0;2118:38:::1;2166:6;:17:::0;;-1:-1:-1;;;;;;2166:17:1::1;-1:-1:-1::0;;;;;2166:17:1;;;::::1;::::0;;;::::1;::::0;;1950:240::o;3363:265:2:-;3432:7;3615:4;3562:58;;;;;;;;:::i;:::-;;;;;;;;;;;;;3552:69;;;;;;3545:76;;3363:265;;;:::o;481:238:72:-;670:1;656:11;:15;648:24;;;;;;682:30;;481:238::o;5141:152:16:-;5221:4;5258:19;5275:1;5258:16;:19::i;:::-;:28;5244:10;;:42;;;-1:-1:-1;5141:152: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;1310:271:72:-;1404:7;;1445:84;1474:53;1466:62;1445:16;;;:20;:84::i;:::-;1423:106;-1:-1:-1;1546:28:72;:11;1423:106;1546:15;:28::i;:::-;1539:35;1310:271;-1:-1:-1;;;;1310:271:72:o;8186:650:16:-;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;40461:253:73:-;40601:20;40640:13;:67;40654:52;40674:10;40686:4;40692:13;40654:19;:52::i;:::-;40640:67;;;;;;;;;;;40633:74;;40461:253;;;;;:::o;41863:1068::-;41985:18;;;;-1:-1:-1;;41985:30:73;41981:67;;;42031:7;;41981:67;42058:15;42075:20;42111:90;42159:41;42171:12;:28;;;42159:11;:41::i;:::-;42111:30;;;;:47;:90::i;:::-;42057:144;;;;42219:10;42211:55;;;;-1:-1:-1;;;42211:55:73;;;;;;;:::i;:::-;42369:20;:27;;-1:-1:-1;;42369:31:73;;;42349:17;;42450:13;;42349:17;;42369:31;;42464;;;;;;;;;;;;;;42450:46;;;;;;;;;;;42410:86;;42531:12;:18;;;42506:16;:22;;:43;;;;42602:20;42623:9;42602:31;;;;;;;;;;;;;;;;42559:20;42580:12;:18;;;42559:40;;;;;;;;;;;;;;;;;:74;42643:20;:26;;;;;;;;;;;;;;;;-1:-1:-1;;42643:26:73;;;;;;;;;;;;;42680:18;;;:29;42793:23;;42751:28;;;;42643:26;42830:17;;;42724:200;;;;;;42861:13;;42888:26;;;;42724:200;:::i;:::-;;;;;;;;41863:1068;;;;;;:::o;3646:165:69:-;3730:4;3786;:16;;;3775:28;;;;;;;;:::i;:::-;;;;;;;;;;;;;3765:39;;;;;;3753:8;:51;3746:58;;3646:165;;;;:::o;4194:168::-;4274:26;;:::i;:::-;-1:-1:-1;4338:16:69;;;;4319:18;:36;;;;;;;;;;;;4312:43;;;;;;;;;;;;4194:168::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;:::-;11170:59;;11163:66;10602:634;-1:-1:-1;;;10602:634:16:o;6433:151::-;6507:15;;:::i;:::-;6541:36;;;;;;;;;6565:10;;6550;;6541:36;;6550:26;;:10;:14;:26::i;2248:147::-;2324:4;2360:19;2377:1;2360:16;:19::i;:::-;:28;2347:10;;:41;;-1:-1:-1;2248:147:16;;;;:::o;2421:466:72:-;2537:30;2765:101;2811:53;2781:16;;2765:33;;:11;;:15;:33::i;:::-;:37;;:101::i;:::-;2717:163;;;;;;;1031:2068:2;1109:7;1170:9;:16;1190:2;1170:22;1166:94;;1208:41;;-1:-1:-1;;;1208:41:2;;;;;;;:::i;1166:94::-;1610:4;1595:20;;1589:27;1655:4;1640:20;;1634:27;1708:4;1693:20;;1687:27;1326:9;1679:36;2626:66;2613:79;;2609:154;;;2708:44;;-1:-1:-1;;;2708:44:2;;;;;;;:::i;2609:154::-;2777:1;:7;;2782:2;2777:7;;:18;;;;;2788:1;:7;;2793:2;2788:7;;2777:18;2773:93;;;2811:44;;-1:-1:-1;;;2811:44:2;;;;;;;:::i;2773:93::-;2960:14;2977:24;2987:4;2993:1;2996;2999;2977:24;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2977:24:2;;-1:-1:-1;;2977:24:2;;;-1:-1:-1;;;;;;;3019:20:2;;3011:57;;;;-1:-1:-1;;;3011:57:2;;;;;;;:::i;:::-;3086:6;1031:2068;-1:-1:-1;;;;;;1031:2068:2:o;40956:901:73:-;41022:19;41044:15;;;:6;:15;;;;;41157:16;;41153:698;;41272:11;;;;;;;;;-1:-1:-1;;;;;41272:11:73;-1:-1:-1;;;;;41272:20:73;;:22;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;41253:41;;:16;41391:15;;;:6;:15;;;;;;;;41423:13;41391:45;:29;;;:45;41559:13;41527:45;:29;;:45;41802:24;;41741:99;;:39;:10;41398:7;41741:30;:39::i;:::-;:43;;:99::i;:::-;41701:15;;;;:6;:15;;;;;:37;;:139;40956:901;;:::o;735:104:0:-;822:10;735:104;:::o;4184:144:16:-;4257:4;4293:19;4310:1;4293:16;:19::i;:::-;:28;4280:10;;:41;;-1:-1:-1;4184:144:16;;;;:::o;44641:231:73:-;44795:6;;:69;;-1:-1:-1;;;44795:69:73;;44698:49;;-1:-1:-1;;;;;44795:6:73;;:31;;:69;;-1:-1:-1;;;44827:36:73;44795:69;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;40720:230::-;40863:7;40910:10;40922:4;40928:13;40899:43;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;40889:54;;;;;;40882:61;;40720:230;;;;;:::o;43736:899::-;43868:13;43901:12;:28;;;43933:1;43901:33;43897:732;;;-1:-1:-1;43957:26:73;43950:33;;43897:732;44035:14;44004:12;:28;;;:45;44000:629;;;44128:28;;;;44065:33;44101:56;;;:26;;;:56;;;;;;44209:90;;44257:41;;:11;:41::i;:::-;44209:30;;;;:47;:90::i;:::-;44171:128;;;44320:10;:58;;44358:20;44320:58;;;44333:22;44320:58;44313:65;;;;;;44000:629;44431:14;44399:12;:28;;;:46;44395:234;;;-1:-1:-1;44468:20:73;44461:27;;44395:234;-1:-1:-1;44598:20:73;44591:27;;834:176:4;892:7;923:5;;;946:6;;;;938:46;;;;-1:-1:-1;;;938:46:4;;;;;;;:::i;39247:1208:73:-;39424:4;39442:6;39462:13;39500:33;39536:49;39553:10;39565:4;39571:13;39536:16;:49::i;:::-;39500:85;;39595:22;39620:50;39653:16;:14;:16::i;39620:50::-;39595:75;;39681:27;39711:47;39729:12;39743:14;39711:17;:47::i;:::-;39681:77;-1:-1:-1;39789:20:73;39772:13;:37;;;;;;;;;39768:681;;;39833:5;39840:1;39825:51;;;;;;;;;;;;;;;;;;;;;;;;;;;;39768:681;39914:22;39897:13;:39;;;;;;;;;39893:556;;;40015:28;;;;39952:33;39988:56;;;:26;;;:56;;;;;;40101:90;;40149:41;;:11;:41::i;40101:90::-;40205:32;;;;;;;;;-1:-1:-1;40205:32:73;;40213:4;;-1:-1:-1;40058:133:73;;-1:-1:-1;40205:32:73;-1:-1:-1;40205:32:73;;-1:-1:-1;;;;;;40205:32:73;39893:556;40275:20;40258:13;:37;;;;;;;;;40254:195;;;40319:5;40326:1;40311:50;;;;;;;;;;;;;;;;;;;;;;;;;;;;40254:195;40400:5;40407:1;40392:46;;;;;;;;;;;;;;;;;;;;;;;;;;39247:1208;;;;;;;;:::o;1302:507:69:-;1464:19;;;;;;;;;:15;;;:19;;;:33;;1484:12;1464:19;:33::i;:::-;1446:51;:15;;;:51;;1539:29;;;;;;;;;;;;:33;;;;;;;;;;;;:47;;1573:12;1539:33;:47::i;:::-;1507:18;:29;;;;;;;;;;:79;;;;1626:16;;;;1613:29;;;;;:126;;-1:-1:-1;1721:16:69;;;;1702:18;:36;;;;;;;;;;;1658:81;;;;;;;;;;;:29;;;;;;;;;;:43;;;;;;;;;;;:81;;:43;:81::i;:::-;1596:207;;;-1:-1:-1;1764:16:69;;;;;:28;1302:507::o;1032:135:16:-;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;3033:130:4:-;3091:7;3117:39;3121:1;3124;3117:39;;;;;;;;;;;;;;;;;:3;:39::i;42937:793:73:-;42997:26;;:::i;:::-;43035:18;43056:15;;;:6;:15;;;;;:26;43096:15;43092:159;;-1:-1:-1;;43211:29:73;;;;;;;;;-1:-1:-1;;43211:29:73;;43204:36;;43092:159;43438:44;;:::i;:::-;43485:58;;;;;;;;;;43505:11;;-1:-1:-1;;;43505:37:73;;;43485:58;;;-1:-1:-1;;;;;43505:11:73;:25;:37;43531:10;43505:37;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;43485:58;;43693:15;;;;:6;:15;;;;;;;;;43671:52;;;;;;;;43693:29;;43671:52;;;43438:105;;-1:-1:-1;43671:52:73;;43438:105;;43671:21;:52::i;2576:699:69:-;2713:15;2730:12;2758:40;;:::i;:::-;2801;2837:3;2801:31;2829:2;2801:27;:31::i;:::-;:35;;:40::i;:::-;2869:29;;;;;;;;;:15;;;:29;;;2758:83;;-1:-1:-1;2869:47:69;;2899:16;2869:29;:47::i;:::-;:149;;;;-1:-1:-1;2932:57:69;;;;;;;;;2973:15;;;2932:57;;;2951:16;;;;-1:-1:-1;2932:36:69;;;;;;;;;:40;;;;;;;;;;;;:86;;3004:13;;2932:57;;:40;;:57::i;:::-;:71;;:86::i;:::-;2852:417;;;3167:4;3154:17;;3193:4;:16;;;3185:24;;2852:417;;;3253:5;3240:18;;2852:417;2576:699;;;;;;:::o;4420:128:4:-;4478:7;4504:37;4508:1;4511;4504:37;;;;;;;;;;;;;;;;;:3;:37::i;1846:268:72:-;1934:7;;1975:84;2004:53;1996:62;;1975:84;1953:106;-1:-1:-1;2076:31:72;2092:14;:7;2104:1;2092:11;:14::i;:::-;2076:11;;:15;:31::i;1942:137:16:-;2062:10;2049;;:23;;1942:137::o;3638:338:4:-;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;11534:134:16:-;11600:15;;:::i;:::-;11634:27;;;;;;;;;11643:10;;11634:27;;11643:17;;11658:1;11643:14;:17::i;5012:163:4:-;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;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;2028:804;;2187:3;2180:4;2172:6;2168:17;2164:27;2154:2;;-1:-1;;2195:12;2154:2;2242:6;2229:20;2264:122;2279:106;2378:6;2279:106;:::i;:::-;2264:122;:::i;:::-;2414:21;;;2255:131;-1:-1;2458:4;2471:14;;;;2446:17;;;2566:1;2551:275;2576:6;2573:1;2570:13;2551:275;;;2659:3;2646:17;2450:6;2634:30;8893:4;;8872:19;;2634:30;8876:3;8872:19;;8868:30;8865:2;;;2566:1;;8901:12;8865:2;8929:20;8893:4;8929:20;:::i;:::-;2634:30;;;5580:20;9012:75;;9202:22;;;;12356:20;9163:16;;;9156:75;9314:18;;;9301:32;;-1:-1;;;;;9342:30;;9339:2;;;2566:1;;9375:12;9339:2;9420:58;9474:3;2458:4;9465:6;2634:30;9450:22;;9420:58;:::i;:::-;9402:16;;;9395:84;2671:92;;-1:-1;;2777:14;;;;2805;;;;2598:1;2591:9;2551:275;;;2555:14;;;;;2147:685;;;;:::o;2888:806::-;;3038:3;3031:4;3023:6;3019:17;3015:27;3005:2;;-1:-1;;3046:12;3005:2;3093:6;3080:20;3115:113;3130:97;3220:6;3130:97;:::i;3115:113::-;3256:21;;;3106:122;-1:-1;3300:4;3313:14;;;;3288:17;;;3414:4;3402:17;;;3393:27;;;;3390:36;-1:-1;3387:2;;;3439:1;;3429:12;3387:2;3464:1;;3449:239;3474:6;3471:1;3468:13;3449:239;;;3414:4;9661:9;9656:3;9652:19;9648:30;9645:2;;;3464:1;;9681:12;9645:2;9709:20;3414:4;9709:20;:::i;:::-;5580;;9792:75;;9982:22;;;12356:20;9943:16;;;9936:75;3542:83;;3639:14;;;;3667;;;;3496:1;3489:9;3449:239;;;3453:14;;;;;;;2998:696;;;;:::o;6009:440::-;;6110:3;6103:4;6095:6;6091:17;6087:27;6077:2;;-1:-1;;6118:12;6077:2;6165:6;6152:20;-1:-1;;;;;69632:6;69629:30;69626:2;;;-1:-1;;69662:12;69626:2;6187:64;8872:19;69716:17;;-1:-1;;69712:33;69803:4;69793:15;6187:64;:::i;:::-;6178:73;;6271:6;6264:5;6257:21;6375:3;69803:4;6366:6;6299;6357:16;;6354:25;6351:2;;;6392:1;;6382:12;6351:2;74767:6;69803:4;6299:6;6295:17;69803:4;6333:5;6329:16;74744:30;74823:1;74805:16;;;69803:4;74805:16;74798:27;6333:5;6070:379;-1:-1;;6070:379::o;12567:241::-;;12671:2;12659:9;12650:7;12646:23;12642:32;12639:2;;;-1:-1;;12677:12;12639:2;85:6;72:20;97:33;124:5;97:33;:::i;12815:263::-;;12930:2;12918:9;12909:7;12905:23;12901:32;12898:2;;;-1:-1;;12936:12;12898:2;226:6;220:13;238:33;265:5;238:33;:::i;13085:711::-;;;;13290:2;13278:9;13269:7;13265:23;13261:32;13258:2;;;-1:-1;;13296:12;13258:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;13348:63;-1:-1;13448:2;13487:22;;12356:20;;-1:-1;13584:2;13569:18;;13556:32;-1:-1;;;;;13597:30;;13594:2;;;-1:-1;;13630:12;13594:2;13660:120;13772:7;13763:6;13752:9;13748:22;13660:120;:::i;:::-;13650:130;;;13252:544;;;;;:::o;13803:693::-;;;;13999:2;13987:9;13978:7;13974:23;13970:32;13967:2;;;-1:-1;;14005:12;13967:2;85:6;72:20;97:33;124:5;97:33;:::i;:::-;14057:63;-1:-1;14157:2;14196:22;;12356:20;;-1:-1;14293:2;14278:18;;14265:32;-1:-1;;;;;14306:30;;14303:2;;;-1:-1;;14339:12;14303:2;14369:111;14472:7;14463:6;14452:9;14448:22;14369:111;:::i;14503:453::-;;14670:2;;14658:9;14649:7;14645:23;14641:32;14638:2;;;-1:-1;;14676:12;14638:2;14734:17;14721:31;-1:-1;;;;;14772:18;14764:6;14761:30;14758:2;;;-1:-1;;14794:12;14758:2;14923:6;14912:9;14908:22;;;500:3;493:4;485:6;481:17;477:27;467:2;;-1:-1;;508:12;467:2;555:6;542:20;577:118;592:102;687:6;592:102;:::i;577:118::-;723:21;;;780:14;;;;755:17;;;-1:-1;860:271;885:6;882:1;879:13;860:271;;;968:3;955:17;759:6;943:30;6777:4;;8872:19;;943:30;6760:3;6756:19;;6752:30;6749:2;;;-1:-1;;6785:12;6749:2;6813:20;6777:4;6813:20;:::i;:::-;14670:2;943:30;;5580:20;6903:16;6896:75;7086:22;943:30;7086:22;12356:20;14670:2;7051:5;7047:16;7040:75;7198:18;943:30;7198:18;7185:32;14772:18;7229:6;7226:30;7223:2;;;-1:-1;;7259:12;7223:2;7304:58;7358:3;14670:2;7349:6;943:30;7334:22;;7304:58;:::i;:::-;7086:22;7286:16;;7279:84;-1:-1;7478:22;;;5580:20;7198:18;7439:16;;7432:75;7590:19;;;7577:33;;7619:30;;;7616:2;;;-1:-1;;7652:12;7616:2;7697:58;7751:3;14670:2;7742:6;943:30;7727:22;;7697:58;:::i;:::-;7478:22;7679:16;;7672:84;980:88;;-1:-1;;1082:14;;;;1110;;;;907:1;900:9;860:271;;;-1:-1;14814:126;;14632:324;-1:-1;;;;;;;;;14632:324::o;14963:435::-;;15121:2;;15109:9;15100:7;15096:23;15092:32;15089:2;;;-1:-1;;15127:12;15089:2;15185:17;15172:31;-1:-1;;;;;15223:18;15215:6;15212:30;15209:2;;;-1:-1;;15245:12;15209:2;15365:6;15354:9;15350:22;;;1335:3;1328:4;1320:6;1316:17;1312:27;1302:2;;-1:-1;;1343:12;1302:2;1390:6;1377:20;1412:109;1427:93;1513:6;1427:93;:::i;1412:109::-;1549:21;;;1606:14;;;;1581:17;;;-1:-1;1686:262;1711:6;1708:1;1705:13;1686:262;;;1794:3;1781:17;1585:6;1769:30;7942:4;;8872:19;;1769:30;7925:3;7921:19;;7917:30;7914:2;;;-1:-1;;7950:12;7914:2;7978:20;7942:4;7978:20;:::i;:::-;1769:30;;;5580:20;8061:75;;8251:22;;;;12356:20;8212:16;;;8205:75;8395:22;;;5580:20;8356:16;;;8349:75;8507:18;;;8494:32;;8535:30;;;8532:2;;;-1:-1;;8568:12;8532:2;8613:58;8667:3;15121:2;8658:6;1769:30;8643:22;;8613:58;:::i;:::-;8395:22;8595:16;;8588:84;1806:79;;-1:-1;;1899:14;;;;1927;;;;1733:1;1726:9;1686:262;;15405:461;;15576:2;15564:9;15555:7;15551:23;15547:32;15544:2;;;-1:-1;;15582:12;15544:2;15640:17;15627:31;-1:-1;;;;;15670:6;15667:30;15664:2;;;-1:-1;;15700:12;15664:2;15730:120;15842:7;15833:6;15822:9;15818:22;15730:120;:::i;15873:443::-;;16035:2;16023:9;16014:7;16010:23;16006:32;16003:2;;;-1:-1;;16041:12;16003:2;16099:17;16086:31;-1:-1;;;;;16129:6;16126:30;16123:2;;;-1:-1;;16159:12;16123:2;16189:111;16292:7;16283:6;16272:9;16268:22;16189:111;:::i;16323:445::-;;16486:2;;16474:9;16465:7;16461:23;16457:32;16454:2;;;-1:-1;;16492:12;16454:2;16550:17;16537:31;-1:-1;;;;;16588:18;16580:6;16577:30;16574:2;;;-1:-1;;16610:12;16574:2;16735:6;16724:9;16720:22;;;3911:3;3904:4;3896:6;3892:17;3888:27;3878:2;;-1:-1;;3919:12;3878:2;3966:6;3953:20;3988:114;4003:98;4094:6;4003:98;:::i;3988:114::-;4130:21;;;4187:14;;;;4162:17;;;-1:-1;4267:267;4292:6;4289:1;4286:13;4267:267;;;4375:3;4362:17;4166:6;4350:30;10216:4;;8872:19;;4350:30;10199:3;10195:19;;10191:30;10188:2;;;-1:-1;;10224:12;10188:2;10252:20;10216:4;10252:20;:::i;:::-;4350:30;;;5580:20;10335:75;;10525:22;;;;12356:20;10486:16;;;10479:75;10669:22;;;6523:20;10631:16;;;10624:74;10781:18;;;10768:32;10809:30;;;10806:2;;;-1:-1;;10842:12;10806:2;10887:58;10941:3;16486:2;10932:6;4350:30;10917:22;;10887:58;:::i;:::-;10669:22;10869:16;;10862:84;-1:-1;11061:22;;6523:20;10781:18;11023:16;;11016:74;4387:84;;4485:14;;;;4513;;;;4314:1;4307:9;4267:267;;16775:427;;16929:2;;16917:9;16908:7;16904:23;16900:32;16897:2;;;-1:-1;;16935:12;16897:2;16993:17;16980:31;-1:-1;;;;;17023:6;17020:30;17017:2;;;-1:-1;;17053:12;17017:2;17154:22;;4723:4;4711:17;;4707:27;-1:-1;4697:2;;-1:-1;;4738:12;4697:2;4785:6;4772:20;4807:105;4822:89;4904:6;4822:89;:::i;4807:105::-;4940:21;;;4997:14;;;;4972:17;;;5098:4;5086:17;;;5077:27;;;;5074:36;-1:-1;5071:2;;;-1:-1;;5113:12;5071:2;-1:-1;5139:10;;5133:231;5158:6;5155:1;5152:13;5133:231;;;5098:4;11256:9;11251:3;11247:19;11243:30;11240:2;;;-1:-1;;11276:12;11240:2;11304:20;5098:4;11304:20;:::i;:::-;5580;;11387:75;;11577:22;;;12356:20;11538:16;;;11531:75;11668:2;11721:22;;;6523:20;11683:16;;;11676:74;11811:2;11864:22;;;6523:20;11826:16;;;11819:74;5226:75;;5180:1;5173:9;;;;;5315:14;;;;5343;;;;5133:231;;;-1:-1;17073:113;;16891:311;-1:-1;;;;;;;;16891:311::o;17209:257::-;;17321:2;17309:9;17300:7;17296:23;17292:32;17289:2;;;-1:-1;;17327:12;17289:2;5459:6;5453:13;76202:5;72827:13;72820:21;76180:5;76177:32;76167:2;;-1:-1;;76213:12;17473:366;;;17594:2;17582:9;17573:7;17569:23;17565:32;17562:2;;;-1:-1;;17600:12;17562:2;-1:-1;;5580:20;;;17752:2;17791:22;;;12356:20;;-1:-1;17556:283::o;17846:491::-;;;;17984:2;17972:9;17963:7;17959:23;17955:32;17952:2;;;-1:-1;;17990:12;17952:2;-1:-1;;5580:20;;;18142:2;18181:22;;12356:20;;-1:-1;18250:2;18289:22;;;5580:20;;17946:391;-1:-1;17946:391::o;18344:721::-;;;;;18508:3;18496:9;18487:7;18483:23;18479:33;18476:2;;;-1:-1;;18515:12;18476:2;5593:6;5580:20;18567:63;;18667:2;18710:9;18706:22;12356:20;18675:63;;18775:2;18818:9;18814:22;5580:20;18783:63;;18911:2;18900:9;18896:18;18883:32;-1:-1;;;;;18927:6;18924:30;18921:2;;;-1:-1;;18957:12;18921:2;18987:62;19041:7;19032:6;19021:9;19017:22;18987:62;:::i;:::-;18977:72;;;18470:595;;;;;;;:::o;19072:::-;;;;19219:2;19207:9;19198:7;19194:23;19190:32;19187:2;;;-1:-1;;19225:12;19187:2;5593:6;5580:20;19277:63;;19377:2;19420:9;19416:22;12356:20;19385:63;;19513:2;19502:9;19498:18;19485:32;-1:-1;;;;;19529:6;19526:30;19523:2;;;-1:-1;;19559:12;19523:2;19589:62;19643:7;19634:6;19623:9;19619:22;19589:62;:::i;19674:721::-;;;;;19838:3;19826:9;19817:7;19813:23;19809:33;19806:2;;;-1:-1;;19845:12;19806:2;5593:6;5580:20;19897:63;;19997:2;20040:9;20036:22;12356:20;20005:63;;20133:2;20122:9;20118:18;20105:32;-1:-1;;;;;20149:6;20146:30;20143:2;;;-1:-1;;20179:12;20143:2;20209:62;20263:7;20254:6;20243:9;20239:22;20209:62;:::i;:::-;19800:595;;;;-1:-1;20199:72;;20308:2;20347:22;5580:20;;-1:-1;;;19800:595::o;20402:951::-;;;;;;20592:3;20580:9;20571:7;20567:23;20563:33;20560:2;;;-1:-1;;20599:12;20560:2;5593:6;5580:20;20651:63;;20751:2;20794:9;20790:22;12356:20;20759:63;;20887:2;20876:9;20872:18;20859:32;-1:-1;;;;;20911:18;20903:6;20900:30;20897:2;;;-1:-1;;20933:12;20897:2;20963:62;21017:7;21008:6;20997:9;20993:22;20963:62;:::i;:::-;20953:72;;21062:2;21105:9;21101:22;5580:20;21070:63;;21198:3;21187:9;21183:19;21170:33;21156:47;;20911:18;21215:6;21212:30;21209:2;;;-1:-1;;21245:12;21209:2;;21275:62;21329:7;21320:6;21309:9;21305:22;21275:62;:::i;:::-;21265:72;;;20554:799;;;;;;;;:::o;21360:843::-;;;;;;21539:3;21527:9;21518:7;21514:23;21510:33;21507:2;;;-1:-1;;21546:12;21507:2;5593:6;5580:20;21598:63;;21698:2;21741:9;21737:22;12356:20;21706:63;;21806:2;21848:9;21844:22;6523:20;21814:62;;21941:2;21930:9;21926:18;21913:32;-1:-1;;;;;21957:6;21954:30;21951:2;;;-1:-1;;21987:12;21951:2;22017:62;22071:7;22062:6;22051:9;22047:22;22017:62;:::i;:::-;21501:702;;;;-1:-1;21501:702;;22116:3;22155:22;6523:20;;21501:702;-1:-1;;;21501:702::o;22210:613::-;;;;;22363:3;22351:9;22342:7;22338:23;22334:33;22331:2;;;-1:-1;;22370:12;22331:2;-1:-1;;5580:20;;;22522:2;22561:22;;12356:20;;-1:-1;22630:2;22668:22;;6523:20;;22737:2;22775:22;6523:20;;-1:-1;22325:498;-1:-1;22325:498::o;22830:365::-;;;22953:2;22941:9;22932:7;22928:23;22924:32;22921:2;;;-1:-1;;22959:12;22921:2;23017:17;23004:31;-1:-1;;;;;23055:18;23047:6;23044:30;23041:2;;;-1:-1;;23077:12;23041:2;23162:6;23151:9;23147:22;;;5778:3;5771:4;5763:6;5759:17;5755:27;5745:2;;-1:-1;;5786:12;5745:2;5829:6;5816:20;23055:18;5848:6;5845:30;5842:2;;;-1:-1;;5878:12;5842:2;5973:3;22953:2;5953:17;5914:6;5939:32;;5936:41;5933:2;;;-1:-1;;5980:12;5933:2;22953;5910:17;;;;;23097:82;;-1:-1;22915:280;;-1:-1;;;;22915:280::o;23202:293::-;;23332:2;23320:9;23311:7;23307:23;23303:32;23300:2;;;-1:-1;;23338:12;23300:2;12105:20;23332:2;12105:20;:::i;:::-;12356;;12186:75;;-1:-1;12193:16;23294:201;-1:-1;23294:201::o;23502:241::-;;23606:2;23594:9;23585:7;23581:23;23577:32;23574:2;;;-1:-1;;23612:12;23574:2;-1:-1;12356:20;;23568:175;-1:-1;23568:175::o;23750:263::-;;23865:2;23853:9;23844:7;23840:23;23836:32;23833:2;;;-1:-1;;23871:12;23833:2;-1:-1;12504:13;;23827:186;-1:-1;23827:186::o;27950:323::-;;28082:5;70523:12;71472:6;71467:3;71460:19;28165:52;28210:6;71509:4;71504:3;71500:14;71509:4;28191:5;28187:16;28165:52;:::i;:::-;8872:19;75632:14;-1:-1;;75628:28;28229:39;;;;71509:4;28229:39;;28030:243;-1:-1;;28030:243::o;43795:1129::-;;27652:5;27629:3;27622:37;27652:5;44216:2;44211:3;44207:12;27622:37;75739:14;;24976:5;75743:2;75739:14;;44316:12;44211:3;44316:12;24910:74;27652:5;44443:12;44211:3;44443:12;27622:37;28790:5;70523:12;28901:52;28946:6;44554:12;44211:3;44554:12;44216:2;28927:5;28923:16;28901:52;:::i;:::-;28965:16;;;44554:12;28965:16;;27622:37;;;;-1:-1;44776:12;;;27622:37;44887:12;;;;-1:-1;;;;;44109:815::o;44931:520::-;32070:66;32050:87;;32034:2;32156:12;;27622:37;;;;45414:12;;;45148:303::o;45458:222::-;-1:-1;;;;;73358:54;;;;25067:37;;45585:2;45570:18;;45556:124::o;45932:349::-;-1:-1;;;;;73358:54;;;;24735:58;;46267:2;46252:18;;27622:37;46095:2;46080:18;;46066:215::o;46628:538::-;46889:2;46903:47;;;70523:12;;46874:18;;;71460:19;;;46628:538;;46889:2;71500:14;;;;;;25762:17;;;25753:27;;;;69982:14;;;46628:538;25932:456;25957:6;25954:1;25951:13;25932:456;;;26009:20;;;-1:-1;;26009:20;25997:33;;26058:13;;41794:23;;27622:37;;41954:16;;;41948:23;42025:14;;;27622:37;42117:16;;42111:23;41722:4;42154:14;;;42147:38;;;42200:71;41713:14;;;42111:23;42200:71;:::i;:::-;26367:14;;;;26078:156;-1:-1;;;71127:14;;;;25979:1;25972:9;25932:456;;47173:494;47412:2;47426:47;;;70523:12;;47397:18;;;71460:19;;;47173:494;;47412:2;71500:14;;;;;;69982;;;47173:494;27067:353;27092:6;27089:1;27086:13;27067:353;;;27159:6;27153:13;42598:16;42592:23;75968:1;75961:5;75958:12;75948:2;;75974:9;75948:2;30246:67;;42780:16;;42774:23;42851:14;;;27622:37;24628:14;;;;71127;;;;27114:1;27107:9;27067:353;;;-1:-1;47479:178;;47383:284;-1:-1;;;;;;;47383:284::o;47674:210::-;72827:13;;72820:21;27515:34;;47795:2;47780:18;;47766:118::o;47891:222::-;27622:37;;;48018:2;48003:18;;47989:124::o;48120:528::-;;27652:5;27629:3;27622:37;27652:5;48485:2;48474:9;48470:18;27622:37;48321:2;48522;48511:9;48507:18;48500:48;48562:76;48321:2;48310:9;48306:18;48624:6;48562:76;:::i;48655:548::-;27622:37;;;73574:4;73563:16;;;;49023:2;49008:18;;43748:35;49106:2;49091:18;;27622:37;49189:2;49174:18;;27622:37;48862:3;48847:19;;48833:370::o;49481:240::-;49617:2;49602:18;;75851:1;75841:12;;75831:2;;75857:9;75831:2;30097:59;;;49588:133;:::o;49953:310::-;;50100:2;50121:17;50114:47;50175:78;50100:2;50089:9;50085:18;50239:6;50175:78;:::i;50270:416::-;50470:2;50484:47;;;31325:2;50455:18;;;71460:19;31361:26;71500:14;;;31341:47;31407:12;;;50441:245::o;50693:416::-;50893:2;50907:47;;;31658:2;50878:18;;;71460:19;31694:33;71500:14;;;31674:54;31747:12;;;50864:245::o;51116:416::-;51316:2;51330:47;;;32407:2;51301:18;;;71460:19;32443:30;71500:14;;;32423:51;32493:12;;;51287:245::o;51539:416::-;51739:2;51753:47;;;32744:2;51724:18;;;71460:19;32780:34;71500:14;;;32760:55;-1:-1;;;32835:12;;;32828:30;32877:12;;;51710:245::o;51962:416::-;52162:2;52176:47;;;33128:2;52147:18;;;71460:19;33164:29;71500:14;;;33144:50;33213:12;;;52133:245::o;52385:416::-;52585:2;52599:47;;;33464:2;52570:18;;;71460:19;33500:31;71500:14;;;33480:52;33551:12;;;52556:245::o;52808:416::-;53008:2;53022:47;;;33802:2;52993:18;;;71460:19;33838:34;71500:14;;;33818:55;-1:-1;;;33893:12;;;33886:26;33931:12;;;52979:245::o;53231:416::-;53431:2;53445:47;;;34182:2;53416:18;;;71460:19;34218:29;71500:14;;;34198:50;34267:12;;;53402:245::o;53654:416::-;53854:2;53868:47;;;34518:2;53839:18;;;71460:19;-1:-1;;;71500:14;;;34534:38;34591:12;;;53825:245::o;54077:416::-;54277:2;54291:47;;;34842:2;54262:18;;;71460:19;34878:34;71500:14;;;34858:55;-1:-1;;;34933:12;;;34926:26;34971:12;;;54248:245::o;54500:416::-;54700:2;54714:47;;;35222:2;54685:18;;;71460:19;-1:-1;;;71500:14;;;35238:45;35302:12;;;54671:245::o;54923:416::-;55123:2;55137:47;;;35553:2;55108:18;;;71460:19;35589:32;71500:14;;;35569:53;35641:12;;;55094:245::o;55346:416::-;55546:2;55560:47;;;35892:2;55531:18;;;71460:19;35928:34;71500:14;;;35908:55;-1:-1;;;35983:12;;;35976:25;36020:12;;;55517:245::o;55769:416::-;55969:2;55983:47;;;55954:18;;;71460:19;36307:34;71500:14;;;36287:55;36361:12;;;55940:245::o;56192:416::-;56392:2;56406:47;;;36612:2;56377:18;;;71460:19;36648:32;71500:14;;;36628:53;36700:12;;;56363:245::o;56615:416::-;56815:2;56829:47;;;36951:2;56800:18;;;71460:19;36987:29;71500:14;;;36967:50;37036:12;;;56786:245::o;57038:416::-;57238:2;57252:47;;;37287:2;57223:18;;;71460:19;37323:30;71500:14;;;37303:51;37373:12;;;57209:245::o;57461:416::-;57661:2;57675:47;;;37624:2;57646:18;;;71460:19;-1:-1;;;71500:14;;;37640:44;37703:12;;;57632:245::o;57884:416::-;58084:2;58098:47;;;37954:2;58069:18;;;71460:19;-1:-1;;;71500:14;;;37970:42;38031:12;;;58055:245::o;58307:416::-;58507:2;58521:47;;;38282:2;58492:18;;;71460:19;38318:26;71500:14;;;38298:47;38364:12;;;58478:245::o;58730:416::-;58930:2;58944:47;;;38615:2;58915:18;;;71460:19;38651:31;71500:14;;;38631:52;38702:12;;;58901:245::o;59153:416::-;59353:2;59367:47;;;59338:18;;;71460:19;38989:34;71500:14;;;38969:55;39043:12;;;59324:245::o;59576:416::-;59776:2;59790:47;;;39294:2;59761:18;;;71460:19;39330:27;71500:14;;;39310:48;39377:12;;;59747:245::o;59999:416::-;60199:2;60213:47;;;39628:2;60184:18;;;71460:19;39664:31;71500:14;;;39644:52;39715:12;;;60170:245::o;60422:416::-;60622:2;60636:47;;;39966:2;60607:18;;;71460:19;40002:32;71500:14;;;39982:53;40054:12;;;60593:245::o;60845:416::-;61045:2;61059:47;;;40305:2;61030:18;;;71460:19;-1:-1;;;71500:14;;;40321:44;40384:12;;;61016:245::o;61268:416::-;61468:2;61482:47;;;40635:2;61453:18;;;71460:19;40671:31;71500:14;;;40651:52;40722:12;;;61439:245::o;61691:416::-;61891:2;61905:47;;;40973:2;61876:18;;;71460:19;41009:31;71500:14;;;40989:52;41060:12;;;61862:245::o;62114:416::-;62314:2;62328:47;;;41311:2;62299:18;;;71460:19;41347:33;71500:14;;;41327:54;41400:12;;;62285:245::o;62537:326::-;43172:23;;27622:37;;62716:2;62701:18;;62687:176::o;63099:417::-;;27652:5;27629:3;27622:37;63272:2;63390;63379:9;63375:18;63368:48;63430:76;63272:2;63261:9;63257:18;63492:6;63430:76;:::i;63523:612::-;;27652:5;27629:3;27622:37;63742:2;63860;63849:9;63845:18;63838:48;63900:76;63742:2;63731:9;63727:18;63962:6;63900:76;:::i;:::-;64024:9;64018:4;64014:20;64009:2;63998:9;63994:18;63987:48;64049:76;64120:4;64111:6;64049:76;:::i;64142:544::-;;27652:5;27629:3;27622:37;64351:2;64469;64458:9;64454:18;64447:48;64509:76;64351:2;64340:9;64336:18;64571:6;64509:76;:::i;:::-;64501:84;;30727:5;64672:2;64661:9;64657:18;30676:58;64322:364;;;;;;:::o;65228:636::-;;27652:5;27629:3;27622:37;27652:5;65618:2;65607:9;65603:18;27622:37;65455:3;65655:2;65644:9;65640:18;65633:48;65695:76;65455:3;65444:9;65440:19;65757:6;65695:76;:::i;:::-;65687:84;;27652:5;65850:2;65839:9;65835:18;27622:37;65426:438;;;;;;;:::o;65871:518::-;;66067:2;66056:9;66052:18;27652:5;27629:3;27622:37;66229:2;27652:5;66229:2;66218:9;66214:18;27622:37;66067:2;66266;66255:9;66251:18;66244:48;-1:-1;29133:5;29127:12;29167:1;;29156:9;29152:17;29180:1;29175:247;;;;29433:1;29428:400;;;;29145:683;;29175:247;29272:70;29253:4;29249:1;29238:9;29234:17;29230:28;29330:3;29272:70;:::i;:::-;-1:-1;;29361:25;;29349:38;;29401:14;;;-1:-1;29175:247;;29428:400;29497:1;29486:9;29482:17;29513:70;29576:6;29571:3;29513:70;:::i;:::-;29605:37;29636:5;29605:37;:::i;:::-;-1:-1;29666:130;29680:6;29677:1;29674:13;29666:130;;;29739:14;;29726:11;;;29719:35;29773:15;;;;29695:12;;29666:130;;;29810:11;;;;;-1:-1;;;29145:683;-1:-1;66298:81;;66038:351;-1:-1;;;;;;;;;66038:351::o;66396:764::-;27622:37;;;43172:23;;66928:2;66913:18;;27622:37;43172:23;67063:2;67048:18;;27622:37;67146:2;67131:18;;27622:37;66711:3;66696:19;;66682:478::o;67167:256::-;67229:2;67223:9;67255:17;;;-1:-1;;;;;67315:34;;67351:22;;;67312:62;67309:2;;;67387:1;;67377:12;67309:2;67229;67396:22;67207:216;;-1:-1;67207:216::o;67430:342::-;;-1:-1;;;;;67619:6;67616:30;67613:2;;;-1:-1;;67649:12;67613:2;-1:-1;67694:4;67682:17;;;67747:15;;67550:222::o;70214:157::-;;70308:14;;;70350:4;70337:18;;;70267:104::o;74840:268::-;74905:1;74912:101;74926:6;74923:1;74920:13;74912:101;;;74993:11;;;74987:18;74974:11;;;74967:39;74948:2;74941:10;74912:101;;;75028:6;75025:1;75022:13;75019:2;;;-1:-1;;74905:1;75075:16;;75068:27;74889:219::o;75997:117::-;-1:-1;;;;;73358:54;;76056:35;;76046:2;;76105:1;;76095:12;76046:2;76040:74;:::o
Swarm Source
ipfs://22f7fc478ff7feaaea6add2866b0175f606be8ef9f88a726e829e809654fef6b
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ 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.