Skip to content

Instantly share code, notes, and snippets.

@siman
Last active April 13, 2018 14:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save siman/a234e5b918804d8b687f9a40184a2f52 to your computer and use it in GitHub Desktop.
Save siman/a234e5b918804d8b687f9a40184a2f52 to your computer and use it in GitHub Desktop.
FTEC ERC20 token
pragma solidity ^0.4.18;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
contract MultiOwnable {
mapping (address => bool) public isOwner;
address[] public ownerHistory;
event OwnerAddedEvent(address indexed _newOwner);
event OwnerRemovedEvent(address indexed _oldOwner);
function MultiOwnable() public {
// Add default owner
address owner = msg.sender;
ownerHistory.push(owner);
isOwner[owner] = true;
}
modifier onlyOwner() {
require(isOwner[msg.sender]);
_;
}
function ownerHistoryCount() public view returns (uint) {
return ownerHistory.length;
}
/** Add extra owner. */
function addOwner(address owner) onlyOwner public {
require(owner != address(0));
require(!isOwner[owner]);
ownerHistory.push(owner);
isOwner[owner] = true;
OwnerAddedEvent(owner);
}
/** Remove extra owner. */
function removeOwner(address owner) onlyOwner public {
require(isOwner[owner]);
isOwner[owner] = false;
OwnerRemovedEvent(owner);
}
}
contract Pausable is MultiOwnable {
bool public paused;
modifier ifNotPaused {
require(!paused);
_;
}
modifier ifPaused {
require(paused);
_;
}
// Called by the owner on emergency, triggers paused state
function pause() external onlyOwner ifNotPaused {
paused = true;
}
// Called by the owner on end of emergency, returns to normal state
function resume() external onlyOwner ifPaused {
paused = false;
}
}
contract ERC20 {
uint256 public totalSupply;
function balanceOf(address _owner) public view returns (uint256 balance);
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
function allowance(address _owner, address _spender) public view returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
contract StandardToken is ERC20 {
using SafeMath for uint;
mapping(address => uint256) balances;
mapping(address => mapping(address => uint256)) allowed;
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
function transfer(address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
return true;
}
/// @dev Allows allowed third party to transfer tokens from one address to another. Returns success.
/// @param _from Address from where tokens are withdrawn.
/// @param _to Address to where tokens are sent.
/// @param _value Number of tokens to transfer.
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
Transfer(_from, _to, _value);
return true;
}
/// @dev Sets approved amount of tokens for spender. Returns success.
/// @param _spender Address of allowed account.
/// @param _value Number of approved tokens.
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
/// @dev Returns number of allowed tokens for given address.
/// @param _owner Address of token owner.
/// @param _spender Address of token spender.
function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return allowed[_owner][_spender];
}
}
contract CommonToken is StandardToken, MultiOwnable {
string public constant name = 'FTEC';
string public constant symbol = 'FTEC';
uint8 public constant decimals = 18;
uint256 public saleLimit; // 85% of tokens for sale.
uint256 public teamTokens; // 7% of tokens goes to the team and will be locked for 1 year.
// 8% of the rest tokens will be used for bounty, advisors, and airdrops.
// 7% of team tokens will be locked at this address for 1 year.
address public teamWallet; // Team address.
uint public unlockTeamTokensTime = now + 1 years;
// The main account that holds all tokens at the beginning and during tokensale.
address public seller; // Seller address (main holder of tokens)
uint256 public tokensSold; // (e18) Number of tokens sold through all tiers or tokensales.
uint256 public totalSales; // Total number of sales (including external sales) made through all tiers or tokensales.
// Lock the transfer functions during tokensales to prevent price speculations.
bool public locked = true;
event SellEvent(address indexed _seller, address indexed _buyer, uint256 _value);
event ChangeSellerEvent(address indexed _oldSeller, address indexed _newSeller);
event Burn(address indexed _burner, uint256 _value);
event Unlock();
function CommonToken(
address _seller,
address _teamWallet
) MultiOwnable() public {
totalSupply = 998400000 ether;
saleLimit = 848640000 ether;
teamTokens = 69888000 ether;
seller = _seller;
teamWallet = _teamWallet;
uint sellerTokens = totalSupply - teamTokens;
balances[seller] = sellerTokens;
Transfer(0x0, seller, sellerTokens);
balances[teamWallet] = teamTokens;
Transfer(0x0, teamWallet, teamTokens);
}
modifier ifUnlocked(address _from) {
require(!locked);
// If requested a transfer from the team wallet:
if (_from == teamWallet) {
require(now >= unlockTeamTokensTime);
}
_;
}
/** Can be called once by super owner. */
function unlock() onlyOwner public {
require(locked);
locked = false;
Unlock();
}
/**
* An address can become a new seller only in case it has no tokens.
* This is required to prevent stealing of tokens from newSeller via
* 2 calls of this function.
*/
function changeSeller(address newSeller) onlyOwner public returns (bool) {
require(newSeller != address(0));
require(seller != newSeller);
// To prevent stealing of tokens from newSeller via 2 calls of changeSeller:
require(balances[newSeller] == 0);
address oldSeller = seller;
uint256 unsoldTokens = balances[oldSeller];
balances[oldSeller] = 0;
balances[newSeller] = unsoldTokens;
Transfer(oldSeller, newSeller, unsoldTokens);
seller = newSeller;
ChangeSellerEvent(oldSeller, newSeller);
return true;
}
/**
* User-friendly alternative to sell() function.
*/
function sellNoDecimals(address _to, uint256 _value) public returns (bool) {
return sell(_to, _value * 1e18);
}
function sell(address _to, uint256 _value) onlyOwner public returns (bool) {
// Check that we are not out of limit and still can sell tokens:
require(tokensSold.add(_value) <= saleLimit);
require(_to != address(0));
require(_value > 0);
require(_value <= balances[seller]);
balances[seller] = balances[seller].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(seller, _to, _value);
totalSales++;
tokensSold = tokensSold.add(_value);
SellEvent(seller, _to, _value);
return true;
}
/**
* Until all tokens are sold, tokens can be transfered to/from owner's accounts.
*/
function transfer(address _to, uint256 _value) ifUnlocked(msg.sender) public returns (bool) {
return super.transfer(_to, _value);
}
/**
* Until all tokens are sold, tokens can be transfered to/from owner's accounts.
*/
function transferFrom(address _from, address _to, uint256 _value) ifUnlocked(_from) public returns (bool) {
return super.transferFrom(_from, _to, _value);
}
function burn(uint256 _value) public returns (bool) {
require(_value > 0);
balances[msg.sender] = balances[msg.sender].sub(_value);
totalSupply = totalSupply.sub(_value);
Transfer(msg.sender, 0x0, _value);
Burn(msg.sender, _value);
return true;
}
}
contract CommonTokensale is MultiOwnable, Pausable {
using SafeMath for uint;
address public beneficiary1;
address public beneficiary2;
address public beneficiary3;
// Balances of beneficiaries:
uint public balance1;
uint public balance2;
uint public balance3;
// Token contract reference.
CommonToken public token;
uint public minPaymentWei = 0.1 ether;
uint public minCapWei;
uint public maxCapWei;
uint public startTime;
uint public endTime;
// Stats for current tokensale:
uint public totalTokensSold; // Total amount of tokens sold during this tokensale.
uint public totalWeiReceived; // Total amount of wei received during this tokensale.
// This mapping stores info on how many ETH (wei) have been sent to this tokensale from specific address.
mapping (address => uint256) public buyerToSentWei;
event ReceiveEthEvent(address indexed _buyer, uint256 _amountWei);
function CommonTokensale(
address _token,
address _beneficiary1,
address _beneficiary2,
address _beneficiary3,
uint _startTime,
uint _endTime
) MultiOwnable() public {
require(_token != address(0));
token = CommonToken(_token);
beneficiary1 = _beneficiary1;
beneficiary2 = _beneficiary2;
beneficiary3 = _beneficiary3;
startTime = _startTime;
endTime = _endTime;
}
/** The fallback function corresponds to a donation in ETH. */
function() public payable {
sellTokensForEth(msg.sender, msg.value);
}
function sellTokensForEth(
address _buyer,
uint256 _amountWei
) ifNotPaused internal {
require(startTime <= now && now <= endTime);
require(_amountWei >= minPaymentWei);
require(totalWeiReceived.add(_amountWei) <= maxCapWei);
uint tokensE18 = weiToTokens(_amountWei);
// Transfer tokens to buyer.
require(token.sell(_buyer, tokensE18));
// Update total stats:
totalTokensSold = totalTokensSold.add(tokensE18);
totalWeiReceived = totalWeiReceived.add(_amountWei);
buyerToSentWei[_buyer] = buyerToSentWei[_buyer].add(_amountWei);
ReceiveEthEvent(_buyer, _amountWei);
// Split received amount between balances of three beneficiaries.
uint part = _amountWei / 3;
balance1 = balance1.add(_amountWei - part * 2);
balance2 = balance2.add(part);
balance3 = balance3.add(part);
}
/** Calc how much tokens you can buy at current time. */
function weiToTokens(uint _amountWei) public view returns (uint) {
return _amountWei.mul(tokensPerWei(_amountWei));
}
function tokensPerWei(uint _amountWei) public view returns (uint256) {
uint expectedTotal = totalWeiReceived.add(_amountWei);
// Presale pricing rules:
if (expectedTotal < 1000 ether) return 39960;
if (expectedTotal < 2000 ether) return 37480;
if (expectedTotal < 4000 ether) return 35270;
// Public sale pricing rules:
if (expectedTotal < 6000 ether) return 33300;
if (expectedTotal < 8000 ether) return 32580;
if (expectedTotal < 11000 ether) return 31880;
if (expectedTotal < 15500 ether) return 31220;
if (expectedTotal < 20500 ether) return 30590;
if (expectedTotal < 26500 ether) return 29970;
return 29970; // Default token price with no bonuses.
}
function canWithdraw() public view returns (bool);
function withdraw1(address _to) public {
require(canWithdraw());
require(msg.sender == beneficiary1);
require(balance1 > 0);
uint bal = balance1;
balance1 = 0;
_to.transfer(bal);
}
function withdraw2(address _to) public {
require(canWithdraw());
require(msg.sender == beneficiary2);
require(balance2 > 0);
uint bal = balance2;
balance2 = 0;
_to.transfer(bal);
}
function withdraw3(address _to) public {
require(canWithdraw());
require(msg.sender == beneficiary3);
require(balance3 > 0);
uint bal = balance3;
balance3 = 0;
_to.transfer(bal);
}
}
contract Presale is CommonTokensale {
// In case min (soft) cap is not reached, token buyers will be able to
// refund their contributions during 3 months after presale is finished.
uint public refundDeadlineTime;
// Total amount of wei refunded if min (soft) cap is not reached.
uint public totalWeiRefunded;
event RefundEthEvent(address indexed _buyer, uint256 _amountWei);
function Presale(
address _token,
address _beneficiary1,
address _beneficiary2,
address _beneficiary3,
uint _startTime,
uint _endTime
) CommonTokensale(
_token,
_beneficiary1,
_beneficiary2,
_beneficiary3,
_startTime,
_endTime
) public {
minCapWei = 2000 ether;
maxCapWei = 4000 ether;
refundDeadlineTime = _endTime + 3 * 30 days;
}
/**
* During presale it will be possible to withdraw only in two cases:
* min cap reached OR refund period expired.
*/
function canWithdraw() public view returns (bool) {
return totalWeiReceived >= minCapWei || now > refundDeadlineTime;
}
/**
* It will be possible to refund only if min (soft) cap is not reached and
* refund requested during 3 months after presale finished.
*/
function canRefund() public view returns (bool) {
return totalWeiReceived < minCapWei && endTime < now && now <= refundDeadlineTime;
}
function refund() public {
require(canRefund());
address buyer = msg.sender;
uint amount = buyerToSentWei[buyer];
require(amount > 0);
// Redistribute left balance between three beneficiaries.
uint newBal = this.balance.sub(amount);
uint part = newBal / 3;
balance1 = newBal - part * 2;
balance2 = part;
balance3 = part;
RefundEthEvent(buyer, amount);
buyerToSentWei[buyer] = 0;
totalWeiRefunded = totalWeiRefunded.add(amount);
buyer.transfer(amount);
}
}
contract PublicSale is CommonTokensale {
function PublicSale(
address _token,
address _beneficiary1,
address _beneficiary2,
address _beneficiary3,
uint _startTime,
uint _endTime,
uint _presaleCapEth
) CommonTokensale(
_token,
_beneficiary1,
_beneficiary2,
_beneficiary3,
_startTime,
_endTime
) public {
maxCapWei = 26500 ether;
uint presaleCapWei = _presaleCapEth * 1e18;
totalWeiReceived = totalWeiReceived.add(presaleCapWei);
}
/**
* During public sale it is possinble to withdraw at any time.
*/
function canWithdraw() public view returns (bool) {
return true;
}
}
contract ProdToken is CommonToken {
function ProdToken() CommonToken(
0x123, // TODO Set seller address (main holder of tokens)
0x123 // TODO Set team address.
) public {}
}
contract ProdPresale is Presale {
function ProdPresale() Presale(
0x123, // TODO Set token address
0x123, // TODO Set beneficiary1 address
0x123, // TODO Set beneficiary2 address
0x123, // TODO Set beneficiary3 address
1524578400, // 2018-04-24 14:00 - startTime
1526997600 // 2018-05-22 14:00 - endTime
) public {}
}
// Will be deployed after ProdPresale capmaign ended successfully.
contract ProdPublicSale is PublicSale {
function ProdPublicSale() PublicSale(
0x123, // TODO Set token address
0x123, // TODO Set beneficiary1 address
0x123, // TODO Set beneficiary2 address
0x123, // TODO Set beneficiary3 address
1529503200, // 2018-06-20 14:00 - startTime
1531922400, // 2018-07-18 14:00 - endTime
0 // TODO Set presale cap in ETH (how much was rised during presale?)
) public {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment