1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
pragma solidity ^0.4.11;
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
)
public
{
// Deadline is in the future
require(_deadline > now);
// 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 <= now);
// 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
view
returns (bool)
{
return isSet;
}
/// @dev Returns winning outcome
/// @return Outcome
function getOutcome()
public
view
returns (int)
{
return int(winningMarketIndex);
}
}
|