Skip to content

Commit

Permalink
Renamed contracts to make naming more precise. Added bonus finalizer.
Browse files Browse the repository at this point in the history
  • Loading branch information
miohtama committed Apr 5, 2017
1 parent 102a678 commit 2cea101
Show file tree
Hide file tree
Showing 16 changed files with 475 additions and 296 deletions.
60 changes: 60 additions & 0 deletions contracts/BonusFinalizeAgent.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
pragma solidity ^0.4.6;

import "./Crowdsale.sol";
import "./CrowdsaleToken.sol";
import "./SafeMathLib.sol";

/**
* At the end of the successful crowdsale allocate % bonus of tokens to the team.
*
* Unlock tokens.
*
* BonusAllocationFinal must be set as the minting agent for the MintableToken.
*
*/
contract BonusFinalizeAgent is FinalizeAgent {

using SafeMathLib for uint;

CrowdsaleToken public token;
Crowdsale public crowdsale;

/** Total percent of tokens minted to the team at the end of the sale as base points (0.0001) */
uint public bonusBasePoints;

/** Where we move the tokens at the end of the sale. */
address public teamMultisig;

/* How much bonus tokens we allocated */
uint public allocatedBonus;

function BonusFinalizeAgent(CrowdsaleToken _token, Crowdsale _crowdsale, uint _bonusBasePoints, address _teamMultisig) {
token = _token;
crowdsale = _crowdsale;
bonusBasePoints = _bonusBasePoints;
teamMultisig = _teamMultisig;
}

/* Can we run finalize properly */
function isSane() public constant returns (bool) {
return (token.mintAgents(address(this)) == true) && (token.releaseAgent() == address(this));
}

/** Called once by crowdsale finalize() if the sale was success. */
function finalizeCrowdsale() {
if(msg.sender != address(crowdsale)) {
throw;
}

// How many % of tokens the founders and others get
uint tokensSold = crowdsale.tokensSold();
allocatedBonus = tokensSold.times(bonusBasePoints) / 10000;

// move tokens to the team multisig wallet
token.mint(teamMultisig, allocatedBonus);

// Make token transferable
token.releaseTokenTransfer();
}

}
20 changes: 7 additions & 13 deletions contracts/Crowdsale.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ contract Crowdsale is Haltable {
/* tokens will be transfered from this address */
address public multisigWallet;

/* The party who holds the full token pool and has approve()'ed tokens for this crowdsale */
address public beneficiary;

/* if the funding goal is not reached, investors may withdraw their funds */
uint public minimumFundingGoal;

Expand Down Expand Up @@ -72,19 +69,20 @@ contract Crowdsale is Haltable {

/** State machine
*
* - Preparing: All contract initialization calls and variables have not been set yet
* - Prefunding: We have not started yet
* - Funding: Active crowdsale
* - Success: Minimum funding goal reached
* - Failure: Minimum funding goal not reached before ending time
* - Finalized: The finalized has been called and succesfully executed
* - Refunding: Refunds are loaded on the contract for reclaim.
*/
enum State{Unknown, PreFunding, Funding, Success, Failure, Finalized, Refunding}
enum State{Unknown, Preparing, PreFunding, Funding, Success, Failure, Finalized, Refunding}

event Invested(address investor, uint weiAmount, uint tokenAmount);
event Refund(address investor, uint weiAmount);

function Crowdsale(address _token, address _pricingStrategy, address _multisigWallet, address _beneficiary, uint _start, uint _end, uint _minimumFundingGoal) {
function Crowdsale(address _token, address _pricingStrategy, address _multisigWallet, uint _start, uint _end, uint _minimumFundingGoal) {

owner = msg.sender;

Expand All @@ -97,12 +95,6 @@ contract Crowdsale is Haltable {
throw;
}

// TODO: remove beneficiary from the base class
beneficiary = _beneficiary;
if(beneficiary == 0) {
throw;
}

if(_start == 0) {
throw;
}
Expand Down Expand Up @@ -207,7 +199,7 @@ contract Crowdsale is Haltable {
finalized = true;
}

function setFinalizeAgent(FinalizeAgent addr) onlyOwner inState(State.PreFunding) {
function setFinalizeAgent(FinalizeAgent addr) onlyOwner inState(State.Preparing) {
finalizeAgent = addr;

// Don't allow setting bad agent
Expand Down Expand Up @@ -252,6 +244,8 @@ contract Crowdsale is Haltable {
*/
function getState() public constant returns (State) {
if(finalized) return State.Finalized;
else if (address(finalizeAgent) == 0) return State.Preparing;
else if (!finalizeAgent.isSane()) return State.Preparing;
else if (block.timestamp < startsAt) return State.PreFunding;
else if (block.timestamp <= endsAt && !isCrowdsaleFull()) return State.Funding;
else if (isMinimumGoalReached()) return State.Success;
Expand Down Expand Up @@ -284,7 +278,7 @@ contract Crowdsale is Haltable {
* @param weiAmount The amount of wei the investor tries to invest in the current transaction
* @param tokenAmount The amount of tokens we try to give to the investor in the current transaction
* @param weiRaisedTotal What would be our total raised balance after this transaction
* @param tokensSoldTotal What would be our total sold tokens countafter this transaction
* @param tokensSoldTotal What would be our total sold tokens count after this transaction
*
* @return true if taking this investment would break our cap rules
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,23 @@ import "./ReleasableToken.sol";
*
* Unlock tokens.
*/
contract DefaultCrowdsaleFinal is FinalizeAgent {
contract DefaultFinalizeAgent is FinalizeAgent {

ReleasableToken public token;
Crowdsale public crowdsale;

function DefaultCrowdsaleFinal(ReleasableToken _token, Crowdsale _crowdsale) {
function DefaultFinalizeAgent(ReleasableToken _token, Crowdsale _crowdsale) {
token = _token;
crowdsale = _crowdsale;
}

/** Check that we can release the token */
function isSane() public constant returns (bool) {
return (token.releaseAgent() == address(this));
}

/** Called once by crowdsale finalize() if the sale was success. */
function finalizeCrowdsale() {
function finalizeCrowdsale() public {
if(msg.sender != address(crowdsale)) {
throw;
}
Expand Down
7 changes: 7 additions & 0 deletions contracts/FinalizeAgent.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ contract FinalizeAgent {
return true;
}

/** Return true if we can run finalizeCrowdsale() properly.
*
* This is a safety check function that doesn't allow crowdsale to begin
* unless the finalizer has been set up properly.
*/
function isSane() public constant returns (bool);

/** Called once by crowdsale finalize() if the sale was success. */
function finalizeCrowdsale();

Expand Down
42 changes: 42 additions & 0 deletions contracts/MintenTokenCappedCrowdsale.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
pragma solidity ^0.4.8;

import "./Crowdsale.sol";
import "./MintableToken.sol";

/**
* ICO crowdsale contract that is capped by amout of tokens.
*
* - Tokens are dynamically created during the crowdsale
*
* - At the end of the crowdsale, a
*
*
*/
contract MintedTokenCappedCrowdsale is Crowdsale {

/* Maximum amount of tokens this crowdsale can sell. */
uint public maximumTokensSold;

function MintedTokenCappedCrowdsale(address _token, address _pricingStrategy, address _multisigWallet, uint _start, uint _end, uint _minimumFundingGoal, uint _maximumTokensSold) Crowdsale(_token, _pricingStrategy, _multisigWallet, _start, _end, _minimumFundingGoal) {
maximumTokensSold = _maximumTokensSold;
}

/**
* Called from invest() to confirm if the curret investment does not break our cap rule.
*/
function isBreakingCap(uint weiAmount, uint tokenAmount, uint weiRaisedTotal, uint tokensSoldTotal) constant returns (bool limitBroken) {
return tokensSoldTotal > maximumTokensSold;
}

function isCrowdsaleFull() public constant returns (bool) {
return tokensSold >= maximumTokensSold;
}

/**
* Dynamically create tokens and assign them to the investor.
*/
function assignTokens(address receiver, uint tokenAmount) private {
MintableToken mintableToken = MintableToken(token);
mintableToken.mint(receiver, tokenAmount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,24 @@ import "./Crowdsale.sol";
*
* Intended usage
*
* - Tokens have precreated supply "premined"
* - Small share of tokens of the actual crowdsale
* - A short time window
* - Flat price
* - beneficiary is the party who is supplying the tokens for this ICO
*
*/
contract CappedCrowdsale is Crowdsale {
contract PreminedCappedCrowdsale is Crowdsale {

/** How many ETH in max we are allowed to raise */
uint public weiCap;

function CappedCrowdsale(address _token, address _pricingStrategy, address _multisigWallet, address _beneficiary, uint _start, uint _end, uint _minimumFundingGoal, uint _weiCap) Crowdsale(_token, _pricingStrategy, _multisigWallet, _beneficiary, _start, _end, _minimumFundingGoal) {
/* The party who holds the full token pool and has approve()'ed tokens for this crowdsale */
address public beneficiary;

function PreminedCappedCrowdsale(address _token, address _pricingStrategy, address _multisigWallet, uint _start, uint _end, uint _minimumFundingGoal, uint _weiCap, address _beneficiary) Crowdsale(_token, _pricingStrategy, _multisigWallet, _start, _end, _minimumFundingGoal) {
weiCap = _weiCap;
beneficiary = _beneficiary;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion contracts/UncappedCrowdsale.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.4.8;
import "./Crowdsale.sol";
import "./MintableToken.sol";


/**
* Uncapped ICO crowdsale contract.
*
Expand All @@ -16,7 +17,7 @@ import "./MintableToken.sol";
*/
contract UncappedCrowdsale is Crowdsale {

function UncappedCrowdsale(address _token, address _pricingStrategy, address _multisigWallet, address _beneficiary, uint _start, uint _end, uint _minimumFundingGoal) Crowdsale(_token, _pricingStrategy, _multisigWallet, _beneficiary, _start, _end, _minimumFundingGoal) {
function UncappedCrowdsale(address _token, address _pricingStrategy, address _multisigWallet, uint _start, uint _end, uint _minimumFundingGoal) Crowdsale(_token, _pricingStrategy, _multisigWallet, _start, _end, _minimumFundingGoal) {

}

Expand Down
13 changes: 7 additions & 6 deletions ico/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
class CrowdsaleState(IntEnum):
"""Match Crowdsale.State in the contract."""
Unknown = 0
PreFunding = 1
Funding = 2
Success = 3
Failure = 4
Finalized = 5
Refunding = 6
Preparing = 1
PreFunding = 2
Funding = 3
Success = 4
Failure = 5
Finalized = 6
Refunding = 7


class UpgradeState(IntEnum):
Expand Down
27 changes: 23 additions & 4 deletions ico/tests/contracts/test_milestone_pricing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@


from ico.tests.utils import time_travel
from ico.state import CrowdsaleState


@pytest.fixture
Expand Down Expand Up @@ -62,7 +63,7 @@ def milestone_pricing(chain, presale_fund_collector, start_time):


@pytest.fixture
def milestone_ico(chain, beneficiary, team_multisig, start_time, milestone_pricing, preico_cap, preico_funding_goal, token, presale_fund_collector) -> Contract:
def milestone_ico(chain, team_multisig, start_time, milestone_pricing, preico_cap, preico_funding_goal, token, presale_fund_collector) -> Contract:
"""Create a crowdsale contract that uses milestone based pricing."""

ends_at = start_time + 4*24*3600
Expand All @@ -71,7 +72,6 @@ def milestone_ico(chain, beneficiary, team_multisig, start_time, milestone_prici
token.address,
milestone_pricing.address,
team_multisig,
beneficiary,
start_time,
ends_at,
0,
Expand All @@ -86,13 +86,30 @@ def milestone_ico(chain, beneficiary, team_multisig, start_time, milestone_prici
assert contract.call().owner() == team_multisig
assert not token.call().released()

# Allow pre-ico contract to do mint()
# Allow crowdsale contract to do mint()
token.transact({"from": team_multisig}).setMintAgent(contract.address, True)
assert token.call().mintAgents(contract.address) == True

return contract



@pytest.fixture()
def finalizer(chain, token, milestone_ico, team_multisig) -> Contract:
"""Set crowdsale end strategy."""

# Create finalizer contract
args = [
token.address,
milestone_ico.address,
]
contract, hash = chain.provider.deploy_contract('DefaultFinalizeAgent', deploy_args=args)

token.transact({"from": team_multisig}).setReleaseAgent(contract.address)
milestone_ico.transact({"from": team_multisig}).setFinalizeAgent(contract.address)
return contract


def test_milestone_getter(chain, milestone_pricing, start_time):
"""Milestone data is exposed to the world."""

Expand Down Expand Up @@ -147,7 +164,7 @@ def test_milestone_calculate_preico_price(chain, milestone_pricing, start_time,
) == 1


def test_presale_move_to_milestone_crowdsale(chain, presale_fund_collector, milestone_ico, token, start_time, team_multisig, customer, customer_2):
def test_presale_move_to_milestone_based_crowdsale(chain, presale_fund_collector, milestone_ico, finalizer, token, start_time, team_multisig, customer, customer_2):
"""When pre-ico contract funds are moved to the crowdsale, the pre-sale investors gets tokens with a preferred price and not the current milestone price."""

value = to_wei(50, "ether")
Expand All @@ -157,6 +174,8 @@ def test_presale_move_to_milestone_crowdsale(chain, presale_fund_collector, mile
presale_fund_collector.transact({"from": team_multisig}).setCrowdsale(milestone_ico.address)
time_travel(chain, start_time)

assert milestone_ico.call().getState() == CrowdsaleState.Funding

# Load funds to ICO
presale_fund_collector.transact().parcipateCrowdsaleAll()

Expand Down

0 comments on commit 2cea101

Please sign in to comment.