mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
197 lines
6.7 KiB
Solidity
197 lines
6.7 KiB
Solidity
pragma solidity >=0.0;
|
|
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
|
|
constructor(
|
|
Oracle _forwardedOracle,
|
|
Token _collateralToken,
|
|
uint8 _spreadMultiplier,
|
|
uint _challengePeriod,
|
|
uint _challengeAmount,
|
|
uint _frontRunnerPeriod
|
|
)
|
|
{
|
|
// Validate inputs
|
|
require( address(_forwardedOracle) != address(0)
|
|
&& address(_collateralToken) != address(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 = block.timestamp;
|
|
emit 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, address(this), challengeAmount));
|
|
outcomeAmounts[msg.sender][_outcome] = challengeAmount;
|
|
totalOutcomeAmounts[_outcome] = challengeAmount;
|
|
totalAmount = challengeAmount;
|
|
frontRunner = _outcome;
|
|
frontRunnerSetTimestamp = block.timestamp;
|
|
emit 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, address(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 = block.timestamp;
|
|
}
|
|
emit OutcomeVote(msg.sender, _outcome, amount);
|
|
}
|
|
|
|
/// @dev Withdraws winnings for user
|
|
/// @return amount 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));
|
|
emit Withdrawal(msg.sender, amount);
|
|
}
|
|
|
|
/// @dev Checks if time to challenge the outcome is over
|
|
/// @return Is challenge period over?
|
|
function isChallengePeriodOver()
|
|
public
|
|
view
|
|
returns (bool)
|
|
{
|
|
return forwardedOutcomeSetTimestamp != 0 && (block.timestamp).sub(forwardedOutcomeSetTimestamp) > challengePeriod;
|
|
}
|
|
|
|
/// @dev Checks if time to overbid the front runner is over
|
|
/// @return Is front runner period over?
|
|
function isFrontRunnerPeriodOver()
|
|
public
|
|
view
|
|
returns (bool)
|
|
{
|
|
return frontRunnerSetTimestamp != 0 && (block.timestamp).sub(frontRunnerSetTimestamp) > frontRunnerPeriod;
|
|
}
|
|
|
|
/// @dev Checks if outcome was challenged
|
|
/// @return Is challenged?
|
|
function isChallenged()
|
|
public
|
|
view
|
|
returns (bool)
|
|
{
|
|
return frontRunnerSetTimestamp != 0;
|
|
}
|
|
|
|
/// @dev Returns if winning outcome is set
|
|
/// @return Is outcome set?
|
|
function isOutcomeSet()
|
|
public
|
|
override
|
|
view
|
|
returns (bool)
|
|
{
|
|
return isChallengePeriodOver() && !isChallenged()
|
|
|| isFrontRunnerPeriodOver();
|
|
}
|
|
|
|
/// @dev Returns winning outcome
|
|
/// @return Outcome
|
|
function getOutcome()
|
|
public
|
|
override
|
|
view
|
|
returns (int)
|
|
{
|
|
if (isFrontRunnerPeriodOver())
|
|
return frontRunner;
|
|
return forwardedOutcome;
|
|
}
|
|
}
|