bug: Wrong redistribution amount + limited demurrage apply
This commit is contained in:
parent
d0c02eadbf
commit
db56e0d33f
@ -1,4 +1,7 @@
|
|||||||
- 0.0.2-pending
|
- 0.0.9
|
||||||
|
* Fix wrong redistribution calculation in single nocap
|
||||||
|
[...]
|
||||||
|
- 0.0.2
|
||||||
* Move to chainlib-eth
|
* Move to chainlib-eth
|
||||||
- 0.0.1-unreleased
|
- 0.0.1-unreleased
|
||||||
* Interface for redistributed and non-redistributed, with or without cap
|
* Interface for redistributed and non-redistributed, with or without cap
|
||||||
|
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
File diff suppressed because one or more lines are too long
@ -325,8 +325,19 @@ class DemurrageToken(ERC20):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
|
|
||||||
def apply_demurrage(self, contract_address, sender_address):
|
def apply_demurrage(self, contract_address, sender_address, limit=0, tx_format=TxFormat.JSONRPC):
|
||||||
return self.transact_noarg('applyDemurrage', contract_address, sender_address)
|
if limit == 0:
|
||||||
|
return self.transact_noarg('applyDemurrage', contract_address, sender_address)
|
||||||
|
|
||||||
|
enc = ABIContractEncoder()
|
||||||
|
enc.method('applyDemurrageLimited')
|
||||||
|
enc.typ(ABIContractType.UINT256)
|
||||||
|
enc.uint256(limit)
|
||||||
|
data = enc.get()
|
||||||
|
tx = self.template(sender_address, contract_address, use_nonce=True)
|
||||||
|
tx = self.set_code(tx, data)
|
||||||
|
tx = self.finalize(tx, tx_format)
|
||||||
|
return tx
|
||||||
|
|
||||||
|
|
||||||
def change_period(self, contract_address, sender_address):
|
def change_period(self, contract_address, sender_address):
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
chainlib-eth~=0.0.15
|
chainlib-eth~=0.0.27
|
||||||
eth-erc20~=0.1.5
|
eth-erc20~=0.1.10
|
||||||
funga-eth~=0.5.1
|
funga-eth~=0.5.6
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = erc20-demurrage-token
|
name = erc20-demurrage-token
|
||||||
version = 0.0.8
|
version = 0.0.10
|
||||||
description = ERC20 token with redistributed continual demurrage
|
description = ERC20 token with redistributed continual demurrage
|
||||||
author = Louis Holbrook
|
author = Louis Holbrook
|
||||||
author_email = dev@holbrook.no
|
author_email = dev@holbrook.no
|
||||||
|
@ -54,6 +54,32 @@ class TestBasic(TestDemurrageDefault):
|
|||||||
self.assertEqual(balance, 1024)
|
self.assertEqual(balance, 1024)
|
||||||
|
|
||||||
|
|
||||||
|
def test_apply_demurrage_limited(self):
|
||||||
|
modifier = (10 ** 28)
|
||||||
|
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||||
|
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
|
||||||
|
o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
demurrage_amount = c.parse_demurrage_amount(r)
|
||||||
|
self.assertEqual(modifier, demurrage_amount)
|
||||||
|
|
||||||
|
self.backend.time_travel(self.start_time + 120)
|
||||||
|
(tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0], limit=1)
|
||||||
|
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
|
||||||
|
modifier = int(modifier_base * (10 ** 22)) # 38 decimal places minus 6 (1000000)
|
||||||
|
self.assertEqual(modifier, demurrage_amount)
|
||||||
|
|
||||||
|
|
||||||
def test_apply_demurrage(self):
|
def test_apply_demurrage(self):
|
||||||
modifier = (10 ** 28)
|
modifier = (10 ** 28)
|
||||||
|
|
||||||
@ -93,7 +119,6 @@ class TestBasic(TestDemurrageDefault):
|
|||||||
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_base = 1000000 - self.tax_level
|
modifier_base = 1000000 - self.tax_level
|
||||||
logg.debug('modifier base {}'.format(modifier_base))
|
|
||||||
modifier = int(modifier_base * (10 ** 22)) # 38 decimal places minus 6 (1000000)
|
modifier = int(modifier_base * (10 ** 22)) # 38 decimal places minus 6 (1000000)
|
||||||
self.assertEqual(modifier, demurrage_amount)
|
self.assertEqual(modifier, demurrage_amount)
|
||||||
|
|
||||||
@ -107,10 +132,11 @@ class TestBasic(TestDemurrageDefault):
|
|||||||
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_base = ((1000000 - self.tax_level) / 1000000) ** 10
|
modifier_base = ((1000000 - self.tax_level) / 1000000) ** 10
|
||||||
|
logg.warning('mod base {}'.format(modifier_base))
|
||||||
modifier = int(modifier_base * (10 ** 12))
|
modifier = int(modifier_base * (10 ** 12))
|
||||||
|
|
||||||
rounding_tolerance_nano = 4000000 # 0.000004% precision
|
rounding_tolerance_nano = 4000000 # 0.000004% precision
|
||||||
demurrage_amount_truncate = int(demurrage_amount / (10 ** 26)) # equals 12 decimal places
|
demurrage_amount_truncate = int(demurrage_amount / (10 ** 16)) # equals 38 decimal places - 14 for the modifier magniture - 2 for percent int calc + 6 for token decimals <- TODO verify this calc
|
||||||
self.assertGreaterEqual(modifier, demurrage_amount_truncate - rounding_tolerance_nano)
|
self.assertGreaterEqual(modifier, demurrage_amount_truncate - rounding_tolerance_nano)
|
||||||
self.assertLessEqual(modifier, demurrage_amount_truncate)
|
self.assertLessEqual(modifier, demurrage_amount_truncate)
|
||||||
|
|
||||||
|
@ -7,7 +7,15 @@ import logging
|
|||||||
# external imports
|
# external imports
|
||||||
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,
|
||||||
|
TxFactory,
|
||||||
|
TxFormat,
|
||||||
|
)
|
||||||
|
from chainlib.eth.contract import (
|
||||||
|
ABIContractEncoder,
|
||||||
|
ABIContractType,
|
||||||
|
)
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from erc20_demurrage_token import DemurrageToken
|
from erc20_demurrage_token import DemurrageToken
|
||||||
@ -103,5 +111,90 @@ class TestPeriod(TestDemurrageDefault):
|
|||||||
self.assertEqual(modifier, period)
|
self.assertEqual(modifier, period)
|
||||||
|
|
||||||
|
|
||||||
|
def test_change_sink(self):
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||||
|
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, ZERO_ADDRESS, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance_of(r)
|
||||||
|
self.assertEqual(balance, 0)
|
||||||
|
|
||||||
|
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 102400000000)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
|
||||||
|
self.backend.time_travel(self.start_time + self.period_seconds + 1)
|
||||||
|
|
||||||
|
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
(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.balance_of(self.address, ZERO_ADDRESS, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance_of(r)
|
||||||
|
self.assertGreater(balance, 0)
|
||||||
|
old_sink_balance = balance
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.accounts[3], sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance_of(r)
|
||||||
|
self.assertEqual(balance, 0)
|
||||||
|
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[5], self.rpc)
|
||||||
|
c = TxFactory(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
enc = ABIContractEncoder()
|
||||||
|
enc.method('setSinkAddress')
|
||||||
|
enc.typ(ABIContractType.ADDRESS)
|
||||||
|
enc.address(self.accounts[3])
|
||||||
|
data = enc.get()
|
||||||
|
o = c.template(self.accounts[5], self.address, use_nonce=True)
|
||||||
|
o = c.set_code(o, data)
|
||||||
|
(tx_hash, o) = c.finalize(o, TxFormat.JSONRPC)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 0)
|
||||||
|
|
||||||
|
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||||
|
c = TxFactory(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
enc = ABIContractEncoder()
|
||||||
|
enc.method('setSinkAddress')
|
||||||
|
enc.typ(ABIContractType.ADDRESS)
|
||||||
|
enc.address(self.accounts[3])
|
||||||
|
data = enc.get()
|
||||||
|
o = c.template(self.accounts[0], self.address, use_nonce=True)
|
||||||
|
o = c.set_code(o, data)
|
||||||
|
(tx_hash, o) = c.finalize(o, TxFormat.JSONRPC)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
o = receipt(tx_hash)
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
self.assertEqual(r['status'], 1)
|
||||||
|
|
||||||
|
self.backend.time_travel(self.start_time + (self.period_seconds * 2) + 1)
|
||||||
|
|
||||||
|
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||||
|
(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.balance_of(self.address, ZERO_ADDRESS, sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance_of(r)
|
||||||
|
self.assertLess(balance, old_sink_balance)
|
||||||
|
|
||||||
|
o = c.balance_of(self.address, self.accounts[3], sender_address=self.accounts[0])
|
||||||
|
r = self.rpc.do(o)
|
||||||
|
balance = c.parse_balance_of(r)
|
||||||
|
self.assertGreater(balance, 0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -128,6 +128,12 @@ contract DemurrageTokenSingleCap {
|
|||||||
minimumParticipantSpend = 10 ** uint256(_decimals);
|
minimumParticipantSpend = 10 ** uint256(_decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Change sink address for redistribution
|
||||||
|
function setSinkAddress(address _sinkAddress) public {
|
||||||
|
require(msg.sender == owner);
|
||||||
|
sinkAddress = _sinkAddress;
|
||||||
|
}
|
||||||
|
|
||||||
// Given address will be allowed to call the mintTo() function
|
// Given address will be allowed to call the mintTo() function
|
||||||
function addMinter(address _minter) public returns (bool) {
|
function addMinter(address _minter) public returns (bool) {
|
||||||
require(msg.sender == owner);
|
require(msg.sender == owner);
|
||||||
@ -311,6 +317,10 @@ contract DemurrageTokenSingleCap {
|
|||||||
|
|
||||||
// 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) {
|
||||||
|
return applyDemurrageLimited(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyDemurrageLimited(uint256 _rounds) public returns (bool) {
|
||||||
//uint128 epochPeriodCount;
|
//uint128 epochPeriodCount;
|
||||||
uint256 periodCount;
|
uint256 periodCount;
|
||||||
uint256 lastDemurrageAmount;
|
uint256 lastDemurrageAmount;
|
||||||
@ -323,6 +333,12 @@ contract DemurrageTokenSingleCap {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
lastDemurrageAmount = demurrageAmount;
|
lastDemurrageAmount = demurrageAmount;
|
||||||
|
// safety limit for exponential calculation to ensure that we can always
|
||||||
|
// execute this code no matter how much time passes.
|
||||||
|
if (_rounds > 0 && _rounds < periodCount) {
|
||||||
|
periodCount = _rounds;
|
||||||
|
}
|
||||||
|
|
||||||
demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount));
|
demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount));
|
||||||
//demurragePeriod = epochPeriodCount;
|
//demurragePeriod = epochPeriodCount;
|
||||||
demurrageTimestamp = demurrageTimestamp + (periodCount * 60);
|
demurrageTimestamp = demurrageTimestamp + (periodCount * 60);
|
||||||
|
@ -124,6 +124,13 @@ contract DemurrageTokenSingleCap {
|
|||||||
minimumParticipantSpend = 10 ** uint256(_decimals);
|
minimumParticipantSpend = 10 ** uint256(_decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Change sink address for redistribution
|
||||||
|
function setSinkAddress(address _sinkAddress) public {
|
||||||
|
require(msg.sender == owner);
|
||||||
|
sinkAddress = _sinkAddress;
|
||||||
|
}
|
||||||
|
|
||||||
// Given address will be allowed to call the mintTo() function
|
// Given address will be allowed to call the mintTo() function
|
||||||
function addMinter(address _minter) public returns (bool) {
|
function addMinter(address _minter) public returns (bool) {
|
||||||
require(msg.sender == owner);
|
require(msg.sender == owner);
|
||||||
@ -278,7 +285,8 @@ contract DemurrageTokenSingleCap {
|
|||||||
function getDistribution(uint256 _supply, uint256 _demurrageAmount) public view returns (uint256) {
|
function getDistribution(uint256 _supply, uint256 _demurrageAmount) public view returns (uint256) {
|
||||||
uint256 difference;
|
uint256 difference;
|
||||||
|
|
||||||
difference = _supply * (resolutionFactor - _demurrageAmount); //(nanoDivider - ((resolutionFactor - _demurrageAmount) / nanoDivider));
|
//difference = _supply * (resolutionFactor - _demurrageAmount); //(nanoDivider - ((resolutionFactor - _demurrageAmount) / nanoDivider));
|
||||||
|
difference = _supply * (resolutionFactor - (_demurrageAmount * 10000000000)); //(nanoDivider - ((resolutionFactor - _demurrageAmount) / nanoDivider));
|
||||||
return difference / resolutionFactor;
|
return difference / resolutionFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,6 +315,10 @@ contract DemurrageTokenSingleCap {
|
|||||||
|
|
||||||
// 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) {
|
||||||
|
return applyDemurrageLimited(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyDemurrageLimited(uint256 _rounds) public returns (bool) {
|
||||||
//uint128 epochPeriodCount;
|
//uint128 epochPeriodCount;
|
||||||
uint256 periodCount;
|
uint256 periodCount;
|
||||||
uint256 lastDemurrageAmount;
|
uint256 lastDemurrageAmount;
|
||||||
@ -319,6 +331,13 @@ contract DemurrageTokenSingleCap {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
lastDemurrageAmount = demurrageAmount;
|
lastDemurrageAmount = demurrageAmount;
|
||||||
|
|
||||||
|
// safety limit for exponential calculation to ensure that we can always
|
||||||
|
// execute this code no matter how much time passes.
|
||||||
|
if (_rounds > 0 && _rounds < periodCount) {
|
||||||
|
periodCount = _rounds;
|
||||||
|
}
|
||||||
|
|
||||||
demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount));
|
demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount));
|
||||||
//demurragePeriod = epochPeriodCount;
|
//demurragePeriod = epochPeriodCount;
|
||||||
demurrageTimestamp = demurrageTimestamp + (periodCount * 60);
|
demurrageTimestamp = demurrageTimestamp + (periodCount * 60);
|
||||||
|
Reference in New Issue
Block a user