aboutsummaryrefslogtreecommitdiffstats
path: root/test/compilationTests/gnosis/Oracles/FutarchyOracle.sol
blob: 83d10b2ebd587ec2066bec21266cb43d7cc094e8 (plain) (blame)
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);
    }
}