Compare commits
42 Commits
lash/sim-m
...
dev-0.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcc957f861
|
||
|
|
84b1a5b439
|
||
|
|
5941a6abdf | ||
|
|
c5de0e3300
|
||
|
|
cc1a84f818
|
||
|
|
ee871730dc
|
||
|
|
31faa78346
|
||
|
|
18ee9c5f9b
|
||
|
|
a0557b35a0
|
||
|
|
127c67e665
|
||
|
|
1387451e01
|
||
|
|
226f81fc5c | ||
|
|
370efb3192 | ||
|
|
3a1fb22631
|
||
|
|
f1a2a78eb4 | ||
|
|
47ee1cfa45 | ||
|
|
db56e0d33f | ||
|
|
d0c02eadbf | ||
|
|
ed60b5923b | ||
|
|
1e24ec1352
|
||
|
|
a04c826ba7
|
||
|
|
04f50cdede
|
||
| 21d65522a8 | |||
|
130b5ea587
|
|||
|
e486e9f31a
|
|||
|
959b018247
|
|||
|
6ecacd60d4
|
|||
|
c40157318f
|
|||
|
|
025ef614a5
|
||
|
|
43b3d2b488
|
||
|
|
0e1613c5f6
|
||
|
|
899efb65fc
|
||
|
|
f84edb5f3b
|
||
|
|
c6b5d9a8e0
|
||
|
|
abe82949ea
|
||
|
|
a6f53e7278
|
||
|
|
98c460dc2f
|
||
|
|
00bb87e3ec
|
||
|
|
294ded19f5
|
||
|
|
2c1b7cbb1e
|
||
|
|
d8f9fedecf
|
||
|
|
c3a6a692ed
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,3 +6,5 @@ __pycache__
|
||||
gmon.out
|
||||
solidity/*.json
|
||||
solidity/*.bin
|
||||
.venv
|
||||
venv
|
||||
36
.gitlab-ci.yml
Normal file
36
.gitlab-ci.yml
Normal 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
|
||||
|
||||
14
python/CHANGELOG
Normal file
14
python/CHANGELOG
Normal file
@@ -0,0 +1,14 @@
|
||||
- 0.1.1
|
||||
* Settable demurrage steps for apply demurrage cli tool
|
||||
- 0.1.0
|
||||
* Dependency upgrades
|
||||
- 0.0.11
|
||||
* Apply demurrage cli tool
|
||||
- 0.0.10
|
||||
* Settable sink address
|
||||
- 0.0.9
|
||||
* Correct redistribution amount for SingleNocap contract
|
||||
- 0.0.2
|
||||
* Move to chainlib-eth
|
||||
- 0.0.1
|
||||
* Interface for redistributed and non-redistributed, with or without cap
|
||||
@@ -1 +1 @@
|
||||
include erc20_demurrage_token/data/*
|
||||
include erc20_demurrage_token/data/* erc20_demurrage_token/data/config/*.ini *requirements.txt
|
||||
|
||||
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
67
python/erc20_demurrage_token/demurrage.py
Normal file
67
python/erc20_demurrage_token/demurrage.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import datetime
|
||||
import math
|
||||
|
||||
# eternal imports
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
|
||||
# local imports
|
||||
from .token import DemurrageToken
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DemurrageCalculator:
|
||||
|
||||
def __init__(self, interest_f_minute):
|
||||
|
||||
self.r_min = interest_f_minute
|
||||
self.r_hour = 1 - ((1 -self.r_min) ** 60)
|
||||
self.r_day = 1 - ((1 -self.r_hour) ** 24)
|
||||
#self.r_week = interest_f_day ** 7
|
||||
logg.info('demurrage calculator set with min {:.32f} hour {:.32f} day {:.32f}'.format(self.r_min, self.r_hour, self.r_day))
|
||||
|
||||
|
||||
def amount_since(self, amount, timestamp):
|
||||
delta = datetime.datetime.utcnow() - datetime.datetime.fromtimestamp(timestamp)
|
||||
adjusted_amount = amount * ((1 - self.r_day) ** (delta.days))
|
||||
logg.debug('adjusted for {} days {} -> {}'.format(delta.days, amount, adjusted_amount))
|
||||
|
||||
remainder = delta.seconds
|
||||
remainder_hours = math.floor(remainder / (60 * 60))
|
||||
adjusted_delta = adjusted_amount * ((1 - self.r_hour) ** remainder_hours)
|
||||
adjusted_amount -= (adjusted_amount - adjusted_delta)
|
||||
logg.debug('adjusted for {} hours {} -> {} delta {}'.format(remainder_hours, amount, adjusted_amount, adjusted_delta))
|
||||
|
||||
remainder -= (remainder_hours * (60 * 60))
|
||||
remainder_minutes = math.floor(remainder / 60)
|
||||
adjusted_delta = adjusted_amount * ((1 - self.r_min) ** remainder_minutes)
|
||||
adjusted_amount -= (adjusted_amount - adjusted_delta)
|
||||
logg.debug('adjusted for {} minutes {} -> {} delta {}'.format(remainder_minutes, amount, adjusted_amount, adjusted_delta))
|
||||
|
||||
return adjusted_amount
|
||||
|
||||
|
||||
def amount_since_slow(self, amount, timestamp):
|
||||
delta = datetime.datetime.utcnow() - datetime.datetime.fromtimestamp(timestamp)
|
||||
remainder_minutes = math.floor(delta.total_seconds() / 60)
|
||||
adjusted_amount = amount * ((1 - self.r_min) ** remainder_minutes)
|
||||
logg.debug('adjusted for {} minutes {} -> {} delta {}'.format(remainder_minutes, amount, adjusted_amount, amount - adjusted_amount))
|
||||
|
||||
return adjusted_amount
|
||||
|
||||
|
||||
@staticmethod
|
||||
def from_contract(rpc, chain_spec, contract_address, sender_address=ZERO_ADDRESS):
|
||||
c = DemurrageToken(chain_spec)
|
||||
o = c.tax_level(contract_address, sender_address=sender_address)
|
||||
r = rpc.do(o)
|
||||
taxlevel_i = c.parse_tax_level(r)
|
||||
|
||||
o = c.resolution_factor(contract_address, sender_address=sender_address)
|
||||
r = rpc.do(o)
|
||||
divider = c.parse_resolution_factor(r)
|
||||
logg.debug('taxlevel {} f {}'.format(taxlevel_i, divider))
|
||||
taxlevel_f = taxlevel_i / divider
|
||||
return DemurrageCalculator(taxlevel_f)
|
||||
144
python/erc20_demurrage_token/runnable/apply.py
Normal file
144
python/erc20_demurrage_token/runnable/apply.py
Normal file
@@ -0,0 +1,144 @@
|
||||
"""Deploy sarafu token
|
||||
|
||||
.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
|
||||
.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
|
||||
|
||||
"""
|
||||
|
||||
# standard imports
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import argparse
|
||||
import logging
|
||||
import datetime
|
||||
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,
|
||||
)
|
||||
from chainlib.eth.block import (
|
||||
block_latest,
|
||||
block_by_number,
|
||||
Block,
|
||||
)
|
||||
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
|
||||
|
||||
# local imports
|
||||
import erc20_demurrage_token
|
||||
from erc20_demurrage_token import (
|
||||
DemurrageToken,
|
||||
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')
|
||||
|
||||
arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC
|
||||
argparser = chainlib.eth.cli.ArgumentParser(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.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'))
|
||||
|
||||
|
||||
def main():
|
||||
o = block_latest()
|
||||
r = conn.do(o)
|
||||
|
||||
block_start_number = None
|
||||
try:
|
||||
block_start_number = hex_to_int(r)
|
||||
except TypeError:
|
||||
block_start_number = int(r)
|
||||
|
||||
o = block_by_number(block_start_number)
|
||||
r = conn.do(o)
|
||||
|
||||
block_start = Block(r)
|
||||
block_start_timestamp = block_start.timestamp
|
||||
block_start_datetime = datetime.datetime.fromtimestamp(block_start_timestamp)
|
||||
|
||||
gas_oracle = rpc.get_gas_oracle()
|
||||
c = DemurrageToken(chain_spec, gas_oracle=gas_oracle)
|
||||
o = c.demurrage_timestamp(config.get('_EXEC_ADDRESS'))
|
||||
r = conn.do(o)
|
||||
|
||||
demurrage_timestamp = None
|
||||
try:
|
||||
demurrage_timestamp = hex_to_int(r)
|
||||
except TypeError:
|
||||
demurrage_timestamp = int(r)
|
||||
demurrage_datetime = datetime.datetime.fromtimestamp(demurrage_timestamp)
|
||||
|
||||
total_seconds = block_start_timestamp - demurrage_timestamp
|
||||
total_steps = total_seconds / 60
|
||||
|
||||
if total_steps < 1.0:
|
||||
logg.error('only {} seconds since last demurrage application, skipping'.format(total_seconds))
|
||||
return
|
||||
|
||||
logg.debug('block start is at {} demurrage is at {} -> {} minutes'.format(
|
||||
block_start_datetime,
|
||||
demurrage_datetime,
|
||||
total_steps,
|
||||
))
|
||||
|
||||
rounds = 1
|
||||
if config.get('_STEPS') > 0:
|
||||
rounds = math.ceil(total_steps / config.get('_STEPS'))
|
||||
|
||||
logg.info('will perform {} rounds of {} steps'.format(rounds, config.get('_STEPS')))
|
||||
|
||||
last_tx_hash = None
|
||||
for i in range(rounds):
|
||||
signer = rpc.get_signer()
|
||||
signer_address = rpc.get_sender_address()
|
||||
|
||||
nonce_oracle = rpc.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'):
|
||||
print(tx_hash_hex)
|
||||
conn.do(o)
|
||||
if config.get('_WAIT_ALL') or (i == rounds - 1 and config.get('_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')
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(o)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -14,8 +14,8 @@ import logging
|
||||
|
||||
# external imports
|
||||
import confini
|
||||
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
|
||||
from crypto_dev_signer.keystore.dict import DictKeystore
|
||||
from funga.eth.signer import EIP155Signer
|
||||
from funga.eth.keystore.dict import DictKeystore
|
||||
from chainlib.chain import ChainSpec
|
||||
from chainlib.eth.nonce import (
|
||||
RPCNonceOracle,
|
||||
@@ -28,8 +28,10 @@ from chainlib.eth.gas import (
|
||||
from chainlib.eth.connection import EthHTTPConnection
|
||||
from chainlib.eth.tx import receipt
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
import chainlib.eth.cli
|
||||
|
||||
# local imports
|
||||
import erc20_demurrage_token
|
||||
from erc20_demurrage_token import (
|
||||
DemurrageToken,
|
||||
DemurrageTokenSettings,
|
||||
@@ -41,56 +43,37 @@ logg = logging.getLogger()
|
||||
script_dir = os.path.dirname(__file__)
|
||||
data_dir = os.path.join(script_dir, '..', 'data')
|
||||
|
||||
default_config_dir = os.environ.get('CONFINI_DIR', '/usr/local/share/sarafu-token')
|
||||
config_dir = os.path.join(data_dir, 'config')
|
||||
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument('-c', '--config', dest='c', type=str, default=default_config_dir, help='configuration directory')
|
||||
argparser.add_argument('-p', '--provider', dest='p', default='http://localhost:8545', type=str, help='Web3 provider url (http only)')
|
||||
argparser.add_argument('-w', action='store_true', help='Wait for the last transaction to be confirmed')
|
||||
argparser.add_argument('-ww', action='store_true', help='Wait for every transaction to be confirmed')
|
||||
argparser.add_argument('-i', '--chain-spec', dest='i', type=str, default='evm:ethereum:1', help='Chain specification string')
|
||||
argparser.add_argument('-y', '--key-file', dest='y', type=str, help='Ethereum keystore file to use for signing')
|
||||
argparser.add_argument('-v', action='store_true', help='Be verbose')
|
||||
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
|
||||
argparser.add_argument('-d', action='store_true', help='Dump RPC calls to terminal and do not send')
|
||||
argparser.add_argument('--name', type=str, help='Token name')
|
||||
argparser.add_argument('--decimals', default=6, type=int, help='Token decimals')
|
||||
argparser.add_argument('--gas-price', type=int, dest='gas_price', help='Override gas price')
|
||||
argparser.add_argument('--nonce', type=int, help='Override transaction nonce')
|
||||
argparser.add_argument('--sink-address', dest='sink_address', default=ZERO_ADDRESS, type=str, help='demurrage level,ppm per minute')
|
||||
arg_flags = chainlib.eth.cli.argflag_std_write
|
||||
argparser = chainlib.eth.cli.ArgumentParser(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('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration')
|
||||
argparser.add_argument('--symbol', type=str, help='Token symbol')
|
||||
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()
|
||||
|
||||
if args.vv:
|
||||
logg.setLevel(logging.DEBUG)
|
||||
elif args.v:
|
||||
logg.setLevel(logging.INFO)
|
||||
arg_flags = chainlib.eth.cli.argflag_std_write
|
||||
|
||||
block_last = args.w
|
||||
block_all = args.ww
|
||||
|
||||
# process config
|
||||
config = confini.Config(args.c)
|
||||
config.process()
|
||||
args_override = {
|
||||
'TOKEN_REDISTRIBUTION_PERIOD': getattr(args, 'redistribution_period'),
|
||||
'TOKEN_DEMURRAGE_LEVEL': getattr(args, 'demurrage_level'),
|
||||
'TOKEN_SUPPLY_LIMIT': getattr(args, 'supply_limit'),
|
||||
'TOKEN_SYMBOL': getattr(args, 'symbol'),
|
||||
'TOKEN_NAME': getattr(args, 'name'),
|
||||
'TOKEN_DECIMALS': getattr(args, 'decimals'),
|
||||
'TOKEN_SINK_ADDRESS': getattr(args, 'sink_address'),
|
||||
'SESSION_CHAIN_SPEC': getattr(args, 'i'),
|
||||
'ETH_PROVIDER': getattr(args, 'p'),
|
||||
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,
|
||||
}
|
||||
if config.get('TOKEN_NAME') == 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)
|
||||
config.dict_override(args_override, 'cli args')
|
||||
|
||||
if config.get('TOKEN_SUPPLY_LIMIT') == None:
|
||||
config.add(0, 'TOKEN_SUPPLY_LIMIT', True)
|
||||
@@ -99,44 +82,21 @@ if config.get('TOKEN_REDISTRIBUTION_PERIOD') == None:
|
||||
config.add(10800, 'TOKEN_REDISTRIBUTION_PERIOD', True)
|
||||
logg.debug('config loaded:\n{}'.format(config))
|
||||
|
||||
passphrase_env = 'ETH_PASSPHRASE'
|
||||
if args.env_prefix != None:
|
||||
passphrase_env = args.env_prefix + '_' + passphrase_env
|
||||
passphrase = os.environ.get(passphrase_env)
|
||||
if passphrase == None:
|
||||
logg.warning('no passphrase given')
|
||||
passphrase=''
|
||||
wallet = chainlib.eth.cli.Wallet()
|
||||
wallet.from_config(config)
|
||||
|
||||
signer_address = None
|
||||
keystore = DictKeystore()
|
||||
if args.y != None:
|
||||
logg.debug('loading keystore file {}'.format(args.y))
|
||||
signer_address = keystore.import_keystore_file(args.y, password=passphrase)
|
||||
logg.debug('now have key for signer address {}'.format(signer_address))
|
||||
signer = EIP155Signer(keystore)
|
||||
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
|
||||
conn = rpc.connect_by_config(config)
|
||||
|
||||
chain_spec = ChainSpec.from_chain_str(args.i)
|
||||
|
||||
rpc = EthHTTPConnection(args.p)
|
||||
nonce_oracle = None
|
||||
if args.nonce != None:
|
||||
nonce_oracle = OverrideNonceOracle(signer_address, args.nonce)
|
||||
else:
|
||||
nonce_oracle = RPCNonceOracle(signer_address, rpc)
|
||||
|
||||
gas_oracle = None
|
||||
if args.gas_price !=None:
|
||||
gas_oracle = OverrideGasOracle(price=args.gas_price, conn=rpc, code_callback=DemurrageToken.gas)
|
||||
else:
|
||||
gas_oracle = RPCGasOracle(rpc, code_callback=DemurrageToken.gas)
|
||||
|
||||
dummy = args.d
|
||||
|
||||
token_name = args.name
|
||||
if token_name == None:
|
||||
token_name = args.symbol
|
||||
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
|
||||
|
||||
def main():
|
||||
signer = rpc.get_signer()
|
||||
signer_address = rpc.get_sender_address()
|
||||
|
||||
gas_oracle = rpc.get_gas_oracle()
|
||||
nonce_oracle = rpc.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')
|
||||
@@ -149,16 +109,13 @@ def main():
|
||||
(tx_hash_hex, o) = c.constructor(
|
||||
signer_address,
|
||||
settings,
|
||||
redistribute=settings.period_minutes > 0,
|
||||
redistribute=config.true('_MULTI'),
|
||||
cap=int(config.get('TOKEN_SUPPLY_LIMIT')),
|
||||
)
|
||||
if dummy:
|
||||
print(tx_hash_hex)
|
||||
print(o)
|
||||
else:
|
||||
rpc.do(o)
|
||||
if block_last:
|
||||
r = rpc.wait(tx_hash_hex)
|
||||
if config.get('_RPC_SEND'):
|
||||
conn.do(o)
|
||||
if config.get('_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')
|
||||
sys.exit(1)
|
||||
@@ -169,6 +126,9 @@ def main():
|
||||
else:
|
||||
print(tx_hash_hex)
|
||||
|
||||
else:
|
||||
print(o)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -20,8 +20,8 @@ from chainlib.eth.block import (
|
||||
block_by_number,
|
||||
block_by_hash,
|
||||
)
|
||||
from crypto_dev_signer.keystore.dict import DictKeystore
|
||||
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
|
||||
from funga.eth.keystore.dict import DictKeystore
|
||||
from funga.eth.signer import EIP155Signer
|
||||
from hexathon import (
|
||||
strip_0x,
|
||||
add_0x,
|
||||
@@ -200,7 +200,7 @@ class DemurrageTokenSimulation:
|
||||
else:
|
||||
o = self.caller_contract.balance_of(self.address, holder, sender_address=self.caller_address)
|
||||
r = self.rpc.do(o)
|
||||
return self.caller_contract.parse_balance_of(r)
|
||||
return self.caller_contract.parse_balance(r)
|
||||
|
||||
|
||||
def __next_block(self):
|
||||
|
||||
@@ -10,11 +10,12 @@ from chainlib.eth.tx import (
|
||||
from chainlib.hash import keccak256_string_to_hex
|
||||
from chainlib.eth.contract import (
|
||||
ABIContractEncoder,
|
||||
ABIContractDecoder,
|
||||
ABIContractType,
|
||||
abi_decode_single,
|
||||
)
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
from chainlib.jsonrpc import jsonrpc_template
|
||||
from chainlib.jsonrpc import JSONRPCRequest
|
||||
from eth_erc20 import ERC20
|
||||
from hexathon import (
|
||||
add_0x,
|
||||
@@ -38,6 +39,15 @@ class DemurrageTokenSettings:
|
||||
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):
|
||||
|
||||
__abi = {}
|
||||
@@ -70,7 +80,7 @@ class DemurrageToken(ERC20):
|
||||
|
||||
@staticmethod
|
||||
def gas(code=None):
|
||||
return 3500000
|
||||
return 4000000
|
||||
|
||||
|
||||
@staticmethod
|
||||
@@ -108,6 +118,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')
|
||||
@@ -146,8 +184,36 @@ class DemurrageToken(ERC20):
|
||||
return tx
|
||||
|
||||
|
||||
def to_base_amount(self, contract_address, value, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def burn(self, contract_address, sender_address, value, tx_format=TxFormat.JSONRPC):
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('burn')
|
||||
enc.typ(ABIContractType.UINT256)
|
||||
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 total_burned(self, contract_address, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('totalBurned')
|
||||
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 to_base_amount(self, contract_address, value, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('toBaseAmount')
|
||||
@@ -158,11 +224,13 @@ class DemurrageToken(ERC20):
|
||||
tx = self.set_code(tx, data)
|
||||
o['params'].append(self.normalize(tx))
|
||||
o['params'].append('latest')
|
||||
o = j.finalize(o)
|
||||
return o
|
||||
|
||||
|
||||
def remainder(self, contract_address, parts, whole, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def remainder(self, contract_address, parts, whole, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('remainder')
|
||||
@@ -175,11 +243,13 @@ class DemurrageToken(ERC20):
|
||||
tx = self.set_code(tx, data)
|
||||
o['params'].append(self.normalize(tx))
|
||||
o['params'].append('latest')
|
||||
o = j.finalize(o)
|
||||
return o
|
||||
|
||||
|
||||
def redistributions(self, contract_address, idx, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def redistributions(self, contract_address, idx, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('redistributions')
|
||||
@@ -190,11 +260,13 @@ class DemurrageToken(ERC20):
|
||||
tx = self.set_code(tx, data)
|
||||
o['params'].append(self.normalize(tx))
|
||||
o['params'].append('latest')
|
||||
o = j.finalize(o)
|
||||
return o
|
||||
|
||||
|
||||
def account_period(self, contract_address, address, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def account_period(self, contract_address, address, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('accountPeriod')
|
||||
@@ -205,11 +277,13 @@ class DemurrageToken(ERC20):
|
||||
tx = self.set_code(tx, data)
|
||||
o['params'].append(self.normalize(tx))
|
||||
o['params'].append('latest')
|
||||
o = j.finalize(o)
|
||||
return o
|
||||
|
||||
|
||||
def to_redistribution(self, contract_address, participants, demurrage_modifier_ppm, value, period, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def to_redistribution(self, contract_address, participants, demurrage_modifier_ppm, value, period, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('toRedistribution')
|
||||
@@ -226,72 +300,95 @@ class DemurrageToken(ERC20):
|
||||
tx = self.set_code(tx, data)
|
||||
o['params'].append(self.normalize(tx))
|
||||
o['params'].append('latest')
|
||||
o = j.finalize(o)
|
||||
return o
|
||||
|
||||
|
||||
|
||||
def to_redistribution_period(self, contract_address, redistribution, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def to_redistribution_period(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('toRedistributionPeriod')
|
||||
enc.typ(ABIContractType.BYTES32)
|
||||
enc.bytes32(redistribution)
|
||||
v = strip_0x(redistribution)
|
||||
enc.typ_literal('(uint32,uint72,uint104)')
|
||||
enc.bytes32(v[:64])
|
||||
enc.bytes32(v[64:128])
|
||||
enc.bytes32(v[128:192])
|
||||
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 to_redistribution_participants(self, contract_address, redistribution, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('toRedistributionParticipants')
|
||||
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')
|
||||
return o
|
||||
# def to_redistribution_participants(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
# j = JSONRPCRequest(id_generator)
|
||||
# o = j.template()
|
||||
# o['method'] = 'eth_call'
|
||||
# enc = ABIContractEncoder()
|
||||
# enc.method('toRedistributionParticipants')
|
||||
# v = strip_0x(redistribution)
|
||||
# enc.typ_literal('(uint32,uint72,uint104)')
|
||||
# #enc.typ(ABIContractType.BYTES32)
|
||||
# enc.bytes32(v[:64])
|
||||
# enc.bytes32(v[64:128])
|
||||
# enc.bytes32(v[128:192])
|
||||
# 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 to_redistribution_supply(self, contract_address, redistribution, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def to_redistribution_supply(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('toRedistributionSupply')
|
||||
enc.typ(ABIContractType.BYTES32)
|
||||
enc.bytes32(redistribution)
|
||||
v = strip_0x(redistribution)
|
||||
enc.typ_literal('(uint32,uint72,uint104)')
|
||||
enc.bytes32(v[:64])
|
||||
enc.bytes32(v[64:128])
|
||||
enc.bytes32(v[128:192])
|
||||
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 to_redistribution_demurrage_modifier(self, contract_address, redistribution, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def to_redistribution_demurrage_modifier(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('toRedistributionDemurrageModifier')
|
||||
enc.typ(ABIContractType.BYTES32)
|
||||
enc.bytes32(redistribution)
|
||||
v = strip_0x(redistribution)
|
||||
enc.typ_literal('(uint32,uint72,uint104)')
|
||||
enc.bytes32(v[:64])
|
||||
enc.bytes32(v[64:128])
|
||||
enc.bytes32(v[128:192])
|
||||
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 base_balance_of(self, contract_address, address, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def base_balance_of(self, contract_address, address, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('baseBalanceOf')
|
||||
@@ -302,11 +399,23 @@ class DemurrageToken(ERC20):
|
||||
tx = self.set_code(tx, data)
|
||||
o['params'].append(self.normalize(tx))
|
||||
o['params'].append('latest')
|
||||
o = j.finalize(o)
|
||||
return o
|
||||
|
||||
|
||||
def apply_demurrage(self, contract_address, sender_address):
|
||||
return self.transact_noarg('applyDemurrage', 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)
|
||||
|
||||
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):
|
||||
@@ -323,7 +432,15 @@ class DemurrageToken(ERC20):
|
||||
tx = self.set_code(tx, data)
|
||||
tx = self.finalize(tx, tx_format)
|
||||
return tx
|
||||
|
||||
|
||||
|
||||
def tax_level(self, contract_address, sender_address=ZERO_ADDRESS):
|
||||
return self.call_noarg('taxLevel', contract_address, sender_address=sender_address)
|
||||
|
||||
|
||||
def resolution_factor(self, contract_address, sender_address=ZERO_ADDRESS):
|
||||
return self.call_noarg('resolutionFactor', contract_address, sender_address=sender_address)
|
||||
|
||||
|
||||
def actual_period(self, contract_address, sender_address=ZERO_ADDRESS):
|
||||
return self.call_noarg('actualPeriod', contract_address, sender_address=sender_address)
|
||||
@@ -341,29 +458,36 @@ class DemurrageToken(ERC20):
|
||||
return self.call_noarg('demurrageAmount', contract_address, sender_address=sender_address)
|
||||
|
||||
|
||||
def demurrage_timestamp(self, contract_address, sender_address=ZERO_ADDRESS):
|
||||
return self.call_noarg('demurrageTimestamp', contract_address, sender_address=sender_address)
|
||||
|
||||
|
||||
def supply_cap(self, contract_address, sender_address=ZERO_ADDRESS):
|
||||
return self.call_noarg('supplyCap', contract_address, sender_address=sender_address)
|
||||
|
||||
|
||||
def grow_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('growBy')
|
||||
enc.typ(ABIContractType.UINT256)
|
||||
enc.typ(ABIContractType.UINT256)
|
||||
enc.uint256(value)
|
||||
enc.uint256(period)
|
||||
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')
|
||||
return o
|
||||
# def grow_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
# j = JSONRPCRequest(id_generator)
|
||||
# o = j.template()
|
||||
# o['method'] = 'eth_call'
|
||||
# enc = ABIContractEncoder()
|
||||
# enc.method('growBy')
|
||||
# enc.typ(ABIContractType.UINT256)
|
||||
# enc.typ(ABIContractType.UINT256)
|
||||
# enc.uint256(value)
|
||||
# enc.uint256(period)
|
||||
# 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 decay_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def decay_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('decayBy')
|
||||
@@ -376,11 +500,13 @@ class DemurrageToken(ERC20):
|
||||
tx = self.set_code(tx, data)
|
||||
o['params'].append(self.normalize(tx))
|
||||
o['params'].append('latest')
|
||||
o = j.finalize(o)
|
||||
return o
|
||||
|
||||
|
||||
def get_distribution(self, contract_address, supply, demurrage_amount, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def get_distribution(self, contract_address, supply, demurrage_amount, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('getDistribution')
|
||||
@@ -393,21 +519,27 @@ class DemurrageToken(ERC20):
|
||||
tx = self.set_code(tx, data)
|
||||
o['params'].append(self.normalize(tx))
|
||||
o['params'].append('latest')
|
||||
o = j.finalize(o)
|
||||
return o
|
||||
|
||||
|
||||
def get_distribution_from_redistribution(self, contract_address, redistribution, sender_address=ZERO_ADDRESS):
|
||||
o = jsonrpc_template()
|
||||
def get_distribution_from_redistribution(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('getDistributionFromRedistribution')
|
||||
enc.typ(ABIContractType.BYTES32)
|
||||
enc.bytes32(redistribution)
|
||||
v = strip_0x(redistribution)
|
||||
enc.typ_literal('(uint32,uint72,uint104)')
|
||||
enc.bytes32(v[:64])
|
||||
enc.bytes32(v[64:128])
|
||||
enc.bytes32(v[128:192])
|
||||
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
|
||||
|
||||
|
||||
@@ -444,7 +576,16 @@ class DemurrageToken(ERC20):
|
||||
|
||||
@classmethod
|
||||
def parse_redistributions(self, v):
|
||||
return abi_decode_single(ABIContractType.BYTES32, v)
|
||||
d = ABIContractDecoder()
|
||||
v = strip_0x(v)
|
||||
d.typ(ABIContractType.BYTES32)
|
||||
d.typ(ABIContractType.BYTES32)
|
||||
d.typ(ABIContractType.BYTES32)
|
||||
d.val(v[:64])
|
||||
d.val(v[64:128])
|
||||
d.val(v[128:192])
|
||||
r = d.decode()
|
||||
return ''.join(r)
|
||||
|
||||
|
||||
@classmethod
|
||||
@@ -466,6 +607,7 @@ class DemurrageToken(ERC20):
|
||||
def parse_supply_cap(self, v):
|
||||
return abi_decode_single(ABIContractType.UINT256, v)
|
||||
|
||||
|
||||
@classmethod
|
||||
def parse_grow_by(self, v):
|
||||
return abi_decode_single(ABIContractType.UINT256, v)
|
||||
@@ -479,3 +621,18 @@ class DemurrageToken(ERC20):
|
||||
@classmethod
|
||||
def parse_get_distribution(self, v):
|
||||
return abi_decode_single(ABIContractType.UINT256, v)
|
||||
|
||||
|
||||
@classmethod
|
||||
def parse_tax_level(self, v):
|
||||
return abi_decode_single(ABIContractType.UINT256, v)
|
||||
|
||||
|
||||
@classmethod
|
||||
def parse_resolution_factor(self, v):
|
||||
return abi_decode_single(ABIContractType.UINT256, v)
|
||||
|
||||
|
||||
@classmethod
|
||||
def parse_total_burned(self, v):
|
||||
return abi_decode_single(ABIContractType.UINT256, v)
|
||||
|
||||
1
python/erc20_demurrage_token/unittest/__init__.py
Normal file
1
python/erc20_demurrage_token/unittest/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .base import *
|
||||
@@ -12,6 +12,7 @@ from chainlib.eth.block import (
|
||||
block_by_number,
|
||||
)
|
||||
from chainlib.eth.nonce import RPCNonceOracle
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
|
||||
# local imports
|
||||
from erc20_demurrage_token import (
|
||||
@@ -29,64 +30,99 @@ TAX_LEVEL = int(10000 * 2) # 2%
|
||||
PERIOD = 10
|
||||
|
||||
|
||||
class TestDemurrage(EthTesterCase):
|
||||
class TestTokenDeploy:
|
||||
|
||||
def setUp(self):
|
||||
super(TestDemurrage, self).setUp()
|
||||
def __init__(self, rpc, token_symbol='FOO', token_name='Foo Token', sink_address=ZERO_ADDRESS, supply=10**12, tax_level=TAX_LEVEL, period=PERIOD):
|
||||
self.tax_level = tax_level
|
||||
self.period_seconds = period * 60
|
||||
|
||||
self.tax_level = TAX_LEVEL
|
||||
self.period_seconds = PERIOD * 60
|
||||
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
self.settings = DemurrageTokenSettings()
|
||||
self.settings.name = 'Foo Token'
|
||||
self.settings.symbol = 'FOO'
|
||||
self.settings.name = token_name
|
||||
self.settings.symbol = token_symbol
|
||||
self.settings.decimals = 6
|
||||
self.settings.demurrage_level = TAX_LEVEL * (10 ** 32)
|
||||
self.settings.period_minutes = PERIOD
|
||||
self.settings.sink_address = self.accounts[9]
|
||||
self.settings.demurrage_level = tax_level * (10 ** 32)
|
||||
self.settings.period_minutes = period
|
||||
self.settings.sink_address = sink_address
|
||||
self.sink_address = self.settings.sink_address
|
||||
logg.debug('using demurrage token settings: {}'.format(self.settings))
|
||||
|
||||
o = block_latest()
|
||||
self.start_block = self.rpc.do(o)
|
||||
self.start_block = rpc.do(o)
|
||||
|
||||
o = block_by_number(self.start_block, include_tx=False)
|
||||
r = self.rpc.do(o)
|
||||
r = rpc.do(o)
|
||||
|
||||
try:
|
||||
self.start_time = int(r['timestamp'], 16)
|
||||
except TypeError:
|
||||
self.start_time = int(r['timestamp'])
|
||||
|
||||
self.default_supply = 10 ** 12
|
||||
self.default_supply = supply
|
||||
self.default_supply_cap = int(self.default_supply * 10)
|
||||
|
||||
|
||||
def deploy(self, interface, mode):
|
||||
def deploy(self, rpc, deployer_address, interface, mode, supply_cap=10**12):
|
||||
tx_hash = None
|
||||
o = None
|
||||
logg.debug('mode {} {}'.format(mode, self.settings))
|
||||
self.mode = mode
|
||||
if mode == 'MultiNocap':
|
||||
(tx_hash, o) = interface.constructor(self.accounts[0], self.settings, redistribute=True, cap=0)
|
||||
(tx_hash, o) = interface.constructor(deployer_address, self.settings, redistribute=True, cap=0)
|
||||
elif mode == 'SingleNocap':
|
||||
(tx_hash, o) = interface.constructor(self.accounts[0], self.settings, redistribute=False, cap=0)
|
||||
(tx_hash, o) = interface.constructor(deployer_address, self.settings, redistribute=False, cap=0)
|
||||
elif mode == 'MultiCap':
|
||||
(tx_hash, o) = interface.constructor(self.accounts[0], self.settings, redistribute=True, cap=self.default_supply_cap)
|
||||
(tx_hash, o) = interface.constructor(deployer_address, self.settings, redistribute=True, cap=supply_cap)
|
||||
elif mode == 'SingleCap':
|
||||
(tx_hash, o) = interface.constructor(self.accounts[0], self.settings, redistribute=False, cap=self.default_supply_cap)
|
||||
(tx_hash, o) = interface.constructor(deployer_address, self.settings, redistribute=False, cap=supply_cap)
|
||||
else:
|
||||
raise ValueError('Invalid mode "{}", valid are {}'.format(self.mode, DemurrageToken.valid_modes))
|
||||
raise ValueError('Invalid mode "{}", valid are {}'.format(mode, DemurrageToken.valid_modes))
|
||||
|
||||
r = self.rpc.do(o)
|
||||
r = rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
r = rpc.do(o)
|
||||
assert r['status'] == 1
|
||||
self.start_block = r['block_number']
|
||||
self.address = r['contract_address']
|
||||
|
||||
o = block_by_number(r['block_number'])
|
||||
r = self.rpc.do(o)
|
||||
r = rpc.do(o)
|
||||
self.start_time = r['timestamp']
|
||||
|
||||
return self.address
|
||||
|
||||
|
||||
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')
|
||||
except AttributeError as e:
|
||||
pass
|
||||
self.deployer = TestTokenDeploy(self.rpc, period=period)
|
||||
self.default_supply = self.deployer.default_supply
|
||||
self.default_supply_cap = self.deployer.default_supply_cap
|
||||
self.start_block = None
|
||||
self.address = None
|
||||
self.start_time = None
|
||||
|
||||
|
||||
def deploy(self, interface, mode):
|
||||
self.address = self.deployer.deploy(self.rpc, self.accounts[0], interface, mode, supply_cap=self.default_supply_cap)
|
||||
self.start_block = self.deployer.start_block
|
||||
self.start_time = self.deployer.start_time
|
||||
self.tax_level = self.deployer.tax_level
|
||||
self.period_seconds = self.deployer.period_seconds
|
||||
self.sink_address = self.deployer.sink_address
|
||||
|
||||
logg.debug('contract address {} start block {} start time {}'.format(self.address, self.start_block, self.start_time))
|
||||
|
||||
|
||||
@@ -173,10 +209,11 @@ class TestDemurrageCap(TestDemurrage):
|
||||
class TestDemurrageUnit(TestDemurrage):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDemurrage, self).setUp()
|
||||
self.period = 1
|
||||
self.period_seconds = self.period * 60
|
||||
self.tax_level = TAX_LEVEL
|
||||
|
||||
self.tax_level = 50
|
||||
self.period_seconds = 60
|
||||
super(TestDemurrageUnit, self).setUp()
|
||||
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
self.settings = DemurrageTokenSettings()
|
||||
@@ -17,9 +17,10 @@ settings.symbol = 'SIM'
|
||||
settings.decimals = 6
|
||||
settings.demurrage_level = int(decay_per_minute*(10**38))
|
||||
#settings.period_minutes = 1 # 1 week in minutes
|
||||
settings.period_minutes = 10
|
||||
settings.period_minutes = 60*24*7
|
||||
chain = 'evm:foochain:42'
|
||||
cap = (10 ** 6) * (10 ** 12)
|
||||
#cap = 0
|
||||
|
||||
# instantiate simulation
|
||||
sim = DemurrageTokenSimulation(chain, settings, redistribute=False, cap=cap, actors=10)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
chainlib~=0.0.3rc3
|
||||
eth-erc20~=0.0.9a4
|
||||
crypto-dev-signer~=0.4.14b3
|
||||
chainlib-eth>=0.1.0,<0.2.0
|
||||
eth-erc20~=0.3.0
|
||||
funga-eth~=0.6.0
|
||||
|
||||
@@ -1,29 +1,38 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
set -e
|
||||
|
||||
export PYTHONPATH=.
|
||||
|
||||
#modes=(MultiNocap MultiCap SingleCap SingleNocap)
|
||||
modes=(SingleCap SingleNocap) # other contracts need to be updted
|
||||
#modes=(SingleCap SingleNocap) # other contracts need to be updted
|
||||
modes=(SingleNocap) # other contracts need to be updted
|
||||
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_basic.py
|
||||
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
|
||||
done
|
||||
|
||||
modes=(MultiCap SingleCap)
|
||||
#modes=(SingleCap) # other contracts need to be updted
|
||||
modes=()
|
||||
for m in ${modes[@]}; do
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_period.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_single.py
|
||||
done
|
||||
|
||||
#modes=(MultiCap SingleCap)
|
||||
modes=()
|
||||
for m in ${modes[@]}; do
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_cap.py
|
||||
done
|
||||
|
||||
modes=(SingleCap SingleNocap)
|
||||
for m in ${modes[@]}; do
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_single.py
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution_unit.py
|
||||
done
|
||||
|
||||
#modes=(MultiCap MultiNocap)
|
||||
#for m in ${modes[@]}; do
|
||||
# ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_remainder.py
|
||||
@@ -31,3 +40,4 @@ done
|
||||
#done
|
||||
|
||||
set +e
|
||||
set +x
|
||||
@@ -1,10 +1,10 @@
|
||||
[metadata]
|
||||
name = erc20-demurrage-token
|
||||
version = 0.0.1b3
|
||||
version = 0.1.1
|
||||
description = ERC20 token with redistributed continual demurrage
|
||||
author = Louis Holbrook
|
||||
author_email = dev@holbrook.no
|
||||
url = https://gitlab.com/grassrootseconomics/sarafu-token
|
||||
url = https://gitlab.com/ccicnet/erc20-demurrage-token
|
||||
keywords =
|
||||
ethereum
|
||||
blockchain
|
||||
@@ -25,12 +25,13 @@ licence_files =
|
||||
|
||||
[options]
|
||||
include_package_data = True
|
||||
python_requires = >= 3.6
|
||||
python_requires = >= 3.7
|
||||
packages =
|
||||
erc20_demurrage_token
|
||||
erc20_demurrage_token.runnable
|
||||
erc20_demurrage_token.data
|
||||
erc20_demurrage_token.sim
|
||||
erc20_demurrage_token.unittest
|
||||
|
||||
[options.package_data]
|
||||
* =
|
||||
@@ -40,3 +41,4 @@ packages =
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
erc20-demurrage-token-deploy = erc20_demurrage_token.runnable.deploy:main
|
||||
erc20-demurrage-token-apply = erc20_demurrage_token.runnable.apply:main
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
#web3==5.12.2
|
||||
eth_tester==0.5.0b3
|
||||
py-evm==0.3.0a20
|
||||
|
||||
@@ -13,7 +13,7 @@ from chainlib.eth.tx import receipt
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
|
||||
# test imports
|
||||
from tests.base import TestDemurrageDefault
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageDefault
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
@@ -35,7 +35,7 @@ class TestAmounts(TestDemurrageDefault):
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
self.assertEqual(balance, 817)
|
||||
|
||||
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1000)
|
||||
@@ -43,7 +43,7 @@ class TestAmounts(TestDemurrageDefault):
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
self.assert_within_lower(balance, 1817, 750)
|
||||
|
||||
self.backend.time_travel(self.start_time + self.period_seconds * 2)
|
||||
@@ -53,7 +53,7 @@ class TestAmounts(TestDemurrageDefault):
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
|
||||
expected_balance = ((1 - self.tax_level / 1000000) ** 10) * 1000
|
||||
expected_balance += ((1 - self.tax_level / 1000000) ** 20) * 1000
|
||||
@@ -73,7 +73,7 @@ class TestAmounts(TestDemurrageDefault):
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
self.assertEqual(balance, 1634)
|
||||
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
|
||||
@@ -83,12 +83,12 @@ class TestAmounts(TestDemurrageDefault):
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
self.assertEqual(balance, 1134)
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[2], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
self.assert_within_lower(balance, 500, 2000)
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ class TestAmounts(TestDemurrageDefault):
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
self.assertEqual(balance, case[1])
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ from chainlib.eth.block import (
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
|
||||
# test imports
|
||||
from tests.base import TestDemurrageDefault
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageDefault
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
@@ -28,179 +28,205 @@ testdir = os.path.dirname(__file__)
|
||||
|
||||
class TestBasic(TestDemurrageDefault):
|
||||
|
||||
# def test_hello(self):
|
||||
# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
# o = c.actual_period(self.address, sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
#
|
||||
# self.backend.time_travel(self.start_time + self.period_seconds + 1)
|
||||
# o = c.actual_period(self.address, sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
#
|
||||
#
|
||||
# def test_balance(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], 1024)
|
||||
# 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, self.accounts[1], sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# balance = c.parse_balance_of(r)
|
||||
# self.assertEqual(balance, 1024)
|
||||
#
|
||||
#
|
||||
# def test_apply_demurrage(self):
|
||||
# modifier = (10 ** 38)
|
||||
#
|
||||
# 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)
|
||||
#
|
||||
# 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])
|
||||
# r = self.rpc.do(o)
|
||||
# demurrage_amount = c.parse_demurrage_amount(r)
|
||||
# self.assertEqual(modifier, demurrage_amount)
|
||||
#
|
||||
# self.backend.time_travel(self.start_time + 61)
|
||||
# (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
|
||||
# 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.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):
|
||||
# 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], 1024)
|
||||
# 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, self.accounts[1], sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# balance = c.parse_balance_of(r)
|
||||
# self.assertEqual(balance, 1024)
|
||||
#
|
||||
# (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 976)
|
||||
# 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, self.accounts[1], sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# balance = c.parse_balance_of(r)
|
||||
# self.assertEqual(balance, 2000)
|
||||
#
|
||||
#
|
||||
# self.backend.time_travel(self.start_time + 61)
|
||||
# (tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# balance = c.parse_balance_of(r)
|
||||
# self.assertEqual(balance, int(2000 * 0.98))
|
||||
#
|
||||
#
|
||||
# def test_minter_control(self):
|
||||
# nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
|
||||
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
#
|
||||
# (tx_hash, o) = c.mint_to(self.address, self.accounts[1], self.accounts[2], 1024)
|
||||
# self.rpc.do(o)
|
||||
# o = receipt(tx_hash)
|
||||
# r = self.rpc.do(o)
|
||||
# self.assertEqual(r['status'], 0)
|
||||
#
|
||||
# (tx_hash, o) = c.add_minter(self.address, self.accounts[1], self.accounts[1])
|
||||
# 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 = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
# (tx_hash, o) = c.add_minter(self.address, self.accounts[0], self.accounts[1])
|
||||
# self.rpc.do(o)
|
||||
# o = receipt(tx_hash)
|
||||
# r = self.rpc.do(o)
|
||||
# self.assertEqual(r['status'], 1)
|
||||
#
|
||||
# nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
|
||||
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
# (tx_hash, o) = c.mint_to(self.address, self.accounts[1], self.accounts[2], 1024)
|
||||
# self.rpc.do(o)
|
||||
# o = receipt(tx_hash)
|
||||
# r = self.rpc.do(o)
|
||||
# self.assertEqual(r['status'], 1)
|
||||
#
|
||||
# (tx_hash, o) = c.add_minter(self.address, self.accounts[1], self.accounts[2])
|
||||
# self.rpc.do(o)
|
||||
# o = receipt(tx_hash)
|
||||
# r = self.rpc.do(o)
|
||||
# self.assertEqual(r['status'], 0)
|
||||
#
|
||||
# (tx_hash, o) = c.remove_minter(self.address, self.accounts[1], self.accounts[1])
|
||||
# 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[1], self.accounts[2], 1024)
|
||||
# self.rpc.do(o)
|
||||
# o = receipt(tx_hash)
|
||||
# r = self.rpc.do(o)
|
||||
# self.assertEqual(r['status'], 0)
|
||||
#
|
||||
#
|
||||
def test_hello(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
o = c.actual_period(self.address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
|
||||
self.backend.time_travel(self.start_time + self.period_seconds + 1)
|
||||
o = c.actual_period(self.address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
|
||||
|
||||
def test_balance(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], 1024)
|
||||
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, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
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):
|
||||
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)
|
||||
|
||||
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])
|
||||
r = self.rpc.do(o)
|
||||
demurrage_amount = c.parse_demurrage_amount(r)
|
||||
self.assertEqual(modifier, demurrage_amount)
|
||||
|
||||
self.backend.time_travel(self.start_time + 61)
|
||||
(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
|
||||
modifier = int(modifier_base * (10 ** 22)) # 38 decimal places minus 6 (1000000)
|
||||
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
|
||||
logg.warning('mod base {}'.format(modifier_base))
|
||||
modifier = int(modifier_base * (10 ** 12))
|
||||
|
||||
rounding_tolerance_nano = 4000000 # 0.000004% precision
|
||||
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.assertLessEqual(modifier, demurrage_amount_truncate)
|
||||
|
||||
|
||||
def test_mint(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], 1024)
|
||||
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, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
self.assertEqual(balance, 1024)
|
||||
|
||||
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 976)
|
||||
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, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
self.assertEqual(balance, 2000)
|
||||
|
||||
|
||||
self.backend.time_travel(self.start_time + 61)
|
||||
(tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
self.assertEqual(balance, int(2000 * 0.98))
|
||||
|
||||
|
||||
def test_minter_control(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
(tx_hash, o) = c.mint_to(self.address, self.accounts[1], self.accounts[2], 1024)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 0)
|
||||
|
||||
(tx_hash, o) = c.add_minter(self.address, self.accounts[1], self.accounts[1])
|
||||
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 = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
(tx_hash, o) = c.add_minter(self.address, self.accounts[0], self.accounts[1])
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
(tx_hash, o) = c.mint_to(self.address, self.accounts[1], self.accounts[2], 1024)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.add_minter(self.address, self.accounts[1], self.accounts[2])
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 0)
|
||||
|
||||
(tx_hash, o) = c.remove_minter(self.address, self.accounts[1], self.accounts[1])
|
||||
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[1], self.accounts[2], 1024)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 0)
|
||||
|
||||
|
||||
def test_base_amount(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
@@ -216,78 +242,129 @@ class TestBasic(TestDemurrageDefault):
|
||||
amount = c.parse_to_base_amount(r)
|
||||
self.assertEqual(amount, 1020)
|
||||
|
||||
#
|
||||
# def test_transfer(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], 1024)
|
||||
# self.rpc.do(o)
|
||||
#
|
||||
# nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
|
||||
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
# (tx_hash, o) = c.transfer(self.address, self.accounts[1], self.accounts[2], 500)
|
||||
# self.rpc.do(o)
|
||||
# o = receipt(tx_hash)
|
||||
# r = self.rpc.do(o)
|
||||
# self.assertEqual(r['status'], 1)
|
||||
#
|
||||
# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# balance = c.parse_balance_of(r)
|
||||
# self.assertEqual(balance, 524)
|
||||
#
|
||||
# o = c.balance_of(self.address, self.accounts[2], sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# balance = c.parse_balance_of(r)
|
||||
# self.assertEqual(balance, 500)
|
||||
#
|
||||
# nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc)
|
||||
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
# (tx_hash, o) = c.transfer(self.address, self.accounts[2], self.accounts[1], 500)
|
||||
# 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)
|
||||
# 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], 1024)
|
||||
# self.rpc.do(o)
|
||||
#
|
||||
# nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
|
||||
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
# (tx_hash, o) = c.approve(self.address, self.accounts[1], self.accounts[2], 500)
|
||||
# self.rpc.do(o)
|
||||
# o = receipt(tx_hash)
|
||||
# r = self.rpc.do(o)
|
||||
# self.assertEqual(r['status'], 1)
|
||||
#
|
||||
# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# balance = c.parse_balance_of(r)
|
||||
# self.assertEqual(balance, 1024)
|
||||
#
|
||||
# nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc)
|
||||
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
# (tx_hash, o) = c.transfer_from(self.address, self.accounts[2], self.accounts[1], self.accounts[3], 500)
|
||||
# self.rpc.do(o)
|
||||
# o = receipt(tx_hash)
|
||||
# r = self.rpc.do(o)
|
||||
# self.assertEqual(r['status'], 1)
|
||||
#
|
||||
# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# balance = c.parse_balance_of(r)
|
||||
# self.assertEqual(balance, 524)
|
||||
#
|
||||
# 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, 500)
|
||||
|
||||
def test_transfer(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], 1024)
|
||||
self.rpc.do(o)
|
||||
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
(tx_hash, o) = c.transfer(self.address, self.accounts[1], self.accounts[2], 500)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
self.assertEqual(balance, 524)
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[2], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
self.assertEqual(balance, 500)
|
||||
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
(tx_hash, o) = c.transfer(self.address, self.accounts[2], self.accounts[1], 500)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
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)
|
||||
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], 1024)
|
||||
self.rpc.do(o)
|
||||
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
(tx_hash, o) = c.approve(self.address, self.accounts[1], self.accounts[2], 500)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
self.assertEqual(balance, 1024)
|
||||
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
(tx_hash, o) = c.transfer_from(self.address, self.accounts[2], self.accounts[1], self.accounts[3], 500)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
self.assertEqual(balance, 524)
|
||||
|
||||
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, 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__':
|
||||
|
||||
@@ -18,7 +18,7 @@ from hexathon import (
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
|
||||
# test imports
|
||||
from tests.base import TestDemurrageCap
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageCap
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
|
||||
41
python/tests/test_demurrage_ext.py
Normal file
41
python/tests/test_demurrage_ext.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# standard imports
|
||||
import datetime
|
||||
import unittest
|
||||
|
||||
# external imports
|
||||
from chainlib.eth.nonce import RPCNonceOracle
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
class TestEmulate(TestDemurrage):
|
||||
|
||||
def test_amount_since(self):
|
||||
d = datetime.datetime.utcnow() - datetime.timedelta(seconds=29, hours=5, minutes=3, days=4)
|
||||
c = DemurrageCalculator(0.00000050105908373373)
|
||||
a = c.amount_since(100, d.timestamp())
|
||||
self.assert_within_lower(a, 99.69667, 0.1)
|
||||
|
||||
|
||||
def test_amount_since_slow(self):
|
||||
d = datetime.datetime.utcnow() - datetime.timedelta(seconds=29, hours=5, minutes=3, days=4)
|
||||
c = DemurrageCalculator(0.00000050105908373373)
|
||||
a = c.amount_since_slow(100, d.timestamp())
|
||||
self.assert_within_lower(a, 99.69667, 0.1)
|
||||
|
||||
|
||||
def test_from_contract(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
self.deploy(c, 'SingleNocap')
|
||||
dc = DemurrageCalculator.from_contract(self.rpc, self.chain_spec, self.address, sender_address=self.accounts[0])
|
||||
self.assertEqual(dc.r_min, 0.02)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -18,7 +18,7 @@ from chainlib.eth.block import (
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
|
||||
# test imports
|
||||
from tests.base import TestDemurrageDefault
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageDefault
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
@@ -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__':
|
||||
|
||||
@@ -7,13 +7,21 @@ import logging
|
||||
# external imports
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
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
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
|
||||
# test imports
|
||||
from tests.base import TestDemurrageDefault
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageDefault
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
@@ -70,10 +78,10 @@ class TestPeriod(TestDemurrageDefault):
|
||||
# allow test code float rounding error to billionth
|
||||
modifier = (1 - (self.tax_level / 1000000)) ** (self.period_seconds / 60)
|
||||
modifier *= 10 ** 9
|
||||
modifier = int(modifier) * (10 ** (38 - 9))
|
||||
modifier = int(modifier) * (10 ** (28 - 9))
|
||||
|
||||
period /= (10 ** (38 - 9))
|
||||
period = int(period) * (10 ** (38 - 9))
|
||||
period /= (10 ** (28 - 9))
|
||||
period = int(period) * (10 ** (28 - 9))
|
||||
self.assertEqual(modifier, period)
|
||||
|
||||
self.backend.time_travel(self.start_time + self.period_seconds * 2)
|
||||
@@ -96,12 +104,97 @@ class TestPeriod(TestDemurrageDefault):
|
||||
# allow test code float rounding error to billionth
|
||||
modifier = (1 - (self.tax_level / 1000000)) ** ((self.period_seconds * 2) / 60)
|
||||
modifier *= 10 ** 9
|
||||
modifier = int(modifier) * (10 ** (38 - 9))
|
||||
modifier = int(modifier) * (10 ** (28 - 9))
|
||||
|
||||
period /= (10 ** (38 - 9))
|
||||
period = int(period) * (10 ** (38 - 9))
|
||||
period /= (10 ** (28 - 9))
|
||||
period = int(period) * (10 ** (28 - 9))
|
||||
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__':
|
||||
unittest.main()
|
||||
|
||||
@@ -22,7 +22,7 @@ from hexathon import (
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
|
||||
# test imports
|
||||
from tests.base import TestDemurrageDefault
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageDefault
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
@@ -31,6 +31,8 @@ testdir = os.path.dirname(__file__)
|
||||
|
||||
class TestRedistribution(TestDemurrageDefault):
|
||||
|
||||
|
||||
|
||||
def test_whole_is_parts(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
198
python/tests/test_redistribution_single.py
Normal file
198
python/tests/test_redistribution_single.py
Normal file
@@ -0,0 +1,198 @@
|
||||
# standard imports
|
||||
import os
|
||||
import unittest
|
||||
import json
|
||||
import logging
|
||||
|
||||
# external imports
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
from chainlib.eth.nonce import RPCNonceOracle
|
||||
from chainlib.eth.tx import receipt
|
||||
from chainlib.eth.block import (
|
||||
block_latest,
|
||||
block_by_number,
|
||||
)
|
||||
from chainlib.eth.address import to_checksum_address
|
||||
from hexathon import (
|
||||
strip_0x,
|
||||
add_0x,
|
||||
)
|
||||
|
||||
# local imports
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
|
||||
# test imports
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageDefault
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logg = logging.getLogger()
|
||||
|
||||
testdir = os.path.dirname(__file__)
|
||||
|
||||
class TestRedistribution(TestDemurrageDefault):
|
||||
|
||||
|
||||
def test_redistribution_periods(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)
|
||||
|
||||
for i in range(1, 10):
|
||||
logg.debug('execute time travel to period {}'.format(i))
|
||||
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 = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
o = c.redistributions(self.address, i, sender_address=self.accounts[0])
|
||||
redistribution = self.rpc.do(o)
|
||||
|
||||
o = c.to_redistribution_demurrage_modifier(self.address, redistribution, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
demurrage = c.parse_to_redistribution_item(r)
|
||||
|
||||
o = c.redistributions(self.address, i-1, sender_address=self.accounts[0])
|
||||
redistribution = self.rpc.do(o)
|
||||
|
||||
o = c.to_redistribution_demurrage_modifier(self.address, redistribution, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
demurrage_previous = c.parse_to_redistribution_item(r)
|
||||
|
||||
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)
|
||||
|
||||
logg.debug('testing sink {} mint {} adds up to supply {} with demurrage between {} and {}'.format(balance_sink, balance_minter, supply, demurrage_previous, demurrage))
|
||||
|
||||
self.assert_within_lower(balance_minter + balance_sink, supply, 0.001)
|
||||
|
||||
|
||||
def test_redistribution_catchup_periods(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)
|
||||
|
||||
self.backend.time_travel(self.start_time + (self.period_seconds * 100))
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
o = c.to_redistribution_demurrage_modifier(self.address, redistribution, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
demurrage = c.parse_to_redistribution_item(r)
|
||||
|
||||
o = c.redistributions(self.address, i-1, sender_address=self.accounts[0])
|
||||
redistribution = self.rpc.do(o)
|
||||
|
||||
o = c.to_redistribution_demurrage_modifier(self.address, redistribution, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
demurrage_previous = c.parse_to_redistribution_item(r)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -21,7 +21,7 @@ from hexathon import (
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
|
||||
# test imports
|
||||
from tests.base import TestDemurrageUnit
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageUnit
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
@@ -31,40 +31,43 @@ testdir = os.path.dirname(__file__)
|
||||
|
||||
class TestRedistribution(TestDemurrageUnit):
|
||||
|
||||
|
||||
# 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)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
demurrage = (1 - (self.tax_level / 1000000)) * (10**38)
|
||||
demurrage = (1 - (self.tax_level / 1000000)) * (10**28)
|
||||
supply = self.default_supply
|
||||
|
||||
o = c.get_distribution(self.address, supply, demurrage, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
distribution = c.parse_get_distribution(r)
|
||||
expected_distribution = self.default_supply * (self.tax_level / 1000000)
|
||||
self.assertEqual(distribution, expected_distribution)
|
||||
self.assert_within_lower(distribution, expected_distribution, 1000)
|
||||
|
||||
|
||||
def test_distribution_from_redistribution(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**38)
|
||||
demurrage = (1 - (self.tax_level / 100000)) * (10**28)
|
||||
|
||||
logg.debug('demurrage {}'.format(demurrage))
|
||||
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, supply, 2, sender_address=self.accounts[0])
|
||||
redistribution = self.rpc.do(o)
|
||||
|
||||
o = c.get_distribution_from_redistribution(self.address, redistribution, self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
distribution = c.parse_get_distribution(r)
|
||||
expected_distribution = self.default_supply * (self.tax_level / 1000000)
|
||||
self.assertEqual(distribution, expected_distribution)
|
||||
expected_distribution = (self.default_supply * self.tax_level) / 100000
|
||||
logg.debug('distribution {} supply {}'.format(distribution, self.default_supply))
|
||||
self.assert_within_lower(distribution, expected_distribution, 1000)
|
||||
|
||||
|
||||
|
||||
def test_single_step(self):
|
||||
def test_single_step_basic(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
@@ -80,9 +83,15 @@ class TestRedistribution(TestDemurrageUnit):
|
||||
|
||||
expected_balance = int(mint_amount - ((self.tax_level / 1000000) * mint_amount))
|
||||
|
||||
o = c.balance_of(self.address, ZERO_ADDRESS, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance(r)
|
||||
|
||||
logg.debug('balance {}'.format(balance))
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
|
||||
self.assertEqual(balance, expected_balance)
|
||||
|
||||
@@ -107,7 +116,7 @@ class TestRedistribution(TestDemurrageUnit):
|
||||
for i in range(3):
|
||||
o = c.balance_of(self.address, self.accounts[i+1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
self.assertEqual(balance, expected_balance)
|
||||
|
||||
|
||||
@@ -146,7 +155,7 @@ class TestRedistribution(TestDemurrageUnit):
|
||||
expected_balance = mint_amount - demurrage_amount
|
||||
o = c.balance_of(self.address, self.accounts[2], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
self.assertEqual(balance, expected_balance)
|
||||
|
||||
half_demurrage_amount = int((self.tax_level / 1000000) * half_mint_amount)
|
||||
@@ -154,12 +163,12 @@ class TestRedistribution(TestDemurrageUnit):
|
||||
expected_balance = half_mint_amount - half_demurrage_amount
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
self.assertEqual(balance, expected_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)
|
||||
balance = c.parse_balance(r)
|
||||
self.assertEqual(balance, expected_balance)
|
||||
|
||||
o = c.total_supply(self.address, sender_address=self.accounts[0])
|
||||
@@ -168,15 +177,13 @@ class TestRedistribution(TestDemurrageUnit):
|
||||
|
||||
o = c.redistributions(self.address, 0, sender_address=self.accounts[0])
|
||||
redistribution = self.rpc.do(o)
|
||||
logg.debug('redistribution {}'.format(redistribution))
|
||||
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.to_redistribution_demurrage_modifier(self.address, redistribution, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
demurrage = 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('\nrediistribution {}\ndemurrage {}\nsupply {}'.format(redistribution, demurrage, supply))
|
||||
|
||||
expected_balance = int(supply * (self.tax_level / 1000000))
|
||||
@@ -184,7 +191,7 @@ class TestRedistribution(TestDemurrageUnit):
|
||||
|
||||
o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
self.assert_within_lower(balance, expected_balance, 1000)
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import eth_tester
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
|
||||
# test imports
|
||||
from tests.base import TestDemurrageDefault
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageDefault
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
|
||||
@@ -18,7 +18,7 @@ from hexathon import (
|
||||
from erc20_demurrage_token import DemurrageToken
|
||||
|
||||
# test imports
|
||||
from tests.base import TestDemurrageSingle
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageSingle
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
@@ -67,18 +67,18 @@ class TestRedistributionSingle(TestDemurrageSingle):
|
||||
tax_modifier = (1 - (self.tax_level / 1000000)) ** 10
|
||||
o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
self.assertEqual(balance, int(mint_amount * tax_modifier))
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[2], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
base_amount = mint_amount - int(mint_amount * 0.1)
|
||||
self.assertEqual(balance, int(base_amount * tax_modifier)) #(base_amount - (base_amount * (self.tax_level / 1000000))))
|
||||
|
||||
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)
|
||||
balance = c.parse_balance(r)
|
||||
base_amount = mint_amount - int(mint_amount * 0.2)
|
||||
self.assertEqual(balance, int(base_amount * tax_modifier)) #(base_amount - (base_amount * (self.tax_level / 1000000))))
|
||||
|
||||
@@ -88,7 +88,7 @@ class TestRedistributionSingle(TestDemurrageSingle):
|
||||
|
||||
o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance = c.parse_balance_of(r)
|
||||
balance = c.parse_balance(r)
|
||||
expected_balance = new_supply - (new_supply * tax_modifier)
|
||||
self.assert_within_lower(balance, expected_balance, 1)
|
||||
|
||||
|
||||
@@ -61,6 +61,9 @@ contract DemurrageTokenMultiCap {
|
||||
// (this constant x 1000000 is contained within 128 bits)
|
||||
uint256 constant ppmDivider = 100000000000000000000000000000000;
|
||||
|
||||
// demurrage decimal width; 38 places
|
||||
uint256 public immutable resolutionFactor = ppmDivider * 1000000;
|
||||
|
||||
// Timestamp of start of periods (time which contract constructor was called)
|
||||
uint256 public immutable periodStart;
|
||||
|
||||
|
||||
@@ -56,6 +56,9 @@ contract DemurrageTokenMultiNocap {
|
||||
// (this constant x 1000000 is contained within 128 bits)
|
||||
uint256 constant ppmDivider = 100000000000000000000000000000000;
|
||||
|
||||
// demurrage decimal width; 38 places
|
||||
uint256 public immutable resolutionFactor = ppmDivider * 1000000;
|
||||
|
||||
// Timestamp of start of periods (time which contract constructor was called)
|
||||
uint256 public immutable periodStart;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ contract DemurrageTokenSingleCap {
|
||||
uint8 constant shiftRedistributionValue = 32;
|
||||
uint256 constant maskRedistributionValue = 0x00000000000000000000000000000000000000ffffffffffffffffff00000000; // ((1 << 72) - 1) << 32
|
||||
uint8 constant shiftRedistributionDemurrage = 104;
|
||||
uint256 constant maskRedistributionDemurrage = 0x000000ffffffffffffffffffffffffffffffff00000000000000000000000000; // ((1 << 20) - 1) << 140
|
||||
uint256 constant maskRedistributionDemurrage = 0x0000000000ffffffffffffffffffffffffffff00000000000000000000000000; // ((1 << 20) - 1) << 140
|
||||
|
||||
// Account balances
|
||||
mapping (address => uint256) account;
|
||||
@@ -56,7 +56,7 @@ contract DemurrageTokenSingleCap {
|
||||
uint256 constant growthResolutionFactor = 1000000000000;
|
||||
|
||||
// demurrage decimal width; 38 places
|
||||
uint256 immutable resolutionFactor = nanoDivider * growthResolutionFactor;
|
||||
uint256 public immutable resolutionFactor = nanoDivider * growthResolutionFactor;
|
||||
|
||||
// Timestamp of start of periods (time which contract constructor was called)
|
||||
uint256 public immutable periodStart;
|
||||
@@ -115,7 +115,8 @@ contract DemurrageTokenSingleCap {
|
||||
periodStart = demurrageTimestamp;
|
||||
periodDuration = _periodMinutes * 60;
|
||||
//demurrageAmount = 100000000000000000000000000000000000000 - _taxLevelMinute; // Represents 38 decimal places, same as resolutionFactor
|
||||
demurrageAmount = 100000000000000000000000000000000000000;
|
||||
//demurrageAmount = 100000000000000000000000000000000000000;
|
||||
demurrageAmount = 10000000000000000000000000000;
|
||||
//demurragePeriod = 1;
|
||||
taxLevel = _taxLevelMinute; // Represents 38 decimal places
|
||||
bytes32 initialRedistribution = toRedistribution(0, demurrageAmount, 0, 1);
|
||||
@@ -127,6 +128,12 @@ contract DemurrageTokenSingleCap {
|
||||
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
|
||||
function addMinter(address _minter) public returns (bool) {
|
||||
require(msg.sender == owner);
|
||||
@@ -152,7 +159,7 @@ contract DemurrageTokenSingleCap {
|
||||
//periodCount = actualPeriod() - demurragePeriod;
|
||||
periodCount = getMinutesDelta(demurrageTimestamp);
|
||||
|
||||
currentDemurragedAmount = uint128(decayBy(demurrageAmount, periodCount));
|
||||
currentDemurragedAmount = uint128(decayBy(demurrageAmount * 10000000000, periodCount));
|
||||
|
||||
return (baseBalance * currentDemurragedAmount) / (nanoDivider * 1000000000000);
|
||||
}
|
||||
@@ -281,7 +288,7 @@ contract DemurrageTokenSingleCap {
|
||||
function getDistribution(uint256 _supply, uint256 _demurrageAmount) public view returns (uint256) {
|
||||
uint256 difference;
|
||||
|
||||
difference = _supply * (resolutionFactor - _demurrageAmount); //(nanoDivider - ((resolutionFactor - _demurrageAmount) / nanoDivider));
|
||||
difference = _supply * (resolutionFactor - (_demurrageAmount * 10000000000)); //(nanoDivider - ((resolutionFactor - _demurrageAmount) / nanoDivider));
|
||||
return difference / resolutionFactor;
|
||||
}
|
||||
|
||||
@@ -310,6 +317,10 @@ contract DemurrageTokenSingleCap {
|
||||
|
||||
// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
|
||||
function applyDemurrage() public returns (bool) {
|
||||
return applyDemurrageLimited(0);
|
||||
}
|
||||
|
||||
function applyDemurrageLimited(uint256 _rounds) public returns (bool) {
|
||||
//uint128 epochPeriodCount;
|
||||
uint256 periodCount;
|
||||
uint256 lastDemurrageAmount;
|
||||
@@ -322,6 +333,12 @@ contract DemurrageTokenSingleCap {
|
||||
return false;
|
||||
}
|
||||
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));
|
||||
//demurragePeriod = epochPeriodCount;
|
||||
demurrageTimestamp = demurrageTimestamp + (periodCount * 60);
|
||||
@@ -407,7 +424,7 @@ contract DemurrageTokenSingleCap {
|
||||
|
||||
// Inflates the given amount according to the current demurrage modifier
|
||||
function toBaseAmount(uint256 _value) public view returns (uint256) {
|
||||
return (_value * resolutionFactor) / demurrageAmount;
|
||||
return (_value * resolutionFactor) / (demurrageAmount * 10000000000);
|
||||
}
|
||||
|
||||
// Implements ERC20, triggers tax and/or redistribution
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
pragma solidity > 0.6.11;
|
||||
pragma solidity >= 0.8.0;
|
||||
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
contract DemurrageTokenSingleCap {
|
||||
|
||||
// Redistribution bit field, with associated shifts and masks
|
||||
// (Uses sub-byte boundaries)
|
||||
bytes32[] public redistributions; // uint51(unused) | uint64(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period)
|
||||
uint8 constant shiftRedistributionPeriod = 0;
|
||||
uint256 constant maskRedistributionPeriod = 0x00000000000000000000000000000000000000000000000000000000ffffffff; // (1 << 32) - 1
|
||||
uint8 constant shiftRedistributionValue = 32;
|
||||
uint256 constant maskRedistributionValue = 0x00000000000000000000000000000000000000ffffffffffffffffff00000000; // ((1 << 72) - 1) << 32
|
||||
uint8 constant shiftRedistributionDemurrage = 104;
|
||||
uint256 constant maskRedistributionDemurrage = 0x000000ffffffffffffffffffffffffffffffff00000000000000000000000000; // ((1 << 20) - 1) << 140
|
||||
|
||||
uint8 constant shiftRedistributionIsUsed = 255;
|
||||
uint256 constant maskRedistributionIsUsed = 0x4000000000000000000000000000000000000000000000000000000000000000; // 1 << 255
|
||||
|
||||
struct redistributionItem {
|
||||
uint32 period;
|
||||
uint72 value;
|
||||
uint104 demurrage;
|
||||
}
|
||||
redistributionItem[] public redistributions; // uint51(unused) | uint64(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period)
|
||||
|
||||
// Account balances
|
||||
mapping (address => uint256) account;
|
||||
@@ -24,8 +17,6 @@ contract DemurrageTokenSingleCap {
|
||||
// Cached demurrage amount, ppm with 38 digit resolution
|
||||
uint128 public demurrageAmount;
|
||||
|
||||
// Cached demurrage period; the period for which demurrageAmount was calculated
|
||||
//uint128 public demurragePeriod;
|
||||
// Cached demurrage timestamp; the timestamp for which demurrageAmount was last calculated
|
||||
uint256 public demurrageTimestamp;
|
||||
|
||||
@@ -44,10 +35,17 @@ contract DemurrageTokenSingleCap {
|
||||
uint256 public decimals;
|
||||
|
||||
// Implements ERC20
|
||||
uint256 public totalSupply;
|
||||
//uint256 public totalSupply;
|
||||
uint256 supply;
|
||||
|
||||
// Minimum amount of (demurraged) tokens an account must spend to participate in redistribution for a particular period
|
||||
uint256 public minimumParticipantSpend;
|
||||
// Last executed period
|
||||
uint256 public lastPeriod;
|
||||
|
||||
// Last sink redistribution amount
|
||||
uint256 public totalSink;
|
||||
|
||||
// Value of burnt tokens (burnt tokens do not decay)
|
||||
uint256 public burned;
|
||||
|
||||
// 128 bit resolution of the demurrage divisor
|
||||
// (this constant x 1000000 is contained within 128 bits)
|
||||
@@ -57,7 +55,7 @@ contract DemurrageTokenSingleCap {
|
||||
uint256 constant growthResolutionFactor = 1000000000000;
|
||||
|
||||
// demurrage decimal width; 38 places
|
||||
uint256 immutable resolutionFactor = nanoDivider * growthResolutionFactor;
|
||||
uint256 public immutable resolutionFactor = nanoDivider * growthResolutionFactor;
|
||||
|
||||
// Timestamp of start of periods (time which contract constructor was called)
|
||||
uint256 public immutable periodStart;
|
||||
@@ -75,7 +73,7 @@ contract DemurrageTokenSingleCap {
|
||||
mapping (address => mapping (address => uint256 ) ) allowance; // holder -> spender -> amount (amount is subject to demurrage)
|
||||
|
||||
// Address to send unallocated redistribution tokens
|
||||
address sinkAddress;
|
||||
address public sinkAddress;
|
||||
|
||||
// Implements ERC20
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
||||
@@ -98,10 +96,15 @@ contract DemurrageTokenSingleCap {
|
||||
// Temporary event used in development, will be removed on prod
|
||||
event Debug(bytes32 _foo);
|
||||
|
||||
// Emitted when tokens are burned
|
||||
event Burn(address indexed _burner, uint256 _value);
|
||||
|
||||
// EIP173
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173
|
||||
|
||||
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint128 _taxLevelMinute, uint256 _periodMinutes, address _defaultSinkAddress) public {
|
||||
redistributionItem memory initialRedistribution;
|
||||
|
||||
// ACL setup
|
||||
owner = msg.sender;
|
||||
minter[owner] = true;
|
||||
@@ -115,16 +118,20 @@ contract DemurrageTokenSingleCap {
|
||||
demurrageTimestamp = block.timestamp;
|
||||
periodStart = demurrageTimestamp;
|
||||
periodDuration = _periodMinutes * 60;
|
||||
//demurrageAmount = 100000000000000000000000000000000000000 - _taxLevelMinute; // Represents 38 decimal places, same as resolutionFactor
|
||||
demurrageAmount = 100000000000000000000000000000000000000;
|
||||
//demurragePeriod = 1;
|
||||
demurrageAmount = uint128(nanoDivider) * 100;
|
||||
taxLevel = _taxLevelMinute; // Represents 38 decimal places
|
||||
bytes32 initialRedistribution = toRedistribution(0, demurrageAmount, 0, 1);
|
||||
initialRedistribution = toRedistribution(0, demurrageAmount, 0, 1);
|
||||
redistributions.push(initialRedistribution);
|
||||
|
||||
// Misc settings
|
||||
sinkAddress = _defaultSinkAddress;
|
||||
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
|
||||
@@ -149,10 +156,9 @@ contract DemurrageTokenSingleCap {
|
||||
|
||||
baseBalance = baseBalanceOf(_account);
|
||||
|
||||
//periodCount = actualPeriod() - demurragePeriod;
|
||||
periodCount = getMinutesDelta(demurrageTimestamp);
|
||||
|
||||
currentDemurragedAmount = uint128(decayBy(demurrageAmount, periodCount));
|
||||
currentDemurragedAmount = uint128(decayBy(demurrageAmount * 10000000000, periodCount));
|
||||
|
||||
return (baseBalance * currentDemurragedAmount) / (nanoDivider * 1000000000000);
|
||||
}
|
||||
@@ -206,7 +212,7 @@ contract DemurrageTokenSingleCap {
|
||||
|
||||
changePeriod();
|
||||
baseAmount = toBaseAmount(_amount);
|
||||
totalSupply += _amount;
|
||||
supply += _amount;
|
||||
increaseBaseBalance(_beneficiary, baseAmount);
|
||||
emit Mint(msg.sender, _beneficiary, _amount);
|
||||
saveRedistributionSupply();
|
||||
@@ -214,31 +220,32 @@ contract DemurrageTokenSingleCap {
|
||||
}
|
||||
|
||||
// Deserializes the redistribution word
|
||||
// uint95(unused) | uint20(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period)
|
||||
function toRedistribution(uint256 _participants, uint256 _demurrageModifierPpm, uint256 _value, uint256 _period) public pure returns(bytes32) {
|
||||
bytes32 redistribution;
|
||||
function toRedistribution(uint256 _participants, uint256 _demurrageModifierPpm, uint256 _value, uint256 _period) public pure returns(redistributionItem memory) {
|
||||
redistributionItem memory redistribution;
|
||||
|
||||
redistribution |= bytes32((_demurrageModifierPpm << shiftRedistributionDemurrage) & maskRedistributionDemurrage);
|
||||
redistribution |= bytes32((_value << shiftRedistributionValue) & maskRedistributionValue);
|
||||
redistribution |= bytes32(_period & maskRedistributionPeriod);
|
||||
redistribution.period = uint32(_period);
|
||||
redistribution.value = uint72(_value);
|
||||
redistribution.demurrage = uint104(_demurrageModifierPpm);
|
||||
return redistribution;
|
||||
|
||||
}
|
||||
|
||||
// Serializes the demurrage period part of the redistribution word
|
||||
function toRedistributionPeriod(bytes32 redistribution) public pure returns (uint256) {
|
||||
return uint256(redistribution) & maskRedistributionPeriod;
|
||||
function toRedistributionPeriod(redistributionItem memory _redistribution) public pure returns (uint256) {
|
||||
return uint256(_redistribution.period);
|
||||
}
|
||||
|
||||
// Serializes the supply part of the redistribution word
|
||||
function toRedistributionSupply(bytes32 redistribution) public pure returns (uint256) {
|
||||
return (uint256(redistribution) & maskRedistributionValue) >> shiftRedistributionValue;
|
||||
function toRedistributionSupply(redistributionItem memory _redistribution) public pure returns (uint256) {
|
||||
return uint256(_redistribution.value);
|
||||
}
|
||||
|
||||
// Serializes the number of participants part of the redistribution word
|
||||
function toRedistributionDemurrageModifier(bytes32 redistribution) public pure returns (uint256) {
|
||||
return (uint256(redistribution) & maskRedistributionDemurrage) >> shiftRedistributionDemurrage;
|
||||
function toRedistributionDemurrageModifier(redistributionItem memory _redistribution) public pure returns (uint256) {
|
||||
return uint256(_redistribution.demurrage);
|
||||
}
|
||||
|
||||
|
||||
// Client accessor to the redistributions array length
|
||||
function redistributionCount() public view returns (uint256) {
|
||||
return redistributions.length;
|
||||
@@ -246,16 +253,14 @@ contract DemurrageTokenSingleCap {
|
||||
|
||||
// Save the current total supply amount to the current redistribution period
|
||||
function saveRedistributionSupply() private returns (bool) {
|
||||
uint256 currentRedistribution;
|
||||
redistributionItem memory currentRedistribution;
|
||||
uint256 grownSupply;
|
||||
|
||||
//grownSupply = growBy(totalSupply, 1);
|
||||
grownSupply = totalSupply;
|
||||
currentRedistribution = uint256(redistributions[redistributions.length-1]);
|
||||
currentRedistribution &= (~maskRedistributionValue);
|
||||
currentRedistribution |= (grownSupply << shiftRedistributionValue);
|
||||
grownSupply = totalSupply();
|
||||
currentRedistribution = redistributions[redistributions.length-1];
|
||||
currentRedistribution.value = uint72(grownSupply);
|
||||
|
||||
redistributions[redistributions.length-1] = bytes32(currentRedistribution);
|
||||
redistributions[redistributions.length-1] = currentRedistribution;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -264,15 +269,16 @@ contract DemurrageTokenSingleCap {
|
||||
return uint128((block.timestamp - periodStart) / periodDuration + 1);
|
||||
}
|
||||
|
||||
// Add an entered demurrage period to the redistribution array
|
||||
function checkPeriod() private view returns (bytes32) {
|
||||
bytes32 lastRedistribution;
|
||||
// Retrieve next redistribution if the period threshold has been crossed
|
||||
function checkPeriod() private view returns (redistributionItem memory) {
|
||||
redistributionItem memory lastRedistribution;
|
||||
redistributionItem memory emptyRedistribution;
|
||||
uint256 currentPeriod;
|
||||
|
||||
lastRedistribution = redistributions[redistributions.length-1];
|
||||
lastRedistribution = redistributions[lastPeriod];
|
||||
currentPeriod = this.actualPeriod();
|
||||
if (currentPeriod <= toRedistributionPeriod(lastRedistribution)) {
|
||||
return bytes32(0x00);
|
||||
return emptyRedistribution;
|
||||
}
|
||||
return lastRedistribution;
|
||||
}
|
||||
@@ -280,11 +286,11 @@ contract DemurrageTokenSingleCap {
|
||||
function getDistribution(uint256 _supply, uint256 _demurrageAmount) public view returns (uint256) {
|
||||
uint256 difference;
|
||||
|
||||
difference = _supply * (resolutionFactor - _demurrageAmount); //(nanoDivider - ((resolutionFactor - _demurrageAmount) / nanoDivider));
|
||||
difference = _supply * (resolutionFactor - (_demurrageAmount * 10000000000));
|
||||
return difference / resolutionFactor;
|
||||
}
|
||||
|
||||
function getDistributionFromRedistribution(bytes32 _redistribution) public returns (uint256) {
|
||||
function getDistributionFromRedistribution(redistributionItem memory _redistribution) public returns (uint256) {
|
||||
uint256 redistributionSupply;
|
||||
uint256 redistributionDemurrage;
|
||||
|
||||
@@ -294,11 +300,15 @@ contract DemurrageTokenSingleCap {
|
||||
}
|
||||
|
||||
// Returns the amount sent to the sink address
|
||||
function applyDefaultRedistribution(bytes32 _redistribution) private returns (uint256) {
|
||||
function applyDefaultRedistribution(redistributionItem memory _redistribution) private returns (uint256) {
|
||||
uint256 unit;
|
||||
uint256 baseUnit;
|
||||
|
||||
unit = getDistributionFromRedistribution(_redistribution);
|
||||
increaseBaseBalance(sinkAddress, toBaseAmount(unit));
|
||||
baseUnit = toBaseAmount(unit) - totalSink;
|
||||
increaseBaseBalance(sinkAddress, baseUnit);
|
||||
lastPeriod += 1;
|
||||
totalSink += baseUnit;
|
||||
return unit;
|
||||
}
|
||||
|
||||
@@ -309,18 +319,25 @@ contract DemurrageTokenSingleCap {
|
||||
|
||||
// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
|
||||
function applyDemurrage() public returns (bool) {
|
||||
//uint128 epochPeriodCount;
|
||||
return applyDemurrageLimited(0);
|
||||
}
|
||||
|
||||
function applyDemurrageLimited(uint256 _rounds) public returns (bool) {
|
||||
uint256 periodCount;
|
||||
uint256 lastDemurrageAmount;
|
||||
|
||||
//epochPeriodCount = actualPeriod();
|
||||
//periodCount = epochPeriodCount - demurragePeriod;
|
||||
|
||||
periodCount = getMinutesDelta(demurrageTimestamp);
|
||||
if (periodCount == 0) {
|
||||
return false;
|
||||
}
|
||||
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));
|
||||
//demurragePeriod = epochPeriodCount;
|
||||
demurrageTimestamp = demurrageTimestamp + (periodCount * 60);
|
||||
@@ -338,37 +355,46 @@ contract DemurrageTokenSingleCap {
|
||||
return (block.timestamp - _target) / 60;
|
||||
}
|
||||
|
||||
function isEmptyRedistribution(redistributionItem memory _redistribution) public pure returns(bool) {
|
||||
if (_redistribution.period > 0) {
|
||||
return false;
|
||||
}
|
||||
if (_redistribution.value > 0) {
|
||||
return false;
|
||||
}
|
||||
if (_redistribution.demurrage > 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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;
|
||||
redistributionItem memory currentRedistribution;
|
||||
redistributionItem memory nextRedistribution;
|
||||
redistributionItem memory lastRedistribution;
|
||||
uint256 currentPeriod;
|
||||
uint256 currentDemurrageAmount;
|
||||
uint256 lastDemurrageAmount;
|
||||
uint256 nextRedistributionDemurrage;
|
||||
uint256 demurrageCounts;
|
||||
uint256 periodTimestamp;
|
||||
uint256 nextPeriod;
|
||||
|
||||
applyDemurrage();
|
||||
currentRedistribution = checkPeriod();
|
||||
if (currentRedistribution == bytes32(0x00)) {
|
||||
if (isEmptyRedistribution(currentRedistribution)) {
|
||||
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;
|
||||
}
|
||||
|
||||
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod);
|
||||
lastDemurrageAmount = toRedistributionDemurrageModifier(lastRedistribution);
|
||||
demurrageCounts = periodDuration / 60;
|
||||
nextRedistributionDemurrage = decayBy(lastDemurrageAmount, demurrageCounts);
|
||||
|
||||
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply(), nextPeriod);
|
||||
redistributions.push(nextRedistribution);
|
||||
|
||||
applyDefaultRedistribution(nextRedistribution);
|
||||
@@ -377,18 +403,18 @@ contract DemurrageTokenSingleCap {
|
||||
}
|
||||
|
||||
// Reverse a value reduced by demurrage by the given period to its original value
|
||||
function growBy(uint256 _value, uint256 _period) public view returns (uint256) {
|
||||
uint256 valueFactor;
|
||||
uint256 truncatedTaxLevel;
|
||||
|
||||
valueFactor = growthResolutionFactor;
|
||||
truncatedTaxLevel = taxLevel / nanoDivider;
|
||||
|
||||
for (uint256 i = 0; i < _period; i++) {
|
||||
valueFactor = valueFactor + ((valueFactor * truncatedTaxLevel) / growthResolutionFactor);
|
||||
}
|
||||
return (valueFactor * _value) / growthResolutionFactor;
|
||||
}
|
||||
// function growBy(uint256 _value, uint256 _period) public view returns (uint256) {
|
||||
// uint256 valueFactor;
|
||||
// uint256 truncatedTaxLevel;
|
||||
//
|
||||
// valueFactor = growthResolutionFactor;
|
||||
// truncatedTaxLevel = taxLevel / nanoDivider;
|
||||
//
|
||||
// for (uint256 i = 0; i < _period; i++) {
|
||||
// valueFactor = valueFactor + ((valueFactor * truncatedTaxLevel) / growthResolutionFactor);
|
||||
// }
|
||||
// return (valueFactor * _value) / growthResolutionFactor;
|
||||
// }
|
||||
|
||||
// Calculate a value reduced by demurrage by the given period
|
||||
function decayBy(uint256 _value, uint256 _period) public view returns (uint256) {
|
||||
@@ -406,21 +432,52 @@ contract DemurrageTokenSingleCap {
|
||||
|
||||
// Inflates the given amount according to the current demurrage modifier
|
||||
function toBaseAmount(uint256 _value) public view returns (uint256) {
|
||||
return (_value * resolutionFactor) / demurrageAmount;
|
||||
return (_value * resolutionFactor) / (demurrageAmount * 10000000000);
|
||||
}
|
||||
|
||||
// Implements ERC20, triggers tax and/or redistribution
|
||||
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;
|
||||
@@ -444,7 +501,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;
|
||||
}
|
||||
@@ -456,7 +515,6 @@ contract DemurrageTokenSingleCap {
|
||||
decreaseBaseBalance(_from, _value);
|
||||
increaseBaseBalance(_to, _value);
|
||||
|
||||
//period = actualPeriod();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -477,6 +535,29 @@ contract DemurrageTokenSingleCap {
|
||||
emit OwnershipTransferred(oldOwner, owner);
|
||||
}
|
||||
|
||||
// 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]);
|
||||
uint256 _delta = toBaseAmount(_value);
|
||||
|
||||
applyDemurrage();
|
||||
decreaseBaseBalance(msg.sender, _delta);
|
||||
burned += _value;
|
||||
emit Burn(msg.sender, _value);
|
||||
}
|
||||
|
||||
// Implements ERC20
|
||||
function totalSupply() public view returns (uint256) {
|
||||
return supply - burned;
|
||||
}
|
||||
|
||||
// Return total number of burned tokens
|
||||
function totalBurned() public view returns (uint256) {
|
||||
return burned;
|
||||
}
|
||||
|
||||
// Implements EIP165
|
||||
function supportsInterface(bytes4 _sum) public pure returns (bool) {
|
||||
if (_sum == 0xc6bb4b70) { // ERC20
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SOLC = /usr/bin/solc
|
||||
|
||||
all: multi single
|
||||
all: single_nocap
|
||||
|
||||
multi_nocap:
|
||||
$(SOLC) DemurrageTokenMultiNocap.sol --abi --evm-version byzantium | awk 'NR>3' > DemurrageTokenMultiNocap.json
|
||||
@@ -33,6 +33,7 @@ test: all
|
||||
python ../python/tests/test_pure.py
|
||||
|
||||
install: all
|
||||
cp -v DemurrageToken*.{json,bin} ../python/erc20_demurrage_token/data/
|
||||
cp -v DemurrageToken*.json ../python/erc20_demurrage_token/data/
|
||||
cp -v DemurrageToken*.bin ../python/erc20_demurrage_token/data/
|
||||
|
||||
.PHONY: test install
|
||||
|
||||
Reference in New Issue
Block a user