Skip to content

Instantly share code, notes, and snippets.

@crzang
Created March 28, 2018 12:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save crzang/a784cec819c1aaf559d9a7ef4a65ba41 to your computer and use it in GitHub Desktop.
Save crzang/a784cec819c1aaf559d9a7ef4a65ba41 to your computer and use it in GitHub Desktop.
pragma solidity ^0.4.18;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
uint256 public totalSupply;
function balanceOf(address who) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(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;
}
function percent(uint256 a,uint256 b) internal pure returns (uint256){
return mul(div(a,uint256(100)),b);
}
function power(uint256 a,uint256 b) internal pure returns (uint256){
return mul(a,10**b);
}
}
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is ERC20Basic {
using SafeMath for uint256;
mapping(address => uint256) balances;
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[msg.sender]);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is ERC20, BasicToken {
mapping (address => mapping (address => uint256)) internal allowed;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint256 the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
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 Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
*
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public view returns (uint256) {
return allowed[_owner][_spender];
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _addedValue The amount of tokens to increase the allowance by.
*/
function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
}
contract RDOToken is StandardToken {
string public name = "RDO roken";
string public symbol = "RDO";
uint256 public decimals = 8;
address owner;
address crowdsale;
event Burn(address indexed burner, uint256 value);
function RDOToken() public {
owner=msg.sender;
uint256 initialTotalSupply=1000000000;
totalSupply=initialTotalSupply.power(decimals);
balances[msg.sender]=totalSupply;
crowdsale=new RDOCrowdsale(this,msg.sender);
allocate(crowdsale,75);
allocate(0x523f6034c79915cE9AacD06867721D444c45a6a5,12);
allocate(0x50d0a8eDe1548E87E5f8103b89626bC9C76fe2f8,7);
allocate(0xD8889ff86b9454559979Aa20bb3b41527AE4b74b,3);
allocate(0x5F900841910baaC70e8b736632600c409Af05bf8,3);
}
/**
* @dev Burns a specific amount of tokens.
* @param _value The amount of token to be burned.
*/
function burn(uint256 _value) public {
require(_value <= balances[msg.sender]);
// no need to require value <= totalSupply, since that would imply the
// sender's balance is greater than the totalSupply, which *should* be an assertion failure
address burner = msg.sender;
balances[burner] = balances[burner].sub(_value);
totalSupply = totalSupply.sub(_value);
Burn(burner, _value);
}
function allocate(address _address,uint256 percent) private{
uint256 bal=totalSupply.percent(percent);
transfer(_address,bal);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender==owner);
_;
}
function stopCrowdfunding() onlyOwner public {
if(crowdsale!=0x0){
RDOCrowdsale(crowdsale).stopCrowdsale();
crowdsale=0x0;
}
}
function getCrowdsaleAddress() constant public returns(address){
return crowdsale;
}
}
/**
* @title RPOCrowdsale
* @dev RPOCrowdsale is a contract for managing a token crowdsale for RPO project.
* Crowdsale have 9 phases with start and end timestamps, where investors can make
* token purchases and the crowdsale will assign them tokens based
* on a token per ETH rate and bonuses. Collected funds are forwarded to a wallet
* as they arrive.
*/
contract RDOCrowdsale {
using SafeMath for uint256;
// The token being sold
RDOToken public token;
// External wallet where funds get forwarded
address public wallet;
// Crowdsale administrator
address public owners;
// price per 1 RDO
uint256 public price=0.55 finney;
// Phases list, see schedule in constructor
mapping (uint => Phase) phases;
// The total number of phases (0...9)
uint public totalPhases = 9;
// Description for each phase
struct Phase {
uint256 startTime;
uint256 endTime;
uint256 bonusPercent;
}
// Bonus based on value
BonusValue[] bonusValue;
struct BonusValue{
uint256 minimum;
uint256 maximum;
uint256 bonus;
}
// Minimum Deposit in eth
uint256 public constant minContribution = 100 finney;
// Amount of raised Ethers (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 bonusPercent free tokens percantage for the phase
* @param amount amount of tokens purchased
*/
event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 bonusPercent, uint256 amount);
// event for wallet update
event WalletSet(address indexed wallet);
function RDOCrowdsale(address _tokenAddress, address _wallet) public {
require(_tokenAddress != address(0));
token = RDOToken(_tokenAddress);
wallet = _wallet;
owners=msg.sender;
/*
ICO SCHEDULE
Bonus
40% 1 round
30% 2 round
25% 3 round
20% 4 round
15% 5 round
10% 6 round
7% 7 round
5% 8 round
3% 9 round
*/
fillPhase(0,40,25 days);
fillPhase(1,30,15 days);
fillPhase(2,25,15 days);
fillPhase(3,20,15 days);
fillPhase(4,15,15 days);
fillPhase(5,10,15 days);
fillPhase(6,7,15 days);
fillPhase(7,5,15 days);
fillPhase(8,3,15 days);
// Fill bonus based on value
bonusValue.push(BonusValue({
minimum:5 ether,
maximum:25 ether,
bonus:5
}));
bonusValue.push(BonusValue({
minimum:26 ether,
maximum:100 ether,
bonus:8
}));
bonusValue.push(BonusValue({
minimum:101 ether,
maximum:100000 ether,
bonus:10
}));
}
function fillPhase(uint8 index,uint256 bonus,uint256 delay) private{
phases[index].bonusPercent=bonus;
if(index==0){
phases[index].startTime = now;
}
else{
phases[index].startTime = phases[index-1].endTime;
}
phases[index].endTime = phases[index].startTime+delay;
}
// fallback function can be used to buy tokens
function () external payable {
buyTokens(msg.sender);
}
// low level token purchase function
function buyTokens(address beneficiary) public payable {
require(beneficiary != address(0));
require(msg.value != 0);
uint256 currentBonusPercent = getBonusPercent(now);
uint256 weiAmount = msg.value;
uint256 volumeBonus=getVolumeBonus(weiAmount);
require(weiAmount>=minContribution);
// calculate token amount to be created
uint256 tokens = calculateTokenAmount(weiAmount, currentBonusPercent,volumeBonus);
// update state
weiRaised = weiRaised.add(weiAmount);
token.transfer(beneficiary, tokens);
TokenPurchase(msg.sender, beneficiary, weiAmount, currentBonusPercent, tokens);
forwardFunds();
}
function getVolumeBonus(uint256 _wei) private view returns(uint256){
for(uint256 i=0;i<bonusValue.length;++i){
if(_wei>bonusValue[i].minimum && _wei<bonusValue[i].maximum){
return bonusValue[i].bonus;
}
}
return 0;
}
// If phase exists return corresponding bonus for the given date
// else return 0 (percent)
function getBonusPercent(uint256 datetime) private view returns (uint256) {
for (uint i = 0; i < totalPhases; i++) {
if (datetime >= phases[i].startTime && datetime <= phases[i].endTime) {
return phases[i].bonusPercent;
}
}
return 0;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owners==msg.sender);
_;
}
// calculates how much tokens will beneficiary get
// for given amount of wei
function calculateTokenAmount(uint256 _weiDeposit, uint256 _bonusTokensPercent,uint256 _volumeBonus) private view returns (uint256) {
uint256 mainTokens = _weiDeposit.div(price);
uint256 bonusTokens = mainTokens.percent(_bonusTokensPercent);
uint256 volumeBonus=mainTokens.percent(_volumeBonus);
return mainTokens.add(bonusTokens).add(volumeBonus);
}
// send ether to the fund collection wallet
// override to create custom fund forwarding mechanisms
function forwardFunds() internal {
wallet.transfer(msg.value);
}
function stopCrowdsale() public {
token.burn(token.balanceOf(this));
selfdestruct(wallet);
}
function getCurrentBonus() public constant returns(uint256){
return getBonusPercent(now);
}
function calculateEstimateToken(uint256 _wei) public constant returns(uint256){
uint256 timeBonus=getCurrentBonus();
uint256 volumeBonus=getVolumeBonus(_wei);
return calculateTokenAmount(_wei,timeBonus,volumeBonus);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment