pragma solidity ^0.4.18;
// import "./Pausable.sol";
// import "./CratePreSale.sol";
contract NewCratePreSale {
// migration functions migrate the data from the previous contract in stages
// all addresses are included for transparency and easy verification
// however addresses with no robots (i.e. failed transaction and never bought properly) have been commented out.
// to view the full list of state assignments, go to etherscan.io/address/{address} and you can view the verified
mapping (address => uint[]) public userToRobots;
function _migrate(uint _index) external onlyOwner {
bytes4 selector = bytes4(sha3("setData()"));
address a = migrators[_index];
require(a.delegatecall(selector));
}
// source code - feel free to verify the migration
address[6] migrators = [
0x700febd9360ac0a0a72f371615427bec4e4454e5, //0x97ae01893e42d6d33fd9851a28e5627222af7bbb,
0x72cc898de0a4eac49c46ccb990379099461342f6,
0xc3cc48da3b8168154e0f14bf0446c7a93613f0a7,
0x4cc96f2ddf6844323ae0d8461d418a4d473b9ac3,
0xa52bfcb5ff599e29ee2b9130f1575babaa27de0a,
0xe503b42aabda22974e2a8b75fa87e010e1b13584
];
function NewCratePreSale() public payable {
owner = msg.sender;
// one time transfer of state from the previous contract
// var previous = CratePreSale(0x3c7767011C443EfeF2187cf1F2a4c02062da3998); //MAINNET
// oldAppreciationRateWei = previous.appreciationRateWei();
oldAppreciationRateWei = 100000000000000;
appreciationRateWei = oldAppreciationRateWei;
// oldPrice = previous.currentPrice();
oldPrice = 232600000000000000;
currentPrice = oldPrice;
// oldCratesSold = previous.cratesSold();
oldCratesSold = 1075;
cratesSold = oldCratesSold;
// Migration Rationale
// due to solidity issues with enumerability (contract calls cannot return dynamic arrays etc)
// no need for trust -> can still use web3 to call the previous contract and check the state
// will only change in the future if people send more eth
// and will be obvious due to change in crate count. Any purchases on the old contract
// after this contract is deployed will be fully refunded, and those robots bought will be voided.
// feel free to validate any address on the old etherscan:
// https://etherscan.io/address/0x3c7767011C443EfeF2187cf1F2a4c02062da3998
// can visit the exact contracts at the addresses listed above
}
// ------ STATE ------
uint256 constant public MAX_CRATES_TO_SELL = 3900; // Max no. of robot crates to ever be sold
uint256 constant public PRESALE_END_TIMESTAMP = 1518699600; // End date for the presale - no purchases can be made after this date - Midnight 16 Feb 2018 UTC
uint256 public appreciationRateWei;
uint32 public cratesSold;
uint256 public currentPrice;
// preserve these for later verification
uint32 public oldCratesSold;
uint256 public oldPrice;
uint256 public oldAppreciationRateWei;
// mapping (address => uint32) public userCrateCount; // replaced with more efficient method
// store the unopened crates of this user
// actually stores the blocknumber of each crate
mapping (address => uint[]) public addressToPurchasedBlocks;
// store the number of expired crates for each user
// i.e. crates where the user failed to open the crate within 256 blocks (~1 hour)
// these crates will be able to be opened post-launch
mapping (address => uint) public expiredCrates;
// store the part information of purchased crates
function openAll() public {
uint len = addressToPurchasedBlocks[msg.sender].length;
require(len > 0);
uint8 count = 0;
// len > i to stop predicatable wraparound
for (uint i = len - 1; i >= 0 && len > i; i--) {
uint crateBlock = addressToPurchasedBlocks[msg.sender][i];
require(block.number > crateBlock);
// can't open on the same timestamp
var hash = block.blockhash(crateBlock);
if (uint(hash) != 0) {
// different results for all different crates, even on the same block/same user
// randomness is already taken care of
uint rand = uint(keccak256(hash, msg.sender, i)) % (10 ** 20);
userToRobots[msg.sender].push(rand);
count++;
} else {
// all others will be expired
expiredCrates[msg.sender] += (i + 1);
break;
}
}
CratesOpened(msg.sender, count);
delete addressToPurchasedBlocks[msg.sender];
}
// ------ EVENTS ------
event CratesPurchased(address indexed _from, uint8 _quantity);
event CratesOpened(address indexed _from, uint8 _quantity);
// ------ FUNCTIONS ------
function getPrice() view public returns (uint256) {
return currentPrice;
}
function getRobotCountForUser(address _user) external view returns(uint256) {
return userToRobots[_user].length;
}
function getRobotForUserByIndex(address _user, uint _index) external view returns(uint) {
return userToRobots[_user][_index];
}
function getRobotsForUser(address _user) view public returns (uint[]) {
return userToRobots[_user];
}
function getPendingCratesForUser(address _user) external view returns(uint[]) {
return addressToPurchasedBlocks[_user];
}
function getPendingCrateForUserByIndex(address _user, uint _index) external view returns(uint) {
return addressToPurchasedBlocks[_user][_index];
}
function getExpiredCratesForUser(address _user) external view returns(uint) {
return expiredCrates[_user];
}
function incrementPrice() private {
// Decrease the rate of increase of the crate price
// as the crates become more expensive
// to avoid runaway pricing
// (halving rate of increase at 0.1 ETH, 0.2 ETH, 0.3 ETH).
if ( currentPrice == 100000000000000000 ) {
appreciationRateWei = 200000000000000;
} else if ( currentPrice == 200000000000000000) {
appreciationRateWei = 100000000000000;
} else if (currentPrice == 300000000000000000) {
appreciationRateWei = 50000000000000;
}
currentPrice += appreciationRateWei;
}
function purchaseCrates(uint8 _cratesToBuy) public payable whenNotPaused {
require(now < PRESALE_END_TIMESTAMP); // Check presale is still ongoing.
require(_cratesToBuy <= 10); // Can only buy max 10 crates at a time. Don't be greedy!
require(_cratesToBuy >= 1); // Sanity check. Also, you have to buy a crate.
require(cratesSold + _cratesToBuy <= MAX_CRATES_TO_SELL); // Check max crates sold is less than hard limit
uint256 priceToPay = _calculatePayment(_cratesToBuy);
require(msg.value >= priceToPay); // Check buyer sent sufficient funds to purchase
if (msg.value > priceToPay) { //overpaid, return excess
msg.sender.transfer(msg.value-priceToPay);
}
//all good, payment received. increment number sold, price, and generate crate receipts!
cratesSold += _cratesToBuy;
for (uint8 i = 0; i < _cratesToBuy; i++) {
incrementPrice();
addressToPurchasedBlocks[msg.sender].push(block.number);
}
CratesPurchased(msg.sender, _cratesToBuy);
}
function _calculatePayment (uint8 _cratesToBuy) private view returns (uint256) {
uint256 tempPrice = currentPrice;
for (uint8 i = 1; i < _cratesToBuy; i++) {
tempPrice += (currentPrice + (appreciationRateWei * i));
} // for every crate over 1 bought, add current Price and a multiple of the appreciation rate
// very small edge case of buying 10 when you the appreciation rate is about to halve
// is compensated by the great reduction in gas by buying N at a time.
return tempPrice;
}
//owner only withdrawal function for the presale
function withdraw() onlyOwner public {
owner.transfer(this.balance);
}
function addFunds() onlyOwner external payable {
}
event SetPaused(bool paused);
// starts unpaused
bool public paused = false;
modifier whenNotPaused() {
require(!paused);
_;
}
modifier whenPaused() {
require(paused);
_;
}
function pause() external onlyOwner whenNotPaused returns (bool) {
paused = true;
SetPaused(paused);
return true;
}
function unpause() external onlyOwner whenPaused returns (bool) {
paused = false;
SetPaused(paused);
return true;
}
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
{
"compilationTarget": {
"NewCratePreSale.sol": "NewCratePreSale"
},
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[{"name":"_user","type":"address"}],"name":"getRobotCountForUser","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cratesSold","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"expiredCrates","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oldAppreciationRateWei","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_user","type":"address"},{"name":"_index","type":"uint256"}],"name":"getPendingCrateForUserByIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"oldPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_user","type":"address"}],"name":"getRobotsForUser","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"addressToPurchasedBlocks","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_cratesToBuy","type":"uint8"}],"name":"purchaseCrates","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_CRATES_TO_SELL","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PRESALE_END_TIMESTAMP","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"currentPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"addFunds","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"openAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_user","type":"address"},{"name":"_index","type":"uint256"}],"name":"getRobotForUserByIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_index","type":"uint256"}],"name":"_migrate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"userToRobots","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"appreciationRateWei","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_user","type":"address"}],"name":"getPendingCratesForUser","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_user","type":"address"}],"name":"getExpiredCratesForUser","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oldCratesSold","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":true,"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":false,"name":"_quantity","type":"uint8"}],"name":"CratesPurchased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":false,"name":"_quantity","type":"uint8"}],"name":"CratesOpened","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"paused","type":"bool"}],"name":"SetPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]