mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
201 lines
8.4 KiB
Solidity
201 lines
8.4 KiB
Solidity
pragma solidity >=0.0;
|
|
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)
|
|
{
|
|
// 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
|
|
override
|
|
isCreator
|
|
atStage(Stages.MarketCreated)
|
|
{
|
|
// Request collateral tokens and allow event contract to transfer them to buy all outcomes
|
|
require( eventContract.collateralToken().transferFrom(msg.sender, address(this), _funding)
|
|
&& eventContract.collateralToken().approve(address(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()
|
|
override
|
|
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(address(this))));
|
|
stage = Stages.MarketClosed;
|
|
emit MarketClosing();
|
|
}
|
|
|
|
/// @dev Allows market creator to withdraw fees generated by trades
|
|
/// @return fees Fee amount
|
|
function withdrawFees()
|
|
public
|
|
override
|
|
isCreator
|
|
returns (uint fees)
|
|
{
|
|
fees = eventContract.collateralToken().balanceOf(address(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 Cost in collateral tokens
|
|
function buy(uint8 outcomeTokenIndex, uint outcomeTokenCount, uint maxCost)
|
|
public
|
|
override
|
|
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, address(this), cost)
|
|
&& eventContract.collateralToken().approve(address(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 Profit in collateral tokens
|
|
function sell(uint8 outcomeTokenIndex, uint outcomeTokenCount, uint minProfit)
|
|
public
|
|
override
|
|
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, address(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 Cost to short sell outcome in collateral tokens
|
|
function shortSell(uint8 outcomeTokenIndex, uint outcomeTokenCount, uint minProfit)
|
|
public
|
|
override
|
|
returns (uint cost)
|
|
{
|
|
// Buy all outcomes
|
|
require( eventContract.collateralToken().transferFrom(msg.sender, address(this), outcomeTokenCount)
|
|
&& eventContract.collateralToken().approve(address(eventContract), outcomeTokenCount));
|
|
eventContract.buyAllOutcomes(outcomeTokenCount);
|
|
// Short sell selected outcome
|
|
eventContract.outcomeTokens(outcomeTokenIndex).approve(address(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
|
|
override
|
|
view
|
|
returns (uint)
|
|
{
|
|
return outcomeTokenCost * fee / FEE_RANGE;
|
|
}
|
|
}
|