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
|
pragma solidity ^0.4.11;
import "../Markets/Market.sol";
import "../Tokens/Token.sol";
import "../Events/Event.sol";
import "../MarketMakers/MarketMaker.sol";
/// @title Market factory contract - Allows to create market contracts
/// @author Stefan George - <stefan@gnosis.pm>
contract StandardMarket is Market {
using Math for *;
/*
* Constants
*/
uint24 public constant FEE_RANGE = 1000000; // 100%
/*
* Modifiers
*/
modifier isCreator () {
// Only creator is allowed to proceed
require(msg.sender == creator);
_;
}
modifier atStage(Stages _stage) {
// Contract has to be in given stage
require(stage == _stage);
_;
}
/*
* Public functions
*/
/// @dev Constructor validates and sets market properties
/// @param _creator Market creator
/// @param _eventContract Event contract
/// @param _marketMaker Market maker contract
/// @param _fee Market fee
constructor(address _creator, Event _eventContract, MarketMaker _marketMaker, uint24 _fee)
public
{
// Validate inputs
require(address(_eventContract) != address(0) && address(_marketMaker) != address(0) && _fee < FEE_RANGE);
creator = _creator;
createdAtBlock = block.number;
eventContract = _eventContract;
netOutcomeTokensSold = new int[](eventContract.getOutcomeCount());
fee = _fee;
marketMaker = _marketMaker;
stage = Stages.MarketCreated;
}
/// @dev Allows to fund the market with collateral tokens converting them into outcome tokens
/// @param _funding Funding amount
function fund(uint _funding)
public
isCreator
atStage(Stages.MarketCreated)
{
// Request collateral tokens and allow event contract to transfer them to buy all outcomes
require( eventContract.collateralToken().transferFrom(msg.sender, this, _funding)
&& eventContract.collateralToken().approve(eventContract, _funding));
eventContract.buyAllOutcomes(_funding);
funding = _funding;
stage = Stages.MarketFunded;
emit MarketFunding(funding);
}
/// @dev Allows market creator to close the markets by transferring all remaining outcome tokens to the creator
function close()
public
isCreator
atStage(Stages.MarketFunded)
{
uint8 outcomeCount = eventContract.getOutcomeCount();
for (uint8 i = 0; i < outcomeCount; i++)
require(eventContract.outcomeTokens(i).transfer(creator, eventContract.outcomeTokens(i).balanceOf(this)));
stage = Stages.MarketClosed;
emit MarketClosing();
}
/// @dev Allows market creator to withdraw fees generated by trades
/// @return Fee amount
function withdrawFees()
public
isCreator
returns (uint fees)
{
fees = eventContract.collateralToken().balanceOf(this);
// Transfer fees
require(eventContract.collateralToken().transfer(creator, fees));
emit FeeWithdrawal(fees);
}
/// @dev Allows to buy outcome tokens from market maker
/// @param outcomeTokenIndex Index of the outcome token to buy
/// @param outcomeTokenCount Amount of outcome tokens to buy
/// @param maxCost The maximum cost in collateral tokens to pay for outcome tokens
/// @return Cost in collateral tokens
function buy(uint8 outcomeTokenIndex, uint outcomeTokenCount, uint maxCost)
public
atStage(Stages.MarketFunded)
returns (uint cost)
{
// Calculate cost to buy outcome tokens
uint outcomeTokenCost = marketMaker.calcCost(this, outcomeTokenIndex, outcomeTokenCount);
// Calculate fees charged by market
uint fees = calcMarketFee(outcomeTokenCost);
cost = outcomeTokenCost.add(fees);
// Check cost doesn't exceed max cost
require(cost > 0 && cost <= maxCost);
// Transfer tokens to markets contract and buy all outcomes
require( eventContract.collateralToken().transferFrom(msg.sender, this, cost)
&& eventContract.collateralToken().approve(eventContract, outcomeTokenCost));
// Buy all outcomes
eventContract.buyAllOutcomes(outcomeTokenCost);
// Transfer outcome tokens to buyer
require(eventContract.outcomeTokens(outcomeTokenIndex).transfer(msg.sender, outcomeTokenCount));
// Add outcome token count to market maker net balance
require(int(outcomeTokenCount) >= 0);
netOutcomeTokensSold[outcomeTokenIndex] = netOutcomeTokensSold[outcomeTokenIndex].add(int(outcomeTokenCount));
emit OutcomeTokenPurchase(msg.sender, outcomeTokenIndex, outcomeTokenCount, cost);
}
/// @dev Allows to sell outcome tokens to market maker
/// @param outcomeTokenIndex Index of the outcome token to sell
/// @param outcomeTokenCount Amount of outcome tokens to sell
/// @param minProfit The minimum profit in collateral tokens to earn for outcome tokens
/// @return Profit in collateral tokens
function sell(uint8 outcomeTokenIndex, uint outcomeTokenCount, uint minProfit)
public
atStage(Stages.MarketFunded)
returns (uint profit)
{
// Calculate profit for selling outcome tokens
uint outcomeTokenProfit = marketMaker.calcProfit(this, outcomeTokenIndex, outcomeTokenCount);
// Calculate fee charged by market
uint fees = calcMarketFee(outcomeTokenProfit);
profit = outcomeTokenProfit.sub(fees);
// Check profit is not too low
require(profit > 0 && profit >= minProfit);
// Transfer outcome tokens to markets contract to sell all outcomes
require(eventContract.outcomeTokens(outcomeTokenIndex).transferFrom(msg.sender, this, outcomeTokenCount));
// Sell all outcomes
eventContract.sellAllOutcomes(outcomeTokenProfit);
// Transfer profit to seller
require(eventContract.collateralToken().transfer(msg.sender, profit));
// Subtract outcome token count from market maker net balance
require(int(outcomeTokenCount) >= 0);
netOutcomeTokensSold[outcomeTokenIndex] = netOutcomeTokensSold[outcomeTokenIndex].sub(int(outcomeTokenCount));
emit OutcomeTokenSale(msg.sender, outcomeTokenIndex, outcomeTokenCount, profit);
}
/// @dev Buys all outcomes, then sells all shares of selected outcome which were bought, keeping
/// shares of all other outcome tokens.
/// @param outcomeTokenIndex Index of the outcome token to short sell
/// @param outcomeTokenCount Amount of outcome tokens to short sell
/// @param minProfit The minimum profit in collateral tokens to earn for short sold outcome tokens
/// @return Cost to short sell outcome in collateral tokens
function shortSell(uint8 outcomeTokenIndex, uint outcomeTokenCount, uint minProfit)
public
returns (uint cost)
{
// Buy all outcomes
require( eventContract.collateralToken().transferFrom(msg.sender, this, outcomeTokenCount)
&& eventContract.collateralToken().approve(eventContract, outcomeTokenCount));
eventContract.buyAllOutcomes(outcomeTokenCount);
// Short sell selected outcome
eventContract.outcomeTokens(outcomeTokenIndex).approve(this, outcomeTokenCount);
uint profit = this.sell(outcomeTokenIndex, outcomeTokenCount, minProfit);
cost = outcomeTokenCount - profit;
// Transfer outcome tokens to buyer
uint8 outcomeCount = eventContract.getOutcomeCount();
for (uint8 i = 0; i < outcomeCount; i++)
if (i != outcomeTokenIndex)
require(eventContract.outcomeTokens(i).transfer(msg.sender, outcomeTokenCount));
// Send change back to buyer
require(eventContract.collateralToken().transfer(msg.sender, profit));
emit OutcomeTokenShortSale(msg.sender, outcomeTokenIndex, outcomeTokenCount, cost);
}
/// @dev Calculates fee to be paid to market maker
/// @param outcomeTokenCost Cost for buying outcome tokens
/// @return Fee for trade
function calcMarketFee(uint outcomeTokenCost)
public
view
returns (uint)
{
return outcomeTokenCost * fee / FEE_RANGE;
}
}
|