diff --git a/DolphinPresaleToken.sol b/DolphinPresaleToken.sol index 1d8cc47..c923893 100644 --- a/DolphinPresaleToken.sol +++ b/DolphinPresaleToken.sol @@ -27,17 +27,52 @@ library SafeMath { } -contract CMCEthereumTicker is usingOraclize { +contract Ownable { + address public owner; + + function Ownable() { + owner = msg.sender; + } + + modifier onlyOwner() { + require(msg.sender == owner); + _; + } + + function transferOwnership(address newOwner) onlyOwner { + if (newOwner != address(0)) { + owner = newOwner; + } + } + +} + +contract Freezable is Ownable { + + bool frozen = false; + + modifier whileNotFrozen {assert(frozen != true); _;} + + function freeze() onlyOwner { + frozen = true; + } + + function unfreeze() onlyOwner { + frozen = false; + } + +} + +contract CMCEthereumTicker is usingOraclize, Ownable { using SafeMath for uint; - uint centsPerETH; - uint delay; + uint256 centsPerETH; + uint256 delay; bool enabled; - address parent; address manager; - modifier onlyParentOrManager() { require(msg.sender == parent || msg.sender == manager); _; } + modifier onlyOwnerOrManager() { require(msg.sender == owner || msg.sender == manager); _; } event newOraclizeQuery(string description); event newPriceTicker(string price); @@ -46,12 +81,11 @@ contract CMCEthereumTicker is usingOraclize { function CMCEthereumTicker(address _manager, uint256 _delay) { oraclize_setProof(proofType_NONE); enabled = false; - parent = msg.sender; manager = _manager; delay = _delay; } - function getCentsPerETH() constant returns(uint) { + function getCentsPerETH() constant returns(uint256) { return centsPerETH; } @@ -60,7 +94,7 @@ contract CMCEthereumTicker is usingOraclize { } function enable() - onlyParentOrManager + onlyOwnerOrManager { require(enabled == false); enabled = true; @@ -68,7 +102,7 @@ contract CMCEthereumTicker is usingOraclize { } function disable() - onlyParentOrManager + onlyOwnerOrManager { require(enabled == true); enabled = false; @@ -83,7 +117,7 @@ contract CMCEthereumTicker is usingOraclize { } } - function update_instant() payable { + function update_instant() private { if (oraclize.getPrice("URL") > this.balance) { newOraclizeQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee"); } else { @@ -92,7 +126,7 @@ contract CMCEthereumTicker is usingOraclize { } } - function update() payable { + function update() private { if (oraclize.getPrice("URL") > this.balance) { newOraclizeQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee"); } else { @@ -101,8 +135,8 @@ contract CMCEthereumTicker is usingOraclize { } } - function payToManager(uint _amount) - onlyParentOrManager + function payToManager(uint256 _amount) + onlyOwnerOrManager { manager.transfer(_amount); } @@ -111,99 +145,114 @@ contract CMCEthereumTicker is usingOraclize { } -contract PresaleToken { - using SafeMath for uint256; - - function PresaleToken(uint256 _limitUSD, uint256 _priceCents) { - tokenManager = msg.sender; - priceCents = _priceCents; - maxSupply = (uint(10)**decimals).mul(100).mul(_limitUSD).div(_priceCents); +contract TickerController is Ownable { + + //Ticker contract + CMCEthereumTicker priceTicker; + + function createTicker(uint256 _delay) + onlyOwner + { + priceTicker = new CMCEthereumTicker(owner, _delay); } - enum Phase { - Created, - Running, - Finished, - Finalized, - Migrating, - Migrated + function attachTicker(address _tickerAddress) + onlyOwner + { + priceTicker = CMCEthereumTicker(_tickerAddress); } - // maximum token supply - uint public maxSupply; - // price of 1 token in USD cents - uint public priceCents; - // Ticker contract - CMCEthereumTicker priceTicker; - + function enableTicker() + onlyOwner + { + priceTicker.enable(); + } - //Phase on contract creation - Phase public currentPhase = Phase.Created; - - // amount of tokens already sold - uint supply = 0; - // amount of tokens given via giveTokens - uint public givenSupply = 0; + function disableTicker() + onlyOwner + { + priceTicker.disable(); + } + + function sendToTicker() payable + onlyOwner + { + assert(address(priceTicker) != 0x0); + address(priceTicker).transfer(msg.value); + } + + function withdrawFromTicker(uint _amount) + onlyOwner + { + assert(address(priceTicker) != 0x0); + priceTicker.payToManager(_amount); + } + + function tickerAddress() constant returns (address) { + return address(priceTicker); + } + + function getCentsPerETH() constant returns (uint256) { + return priceTicker.getCentsPerETH(); + } +} - // Token manager has exclusive priveleges to call administrative - // functions on this contract. - address public tokenManager; - // Migration manager has privileges to burn tokens during migration. - address public migrationManager; +contract DBIPToken is Freezable { + using SafeMath for uint256; - // The last buyer is the buyer that purchased - // tokens that add up to the maxSupply or more. - // During the presale finalization they are refunded - // the excess USD according to lastCentsPerETH. - address lastBuyer; - uint refundValue = 0; - uint lastCentsPerETH = 0; + //ERC20 Fields + + uint supply; - //ERC 20 Containers + //ERC20 Containers mapping (address => uint256) private balance; mapping (address => mapping(address => uint256)) private allowed; //ERC 20 Additional info string public constant name = "Dolphin Presale Token"; string public constant symbol = "DBIP"; - uint8 public constant decimals = 18; - - //External access modifiers - modifier onlyTokenManager() { require(msg.sender == tokenManager); _; } - modifier onlyMigrationManager() { require(msg.sender == migrationManager); _; } + uint256 public constant decimals = 18; - //Presale phase modifiers - modifier onlyWhileCreated() {assert(currentPhase == Phase.Created); _;} - modifier onlyWhileRunning() {assert(currentPhase == Phase.Running); _;} - modifier onlyWhileFinished() {assert(currentPhase == Phase.Finished); _;} - modifier onlyWhileFinalized() {assert(currentPhase == Phase.Finalized); _;} - modifier onlyBeforeMigration() {assert(currentPhase != Phase.Migrating && currentPhase != Phase.Migrated); _;} - modifier onlyWhileMigrating() {assert(currentPhase == Phase.Migrating); _;} + //ERC20 events + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); + //Modifier to defend against shortened address attack - modifier minimalPayloadSize(uint size) { + modifier minimalPayloadSize(uint256 size) { assert(msg.data.length >= size + 4); _; } - //Presale events - event LogBuy(address indexed owner, uint value, uint centsPerETH); - event LogGive(address indexed owner, uint value, string reason); - event LogMigrate(address indexed owner, uint value); - event LogPhaseSwitch(Phase newPhase); + function DBIPToken (uint256 _initial_supply) { + balance[owner] = _initial_supply; + supply = _initial_supply; + } - //ERC20 events - event Transfer(address indexed _from, address indexed _to, uint256 _value); - event Approval(address indexed _owner, address indexed _spender, uint256 _value); - - + //Method to generate additional tokens. Can only be called by parent contract. - function() payable { - buyTokens(msg.sender); + function raiseSupply (uint256 _new_supply) + onlyOwner + { + uint256 increase = _new_supply.sub(supply); + balance[owner] = balance[owner].add(increase); + supply = supply.add(increase); + } + + // Method that allows parent to transfer during freeze + + function ownerTransfer(address _to, uint256 _value) onlyOwner returns (bool success) + { + assert(_value > 0); + + balance[owner] = balance[owner].sub(_value); + balance[_to] = balance[_to].add(_value); + Transfer(owner,_to,_value); + + return true; } - ///ERC20 Interface functions function balanceOf(address _owner) constant returns (uint256 balanceOf) { @@ -218,7 +267,7 @@ contract PresaleToken { return allowed[_owner][_spender]; } - function transfer(address _to, uint256 _value) onlyBeforeMigration minimalPayloadSize(2 * 32) returns (bool success) + function transfer(address _to, uint256 _value) whileNotFrozen minimalPayloadSize(2 * 32) returns (bool success) { assert(_value > 0); @@ -229,7 +278,7 @@ contract PresaleToken { return true; } - function approve(address _spender, uint256 _value) onlyBeforeMigration returns (bool success) { + function approve(address _spender, uint256 _value) whileNotFrozen returns (bool success) { assert((_value == 0) || (allowed[msg.sender][_spender] == 0)); allowed[msg.sender][_spender] = _value; @@ -239,7 +288,7 @@ contract PresaleToken { } - function transferFrom(address _from, address _to, uint256 _value) onlyBeforeMigration minimalPayloadSize(3 * 32) returns (bool success) { + function transferFrom(address _from, address _to, uint256 _value) whileNotFrozen minimalPayloadSize(3 * 32) returns (bool success) { assert(_value > 0); @@ -251,36 +300,167 @@ contract PresaleToken { return true; } + +} +contract ReferralProxyHandler is Ownable { + + // Proxy is a contract throught which referral investors buy tokens + address public proxy; + // amount of tokens sold through proxy + uint256 public fundedProxy = 0; + + modifier onlyProxy { require(msg.sender == proxy); _; } + + function setProxy(address _proxy) + onlyOwner + { + proxy = _proxy; + } + + function buyThroughProxy(address _buyer) payable; + +} + +contract PresaleToken is TickerController, ReferralProxyHandler, Freezable { + using SafeMath for uint256; + + function PresaleToken(uint256 _limitUSD, uint256 _priceCents) { + priceCents = _priceCents; + maxSupply = (uint256(10)**decimals).mul(100).mul(_limitUSD).div(_priceCents); + token = new DBIPToken(maxSupply); + assert(decimals == token.decimals()); + } + + enum Phase { + Created, + Running, + Finished, + Finalized, + Migrating, + Migrated + } + + // Token + DBIPToken public token; + // maximum token supply + uint256 public maxSupply; + // price of 1 token in USD cents + uint256 public priceCents; + // Decimals of token needed for most operations + uint256 public decimals = 18; + + //Phase on contract creation + Phase public currentPhase = Phase.Created; + + // amount of tokens already sold + uint256 public funded = 0; + // amount of tokens given via giveTokens + uint256 public given = 0; + + + // Migration manager has privileges to burn tokens during migration. + address public migrationManager; + + // The last buyer is the buyer that purchased + // tokens that add up to the maxSupply or more. + // During the presale finalization they are refunded + // the excess USD according to lastCentsPerETH. + address lastBuyer; + uint256 refundValue = 0; + uint256 lastCentsPerETH = 0; + + // Whether the funding cap was already raised + bool capRaised = false; + + //External access modifier + modifier onlyMigrationManager() { require(msg.sender == migrationManager); _; } + + //Presale phase modifiers + modifier onlyWhileCreated() {assert(currentPhase == Phase.Created); _;} + modifier onlyWhileRunning() {assert(currentPhase == Phase.Running); _;} + modifier onlyWhileFinished() {assert(currentPhase == Phase.Finished); _;} + modifier onlyWhileFinalized() {assert(currentPhase == Phase.Finalized); _;} + modifier onlyBeforeMigration() {assert(currentPhase != Phase.Migrating && currentPhase != Phase.Migrated); _;} + modifier onlyWhileMigrating() {assert(currentPhase == Phase.Migrating); _;} + + //Presale events + event LogBuy(address indexed owner, uint256 value, uint256 centsPerETH); + event LogGive(address indexed owner, uint256 value, string reason); + event LogMigrate(address indexed owner, uint256 value); + event LogPhaseSwitch(Phase newPhase); + ///Presale-specific functions + + function() payable { + buyTokens(msg.sender); + } function buyTokens(address _buyer) payable + whileNotFrozen + onlyWhileRunning + { + require(msg.value != 0); + require(priceTicker.getEnabled()); + + uint256 centsPerETH = getCentsPerETH(); + require(centsPerETH != 0); + + uint256 newTokens = msg.value.mul(centsPerETH).mul(uint256(10)**decimals).div(priceCents.mul(1 ether / 1 wei)); + assert(newTokens != 0); + + if (funded.add(newTokens) > maxSupply) { + uint256 remainder = maxSupply.sub(funded); + token.ownerTransfer(_buyer, remainder); + funded = funded.add(remainder); + lastBuyer = _buyer; + refundValue = newTokens.sub(remainder); + LogBuy(_buyer, remainder, centsPerETH); + } + else { + token.ownerTransfer(_buyer, newTokens); + funded = funded.add(newTokens); + LogBuy(_buyer, newTokens, centsPerETH); + } + + if (funded == maxSupply) { + lastCentsPerETH = centsPerETH; + currentPhase = Phase.Finished; + LogPhaseSwitch(Phase.Finished); + } + } + + function buyThroughProxy(address _buyer) payable + whileNotFrozen + onlyProxy onlyWhileRunning { require(msg.value != 0); require(priceTicker.getEnabled()); - var centsPerETH = getCentsPerETH(); + uint256 centsPerETH = getCentsPerETH(); require(centsPerETH != 0); - var newTokens = msg.value.mul(centsPerETH).mul(uint(10)**decimals).div(priceCents.mul(1 ether / 1 wei)); + uint256 newTokens = msg.value.mul(centsPerETH).mul(uint256(10)**decimals).div(priceCents.mul(1 ether / 1 wei)); assert(newTokens != 0); - if (supply.add(newTokens) > maxSupply) { - var remainder = maxSupply.sub(supply); - balance[_buyer] = balance[_buyer].add(remainder); - supply = supply.add(remainder); + if (funded.add(newTokens) > maxSupply) { + uint256 remainder = maxSupply.sub(funded); + token.transfer(_buyer, remainder); + funded = funded.add(remainder); + fundedProxy = fundedProxy.add(remainder); lastBuyer = _buyer; refundValue = newTokens.sub(remainder); LogBuy(_buyer, remainder, centsPerETH); } else { - balance[_buyer] = balance[_buyer].add(newTokens); - supply = supply.add(newTokens); + token.transfer(_buyer, newTokens); + funded = funded.add(newTokens); + fundedProxy = fundedProxy.add(newTokens); LogBuy(_buyer, newTokens, centsPerETH); } - if (supply == maxSupply) { + if (funded == maxSupply) { lastCentsPerETH = centsPerETH; currentPhase = Phase.Finished; LogPhaseSwitch(Phase.Finished); @@ -291,29 +471,28 @@ contract PresaleToken { onlyMigrationManager onlyWhileMigrating { - assert(balance[_owner] != 0); - var migratedValue = balance[_owner]; - supply = supply.sub(migratedValue); - balance[_owner] = 0; + assert(token.balanceOf(_owner) != 0); + var migratedValue = token.balanceOf(_owner); + funded = funded.sub(migratedValue); LogMigrate(_owner, migratedValue); - - if(supply == 0) { + if(funded == 0) { currentPhase = Phase.Migrated; LogPhaseSwitch(Phase.Migrated); } } function startPresale() - onlyTokenManager + onlyOwner onlyWhileCreated { - assert(address(priceTicker) != 0x0); + assert(address(priceTicker) != address(0)); + token.freeze(); currentPhase = Phase.Running; LogPhaseSwitch(Phase.Running); } function finishPresale() - onlyTokenManager + onlyOwner onlyWhileRunning { lastCentsPerETH = priceTicker.getCentsPerETH(); @@ -323,22 +502,24 @@ contract PresaleToken { function finalizePresale() - onlyTokenManager + onlyOwner onlyWhileFinished { if (refundValue != 0) { - lastBuyer.transfer((refundValue.mul(priceCents).mul(1 ether / 1 wei)).div(lastCentsPerETH.mul(uint(10)**decimals))); + lastBuyer.transfer((refundValue.mul(priceCents).mul(1 ether / 1 wei)).div(lastCentsPerETH.mul(uint256(10)**decimals))); } withdrawEther(); + token.unfreeze(); currentPhase = Phase.Finalized; LogPhaseSwitch(Phase.Finalized); } function startMigration() - onlyTokenManager + onlyOwner onlyWhileFinalized { - assert(migrationManager != 0x0); + assert(migrationManager != address(0)); + token.freeze(); currentPhase = Phase.Migrating; LogPhaseSwitch(Phase.Migrating); } @@ -346,81 +527,43 @@ contract PresaleToken { function withdrawEther() private { assert(this.balance > 0); - tokenManager.transfer(this.balance); + owner.transfer(this.balance); } function setMigrationManager(address _mgr) - onlyTokenManager + onlyOwner onlyWhileFinalized { migrationManager = _mgr; } function raiseCap(uint _newCap) - onlyTokenManager - onlyWhileFinalized + whileNotFrozen + onlyOwner + onlyWhileFinished { + assert(!capRaised); assert(_newCap > maxSupply); maxSupply = _newCap; + token.raiseSupply(_newCap); + if (refundValue != 0) { + token.ownerTransfer(lastBuyer, refundValue); + funded = funded.add(refundValue); + refundValue = 0; + } currentPhase = Phase.Running; LogPhaseSwitch(Phase.Running); } - - function giveTokens(address _address, uint _value, string _reason) - onlyTokenManager + + function giveTokens(address _address, uint _value, string _reason) + whileNotFrozen + onlyOwner onlyBeforeMigration - { - balance[_address] = balance[_address].add(_value); - supply = supply.add(_value); - givenSupply = givenSupply.add(_value); + token.ownerTransfer(_address, _value); + funded = funded.add(_value); + given = given.add(_value); LogGive(_address, _value, _reason); } - ///Ticker interaction functions - - function createTicker(uint256 _delay) - onlyTokenManager - { - priceTicker = new CMCEthereumTicker(tokenManager, _delay); - } - - function attachTicker(address _tickerAddress) - onlyTokenManager - { - priceTicker = CMCEthereumTicker(_tickerAddress); - } - - function enableTicker() - onlyTokenManager - { - priceTicker.enable(); - } - - function disableTicker() - onlyTokenManager - { - priceTicker.disable(); - } - - function sendToTicker() payable - onlyTokenManager - { - assert(address(priceTicker) != 0x0); - address(priceTicker).transfer(msg.value); - } - - function withdrawFromTicker(uint _amount) { - assert(address(priceTicker) != 0x0); - priceTicker.payToManager(_amount); - } - - function tickerAddress() constant returns (address) { - return address(priceTicker); - } - - function getCentsPerETH() constant returns (uint) { - return priceTicker.getCentsPerETH(); - } - } \ No newline at end of file diff --git a/ReferralProxy.sol b/ReferralProxy.sol new file mode 100644 index 0000000..a234412 --- /dev/null +++ b/ReferralProxy.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.13; + +interface IReferralProxyHandler { + function buyThroughProxy(address _buyer) payable; +} + +contract ReferralProxy { + + IReferralProxyHandler public presaleContract; + + function ReferralProxy(address _presaleContract) { + presaleContract = IReferralProxyHandler(_presaleContract); + } + + function () payable { + presaleContract.buyThroughProxy.value(msg.value)(msg.sender); + } + + function buyTokens(address _buyer) payable { + presaleContract.buyThroughProxy.value(msg.value)(_buyer); + } + +} \ No newline at end of file