mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			193 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
pragma solidity ^0.4.11;
 | 
						|
import "../Oracles/Oracle.sol";
 | 
						|
import "../Tokens/Token.sol";
 | 
						|
import "../Utils/Math.sol";
 | 
						|
 | 
						|
 | 
						|
/// @title Ultimate oracle contract - Allows to swap oracle result for ultimate oracle result
 | 
						|
/// @author Stefan George - <stefan@gnosis.pm>
 | 
						|
contract UltimateOracle is Oracle {
 | 
						|
    using Math for *;
 | 
						|
 | 
						|
    /*
 | 
						|
     *  Events
 | 
						|
     */
 | 
						|
    event ForwardedOracleOutcomeAssignment(int outcome);
 | 
						|
    event OutcomeChallenge(address indexed sender, int outcome);
 | 
						|
    event OutcomeVote(address indexed sender, int outcome, uint amount);
 | 
						|
    event Withdrawal(address indexed sender, uint amount);
 | 
						|
 | 
						|
    /*
 | 
						|
     *  Storage
 | 
						|
     */
 | 
						|
    Oracle public forwardedOracle;
 | 
						|
    Token public collateralToken;
 | 
						|
    uint8 public spreadMultiplier;
 | 
						|
    uint public challengePeriod;
 | 
						|
    uint public challengeAmount;
 | 
						|
    uint public frontRunnerPeriod;
 | 
						|
 | 
						|
    int public forwardedOutcome;
 | 
						|
    uint public forwardedOutcomeSetTimestamp;
 | 
						|
    int public frontRunner;
 | 
						|
    uint public frontRunnerSetTimestamp;
 | 
						|
 | 
						|
    uint public totalAmount;
 | 
						|
    mapping (int => uint) public totalOutcomeAmounts;
 | 
						|
    mapping (address => mapping (int => uint)) public outcomeAmounts;
 | 
						|
 | 
						|
    /*
 | 
						|
     *  Public functions
 | 
						|
     */
 | 
						|
    /// @dev Constructor sets ultimate oracle properties
 | 
						|
    /// @param _forwardedOracle Oracle address
 | 
						|
    /// @param _collateralToken Collateral token address
 | 
						|
    /// @param _spreadMultiplier Defines the spread as a multiple of the money bet on other outcomes
 | 
						|
    /// @param _challengePeriod Time to challenge oracle outcome
 | 
						|
    /// @param _challengeAmount Amount to challenge the outcome
 | 
						|
    /// @param _frontRunnerPeriod Time to overbid the front-runner
 | 
						|
    function UltimateOracle(
 | 
						|
        Oracle _forwardedOracle,
 | 
						|
        Token _collateralToken,
 | 
						|
        uint8 _spreadMultiplier,
 | 
						|
        uint _challengePeriod,
 | 
						|
        uint _challengeAmount,
 | 
						|
        uint _frontRunnerPeriod
 | 
						|
    )
 | 
						|
        public
 | 
						|
    {
 | 
						|
        // Validate inputs
 | 
						|
        require(   address(_forwardedOracle) != 0
 | 
						|
                && address(_collateralToken) != 0
 | 
						|
                && _spreadMultiplier >= 2
 | 
						|
                && _challengePeriod > 0
 | 
						|
                && _challengeAmount > 0
 | 
						|
                && _frontRunnerPeriod > 0);
 | 
						|
        forwardedOracle = _forwardedOracle;
 | 
						|
        collateralToken = _collateralToken;
 | 
						|
        spreadMultiplier = _spreadMultiplier;
 | 
						|
        challengePeriod = _challengePeriod;
 | 
						|
        challengeAmount = _challengeAmount;
 | 
						|
        frontRunnerPeriod = _frontRunnerPeriod;
 | 
						|
    }
 | 
						|
 | 
						|
    /// @dev Allows to set oracle outcome
 | 
						|
    function setForwardedOutcome()
 | 
						|
        public
 | 
						|
    {
 | 
						|
        // There was no challenge and the outcome was not set yet in the ultimate oracle but in the forwarded oracle
 | 
						|
        require(   !isChallenged()
 | 
						|
                && forwardedOutcomeSetTimestamp == 0
 | 
						|
                && forwardedOracle.isOutcomeSet());
 | 
						|
        forwardedOutcome = forwardedOracle.getOutcome();
 | 
						|
        forwardedOutcomeSetTimestamp = now;
 | 
						|
        ForwardedOracleOutcomeAssignment(forwardedOutcome);
 | 
						|
    }
 | 
						|
 | 
						|
    /// @dev Allows to challenge the oracle outcome
 | 
						|
    /// @param _outcome Outcome to bid on
 | 
						|
    function challengeOutcome(int _outcome)
 | 
						|
        public
 | 
						|
    {
 | 
						|
        // There was no challenge yet or the challenge period expired
 | 
						|
        require(   !isChallenged()
 | 
						|
                && !isChallengePeriodOver()
 | 
						|
                && collateralToken.transferFrom(msg.sender, this, challengeAmount));
 | 
						|
        outcomeAmounts[msg.sender][_outcome] = challengeAmount;
 | 
						|
        totalOutcomeAmounts[_outcome] = challengeAmount;
 | 
						|
        totalAmount = challengeAmount;
 | 
						|
        frontRunner = _outcome;
 | 
						|
        frontRunnerSetTimestamp = now;
 | 
						|
        OutcomeChallenge(msg.sender, _outcome);
 | 
						|
    }
 | 
						|
 | 
						|
    /// @dev Allows to challenge the oracle outcome
 | 
						|
    /// @param _outcome Outcome to bid on
 | 
						|
    /// @param amount Amount to bid
 | 
						|
    function voteForOutcome(int _outcome, uint amount)
 | 
						|
        public
 | 
						|
    {
 | 
						|
        uint maxAmount = (totalAmount - totalOutcomeAmounts[_outcome]).mul(spreadMultiplier);
 | 
						|
        if (amount > maxAmount)
 | 
						|
            amount = maxAmount;
 | 
						|
        // Outcome is challenged and front runner period is not over yet and tokens can be transferred
 | 
						|
        require(   isChallenged()
 | 
						|
                && !isFrontRunnerPeriodOver()
 | 
						|
                && collateralToken.transferFrom(msg.sender, this, amount));
 | 
						|
        outcomeAmounts[msg.sender][_outcome] = outcomeAmounts[msg.sender][_outcome].add(amount);
 | 
						|
        totalOutcomeAmounts[_outcome] = totalOutcomeAmounts[_outcome].add(amount);
 | 
						|
        totalAmount = totalAmount.add(amount);
 | 
						|
        if (_outcome != frontRunner && totalOutcomeAmounts[_outcome] > totalOutcomeAmounts[frontRunner])
 | 
						|
        {
 | 
						|
            frontRunner = _outcome;
 | 
						|
            frontRunnerSetTimestamp = now;
 | 
						|
        }
 | 
						|
        OutcomeVote(msg.sender, _outcome, amount);
 | 
						|
    }
 | 
						|
 | 
						|
    /// @dev Withdraws winnings for user
 | 
						|
    /// @return Winnings
 | 
						|
    function withdraw()
 | 
						|
        public
 | 
						|
        returns (uint amount)
 | 
						|
    {
 | 
						|
        // Outcome was challenged and ultimate outcome decided
 | 
						|
        require(isFrontRunnerPeriodOver());
 | 
						|
        amount = totalAmount.mul(outcomeAmounts[msg.sender][frontRunner]) / totalOutcomeAmounts[frontRunner];
 | 
						|
        outcomeAmounts[msg.sender][frontRunner] = 0;
 | 
						|
        // Transfer earnings to contributor
 | 
						|
        require(collateralToken.transfer(msg.sender, amount));
 | 
						|
        Withdrawal(msg.sender, amount);
 | 
						|
    }
 | 
						|
 | 
						|
    /// @dev Checks if time to challenge the outcome is over
 | 
						|
    /// @return Is challenge period over?
 | 
						|
    function isChallengePeriodOver()
 | 
						|
        public
 | 
						|
        returns (bool)
 | 
						|
    {
 | 
						|
        return forwardedOutcomeSetTimestamp != 0 && now.sub(forwardedOutcomeSetTimestamp) > challengePeriod;
 | 
						|
    }
 | 
						|
 | 
						|
    /// @dev Checks if time to overbid the front runner is over
 | 
						|
    /// @return Is front runner period over?
 | 
						|
    function isFrontRunnerPeriodOver()
 | 
						|
        public
 | 
						|
        returns (bool)
 | 
						|
    {
 | 
						|
        return frontRunnerSetTimestamp != 0 && now.sub(frontRunnerSetTimestamp) > frontRunnerPeriod;
 | 
						|
    }
 | 
						|
 | 
						|
    /// @dev Checks if outcome was challenged
 | 
						|
    /// @return Is challenged?
 | 
						|
    function isChallenged()
 | 
						|
        public
 | 
						|
        returns (bool)
 | 
						|
    {
 | 
						|
        return frontRunnerSetTimestamp != 0;
 | 
						|
    }
 | 
						|
 | 
						|
    /// @dev Returns if winning outcome is set
 | 
						|
    /// @return Is outcome set?
 | 
						|
    function isOutcomeSet()
 | 
						|
        public
 | 
						|
        constant
 | 
						|
        returns (bool)
 | 
						|
    {
 | 
						|
        return    isChallengePeriodOver() && !isChallenged()
 | 
						|
               || isFrontRunnerPeriodOver();
 | 
						|
    }
 | 
						|
 | 
						|
    /// @dev Returns winning outcome
 | 
						|
    /// @return Outcome
 | 
						|
    function getOutcome()
 | 
						|
        public
 | 
						|
        constant
 | 
						|
        returns (int)
 | 
						|
    {
 | 
						|
        if (isFrontRunnerPeriodOver())
 | 
						|
            return frontRunner;
 | 
						|
        return forwardedOutcome;
 | 
						|
    }
 | 
						|
}
 |