erc20-demurrage-token

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

test_burn.py (12440B)


      1 # standard imports
      2 import os
      3 import unittest
      4 import json
      5 import logging
      6 import datetime
      7 
      8 # external imports
      9 from chainlib.eth.constant import ZERO_ADDRESS
     10 from chainlib.eth.nonce import RPCNonceOracle
     11 from chainlib.eth.tx import receipt
     12 from chainlib.eth.block import (
     13         block_latest,
     14         block_by_number,
     15     )
     16 
     17 # local imports
     18 from erc20_demurrage_token import DemurrageToken
     19 
     20 # test imports
     21 from erc20_demurrage_token.unittest.base import TestDemurrageDefault
     22 
     23 logging.basicConfig(level=logging.INFO)
     24 logg = logging.getLogger()
     25 
     26 testdir = os.path.dirname(__file__)
     27 
     28 #TAX_LEVEL = 2
     29 
     30 class TestBurn(TestDemurrageDefault):
     31 
     32     def setUp(self):
     33         super(TestBurn, self).setUp()
     34 
     35 #
     36 #    def publish(self, tax_level=None):
     37 #        nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
     38 #        c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
     39 #
     40 #        if tax_level != None:
     41 #            self.publisher.settings.demurrage_level = tax_level * (10 ** 32)
     42 #        self.publisher.settings.sink_address = self.accounts[9]
     43 #        self.publisher.sink_address = self.accounts[9]
     44 #        super(TestBurn, self).publish(c)
     45 
     46 
     47     # Burn tokens and immediately check balances and supply
     48     def test_burn_basic(self):
     49         nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
     50         c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
     51 
     52         (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1000000)
     53         r = self.rpc.do(o)
     54 
     55         nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
     56         c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
     57         (tx_hash, o) = c.burn(self.address, self.accounts[1], 600000)
     58         r = self.rpc.do(o)
     59         o = receipt(tx_hash)
     60         r = self.rpc.do(o)
     61         self.assertEqual(r['status'], 0)
     62 
     63         nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
     64         c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
     65         (tx_hash, o) = c.add_minter(self.address, self.accounts[0], self.accounts[1])
     66         r = self.rpc.do(o)
     67 
     68         nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
     69         c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
     70         (tx_hash, o) = c.burn(self.address, self.accounts[1], 600000)
     71         r = self.rpc.do(o)
     72         o = receipt(tx_hash)
     73         r = self.rpc.do(o)
     74         self.assertEqual(r['status'], 1)
     75 
     76         o = c.total_supply(self.address, sender_address=self.accounts[0])
     77         r = self.rpc.do(o)
     78         new_supply = c.parse_total_supply(r)
     79         self.assertEqual(new_supply, 400000)
     80 
     81         o = c.total_burned(self.address, sender_address=self.accounts[0])
     82         r = self.rpc.do(o)
     83         burned = c.parse_total_burned(r)
     84         self.assertEqual(burned, 600000)
     85 
     86 
     87     # burn tokens and check sink balance and supply after first redistribution period
     88     def test_burned_redistribution(self):
     89         nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
     90         c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
     91 
     92         (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[0], 1000000000)
     93         r = self.rpc.do(o)
     94 
     95         (tx_hash, o) = c.burn(self.address, self.accounts[0], 500000000)
     96         self.rpc.do(o)
     97         
     98         (tx_hash, o) = c.transfer(self.address, self.accounts[0], self.sink_address, 500000000)
     99         r = self.rpc.do(o)
    100 
    101         self.backend.time_travel(self.start_time + self.period_seconds)
    102 
    103         o = c.balance(self.address, self.sink_address, sender_address=self.accounts[0])
    104         r = self.rpc.do(o)
    105         bal = c.parse_balance(r)
    106         self.assert_within(bal, 490000000, 1) # 2% == 10000000
    107 
    108         (tx_hash, o) = c.change_period(self.address, self.accounts[0])
    109         r = self.rpc.do(o)
    110         o = receipt(tx_hash)
    111         r = self.rpc.do(o)
    112         self.assertEqual(r['status'], 1)
    113 
    114         o = c.total_supply(self.address, sender_address=self.accounts[0])
    115         r = self.rpc.do(o)
    116         new_supply = c.parse_total_supply(r)
    117         self.assertEqual(new_supply, 500000000)
    118 
    119         o = c.balance(self.address, self.sink_address, sender_address=self.accounts[0])
    120         r = self.rpc.do(o)
    121         bal = c.parse_balance(r)
    122         self.assert_within(bal, 500000000, 1)
    123 
    124         self.backend.time_travel(self.start_time + (self.period_seconds * 2))
    125 
    126         (tx_hash, o) = c.change_period(self.address, self.accounts[0])
    127         r = self.rpc.do(o)
    128         o = receipt(tx_hash)
    129         r = self.rpc.do(o)
    130         self.assertEqual(r['status'], 1)
    131 
    132         o = c.total_supply(self.address, sender_address=self.accounts[0])
    133         r = self.rpc.do(o)
    134         new_supply = c.parse_total_supply(r)
    135         self.assertEqual(new_supply, 500000000)
    136 
    137         # if we don't burn anything more it should be the same 
    138         o = c.balance(self.address, self.sink_address, sender_address=self.accounts[0])
    139         r = self.rpc.do(o)
    140         bal = c.parse_balance(r)
    141         self.assert_within_lower(bal, 500000000, 1)
    142 
    143 
    144     # burn tokens and check sink and taxed balance and supply after first redistribution period
    145     def test_burned_other_redistribution(self):
    146         nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
    147         c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
    148 
    149         (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[0], 1000000000)
    150         r = self.rpc.do(o)
    151 
    152         (tx_hash, o) = c.burn(self.address, self.accounts[0], 500000000)
    153         r = self.rpc.do(o)
    154 
    155         (tx_hash, o) = c.transfer(self.address, self.accounts[0], self.accounts[1], 500000000)
    156         r = self.rpc.do(o)
    157 
    158         self.backend.time_travel(self.start_time + self.period_seconds)
    159 
    160         o = c.balance(self.address, self.accounts[1], sender_address=self.accounts[0])
    161         r = self.rpc.do(o)
    162         bal = c.parse_balance(r)
    163         #self.assertEqual(bal, 416873881) # 9 periods demurrage
    164         self.assert_within(bal, 490000000, 1)
    165 
    166         (tx_hash, o) = c.change_period(self.address, self.accounts[0])
    167         r = self.rpc.do(o)
    168         o = receipt(tx_hash)
    169         r = self.rpc.do(o)
    170         self.assertEqual(r['status'], 1)
    171 
    172         o = c.total_supply(self.address, sender_address=self.accounts[0])
    173         r = self.rpc.do(o)
    174         new_supply = c.parse_total_supply(r)
    175         self.assertEqual(new_supply, 500000000)
    176 
    177         o = c.balance(self.address, self.accounts[1], sender_address=self.accounts[0])
    178         r = self.rpc.do(o)
    179         bal = c.parse_balance(r)
    180         self.assert_within(bal, 490000000, 1)
    181 
    182         o = c.balance(self.address, self.sink_address, sender_address=self.accounts[0])
    183         r = self.rpc.do(o)
    184         sink_bal = c.parse_balance(r)
    185         self.assert_within_lower(sink_bal, 10000000, 1) # TODO is this ok variance, 1.0 is ppm?
    186 
    187         self.backend.time_travel(self.start_time + (self.period_seconds * 2))
    188 
    189         (tx_hash, o) = c.change_period(self.address, self.accounts[0])
    190         r = self.rpc.do(o)
    191         o = receipt(tx_hash)
    192         r = self.rpc.do(o)
    193         self.assertEqual(r['status'], 1)
    194 
    195         o = c.total_supply(self.address, sender_address=self.accounts[0])
    196         r = self.rpc.do(o)
    197         new_supply = c.parse_total_supply(r)
    198         self.assertEqual(new_supply, 500000000)
    199 
    200         o = c.balance(self.address, self.accounts[1], sender_address=self.accounts[0])
    201         r = self.rpc.do(o)
    202         next_bal = c.parse_balance(r)
    203         self.assert_within(next_bal, 480200000, 0.01)
    204 
    205         o = c.balance(self.address, self.sink_address, sender_address=self.accounts[0])
    206         r = self.rpc.do(o)
    207         prev_sink_bal = sink_bal
    208         bal = prev_sink_bal + (bal - next_bal)
    209         sink_bal = c.parse_balance(r)
    210         self.assert_within_lower(sink_bal, bal, 0.09) # TODO is this ok variance, 1.0 is ppm?
    211 
    212 
    213     # verify expected results of balance and supply after multiple redistribution periods
    214     def test_burn_accumulate(self):
    215         nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
    216         c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
    217 
    218         (tx_hash, o) = c.add_minter(self.address, self.accounts[0], self.sink_address)
    219         self.rpc.do(o)
    220         o = receipt(tx_hash)
    221         r = self.rpc.do(o)
    222         self.assertEqual(r['status'], 1)
    223 
    224         (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.sink_address, self.default_supply)
    225         r = self.rpc.do(o)
    226 
    227         balance_share = int(self.default_supply / 2)
    228         nonce_oracle = RPCNonceOracle(self.sink_address, self.rpc)
    229         c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
    230         (tx_hash, o) = c.transfer(self.address, self.sink_address, self.accounts[1], balance_share)
    231         r = self.rpc.do(o)
    232 
    233         new_supply = None
    234         burn_rate = 1000
    235         sink_bal = None
    236         bob_bal = None
    237         bob_refund = None
    238 
    239         o = c.balance(self.address, self.accounts[1], sender_address=self.accounts[0])
    240         r = self.rpc.do(o)
    241         bob_bal = c.parse_balance(r)
    242         prev_bob_bal = bob_bal
    243 
    244         o = c.balance(self.address, self.sink_address, sender_address=self.accounts[0])
    245         r = self.rpc.do(o)
    246         logg.info('sink has balance {}'.format(c.parse_balance(r)))
    247 
    248         iterations = 100
    249 
    250         for i in range(1, iterations + 1):
    251             nonce_oracle = RPCNonceOracle(self.sink_address, self.rpc)
    252             c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
    253 
    254             if bob_refund != None:
    255                 (tx_hash, o) = c.transfer(self.address, self.sink_address, self.accounts[1], bob_refund)
    256                 r = self.rpc.do(o)
    257                 o = receipt(tx_hash)
    258                 r = self.rpc.do(o)
    259                 self.assertEqual(r['status'], 1)
    260 
    261             (tx_hash, o) = c.burn(self.address, self.sink_address, burn_rate)
    262             r = self.rpc.do(o)
    263             o = receipt(tx_hash)
    264             r = self.rpc.do(o)
    265             self.assertEqual(r['status'], 1)
    266 
    267             nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
    268             c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
    269             o = c.total_supply(self.address, sender_address=self.accounts[0])
    270             r = self.rpc.do(o)
    271             new_supply = c.parse_total_supply(r)
    272 
    273             self.backend.time_travel(self.start_time + (self.period_seconds * i))
    274 
    275             (tx_hash, o) = c.change_period(self.address, self.accounts[0])
    276             self.rpc.do(o)
    277             
    278             o = c.balance(self.address, self.accounts[1], sender_address=self.accounts[0])
    279             r = self.rpc.do(o)
    280             bob_bal = c.parse_balance(r)
    281             bob_refund = prev_bob_bal - bob_bal
    282 
    283             o = c.balance(self.address, self.sink_address, sender_address=self.accounts[0])
    284             r = self.rpc.do(o)
    285             burner_bal = c.parse_balance(r)
    286 
    287             sum_supply = bob_bal + burner_bal
    288           
    289             o = c.total_burned(self.address, sender_address=self.accounts[0])
    290             r = self.rpc.do(o)
    291             total_burned = c.parse_balance(r)
    292 
    293             o = c.to_base_amount(self.address, total_burned, sender_address=self.accounts[0])
    294             r = self.rpc.do(o)
    295             total_burned_base = c.parse_balance(r)
    296 
    297             expected_supply = self.default_supply - (burn_rate * i)
    298             logg.info('checking burn round {} balance burner {} bob {} supply {} expected {} summed {} burned {} base {}'.format(i, burner_bal, bob_bal, new_supply, expected_supply, sum_supply, total_burned, total_burned_base))
    299             self.assertEqual(new_supply, expected_supply)
    300 
    301         sum_supply = burner_bal + bob_bal
    302         logg.debug('balances sink {} bob {} total {} supply real {} original {}'.format(sink_bal, bob_bal, sum_supply, new_supply, self.default_supply))
    303 
    304         self.assert_within_lower(sum_supply, new_supply, 1)
    305         self.assert_within_lower(burner_bal, balance_share - total_burned + bob_refund, 1)
    306 
    307         bob_delta = self.default_supply * ((2 / 1000000) / 1000)
    308         self.assert_within_greater(bob_bal, balance_share - bob_delta - bob_refund, 1)
    309         
    310         self.assertEqual(total_burned, iterations * burn_rate)
    311 
    312 
    313 if __name__ == '__main__':
    314     unittest.main()