forked from miladmostavi/gnosis-contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FutarchyOracle.sol
281 lines (264 loc) · 12.2 KB
/
FutarchyOracle.sol
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
pragma solidity ^0.4.0;
import "Oracles/AbstractOracle.sol";
import "EventFactory/AbstractEventFactory.sol";
import "MarketFactories/AbstractMarketFactory.sol";
import "Tokens/AbstractToken.sol";
/// @title Futarchy oracle contract - Allows resolving an event based on other events.
/// @author Stefan George - <stefan.george@consensys.net>
/// @author Martin Koeppelmann - <martin.koeppelmann@consensys.net>
contract FutarchyOracle is Oracle {
/*
* External contracts
*/
EventFactory constant eventFactory = EventFactory({{EventFactory}});
MarketFactory constant marketFactory = MarketFactory({{DefaultMarketFactory}});
Token constant etherToken = Token({{EtherToken}});
address constant marketMaker = {{LMSRMarketMaker}};
/*
* Constants
*/
// Oracle meta data
string constant public name = "Futarchy Oracle";
/*
* Data structures
*/
// proposalHash => FutarchyDecision
mapping (bytes32 => FutarchyDecision) public futarchyDecisions;
struct FutarchyDecision {
bytes32 marketHash1;
bytes32 marketHash2;
uint decisionTime;
bool isWinningOutcomeSet;
int winningOutcome;
}
/*
* Read and write functions
*/
// @dev Contract constructor allows market contract to permanently trade event shares.
function FutarchyOracle() {
// Give permanent approval to market contract to trade event shares
eventFactory.permitPermanentApproval(marketFactory);
}
/// @dev Creates parent and depended eventFactory and marketFactory for futarchy decision. Returns success.
/// @param proposalHash Hash identifying proposal description.
/// @param decisionTime Time when parent event can be resolved.
/// @param lowerBound Lower bound for valid outcomes for depended eventFactory.
/// @param upperBound Upper bound for valid outcomes for depended eventFactory..
/// @param resolverAddress Resolver for depended eventFactory.
/// @param data Encoded data used to resolve event.
/// @param initialFunding Initial funding for marketFactory.
function createFutarchyDecision(bytes32 proposalHash,
uint decisionTime,
int lowerBound,
int upperBound,
address resolverAddress,
bytes32[] data,
uint initialFunding
)
public
{
macro: $futarchyDecision = futarchyDecisions[proposalHash];
if ($futarchyDecision.decisionTime > 0 || now >= decisionTime) {
// Futarchy decision exists already or decision time is in the past
throw;
}
$futarchyDecision.decisionTime = decisionTime;
// Create parent event traded in Ether
bytes32[] memory parentData = new bytes32[](1);
parentData[0] = proposalHash;
bytes32 parentEventHash = eventFactory.createEvent(proposalHash, false, 0, 0, 2, etherToken, this, parentData);
if (parentEventHash == 0) {
// Creation of parent event failed
throw;
}
// Buy all outcomes of parent event to create depended eventFactory
uint buyAllOutcomesCosts = initialFunding + eventFactory.calcBaseFeeForShares(etherToken, initialFunding);
buyAllOutcomesCosts += eventFactory.calcBaseFeeForShares(etherToken, buyAllOutcomesCosts);
if ( !etherToken.transferFrom(msg.sender, this, buyAllOutcomesCosts)
|| !etherToken.approve(eventFactory, buyAllOutcomesCosts))
{
// Buy all outcomes failed
throw;
}
eventFactory.buyAllOutcomes(parentEventHash, buyAllOutcomesCosts);
// Create depended eventFactory traded in parent event shares
bytes32[2] memory dependedEventFactoryHashes = createEventFactory(proposalHash,
lowerBound,
upperBound,
resolverAddress,
data,
parentEventHash);
if (dependedEventFactoryHashes[0] == 0 || dependedEventFactoryHashes[1] == 0) {
// Creation of eventFactory failed
throw;
}
// Create marketFactory based on depended eventFactory
bytes32[2] memory dependedMarketFactoryHashes = createMarketFactory(dependedEventFactoryHashes, initialFunding);
if (dependedMarketFactoryHashes[0] == 0 || dependedMarketFactoryHashes[1] == 0) {
// Creation of marketFactory failed
throw;
}
// Add futarchy decision
$futarchyDecision.marketHash1 = dependedMarketFactoryHashes[0];
$futarchyDecision.marketHash2 = dependedMarketFactoryHashes[1];
$futarchyDecision.decisionTime = decisionTime;
}
function createEventFactory(bytes32 proposalHash,
int lowerBound,
int upperBound,
address resolverAddress,
bytes32[] data,
bytes32 parentEventHash
)
private
returns (bytes32[2] dependedEventFactoryHashes)
{
dependedEventFactoryHashes[0] = eventFactory.createEvent(proposalHash,
true,
lowerBound,
upperBound,
2,
eventFactory.getOutcomeToken(parentEventHash, 0),
resolverAddress,
data);
dependedEventFactoryHashes[1] = eventFactory.createEvent(proposalHash,
true,
lowerBound,
upperBound,
2,
eventFactory.getOutcomeToken(parentEventHash, 1),
resolverAddress,
data);
}
function createMarketFactory(bytes32[2] dependedEventFactoryHashes, uint shareCount)
private
returns (bytes32[2] dependedMarketFactoryHashes)
{
// MarketFactory have no fee
dependedMarketFactoryHashes[0] = marketFactory.createMarket(dependedEventFactoryHashes[0],
0,
shareCount,
marketMaker);
dependedMarketFactoryHashes[1] = marketFactory.createMarket(dependedEventFactoryHashes[1],
0,
shareCount,
marketMaker);
}
/// @dev Sets difficulty as winning outcome for a specific block. Returns success.
/// @param proposalHash Hash identifying proposal description.
/// @param data Not used.
function setOutcome(bytes32 proposalHash, bytes32[] data)
external
{
macro: $futarchyDecision = futarchyDecisions[proposalHash];
if (now < $futarchyDecision.decisionTime || $futarchyDecision.isWinningOutcomeSet) {
// Decision time is not reached yet or outcome was set already
throw;
}
uint[256] memory shareDistributionMarket1 = marketFactory.getShareDistribution($futarchyDecision.marketHash1);
uint[256] memory shareDistributionMarket2 = marketFactory.getShareDistribution($futarchyDecision.marketHash2);
if (int(shareDistributionMarket1[0] - shareDistributionMarket1[1]) > int(shareDistributionMarket2[0] - shareDistributionMarket2[1])) {
$futarchyDecision.winningOutcome = 0;
}
else {
$futarchyDecision.winningOutcome = 1;
}
$futarchyDecision.isWinningOutcomeSet = true;
}
/*
* Read functions
*/
/// @dev Validates and registers event. Returns event identifier.
/// @param data Array of oracle addresses used for event resolution.
/// @return proposalHash Returns proposal hash.
function registerEvent(bytes32[] data)
public
returns (bytes32 proposalHash)
{
proposalHash = data[0];
if (futarchyDecisions[proposalHash].decisionTime == 0) {
// There is no futarchy event
throw;
}
EventRegistration(msg.sender, proposalHash);
}
/// @dev Returns if winning outcome is set.
/// @param proposalHash Hash identifying proposal description.
/// @return isSet Returns if outcome is set.
function isOutcomeSet(bytes32 proposalHash)
constant
public
returns (bool isSet)
{
return futarchyDecisions[proposalHash].isWinningOutcomeSet;
}
/// @dev Returns winning outcome/difficulty for a specific block number.
/// @param proposalHash Hash identifying proposal description.
/// @return outcome Returns outcome.
function getOutcome(bytes32 proposalHash)
constant
public
returns (int outcome)
{
return futarchyDecisions[proposalHash].winningOutcome;
}
/// @dev Returns encoded futarchy decisions associated to proposal hashes.
/// @param proposalHashes Array of hashes identifying proposals.
/// @return allFutarchyDecisions Returns encoded futarchy decisions.
function getFutarchyDecisions(bytes32[] proposalHashes)
constant
public
returns (uint[] allFutarchyDecisions)
{
// Calculate array size
uint arrPos = 0;
for (uint i=0; i<proposalHashes.length; i++) {
macro: $futarchyDecision = futarchyDecisions[proposalHashes[i]];
if ($futarchyDecision.decisionTime > 0) {
arrPos += 6;
}
}
// Fill array
allFutarchyDecisions = new uint[](arrPos);
arrPos = 0;
for (i=0; i<proposalHashes.length; i++) {
macro: $futarchyDecision = futarchyDecisions[proposalHashes[i]];
allFutarchyDecisions[arrPos] = uint(proposalHashes[i]);
allFutarchyDecisions[arrPos + 1] = uint($futarchyDecision.marketHash1);
allFutarchyDecisions[arrPos + 2] = uint($futarchyDecision.marketHash2);
allFutarchyDecisions[arrPos + 3] = $futarchyDecision.decisionTime;
if ($futarchyDecision.isWinningOutcomeSet) {
allFutarchyDecisions[arrPos + 4] = 1;
}
else {
allFutarchyDecisions[arrPos + 4] = 0;
}
allFutarchyDecisions[arrPos + 5] = uint($futarchyDecision.winningOutcome);
}
}
/// @dev Returns data needed to identify an event.
/// @param proposalHash Hash identifying an event.
/// @return data Returns event data.
function getEventData(bytes32 proposalHash)
constant
public
returns (bytes32[] data)
{
data = new bytes32[](3);
data[0] = futarchyDecisions[proposalHash].marketHash1;
data[1] = futarchyDecisions[proposalHash].marketHash2;
data[2] = bytes32(futarchyDecisions[proposalHash].decisionTime);
}
/// @dev Returns total fees for oracle.
/// @param data Event data used for event resolution.
/// @return fee Returns fee.
/// @return token Returns token.
function getFee(bytes32[] data)
constant
public
returns (uint fee, address token)
{
fee = 0;
token = 0;
}
}