Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pioneer-related features #17

Open
wants to merge 61 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
76c2a2f
refactor. add pioneer-related features.
Roger-Wu Sep 2, 2018
1e26c66
remove legacy code.
Roger-Wu Sep 2, 2018
c5da7a4
rename some variables. fix some bugs.
Roger-Wu Sep 2, 2018
114e8ec
init pioneerTimeEnd in constructor.
Roger-Wu Sep 2, 2018
a9a30c1
rename uint to uint256
Roger-Wu Sep 2, 2018
a94c4fe
rename referral and referred to referSender and referReceiver.
Roger-Wu Sep 2, 2018
e36c78e
add more setters.
Roger-Wu Sep 2, 2018
621cbcd
test framework
Sep 2, 2018
18dd435
Merge branch 'pioneer' of https://github.com/Roger-Wu/self-token-crow…
Sep 2, 2018
4028810
Merge branch 'pioneer' of https://github.com/Roger-Wu/self-token-crow…
Sep 2, 2018
38acf4a
simplify the flow of updating token balances.
Roger-Wu Sep 2, 2018
a2a0dcb
init crowdsale contract
Sep 2, 2018
fdda0eb
Merge branch 'pioneer' of github.com:Roger-Wu/self-token-crowdsale in…
Roger-Wu Sep 2, 2018
759d9f2
add constraints for some setters. remove a dangerous setter.
Roger-Wu Sep 2, 2018
7984969
remove done TODOs.
Roger-Wu Sep 2, 2018
1019f9b
fix currentStage bug. re-arrange comments.
Roger-Wu Sep 2, 2018
c5de1a0
fix: division by zero error raised if _totalWeight == 0.
Roger-Wu Sep 2, 2018
83185e9
test ownership transfer
Sep 3, 2018
eb461a2
Merge branch 'pioneer' of https://github.com/Roger-Wu/self-token-crow…
Sep 3, 2018
631d94b
test ownership transfer, setter, and event emit
Sep 3, 2018
59382b7
basic withdraw test
Sep 3, 2018
465a89c
check msg.value > 0 when purchaseTokens. add function totalSupply() a…
Roger-Wu Sep 3, 2018
eb513a7
update comment.
Roger-Wu Sep 3, 2018
8c8f622
make variables public.
Roger-Wu Sep 3, 2018
b4aa4f0
add test helpers.
Roger-Wu Sep 3, 2018
2d1b663
add test scripts.
Roger-Wu Sep 3, 2018
6aa4c46
add and test Pausable
Sep 3, 2018
4943bc0
add external input check, event, and test withdraw
Sep 3, 2018
6deabe5
update piorner bonus test: begin state and buyer2 referral bonus
yhuag Sep 3, 2018
364a965
Merge branch 'pioneer' of https://github.com/Roger-Wu/self-token-crow…
yhuag Sep 3, 2018
ac9a323
test refund if exceed hardcap
Sep 3, 2018
a8cc41a
Merge branch 'pioneer' of https://github.com/Roger-Wu/self-token-crow…
Sep 3, 2018
d166567
update pioneer bonus test
yhuag Sep 3, 2018
60ec4ef
Merge branch 'pioneer' of https://github.com/Roger-Wu/self-token-crow…
yhuag Sep 3, 2018
560777f
update pioneer rule test
yhuag Sep 3, 2018
58bb62b
update purchase token test and fix bug of transaction fee ignorance
yhuag Sep 3, 2018
2eaa690
minor format
yhuag Sep 3, 2018
fd40097
add isPioneer check before cal, add end to end test
Sep 3, 2018
39ced98
Merge branch 'pioneer' of https://github.com/Roger-Wu/self-token-crow…
Sep 3, 2018
2efb517
finish test: buyer3 is not a pioneer but tries to refer buyer2
Sep 3, 2018
7aa89bf
end to end test add enter crowdsale next stage
Sep 3, 2018
4ab1e03
test reach hardcap and withdraw
Sep 3, 2018
71f8339
finish test pause
Sep 3, 2018
625700c
update ETE test
yhuag Sep 4, 2018
789c9ea
add readme
Sep 4, 2018
f828d31
update readme
Sep 4, 2018
5e29082
revert-invalid-referrer and reorganize purchaseToken
Sep 4, 2018
96854a2
fix referrer logic
Sep 4, 2018
298002b
fix test
Sep 4, 2018
421f43d
fix test pioneer bonus - anyone problem
yhuag Sep 4, 2018
759e836
fix end to end test
Sep 4, 2018
31b460c
Merge branch 'pioneer' of https://github.com/Roger-Wu/self-token-crow…
Sep 4, 2018
3b2c274
fix pioneer bonus back to past
yhuag Sep 4, 2018
1949c62
purchaser = msg sender
Sep 4, 2018
f959ad0
Merge branch 'pioneer' of https://github.com/Roger-Wu/self-token-crow…
Sep 4, 2018
be91a05
Apply whenNotPaused
yhuag Sep 4, 2018
07a6b18
ERROR
Sep 4, 2018
9bc605f
Merge branch 'pioneer' of https://github.com/Roger-Wu/self-token-crow…
Sep 4, 2018
2b8b556
fix event order
Sep 4, 2018
edddab6
remove todo
Sep 4, 2018
dccc53c
add pause by new owner test
Sep 4, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 233 additions & 54 deletions contract/contracts/Crowdsale.sol
Original file line number Diff line number Diff line change
@@ -1,98 +1,277 @@
pragma solidity ^0.4.24;

import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";

/*
Doc:
https://docs.google.com/document/d/1Fe5MQ0NLFEhHXhliSfrTid194W1rqSQzx1e-kjpeoLQ/edit#

Prerequisites:
1. An ERC20 contract is deployed
2. An account A is granted at least the salable tokens
3. The Crowdsale contract is deployed at address C
4. The account A approve()s C of the salable amount
*/
contract Crowdsale is Ownable {
using SafeMath for uint;

ERC20 tokenContract;
address account; // the account holding the crowdsale tokens
// -----------------------------------------
// configs
// -----------------------------------------

uint priceInWei; // how many weis for one token
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

為什麼有些是uint有些是uint256? 因為uint=uint256 你這裡應該是想用uint8? range是0-255

Copy link
Member Author

@Roger-Wu Roger-Wu Sep 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我習慣寫 uint ,飛虎他們有人習慣寫 uint256 所以還沒整合好而已
看要全部改成哪一種

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我偏好explicitly寫成uint256 對別人來說也避免誤會

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

uint referalBonusPercentage = 5; // 5%
uint256 public openingTime;
uint256 public closingTime;
uint minPurchaseWei = 0.1 ether;
// How many token units a buyer gets per wei.
// If ERC20 decimals = 18, then a token unit is (10 ** (-18)) token
uint256 public rate = 3600;

uint256 public hardTop; // how many eth can be recieved in this contract
uint256 public openingTime; // 2018/9/3 12:00 (UTC+8)
uint256 public closingTime; // 2018/10/31 24:00 (UTC+8)

constructor (address tokenContractAddr, address _account, uint price, uint _openingTime, uint _closingTime) public {
tokenContract = ERC20(tokenContractAddr);
account = _account;
priceInWei = price;
openingTime = _openingTime;
closingTime = _closingTime;
hardTop = 10000 ether;
}
/// @notice The min total amount of tokens a user have to buy.
/// Not the minimum amount of tokens purchased in each transaction.
/// A user can buy 200 tokens and then buy 100 tokens.
uint public minTokensPurchased = 200 ether; // 200 tokens
uint public hardCap = 10000 ether; // hard cap

uint public referralBonusPercentage = 5; // 5%. both referrer's bonus
uint public referredBonusPercentage = 5; // 5%. referred purchaser's bonus

// airdrop 45000 tokens to pioneers whenever 1000 ETH is raised.
// until 10000 ETH is reached (or 10 stages)
uint public pioneerBonusPerStage = 45000 ether; // 45000 tokens
uint public weiRaisedPerStage = 1000 ether; // 1000 ETH
uint public totalStages = 10;

/// @notice After this moment, users are not becoming pioneers anymore.
uint public pioneerTimeEnd; // 2018/9/17 24:00 (UTC+8)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

忘記在 constructor 裡 initialize


uint public pioneerWeiThreshold = 1 ether;

// -----------------------------------------
// states
// -----------------------------------------

/// total received wei
uint256 public weiRaised;

/// weiRaisedFrom[_userAddress]
mapping(address => uint) public weiRaisedFrom;

/// @dev isPioneer[_userAddress]
mapping(address => bool) public isPioneer;

/// @dev increasedPioneerWeightOfUserInStage[_userAddress][_stageIdx]
mapping(address => mapping(uint => uint)) public increasedPioneerWeightOfUserInStage;

/// @notice total increased pioneer weight in stage
/// @dev increasedPioneerWeightInStage[_stageIdx]
mapping(uint => uint) public increasedPioneerWeightInStage;

// including purchased tokens and referred bonus
mapping(address => uint) public tokensPurchased;

mapping(address => uint) public tokensReferralBonus;

mapping(address => uint) public tokensReferredBonus;

// -----------------------------------------
// events
// -----------------------------------------

/**
* Event for token purchase logging
* @param purchaser who paid for the tokens
* @param referrer who referred the purchaser. address(0) if not valid referrer
* @param weis weis paid for purchase
* @param tokens amount of tokens purchased, not including any bonus
*/
event TokensPurchased (
address indexed purchaser,
address indexed referrer,
uint256 weis,
uint256 tokens
);

event RateChanged (uint256 rate);
event HardCapChanged (uint256 cap);
event ReferralBonusPercentageChanged (uint256 percentage);
event ReferredBonusPercentageChanged (uint256 percentage);

modifier onlyWhileOpen {
require(block.timestamp >= openingTime && block.timestamp <= closingTime);
_;
}

function salableTokenAmount () public view returns (uint) {
return tokenContract.allowance(account, address(this));
constructor (uint _openingTime, uint _closingTime) public {
openingTime = _openingTime;
closingTime = _closingTime;
}

//? emit event
function setPrice (uint price) public onlyOwner {
priceInWei = price;
// -----------------------------------------
// Crowdsale external interface
// -----------------------------------------

function () external payable onlyWhileOpen {
purchaseTokens(address(0));
}

//? emit event
function setHardtop (uint _hardTop) public onlyOwner {
hardTop = _hardTop;
function purchaseTokens (address _referrer) public payable onlyWhileOpen {
// Check if hard cap has been reached.
require(weiRaised < hardCap, "Hard cap has been reached.");

uint _weiPaid = msg.value;

// If hard cap is reached in this tx, pay as much ETH as possible
if (weiRaised.add(_weiPaid) > hardCap) {
_weiPaid = hardCap.sub(weiRaised);
}

uint _tokensPurchased = _weiPaid.mul(rate);

// Check if buying enough tokens
require(_tokensPurchased >= minTokensPurchased, "Purchasing not enough amount of tokens.");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

應改成
require(tokensPurchased[msg.sender].add(_tokensPurchased) >= minTokensPurchased, "Purchasing not enough amount of tokens.");
如此可以買 200 token 然後買 1 tokens


bool isValidReferrer = (_referrer != address(0))
&& (tokensPurchased[_referrer] > 0)
&& (_referrer != msg.sender);

// update token balances
if (isValidReferrer) {
uint _referralTokens = _tokensPurchased.mul(referralBonusPercentage).div(100);
uint _referredTokens = _tokensPurchased.mul(referredBonusPercentage).div(100);

tokensPurchased[msg.sender] = tokensPurchased[msg.sender].add(_tokensPurchased);
tokensReferredBonus[msg.sender] = tokensReferredBonus[msg.sender].add(_referredTokens);
tokensReferralBonus[_referrer] = tokensReferralBonus[_referrer].add(_referralTokens);

} else {
tokensPurchased[msg.sender] = tokensPurchased[msg.sender].add(_tokensPurchased);
_referrer = address(0); // means that the referrer is not valid
}

emit TokensPurchased(
msg.sender,
_referrer,
_weiPaid,
_tokensPurchased
);

// update wei raised
weiRaisedFrom[msg.sender] = weiRaisedFrom[msg.sender].add(_weiPaid);
weiRaised = weiRaised.add(_weiPaid);

// update pioneer bonus weight
uint _stageIdx = currentStage();
// if the sender has been a pioneer
if (isPioneer[msg.sender]) {
// uint _increasedPioneerWeight == _weiPaid;

// add _increasedPioneerWeight to increasedPioneerWeightOfUserInStage
increasedPioneerWeightOfUserInStage[msg.sender][_stageIdx] = increasedPioneerWeightOfUserInStage[msg.sender][_stageIdx].add(_weiPaid);

// add _increasedPioneerWeight to increasedPioneerWeightInStage
increasedPioneerWeightInStage[_stageIdx] = increasedPioneerWeightInStage[_stageIdx].add(_weiPaid);
}
// if the sender was not a pioneer
else {
// During the time that users can become pioneers.
if (block.timestamp <= pioneerTimeEnd
// sender has paid >= pioneerWeiThreshold
&& weiRaisedFrom[msg.sender] >= pioneerWeiThreshold) {
// the sender becomes a pioneer
isPioneer[msg.sender] = true;

// uint _increasedPioneerWeight = weiRaisedFrom[msg.sender];

// add _increasedPioneerWeight to increasedPioneerWeightOfUserInStage
increasedPioneerWeightOfUserInStage[msg.sender][_stageIdx] = increasedPioneerWeightOfUserInStage[msg.sender][_stageIdx].add(weiRaisedFrom[msg.sender]);

// add _increasedPioneerWeight to increasedPioneerWeightInStage
increasedPioneerWeightInStage[_stageIdx] = increasedPioneerWeightInStage[_stageIdx].add(weiRaisedFrom[msg.sender]);
}
}

// pay back unused ETH
if (msg.value != _weiPaid) {
msg.sender.transfer(msg.value.sub(_weiPaid));
}
}

function setReferalBonusPercentage (uint n) public onlyOwner {
referalBonusPercentage = n;
// -----------------------------------------
// getters
// -----------------------------------------

// equals to completed stage
function currentStage() public view returns (uint _stageIdx) {
_stageIdx = weiRaised.div(weiRaisedPerStage);
return (_stageIdx >= totalStages) ? totalStages : _stageIdx;
}

function setOpeningTime (uint time) public onlyOwner {
openingTime = time;
/// @return amount of pioneer bonus tokens
function calcPioneerBonus(address _user) public view returns (uint _tokens) {
uint _userWeight = 0;
uint _totalWeight = 0;
uint _currentStage = currentStage();
for (uint _stageIdx = 0; _stageIdx < _currentStage; _stageIdx++) {
_userWeight = _userWeight.add(increasedPioneerWeightOfUserInStage[_user][_stageIdx]);
_totalWeight = _totalWeight.add(increasedPioneerWeightInStage[_stageIdx]);

_tokens = _tokens.add(
pioneerBonusPerStage.mul(_userWeight).div(_totalWeight)
);
}
return _tokens;
}

function setClosingTime (uint time) public onlyOwner {
closingTime = time;
/// @return token balance of a user
function balanceOf(address _user) public view returns (uint _balance) {
return (
tokensPurchased[_user]
+ tokensReferralBonus[_user]
+ tokensReferredBonus[_user]
+ calcPioneerBonus(_user)
);
}

function setMinPurchase (uint n) public onlyOwner {
minPurchaseWei = n;
// -----------------------------------------
// setters
// -----------------------------------------

function setRate (uint _rate) public onlyOwner {
rate = _rate;
emit RateChanged(_rate);
}

function purchase (address referer) public payable onlyWhileOpen {
function setMinTokensPurchased (uint _amount) public onlyOwner {
minTokensPurchased = _amount;
}

function setHardCap (uint _hardCap) public onlyOwner {
hardCap = _hardCap;
emit HardCapChanged(_hardCap);
}

require(address(this).balance + msg.value <= hardTop);
require(msg.value >= minPurchaseWei);

bool validReferer = tokenContract.balanceOf(referer) > 0;
function setReferralBonusPercentage (uint _percentage) public onlyOwner {
referralBonusPercentage = _percentage;
emit ReferralBonusPercentageChanged(_percentage);
}

uint base = msg.value.div(priceInWei);
uint bonus = base.mul(referalBonusPercentage).div(100);
function setReferredBonusPercentage (uint _percentage) public onlyOwner {
referredBonusPercentage = _percentage;
emit ReferredBonusPercentageChanged(_percentage);
}

uint totalToken = validReferer ? base.add(bonus).add(bonus) : base.add(bonus);
require(salableTokenAmount() >= totalToken);
function setOpeningTime (uint _time) public onlyOwner {
openingTime = _time;
}

tokenContract.transferFrom(account, msg.sender, base.add(bonus));

if (validReferer) {
tokenContract.transferFrom(account, referer, bonus);
}
function setClosingTime (uint _time) public onlyOwner {
closingTime = _time;
}

function withDrawEther (uint amount) public onlyOwner {
// -----------------------------------------
// other owner operation
// -----------------------------------------

function withdraw (uint amount) public onlyOwner {
msg.sender.transfer(amount);
}

function withdrawAll () public onlyOwner {
msg.sender.transfer(address(this).balance);
}
}
2 changes: 1 addition & 1 deletion contract/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
"author": "",
"license": "ISC",
"dependencies": {
"openzeppelin-solidity": "^1.12.0"
"openzeppelin-solidity": "1.12.0"
}
}