1pragma solidity >=0.0; 2import "../Oracles/Oracle.sol"; 3import "../Events/EventFactory.sol"; 4import "../Markets/MarketFactory.sol"; 5 6 7/// @title Futarchy oracle contract - Allows to create an oracle based on market behaviour 8/// @author Stefan George - <stefan@gnosis.pm> 9contract FutarchyOracle is Oracle { 10 using Math for *; 11 12 /* 13 * Events 14 */ 15 event FutarchyFunding(uint funding); 16 event FutarchyClosing(); 17 event OutcomeAssignment(uint winningMarketIndex); 18 19 /* 20 * Constants 21 */ 22 uint8 public constant LONG = 1; 23 24 /* 25 * Storage 26 */ 27 address creator; 28 Market[] public markets; 29 CategoricalEvent public categoricalEvent; 30 uint public deadline; 31 uint public winningMarketIndex; 32 bool public isSet; 33 34 /* 35 * Modifiers 36 */ 37 modifier isCreator () { 38 // Only creator is allowed to proceed 39 require(msg.sender == creator); 40 _; 41 } 42 43 /* 44 * Public functions 45 */ 46 /// @dev Constructor creates events and markets for futarchy oracle 47 /// @param _creator Oracle creator 48 /// @param eventFactory Event factory contract 49 /// @param collateralToken Tokens used as collateral in exchange for outcome tokens 50 /// @param oracle Oracle contract used to resolve the event 51 /// @param outcomeCount Number of event outcomes 52 /// @param lowerBound Lower bound for event outcome 53 /// @param upperBound Lower bound for event outcome 54 /// @param marketFactory Market factory contract 55 /// @param marketMaker Market maker contract 56 /// @param fee Market fee 57 /// @param _deadline Decision deadline 58 constructor( 59 address _creator, 60 EventFactory eventFactory, 61 Token collateralToken, 62 Oracle oracle, 63 uint8 outcomeCount, 64 int lowerBound, 65 int upperBound, 66 MarketFactory marketFactory, 67 MarketMaker marketMaker, 68 uint24 fee, 69 uint _deadline 70 ) 71 { 72 // Deadline is in the future 73 require(_deadline > block.timestamp); 74 // Create decision event 75 categoricalEvent = eventFactory.createCategoricalEvent(collateralToken, this, outcomeCount); 76 // Create outcome events 77 for (uint8 i = 0; i < categoricalEvent.getOutcomeCount(); i++) { 78 ScalarEvent scalarEvent = eventFactory.createScalarEvent( 79 categoricalEvent.outcomeTokens(i), 80 oracle, 81 lowerBound, 82 upperBound 83 ); 84 markets.push(marketFactory.createMarket(scalarEvent, marketMaker, fee)); 85 } 86 creator = _creator; 87 deadline = _deadline; 88 } 89 90 /// @dev Funds all markets with equal amount of funding 91 /// @param funding Amount of funding 92 function fund(uint funding) 93 public 94 isCreator 95 { 96 // Buy all outcomes 97 require( categoricalEvent.collateralToken().transferFrom(creator, address(this), funding) 98 && categoricalEvent.collateralToken().approve(address(categoricalEvent), funding)); 99 categoricalEvent.buyAllOutcomes(funding); 100 // Fund each market with outcome tokens from categorical event 101 for (uint8 i = 0; i < markets.length; i++) { 102 Market market = markets[i]; 103 // Approve funding for market 104 require(market.eventContract().collateralToken().approve(address(market), funding)); 105 market.fund(funding); 106 } 107 emit FutarchyFunding(funding); 108 } 109 110 /// @dev Closes market for winning outcome and redeems winnings and sends all collateral tokens to creator 111 function close() 112 public 113 isCreator 114 { 115 // Winning outcome has to be set 116 Market market = markets[uint(getOutcome())]; 117 require(categoricalEvent.isOutcomeSet() && market.eventContract().isOutcomeSet()); 118 // Close market and transfer all outcome tokens from winning outcome to this contract 119 market.close(); 120 market.eventContract().redeemWinnings(); 121 market.withdrawFees(); 122 // Redeem collateral token for winning outcome tokens and transfer collateral tokens to creator 123 categoricalEvent.redeemWinnings(); 124 require(categoricalEvent.collateralToken().transfer(creator, categoricalEvent.collateralToken().balanceOf(address(this)))); 125 emit FutarchyClosing(); 126 } 127 128 /// @dev Allows to set the oracle outcome based on the market with largest long position 129 function setOutcome() 130 public 131 { 132 // Outcome is not set yet and deadline has passed 133 require(!isSet && deadline <= block.timestamp); 134 // Find market with highest marginal price for long outcome tokens 135 uint highestMarginalPrice = markets[0].marketMaker().calcMarginalPrice(markets[0], LONG); 136 uint highestIndex = 0; 137 for (uint8 i = 1; i < markets.length; i++) { 138 uint marginalPrice = markets[i].marketMaker().calcMarginalPrice(markets[i], LONG); 139 if (marginalPrice > highestMarginalPrice) { 140 highestMarginalPrice = marginalPrice; 141 highestIndex = i; 142 } 143 } 144 winningMarketIndex = highestIndex; 145 isSet = true; 146 emit OutcomeAssignment(winningMarketIndex); 147 } 148 149 /// @dev Returns if winning outcome is set 150 /// @return Is outcome set? 151 function isOutcomeSet() 152 public 153 override 154 view 155 returns (bool) 156 { 157 return isSet; 158 } 159 160 /// @dev Returns winning outcome 161 /// @return Outcome 162 function getOutcome() 163 public 164 override 165 view 166 returns (int) 167 { 168 return int(winningMarketIndex); 169 } 170} 171