Permalink
92 lines (78 sloc) 2.63 KB
pragma solidity ^0.4.24;
import "../../math/SafeMath.sol";
import "./FinalizableCrowdsale.sol";
import "../../payment/escrow/RefundEscrow.sol";
/**
* @title RefundableCrowdsale
* @dev Extension of Crowdsale contract that adds a funding goal, and
* the possibility of users getting a refund if goal is not met.
* WARNING: note that if you allow tokens to be traded before the goal
* is met, then an attack is possible in which the attacker purchases
* tokens from the crowdsale and when they sees that the goal is
* unlikely to be met, they sell their tokens (possibly at a discount).
* The attacker will be refunded when the crowdsale is finalized, and
* the users that purchased from them will be left with worthless
* tokens. There are many possible ways to avoid this, like making the
* the crowdsale inherit from PostDeliveryCrowdsale, or imposing
* restrictions on token trading until the crowdsale is finalized.
* This is being discussed in
* https://github.com/OpenZeppelin/openzeppelin-solidity/issues/877
* This contract will be updated when we agree on a general solution
* for this problem.
*/
contract RefundableCrowdsale is FinalizableCrowdsale {
using SafeMath for uint256;
// minimum amount of funds to be raised in weis
uint256 private _goal;
// refund escrow used to hold funds while crowdsale is running
RefundEscrow private _escrow;
/**
* @dev Constructor, creates RefundEscrow.
* @param goal Funding goal
*/
constructor(uint256 goal) internal {
require(goal > 0);
_escrow = new RefundEscrow(wallet());
_goal = goal;
}
/**
* @return minimum amount of funds to be raised in wei.
*/
function goal() public view returns(uint256) {
return _goal;
}
/**
* @dev Investors can claim refunds here if crowdsale is unsuccessful
* @param refundee Whose refund will be claimed.
*/
function claimRefund(address refundee) public {
require(finalized());
require(!goalReached());
_escrow.withdraw(refundee);
}
/**
* @dev Checks whether funding goal was reached.
* @return Whether funding goal was reached
*/
function goalReached() public view returns (bool) {
return weiRaised() >= _goal;
}
/**
* @dev escrow finalization task, called when finalize() is called
*/
function _finalization() internal {
if (goalReached()) {
_escrow.close();
_escrow.beneficiaryWithdraw();
} else {
_escrow.enableRefunds();
}
super._finalization();
}
/**
* @dev Overrides Crowdsale fund forwarding, sending funds to escrow.
*/
function _forwardFunds() internal {
_escrow.deposit.value(msg.value)(msg.sender);
}
}