diff --git a/hatch.py b/hatch.py index fd6ec0b..384ed8a 100644 --- a/hatch.py +++ b/hatch.py @@ -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) 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 - initial_token_supply: NOT denominated in millions + hatcher_contributions: a list of hatcher contributions in DAI/ETH/whatever + 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 """ 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 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) 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: 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.spent = 0 + self.current_date = datetime.today() # to be set externally before each spend check + 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 - def unlocked_fraction(self, day: datetime = datetime.today()) -> float: + def unlocked_fraction(self) -> float: """ returns what fraction of the TokenBatch is unlocked to date """ 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) return u if u > 0 else 0 else: @@ -72,7 +75,7 @@ class TokenBatch: returns the argument if successful for your convenience """ 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.spent += x @@ -98,7 +101,7 @@ class Commons: # Options self.exit_tribute = exit_tribute - + def deposit(self, dai): """ Deposit DAI after the hatch phase. This means all the incoming deposit goes to the collateral pool. @@ -118,8 +121,8 @@ class Commons: money_returned = dai if self.exit_tribute: - self._funding_pool += commons.exit_tribute * dai - money_returned = (1-commons.exit_tribute) * dai + self._funding_pool += self.exit_tribute * dai + money_returned = (1-self.exit_tribute) * dai return money_returned, realized_price diff --git a/hatch_test.py b/hatch_test.py index 944d173..a173b28 100644 --- a/hatch_test.py +++ b/hatch_test.py @@ -2,7 +2,7 @@ from hatch import * import unittest import datetime -class TestHatch(unittest.TestCase): +class HatchTest(unittest.TestCase): 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(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) 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(datetime.datetime.today() + datetime.timedelta(days=3)), 0) - self.assertEqual(tbh.unlocked_fraction(datetime.datetime.today() + datetime.timedelta(days=6)), 0.5) + tbh.current_date = datetime.datetime.today() + datetime.timedelta(days=6) + self.assertEqual(tbh.unlocked_fraction(), 0.5) self.assertEqual(tb.unlocked_fraction(), 1.0) @@ -39,22 +41,30 @@ class TokenBatchTest(unittest.TestCase): with self.assertRaises(Exception): tb.spend(10000) -class TestSystem(unittest.TestCase): - def test_system(self): +class CommonsTest(unittest.TestCase): + def setUp(self): # 100,000 DAI invested for 1,000,000 tokens. - desired_token_price = 0.1 - hatcher_contributions = [25000, 25000, 50000] - token_supply_initial = sum(hatcher_contributions) / desired_token_price - token_batches = contributions_to_token_batches(hatcher_contributions, token_supply_initial, 90) + self.desired_token_price = 0.1 + self.hatcher_contributions = [25000, 25000, 50000] + self.token_batches, self.token_supply_initial = contributions_to_token_batches(self.hatcher_contributions, self.desired_token_price, 90) # 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.assertEqual(o._collateral_pool, 70000) - self.assertEqual(o._funding_pool, 30000) - self.assertEqual(o._token_supply, token_supply_initial) + self.commons = Commons(sum(self.hatcher_contributions), self.token_supply_initial, hatch_tribute=0.3) - self.assertEqual(o.token_price(), 0.14) - print(token_batches) - # print(o.deposit(100)) - # print(o.token_price()) - # print(o._collateral_pool) \ No newline at end of file + def test_initialization(self): + self.assertEqual(self.commons._collateral_pool, 70000) + self.assertEqual(self.commons._funding_pool, 30000) + self.assertEqual(self.commons._token_supply, self.token_supply_initial) + + 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) \ No newline at end of file