Skip to content

Commit

Permalink
Merge pull request #58 from TokenMarketNet/feat/preico-improvements
Browse files Browse the repository at this point in the history
Various pre-ico improvements
  • Loading branch information
miohtama committed Aug 31, 2017
2 parents 68c2b86 + fa7feed commit 0d9e9ca
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 32 deletions.
5 changes: 5 additions & 0 deletions contracts/Haltable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ contract Haltable is Ownable {
_;
}

modifier stopNonOwnersInEmergency {
if (halted && msg.sender != owner) throw;
_;
}

modifier onlyInEmergency {
if (!halted) throw;
_;
Expand Down
46 changes: 36 additions & 10 deletions contracts/PreICOProxyBuyer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ contract PreICOProxyBuyer is Ownable, Haltable, SafeMath {
uint public investorCount;

/** How many wei we have raised totla. */
uint public weiRaisedTotal;
uint public weiRaised;

/** Who are our investors (iterable) */
address[] public investors;
Expand Down Expand Up @@ -61,14 +61,17 @@ contract PreICOProxyBuyer is Ownable, Haltable, SafeMath {

uint public totalClaimed;

/** This is used to signal that we want the refund **/
bool public forcedRefund;

/** Our ICO contract where we will move the funds */
Crowdsale public crowdsale;

/** What is our current state. */
enum State{Unknown, Funding, Distributing, Refunding}

/** Somebody loaded their investment money */
event Invested(address investor, uint value, uint128 customerId);
event Invested(address investor, uint weiAmount, uint tokenAmount, uint128 customerId);

/** Refund claimed */
event Refunded(address investor, uint value);
Expand Down Expand Up @@ -144,19 +147,21 @@ contract PreICOProxyBuyer is Ownable, Haltable, SafeMath {
investorCount++;
}

weiRaisedTotal = safeAdd(weiRaisedTotal, msg.value);
if(weiRaisedTotal > weiCap) {
weiRaised = safeAdd(weiRaised, msg.value);
if(weiRaised > weiCap) {
throw;
}

Invested(investor, msg.value, customerId);
// We will use the same event form the Crowdsale for compatibility reasons
// despite not having a token amount.
Invested(investor, msg.value, 0, customerId);
}

function investWithId(uint128 customerId) public stopInEmergency payable {
function buyWithCustomerId(uint128 customerId) public stopInEmergency payable {
invest(customerId);
}

function investWithoutId() public stopInEmergency payable {
function buy() public stopInEmergency payable {
invest(0x0);
}

Expand All @@ -166,7 +171,7 @@ contract PreICOProxyBuyer is Ownable, Haltable, SafeMath {
*
*
*/
function buyForEverybody() stopInEmergency public {
function buyForEverybody() stopNonOwnersInEmergency public {

if(getState() != State.Funding) {
// Only allow buy once
Expand All @@ -177,7 +182,7 @@ contract PreICOProxyBuyer is Ownable, Haltable, SafeMath {
if(address(crowdsale) == 0) throw;

// Buy tokens on the contract
crowdsale.invest.value(weiRaisedTotal)(address(this));
crowdsale.invest.value(weiRaised)(address(this));

// Record how many tokens we got
tokensBought = getToken().balanceOf(address(this));
Expand All @@ -199,7 +204,7 @@ contract PreICOProxyBuyer is Ownable, Haltable, SafeMath {
if(getState() != State.Distributing) {
throw;
}
return safeMul(balances[investor], tokensBought) / weiRaisedTotal;
return safeMul(balances[investor], tokensBought) / weiRaised;
}

/**
Expand Down Expand Up @@ -270,10 +275,26 @@ contract PreICOProxyBuyer is Ownable, Haltable, SafeMath {
if(!crowdsale.isCrowdsale()) true;
}

/// @dev This is used in the first case scenario, this will force the state
/// to refunding. This can be also used when the ICO fails to meet the cap.
function forceRefund() public onlyOwner {
forcedRefund = true;
}

/// @dev This should be used if the Crowdsale fails, to receive the refuld money.
/// we can't use Crowdsale's refund, since our default function does not
/// accept money in.
function loadRefund() public payable {
if(getState() != State.Refunding) throw;
}

/**
* Resolve the contract umambigious state.
*/
function getState() public returns(State) {
if (forcedRefund)
return State.Refunding;

if(tokensBought == 0) {
if(now >= freezeEndsAt) {
return State.Refunding;
Expand All @@ -285,6 +306,11 @@ contract PreICOProxyBuyer is Ownable, Haltable, SafeMath {
}
}

/** Interface marker. */
function isPresale() public constant returns (bool) {
return true;
}

/** Explicitly call function from your wallet. */
function() payable {
throw;
Expand Down
6 changes: 3 additions & 3 deletions ico/tests/contracts/test_deploy_acceptance.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def proxy_buyers(project, chain, web3, customer, everything_deployed, deploy_add

# Load presale contract with money
assert proxy_buyer.call().getState() == 1
proxy_buyer.transact({"value": to_wei(amount, "ether"), "from": investor}).investWithoutId()
proxy_buyer.transact({"value": to_wei(amount, "ether"), "from": investor}).buy()

# Set ICO
proxy_buyer.transact({"from": deploy_address}).setCrowdsale(crowdsale.address)
Expand Down Expand Up @@ -143,10 +143,10 @@ def test_deploy_all(chain, web3, everything_deployed, proxy_buyers, presale_inve
for proxy_buyer in proxy_buyers:

assert proxy_buyer.call().getState() == 1
assert proxy_buyer.call().weiRaisedTotal() > 0
assert proxy_buyer.call().weiRaised() > 0

# Calculate how much all presales raise total
wei = proxy_buyer.call().weiRaisedTotal()
wei = proxy_buyer.call().weiRaised()
amount = pricing_strategy.call().calculatePrice(
wei,
0,
Expand Down
112 changes: 93 additions & 19 deletions ico/tests/contracts/test_preico_proxy_buy.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,17 @@ def test_proxy_buy(chain, web3, customer, customer_2, team_multisig, proxy_buyer
"""Buy proxy as customer."""

assert proxy_buyer.call().getState() == 1
assert proxy_buyer.call().isPresale() == True

#Change owner to customer_2, and back to team_multisig
proxy_buyer.transact({"from": team_multisig}).transferOwnership(customer_2)
proxy_buyer.transact({"from": customer_2}).transferOwnership(team_multisig)

proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).investWithoutId()
proxy_buyer.transact({"value": to_wei(20000, "ether"), "from": customer_2}).investWithoutId()
proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).buy()
proxy_buyer.transact({"value": to_wei(20000, "ether"), "from": customer_2}).buy()

# Everything funder
assert proxy_buyer.call().weiRaisedTotal() == to_wei(30000, "ether")
assert proxy_buyer.call().weiRaised() == to_wei(30000, "ether")
assert web3.eth.getBalance(proxy_buyer.address) == to_wei(30000, "ether")
assert proxy_buyer.call().balances(customer) == to_wei(10000, "ether")
assert proxy_buyer.call().balances(customer_2) == to_wei(20000, "ether")
Expand Down Expand Up @@ -160,7 +161,7 @@ def test_proxy_buy_with_id(chain, web3, customer, customer_2, team_multisig, pro
assert proxy_buyer.call().getState() == 1

customer_id = int(uuid.uuid4().hex, 16) # Customer ids are 128-bit UUID v4
proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).investWithId(customer_id)
proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).buyWithCustomerId(customer_id)

events = proxy_buyer.pastEvents("Invested").get()
assert len(events) == 1
Expand All @@ -173,7 +174,7 @@ def test_proxy_buy_claim_twice(chain, web3, customer, customer_2, team_multisig,

assert proxy_buyer.call().getState() == 1

proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).investWithoutId()
proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).buy()

# Move over
assert crowdsale.call().getState() == CrowdsaleState.Funding
Expand Down Expand Up @@ -214,8 +215,8 @@ def test_proxy_buy_refund(chain, web3, proxy_buyer, crowdsale, customer, custome
"""We can refund"""

value = to_wei(1, "ether")
proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).investWithoutId()
proxy_buyer.transact({"value": to_wei(20000, "ether"), "from": customer_2}).investWithoutId()
proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).buy()
proxy_buyer.transact({"value": to_wei(20000, "ether"), "from": customer_2}).buy()

time_travel(chain, proxy_buyer.call().freezeEndsAt() + 1)
assert proxy_buyer.call().getState() == 3 # Refunding
Expand All @@ -228,16 +229,57 @@ def test_proxy_buy_refund(chain, web3, proxy_buyer, crowdsale, customer, custome
assert proxy_buyer.call().balances(customer) == 0


def test_proxy_buy_force_refund(chain, web3, proxy_buyer, crowdsale, customer, customer_2, team_multisig):
"""We force the contract into refund"""

value = to_wei(1, "ether")
proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).buy()
proxy_buyer.transact({"value": to_wei(20000, "ether"), "from": customer_2}).buy()

proxy_buyer.transact({"from": team_multisig}).forceRefund()
assert proxy_buyer.call().getState() == 3 # Refunding

before_refund = web3.eth.getBalance(customer)
proxy_buyer.transact({"from": customer}).refund()
after_refund = web3.eth.getBalance(customer)

assert from_wei(after_refund - before_refund, "ether") > 0.99 # gas cost epsilon
assert proxy_buyer.call().balances(customer) == 0


def test_proxy_buy_load_refund(chain, web3, customer, customer_2, team_multisig, proxy_buyer, crowdsale, token):
"""Fail the crowdsale, refund the pre-investors"""

assert proxy_buyer.call().getState() == 1

proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).buy()

# Move over
assert crowdsale.call().getState() == CrowdsaleState.Funding
proxy_buyer.transact({"from": team_multisig}).setCrowdsale(crowdsale.address)
assert proxy_buyer.call().crowdsale() == crowdsale.address
proxy_buyer.transact({"from": customer}).buyForEverybody()

proxy_buyer.transact({"from": team_multisig}).forceRefund()
proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).loadRefund()

before_refund = web3.eth.getBalance(customer)
proxy_buyer.transact({"from": customer}).refund()
after_refund = web3.eth.getBalance(customer)

assert from_wei(after_refund - before_refund, "ether") > 0.99 # gas cost epsilon


def test_proxy_buy_move_funds_twice(chain, web3, customer, customer_2, team_multisig, proxy_buyer, crowdsale, token):
"""We move funds only once."""

assert proxy_buyer.call().getState() == 1

proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).investWithoutId()
proxy_buyer.transact({"value": to_wei(20000, "ether"), "from": customer_2}).investWithoutId()
proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).buy()
proxy_buyer.transact({"value": to_wei(20000, "ether"), "from": customer_2}).buy()

# Everything funder
assert proxy_buyer.call().weiRaisedTotal() == to_wei(30000, "ether")
assert proxy_buyer.call().weiRaised() == to_wei(30000, "ether")
assert web3.eth.getBalance(proxy_buyer.address) == to_wei(30000, "ether")
assert proxy_buyer.call().balances(customer) == to_wei(10000, "ether")
assert proxy_buyer.call().balances(customer_2) == to_wei(20000, "ether")
Expand All @@ -253,12 +295,12 @@ def test_proxy_buy_move_funds_twice(chain, web3, customer, customer_2, team_mult


def test_proxy_buy_claim_too_much(chain, web3, customer, customer_2, team_multisig, proxy_buyer, crowdsale, token):
"""You cannot claim more you got in the fair sahre"""
"""You cannot claim more you got in the fair share"""

assert proxy_buyer.call().getState() == 1

proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).investWithoutId()
proxy_buyer.transact({"value": to_wei(20000, "ether"), "from": customer_2}).investWithoutId()
proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).buy()
proxy_buyer.transact({"value": to_wei(20000, "ether"), "from": customer_2}).buy()

# Move over
assert crowdsale.call().getState() == CrowdsaleState.Funding
Expand Down Expand Up @@ -288,23 +330,23 @@ def test_proxy_buy_too_much(chain, web3, customer, customer_2, team_multisig, pr
assert proxy_buyer.call().getState() == 1

with pytest.raises(TransactionFailed):
proxy_buyer.transact({"value": to_wei(100001, "ether"), "from": customer}).investWithoutId()
proxy_buyer.transact({"value": to_wei(100001, "ether"), "from": customer}).buy()

def test_proxy_min_buyin(chain, web3, customer, customer_2, team_multisig, proxy_buyer, crowdsale, token):
"""Try to buy over the cap."""

assert proxy_buyer.call().getState() == 1

with pytest.raises(TransactionFailed):
proxy_buyer.transact({"value": 1, "from": customer}).investWithoutId()
proxy_buyer.transact({"value": 1, "from": customer}).buy()

def test_proxy_max_buyin(chain, web3, customer, customer_2, team_multisig, proxy_buyer, crowdsale, token):
"""Try to buy over the cap."""

assert proxy_buyer.call().getState() == 1

with pytest.raises(TransactionFailed):
proxy_buyer.transact({"value": to_wei(44001, "ether"), "from": customer}).investWithoutId()
proxy_buyer.transact({"value": to_wei(44001, "ether"), "from": customer}).buy()


def test_proxy_buy_halted(chain, web3, customer, customer_2, team_multisig, proxy_buyer, crowdsale, token):
Expand All @@ -315,7 +357,40 @@ def test_proxy_buy_halted(chain, web3, customer, customer_2, team_multisig, prox
proxy_buyer.transact({"from": team_multisig}).halt()

with pytest.raises(TransactionFailed):
proxy_buyer.transact({"value": to_wei(1, "ether"), "from": customer}).investWithoutId()
proxy_buyer.transact({"value": to_wei(1, "ether"), "from": customer}).buy()


def test_proxy_buyforeverybody_halted(chain, web3, customer, customer_2, team_multisig, proxy_buyer, crowdsale, token):
"""You cannot buy the tokens as non-owner if the contract is halted."""

assert proxy_buyer.call().getState() == 1

proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).buy()
proxy_buyer.transact({"value": to_wei(20000, "ether"), "from": customer_2}).buy()

# Move over
assert crowdsale.call().getState() == CrowdsaleState.Funding
proxy_buyer.transact({"from": team_multisig}).setCrowdsale(crowdsale.address)
assert proxy_buyer.call().crowdsale() == crowdsale.address
proxy_buyer.transact({"from": team_multisig}).halt()
with pytest.raises(TransactionFailed):
proxy_buyer.transact({"from": customer}).buyForEverybody()


def test_proxy_buyforeverybody_halted_owner(chain, web3, customer, customer_2, team_multisig, proxy_buyer, crowdsale, token):
"""You cannot buy the tokens as non-owner if the contract is halted."""

assert proxy_buyer.call().getState() == 1

proxy_buyer.transact({"value": to_wei(10000, "ether"), "from": customer}).buy()
proxy_buyer.transact({"value": to_wei(20000, "ether"), "from": customer_2}).buy()

# Move over
assert crowdsale.call().getState() == CrowdsaleState.Funding
proxy_buyer.transact({"from": team_multisig}).setCrowdsale(crowdsale.address)
assert proxy_buyer.call().crowdsale() == crowdsale.address
proxy_buyer.transact({"from": team_multisig}).halt()
proxy_buyer.transact({"from": team_multisig}).buyForEverybody()


def test_proxy_buy_presale_pricing(chain, proxy_buyer, tranche_crowdsale, finalizer, token, team_multisig, customer, customer_2, tranche_pricing):
Expand All @@ -330,7 +405,7 @@ def test_proxy_buy_presale_pricing(chain, proxy_buyer, tranche_crowdsale, finali
assert tranche_pricing.call().isPresalePurchase(proxy_buyer.address) == True

value = to_wei(20000, "ether")
proxy_buyer.transact({"from": customer, "value": value}).investWithoutId()
proxy_buyer.transact({"from": customer, "value": value}).buy()

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

Expand Down Expand Up @@ -364,4 +439,3 @@ def test_proxy_buy_presale_pricing(chain, proxy_buyer, tranche_crowdsale, finali
# Check that presale participant gets his token with a presale price
proxy_buyer.transact({"from": customer}).claimAll()
token.call().balanceOf(customer) == 20000 / 0.05

0 comments on commit 0d9e9ca

Please sign in to comment.