solidity/test/compilationTests/gnosis/Oracles/FutarchyOracle.sol
2020-07-07 12:16:18 +02:00

171 lines
5.6 KiB
Solidity

pragma solidity >=0.0;
import "../Oracles/Oracle.sol";
import "../Events/EventFactory.sol";
import "../Markets/MarketFactory.sol";
/// @title Futarchy oracle contract - Allows to create an oracle based on market behaviour
/// @author Stefan George - <stefan@gnosis.pm>
contract FutarchyOracle is Oracle {
using Math for *;
/*
* Events
*/
event FutarchyFunding(uint funding);
event FutarchyClosing();
event OutcomeAssignment(uint winningMarketIndex);
/*
* Constants
*/
uint8 public constant LONG = 1;
/*
* Storage
*/
address creator;
Market[] public markets;
CategoricalEvent public categoricalEvent;
uint public deadline;
uint public winningMarketIndex;
bool public isSet;
/*
* Modifiers
*/
modifier isCreator () {
// Only creator is allowed to proceed
require(msg.sender == creator);
_;
}
/*
* Public functions
*/
/// @dev Constructor creates events and markets for futarchy oracle
/// @param _creator Oracle creator
/// @param eventFactory Event factory contract
/// @param collateralToken Tokens used as collateral in exchange for outcome tokens
/// @param oracle Oracle contract used to resolve the event
/// @param outcomeCount Number of event outcomes
/// @param lowerBound Lower bound for event outcome
/// @param upperBound Lower bound for event outcome
/// @param marketFactory Market factory contract
/// @param marketMaker Market maker contract
/// @param fee Market fee
/// @param _deadline Decision deadline
constructor(
address _creator,
EventFactory eventFactory,
Token collateralToken,
Oracle oracle,
uint8 outcomeCount,
int lowerBound,
int upperBound,
MarketFactory marketFactory,
MarketMaker marketMaker,
uint24 fee,
uint _deadline
)
{
// Deadline is in the future
require(_deadline > block.timestamp);
// Create decision event
categoricalEvent = eventFactory.createCategoricalEvent(collateralToken, this, outcomeCount);
// Create outcome events
for (uint8 i = 0; i < categoricalEvent.getOutcomeCount(); i++) {
ScalarEvent scalarEvent = eventFactory.createScalarEvent(
categoricalEvent.outcomeTokens(i),
oracle,
lowerBound,
upperBound
);
markets.push(marketFactory.createMarket(scalarEvent, marketMaker, fee));
}
creator = _creator;
deadline = _deadline;
}
/// @dev Funds all markets with equal amount of funding
/// @param funding Amount of funding
function fund(uint funding)
public
isCreator
{
// Buy all outcomes
require( categoricalEvent.collateralToken().transferFrom(creator, address(this), funding)
&& categoricalEvent.collateralToken().approve(address(categoricalEvent), funding));
categoricalEvent.buyAllOutcomes(funding);
// Fund each market with outcome tokens from categorical event
for (uint8 i = 0; i < markets.length; i++) {
Market market = markets[i];
// Approve funding for market
require(market.eventContract().collateralToken().approve(address(market), funding));
market.fund(funding);
}
emit FutarchyFunding(funding);
}
/// @dev Closes market for winning outcome and redeems winnings and sends all collateral tokens to creator
function close()
public
isCreator
{
// Winning outcome has to be set
Market market = markets[uint(getOutcome())];
require(categoricalEvent.isOutcomeSet() && market.eventContract().isOutcomeSet());
// Close market and transfer all outcome tokens from winning outcome to this contract
market.close();
market.eventContract().redeemWinnings();
market.withdrawFees();
// Redeem collateral token for winning outcome tokens and transfer collateral tokens to creator
categoricalEvent.redeemWinnings();
require(categoricalEvent.collateralToken().transfer(creator, categoricalEvent.collateralToken().balanceOf(address(this))));
emit FutarchyClosing();
}
/// @dev Allows to set the oracle outcome based on the market with largest long position
function setOutcome()
public
{
// Outcome is not set yet and deadline has passed
require(!isSet && deadline <= block.timestamp);
// Find market with highest marginal price for long outcome tokens
uint highestMarginalPrice = markets[0].marketMaker().calcMarginalPrice(markets[0], LONG);
uint highestIndex = 0;
for (uint8 i = 1; i < markets.length; i++) {
uint marginalPrice = markets[i].marketMaker().calcMarginalPrice(markets[i], LONG);
if (marginalPrice > highestMarginalPrice) {
highestMarginalPrice = marginalPrice;
highestIndex = i;
}
}
winningMarketIndex = highestIndex;
isSet = true;
emit OutcomeAssignment(winningMarketIndex);
}
/// @dev Returns if winning outcome is set
/// @return Is outcome set?
function isOutcomeSet()
public
override
view
returns (bool)
{
return isSet;
}
/// @dev Returns winning outcome
/// @return Outcome
function getOutcome()
public
override
view
returns (int)
{
return int(winningMarketIndex);
}
}