Openzeppelin庫第八期:Crowdsale
阿新 • • 發佈:2018-12-16
- Crowdsale.sol:眾籌合約
pragma solidity ^0.4.24; import "../token/ERC20/ERC20.sol"; import "../math/SafeMath.sol"; import "../token/ERC20/SafeERC20.sol"; /** * @title Crowdsale * @dev Crowdsale is a base contract for managing a token crowdsale, * allowing investors to purchase tokens with ether. This contract implements * such functionality in its most fundamental form and can be extended to provide additional * functionality and/or custom behavior. * The external interface represents the basic interface for purchasing tokens, and conform * the base architecture for crowdsales. They are *not* intended to be modified / overridden. * The internal interface conforms the extensible and modifiable surface of crowdsales. Override * the methods to add functionality. Consider using 'super' where appropriate to concatenate * behavior. */ contract Crowdsale { using SafeMath for uint256; using SafeERC20 for ERC20; // The token being sold ERC20 public token; // 用於髮型的TOKEN // Address where funds are collected address public wallet; // 錢包地址,用於儲存眾籌所得的以太幣 // How many token units a buyer gets per wei. // The rate is the conversion between wei and the smallest and indivisible token unit. // So, if you are using a rate of 1 with a DetailedERC20 token with 3 decimals called TOK // 1 wei will give you 1 unit, or 0.001 TOK. uint256 public rate; // 和以太幣的匯率兌換(以wei位基本單位) // Amount of wei raised 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 */ // 購買token的記錄 event TokenPurchase( address indexed purchaser, // 買家 address indexed beneficiary, // 受益人 uint256 value, // 費用 uint256 amount // 購買的token數量 ); /** * @param _rate Number of token units a buyer gets per wei * @param _wallet Address where collected funds will be forwarded to * @param _token Address of the token being sold */ constructor(uint256 _rate, address _wallet, ERC20 _token) public { require(_rate > 0); require(_wallet != address(0)); require(_token != address(0)); rate = _rate; wallet = _wallet; token = _token; } // ----------------------------------------- // Crowdsale external interface // ----------------------------------------- /** * @dev fallback function ***DO NOT OVERRIDE*** */ function () external payable { buyTokens(msg.sender); } /** * @dev low level token purchase ***DO NOT OVERRIDE*** * @param _beneficiary Address performing the token purchase */ // 買token function buyTokens(address _beneficiary) public payable { uint256 weiAmount = msg.value; _preValidatePurchase(_beneficiary, weiAmount); // calculate token amount to be created uint256 tokens = _getTokenAmount(weiAmount); // update state weiRaised = weiRaised.add(weiAmount); _processPurchase(_beneficiary, tokens); emit TokenPurchase( msg.sender, _beneficiary, weiAmount, tokens ); _updatePurchasingState(_beneficiary, weiAmount); _forwardFunds(); _postValidatePurchase(_beneficiary, weiAmount); } // ----------------------------------------- // Internal interface (extensible) // ----------------------------------------- /** * @dev Validation of an incoming purchase. Use require statements to revert state when conditions are not met. Use `super` in contracts that inherit from Crowdsale to extend their validations. * Example from CappedCrowdsale.sol's _preValidatePurchase method: * super._preValidatePurchase(_beneficiary, _weiAmount); * require(weiRaised.add(_weiAmount) <= cap); * @param _beneficiary Address performing the token purchase * @param _weiAmount Value in wei involved in the purchase */ // 前置檢查 function _preValidatePurchase( address _beneficiary, uint256 _weiAmount ) internal { require(_beneficiary != address(0)); require(_weiAmount != 0); } /** * @dev Validation of an executed purchase. Observe state and use revert statements to undo rollback when valid conditions are not met. * @param _beneficiary Address performing the token purchase * @param _weiAmount Value in wei involved in the purchase */ // 購買執行之後的驗證,在條件不滿足時,回滾狀態 function _postValidatePurchase( address _beneficiary, uint256 _weiAmount ) internal { // optional override } /** * @dev Source of tokens. Override this method to modify the way in which the crowdsale ultimately gets and sends its tokens. * @param _beneficiary Address performing the token purchase * @param _tokenAmount Number of tokens to be emitted */ // 互動token function _deliverTokens( address _beneficiary, uint256 _tokenAmount ) internal { token.safeTransfer(_beneficiary, _tokenAmount); } /** * @dev Executed when a purchase has been validated and is ready to be executed. Not necessarily emits/sends tokens. * @param _beneficiary Address receiving the tokens * @param _tokenAmount Number of tokens to be purchased */ function _processPurchase( address _beneficiary, uint256 _tokenAmount ) internal { _deliverTokens(_beneficiary, _tokenAmount); } /** * @dev Override for extensions that require an internal state to check for validity (current user contributions, etc.) * @param _beneficiary Address receiving the tokens * @param _weiAmount Value in wei involved in the purchase */ function _updatePurchasingState( address _beneficiary, uint256 _weiAmount ) internal { // optional override } /** * @dev Override to extend the way in which ether is converted to tokens. * @param _weiAmount Value in wei to be converted into tokens * @return Number of tokens that can be purchased with the specified _weiAmount */ // 獲取到token數量 function _getTokenAmount(uint256 _weiAmount) internal view returns (uint256) { return _weiAmount.mul(rate); } /** * @dev Determines how ETH is stored/forwarded on purchases. */ // 確定購買是轉發以太幣 function _forwardFunds() internal { wallet.transfer(msg.value); } }
- CappedCrowdsale.sol:眾籌上限
pragma solidity ^0.4.24; import "../../math/SafeMath.sol"; import "../Crowdsale.sol"; /** * @title CappedCrowdsale * @dev Crowdsale with a limit for total contributions. */ // 眾籌上限合約 contract CappedCrowdsale is Crowdsale { using SafeMath for uint256; uint256 public cap; // 眾籌的上限 /** * @dev Constructor, takes maximum amount of wei accepted in the crowdsale. * @param _cap Max amount of wei to be contributed */ constructor(uint256 _cap) public { require(_cap > 0); cap = _cap; } /** * @dev Checks whether the cap has been reached. * @return Whether the cap was reached */ // 檢查眾籌金額是否已經達到上限 function capReached() public view returns (bool) { return weiRaised >= cap; } /** * @dev Extend parent behavior requiring purchase to respect the funding cap. * @param _beneficiary Token purchaser * @param _weiAmount Amount of wei contributed */ function _preValidatePurchase( address _beneficiary, uint256 _weiAmount ) internal { super._preValidatePurchase(_beneficiary, _weiAmount); require(weiRaised.add(_weiAmount) <= cap); } }
- IndividuallyCappedCrowdsale.sol:個人的眾籌上限
pragma solidity ^0.4.24; import "../../math/SafeMath.sol"; import "../Crowdsale.sol"; import "../../ownership/Ownable.sol"; /** * @title IndividuallyCappedCrowdsale * @dev Crowdsale with per-user caps. */ // 每一個用的購買上限 contract IndividuallyCappedCrowdsale is Crowdsale, Ownable { using SafeMath for uint256; mapping(address => uint256) public contributions; mapping(address => uint256) public caps; /** * @dev Sets a specific user's maximum contribution. * @param _beneficiary Address to be capped * @param _cap Wei limit for individual contribution */ // 設定指定地址的上限 function setUserCap(address _beneficiary, uint256 _cap) external onlyOwner { caps[_beneficiary] = _cap; } /** * @dev Sets a group of users' maximum contribution. * @param _beneficiaries List of addresses to be capped * @param _cap Wei limit for individual contribution */ // 為指定的地址集合設定相同的上限 function setGroupCap( address[] _beneficiaries, uint256 _cap ) external onlyOwner { for (uint256 i = 0; i < _beneficiaries.length; i++) { caps[_beneficiaries[i]] = _cap; } } /** * @dev Returns the cap of a specific user. * @param _beneficiary Address whose cap is to be checked * @return Current cap for individual user */ // 返回指定地址的購買上限 function getUserCap(address _beneficiary) public view returns (uint256) { return caps[_beneficiary]; } /** * @dev Returns the amount contributed so far by a sepecific user. * @param _beneficiary Address of contributor * @return User contribution so far */ // 返回當前地址的購買量 function getUserContribution(address _beneficiary) public view returns (uint256) { return contributions[_beneficiary]; } /** * @dev Extend parent behavior requiring purchase to respect the user's funding cap. * @param _beneficiary Token purchaser * @param _weiAmount Amount of wei contributed */ function _preValidatePurchase( address _beneficiary, uint256 _weiAmount ) internal { super._preValidatePurchase(_beneficiary, _weiAmount); require(contributions[_beneficiary].add(_weiAmount) <= caps[_beneficiary]); } /** * @dev Extend parent behavior to update user contributions * @param _beneficiary Token purchaser * @param _weiAmount Amount of wei contributed */ // 更新當前地址的眾籌量 function _updatePurchasingState( address _beneficiary, uint256 _weiAmount ) internal { super._updatePurchasingState(_beneficiary, _weiAmount); contributions[_beneficiary] = contributions[_beneficiary].add(_weiAmount); } }
- TimedCrowdsale:眾籌時間範圍
pragma solidity ^0.4.24;
import "../../math/SafeMath.sol";
import "../Crowdsale.sol";
/**
* @title TimedCrowdsale
* @dev Crowdsale accepting contributions only within a time frame.
*/
// 眾籌時間範圍
contract TimedCrowdsale is Crowdsale {
using SafeMath for uint256;
uint256 public openingTime; // 啟動時間
uint256 public closingTime; // 結束時間
/**
* @dev Reverts if not in crowdsale time range.
*/
modifier onlyWhileOpen {
// solium-disable-next-line security/no-block-members
require(block.timestamp >= openingTime && block.timestamp <= closingTime);
_;
}
/**
* @dev Constructor, takes crowdsale opening and closing times.
* @param _openingTime Crowdsale opening time
* @param _closingTime Crowdsale closing time
*/
// 設定時間範圍
constructor(uint256 _openingTime, uint256 _closingTime) public {
// solium-disable-next-line security/no-block-members
require(_openingTime >= block.timestamp);
require(_closingTime >= _openingTime);
openingTime = _openingTime;
closingTime = _closingTime;
}
/**
* @dev Checks whether the period in which the crowdsale is open has already elapsed.
* @return Whether crowdsale period has elapsed
*/
// 檢驗眾籌是否已經關閉
function hasClosed() public view returns (bool) {
// solium-disable-next-line security/no-block-members
return block.timestamp > closingTime;
}
/**
* @dev Extend parent behavior requiring to be within contributing period
* @param _beneficiary Token purchaser
* @param _weiAmount Amount of wei contributed
*/
function _preValidatePurchase(
address _beneficiary,
uint256 _weiAmount
)
internal
onlyWhileOpen
{
super._preValidatePurchase(_beneficiary, _weiAmount);
}
}
- WhitelistedCrowdsale.sol:白名單
pragma solidity ^0.4.24;
import "../Crowdsale.sol";
import "../../access/Whitelist.sol";
/**
* @title WhitelistedCrowdsale
* @dev Crowdsale in which only whitelisted users can contribute.
*/
// 可以參與眾籌的白名單地址列表
contract WhitelistedCrowdsale is Whitelist, Crowdsale {
/**
* @dev Extend parent behavior requiring beneficiary to be in whitelist.
* @param _beneficiary Token beneficiary
* @param _weiAmount Amount of wei contributed
*/
function _preValidatePurchase(
address _beneficiary,
uint256 _weiAmount
)
internal
onlyIfWhitelisted(_beneficiary)
{
super._preValidatePurchase(_beneficiary, _weiAmount);
}
}
- IncreasingPriceCrowdsale.sol:token價格變動
pragma solidity ^0.4.24;
import "../validation/TimedCrowdsale.sol";
import "../../math/SafeMath.sol";
/**
* @title IncreasingPriceCrowdsale
* @dev Extension of Crowdsale contract that increases the price of tokens linearly in time.
* Note that what should be provided to the constructor is the initial and final _rates_, that is,
* the amount of tokens per wei contributed. Thus, the initial rate must be greater than the final rate.
*/
// 擴充套件的TOKEN合約,增加TOKEN價格
contract IncreasingPriceCrowdsale is TimedCrowdsale {
using SafeMath for uint256;
uint256 public initialRate;
uint256 public finalRate;
/**
* @dev Constructor, takes initial and final rates of tokens received per wei contributed.
* @param _initialRate Number of tokens a buyer gets per wei at the start of the crowdsale
* @param _finalRate Number of tokens a buyer gets per wei at the end of the crowdsale
*/
constructor(uint256 _initialRate, uint256 _finalRate) public {
require(_initialRate >= _finalRate);
require(_finalRate > 0);
initialRate = _initialRate;
finalRate = _finalRate;
}
/**
* @dev Returns the rate of tokens per wei at the present time.
* Note that, as price _increases_ with time, the rate _decreases_.
* @return The number of tokens a buyer gets per wei at a given time
*/
// 獲取當前兌換率
function getCurrentRate() public view returns (uint256) {
// solium-disable-next-line security/no-block-members
uint256 elapsedTime = block.timestamp.sub(openingTime);
uint256 timeRange = closingTime.sub(openingTime);
uint256 rateRange = initialRate.sub(finalRate);
return initialRate.sub(elapsedTime.mul(rateRange).div(timeRange));
}
/**
* @dev Overrides parent method taking into account variable rate.
* @param _weiAmount The value in wei to be converted into tokens
* @return The number of tokens _weiAmount wei will buy at present time
*/
// 獲取當前的token數量
function _getTokenAmount(uint256 _weiAmount)
internal view returns (uint256)
{
uint256 currentRate = getCurrentRate();
return currentRate.mul(_weiAmount);
}
}
- RefundableCrowdsale.sol:退款
pragma solidity ^0.4.24;
import "../../math/SafeMath.sol";
import "./FinalizableCrowdsale.sol";
import "../../payment/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.
*/
// 擴充套件合約,增加資金目標,或者在未達到目標是支援使用者退塊
contract RefundableCrowdsale is FinalizableCrowdsale {
using SafeMath for uint256;
// minimum amount of funds to be raised in weis
uint256 public 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) public {
require(_goal > 0);
escrow = new RefundEscrow(wallet);
goal = _goal;
}
/**
* @dev Investors can claim refunds here if crowdsale is unsuccessful
*/
function claimRefund() public {
require(isFinalized);
require(!goalReached());
escrow.withdraw(msg.sender);
}
/**
* @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 owner calls finalize()
*/
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);
}
}
- PostDeliveryCrowdsale.sol:鎖倉
pragma solidity ^0.4.24;
import "../validation/TimedCrowdsale.sol";
import "../../token/ERC20/ERC20.sol";
import "../../math/SafeMath.sol";
/**
* @title PostDeliveryCrowdsale
* @dev Crowdsale that locks tokens from withdrawal until it ends.
*/
// 鎖定TOKEN(鎖倉)
contract PostDeliveryCrowdsale is TimedCrowdsale {
using SafeMath for uint256;
mapping(address => uint256) public balances;
/**
* @dev Withdraw tokens only after crowdsale ends.
*/
function withdrawTokens() public {
require(hasClosed());
uint256 amount = balances[msg.sender];
require(amount > 0);
balances[msg.sender] = 0;
_deliverTokens(msg.sender, amount);
}
/**
* @dev Overrides parent by storing balances instead of issuing tokens right away.
* @param _beneficiary Token purchaser
* @param _tokenAmount Amount of tokens purchased
*/
function _processPurchase(
address _beneficiary,
uint256 _tokenAmount
)
internal
{
balances[_beneficiary] = balances[_beneficiary].add(_tokenAmount);
}
}
- FinalizableCrowdsale.sol:眾籌完成之後的操作
pragma solidity ^0.4.24;
import "../../math/SafeMath.sol";
import "../../ownership/Ownable.sol";
import "../validation/TimedCrowdsale.sol";
/**
* @title FinalizableCrowdsale
* @dev Extension of Crowdsale where an owner can do extra work
* after finishing.
*/
// 在完成眾籌之後所進行的額外操作
contract FinalizableCrowdsale is TimedCrowdsale, Ownable {
using SafeMath for uint256;
bool public isFinalized = false;
event Finalized();
/**
* @dev Must be called after crowdsale ends, to do some extra finalization
* work. Calls the contract's finalization function.
*/
function finalize() public onlyOwner {
require(!isFinalized);
require(hasClosed());
finalization();
emit Finalized();
isFinalized = true;
}
/**
* @dev Can be overridden to add finalization logic. The overriding function
* should call super.finalization() to ensure the chain of finalization is
* executed entirely.
*/
function finalization() internal {
}
}