erc20-demurrage-token

ERC20 token with redistributed continual demurrage
Info | Log | Files | Refs | README

base.py (5227B)


      1 # standard imports
      2 import logging
      3 import os
      4 import math
      5 
      6 # external imports
      7 from chainlib.eth.unittest.ethtester import EthTesterCase
      8 from chainlib.eth.tx import (
      9         receipt,
     10         )
     11 from chainlib.eth.block import (
     12         block_latest,
     13         block_by_number,
     14         )
     15 from chainlib.eth.nonce import RPCNonceOracle
     16 from chainlib.eth.constant import ZERO_ADDRESS
     17 
     18 # local imports
     19 from erc20_demurrage_token import (
     20         DemurrageTokenSettings,
     21         DemurrageToken,
     22         )
     23 from dexif import *
     24 
     25 logg = logging.getLogger()
     26 
     27 #BLOCKTIME = 5 # seconds
     28 TAX_LEVEL = int(10000 * 2) # 2%
     29 # calc "1-(0.98)^(1/518400)" <- 518400 = 30 days of blocks
     30 # 0.00000003897127107225
     31 PERIOD = 43200
     32 
     33 
     34 class TestTokenDeploy:
     35 
     36     """tax level is ppm, 1000000 = 100%"""
     37     def __init__(self, rpc, token_symbol='FOO', token_name='Foo Token', sink_address=ZERO_ADDRESS, tax_level=TAX_LEVEL, period=PERIOD):
     38         self.tax_level = tax_level
     39         self.period_seconds = period * 60
     40 
     41         self.settings = DemurrageTokenSettings()
     42         self.settings.name = token_name
     43         self.settings.symbol = token_symbol
     44         self.settings.decimals = 6
     45         tax_level_input = to_fixed((1 - (tax_level / 1000000)) ** (1 / period))
     46         self.settings.demurrage_level = tax_level_input
     47         self.settings.period_minutes = period
     48         self.settings.sink_address = sink_address
     49         self.sink_address = self.settings.sink_address
     50         logg.debug('using demurrage token settings: {}'.format(self.settings))
     51 
     52         o = block_latest()
     53         self.start_block = rpc.do(o)
     54         
     55         o = block_by_number(self.start_block, include_tx=False)
     56         r = rpc.do(o)
     57 
     58         try:
     59             self.start_time = int(r['timestamp'], 16)
     60         except TypeError:
     61             self.start_time = int(r['timestamp'])
     62 
     63 
     64     def publish(self, rpc, publisher_address, interface, supply_cap=0):
     65         tx_hash = None
     66         o = None
     67         (tx_hash, o) = interface.constructor(publisher_address, self.settings)
     68 
     69         r = rpc.do(o)
     70         o = receipt(tx_hash)
     71         r = rpc.do(o)
     72         assert r['status'] == 1
     73         self.start_block = r['block_number']
     74         self.address = r['contract_address']
     75 
     76         o = block_by_number(r['block_number'])
     77         r = rpc.do(o)
     78         self.start_time = r['timestamp']
     79 
     80         return self.address
     81 
     82 
     83 class TestDemurrage(EthTesterCase):
     84 
     85     def setUp(self):
     86         super(TestDemurrage, self).setUp()
     87         period = PERIOD
     88         try:
     89             period = getattr(self, 'period')
     90         except AttributeError as e:
     91             pass
     92         self.publisher = TestTokenDeploy(self.rpc, period=period, sink_address=self.accounts[9])
     93         self.default_supply = 0
     94         self.default_supply_cap = 0
     95         self.start_block = None
     96         self.address = None
     97         self.start_time = None
     98 
     99 
    100     def publish(self, interface):
    101         self.address = self.publisher.publish(self.rpc, self.accounts[0], interface, supply_cap=self.default_supply_cap)
    102         self.start_block = self.publisher.start_block
    103         self.start_time = self.publisher.start_time
    104         self.tax_level = self.publisher.tax_level
    105         self.period_seconds = self.publisher.period_seconds
    106         self.sink_address = self.publisher.sink_address
    107 
    108         logg.debug('contract address {} start block {} start time {}'.format(self.address, self.start_block, self.start_time))
    109 
    110 
    111     def assert_within(self, v, target, tolerance_ppm):
    112         lower_target = target - (target * (tolerance_ppm / 1000000))
    113         higher_target = target + (target * (tolerance_ppm / 1000000))
    114         #self.assertGreaterEqual(v, lower_target)
    115         #self.assertLessEqual(v, higher_target)
    116         if v >= lower_target and v <= higher_target:
    117             logg.debug('asserted within {} <= {} <= {}'.format(lower_target, v, higher_target))
    118             return
    119         raise AssertionError('{} not within lower {} and higher {}'.format(v, lower_target, higher_target))
    120 
    121 
    122     def assert_within_greater(self, v, target, tolerance_ppm):
    123         greater_target = target + (target * (tolerance_ppm / 1000000))
    124         self.assertLessEqual(v, greater_target)
    125         self.assertGreaterEqual(v, target)
    126         logg.debug('asserted within greater {} >= {} >= {}'.format(greater_target, v, target))
    127 
    128 
    129     def assert_within_lower(self, v, target, tolerance_ppm):
    130         lower_target = target - (target * (tolerance_ppm / 1000000))
    131         self.assertGreaterEqual(v, lower_target)
    132         self.assertLessEqual(v, target)
    133         logg.debug('asserted within lower {} <= {} <= {}'.format(lower_target, v, target))
    134 
    135 
    136     def assert_equal_decimals(self, v, target, precision):
    137         target = int(target * (10 ** precision))
    138         target = target / (10 ** precision)
    139         self.assertEqual(v, target)
    140 
    141 
    142     def tearDown(self):
    143         pass
    144 
    145 
    146 class TestDemurrageDefault(TestDemurrage):
    147 
    148     def setUp(self):
    149         super(TestDemurrageDefault, self).setUp()
    150    
    151         nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
    152         c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
    153 
    154         self.publish(c)
    155 
    156         self.default_supply = 10**12
    157         self.default_supply_cap = self.default_supply