solidity/test/compilationTests/gnosis/Markets/Campaign.sol
2020-07-07 12:16:18 +02:00

176 lines
5.3 KiB
Solidity

pragma solidity >=0.0;
import "../Events/Event.sol";
import "../Markets/StandardMarketFactory.sol";
import "../Utils/Math.sol";
/// @title Campaign contract - Allows to crowdfund a market
/// @author Stefan George - <stefan@gnosis.pm>
contract Campaign {
using Math for *;
/*
* Events
*/
event CampaignFunding(address indexed sender, uint funding);
event CampaignRefund(address indexed sender, uint refund);
event MarketCreation(Market indexed market);
event MarketClosing();
event FeeWithdrawal(address indexed receiver, uint fees);
/*
* Constants
*/
uint24 public constant FEE_RANGE = 1000000; // 100%
/*
* Storage
*/
Event public eventContract;
MarketFactory public marketFactory;
MarketMaker public marketMaker;
Market public market;
uint24 public fee;
uint public funding;
uint public deadline;
uint public finalBalance;
mapping (address => uint) public contributions;
Stages public stage;
enum Stages {
AuctionStarted,
AuctionSuccessful,
AuctionFailed,
MarketCreated,
MarketClosed
}
/*
* Modifiers
*/
modifier atStage(Stages _stage) {
// Contract has to be in given stage
require(stage == _stage);
_;
}
modifier timedTransitions() {
if (stage == Stages.AuctionStarted && deadline < block.timestamp)
stage = Stages.AuctionFailed;
_;
}
/*
* Public functions
*/
/// @dev Constructor validates and sets campaign properties
/// @param _eventContract Event contract
/// @param _marketFactory Market factory contract
/// @param _marketMaker Market maker contract
/// @param _fee Market fee
/// @param _funding Initial funding for market
/// @param _deadline Campaign deadline
constructor(
Event _eventContract,
MarketFactory _marketFactory,
MarketMaker _marketMaker,
uint24 _fee,
uint _funding,
uint _deadline
)
{
// Validate input
require( address(_eventContract) != address(0)
&& address(_marketFactory) != address(0)
&& address(_marketMaker) != address(0)
&& _fee < FEE_RANGE
&& _funding > 0
&& block.timestamp < _deadline);
eventContract = _eventContract;
marketFactory = _marketFactory;
marketMaker = _marketMaker;
fee = _fee;
funding = _funding;
deadline = _deadline;
}
/// @dev Allows to contribute to required market funding
/// @param amount Amount of collateral tokens
function fund(uint amount)
public
timedTransitions
atStage(Stages.AuctionStarted)
{
uint raisedAmount = eventContract.collateralToken().balanceOf(address(this));
uint maxAmount = funding.sub(raisedAmount);
if (maxAmount < amount)
amount = maxAmount;
// Collect collateral tokens
require(eventContract.collateralToken().transferFrom(msg.sender, address(this), amount));
contributions[msg.sender] = contributions[msg.sender].add(amount);
if (amount == maxAmount)
stage = Stages.AuctionSuccessful;
emit CampaignFunding(msg.sender, amount);
}
/// @dev Withdraws refund amount
/// @return refundAmount Refund amount
function refund()
public
timedTransitions
atStage(Stages.AuctionFailed)
returns (uint refundAmount)
{
refundAmount = contributions[msg.sender];
contributions[msg.sender] = 0;
// Refund collateral tokens
require(eventContract.collateralToken().transfer(msg.sender, refundAmount));
emit CampaignRefund(msg.sender, refundAmount);
}
/// @dev Allows to create market after successful funding
/// @return Market address
function createMarket()
public
timedTransitions
atStage(Stages.AuctionSuccessful)
returns (Market)
{
market = marketFactory.createMarket(eventContract, marketMaker, fee);
require(eventContract.collateralToken().approve(address(market), funding));
market.fund(funding);
stage = Stages.MarketCreated;
emit MarketCreation(market);
return market;
}
/// @dev Allows to withdraw fees from market contract to campaign contract
function closeMarket()
public
atStage(Stages.MarketCreated)
{
// Winning outcome should be set
require(eventContract.isOutcomeSet());
market.close();
market.withdrawFees();
eventContract.redeemWinnings();
finalBalance = eventContract.collateralToken().balanceOf(address(this));
stage = Stages.MarketClosed;
emit MarketClosing();
}
/// @dev Allows to withdraw fees from campaign contract to contributor
/// @return fees Fee amount
function withdrawFees()
public
atStage(Stages.MarketClosed)
returns (uint fees)
{
fees = finalBalance.mul(contributions[msg.sender]) / funding;
contributions[msg.sender] = 0;
// Send fee share to contributor
require(eventContract.collateralToken().transfer(msg.sender, fees));
emit FeeWithdrawal(msg.sender, fees);
}
}