Browse Source

Add Crowdsale contracts

Manuel Araoz 8 years ago
parent
commit
de92a02127

+ 35 - 0
contracts/crowdsale/CappedCrowdsale.sol

@@ -0,0 +1,35 @@
+pragma solidity ^0.4.11;
+
+import '../SafeMath.sol';
+import './Crowdsale.sol';
+
+/**
+ * @title CappedCrowdsale
+ * @dev Extension of Crowsdale with a max amount of funds raised
+ */
+contract CappedCrowdsale is Crowdsale {
+  using SafeMath for uint256;
+
+  uint256 public cap;
+
+  function CappedCrowdsale(
+    uint256 _cap
+  ) {
+    cap = _cap;
+  }
+
+  // overriding Crowdsale#canBuy to add extra cap logic
+  // @return true if investors can buy at the moment
+  function canBuy() internal constant returns (bool) {
+    bool withinCap = weiRaised.add(msg.value) <= cap;
+    return super.canBuy() && withinCap;
+  }
+
+  // overriding Crowdsale#hasEnded to add cap logic
+  // @return true if crowdsale event has ended
+  function hasEnded() public constant returns (bool) {
+    bool capReached = weiRaised >= cap;
+    return super.hasEnded() || capReached;
+  }
+
+}

+ 107 - 0
contracts/crowdsale/Crowdsale.sol

@@ -0,0 +1,107 @@
+pragma solidity ^0.4.11;
+
+import '../token/MintableToken.sol';
+import '../SafeMath.sol';
+
+/**
+ * @title Crowdsale 
+ * @dev Crowdsale is a base contract for managing a token crowdsale.
+ * Crowdsales have a start and end block, where investors can make
+ * token purchases and the crowdsale will assign them tokens based
+ * on a token per ETH rate. Funds collected are forwarded to a wallet 
+ * as they arrive.
+ */
+contract Crowdsale {
+  using SafeMath for uint256;
+
+  // The token being sold
+  MintableToken public token;
+
+  // start and end block where investments are allowed (both inclusive)
+  uint256 public startBlock;
+  uint256 public endBlock;
+
+  // address where funds are collected
+  address public wallet;
+
+  // token to ETH conversion rate
+  uint256 public rate;
+
+  // amount of raised money in wei
+  uint256 public weiRaised;
+
+  /**
+   * event for token purchase logging
+   * @param purchaser who paid for the tokens
+   * @param beneficiary who got the tokens
+   * @param value weis paid for purchase
+   * @param amount amount of tokens purchased
+   */ 
+  event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);
+
+
+  function Crowdsale(uint256 _startBlock, uint256 _endBlock, uint256 _rate, address _wallet) {
+    require(_startBlock >= block.number);
+    require(_endBlock >= _startBlock);
+    require(_rate > 0);
+    require(_wallet != 0x0);
+
+    token = createTokenContract();
+    startBlock = _startBlock;
+    endBlock = _endBlock;
+    rate = _rate;
+    wallet = _wallet;
+  }
+
+  // creates the token to be sold. 
+  // override this method to have crowdsale of a specific mintable token.
+  function createTokenContract() internal returns (MintableToken) {
+    return new MintableToken();
+  }
+
+
+  // fallback function can be used to buy tokens
+  function () payable {
+    buyTokens(msg.sender);
+  }
+
+  // low level token purchase function
+  function buyTokens(address beneficiary) payable {
+    require(canBuy());
+
+    uint256 weiAmount = msg.value;
+    uint256 updatedWeiRaised = weiRaised.add(weiAmount);
+
+    // calculate token amount to be created
+    uint256 tokens = weiAmount.mul(rate);
+
+    // update state
+    weiRaised = updatedWeiRaised;
+
+    token.mint(beneficiary, tokens);
+    TokenPurchase(msg.sender, beneficiary, weiAmount, tokens);
+
+    forwardFunds();
+  }
+
+  // send ether to the fund collection wallet
+  // override to create custom fund forwarding mechanisms
+  function forwardFunds() internal {
+    wallet.transfer(msg.value);
+  }
+
+  // @return true if the transaction can buy tokens
+  function canBuy() internal constant returns (bool) {
+    uint256 current = block.number;
+    bool withinPeriod = current >= startBlock && current <= endBlock;
+    bool nonZeroPurchase = msg.value != 0;
+    return withinPeriod && nonZeroPurchase;
+  }
+
+  // @return true if crowdsale event has ended
+  function hasEnded() public constant returns (bool) {
+    return block.number > endBlock;
+  }
+
+
+}

+ 39 - 0
contracts/crowdsale/FinalizableCrowdsale.sol

@@ -0,0 +1,39 @@
+pragma solidity ^0.4.11;
+
+import '../SafeMath.sol';
+import '../ownership/Ownable.sol';
+import './Crowdsale.sol';
+
+/**
+ * @title FinalizableCrowdsale
+ * @dev Extension of Crowsdale where an owner can do extra work
+ * after finishing. By default, it will end token minting.
+ */
+contract FinalizableCrowdsale is Crowdsale, Ownable {
+  using SafeMath for uint256;
+
+  bool public isFinalized = false;
+
+  event Finalized();
+
+  // should be called after crowdsale ends, to do
+  // some extra finalization work
+  function finalize() onlyOwner {
+    require(!isFinalized);
+    require(hasEnded());
+
+    finalization();
+    Finalized();
+    
+    isFinalized = true;
+  }
+
+  // end token minting on finalization
+  // override this with custom logic if needed
+  function finalization() internal {
+    token.finishMinting();
+  }
+
+
+
+}

+ 56 - 0
contracts/crowdsale/RefundVault.sol

@@ -0,0 +1,56 @@
+pragma solidity ^0.4.11;
+
+import '../SafeMath.sol';
+import '../ownership/Ownable.sol';
+
+/**
+ * @title RefundVault
+ * @dev This contract is used for storing funds while a crowdsale
+ * is in progress. Supports refunding the money if crowdsale fails,
+ * and forwarding it if crowdsale is successful.
+ */
+contract RefundVault is Ownable {
+  using SafeMath for uint256;
+
+  enum State { Active, Refunding, Closed }
+
+  mapping (address => uint256) public deposited;
+  address public wallet;
+  State public state;
+
+  event Closed();
+  event RefundsEnabled();
+  event Refunded(address indexed beneficiary, uint256 weiAmount);
+
+  function RefundVault(address _wallet) {
+    require(_wallet != 0x0);
+    wallet = _wallet;
+    state = State.Active;
+  }
+
+  function deposit(address investor) onlyOwner payable {
+    require(state == State.Active);
+    deposited[investor] = deposited[investor].add(msg.value);
+  }
+
+  function close() onlyOwner {
+    require(state == State.Active);
+    state = State.Closed;
+    Closed();
+    wallet.transfer(this.balance);
+  }
+
+  function enableRefunds() onlyOwner {
+    require(state == State.Active);
+    state = State.Refunding;
+    RefundsEnabled();
+  }
+
+  function refund(address investor) {
+    require(state == State.Refunding);
+    uint256 depositedValue = deposited[investor];
+    deposited[investor] = 0;
+    investor.transfer(depositedValue);
+    Refunded(investor, depositedValue);
+  }
+}

+ 61 - 0
contracts/crowdsale/RefundableCrowdsale.sol

@@ -0,0 +1,61 @@
+pragma solidity ^0.4.11;
+
+
+import '../SafeMath.sol';
+import './FinalizableCrowdsale.sol';
+import './RefundVault.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.
+ * Uses a RefundVault as the crowdsale's vault.
+ */
+contract RefundableCrowdsale is FinalizableCrowdsale {
+  using SafeMath for uint256;
+
+  // minimum amount of funds to be raised in weis
+  uint256 public goal;
+
+  // refund vault used to hold funds while crowdsale is running
+  RefundVault public vault;
+
+  function RefundableCrowdsale(
+    uint256 _goal
+  ) {
+    vault = new RefundVault(wallet);
+    goal = _goal;
+  }
+
+  // We're overriding the fund forwarding from Crowdsale.
+  // In addition to sending the funds, we want to call
+  // the RefundVault deposit function
+  function forwardFunds() internal {
+    vault.deposit.value(msg.value)(msg.sender);
+  }
+
+  // if crowdsale is unsuccessful, investors can claim refunds here
+  function claimRefund() {
+    require(hasEnded());
+    require(!goalReached());
+
+    vault.refund(msg.sender);
+  }
+
+  // vault finalization task, called when owner calls finalize()
+  function finalization() internal {
+    if (goalReached()) {
+      vault.close();
+    } else {
+      vault.enableRefunds();
+    }
+
+    super.finalization();
+  }
+
+  function goalReached() public constant returns (bool) {
+    return weiRaised >= goal;
+  }
+
+}

+ 3 - 7
contracts/token/PausableToken.sol

@@ -7,19 +7,15 @@ import '../lifecycle/Pausable.sol';
  * Pausable token
  *
  * Simple ERC20 Token example, with pausable token creation
- * Issue:
- * https://github.com/OpenZeppelin/zeppelin-solidity/issues/194
- * Based on code by BCAPtoken:
- * https://github.com/BCAPtoken/BCAPToken/blob/5cb5e76338cc47343ba9268663a915337c8b268e/sol/BCAPToken.sol#L27
  **/
 
-contract PausableToken is Pausable, StandardToken {
+contract PausableToken is StandardToken, Pausable {
 
-  function transfer(address _to, uint256 _value) whenNotPaused {
+  function transfer(address _to, uint _value) whenNotPaused {
     super.transfer(_to, _value);
   }
 
-  function transferFrom(address _from, address _to, uint256 _value) whenNotPaused {
+  function transferFrom(address _from, address _to, uint _value) whenNotPaused {
     super.transferFrom(_from, _to, _value);
   }
 }