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
@ -98,7 +101,7 @@ class Commons:
# Options # Options
self.exit_tribute = exit_tribute self.exit_tribute = exit_tribute
def deposit(self, dai): def deposit(self, dai):
""" """
Deposit DAI after the hatch phase. This means all the incoming deposit goes to the collateral pool. 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 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)