Apply demurrage on minutes on periods (SingleCap only)

This commit is contained in:
nolash 2021-06-07 12:08:04 +02:00
parent 32ae98d581
commit dd878aa5cd
Signed by: lash
GPG Key ID: 21D2E7BB88C2A746
11 changed files with 320 additions and 252 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -19,7 +19,6 @@ settings.demurrage_level = int(decay_per_minute*(10**38))
settings.period_minutes = 1 # 1 week in minutes settings.period_minutes = 1 # 1 week in minutes
chain = 'evm:foochain:42' chain = 'evm:foochain:42'
cap = (10 ** 6) * (10 ** 12) cap = (10 ** 6) * (10 ** 12)
#cap = 0
# instantiate simulation # instantiate simulation
sim = DemurrageTokenSimulation(chain, settings, redistribute=False, cap=cap, actors=10) sim = DemurrageTokenSimulation(chain, settings, redistribute=False, cap=cap, actors=10)

View File

@ -23,7 +23,7 @@ done
modes=(MultiCap MultiNocap) modes=(MultiCap MultiNocap)
for m in ${modes[@]}; do for m in ${modes[@]}; do
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_pure.py ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_remainder.py
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution.py ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution.py
done done

View File

@ -28,8 +28,7 @@ TAX_LEVEL = int(10000 * 2) # 2%
# calc "1-(0.98)^(1/518400)" <- 518400 = 30 days of blocks # calc "1-(0.98)^(1/518400)" <- 518400 = 30 days of blocks
# 0.00000003897127107225 # 0.00000003897127107225
#PERIOD = int(60/BLOCKTIME) * 60 * 24 * 30 # month #PERIOD = int(60/BLOCKTIME) * 60 * 24 * 30 # month
PERIOD = 1 PERIOD = 10
class TestDemurrage(EthTesterCase): class TestDemurrage(EthTesterCase):

View File

@ -9,6 +9,10 @@ import datetime
from chainlib.eth.constant import ZERO_ADDRESS from chainlib.eth.constant import ZERO_ADDRESS
from chainlib.eth.nonce import RPCNonceOracle from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.tx import receipt from chainlib.eth.tx import receipt
from chainlib.eth.block import (
block_latest,
block_by_number,
)
# local imports # local imports
from erc20_demurrage_token import DemurrageToken from erc20_demurrage_token import DemurrageToken
@ -46,21 +50,55 @@ class TestBasic(TestDemurrageDefault):
demurrage_amount = c.parse_demurrage_amount(r) demurrage_amount = c.parse_demurrage_amount(r)
self.assertEqual(modifier, demurrage_amount) self.assertEqual(modifier, demurrage_amount)
self.backend.time_travel(self.start_time + self.period_seconds - 1) o = block_latest()
r = self.rpc.do(o)
o = block_by_number(r)
b = self.rpc.do(o)
logg.debug('block {} start {}'.format(b['timestamp'], self.start_time))
self.backend.time_travel(self.start_time + 2)
(tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
o = c.demurrage_amount(self.address, sender_address=self.accounts[0]) o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o) r = self.rpc.do(o)
demurrage_amount = c.parse_demurrage_amount(r) demurrage_amount = c.parse_demurrage_amount(r)
self.assertEqual(modifier, demurrage_amount) self.assertEqual(modifier, demurrage_amount)
self.backend.time_travel(self.start_time + self.period_seconds + 1) self.backend.time_travel(self.start_time + 61)
(tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0]) (tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o) r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
o = c.demurrage_amount(self.address, sender_address=self.accounts[0]) o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o) r = self.rpc.do(o)
demurrage_amount = c.parse_demurrage_amount(r) demurrage_amount = c.parse_demurrage_amount(r)
modifier = int(98 * (10 ** 36)) modifier_base = 1000000 - self.tax_level
logg.debug('modifier base {}'.format(modifier_base))
modifier = int(modifier_base * (10 ** 32)) # 38 decimal places minus 6 (1000000)
self.assertEqual(modifier, demurrage_amount) self.assertEqual(modifier, demurrage_amount)
self.backend.time_travel(self.start_time + 601)
(tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o)
demurrage_amount = c.parse_demurrage_amount(r)
modifier_base = ((1000000 - self.tax_level) / 1000000) ** 10
modifier = int(modifier_base * (10 ** 12))
rounding_tolerance_nano = 4000000 # 0.000004% precision
demurrage_amount_truncate = int(demurrage_amount / (10 ** 26)) # equals 12 decimal places
self.assertGreaterEqual(modifier, demurrage_amount_truncate - rounding_tolerance_nano)
self.assertLessEqual(modifier, demurrage_amount_truncate)
def test_mint(self): def test_mint(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)

View File

@ -31,7 +31,6 @@ testdir = os.path.dirname(__file__)
class TestRedistribution(TestDemurrageDefault): class TestRedistribution(TestDemurrageDefault):
@unittest.expectedFailure
def test_whole_is_parts(self): def test_whole_is_parts(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
@ -84,239 +83,244 @@ class TestRedistribution(TestDemurrageDefault):
balance += balance_item balance += balance_item
logg.debug('balance {} {} total {}'.format(i, balance_item, balance)) logg.debug('balance {} {} total {}'.format(i, balance_item, balance))
o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0])
r = self.rpc.do(o)
balance_item = c.parse_balance_of(r)
balance += balance_item
self.assertEqual(balance, 200000000) self.assertEqual(balance, 200000000)
def test_debug_periods(self): # def test_debug_periods(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
#
o = c.actual_period(self.address, sender_address=self.accounts[0]) # o = c.actual_period(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
pactual = c.parse_actual_period(r) # pactual = c.parse_actual_period(r)
#
o = c.period_start(self.address, sender_address=self.accounts[0]) # o = c.period_start(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
pstart = c.parse_actual_period(r) # pstart = c.parse_actual_period(r)
#
o = c.period_duration(self.address, sender_address=self.accounts[0]) # o = c.period_duration(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
pduration = c.parse_actual_period(r) # pduration = c.parse_actual_period(r)
#
o = block_latest() # o = block_latest()
blocknumber = self.rpc.do(o) # blocknumber = self.rpc.do(o)
#
logg.debug('actual {} start {} duration {} blocknumber {}'.format(pactual, pstart, pduration, blocknumber)) # logg.debug('actual {} start {} duration {} blocknumber {}'.format(pactual, pstart, pduration, blocknumber))
#
#
# TODO: check receipt log outputs # # TODO: check receipt log outputs
def test_redistribution_storage(self): # def test_redistribution_storage(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
o = c.redistributions(self.address, 0, sender_address=self.accounts[0]) # o = c.redistributions(self.address, 0, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
self.assertEqual(strip_0x(r), '000000000000000000000000f424000000000000000000000000000000000001') # self.assertEqual(strip_0x(r), '000000000000000000000000f424000000000000000000000000000000000001')
#
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1000000) # (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1000000)
r = self.rpc.do(o) # r = self.rpc.do(o)
#
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[2], 1000000) # (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[2], 1000000)
r = self.rpc.do(o) # r = self.rpc.do(o)
#
external_address = to_checksum_address('0x' + os.urandom(20).hex()) # external_address = to_checksum_address('0x' + os.urandom(20).hex())
#
nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.transfer(self.address, self.accounts[2], external_address, 1000000) # (tx_hash, o) = c.transfer(self.address, self.accounts[2], external_address, 1000000)
r = self.rpc.do(o) # r = self.rpc.do(o)
#
nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.transfer(self.address, self.accounts[1], external_address, 999999) # (tx_hash, o) = c.transfer(self.address, self.accounts[1], external_address, 999999)
r = self.rpc.do(o) # r = self.rpc.do(o)
#
self.backend.time_travel(self.start_time + self.period_seconds + 1) # self.backend.time_travel(self.start_time + self.period_seconds + 1)
#
o = c.redistributions(self.address, 0, sender_address=self.accounts[0]) # o = c.redistributions(self.address, 0, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
self.assertEqual(strip_0x(r), '000000000000000000000000f42400000000010000000000001e848000000001') # self.assertEqual(strip_0x(r), '000000000000000000000000f42400000000010000000000001e848000000001')
#
o = c.redistributions(self.address, 0, sender_address=self.accounts[0]) # o = c.redistributions(self.address, 0, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
self.assertEqual(strip_0x(r), '000000000000000000000000f42400000000010000000000001e848000000001') # self.assertEqual(strip_0x(r), '000000000000000000000000f42400000000010000000000001e848000000001')
#
#
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[0], 1000000) # (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[0], 1000000)
r = self.rpc.do(o) # r = self.rpc.do(o)
#
o = c.redistributions(self.address, 1, sender_address=self.accounts[0]) # o = c.redistributions(self.address, 1, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
self.assertEqual(strip_0x(r), '000000000000000000000000ef4200000000000000000000002dc6c000000002') # self.assertEqual(strip_0x(r), '000000000000000000000000ef4200000000000000000000002dc6c000000002')
#
#
def test_redistribution_balance_on_zero_participants(self): # def test_redistribution_balance_on_zero_participants(self):
supply = self.default_supply # supply = self.default_supply
#
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], supply) # (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], supply)
r = self.rpc.do(o) # r = self.rpc.do(o)
#
self.backend.time_travel(self.start_time + self.period_seconds + 1) # self.backend.time_travel(self.start_time + self.period_seconds + 1)
(tx_hash, o) = c.apply_demurrage(self.address, self.accounts[0]) # (tx_hash, o) = c.apply_demurrage(self.address, self.accounts[0])
self.rpc.do(o) # self.rpc.do(o)
o = receipt(tx_hash) # o = receipt(tx_hash)
rcpt = self.rpc.do(o) # rcpt = self.rpc.do(o)
self.assertEqual(rcpt['status'], 1) # self.assertEqual(rcpt['status'], 1)
#
(tx_hash, o) = c.change_period(self.address, self.accounts[0]) # (tx_hash, o) = c.change_period(self.address, self.accounts[0])
self.rpc.do(o) # self.rpc.do(o)
o = receipt(tx_hash) # o = receipt(tx_hash)
r = self.rpc.do(o) # r = self.rpc.do(o)
self.assertEqual(r['status'], 1) # self.assertEqual(r['status'], 1)
#
o = c.total_supply(self.address, sender_address=self.accounts[0]) # o = c.total_supply(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
total_supply = c.parse_total_supply(r) # total_supply = c.parse_total_supply(r)
sink_increment = int(total_supply * (self.tax_level / 1000000)) # sink_increment = int(total_supply * (self.tax_level / 1000000))
self.assertEqual(supply, total_supply) # self.assertEqual(supply, total_supply)
#
for l in rcpt['logs']: # for l in rcpt['logs']:
if l['topics'][0] == '0xa0717e54e02bd9829db5e6e998aec0ae9de796b8d150a3cc46a92ab869697755': # event Decayed(uint256,uint256,uint256,uint256) # if l['topics'][0] == '0xa0717e54e02bd9829db5e6e998aec0ae9de796b8d150a3cc46a92ab869697755': # event Decayed(uint256,uint256,uint256,uint256)
period = int.from_bytes(bytes.fromhex(strip_0x(l['topics'][1])), 'big') # period = int.from_bytes(bytes.fromhex(strip_0x(l['topics'][1])), 'big')
self.assertEqual(period, 2) # self.assertEqual(period, 2)
b = bytes.fromhex(strip_0x(l['data'])) # b = bytes.fromhex(strip_0x(l['data']))
remainder = int.from_bytes(b, 'big') # remainder = int.from_bytes(b, 'big')
self.assertEqual(remainder, int((1000000 - self.tax_level) * (10 ** 32))) # self.assertEqual(remainder, int((1000000 - self.tax_level) * (10 ** 32)))
#
o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0]) # o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
sink_balance = c.parse_balance_of(r) # sink_balance = c.parse_balance_of(r)
#
self.assertEqual(sink_balance, int(sink_increment * 0.98)) # self.assertEqual(sink_balance, int(sink_increment * 0.98))
self.assertEqual(sink_balance, int(sink_increment * (1000000 - self.tax_level) / 1000000)) # self.assertEqual(sink_balance, int(sink_increment * (1000000 - self.tax_level) / 1000000))
#
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) # o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
balance = c.parse_balance_of(r) # balance = c.parse_balance_of(r)
self.assertEqual(balance, supply - sink_increment) # self.assertEqual(balance, supply - sink_increment)
#
#
def test_redistribution_two_of_ten(self): # def test_redistribution_two_of_ten(self):
mint_amount = 100000000 # mint_amount = 100000000
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
z = 0 # z = 0
for i in range(10): # for i in range(10):
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[i], mint_amount) # (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[i], mint_amount)
self.rpc.do(o) # self.rpc.do(o)
z += mint_amount # z += mint_amount
#
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) # o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
initial_balance = c.parse_balance_of(r) # initial_balance = c.parse_balance_of(r)
#
spend_amount = 1000000 # spend_amount = 1000000
external_address = to_checksum_address('0x' + os.urandom(20).hex()) # external_address = to_checksum_address('0x' + os.urandom(20).hex())
#
nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.transfer(self.address, self.accounts[1], external_address, spend_amount) # (tx_hash, o) = c.transfer(self.address, self.accounts[1], external_address, spend_amount)
self.rpc.do(o) # self.rpc.do(o)
o = receipt(tx_hash) # o = receipt(tx_hash)
r = self.rpc.do(o) # r = self.rpc.do(o)
self.assertEqual(r['status'], 1) # self.assertEqual(r['status'], 1)
#
nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.transfer(self.address, self.accounts[2], external_address, spend_amount) # (tx_hash, o) = c.transfer(self.address, self.accounts[2], external_address, spend_amount)
self.rpc.do(o) # self.rpc.do(o)
o = receipt(tx_hash) # o = receipt(tx_hash)
r = self.rpc.do(o) # r = self.rpc.do(o)
self.assertEqual(r['status'], 1) # self.assertEqual(r['status'], 1)
#
# No cheating! # # No cheating!
nonce_oracle = RPCNonceOracle(self.accounts[3], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[3], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.transfer(self.address, self.accounts[3], self.accounts[3], spend_amount) # (tx_hash, o) = c.transfer(self.address, self.accounts[3], self.accounts[3], spend_amount)
self.rpc.do(o) # self.rpc.do(o)
o = receipt(tx_hash) # o = receipt(tx_hash)
r = self.rpc.do(o) # r = self.rpc.do(o)
self.assertEqual(r['status'], 1) # self.assertEqual(r['status'], 1)
#
# No cheapskating! # # No cheapskating!
nonce_oracle = RPCNonceOracle(self.accounts[4], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[4], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.transfer(self.address, self.accounts[4], external_address, spend_amount-1) # (tx_hash, o) = c.transfer(self.address, self.accounts[4], external_address, spend_amount-1)
self.rpc.do(o) # self.rpc.do(o)
o = receipt(tx_hash) # o = receipt(tx_hash)
r = self.rpc.do(o) # r = self.rpc.do(o)
self.assertEqual(r['status'], 1) # self.assertEqual(r['status'], 1)
#
#
self.backend.time_travel(self.start_time + self.period_seconds + 1) # self.backend.time_travel(self.start_time + self.period_seconds + 1)
#
(tx_hash, o) = c.apply_demurrage(self.address, self.accounts[4]) # (tx_hash, o) = c.apply_demurrage(self.address, self.accounts[4])
self.rpc.do(o) # self.rpc.do(o)
#
(tx_hash, o) = c.change_period(self.address, self.accounts[4]) # (tx_hash, o) = c.change_period(self.address, self.accounts[4])
self.rpc.do(o) # self.rpc.do(o)
#
o = c.balance_of(self.address, self.accounts[3], sender_address=self.accounts[0]) # o = c.balance_of(self.address, self.accounts[3], sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
bummer_balance = c.parse_balance_of(r) # bummer_balance = c.parse_balance_of(r)
#
self.assertEqual(bummer_balance, mint_amount - (mint_amount * (self.tax_level / 1000000))) # self.assertEqual(bummer_balance, mint_amount - (mint_amount * (self.tax_level / 1000000)))
logg.debug('bal {} '.format(bummer_balance)) # logg.debug('bal {} '.format(bummer_balance))
#
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) # o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
bummer_balance = c.parse_balance_of(r) # bummer_balance = c.parse_balance_of(r)
spender_balance = mint_amount - spend_amount # spender_balance = mint_amount - spend_amount
spender_decayed_balance = int(spender_balance - (spender_balance * (self.tax_level / 1000000))) # spender_decayed_balance = int(spender_balance - (spender_balance * (self.tax_level / 1000000)))
self.assertEqual(bummer_balance, spender_decayed_balance) # self.assertEqual(bummer_balance, spender_decayed_balance)
logg.debug('bal {} '.format(bummer_balance)) # logg.debug('bal {} '.format(bummer_balance))
#
(tx_hash, o) = c.apply_redistribution_on_account(self.address, self.accounts[4], self.accounts[1]) # (tx_hash, o) = c.apply_redistribution_on_account(self.address, self.accounts[4], self.accounts[1])
self.rpc.do(o) # self.rpc.do(o)
o = receipt(tx_hash) # o = receipt(tx_hash)
r = self.rpc.do(o) # r = self.rpc.do(o)
self.assertEqual(r['status'], 1) # self.assertEqual(r['status'], 1)
#
(tx_hash, o) = c.apply_redistribution_on_account(self.address, self.accounts[4], self.accounts[2]) # (tx_hash, o) = c.apply_redistribution_on_account(self.address, self.accounts[4], self.accounts[2])
self.rpc.do(o) # self.rpc.do(o)
o = receipt(tx_hash) # o = receipt(tx_hash)
r = self.rpc.do(o) # r = self.rpc.do(o)
self.assertEqual(r['status'], 1) # self.assertEqual(r['status'], 1)
#
o = c.redistributions(self.address, 0, sender_address=self.accounts[0]) # o = c.redistributions(self.address, 0, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
redistribution_data = c.parse_redistributions(r) # redistribution_data = c.parse_redistributions(r)
logg.debug('redist data {}'.format(redistribution_data)) # logg.debug('redist data {}'.format(redistribution_data))
#
o = c.account_period(self.address, self.accounts[1], sender_address=self.accounts[0]) # o = c.account_period(self.address, self.accounts[1], sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
account_period_data = c.parse_account_period(r) # account_period_data = c.parse_account_period(r)
logg.debug('account period {}'.format(account_period_data)) # logg.debug('account period {}'.format(account_period_data))
#
o = c.actual_period(self.address, sender_address=self.accounts[0]) # o = c.actual_period(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
actual_period = c.parse_actual_period(r) # actual_period = c.parse_actual_period(r)
logg.debug('period {}'.format(actual_period)) # logg.debug('period {}'.format(actual_period))
#
redistribution = int((z / 2) * (self.tax_level / 1000000)) # redistribution = int((z / 2) * (self.tax_level / 1000000))
spender_new_base_balance = ((mint_amount - spend_amount) + redistribution) # spender_new_base_balance = ((mint_amount - spend_amount) + redistribution)
spender_new_decayed_balance = int(spender_new_base_balance - (spender_new_base_balance * (self.tax_level / 1000000))) # spender_new_decayed_balance = int(spender_new_base_balance - (spender_new_base_balance * (self.tax_level / 1000000)))
#
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) # o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
spender_actual_balance = c.parse_balance_of(r) # spender_actual_balance = c.parse_balance_of(r)
logg.debug('rrr {} {}'.format(redistribution, spender_new_decayed_balance)) # logg.debug('rrr {} {}'.format(redistribution, spender_new_decayed_balance))
#
self.assertEqual(spender_actual_balance, spender_new_decayed_balance) # self.assertEqual(spender_actual_balance, spender_new_decayed_balance)
#
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -100,6 +100,9 @@ class TestRedistribution(TestDemurrageUnit):
(tx_hash, o) = c.change_period(self.address, self.accounts[1]) (tx_hash, o) = c.change_period(self.address, self.accounts[1])
self.rpc.do(o) self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
demurrage_amount = int((self.tax_level / 1000000) * mint_amount) demurrage_amount = int((self.tax_level / 1000000) * mint_amount)
@ -126,6 +129,17 @@ class TestRedistribution(TestDemurrageUnit):
r = self.rpc.do(o) r = self.rpc.do(o)
supply = c.parse_total_supply(r) supply = c.parse_total_supply(r)
o = c.redistributions(self.address, 0, sender_address=self.accounts[0])
redistribution = self.rpc.do(o)
o = c.to_redistribution_supply(self.address, redistribution, sender_address=self.accounts[0])
r = self.rpc.do(o)
supply = c.parse_to_redistribution_item(r)
o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o)
demurrage = c.parse_demurrage_amount(r)
logg.debug('\ndemurrage {}\nsupply {}'.format(demurrage, supply))
expected_balance = int(supply * (self.tax_level / 1000000)) expected_balance = int(supply * (self.tax_level / 1000000))
expected_balance_tolerance = 1 expected_balance_tolerance = 1

View File

@ -28,6 +28,7 @@ testdir = os.path.dirname(__file__)
class TestRedistributionSingle(TestDemurrageSingle): class TestRedistributionSingle(TestDemurrageSingle):
def test_single_even_if_multiple(self): def test_single_even_if_multiple(self):
mint_amount = 100000000 mint_amount = 100000000

View File

@ -21,7 +21,9 @@ contract DemurrageTokenSingleCap {
uint128 public demurrageAmount; uint128 public demurrageAmount;
// Cached demurrage period; the period for which demurrageAmount was calculated // Cached demurrage period; the period for which demurrageAmount was calculated
uint128 public demurragePeriod; //uint128 public demurragePeriod;
// Cached demurrage timestamp; the timestamp for which demurrageAmount was last calculated
uint256 public demurrageTimestamp;
// Implements EIP172 // Implements EIP172
address public owner; address public owner;
@ -103,10 +105,11 @@ contract DemurrageTokenSingleCap {
decimals = _decimals; decimals = _decimals;
// Demurrage setup // Demurrage setup
periodStart = block.timestamp; demurrageTimestamp = block.timestamp;
periodStart = demurrageTimestamp;
periodDuration = _periodMinutes * 60; periodDuration = _periodMinutes * 60;
demurrageAmount = uint128(ppmDivider * 1000000); // Represents 38 decimal places demurrageAmount = uint128(ppmDivider * 1000000); // Represents 38 decimal places
demurragePeriod = 1; //demurragePeriod = 1;
taxLevel = _taxLevelMinute; // Represents 38 decimal places taxLevel = _taxLevelMinute; // Represents 38 decimal places
bytes32 initialRedistribution = toRedistribution(0, 1000000, 0, 1); bytes32 initialRedistribution = toRedistribution(0, 1000000, 0, 1);
redistributions.push(initialRedistribution); redistributions.push(initialRedistribution);
@ -139,7 +142,8 @@ contract DemurrageTokenSingleCap {
baseBalance = baseBalanceOf(_account); baseBalance = baseBalanceOf(_account);
periodCount = actualPeriod() - demurragePeriod; //periodCount = actualPeriod() - demurragePeriod;
periodCount = getMinutesDelta(demurrageTimestamp);
currentDemurragedAmount = uint128(decayBy(demurrageAmount, periodCount)); currentDemurragedAmount = uint128(decayBy(demurrageAmount, periodCount));
@ -237,10 +241,12 @@ contract DemurrageTokenSingleCap {
// Save the current total supply amount to the current redistribution period // Save the current total supply amount to the current redistribution period
function saveRedistributionSupply() private returns (bool) { function saveRedistributionSupply() private returns (bool) {
uint256 currentRedistribution; uint256 currentRedistribution;
uint256 grownSupply;
grownSupply = growBy(totalSupply, 1);
currentRedistribution = uint256(redistributions[redistributions.length-1]); currentRedistribution = uint256(redistributions[redistributions.length-1]);
currentRedistribution &= (~maskRedistributionValue); currentRedistribution &= (~maskRedistributionValue);
currentRedistribution |= (totalSupply << shiftRedistributionValue); currentRedistribution |= (grownSupply << shiftRedistributionValue);
redistributions[redistributions.length-1] = bytes32(currentRedistribution); redistributions[redistributions.length-1] = bytes32(currentRedistribution);
return true; return true;
@ -270,29 +276,36 @@ contract DemurrageTokenSingleCap {
uint256 unit; uint256 unit;
redistributionSupply = toRedistributionSupply(_redistribution); redistributionSupply = toRedistributionSupply(_redistribution);
unit = redistributionSupply * (ppmDivider - (demurrageAmount / 1000000));
unit = (redistributionSupply * taxLevel) / 1000000;
increaseBaseBalance(sinkAddress, unit / ppmDivider); increaseBaseBalance(sinkAddress, unit / ppmDivider);
return unit; return unit;
} }
// Calculate the time delta in whole minutes passed between given timestamp and current timestamp
function getMinutesDelta(uint256 _lastTimestamp) public view returns (uint256) {
return (block.timestamp - _lastTimestamp) / 60;
}
// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call // Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
function applyDemurrage() public returns (bool) { function applyDemurrage() public returns (bool) {
uint128 epochPeriodCount; uint128 epochPeriodCount;
uint128 periodCount; uint256 periodCount;
uint256 lastDemurrageAmount; uint256 lastDemurrageAmount;
uint256 newDemurrageAmount; uint256 newDemurrageAmount;
epochPeriodCount = actualPeriod(); //epochPeriodCount = actualPeriod();
periodCount = epochPeriodCount - demurragePeriod; //periodCount = epochPeriodCount - demurragePeriod;
periodCount = getMinutesDelta(demurrageTimestamp);
if (periodCount == 0) { if (periodCount == 0) {
return false; return false;
} }
lastDemurrageAmount = demurrageAmount; lastDemurrageAmount = demurrageAmount;
demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount)); demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount));
demurragePeriod = epochPeriodCount; //demurragePeriod = epochPeriodCount;
emit Decayed(epochPeriodCount, periodCount, lastDemurrageAmount, demurrageAmount); demurrageTimestamp = periodStart + (periodCount * 60);
emit Decayed(block.timestamp, periodCount, lastDemurrageAmount, demurrageAmount);
return true; return true;
} }