mirror of
git://holbrook.no/erc20-demurrage-token
synced 2024-11-22 08:16:47 +01:00
Fix cumulative distribution calculation bug in SingleNocap
This commit is contained in:
parent
127c67e665
commit
a0557b35a0
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -38,6 +38,15 @@ class DemurrageTokenSettings:
|
|||||||
self.sink_address = None
|
self.sink_address = None
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'name {} demurrage level {} period minutes {} sink address {}'.format(
|
||||||
|
self.name,
|
||||||
|
self.demurrage_level,
|
||||||
|
self.period_minutes,
|
||||||
|
self.sink_address,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DemurrageToken(ERC20):
|
class DemurrageToken(ERC20):
|
||||||
|
|
||||||
__abi = {}
|
__abi = {}
|
||||||
@ -445,14 +454,16 @@ class DemurrageToken(ERC20):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
|
|
||||||
def get_distribution_from_redistribution(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None):
|
def get_distribution_from_redistribution(self, contract_address, redistribution, redistribution_previous, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||||
j = JSONRPCRequest(id_generator)
|
j = JSONRPCRequest(id_generator)
|
||||||
o = j.template()
|
o = j.template()
|
||||||
o['method'] = 'eth_call'
|
o['method'] = 'eth_call'
|
||||||
enc = ABIContractEncoder()
|
enc = ABIContractEncoder()
|
||||||
enc.method('getDistributionFromRedistribution')
|
enc.method('getDistributionFromRedistribution')
|
||||||
enc.typ(ABIContractType.BYTES32)
|
enc.typ(ABIContractType.BYTES32)
|
||||||
|
enc.typ(ABIContractType.BYTES32)
|
||||||
enc.bytes32(redistribution)
|
enc.bytes32(redistribution)
|
||||||
|
enc.bytes32(redistribution_previous)
|
||||||
data = add_0x(enc.get())
|
data = add_0x(enc.get())
|
||||||
tx = self.template(sender_address, contract_address)
|
tx = self.template(sender_address, contract_address)
|
||||||
tx = self.set_code(tx, data)
|
tx = self.set_code(tx, data)
|
||||||
|
@ -44,6 +44,7 @@ class TestTokenDeploy:
|
|||||||
self.settings.period_minutes = PERIOD
|
self.settings.period_minutes = PERIOD
|
||||||
self.settings.sink_address = sink_address
|
self.settings.sink_address = sink_address
|
||||||
self.sink_address = self.settings.sink_address
|
self.sink_address = self.settings.sink_address
|
||||||
|
logg.debug('using demurrage token settings: {}'.format(self.settings))
|
||||||
|
|
||||||
o = block_latest()
|
o = block_latest()
|
||||||
self.start_block = rpc.do(o)
|
self.start_block = rpc.do(o)
|
||||||
@ -203,7 +204,7 @@ class TestDemurrageCap(TestDemurrage):
|
|||||||
class TestDemurrageUnit(TestDemurrage):
|
class TestDemurrageUnit(TestDemurrage):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestDemurrage, self).setUp()
|
super(TestDemurrageUnit, self).setUp()
|
||||||
|
|
||||||
self.tax_level = 50
|
self.tax_level = 50
|
||||||
self.period_seconds = 60
|
self.period_seconds = 60
|
||||||
|
@ -17,7 +17,12 @@ done
|
|||||||
modes=(SingleCap) # other contracts need to be updted
|
modes=(SingleCap) # other contracts need to be updted
|
||||||
for m in ${modes[@]}; do
|
for m in ${modes[@]}; do
|
||||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_period.py
|
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_period.py
|
||||||
# ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution_unit.py
|
done
|
||||||
|
|
||||||
|
modes=(SingleNocap) # other contracts need to be updted
|
||||||
|
for m in ${modes[@]}; do
|
||||||
|
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution_unit.py
|
||||||
|
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution.py
|
||||||
done
|
done
|
||||||
|
|
||||||
modes=(MultiCap SingleCap)
|
modes=(MultiCap SingleCap)
|
||||||
|
@ -31,6 +31,78 @@ testdir = os.path.dirname(__file__)
|
|||||||
|
|
||||||
class TestRedistribution(TestDemurrageDefault):
|
class TestRedistribution(TestDemurrageDefault):
|
||||||
|
|
||||||
|
|
||||||
|
def test_redistribution_boundaries(self):
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||||
|
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
|
||||||
|
demurrage = (1 - (self.tax_level / 1000000)) * (10**28)
|
||||||
|
supply = self.default_supply
|
||||||
|
|
||||||
|
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[0], supply)
|
||||||
|
self.rpc.do(o)
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance(r)
|
||||||
|
logg.debug('balance before {} supply {}'.format(balance, supply))
|
||||||
|
|
||||||
|
self.backend.time_travel(self.start_time + self.period_seconds)
|
||||||
|
(tx_hash, o) = c.change_period(self.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.redistributions(self.address, 1, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
oo = c.to_redistribution_supply(self.address, r, sender_address=self.accounts[0])
|
||||||
|
rr = self.rpc.do(oo)
|
||||||
|
oo = c.to_redistribution_demurrage_modifier(self.address, r, sender_address=self.accounts[0])
|
||||||
|
rr = self.rpc.do(oo)
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance(r)
|
||||||
|
|
||||||
|
self.backend.time_travel(self.start_time + self.period_seconds * 2 + 1)
|
||||||
|
(tx_hash, o) = c.change_period(self.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.redistributions(self.address, 2, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
oo = c.to_redistribution_supply(self.address, r, sender_address=self.accounts[0])
|
||||||
|
rr = self.rpc.do(oo)
|
||||||
|
oo = c.to_redistribution_demurrage_modifier(self.address, r, sender_address=self.accounts[0])
|
||||||
|
rr = self.rpc.do(oo)
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance(r)
|
||||||
|
|
||||||
|
j = JSONRPCRequest(id_generator)
|
||||||
|
o = j.template()
|
||||||
|
o['method'] = 'eth_call'
|
||||||
|
enc = ABIContractEncoder()
|
||||||
|
enc.method('toRedistributionDemurrageModifier')
|
||||||
|
enc.typ(ABIContractType.BYTES32)
|
||||||
|
enc.bytes32(redistribution)
|
||||||
|
data = add_0x(enc.get())
|
||||||
|
tx = self.template(sender_address, contract_address)
|
||||||
|
tx = self.set_code(tx, data)
|
||||||
|
o['params'].append(self.normalize(tx))
|
||||||
|
o['params'].append('latest')
|
||||||
|
o = j.finalize(o)
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
|
@ -31,8 +31,9 @@ testdir = os.path.dirname(__file__)
|
|||||||
|
|
||||||
class TestRedistribution(TestDemurrageUnit):
|
class TestRedistribution(TestDemurrageUnit):
|
||||||
|
|
||||||
|
|
||||||
# TODO: move to "pure" test file when getdistribution is implemented in all contracts
|
# TODO: move to "pure" test file when getdistribution is implemented in all contracts
|
||||||
def test_distribution(self):
|
def test_distribution_direct(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)
|
||||||
|
|
||||||
@ -40,6 +41,8 @@ class TestRedistribution(TestDemurrageUnit):
|
|||||||
demurrage = (1 - (self.tax_level / 1000000)) * (10**28)
|
demurrage = (1 - (self.tax_level / 1000000)) * (10**28)
|
||||||
supply = self.default_supply
|
supply = self.default_supply
|
||||||
|
|
||||||
|
logg.debug('demurrage {} supply {}'.format(demurrage))
|
||||||
|
|
||||||
o = c.get_distribution(self.address, supply, demurrage, sender_address=self.accounts[0])
|
o = c.get_distribution(self.address, supply, demurrage, sender_address=self.accounts[0])
|
||||||
r = self.rpc.do(o)
|
r = self.rpc.do(o)
|
||||||
distribution = c.parse_get_distribution(r)
|
distribution = c.parse_get_distribution(r)
|
||||||
@ -51,17 +54,23 @@ class TestRedistribution(TestDemurrageUnit):
|
|||||||
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)
|
||||||
|
|
||||||
#demurrage = (1 - (self.tax_level / 1000000)) * (10**38)
|
demurrage_previous = (1 - (self.tax_level / 100000)) * (10**28)
|
||||||
demurrage = (1 - (self.tax_level / 1000000)) * (10**28)
|
demurrage = (1 - ((self.tax_level * 1.33) / 100000)) * (10**28)
|
||||||
|
|
||||||
|
logg.debug('demurrage then {} now {}'.format(demurrage_previous, demurrage))
|
||||||
supply = self.default_supply
|
supply = self.default_supply
|
||||||
|
|
||||||
o = c.to_redistribution(self.address, 0, demurrage, supply, 1, sender_address=self.accounts[0])
|
o = c.to_redistribution(self.address, 0, demurrage_previous, supply, 1, sender_address=self.accounts[0])
|
||||||
|
redistribution_previous = self.rpc.do(o)
|
||||||
|
|
||||||
|
o = c.to_redistribution(self.address, 0, demurrage, supply, 2, sender_address=self.accounts[0])
|
||||||
redistribution = self.rpc.do(o)
|
redistribution = self.rpc.do(o)
|
||||||
|
|
||||||
o = c.get_distribution_from_redistribution(self.address, redistribution, self.accounts[0])
|
o = c.get_distribution_from_redistribution(self.address, redistribution, redistribution_previous, self.accounts[0])
|
||||||
r = self.rpc.do(o)
|
r = self.rpc.do(o)
|
||||||
distribution = c.parse_get_distribution(r)
|
distribution = c.parse_get_distribution(r)
|
||||||
expected_distribution = self.default_supply * (self.tax_level / 1000000)
|
expected_distribution = self.default_supply * (self.tax_level / 1000000)
|
||||||
|
logg.debug('distribution {} supply {}'.format(distribution, self.default_supply))
|
||||||
self.assert_within_lower(distribution, expected_distribution, 1000)
|
self.assert_within_lower(distribution, expected_distribution, 1000)
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ contract DemurrageTokenSingleCap {
|
|||||||
// Cached demurrage amount, ppm with 38 digit resolution
|
// Cached demurrage amount, ppm with 38 digit resolution
|
||||||
uint128 public demurrageAmount;
|
uint128 public demurrageAmount;
|
||||||
|
|
||||||
|
uint256 public demurrageStart;
|
||||||
|
|
||||||
// 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
|
// Cached demurrage timestamp; the timestamp for which demurrageAmount was last calculated
|
||||||
@ -42,8 +44,11 @@ contract DemurrageTokenSingleCap {
|
|||||||
// Implements ERC20
|
// Implements ERC20
|
||||||
uint256 public totalSupply;
|
uint256 public totalSupply;
|
||||||
|
|
||||||
|
// Last executed period
|
||||||
|
uint256 public lastPeriod;
|
||||||
|
|
||||||
// Minimum amount of (demurraged) tokens an account must spend to participate in redistribution for a particular period
|
// Minimum amount of (demurraged) tokens an account must spend to participate in redistribution for a particular period
|
||||||
uint256 public minimumParticipantSpend;
|
//uint256 public minimumParticipantSpend;
|
||||||
|
|
||||||
// 128 bit resolution of the demurrage divisor
|
// 128 bit resolution of the demurrage divisor
|
||||||
// (this constant x 1000000 is contained within 128 bits)
|
// (this constant x 1000000 is contained within 128 bits)
|
||||||
@ -113,7 +118,9 @@ contract DemurrageTokenSingleCap {
|
|||||||
periodDuration = _periodMinutes * 60;
|
periodDuration = _periodMinutes * 60;
|
||||||
//demurrageAmount = 100000000000000000000000000000000000000 - _taxLevelMinute; // Represents 38 decimal places, same as resolutionFactor
|
//demurrageAmount = 100000000000000000000000000000000000000 - _taxLevelMinute; // Represents 38 decimal places, same as resolutionFactor
|
||||||
//demurrageAmount = 100000000000000000000000000000000000000;
|
//demurrageAmount = 100000000000000000000000000000000000000;
|
||||||
demurrageAmount = 10000000000000000000000000000;
|
//demurrageAmount = 10000000000000000000000000000;
|
||||||
|
demurrageAmount = uint128(nanoDivider) * 100;
|
||||||
|
demurrageStart = demurrageAmount;
|
||||||
//demurragePeriod = 1;
|
//demurragePeriod = 1;
|
||||||
taxLevel = _taxLevelMinute; // Represents 38 decimal places
|
taxLevel = _taxLevelMinute; // Represents 38 decimal places
|
||||||
bytes32 initialRedistribution = toRedistribution(0, demurrageAmount, 0, 1);
|
bytes32 initialRedistribution = toRedistribution(0, demurrageAmount, 0, 1);
|
||||||
@ -121,7 +128,7 @@ contract DemurrageTokenSingleCap {
|
|||||||
|
|
||||||
// Misc settings
|
// Misc settings
|
||||||
sinkAddress = _defaultSinkAddress;
|
sinkAddress = _defaultSinkAddress;
|
||||||
minimumParticipantSpend = 10 ** uint256(_decimals);
|
//minimumParticipantSpend = 10 ** uint256(_decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -274,7 +281,8 @@ contract DemurrageTokenSingleCap {
|
|||||||
bytes32 lastRedistribution;
|
bytes32 lastRedistribution;
|
||||||
uint256 currentPeriod;
|
uint256 currentPeriod;
|
||||||
|
|
||||||
lastRedistribution = redistributions[redistributions.length-1];
|
//lastRedistribution = redistributions[redistributions.length-1];
|
||||||
|
lastRedistribution = redistributions[lastPeriod];
|
||||||
currentPeriod = this.actualPeriod();
|
currentPeriod = this.actualPeriod();
|
||||||
if (currentPeriod <= toRedistributionPeriod(lastRedistribution)) {
|
if (currentPeriod <= toRedistributionPeriod(lastRedistribution)) {
|
||||||
return bytes32(0x00);
|
return bytes32(0x00);
|
||||||
@ -290,21 +298,25 @@ contract DemurrageTokenSingleCap {
|
|||||||
return difference / resolutionFactor;
|
return difference / resolutionFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDistributionFromRedistribution(bytes32 _redistribution) public returns (uint256) {
|
function getDistributionFromRedistribution(bytes32 _redistribution, bytes32 _redistributionPrevious) public returns (uint256) {
|
||||||
uint256 redistributionSupply;
|
uint256 redistributionSupply;
|
||||||
uint256 redistributionDemurrage;
|
uint256 redistributionDemurrage;
|
||||||
|
uint256 redistributionDemurragePrevious;
|
||||||
|
|
||||||
redistributionSupply = toRedistributionSupply(_redistribution);
|
redistributionSupply = toRedistributionSupply(_redistribution);
|
||||||
redistributionDemurrage = toRedistributionDemurrageModifier(_redistribution);
|
redistributionDemurrage = toRedistributionDemurrageModifier(_redistribution);
|
||||||
|
redistributionDemurragePrevious = toRedistributionDemurrageModifier(_redistributionPrevious);
|
||||||
|
redistributionDemurrage = demurrageStart - (redistributionDemurragePrevious - redistributionDemurrage);
|
||||||
return getDistribution(redistributionSupply, redistributionDemurrage);
|
return getDistribution(redistributionSupply, redistributionDemurrage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the amount sent to the sink address
|
// Returns the amount sent to the sink address
|
||||||
function applyDefaultRedistribution(bytes32 _redistribution) private returns (uint256) {
|
function applyDefaultRedistribution(bytes32 _redistribution, bytes32 _redistributionPrevious) private returns (uint256) {
|
||||||
uint256 unit;
|
uint256 unit;
|
||||||
|
|
||||||
unit = getDistributionFromRedistribution(_redistribution);
|
unit = getDistributionFromRedistribution(_redistribution, _redistributionPrevious);
|
||||||
increaseBaseBalance(sinkAddress, toBaseAmount(unit));
|
increaseBaseBalance(sinkAddress, toBaseAmount(unit));
|
||||||
|
lastPeriod += 1;
|
||||||
return unit;
|
return unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,7 +400,7 @@ contract DemurrageTokenSingleCap {
|
|||||||
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod);
|
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod);
|
||||||
redistributions.push(nextRedistribution);
|
redistributions.push(nextRedistribution);
|
||||||
|
|
||||||
applyDefaultRedistribution(nextRedistribution);
|
applyDefaultRedistribution(nextRedistribution, currentRedistribution);
|
||||||
emit Period(nextPeriod);
|
emit Period(nextPeriod);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user