Compare commits
11 Commits
lash/burn
...
lash/safe-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00f4ddba68
|
||
|
|
9d7b87be9c
|
||
|
|
697d2bf227
|
||
|
|
e49493def1
|
||
|
|
4f1bd36c12
|
||
|
|
166302ef49
|
||
|
|
2ec72bbc55
|
||
|
|
97415dbed2
|
||
|
|
3b87961d13
|
||
|
|
a56f1c4869
|
||
|
|
ef294eaec2
|
5
CAVEAT
Normal file
5
CAVEAT
Normal file
@@ -0,0 +1,5 @@
|
||||
The contract is intended for slow rates of decay (e.g. 2% per month). Very high levels of decay (2% per minute) will lead to overflows, and will need a more flexible implementation to support it.
|
||||
|
||||
The contract is written with frequent usage in mind. If used for tokens with low usage freqency (e.g. several days idle), it is recommended to run a continuous process triggering the changePeriod() contract call, to reduce the amount of exponential calculation the application of demurrage will trigger.
|
||||
|
||||
When changing the period, the supply for the consecutive period will be taken at the time of code execution, and thus not necessarily at the time when the redistribution period threshold was crossed.
|
||||
8
ROADMAP
Normal file
8
ROADMAP
Normal file
@@ -0,0 +1,8 @@
|
||||
- 0.1.3
|
||||
* Snapshot supply for crossed redistribution thresholds before minting new tokens.
|
||||
- 0.1.4
|
||||
* Implement natural logarithm
|
||||
- 0.1.5
|
||||
* Port changes from SingleNocap to SingleCap
|
||||
- 0.2.0
|
||||
* Make decay resolutions configurable, to support high levels of decay.
|
||||
@@ -1,3 +1,9 @@
|
||||
- 0.2.0
|
||||
* Add token burn function
|
||||
* Fix gas leak when calculating decay on period change
|
||||
* Remove all but SingleNocap contract in make install
|
||||
* Make approve explicitly set value
|
||||
* Add increaseAllowance and decreaseAllowance methods
|
||||
- 0.1.1
|
||||
* Settable demurrage steps for apply demurrage cli tool
|
||||
- 0.1.0
|
||||
|
||||
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -9,8 +9,7 @@ from chainlib.eth.constant import ZERO_ADDRESS
|
||||
# local imports
|
||||
from .token import DemurrageToken
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DemurrageCalculator:
|
||||
|
||||
@@ -16,17 +16,7 @@ import math
|
||||
|
||||
# external imports
|
||||
import confini
|
||||
from funga.eth.signer import EIP155Signer
|
||||
from funga.eth.keystore.dict import DictKeystore
|
||||
from chainlib.chain import ChainSpec
|
||||
from chainlib.eth.nonce import (
|
||||
RPCNonceOracle,
|
||||
OverrideNonceOracle,
|
||||
)
|
||||
from chainlib.eth.gas import (
|
||||
RPCGasOracle,
|
||||
OverrideGasOracle,
|
||||
)
|
||||
import chainlib.eth.cli
|
||||
from chainlib.eth.block import (
|
||||
block_latest,
|
||||
block_by_number,
|
||||
@@ -35,8 +25,20 @@ from chainlib.eth.block import (
|
||||
from chainlib.eth.connection import EthHTTPConnection
|
||||
from chainlib.eth.tx import receipt
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
import chainlib.eth.cli
|
||||
from hexathon import to_int as hex_to_int
|
||||
import chainlib.eth.cli
|
||||
from chainlib.eth.settings import process_settings
|
||||
from chainlib.settings import ChainSettings
|
||||
from chainlib.eth.cli.arg import (
|
||||
Arg,
|
||||
ArgFlag,
|
||||
process_args,
|
||||
)
|
||||
from chainlib.eth.cli.config import (
|
||||
Config,
|
||||
process_config,
|
||||
)
|
||||
from chainlib.eth.cli.log import process_log
|
||||
|
||||
# local imports
|
||||
import erc20_demurrage_token
|
||||
@@ -45,32 +47,38 @@ from erc20_demurrage_token import (
|
||||
DemurrageTokenSettings,
|
||||
)
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
script_dir = os.path.dirname(__file__)
|
||||
data_dir = os.path.join(script_dir, '..', 'data')
|
||||
|
||||
config_dir = os.path.join(data_dir, 'config')
|
||||
def process_config_local(config, arg, args, flags):
|
||||
config.add(args.steps, '_STEPS', False)
|
||||
return config
|
||||
|
||||
arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC
|
||||
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
|
||||
|
||||
arg_flags = ArgFlag()
|
||||
arg = Arg(arg_flags)
|
||||
flags = arg_flags.STD_WRITE | arg_flags.EXEC | arg_flags.WALLET
|
||||
|
||||
argparser = chainlib.eth.cli.ArgumentParser()
|
||||
argparser = process_args(argparser, arg, flags)
|
||||
argparser.add_argument('--steps', type=int, default=0, help='Max demurrage steps to apply per round')
|
||||
args = argparser.parse_args()
|
||||
config = chainlib.eth.cli.Config.from_args(args, arg_flags, default_fee_limit=DemurrageToken.gas(), base_config_dir=config_dir)
|
||||
config.add(args.steps, '_STEPS', False)
|
||||
|
||||
logg = process_log(args, logg)
|
||||
|
||||
config = Config()
|
||||
config = process_config(config, arg, args, flags)
|
||||
config = process_config_local(config, arg, args, flags)
|
||||
logg.debug('config loaded:\n{}'.format(config))
|
||||
|
||||
wallet = chainlib.eth.cli.Wallet()
|
||||
wallet.from_config(config)
|
||||
|
||||
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
|
||||
conn = rpc.connect_by_config(config)
|
||||
|
||||
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
|
||||
settings = ChainSettings()
|
||||
settings = process_settings(settings, config)
|
||||
logg.debug('settings loaded:\n{}'.format(settings))
|
||||
|
||||
|
||||
def main():
|
||||
chain_spec = settings.get('CHAIN_SPEC')
|
||||
conn = settings.get('CONN')
|
||||
o = block_latest()
|
||||
r = conn.do(o)
|
||||
|
||||
@@ -87,9 +95,9 @@ def main():
|
||||
block_start_timestamp = block_start.timestamp
|
||||
block_start_datetime = datetime.datetime.fromtimestamp(block_start_timestamp)
|
||||
|
||||
gas_oracle = rpc.get_gas_oracle()
|
||||
gas_oracle = settings.get('FEE_ORACLE')
|
||||
c = DemurrageToken(chain_spec, gas_oracle=gas_oracle)
|
||||
o = c.demurrage_timestamp(config.get('_EXEC_ADDRESS'))
|
||||
o = c.demurrage_timestamp(settings.get('EXEC'))
|
||||
r = conn.do(o)
|
||||
|
||||
demurrage_timestamp = None
|
||||
@@ -120,17 +128,17 @@ def main():
|
||||
|
||||
last_tx_hash = None
|
||||
for i in range(rounds):
|
||||
signer = rpc.get_signer()
|
||||
signer_address = rpc.get_sender_address()
|
||||
signer = settings.get('SIGNER')
|
||||
signer_address = settings.get('SENDER_ADDRESS')
|
||||
|
||||
nonce_oracle = rpc.get_nonce_oracle()
|
||||
nonce_oracle = settings.get('NONCE_ORACLE')
|
||||
|
||||
c = DemurrageToken(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
|
||||
(tx_hash_hex, o) = c.apply_demurrage(config.get('_EXEC_ADDRESS'), signer_address, limit=config.get('_STEPS'))
|
||||
if config.get('_RPC_SEND'):
|
||||
if settings.get('RPC_SEND'):
|
||||
print(tx_hash_hex)
|
||||
conn.do(o)
|
||||
if config.get('_WAIT_ALL') or (i == rounds - 1 and config.get('_WAIT')):
|
||||
if config.true('_WAIT_ALL') or (i == rounds - 1 and config.true('_WAIT')):
|
||||
r = conn.wait(tx_hash_hex)
|
||||
if r['status'] == 0:
|
||||
sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you')
|
||||
|
||||
@@ -14,21 +14,22 @@ import logging
|
||||
|
||||
# external imports
|
||||
import confini
|
||||
from funga.eth.signer import EIP155Signer
|
||||
from funga.eth.keystore.dict import DictKeystore
|
||||
from chainlib.chain import ChainSpec
|
||||
from chainlib.eth.nonce import (
|
||||
RPCNonceOracle,
|
||||
OverrideNonceOracle,
|
||||
)
|
||||
from chainlib.eth.gas import (
|
||||
RPCGasOracle,
|
||||
OverrideGasOracle,
|
||||
)
|
||||
from chainlib.eth.connection import EthHTTPConnection
|
||||
from chainlib.eth.tx import receipt
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
import chainlib.eth.cli
|
||||
from chainlib.eth.settings import process_settings
|
||||
from chainlib.settings import ChainSettings
|
||||
from chainlib.eth.cli.arg import (
|
||||
Arg,
|
||||
ArgFlag,
|
||||
process_args,
|
||||
)
|
||||
from chainlib.eth.cli.config import (
|
||||
Config,
|
||||
process_config,
|
||||
)
|
||||
from chainlib.eth.cli.log import process_log
|
||||
|
||||
# local imports
|
||||
import erc20_demurrage_token
|
||||
@@ -37,84 +38,75 @@ from erc20_demurrage_token import (
|
||||
DemurrageTokenSettings,
|
||||
)
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
script_dir = os.path.dirname(__file__)
|
||||
data_dir = os.path.join(script_dir, '..', 'data')
|
||||
|
||||
config_dir = os.path.join(data_dir, 'config')
|
||||
def process_config_local(config, arg, args, flags):
|
||||
config.add(args.token_name, 'TOKEN_NAME', False)
|
||||
config.add(args.token_symbol, 'TOKEN_SYMBOL', False)
|
||||
config.add(args.token_decimals, 'TOKEN_DECIMALS', False)
|
||||
config.add(args.sink_address, 'TOKEN_SINK_ADDRESS', False)
|
||||
config.add(args.redistribution_period, 'TOKEN_REDISTRIBUTION_PERIOD', False)
|
||||
config.add(args.demurrage_level, 'TOKEN_DEMURRAGE_LEVEL', False)
|
||||
config.add(0, 'TOKEN_SUPPLY_LIMIT', False)
|
||||
return config
|
||||
|
||||
arg_flags = chainlib.eth.cli.argflag_std_write
|
||||
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
|
||||
|
||||
arg_flags = ArgFlag()
|
||||
arg = Arg(arg_flags)
|
||||
flags = arg_flags.STD_WRITE | arg_flags.EXEC | arg_flags.WALLET
|
||||
|
||||
argparser = chainlib.eth.cli.ArgumentParser()
|
||||
argparser = process_args(argparser, arg, flags)
|
||||
argparser.add_argument('--name', dest='token_name', type=str, help='Token name')
|
||||
argparser.add_argument('--symbol', dest='token_symbol', required=True, type=str, help='Token symbol')
|
||||
argparser.add_argument('--decimals', dest='token_decimals', type=int, help='Token decimals')
|
||||
argparser.add_argument('--sink-address', dest='sink_address', type=str, help='demurrage level,ppm per minute')
|
||||
argparser.add_argument('--supply-limit', dest='supply_limit', type=int, help='token supply limit (0 = no limit)')
|
||||
argparser.add_argument('--redistribution-period', type=int, help='redistribution period, minutes (0 = deactivate)') # default 10080 = week
|
||||
argparser.add_argument('--multi', action='store_true', help='automatic redistribution')
|
||||
#argparser.add_argument('--supply-limit', dest='supply_limit', type=int, help='token supply limit (0 = no limit)')
|
||||
argparser.add_argument('--redistribution-period', dest='redistribution_period', type=int, help='redistribution period, minutes (0 = deactivate)') # default 10080 = week
|
||||
#argparser.add_argument('--multi', action='store_true', help='automatic redistribution')
|
||||
argparser.add_argument('--demurrage-level', dest='demurrage_level', type=int, help='demurrage level, ppm per minute')
|
||||
args = argparser.parse_args()
|
||||
|
||||
arg_flags = chainlib.eth.cli.argflag_std_write
|
||||
logg = process_log(args, logg)
|
||||
|
||||
extra_args = {
|
||||
'redistribution_period': 'TOKEN_REDISTRIBUTION_PERIOD',
|
||||
'demurrage_level': 'TOKEN_DEMURRAGE_LEVEL',
|
||||
'supply_limit': 'TOKEN_SUPPLY_LIMIT',
|
||||
'token_name': 'TOKEN_NAME',
|
||||
'token_symbol': 'TOKEN_SYMBOL',
|
||||
'token_decimals': 'TOKEN_DECIMALS',
|
||||
'sink_address': 'TOKEN_SINK_ADDRESS',
|
||||
'multi': None,
|
||||
}
|
||||
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=DemurrageToken.gas(), base_config_dir=config_dir)
|
||||
|
||||
if not bool(config.get('TOKEN_NAME')):
|
||||
logg.info('token name not set, using symbol {} as name'.format(config.get('TOKEN_SYMBOL')))
|
||||
config.add(config.get('TOKEN_SYMBOL'), 'TOKEN_NAME', True)
|
||||
|
||||
if config.get('TOKEN_SUPPLY_LIMIT') == None:
|
||||
config.add(0, 'TOKEN_SUPPLY_LIMIT', True)
|
||||
|
||||
if config.get('TOKEN_REDISTRIBUTION_PERIOD') == None:
|
||||
config.add(10800, 'TOKEN_REDISTRIBUTION_PERIOD', True)
|
||||
config = Config()
|
||||
config = process_config(config, arg, args, flags)
|
||||
config = process_config_local(config, arg, args, flags)
|
||||
logg.debug('config loaded:\n{}'.format(config))
|
||||
|
||||
wallet = chainlib.eth.cli.Wallet()
|
||||
wallet.from_config(config)
|
||||
settings = ChainSettings()
|
||||
settings = process_settings(settings, config)
|
||||
logg.debug('settings loaded:\n{}'.format(settings))
|
||||
|
||||
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
|
||||
conn = rpc.connect_by_config(config)
|
||||
|
||||
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
|
||||
|
||||
def main():
|
||||
signer = rpc.get_signer()
|
||||
signer_address = rpc.get_sender_address()
|
||||
chain_spec = settings.get('CHAIN_SPEC')
|
||||
conn = settings.get('CONN')
|
||||
signer = settings.get('SIGNER')
|
||||
signer_address = settings.get('SENDER_ADDRESS')
|
||||
|
||||
gas_oracle = rpc.get_gas_oracle()
|
||||
nonce_oracle = rpc.get_nonce_oracle()
|
||||
gas_oracle = settings.get('FEE_ORACLE')
|
||||
nonce_oracle = settings.get('NONCE_ORACLE')
|
||||
|
||||
c = DemurrageToken(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
|
||||
settings = DemurrageTokenSettings()
|
||||
settings.name = config.get('TOKEN_NAME')
|
||||
settings.symbol = config.get('TOKEN_SYMBOL')
|
||||
settings.decimals = int(config.get('TOKEN_DECIMALS'))
|
||||
settings.demurrage_level = int(config.get('TOKEN_DEMURRAGE_LEVEL'))
|
||||
settings.period_minutes = int(config.get('TOKEN_REDISTRIBUTION_PERIOD'))
|
||||
settings.sink_address = config.get('TOKEN_SINK_ADDRESS')
|
||||
token_settings = DemurrageTokenSettings()
|
||||
token_settings.name = config.get('TOKEN_NAME')
|
||||
token_settings.symbol = config.get('TOKEN_SYMBOL')
|
||||
token_settings.decimals = int(config.get('TOKEN_DECIMALS'))
|
||||
token_settings.demurrage_level = int(config.get('TOKEN_DEMURRAGE_LEVEL'))
|
||||
token_settings.period_minutes = int(config.get('TOKEN_REDISTRIBUTION_PERIOD'))
|
||||
token_settings.sink_address = config.get('TOKEN_SINK_ADDRESS')
|
||||
|
||||
(tx_hash_hex, o) = c.constructor(
|
||||
signer_address,
|
||||
settings,
|
||||
token_settings,
|
||||
redistribute=config.true('_MULTI'),
|
||||
cap=int(config.get('TOKEN_SUPPLY_LIMIT')),
|
||||
)
|
||||
if config.get('_RPC_SEND'):
|
||||
if settings.get('RPC_SEND'):
|
||||
conn.do(o)
|
||||
if config.get('_WAIT'):
|
||||
if config.true('_WAIT'):
|
||||
r = conn.wait(tx_hash_hex)
|
||||
if r['status'] == 0:
|
||||
sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you')
|
||||
|
||||
@@ -117,6 +117,34 @@ class DemurrageToken(ERC20):
|
||||
return DemurrageToken.__bytecode[name]
|
||||
|
||||
|
||||
def increase_allowance(self, contract_address, sender_address, address, value, tx_format=TxFormat.JSONRPC):
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('increaseAllowance')
|
||||
enc.typ(ABIContractType.ADDRESS)
|
||||
enc.typ(ABIContractType.UINT256)
|
||||
enc.address(address)
|
||||
enc.uint256(value)
|
||||
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 decrease_allowance(self, contract_address, sender_address, address, value, tx_format=TxFormat.JSONRPC):
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('decreaseAllowance')
|
||||
enc.typ(ABIContractType.ADDRESS)
|
||||
enc.typ(ABIContractType.UINT256)
|
||||
enc.address(address)
|
||||
enc.uint256(value)
|
||||
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 add_minter(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('addMinter')
|
||||
|
||||
@@ -20,7 +20,7 @@ from erc20_demurrage_token import (
|
||||
DemurrageToken,
|
||||
)
|
||||
|
||||
logg = logging.getLogger()
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
#BLOCKTIME = 5 # seconds
|
||||
TAX_LEVEL = int(10000 * 2) # 2%
|
||||
@@ -95,13 +95,6 @@ class TestDemurrage(EthTesterCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDemurrage, self).setUp()
|
||||
# token_deploy = TestTokenDeploy()
|
||||
# self.settings = token_deploy.settings
|
||||
# self.sink_address = token_deploy.sink_address
|
||||
# self.start_block = token_deploy.start_block
|
||||
# self.start_time = token_deploy.start_time
|
||||
# self.default_supply = self.default_supply
|
||||
# self.default_supply_cap = self.default_supply_cap
|
||||
period = PERIOD
|
||||
try:
|
||||
period = getattr(self, 'period')
|
||||
@@ -133,6 +126,13 @@ class TestDemurrage(EthTesterCase):
|
||||
logg.debug('asserted within lower {} <= {} <= {}'.format(lower_target, v, target))
|
||||
|
||||
|
||||
def assert_within_greater(self, v, target, tolerance_ppm):
|
||||
higher_target = target + (target * (tolerance_ppm / 1000000))
|
||||
self.assertLessEqual(v, higher_target)
|
||||
self.assertGreaterEqual(v, target)
|
||||
logg.debug('asserted within lower {} <= {} <= {}'.format(target, v, higher_target))
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ for m in ${modes[@]}; do
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_growth.py
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_amounts.py
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_single.py
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_burn.py
|
||||
done
|
||||
|
||||
#modes=(SingleCap) # other contracts need to be updted
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[metadata]
|
||||
name = erc20-demurrage-token
|
||||
version = 0.1.1
|
||||
version = 0.2.0
|
||||
description = ERC20 token with redistributed continual demurrage
|
||||
author = Louis Holbrook
|
||||
author_email = dev@holbrook.no
|
||||
|
||||
@@ -276,7 +276,52 @@ class TestBasic(TestDemurrageDefault):
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
|
||||
|
||||
def test_approve(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 500)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 600)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 0)
|
||||
|
||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 0)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 600)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.increase_allowance(self.address, self.accounts[0], self.accounts[1], 200)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.decrease_allowance(self.address, self.accounts[0], self.accounts[1], 800)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 42)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
|
||||
def test_transfer_from(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
@@ -315,6 +360,12 @@ class TestBasic(TestDemurrageDefault):
|
||||
balance = c.parse_balance_of(r)
|
||||
self.assertEqual(balance, 500)
|
||||
|
||||
(tx_hash, o) = c.transfer_from(self.address, self.accounts[2], self.accounts[1], self.accounts[3], 1)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -18,17 +18,42 @@ from chainlib.eth.block import (
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
|
||||
# test imports
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageDefault
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrage
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logg = logging.getLogger()
|
||||
|
||||
testdir = os.path.dirname(__file__)
|
||||
|
||||
TAX_LEVEL = 2
|
||||
|
||||
class TestBurn(TestDemurrageDefault):
|
||||
class TestBurn(TestDemurrage):
|
||||
|
||||
def setUp(self):
|
||||
super(TestBurn, self).setUp()
|
||||
|
||||
|
||||
def deploy(self, tax_level=None):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
self.mode = os.environ.get('ERC20_DEMURRAGE_TOKEN_TEST_MODE')
|
||||
if self.mode == None:
|
||||
self.mode = 'MultiNocap'
|
||||
logg.debug('executing test setup default mode {}'.format(self.mode))
|
||||
|
||||
if tax_level != None:
|
||||
self.deployer.settings.demurrage_level = tax_level * (10 ** 32)
|
||||
self.deployer.settings.sink_address = self.accounts[9]
|
||||
self.deployer.sink_address = self.accounts[9]
|
||||
super(TestBurn, self).deploy(c, self.mode)
|
||||
|
||||
logg.info('deployed with mode {}'.format(self.mode))
|
||||
|
||||
|
||||
# Burn tokens and immediately check balances and supply
|
||||
def test_burn_basic(self):
|
||||
self.deploy()
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
@@ -67,7 +92,9 @@ class TestBurn(TestDemurrageDefault):
|
||||
self.assertEqual(burned, 600000)
|
||||
|
||||
|
||||
# burn tokens and check sink balance and supply after first redistribution period
|
||||
def test_burned_redistribution(self):
|
||||
self.deploy()
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
@@ -123,8 +150,9 @@ class TestBurn(TestDemurrageDefault):
|
||||
self.assert_within_lower(bal, 500000000, 0.0025)
|
||||
|
||||
|
||||
|
||||
# burn tokens and check sink and taxed balance and supply after first redistribution period
|
||||
def test_burned_other_redistribution(self):
|
||||
self.deploy()
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
@@ -191,5 +219,102 @@ class TestBurn(TestDemurrageDefault):
|
||||
self.assert_within_lower(sink_bal, bal, 0.09) # TODO is this ok variance, 1.0 is ppm?
|
||||
|
||||
|
||||
# verify expected results of balance and supply after multiple redistribution periods
|
||||
def test_burn_accumulate(self):
|
||||
self.deploy(tax_level=2/1000)
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
(tx_hash, o) = c.add_minter(self.address, self.accounts[0], self.sink_address)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.sink_address, self.default_supply)
|
||||
r = self.rpc.do(o)
|
||||
|
||||
balance_share = int(self.default_supply / 2)
|
||||
nonce_oracle = RPCNonceOracle(self.sink_address, self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
(tx_hash, o) = c.transfer(self.address, self.sink_address, self.accounts[1], balance_share)
|
||||
r = self.rpc.do(o)
|
||||
|
||||
new_supply = None
|
||||
burn_rate = 1000
|
||||
sink_bal = None
|
||||
bob_bal = None
|
||||
bob_refund = None
|
||||
|
||||
o = c.balance(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
bob_bal = c.parse_balance(r)
|
||||
prev_bob_bal = bob_bal
|
||||
|
||||
iterations = 100
|
||||
|
||||
for i in range(1, iterations + 1):
|
||||
nonce_oracle = RPCNonceOracle(self.sink_address, self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
if bob_refund != None:
|
||||
(tx_hash, o) = c.transfer(self.address, self.sink_address, self.accounts[1], bob_refund)
|
||||
r = self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.burn(self.address, self.sink_address, burn_rate)
|
||||
r = self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
o = c.total_supply(self.address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
new_supply = c.parse_total_supply(r)
|
||||
|
||||
self.backend.time_travel(self.start_time + (self.period_seconds * i))
|
||||
|
||||
(tx_hash, o) = c.change_period(self.address, self.accounts[0])
|
||||
self.rpc.do(o)
|
||||
|
||||
o = c.balance(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
bob_bal = c.parse_balance(r)
|
||||
bob_refund = prev_bob_bal - bob_bal
|
||||
|
||||
o = c.balance(self.address, self.sink_address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
burner_bal = c.parse_balance(r)
|
||||
|
||||
sum_supply = bob_bal + burner_bal
|
||||
|
||||
o = c.total_burned(self.address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
total_burned = c.parse_balance(r)
|
||||
|
||||
o = c.to_base_amount(self.address, total_burned, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
total_burned_base = c.parse_balance(r)
|
||||
|
||||
expected_supply = self.default_supply - (burn_rate * i)
|
||||
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))
|
||||
self.assertEqual(new_supply, expected_supply)
|
||||
|
||||
sum_supply = burner_bal + bob_bal
|
||||
logg.debug('balances sink {} bob {} total {} supply real {} original {}'.format(sink_bal, bob_bal, sum_supply, new_supply, self.default_supply))
|
||||
|
||||
self.assert_within_lower(sum_supply, new_supply, 0.00001)
|
||||
self.assert_within_greater(burner_bal, balance_share - total_burned, 0.1)
|
||||
|
||||
bob_delta = self.default_supply * ((2 / 1000000) / 1000)
|
||||
self.assert_within_lower(bob_bal, balance_share - bob_delta, 0.1)
|
||||
|
||||
self.assertEqual(total_burned, iterations * burn_rate)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
133
python/tests/test_demurrage.py
Normal file
133
python/tests/test_demurrage.py
Normal file
@@ -0,0 +1,133 @@
|
||||
# standard imports
|
||||
import datetime
|
||||
import unittest
|
||||
import logging
|
||||
import os
|
||||
|
||||
# external imports
|
||||
from chainlib.eth.nonce import RPCNonceOracle
|
||||
from chainlib.eth.tx import receipt
|
||||
|
||||
# local imports
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
from erc20_demurrage_token.demurrage import DemurrageCalculator
|
||||
|
||||
# test imports
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrage
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
class TestDemurragePeriods(TestDemurrage):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDemurragePeriods, self).setUp()
|
||||
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
self.mode = os.environ.get('ERC20_DEMURRAGE_TOKEN_TEST_MODE')
|
||||
if self.mode == None:
|
||||
self.mode = 'MultiNocap'
|
||||
logg.debug('executing test setup default mode {}'.format(self.mode))
|
||||
|
||||
self.deployer.settings.demurrage_level = (2 / 1000) * (10 ** 32)
|
||||
self.deployer.settings.sink_address = self.accounts[9]
|
||||
self.deployer.sink_address = self.accounts[9]
|
||||
self.deploy(c, self.mode)
|
||||
|
||||
logg.info('deployed with mode {}'.format(self.mode))
|
||||
|
||||
|
||||
# verify that tax level calculation is in ppm as expected
|
||||
def test_ppm(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
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], self.default_supply)
|
||||
r = self.rpc.do(o)
|
||||
|
||||
self.backend.time_travel(self.start_time + 60)
|
||||
|
||||
(tx_hash, o) = c.apply_demurrage(self.address, self.accounts[0])
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
o = c.balance(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
bob_bal = c.parse_balance(r)
|
||||
|
||||
o = c.balance(self.address, self.sink_address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
sink_bal = c.parse_balance(r)
|
||||
|
||||
o = c.total_supply(self.address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
new_supply = c.parse_total_supply(r)
|
||||
|
||||
balance_delta = self.default_supply * ((2 / 1000000) / 1000)
|
||||
self.assertEqual(bob_bal, self.default_supply - balance_delta)
|
||||
|
||||
|
||||
|
||||
# verify balances and supply after multiple demurrage periods
|
||||
def test_over_time(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
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], self.default_supply)
|
||||
r = self.rpc.do(o)
|
||||
|
||||
o = c.balance(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
bob_bal = c.parse_balance(r)
|
||||
prev_bob_bal = bob_bal
|
||||
|
||||
nonce_oracle = RPCNonceOracle(self.sink_address, self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
iterations = 100
|
||||
|
||||
for i in range(1, iterations + 1):
|
||||
self.backend.time_travel(self.start_time + (self.period_seconds * i))
|
||||
|
||||
(tx_hash, o) = c.transfer(self.address, self.sink_address, self.accounts[1], prev_bob_bal - bob_bal)
|
||||
r = self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.apply_demurrage(self.address, self.sink_address)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
o = c.balance(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
bob_bal = c.parse_balance(r)
|
||||
|
||||
o = c.balance(self.address, self.sink_address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
sink_bal = c.parse_balance(r)
|
||||
|
||||
o = c.total_supply(self.address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
new_supply = c.parse_total_supply(r)
|
||||
|
||||
logg.info('round {} supply {} balance sink {} bob {}'.format(i, new_supply, sink_bal, bob_bal))
|
||||
|
||||
sum_supply = sink_bal + bob_bal
|
||||
|
||||
bob_delta = self.default_supply * ((2 / 1000000) / 100)
|
||||
|
||||
self.assert_within_lower(sum_supply, new_supply, 0.00001)
|
||||
self.assert_within_greater(bob_bal, self.default_supply - bob_delta, 0.001)
|
||||
self.assert_within_lower(sink_bal, bob_delta, 1000)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -28,23 +28,23 @@ testdir = os.path.dirname(__file__)
|
||||
|
||||
class TestGrowth(TestDemurrageDefault):
|
||||
|
||||
def test_grow_by(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
growth_factor = (1000000 + self.tax_level) / 1000000
|
||||
v = 1000000000
|
||||
o = c.grow_by(self.address, v, 1, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
g = c.parse_grow_by(r)
|
||||
self.assertEqual(int(v * growth_factor), g)
|
||||
|
||||
period = 10
|
||||
growth_factor = (1 + (self.tax_level) / 1000000) ** period
|
||||
o = c.grow_by(self.address, v, period, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
g = c.parse_grow_by(r)
|
||||
self.assertEqual(int(v * growth_factor), g)
|
||||
# def test_grow_by(self):
|
||||
# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
#
|
||||
# growth_factor = (1000000 + self.tax_level) / 1000000
|
||||
# v = 1000000000
|
||||
# o = c.grow_by(self.address, v, 1, sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# g = c.parse_grow_by(r)
|
||||
# self.assertEqual(int(v * growth_factor), g)
|
||||
#
|
||||
# period = 10
|
||||
# growth_factor = (1 + (self.tax_level) / 1000000) ** period
|
||||
# o = c.grow_by(self.address, v, period, sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# g = c.parse_grow_by(r)
|
||||
# self.assertEqual(int(v * growth_factor), g)
|
||||
|
||||
|
||||
def test_decay_by(self):
|
||||
@@ -63,7 +63,7 @@ class TestGrowth(TestDemurrageDefault):
|
||||
o = c.decay_by(self.address, v, period, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
g = c.parse_decay_by(r)
|
||||
self.assertEqual(int(v * growth_factor), g)
|
||||
self.assertEqual(int(v * growth_factor), g)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -24,7 +24,7 @@ from erc20_demurrage_token import DemurrageToken
|
||||
# test imports
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageDefault
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logg = logging.getLogger()
|
||||
|
||||
testdir = os.path.dirname(__file__)
|
||||
@@ -88,18 +88,31 @@ class TestRedistribution(TestDemurrageDefault):
|
||||
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[0], supply)
|
||||
self.rpc.do(o)
|
||||
|
||||
self.backend.time_travel(self.start_time + (self.period_seconds * 10))
|
||||
self.backend.time_travel(self.start_time + (self.period_seconds * 100))
|
||||
|
||||
for i in range(1, 11):
|
||||
logg.debug('checking period {}'.format(i))
|
||||
balance_minter = None
|
||||
balance_sink = None
|
||||
real_supply = None
|
||||
|
||||
for i in range(1, 101):
|
||||
(tx_hash, o) = c.change_period(self.address, self.accounts[0])
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
i = 10
|
||||
o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance_sink = c.parse_balance(r)
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[0], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance_minter = c.parse_balance(r)
|
||||
|
||||
real_supply = balance_sink + balance_minter
|
||||
logg.info('period {} testing sink {} mint {} adds up to supply {} of original {} (delta {})'.format(i, balance_sink, balance_minter, real_supply, supply, supply - real_supply))
|
||||
|
||||
i = 100
|
||||
o = c.redistributions(self.address, i, sender_address=self.accounts[0])
|
||||
redistribution = self.rpc.do(o)
|
||||
|
||||
@@ -122,7 +135,7 @@ class TestRedistribution(TestDemurrageDefault):
|
||||
r = self.rpc.do(o)
|
||||
balance_minter = c.parse_balance(r)
|
||||
|
||||
logg.debug('testing sink {} mint {} adds up to supply {} with demurrage between {} and {}'.format(balance_sink, balance_minter, supply, demurrage_previous, demurrage))
|
||||
logg.debug('testing sink {} mint {} adds up to supply {} with demurrage between {} and {}'.format(balance_sink, balance_minter, real_supply, demurrage_previous, demurrage))
|
||||
|
||||
self.assert_within_lower(balance_minter + balance_sink, supply, 0.001)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pragma solidity > 0.6.11;
|
||||
pragma solidity >= 0.8.0;
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
contract DemurrageTokenSingleCap {
|
||||
@@ -11,7 +11,7 @@ contract DemurrageTokenSingleCap {
|
||||
uint8 constant shiftRedistributionValue = 32;
|
||||
uint256 constant maskRedistributionValue = 0x00000000000000000000000000000000000000ffffffffffffffffff00000000; // ((1 << 72) - 1) << 32
|
||||
uint8 constant shiftRedistributionDemurrage = 104;
|
||||
uint256 constant maskRedistributionDemurrage = 0x0000000000ffffffffffffffffffffffffffff00000000000000000000000000; // ((1 << 20) - 1) << 140
|
||||
uint256 constant maskRedistributionDemurrage = 0x0000000000ffffffffffffffffffffffffffff00000000000000000000000000; // ((1 << 36) - 1) << 140
|
||||
|
||||
// Account balances
|
||||
mapping (address => uint256) account;
|
||||
@@ -269,7 +269,7 @@ contract DemurrageTokenSingleCap {
|
||||
return uint128((block.timestamp - periodStart) / periodDuration + 1);
|
||||
}
|
||||
|
||||
// Add an entered demurrage period to the redistribution array
|
||||
// Retrieve next redistribution if the period threshold has been crossed
|
||||
function checkPeriod() private view returns (bytes32) {
|
||||
bytes32 lastRedistribution;
|
||||
uint256 currentPeriod;
|
||||
@@ -355,14 +355,15 @@ contract DemurrageTokenSingleCap {
|
||||
}
|
||||
|
||||
// Recalculate the demurrage modifier for the new period
|
||||
// Note that the supply for the consecutive period will be taken at the time of code execution, and thus not necessarily at the time when the redistribution period threshold was crossed.
|
||||
function changePeriod() public returns (bool) {
|
||||
bytes32 currentRedistribution;
|
||||
bytes32 nextRedistribution;
|
||||
uint256 currentPeriod;
|
||||
uint256 currentDemurrageAmount;
|
||||
uint256 lastDemurrageAmount;
|
||||
bytes32 lastRedistribution;
|
||||
uint256 nextRedistributionDemurrage;
|
||||
uint256 demurrageCounts;
|
||||
uint256 periodTimestamp;
|
||||
uint256 nextPeriod;
|
||||
|
||||
applyDemurrage();
|
||||
@@ -371,19 +372,14 @@ contract DemurrageTokenSingleCap {
|
||||
return false;
|
||||
}
|
||||
|
||||
// calculate the decay from previous redistributino
|
||||
lastRedistribution = redistributions[lastPeriod];
|
||||
currentPeriod = toRedistributionPeriod(currentRedistribution);
|
||||
nextPeriod = currentPeriod + 1;
|
||||
periodTimestamp = getPeriodTimeDelta(currentPeriod);
|
||||
|
||||
currentDemurrageAmount = demurrageAmount;
|
||||
|
||||
demurrageCounts = demurrageCycles(periodTimestamp);
|
||||
if (demurrageCounts > 0) {
|
||||
nextRedistributionDemurrage = growBy(currentDemurrageAmount, demurrageCounts);
|
||||
} else {
|
||||
nextRedistributionDemurrage = currentDemurrageAmount;
|
||||
}
|
||||
|
||||
lastDemurrageAmount = toRedistributionDemurrageModifier(lastRedistribution);
|
||||
demurrageCounts = periodDuration / 60;
|
||||
nextRedistributionDemurrage = decayBy(lastDemurrageAmount, demurrageCounts);
|
||||
|
||||
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply(), nextPeriod);
|
||||
redistributions.push(nextRedistribution);
|
||||
|
||||
@@ -429,14 +425,45 @@ contract DemurrageTokenSingleCap {
|
||||
function approve(address _spender, uint256 _value) public returns (bool) {
|
||||
uint256 baseValue;
|
||||
|
||||
if (allowance[msg.sender][_spender] > 0) {
|
||||
require(_value == 0, 'ZERO_FIRST');
|
||||
}
|
||||
|
||||
changePeriod();
|
||||
|
||||
baseValue = toBaseAmount(_value);
|
||||
allowance[msg.sender][_spender] += baseValue;
|
||||
allowance[msg.sender][_spender] = baseValue;
|
||||
emit Approval(msg.sender, _spender, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reduce allowance by amount
|
||||
function decreaseAllowance(address _spender, uint256 _value) public returns (bool) {
|
||||
uint256 baseValue;
|
||||
|
||||
baseValue = toBaseAmount(_value);
|
||||
require(allowance[msg.sender][_spender] >= baseValue);
|
||||
|
||||
changePeriod();
|
||||
|
||||
allowance[msg.sender][_spender] -= baseValue;
|
||||
emit Approval(msg.sender, _spender, allowance[msg.sender][_spender]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Increase allowance by amount
|
||||
function increaseAllowance(address _spender, uint256 _value) public returns (bool) {
|
||||
uint256 baseValue;
|
||||
|
||||
changePeriod();
|
||||
|
||||
baseValue = toBaseAmount(_value);
|
||||
|
||||
allowance[msg.sender][_spender] += baseValue;
|
||||
emit Approval(msg.sender, _spender, allowance[msg.sender][_spender]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implements ERC20, triggers tax and/or redistribution
|
||||
function transfer(address _to, uint256 _value) public returns (bool) {
|
||||
uint256 baseValue;
|
||||
@@ -460,7 +487,9 @@ contract DemurrageTokenSingleCap {
|
||||
baseValue = toBaseAmount(_value);
|
||||
require(allowance[_from][msg.sender] >= baseValue);
|
||||
|
||||
allowance[_from][msg.sender] -= baseValue;
|
||||
result = transferBase(_from, _to, baseValue);
|
||||
|
||||
emit Transfer(_from, _to, _value);
|
||||
return result;
|
||||
}
|
||||
@@ -493,6 +522,7 @@ contract DemurrageTokenSingleCap {
|
||||
}
|
||||
|
||||
// Explicitly and irretrievably burn tokens
|
||||
// Only token minters can burn tokens
|
||||
function burn(uint256 _value) public {
|
||||
require(minter[msg.sender]);
|
||||
require(_value <= account[msg.sender]);
|
||||
|
||||
@@ -32,7 +32,15 @@ test: all
|
||||
python ../python/tests/test_redistribution.py
|
||||
python ../python/tests/test_pure.py
|
||||
|
||||
install: all
|
||||
#install: all
|
||||
install: single_nocap
|
||||
#cp -v DemurrageToken*.{json,bin} ../python/erc20_demurrage_token/data/
|
||||
cp -v DemurrageTokenSingleNocap.json ../python/erc20_demurrage_token/data/
|
||||
cp -v DemurrageTokenSingleNocap.bin ../python/erc20_demurrage_token/data/
|
||||
|
||||
install-broken: all
|
||||
cp -v DemurrageToken*.{json,bin} ../python/erc20_demurrage_token/data/
|
||||
|
||||
|
||||
|
||||
.PHONY: test install
|
||||
|
||||
Reference in New Issue
Block a user