Merge branch 'master' into lash/apply-cli

This commit is contained in:
lash 2022-03-02 09:03:40 +00:00
commit f1a2a78eb4
14 changed files with 218 additions and 15 deletions

2
.gitignore vendored
View File

@ -6,3 +6,5 @@ __pycache__
gmon.out gmon.out
solidity/*.json solidity/*.json
solidity/*.bin solidity/*.bin
.venv
venv

36
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,36 @@
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Python.gitlab-ci.yml
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/python/tags/
image: python:3.8
# Change pip's cache directory to be inside the project directory since we can
# only cache local items.
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
# Pip's cache doesn't store the python packages
# https://pip.pypa.io/en/stable/reference/pip_install/#caching
#
# If you want to also cache the installed packages, you have to install
# them in a virtualenv and cache it as well.
cache:
paths:
- .cache/pip
- venv/
before_script:
- cd ./python
- python --version # For debugging
- pip install virtualenv
- virtualenv venv
- source venv/bin/activate
test:
script:
- pip install -r requirements.txt -r test_requirements.txt --extra-index-url https://pip.grassrootseconomics.net
- bash run_tests.sh

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

View File

@ -325,9 +325,20 @@ 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):
if limit == 0:
return self.transact_noarg('applyDemurrage', contract_address, sender_address) 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):
return self.transact_noarg('changePeriod', contract_address, sender_address) return self.transact_noarg('changePeriod', contract_address, sender_address)

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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);

View File

@ -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);