contributions_to_token_batches(): takes desired_token_price as argument

TokenBatch: self.current_date to be set externally, unittests updated
Commons unittest: initialization and burn()
This commit is contained in:
Andrew Chiw 2020-05-08 01:14:21 +02:00
parent dd98402875
commit b77bb3a87e
2 changed files with 42 additions and 29 deletions

View File

@ -26,13 +26,14 @@ def hatch_raise_split_pools(total_hatch_raise, hatch_tribute) -> Tuple[float, fl
collateral_pool = total_hatch_raise * (1-hatch_tribute) collateral_pool = total_hatch_raise * (1-hatch_tribute)
return funding_pool, collateral_pool return funding_pool, collateral_pool
def contributions_to_token_batches(hatcher_contributions: List[int], initial_token_supply: int, vesting_80p_unlocked: int) -> List[float]: def contributions_to_token_batches(hatcher_contributions: List[int], desired_token_price: float, vesting_80p_unlocked: int) -> Tuple[List[float], float]:
""" """
hatcher_contributions: a list of hatcher contributions hatcher_contributions: a list of hatcher contributions in DAI/ETH/whatever
initial_token_supply: NOT denominated in millions desired_token_price: used to determine the initial token supply
vesting_80p_unlocked: vesting parameter - the number of days after which 80% of tokens will be unlocked, including the cliff period vesting_80p_unlocked: vesting parameter - the number of days after which 80% of tokens will be unlocked, including the cliff period
""" """
total_hatch_raise = sum(hatcher_contributions) total_hatch_raise = sum(hatcher_contributions)
initial_token_supply = total_hatch_raise / desired_token_price
# In the hatch, everyone buys in at the same time, with the same price. So just split the token supply amongst the hatchers proportionally to their contributions # In the hatch, everyone buys in at the same time, with the same price. So just split the token supply amongst the hatchers proportionally to their contributions
tokens_per_hatcher = [(x / total_hatch_raise) * initial_token_supply for x in hatcher_contributions] tokens_per_hatcher = [(x / total_hatch_raise) * initial_token_supply for x in hatcher_contributions]
@ -40,7 +41,7 @@ def contributions_to_token_batches(hatcher_contributions: List[int], initial_tok
cliff_days, halflife_days = convert_80p_to_cliff_and_halflife(vesting_80p_unlocked) cliff_days, halflife_days = convert_80p_to_cliff_and_halflife(vesting_80p_unlocked)
token_batches = [TokenBatch(x, cliff_days, halflife_days, hatch=True) for x in tokens_per_hatcher] token_batches = [TokenBatch(x, cliff_days, halflife_days, hatch=True) for x in tokens_per_hatcher]
return token_batches return token_batches, initial_token_supply
class TokenBatch: class TokenBatch:
def __init__(self, value: float, cliff_days: int, halflife_days: int, hatch = False): def __init__(self, value: float, cliff_days: int, halflife_days: int, hatch = False):
@ -51,16 +52,18 @@ class TokenBatch:
self.halflife_days = halflife_days self.halflife_days = halflife_days
self.spent = 0 self.spent = 0
self.current_date = datetime.today() # to be set externally before each spend check
def __repr__(self): def __repr__(self):
o = "TokenBatch {} {}, Unlocked: {}".format("Hatch" if self.hatch_tokens else "", self.value, self.unlocked_fraction(datetime.today())) o = "TokenBatch {} {}, Unlocked: {}".format("Hatch" if self.hatch_tokens else "", self.value, self.unlocked_fraction())
return o return o
def unlocked_fraction(self, day: datetime = datetime.today()) -> float: def unlocked_fraction(self) -> float:
""" """
returns what fraction of the TokenBatch is unlocked to date returns what fraction of the TokenBatch is unlocked to date
""" """
if self.hatch_tokens: if self.hatch_tokens:
days_delta = day - self.creation_date days_delta = self.current_date - self.creation_date
u = vesting_curve(days_delta.days, self.cliff_days, self.halflife_days) u = vesting_curve(days_delta.days, self.cliff_days, self.halflife_days)
return u if u > 0 else 0 return u if u > 0 else 0
else: else:
@ -72,7 +75,7 @@ class TokenBatch:
returns the argument if successful for your convenience returns the argument if successful for your convenience
""" """
if x > self.spendable(): if x > self.spendable():
raise Exception("Not so many tokens are available for you to spend yet!") raise Exception("Not so many tokens are available for you to spend yet ({})".format(self.current_date))
self.value -= x self.value -= x
self.spent += x self.spent += x
@ -118,8 +121,8 @@ class Commons:
money_returned = dai money_returned = dai
if self.exit_tribute: if self.exit_tribute:
self._funding_pool += commons.exit_tribute * dai self._funding_pool += self.exit_tribute * dai
money_returned = (1-commons.exit_tribute) * dai money_returned = (1-self.exit_tribute) * dai
return money_returned, realized_price return money_returned, realized_price

View File

@ -2,7 +2,7 @@ from hatch import *
import unittest import unittest
import datetime import datetime
class TestHatch(unittest.TestCase): class HatchTest(unittest.TestCase):
def test_vesting_curve(self): def test_vesting_curve(self):
self.assertEqual(vesting_curve(90, 90, 90), 0) # At Day 90, the cliff has just ended and the vesting curve has begun at 0 self.assertEqual(vesting_curve(90, 90, 90), 0) # At Day 90, the cliff has just ended and the vesting curve has begun at 0
self.assertEqual(vesting_curve(180, 90, 90), 0.5) # At Day 180, the cliff has ended and we are in the vesting curve, whose half-life is 90 as well, so at 180 we should get 0.5. self.assertEqual(vesting_curve(180, 90, 90), 0.5) # At Day 180, the cliff has ended and we are in the vesting curve, whose half-life is 90 as well, so at 180 we should get 0.5.
@ -16,9 +16,11 @@ class TokenBatchTest(unittest.TestCase):
tbh = TokenBatch(10000, 3, 3, True) tbh = TokenBatch(10000, 3, 3, True)
tb = TokenBatch(10000, 5, 10, False) tb = TokenBatch(10000, 5, 10, False)
self. assertEqual(tbh.unlocked_fraction(), 0)
tbh.current_date = datetime.datetime.today() + datetime.timedelta(days=3)
self.assertEqual(tbh.unlocked_fraction(), 0) self.assertEqual(tbh.unlocked_fraction(), 0)
self.assertEqual(tbh.unlocked_fraction(datetime.datetime.today() + datetime.timedelta(days=3)), 0) tbh.current_date = datetime.datetime.today() + datetime.timedelta(days=6)
self.assertEqual(tbh.unlocked_fraction(datetime.datetime.today() + datetime.timedelta(days=6)), 0.5) self.assertEqual(tbh.unlocked_fraction(), 0.5)
self.assertEqual(tb.unlocked_fraction(), 1.0) self.assertEqual(tb.unlocked_fraction(), 1.0)
@ -39,22 +41,30 @@ class TokenBatchTest(unittest.TestCase):
with self.assertRaises(Exception): with self.assertRaises(Exception):
tb.spend(10000) tb.spend(10000)
class TestSystem(unittest.TestCase): class CommonsTest(unittest.TestCase):
def test_system(self): def setUp(self):
# 100,000 DAI invested for 1,000,000 tokens. # 100,000 DAI invested for 1,000,000 tokens.
desired_token_price = 0.1 self.desired_token_price = 0.1
hatcher_contributions = [25000, 25000, 50000] self.hatcher_contributions = [25000, 25000, 50000]
token_supply_initial = sum(hatcher_contributions) / desired_token_price self.token_batches, self.token_supply_initial = contributions_to_token_batches(self.hatcher_contributions, self.desired_token_price, 90)
token_batches = contributions_to_token_batches(hatcher_contributions, token_supply_initial, 90)
# Because of hatch_tribute, the collateral_pool is 0.7e6. This causes the token's post-hatch price to be 0.14. # Because of hatch_tribute, the collateral_pool is 0.7e6. This causes the token's post-hatch price to be 0.14.
o = Commons(sum(hatcher_contributions), token_supply_initial, hatch_tribute=0.3) self.commons = Commons(sum(self.hatcher_contributions), self.token_supply_initial, hatch_tribute=0.3)
self.assertEqual(o._collateral_pool, 70000)
self.assertEqual(o._funding_pool, 30000)
self.assertEqual(o._token_supply, token_supply_initial)
self.assertEqual(o.token_price(), 0.14) def test_initialization(self):
print(token_batches) self.assertEqual(self.commons._collateral_pool, 70000)
# print(o.deposit(100)) self.assertEqual(self.commons._funding_pool, 30000)
# print(o.token_price()) self.assertEqual(self.commons._token_supply, self.token_supply_initial)
# print(o._collateral_pool)
self.assertEqual(self.commons.token_price(), 0.14)
def test_burn_without_exit_tribute(self):
old_token_supply = self.commons._token_supply
old_collateral_pool = self.commons._collateral_pool
money_returned, realized_price = self.commons.burn(50000)
self.assertEqual(money_returned, 6825.0)
self.assertEqual(realized_price, 0.1365)
self.assertEqual(self.commons._token_supply, old_token_supply-50000)
self.assertEqual(self.commons._collateral_pool, old_collateral_pool-money_returned)