Skip to content

Commit

Permalink
Added tests for fractional and non-fractional pricing.
Browse files Browse the repository at this point in the history
  • Loading branch information
miohtama committed Apr 20, 2017
1 parent 619a4cc commit 8421a29
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 16 deletions.
99 changes: 95 additions & 4 deletions ico/tests/contracts/test_milestone_pricing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
import datetime

import pytest
from decimal import Decimal
from eth_utils import to_wei
from ethereum.tester import TransactionFailed
from web3.contract import Contract


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


@pytest.fixture
Expand Down Expand Up @@ -39,11 +41,25 @@ def end_time(start_time) -> int:


@pytest.fixture
def token(uncapped_token) -> int:
def token(uncapped_token) -> Contract:
"""Token contract used in milestone tests"""
return uncapped_token


@pytest.fixture
def fractional_token(chain, token_name, token_symbol, team_multisig) -> Contract:
"""Token contract having 8 decimal places."""

args = [token_name, token_symbol, 0, 8] # Owner set

tx = {
"from": team_multisig
}

contract, hash = chain.provider.deploy_contract('CrowdsaleToken', deploy_args=args, deploy_transaction=tx)
return contract


@pytest.fixture
def milestone_pricing(chain, presale_fund_collector, start_time, end_time):

Expand Down Expand Up @@ -130,7 +146,7 @@ def test_milestone_data(chain, milestone_pricing, start_time):
print("-", price)


def test_milestone_prices(chain, milestone_pricing, start_time, customer):
def test_milestone_prices(chain, milestone_pricing, start_time, end_time, customer):
"""We get correct milestone prices for different dates."""

time_travel(chain, start_time - 1)
Expand All @@ -152,15 +168,47 @@ def test_milestone_prices(chain, milestone_pricing, start_time, customer):
time_travel(chain, int((datetime.datetime(2017, 4, 29, 16, 0) - datetime.datetime(1970, 1, 1)).total_seconds()))
assert milestone_pricing.call().getCurrentPrice() == to_wei("0.13", "ether")

# See that we divide price correctly
# 3 week forward + last second
time_travel(chain, end_time - 1)
assert milestone_pricing.call().getCurrentPrice() == to_wei("0.14", "ether")


def test_non_fractional_price(chain, milestone_pricing, customer, end_time):
"""We divide price correctly for integer only amount."""
time_travel(chain, end_time - 1)

assert milestone_pricing.call().calculatePrice(
to_wei("0.28", "ether"),
0,
0,
customer,
0,
) == 2

assert milestone_pricing.call().calculatePrice(
to_wei("0.281", "ether"),
0,
0,
customer,
0,
) == 2

assert milestone_pricing.call().calculatePrice(
to_wei("0.26", "ether"),
to_wei("0.4199", "ether"),
0,
0,
customer,
0,
) == 2

assert milestone_pricing.call().calculatePrice(
to_wei("0.42", "ether"),
0,
0,
customer,
0,
) == 3


def test_milestone_calculate_preico_price(chain, milestone_pricing, start_time, presale_fund_collector):
"""Preico contributors get their special price."""
Expand Down Expand Up @@ -197,3 +245,46 @@ def test_presale_move_to_milestone_based_crowdsale(chain, presale_fund_collector
milestone_ico.call().investedAmountOf(customer) == to_wei(50, "ether")
token.call().balanceOf(customer) == 50 / 0.050


def test_fractional_preico_pricing(presale_fund_collector, milestone_pricing, fractional_token):
"""Pre-ICO amount is calculated correctly for a token having fractions.
"""

amount = milestone_pricing.call().calculatePrice(
to_wei("0.05", "ether"),
0,
0,
presale_fund_collector.address,
fractional_token.call().decimals()
)

assert amount == 100000000
d = decimalize_token_amount(fractional_token, amount)

assert d == 1

# Make sure we get decimals right
assert d.as_tuple() == Decimal("1.00000000").as_tuple()


def test_fractional_milestone_pricing(chain, presale_fund_collector, milestone_pricing, fractional_token, customer):
"""Milestone amount is calculated correctly for a token having fractions."""

time_travel(chain, milestone_pricing.call().getPricingStartsAt() + 1)

amount = milestone_pricing.call().calculatePrice(
to_wei("0.512345678", "ether"),
0,
0,
customer,
fractional_token.call().decimals()
)

assert amount == 512345678
d = decimalize_token_amount(fractional_token, amount)

assert d == Decimal("5.12345678")

# Make sure we get decimals right
assert d.as_tuple() == Decimal("5.12345678").as_tuple()
40 changes: 28 additions & 12 deletions ico/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Optional

from decimal import Decimal
from eth_abi import encode_abi
from eth_abi.exceptions import EncodingError
from eth_utils import add_0x_prefix
Expand All @@ -17,6 +18,22 @@
from populus.utils.linking import find_link_references


truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1'))
falsey = frozenset(('f', 'false', 'n', 'no', 'off', '0'))


def asbool(s):
""" Return the boolean value ``True`` if the case-lowered value of string
input ``s`` is a :term:`truthy string`. If ``s`` is already one of the
boolean values ``True`` or ``False``, return it."""
if s is None:
return False
if isinstance(s, bool):
return s
s = str(s).strip()
return s.lower() in truthy


def check_succesful_tx(web3: Web3, txid: str, timeout=180) -> dict:
"""See if transaction went through (Solidity code did not throw).
Expand Down Expand Up @@ -71,16 +88,15 @@ def get_address(name):
return libraries


truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1'))
falsey = frozenset(('f', 'false', 'n', 'no', 'off', '0'))
def decimalize_token_amount(contract: Contract, amount: int) -> Decimal:
"""Convert raw fixed point token amount to decimal format.
:param contract: ERC-20 token contract with decimals field
:param amount: Raw token amount
:return: The resulting :py:class:`decimal.Decimal` carries a correct decimal places.
"""
val = Decimal(amount) / Decimal(10 ** contract.call().decimals())
quantizer = Decimal(1) / Decimal(10 ** contract.call().decimals())
return val.quantize(quantizer)


def asbool(s):
""" Return the boolean value ``True`` if the case-lowered value of string
input ``s`` is a :term:`truthy string`. If ``s`` is already one of the
boolean values ``True`` or ``False``, return it."""
if s is None:
return False
if isinstance(s, bool):
return s
s = str(s).strip()
return s.lower() in truthy

0 comments on commit 8421a29

Please sign in to comment.