29 Commits

Author SHA1 Message Date
lash
bcc957f861 Remove commented contract code, makefile single nocap only 2023-02-08 08:51:57 +00:00
lash
84b1a5b439 Return redistributions type correctly 2023-02-08 08:44:43 +00:00
lash
5941a6abdf Merge branch 'master' into dev-0.2.0 2022-05-30 07:51:34 +00:00
lash
c5de0e3300 Reactivate test, expose sinkaddress 2022-05-28 09:28:10 +00:00
lash
cc1a84f818 Add catch-up period test 2022-05-27 13:22:25 +00:00
lash
ee871730dc Remove unneeded demurragestart item 2022-05-27 12:53:11 +00:00
lash
31faa78346 Keep cumulative sink total in state and deduct from upcoming demurrage 2022-05-27 12:51:10 +00:00
lash
18ee9c5f9b Make tests pass 2022-05-27 12:02:27 +00:00
lash
a0557b35a0 Fix cumulative distribution calculation bug in SingleNocap 2022-05-27 11:10:31 +00:00
lash
127c67e665 Add steps option to demurrage cli 2022-05-03 18:19:28 +00:00
lash
1387451e01 Bump deps 2022-04-24 18:53:09 +00:00
William Luke
226f81fc5c Merge branch 'lash/apply-cli' into 'master'
Add apply demurrage cli tool

See merge request cicnet/erc20-demurrage-token!9
2022-03-14 12:58:45 +00:00
Louis Holbrook
370efb3192 Add apply demurrage cli tool 2022-03-14 12:58:45 +00:00
lash
3a1fb22631 Remove arg defaults 2022-03-02 13:32:31 +00:00
lash
f1a2a78eb4 Merge branch 'master' into lash/apply-cli 2022-03-02 09:03:40 +00:00
Louis Holbrook
47ee1cfa45 Merge branch 'lash/gas-safety-valve' into 'master'
bug: Wrong redistribution amount + limited demurrage apply

Closes grassrootseconomics/cic-internal-integration#276, grassrootseconomics/cic-internal-integration#273, and grassrootseconomics/cic-internal-integration#272

See merge request cicnet/erc20-demurrage-token!7
2022-03-02 09:01:50 +00:00
Louis Holbrook
db56e0d33f bug: Wrong redistribution amount + limited demurrage apply 2022-03-02 09:01:50 +00:00
Louis Holbrook
d0c02eadbf Merge branch 'lum/add-ci' into 'master'
ci: add basic ci

See merge request cicnet/erc20-demurrage-token!8
2022-03-02 08:54:20 +00:00
William Luke
ed60b5923b ci: add basic ci 2022-03-02 08:54:20 +00:00
lash
1e24ec1352 Add apply demurrage cli tool 2022-03-02 08:15:10 +00:00
nolash
a04c826ba7 Bump deps, version 2021-12-22 20:12:42 +00:00
nolash
04f50cdede Loosen dependencies 2021-12-21 10:47:38 +00:00
21d65522a8 Merge branch 'philip/bumps' into 'master'
Bumps versions of deps and dep.

See merge request cicnet/erc20-demurrage-token!6
2021-12-18 11:49:44 +00:00
130b5ea587 Bumps lib version and deps. 2021-12-18 14:47:02 +03:00
e486e9f31a Moves configs into data folder 2021-12-18 14:34:01 +03:00
959b018247 Bumps deps version for conflict resolution. 2021-12-15 09:35:08 +03:00
6ecacd60d4 Bumps lib patch version. 2021-12-15 09:34:50 +03:00
c40157318f Bumps versions of deps and dep. 2021-12-15 06:18:38 +03:00
nolash
025ef614a5 WIP test rehabilitations 2021-11-15 14:45:46 +01:00
30 changed files with 1239 additions and 446 deletions

2
.gitignore vendored
View File

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

36
.gitlab-ci.yml Normal file
View File

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

View File

@@ -1,4 +1,14 @@
- 0.0.2-pending - 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 * Move to chainlib-eth
- 0.0.1-unreleased - 0.0.1
* Interface for redistributed and non-redistributed, with or without cap * Interface for redistributed and non-redistributed, with or without cap

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -9,8 +9,7 @@ from chainlib.eth.constant import ZERO_ADDRESS
# local imports # local imports
from .token import DemurrageToken from .token import DemurrageToken
logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger(__name__)
logg = logging.getLogger()
class DemurrageCalculator: class DemurrageCalculator:

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

View File

@@ -14,8 +14,8 @@ import logging
# external imports # external imports
import confini import confini
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer from funga.eth.signer import EIP155Signer
from crypto_dev_signer.keystore.dict import DictKeystore from funga.eth.keystore.dict import DictKeystore
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
from chainlib.eth.nonce import ( from chainlib.eth.nonce import (
RPCNonceOracle, RPCNonceOracle,
@@ -49,8 +49,8 @@ arg_flags = chainlib.eth.cli.argflag_std_write
argparser = chainlib.eth.cli.ArgumentParser(arg_flags) argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_argument('--name', dest='token_name', type=str, help='Token name') 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('--symbol', dest='token_symbol', required=True, type=str, help='Token symbol')
argparser.add_argument('--decimals', dest='token_decimals', default=18, type=int, help='Token decimals') argparser.add_argument('--decimals', dest='token_decimals', type=int, help='Token decimals')
argparser.add_argument('--sink-address', dest='sink_address', default=ZERO_ADDRESS, type=str, help='demurrage level,ppm per minute') 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('--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('--redistribution-period', type=int, help='redistribution period, minutes (0 = deactivate)') # default 10080 = week
argparser.add_argument('--multi', action='store_true', help='automatic redistribution') argparser.add_argument('--multi', action='store_true', help='automatic redistribution')
@@ -90,7 +90,6 @@ conn = rpc.connect_by_config(config)
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC')) chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
def main(): def main():
signer = rpc.get_signer() signer = rpc.get_signer()
signer_address = rpc.get_sender_address() signer_address = rpc.get_sender_address()

View File

@@ -20,8 +20,8 @@ from chainlib.eth.block import (
block_by_number, block_by_number,
block_by_hash, block_by_hash,
) )
from crypto_dev_signer.keystore.dict import DictKeystore from funga.eth.keystore.dict import DictKeystore
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer from funga.eth.signer import EIP155Signer
from hexathon import ( from hexathon import (
strip_0x, strip_0x,
add_0x, add_0x,

View File

@@ -10,6 +10,7 @@ from chainlib.eth.tx import (
from chainlib.hash import keccak256_string_to_hex from chainlib.hash import keccak256_string_to_hex
from chainlib.eth.contract import ( from chainlib.eth.contract import (
ABIContractEncoder, ABIContractEncoder,
ABIContractDecoder,
ABIContractType, ABIContractType,
abi_decode_single, abi_decode_single,
) )
@@ -38,6 +39,15 @@ class DemurrageTokenSettings:
self.sink_address = None self.sink_address = None
def __str__(self):
return 'name {} demurrage level {} period minutes {} sink address {}'.format(
self.name,
self.demurrage_level,
self.period_minutes,
self.sink_address,
)
class DemurrageToken(ERC20): class DemurrageToken(ERC20):
__abi = {} __abi = {}
@@ -108,6 +118,34 @@ class DemurrageToken(ERC20):
return DemurrageToken.__bytecode[name] 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): def add_minter(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
enc = ABIContractEncoder() enc = ABIContractEncoder()
enc.method('addMinter') enc.method('addMinter')
@@ -146,6 +184,33 @@ class DemurrageToken(ERC20):
return tx return tx
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): def to_base_amount(self, contract_address, value, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator) j = JSONRPCRequest(id_generator)
o = j.template() o = j.template()
@@ -246,8 +311,11 @@ class DemurrageToken(ERC20):
o['method'] = 'eth_call' o['method'] = 'eth_call'
enc = ABIContractEncoder() enc = ABIContractEncoder()
enc.method('toRedistributionPeriod') enc.method('toRedistributionPeriod')
enc.typ(ABIContractType.BYTES32) v = strip_0x(redistribution)
enc.bytes32(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()) data = add_0x(enc.get())
tx = self.template(sender_address, contract_address) tx = self.template(sender_address, contract_address)
tx = self.set_code(tx, data) tx = self.set_code(tx, data)
@@ -257,22 +325,26 @@ class DemurrageToken(ERC20):
return o return o
def to_redistribution_participants(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None): # def to_redistribution_participants(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator) # j = JSONRPCRequest(id_generator)
o = j.template() # o = j.template()
o['method'] = 'eth_call' # o['method'] = 'eth_call'
enc = ABIContractEncoder() # enc = ABIContractEncoder()
enc.method('toRedistributionParticipants') # enc.method('toRedistributionParticipants')
enc.typ(ABIContractType.BYTES32) # v = strip_0x(redistribution)
enc.bytes32(redistribution) # enc.typ_literal('(uint32,uint72,uint104)')
data = add_0x(enc.get()) # #enc.typ(ABIContractType.BYTES32)
tx = self.template(sender_address, contract_address) # enc.bytes32(v[:64])
tx = self.set_code(tx, data) # enc.bytes32(v[64:128])
o['params'].append(self.normalize(tx)) # enc.bytes32(v[128:192])
o['params'].append('latest') # data = add_0x(enc.get())
o = j.finalize(o) # tx = self.template(sender_address, contract_address)
return o # 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, id_generator=None): def to_redistribution_supply(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator) j = JSONRPCRequest(id_generator)
@@ -280,8 +352,11 @@ class DemurrageToken(ERC20):
o['method'] = 'eth_call' o['method'] = 'eth_call'
enc = ABIContractEncoder() enc = ABIContractEncoder()
enc.method('toRedistributionSupply') enc.method('toRedistributionSupply')
enc.typ(ABIContractType.BYTES32) v = strip_0x(redistribution)
enc.bytes32(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()) data = add_0x(enc.get())
tx = self.template(sender_address, contract_address) tx = self.template(sender_address, contract_address)
tx = self.set_code(tx, data) tx = self.set_code(tx, data)
@@ -297,8 +372,11 @@ class DemurrageToken(ERC20):
o['method'] = 'eth_call' o['method'] = 'eth_call'
enc = ABIContractEncoder() enc = ABIContractEncoder()
enc.method('toRedistributionDemurrageModifier') enc.method('toRedistributionDemurrageModifier')
enc.typ(ABIContractType.BYTES32) v = strip_0x(redistribution)
enc.bytes32(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()) data = add_0x(enc.get())
tx = self.template(sender_address, contract_address) tx = self.template(sender_address, contract_address)
tx = self.set_code(tx, data) tx = self.set_code(tx, data)
@@ -325,9 +403,20 @@ class DemurrageToken(ERC20):
return o return o
def apply_demurrage(self, contract_address, sender_address): def apply_demurrage(self, contract_address, sender_address, limit=0, tx_format=TxFormat.JSONRPC):
if limit == 0:
return self.transact_noarg('applyDemurrage', contract_address, sender_address) return self.transact_noarg('applyDemurrage', contract_address, sender_address)
enc = ABIContractEncoder()
enc.method('applyDemurrageLimited')
enc.typ(ABIContractType.UINT256)
enc.uint256(limit)
data = enc.get()
tx = self.template(sender_address, contract_address, use_nonce=True)
tx = self.set_code(tx, data)
tx = self.finalize(tx, tx_format)
return tx
def change_period(self, contract_address, sender_address): def change_period(self, contract_address, sender_address):
return self.transact_noarg('changePeriod', contract_address, sender_address) return self.transact_noarg('changePeriod', contract_address, sender_address)
@@ -369,28 +458,32 @@ class DemurrageToken(ERC20):
return self.call_noarg('demurrageAmount', contract_address, sender_address=sender_address) 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): def supply_cap(self, contract_address, sender_address=ZERO_ADDRESS):
return self.call_noarg('supplyCap', contract_address, sender_address=sender_address) return self.call_noarg('supplyCap', contract_address, sender_address=sender_address)
def grow_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS, id_generator=None): # def grow_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator) # j = JSONRPCRequest(id_generator)
o = j.template() # o = j.template()
o['method'] = 'eth_call' # o['method'] = 'eth_call'
enc = ABIContractEncoder() # enc = ABIContractEncoder()
enc.method('growBy') # enc.method('growBy')
enc.typ(ABIContractType.UINT256) # enc.typ(ABIContractType.UINT256)
enc.typ(ABIContractType.UINT256) # enc.typ(ABIContractType.UINT256)
enc.uint256(value) # enc.uint256(value)
enc.uint256(period) # enc.uint256(period)
data = add_0x(enc.get()) # data = add_0x(enc.get())
tx = self.template(sender_address, contract_address) # tx = self.template(sender_address, contract_address)
tx = self.set_code(tx, data) # tx = self.set_code(tx, data)
o['params'].append(self.normalize(tx)) # o['params'].append(self.normalize(tx))
o['params'].append('latest') # o['params'].append('latest')
o = j.finalize(o) # o = j.finalize(o)
return o # return o
#
def decay_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS, id_generator=None): def decay_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator) j = JSONRPCRequest(id_generator)
@@ -436,8 +529,11 @@ class DemurrageToken(ERC20):
o['method'] = 'eth_call' o['method'] = 'eth_call'
enc = ABIContractEncoder() enc = ABIContractEncoder()
enc.method('getDistributionFromRedistribution') enc.method('getDistributionFromRedistribution')
enc.typ(ABIContractType.BYTES32) v = strip_0x(redistribution)
enc.bytes32(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()) data = add_0x(enc.get())
tx = self.template(sender_address, contract_address) tx = self.template(sender_address, contract_address)
tx = self.set_code(tx, data) tx = self.set_code(tx, data)
@@ -480,7 +576,16 @@ class DemurrageToken(ERC20):
@classmethod @classmethod
def parse_redistributions(self, v): 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 @classmethod
@@ -502,6 +607,7 @@ class DemurrageToken(ERC20):
def parse_supply_cap(self, v): def parse_supply_cap(self, v):
return abi_decode_single(ABIContractType.UINT256, v) return abi_decode_single(ABIContractType.UINT256, v)
@classmethod @classmethod
def parse_grow_by(self, v): def parse_grow_by(self, v):
return abi_decode_single(ABIContractType.UINT256, v) return abi_decode_single(ABIContractType.UINT256, v)
@@ -525,3 +631,8 @@ class DemurrageToken(ERC20):
@classmethod @classmethod
def parse_resolution_factor(self, v): def parse_resolution_factor(self, v):
return abi_decode_single(ABIContractType.UINT256, v) return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
def parse_total_burned(self, v):
return abi_decode_single(ABIContractType.UINT256, v)

View File

@@ -32,18 +32,19 @@ PERIOD = 10
class TestTokenDeploy: class TestTokenDeploy:
def __init__(self, rpc, token_symbol='FOO', token_name='Foo Token', sink_address=ZERO_ADDRESS, supply=10**12): 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.tax_level = tax_level
self.period_seconds = PERIOD * 60 self.period_seconds = period * 60
self.settings = DemurrageTokenSettings() self.settings = DemurrageTokenSettings()
self.settings.name = token_name self.settings.name = token_name
self.settings.symbol = token_symbol self.settings.symbol = token_symbol
self.settings.decimals = 6 self.settings.decimals = 6
self.settings.demurrage_level = TAX_LEVEL * (10 ** 32) self.settings.demurrage_level = tax_level * (10 ** 32)
self.settings.period_minutes = PERIOD self.settings.period_minutes = period
self.settings.sink_address = sink_address self.settings.sink_address = sink_address
self.sink_address = self.settings.sink_address self.sink_address = self.settings.sink_address
logg.debug('using demurrage token settings: {}'.format(self.settings))
o = block_latest() o = block_latest()
self.start_block = rpc.do(o) self.start_block = rpc.do(o)
@@ -101,7 +102,12 @@ class TestDemurrage(EthTesterCase):
# self.start_time = token_deploy.start_time # self.start_time = token_deploy.start_time
# self.default_supply = self.default_supply # self.default_supply = self.default_supply
# self.default_supply_cap = self.default_supply_cap # self.default_supply_cap = self.default_supply_cap
self.deployer = TestTokenDeploy(self.rpc) 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 = self.deployer.default_supply
self.default_supply_cap = self.deployer.default_supply_cap self.default_supply_cap = self.deployer.default_supply_cap
self.start_block = None self.start_block = None
@@ -203,10 +209,11 @@ class TestDemurrageCap(TestDemurrage):
class TestDemurrageUnit(TestDemurrage): class TestDemurrageUnit(TestDemurrage):
def setUp(self): 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 super(TestDemurrageUnit, self).setUp()
self.period_seconds = 60
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
self.settings = DemurrageTokenSettings() self.settings = DemurrageTokenSettings()

View File

@@ -1,3 +1,3 @@
chainlib>=0.0.9a3,<=0.1.0 chainlib-eth>=0.1.0,<0.2.0
eth-erc20>=0.1.2a1,<0.2.0 eth-erc20~=0.3.0
crypto-dev-signer>=0.4.15a1,<=0.4.15 funga-eth~=0.6.0

View File

@@ -6,7 +6,8 @@ set -e
export PYTHONPATH=. export PYTHONPATH=.
#modes=(MultiNocap MultiCap SingleCap SingleNocap) #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 for m in ${modes[@]}; do
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_basic.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_growth.py
@@ -14,13 +15,20 @@ 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_single.py
done done
modes=(SingleCap) # other contracts need to be updted #modes=(SingleCap) # other contracts need to be updted
modes=()
for m in ${modes[@]}; do for m in ${modes[@]}; do
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_period.py ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_period.py
# ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution_unit.py
done done
modes=(MultiCap SingleCap) 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 for m in ${modes[@]}; do
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_cap.py ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_cap.py
done done

View File

@@ -1,6 +1,6 @@
[metadata] [metadata]
name = erc20-demurrage-token name = erc20-demurrage-token
version = 0.0.5a2 version = 0.1.1
description = ERC20 token with redistributed continual demurrage description = ERC20 token with redistributed continual demurrage
author = Louis Holbrook author = Louis Holbrook
author_email = dev@holbrook.no author_email = dev@holbrook.no
@@ -25,12 +25,13 @@ licence_files =
[options] [options]
include_package_data = True include_package_data = True
python_requires = >= 3.6 python_requires = >= 3.7
packages = packages =
erc20_demurrage_token erc20_demurrage_token
erc20_demurrage_token.runnable erc20_demurrage_token.runnable
erc20_demurrage_token.data erc20_demurrage_token.data
erc20_demurrage_token.sim erc20_demurrage_token.sim
erc20_demurrage_token.unittest
[options.package_data] [options.package_data]
* = * =
@@ -40,3 +41,4 @@ packages =
[options.entry_points] [options.entry_points]
console_scripts = console_scripts =
erc20-demurrage-token-deploy = erc20_demurrage_token.runnable.deploy:main erc20-demurrage-token-deploy = erc20_demurrage_token.runnable.deploy:main
erc20-demurrage-token-apply = erc20_demurrage_token.runnable.apply:main

View File

@@ -28,179 +28,205 @@ testdir = os.path.dirname(__file__)
class TestBasic(TestDemurrageDefault): class TestBasic(TestDemurrageDefault):
# def test_hello(self): def test_hello(self):
# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
# o = c.actual_period(self.address, sender_address=self.accounts[0]) o = c.actual_period(self.address, sender_address=self.accounts[0])
# r = self.rpc.do(o) r = self.rpc.do(o)
#
# self.backend.time_travel(self.start_time + self.period_seconds + 1) self.backend.time_travel(self.start_time + self.period_seconds + 1)
# o = c.actual_period(self.address, sender_address=self.accounts[0]) o = c.actual_period(self.address, sender_address=self.accounts[0])
# r = self.rpc.do(o) r = self.rpc.do(o)
#
#
# def test_balance(self): def test_balance(self):
# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
# (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1024) (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1024)
# r = self.rpc.do(o) r = self.rpc.do(o)
# o = receipt(tx_hash) o = receipt(tx_hash)
# r = self.rpc.do(o) r = self.rpc.do(o)
# self.assertEqual(r['status'], 1) self.assertEqual(r['status'], 1)
#
# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
# r = self.rpc.do(o) r = self.rpc.do(o)
# balance = c.parse_balance_of(r) balance = c.parse_balance_of(r)
# self.assertEqual(balance, 1024) self.assertEqual(balance, 1024)
#
#
# def test_apply_demurrage(self): def test_apply_demurrage_limited(self):
# modifier = (10 ** 38) modifier = (10 ** 28)
#
# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
#
# o = c.demurrage_amount(self.address, sender_address=self.accounts[0]) o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
# r = self.rpc.do(o) r = self.rpc.do(o)
# demurrage_amount = c.parse_demurrage_amount(r) demurrage_amount = c.parse_demurrage_amount(r)
# self.assertEqual(modifier, demurrage_amount) self.assertEqual(modifier, demurrage_amount)
#
# o = block_latest() self.backend.time_travel(self.start_time + 120)
# r = self.rpc.do(o) (tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0], limit=1)
# o = block_by_number(r) r = self.rpc.do(o)
# b = self.rpc.do(o) o = receipt(tx_hash)
# logg.debug('block {} start {}'.format(b['timestamp'], self.start_time)) r = self.rpc.do(o)
# self.assertEqual(r['status'], 1)
# self.backend.time_travel(self.start_time + 2)
# (tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0]) o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
# r = self.rpc.do(o) r = self.rpc.do(o)
# o = receipt(tx_hash) demurrage_amount = c.parse_demurrage_amount(r)
# r = self.rpc.do(o) modifier_base = 1000000 - self.tax_level
# self.assertEqual(r['status'], 1) modifier = int(modifier_base * (10 ** 22)) # 38 decimal places minus 6 (1000000)
# self.assertEqual(modifier, demurrage_amount)
# o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
# r = self.rpc.do(o)
# demurrage_amount = c.parse_demurrage_amount(r) def test_apply_demurrage(self):
# self.assertEqual(modifier, demurrage_amount) modifier = (10 ** 28)
#
# self.backend.time_travel(self.start_time + 61) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
# (tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0]) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
# r = self.rpc.do(o)
# o = receipt(tx_hash) o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
# r = self.rpc.do(o) r = self.rpc.do(o)
# self.assertEqual(r['status'], 1) demurrage_amount = c.parse_demurrage_amount(r)
# o = c.demurrage_amount(self.address, sender_address=self.accounts[0]) self.assertEqual(modifier, demurrage_amount)
# r = self.rpc.do(o)
# demurrage_amount = c.parse_demurrage_amount(r) o = block_latest()
# modifier_base = 1000000 - self.tax_level r = self.rpc.do(o)
# logg.debug('modifier base {}'.format(modifier_base)) o = block_by_number(r)
# modifier = int(modifier_base * (10 ** 32)) # 38 decimal places minus 6 (1000000) b = self.rpc.do(o)
# self.assertEqual(modifier, demurrage_amount) logg.debug('block {} start {}'.format(b['timestamp'], self.start_time))
#
# self.backend.time_travel(self.start_time + 601) self.backend.time_travel(self.start_time + 2)
# (tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0]) (tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0])
# r = self.rpc.do(o) r = self.rpc.do(o)
# o = receipt(tx_hash) o = receipt(tx_hash)
# r = self.rpc.do(o) r = self.rpc.do(o)
# self.assertEqual(r['status'], 1) self.assertEqual(r['status'], 1)
# o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
# r = self.rpc.do(o) o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
# demurrage_amount = c.parse_demurrage_amount(r) r = self.rpc.do(o)
# modifier_base = ((1000000 - self.tax_level) / 1000000) ** 10 demurrage_amount = c.parse_demurrage_amount(r)
# modifier = int(modifier_base * (10 ** 12)) self.assertEqual(modifier, demurrage_amount)
#
# rounding_tolerance_nano = 4000000 # 0.000004% precision self.backend.time_travel(self.start_time + 61)
# demurrage_amount_truncate = int(demurrage_amount / (10 ** 26)) # equals 12 decimal places (tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0])
# self.assertGreaterEqual(modifier, demurrage_amount_truncate - rounding_tolerance_nano) r = self.rpc.do(o)
# self.assertLessEqual(modifier, demurrage_amount_truncate) o = receipt(tx_hash)
# r = self.rpc.do(o)
# self.assertEqual(r['status'], 1)
# def test_mint(self): o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) r = self.rpc.do(o)
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) demurrage_amount = c.parse_demurrage_amount(r)
# (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1024) modifier_base = 1000000 - self.tax_level
# r = self.rpc.do(o) modifier = int(modifier_base * (10 ** 22)) # 38 decimal places minus 6 (1000000)
# o = receipt(tx_hash) self.assertEqual(modifier, demurrage_amount)
# r = self.rpc.do(o)
# self.assertEqual(r['status'], 1) self.backend.time_travel(self.start_time + 601)
# (tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0])
# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) r = self.rpc.do(o)
# r = self.rpc.do(o) o = receipt(tx_hash)
# balance = c.parse_balance_of(r) r = self.rpc.do(o)
# self.assertEqual(balance, 1024) self.assertEqual(r['status'], 1)
# o = c.demurrage_amount(self.address, sender_address=self.accounts[0])
# (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 976) r = self.rpc.do(o)
# r = self.rpc.do(o) demurrage_amount = c.parse_demurrage_amount(r)
# o = receipt(tx_hash) modifier_base = ((1000000 - self.tax_level) / 1000000) ** 10
# r = self.rpc.do(o) logg.warning('mod base {}'.format(modifier_base))
# self.assertEqual(r['status'], 1) modifier = int(modifier_base * (10 ** 12))
#
# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) rounding_tolerance_nano = 4000000 # 0.000004% precision
# r = self.rpc.do(o) 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
# balance = c.parse_balance_of(r) self.assertGreaterEqual(modifier, demurrage_amount_truncate - rounding_tolerance_nano)
# self.assertEqual(balance, 2000) self.assertLessEqual(modifier, demurrage_amount_truncate)
#
#
# self.backend.time_travel(self.start_time + 61) def test_mint(self):
# (tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0]) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
# r = self.rpc.do(o) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1024)
# r = self.rpc.do(o) r = self.rpc.do(o)
# balance = c.parse_balance_of(r) o = receipt(tx_hash)
# self.assertEqual(balance, int(2000 * 0.98)) r = self.rpc.do(o)
# self.assertEqual(r['status'], 1)
#
# def test_minter_control(self): o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
# nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) r = self.rpc.do(o)
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) balance = c.parse_balance_of(r)
# self.assertEqual(balance, 1024)
# (tx_hash, o) = c.mint_to(self.address, self.accounts[1], self.accounts[2], 1024)
# self.rpc.do(o) (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 976)
# o = receipt(tx_hash) r = self.rpc.do(o)
# r = self.rpc.do(o) o = receipt(tx_hash)
# self.assertEqual(r['status'], 0) r = self.rpc.do(o)
# self.assertEqual(r['status'], 1)
# (tx_hash, o) = c.add_minter(self.address, self.accounts[1], self.accounts[1])
# self.rpc.do(o) o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
# o = receipt(tx_hash) r = self.rpc.do(o)
# r = self.rpc.do(o) balance = c.parse_balance_of(r)
# self.assertEqual(r['status'], 0) self.assertEqual(balance, 2000)
#
# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) self.backend.time_travel(self.start_time + 61)
# (tx_hash, o) = c.add_minter(self.address, self.accounts[0], self.accounts[1]) (tx_hash, o) = c.apply_demurrage(self.address, sender_address=self.accounts[0])
# self.rpc.do(o) r = self.rpc.do(o)
# o = receipt(tx_hash) o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
# r = self.rpc.do(o) r = self.rpc.do(o)
# self.assertEqual(r['status'], 1) balance = c.parse_balance_of(r)
# self.assertEqual(balance, int(2000 * 0.98))
# 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) def test_minter_control(self):
# self.rpc.do(o) nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
# o = receipt(tx_hash) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
# 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)
# (tx_hash, o) = c.add_minter(self.address, self.accounts[1], self.accounts[2]) o = receipt(tx_hash)
# self.rpc.do(o) r = self.rpc.do(o)
# o = receipt(tx_hash) self.assertEqual(r['status'], 0)
# 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)
# (tx_hash, o) = c.remove_minter(self.address, self.accounts[1], self.accounts[1]) o = receipt(tx_hash)
# self.rpc.do(o) r = self.rpc.do(o)
# o = receipt(tx_hash) self.assertEqual(r['status'], 0)
# r = self.rpc.do(o)
# self.assertEqual(r['status'], 1) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
# (tx_hash, o) = c.mint_to(self.address, self.accounts[1], self.accounts[2], 1024) (tx_hash, o) = c.add_minter(self.address, self.accounts[0], self.accounts[1])
# self.rpc.do(o) self.rpc.do(o)
# o = receipt(tx_hash) o = receipt(tx_hash)
# r = self.rpc.do(o) r = self.rpc.do(o)
# self.assertEqual(r['status'], 0) 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): def test_base_amount(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
@@ -216,78 +242,129 @@ class TestBasic(TestDemurrageDefault):
amount = c.parse_to_base_amount(r) amount = c.parse_to_base_amount(r)
self.assertEqual(amount, 1020) self.assertEqual(amount, 1020)
#
# def test_transfer(self): def test_transfer(self):
# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
#
# (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1024) (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1024)
# self.rpc.do(o) self.rpc.do(o)
#
# nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc)
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
# (tx_hash, o) = c.transfer(self.address, self.accounts[1], self.accounts[2], 500) (tx_hash, o) = c.transfer(self.address, self.accounts[1], self.accounts[2], 500)
# self.rpc.do(o) self.rpc.do(o)
# o = receipt(tx_hash) o = receipt(tx_hash)
# r = self.rpc.do(o) r = self.rpc.do(o)
# self.assertEqual(r['status'], 1) self.assertEqual(r['status'], 1)
#
# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
# r = self.rpc.do(o) r = self.rpc.do(o)
# balance = c.parse_balance_of(r) balance = c.parse_balance_of(r)
# self.assertEqual(balance, 524) self.assertEqual(balance, 524)
#
# o = c.balance_of(self.address, self.accounts[2], sender_address=self.accounts[0]) o = c.balance_of(self.address, self.accounts[2], sender_address=self.accounts[0])
# r = self.rpc.do(o) r = self.rpc.do(o)
# balance = c.parse_balance_of(r) balance = c.parse_balance_of(r)
# self.assertEqual(balance, 500) self.assertEqual(balance, 500)
#
# nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc)
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
# (tx_hash, o) = c.transfer(self.address, self.accounts[2], self.accounts[1], 500) (tx_hash, o) = c.transfer(self.address, self.accounts[2], self.accounts[1], 500)
# self.rpc.do(o) self.rpc.do(o)
# o = receipt(tx_hash) o = receipt(tx_hash)
# r = self.rpc.do(o) r = self.rpc.do(o)
# self.assertEqual(r['status'], 1) self.assertEqual(r['status'], 1)
#
#
# def test_approve(self):
# def test_transfer_from(self): nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) (tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 500)
# (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1024) self.rpc.do(o)
# self.rpc.do(o) o = receipt(tx_hash)
# r = self.rpc.do(o)
# nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) self.assertEqual(r['status'], 1)
# 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) (tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 600)
# self.rpc.do(o) self.rpc.do(o)
# o = receipt(tx_hash) o = receipt(tx_hash)
# r = self.rpc.do(o) r = self.rpc.do(o)
# self.assertEqual(r['status'], 1) self.assertEqual(r['status'], 0)
#
# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) (tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 0)
# r = self.rpc.do(o) self.rpc.do(o)
# balance = c.parse_balance_of(r) o = receipt(tx_hash)
# self.assertEqual(balance, 1024) r = self.rpc.do(o)
# self.assertEqual(r['status'], 1)
# nonce_oracle = RPCNonceOracle(self.accounts[2], 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], 600)
# (tx_hash, o) = c.transfer_from(self.address, self.accounts[2], self.accounts[1], self.accounts[3], 500) self.rpc.do(o)
# self.rpc.do(o) o = receipt(tx_hash)
# o = receipt(tx_hash) r = self.rpc.do(o)
# r = self.rpc.do(o) self.assertEqual(r['status'], 1)
# self.assertEqual(r['status'], 1)
# (tx_hash, o) = c.increase_allowance(self.address, self.accounts[0], self.accounts[1], 200)
# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) self.rpc.do(o)
# r = self.rpc.do(o) o = receipt(tx_hash)
# balance = c.parse_balance_of(r) r = self.rpc.do(o)
# self.assertEqual(balance, 524) self.assertEqual(r['status'], 1)
#
# o = c.balance_of(self.address, self.accounts[3], sender_address=self.accounts[0]) (tx_hash, o) = c.decrease_allowance(self.address, self.accounts[0], self.accounts[1], 800)
# r = self.rpc.do(o) self.rpc.do(o)
# balance = c.parse_balance_of(r) o = receipt(tx_hash)
# self.assertEqual(balance, 500) 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__': if __name__ == '__main__':

View File

@@ -28,23 +28,23 @@ testdir = os.path.dirname(__file__)
class TestGrowth(TestDemurrageDefault): class TestGrowth(TestDemurrageDefault):
def test_grow_by(self): # def test_grow_by(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) # nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) # c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
#
growth_factor = (1000000 + self.tax_level) / 1000000 # growth_factor = (1000000 + self.tax_level) / 1000000
v = 1000000000 # v = 1000000000
o = c.grow_by(self.address, v, 1, sender_address=self.accounts[0]) # o = c.grow_by(self.address, v, 1, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
g = c.parse_grow_by(r) # g = c.parse_grow_by(r)
self.assertEqual(int(v * growth_factor), g) # self.assertEqual(int(v * growth_factor), g)
#
period = 10 # period = 10
growth_factor = (1 + (self.tax_level) / 1000000) ** period # growth_factor = (1 + (self.tax_level) / 1000000) ** period
o = c.grow_by(self.address, v, period, sender_address=self.accounts[0]) # o = c.grow_by(self.address, v, period, sender_address=self.accounts[0])
r = self.rpc.do(o) # r = self.rpc.do(o)
g = c.parse_grow_by(r) # g = c.parse_grow_by(r)
self.assertEqual(int(v * growth_factor), g) # self.assertEqual(int(v * growth_factor), g)
def test_decay_by(self): def test_decay_by(self):

View File

@@ -7,7 +7,15 @@ import logging
# external imports # external imports
from chainlib.eth.constant import ZERO_ADDRESS from chainlib.eth.constant import ZERO_ADDRESS
from chainlib.eth.nonce import RPCNonceOracle from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.tx import receipt from chainlib.eth.tx import (
receipt,
TxFactory,
TxFormat,
)
from chainlib.eth.contract import (
ABIContractEncoder,
ABIContractType,
)
# local imports # local imports
from erc20_demurrage_token import DemurrageToken from erc20_demurrage_token import DemurrageToken
@@ -103,5 +111,90 @@ class TestPeriod(TestDemurrageDefault):
self.assertEqual(modifier, period) self.assertEqual(modifier, period)
def test_change_sink(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
o = c.balance_of(self.address, ZERO_ADDRESS, sender_address=self.accounts[0])
r = self.rpc.do(o)
balance = c.parse_balance_of(r)
self.assertEqual(balance, 0)
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 102400000000)
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
self.backend.time_travel(self.start_time + self.period_seconds + 1)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.change_period(self.address, self.accounts[0])
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
o = c.balance_of(self.address, ZERO_ADDRESS, sender_address=self.accounts[0])
r = self.rpc.do(o)
balance = c.parse_balance_of(r)
self.assertGreater(balance, 0)
old_sink_balance = balance
o = c.balance_of(self.address, self.accounts[3], sender_address=self.accounts[0])
r = self.rpc.do(o)
balance = c.parse_balance_of(r)
self.assertEqual(balance, 0)
nonce_oracle = RPCNonceOracle(self.accounts[5], self.rpc)
c = TxFactory(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
enc = ABIContractEncoder()
enc.method('setSinkAddress')
enc.typ(ABIContractType.ADDRESS)
enc.address(self.accounts[3])
data = enc.get()
o = c.template(self.accounts[5], self.address, use_nonce=True)
o = c.set_code(o, data)
(tx_hash, o) = c.finalize(o, TxFormat.JSONRPC)
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 0)
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = TxFactory(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
enc = ABIContractEncoder()
enc.method('setSinkAddress')
enc.typ(ABIContractType.ADDRESS)
enc.address(self.accounts[3])
data = enc.get()
o = c.template(self.accounts[0], self.address, use_nonce=True)
o = c.set_code(o, data)
(tx_hash, o) = c.finalize(o, TxFormat.JSONRPC)
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
self.backend.time_travel(self.start_time + (self.period_seconds * 2) + 1)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
(tx_hash, o) = c.change_period(self.address, self.accounts[0])
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
o = c.balance_of(self.address, ZERO_ADDRESS, sender_address=self.accounts[0])
r = self.rpc.do(o)
balance = c.parse_balance_of(r)
self.assertLess(balance, old_sink_balance)
o = c.balance_of(self.address, self.accounts[3], sender_address=self.accounts[0])
r = self.rpc.do(o)
balance = c.parse_balance_of(r)
self.assertGreater(balance, 0)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@@ -31,6 +31,8 @@ testdir = os.path.dirname(__file__)
class TestRedistribution(TestDemurrageDefault): class TestRedistribution(TestDemurrageDefault):
def test_whole_is_parts(self): def test_whole_is_parts(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)

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

View File

@@ -31,12 +31,12 @@ testdir = os.path.dirname(__file__)
class TestRedistribution(TestDemurrageUnit): class TestRedistribution(TestDemurrageUnit):
# TODO: move to "pure" test file when getdistribution is implemented in all contracts # TODO: move to "pure" test file when getdistribution is implemented in all contracts
def test_distribution(self): def test_distribution_direct(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
#demurrage = (1 - (self.tax_level / 1000000)) * (10**38)
demurrage = (1 - (self.tax_level / 1000000)) * (10**28) demurrage = (1 - (self.tax_level / 1000000)) * (10**28)
supply = self.default_supply supply = self.default_supply
@@ -51,21 +51,23 @@ class TestRedistribution(TestDemurrageUnit):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
#demurrage = (1 - (self.tax_level / 1000000)) * (10**38) demurrage = (1 - (self.tax_level / 100000)) * (10**28)
demurrage = (1 - (self.tax_level / 1000000)) * (10**28)
logg.debug('demurrage {}'.format(demurrage))
supply = self.default_supply supply = self.default_supply
o = c.to_redistribution(self.address, 0, demurrage, supply, 1, sender_address=self.accounts[0]) o = c.to_redistribution(self.address, 0, demurrage, supply, 2, sender_address=self.accounts[0])
redistribution = self.rpc.do(o) redistribution = self.rpc.do(o)
o = c.get_distribution_from_redistribution(self.address, redistribution, self.accounts[0]) o = c.get_distribution_from_redistribution(self.address, redistribution, self.accounts[0])
r = self.rpc.do(o) r = self.rpc.do(o)
distribution = c.parse_get_distribution(r) distribution = c.parse_get_distribution(r)
expected_distribution = self.default_supply * (self.tax_level / 1000000) expected_distribution = (self.default_supply * self.tax_level) / 100000
logg.debug('distribution {} supply {}'.format(distribution, self.default_supply))
self.assert_within_lower(distribution, expected_distribution, 1000) self.assert_within_lower(distribution, expected_distribution, 1000)
def test_single_step(self): def test_single_step_basic(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
@@ -81,6 +83,12 @@ class TestRedistribution(TestDemurrageUnit):
expected_balance = int(mint_amount - ((self.tax_level / 1000000) * mint_amount)) 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]) o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0])
r = self.rpc.do(o) r = self.rpc.do(o)
balance = c.parse_balance(r) balance = c.parse_balance(r)
@@ -169,15 +177,13 @@ class TestRedistribution(TestDemurrageUnit):
o = c.redistributions(self.address, 0, sender_address=self.accounts[0]) o = c.redistributions(self.address, 0, sender_address=self.accounts[0])
redistribution = self.rpc.do(o) redistribution = self.rpc.do(o)
logg.debug('redistribution {}'.format(redistribution))
o = c.to_redistribution_supply(self.address, redistribution, sender_address=self.accounts[0]) o = c.to_redistribution_supply(self.address, redistribution, sender_address=self.accounts[0])
r = self.rpc.do(o) r = self.rpc.do(o)
supply = c.parse_to_redistribution_item(r) supply = c.parse_to_redistribution_item(r)
o = c.to_redistribution_demurrage_modifier(self.address, redistribution, sender_address=self.accounts[0]) o = c.to_redistribution_demurrage_modifier(self.address, redistribution, sender_address=self.accounts[0])
r = self.rpc.do(o) r = self.rpc.do(o)
demurrage = c.parse_to_redistribution_item(r) 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)) logg.debug('\nrediistribution {}\ndemurrage {}\nsupply {}'.format(redistribution, demurrage, supply))
expected_balance = int(supply * (self.tax_level / 1000000)) expected_balance = int(supply * (self.tax_level / 1000000))

View File

@@ -128,6 +128,12 @@ contract DemurrageTokenSingleCap {
minimumParticipantSpend = 10 ** uint256(_decimals); minimumParticipantSpend = 10 ** uint256(_decimals);
} }
// Change sink address for redistribution
function setSinkAddress(address _sinkAddress) public {
require(msg.sender == owner);
sinkAddress = _sinkAddress;
}
// Given address will be allowed to call the mintTo() function // Given address will be allowed to call the mintTo() function
function addMinter(address _minter) public returns (bool) { function addMinter(address _minter) public returns (bool) {
require(msg.sender == owner); require(msg.sender == owner);
@@ -311,6 +317,10 @@ contract DemurrageTokenSingleCap {
// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call // Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
function applyDemurrage() public returns (bool) { function applyDemurrage() public returns (bool) {
return applyDemurrageLimited(0);
}
function applyDemurrageLimited(uint256 _rounds) public returns (bool) {
//uint128 epochPeriodCount; //uint128 epochPeriodCount;
uint256 periodCount; uint256 periodCount;
uint256 lastDemurrageAmount; uint256 lastDemurrageAmount;
@@ -323,6 +333,12 @@ contract DemurrageTokenSingleCap {
return false; return false;
} }
lastDemurrageAmount = demurrageAmount; lastDemurrageAmount = demurrageAmount;
// safety limit for exponential calculation to ensure that we can always
// execute this code no matter how much time passes.
if (_rounds > 0 && _rounds < periodCount) {
periodCount = _rounds;
}
demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount)); demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount));
//demurragePeriod = epochPeriodCount; //demurragePeriod = epochPeriodCount;
demurrageTimestamp = demurrageTimestamp + (periodCount * 60); demurrageTimestamp = demurrageTimestamp + (periodCount * 60);

View File

@@ -1,18 +1,15 @@
pragma solidity > 0.6.11; pragma solidity >= 0.8.0;
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
contract DemurrageTokenSingleCap { contract DemurrageTokenSingleCap {
// Redistribution bit field, with associated shifts and masks struct redistributionItem {
// (Uses sub-byte boundaries) uint32 period;
bytes32[] public redistributions; // uint51(unused) | uint64(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period) uint72 value;
uint8 constant shiftRedistributionPeriod = 0; uint104 demurrage;
uint256 constant maskRedistributionPeriod = 0x00000000000000000000000000000000000000000000000000000000ffffffff; // (1 << 32) - 1 }
uint8 constant shiftRedistributionValue = 32; redistributionItem[] public redistributions; // uint51(unused) | uint64(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period)
uint256 constant maskRedistributionValue = 0x00000000000000000000000000000000000000ffffffffffffffffff00000000; // ((1 << 72) - 1) << 32
uint8 constant shiftRedistributionDemurrage = 104;
uint256 constant maskRedistributionDemurrage = 0x0000000000ffffffffffffffffffffffffffff00000000000000000000000000; // ((1 << 20) - 1) << 140
// Account balances // Account balances
mapping (address => uint256) account; mapping (address => uint256) account;
@@ -20,8 +17,6 @@ contract DemurrageTokenSingleCap {
// Cached demurrage amount, ppm with 38 digit resolution // Cached demurrage amount, ppm with 38 digit resolution
uint128 public demurrageAmount; 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 // Cached demurrage timestamp; the timestamp for which demurrageAmount was last calculated
uint256 public demurrageTimestamp; uint256 public demurrageTimestamp;
@@ -40,10 +35,17 @@ contract DemurrageTokenSingleCap {
uint256 public decimals; uint256 public decimals;
// Implements ERC20 // 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 // Last executed period
uint256 public minimumParticipantSpend; 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 // 128 bit resolution of the demurrage divisor
// (this constant x 1000000 is contained within 128 bits) // (this constant x 1000000 is contained within 128 bits)
@@ -71,7 +73,7 @@ contract DemurrageTokenSingleCap {
mapping (address => mapping (address => uint256 ) ) allowance; // holder -> spender -> amount (amount is subject to demurrage) mapping (address => mapping (address => uint256 ) ) allowance; // holder -> spender -> amount (amount is subject to demurrage)
// Address to send unallocated redistribution tokens // Address to send unallocated redistribution tokens
address sinkAddress; address public sinkAddress;
// Implements ERC20 // Implements ERC20
event Transfer(address indexed _from, address indexed _to, uint256 _value); event Transfer(address indexed _from, address indexed _to, uint256 _value);
@@ -94,10 +96,15 @@ contract DemurrageTokenSingleCap {
// Temporary event used in development, will be removed on prod // Temporary event used in development, will be removed on prod
event Debug(bytes32 _foo); event Debug(bytes32 _foo);
// Emitted when tokens are burned
event Burn(address indexed _burner, uint256 _value);
// EIP173 // EIP173
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // 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 { constructor(string memory _name, string memory _symbol, uint8 _decimals, uint128 _taxLevelMinute, uint256 _periodMinutes, address _defaultSinkAddress) public {
redistributionItem memory initialRedistribution;
// ACL setup // ACL setup
owner = msg.sender; owner = msg.sender;
minter[owner] = true; minter[owner] = true;
@@ -111,17 +118,20 @@ contract DemurrageTokenSingleCap {
demurrageTimestamp = block.timestamp; demurrageTimestamp = block.timestamp;
periodStart = demurrageTimestamp; periodStart = demurrageTimestamp;
periodDuration = _periodMinutes * 60; periodDuration = _periodMinutes * 60;
//demurrageAmount = 100000000000000000000000000000000000000 - _taxLevelMinute; // Represents 38 decimal places, same as resolutionFactor demurrageAmount = uint128(nanoDivider) * 100;
//demurrageAmount = 100000000000000000000000000000000000000;
demurrageAmount = 10000000000000000000000000000;
//demurragePeriod = 1;
taxLevel = _taxLevelMinute; // Represents 38 decimal places taxLevel = _taxLevelMinute; // Represents 38 decimal places
bytes32 initialRedistribution = toRedistribution(0, demurrageAmount, 0, 1); initialRedistribution = toRedistribution(0, demurrageAmount, 0, 1);
redistributions.push(initialRedistribution); redistributions.push(initialRedistribution);
// Misc settings // Misc settings
sinkAddress = _defaultSinkAddress; 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 // Given address will be allowed to call the mintTo() function
@@ -146,10 +156,8 @@ contract DemurrageTokenSingleCap {
baseBalance = baseBalanceOf(_account); baseBalance = baseBalanceOf(_account);
//periodCount = actualPeriod() - demurragePeriod;
periodCount = getMinutesDelta(demurrageTimestamp); periodCount = getMinutesDelta(demurrageTimestamp);
//currentDemurragedAmount = uint128(decayBy(demurrageAmount, periodCount));
currentDemurragedAmount = uint128(decayBy(demurrageAmount * 10000000000, periodCount)); currentDemurragedAmount = uint128(decayBy(demurrageAmount * 10000000000, periodCount));
return (baseBalance * currentDemurragedAmount) / (nanoDivider * 1000000000000); return (baseBalance * currentDemurragedAmount) / (nanoDivider * 1000000000000);
@@ -204,7 +212,7 @@ contract DemurrageTokenSingleCap {
changePeriod(); changePeriod();
baseAmount = toBaseAmount(_amount); baseAmount = toBaseAmount(_amount);
totalSupply += _amount; supply += _amount;
increaseBaseBalance(_beneficiary, baseAmount); increaseBaseBalance(_beneficiary, baseAmount);
emit Mint(msg.sender, _beneficiary, _amount); emit Mint(msg.sender, _beneficiary, _amount);
saveRedistributionSupply(); saveRedistributionSupply();
@@ -212,31 +220,32 @@ contract DemurrageTokenSingleCap {
} }
// Deserializes the redistribution word // 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(redistributionItem memory) {
function toRedistribution(uint256 _participants, uint256 _demurrageModifierPpm, uint256 _value, uint256 _period) public pure returns(bytes32) { redistributionItem memory redistribution;
bytes32 redistribution;
redistribution |= bytes32((_demurrageModifierPpm << shiftRedistributionDemurrage) & maskRedistributionDemurrage); redistribution.period = uint32(_period);
redistribution |= bytes32((_value << shiftRedistributionValue) & maskRedistributionValue); redistribution.value = uint72(_value);
redistribution |= bytes32(_period & maskRedistributionPeriod); redistribution.demurrage = uint104(_demurrageModifierPpm);
return redistribution; return redistribution;
} }
// Serializes the demurrage period part of the redistribution word // Serializes the demurrage period part of the redistribution word
function toRedistributionPeriod(bytes32 redistribution) public pure returns (uint256) { function toRedistributionPeriod(redistributionItem memory _redistribution) public pure returns (uint256) {
return uint256(redistribution) & maskRedistributionPeriod; return uint256(_redistribution.period);
} }
// Serializes the supply part of the redistribution word // Serializes the supply part of the redistribution word
function toRedistributionSupply(bytes32 redistribution) public pure returns (uint256) { function toRedistributionSupply(redistributionItem memory _redistribution) public pure returns (uint256) {
return (uint256(redistribution) & maskRedistributionValue) >> shiftRedistributionValue; return uint256(_redistribution.value);
} }
// Serializes the number of participants part of the redistribution word // Serializes the number of participants part of the redistribution word
function toRedistributionDemurrageModifier(bytes32 redistribution) public pure returns (uint256) { function toRedistributionDemurrageModifier(redistributionItem memory _redistribution) public pure returns (uint256) {
return (uint256(redistribution) & maskRedistributionDemurrage) >> shiftRedistributionDemurrage; return uint256(_redistribution.demurrage);
} }
// Client accessor to the redistributions array length // Client accessor to the redistributions array length
function redistributionCount() public view returns (uint256) { function redistributionCount() public view returns (uint256) {
return redistributions.length; return redistributions.length;
@@ -244,16 +253,14 @@ contract DemurrageTokenSingleCap {
// Save the current total supply amount to the current redistribution period // Save the current total supply amount to the current redistribution period
function saveRedistributionSupply() private returns (bool) { function saveRedistributionSupply() private returns (bool) {
uint256 currentRedistribution; redistributionItem memory currentRedistribution;
uint256 grownSupply; uint256 grownSupply;
//grownSupply = growBy(totalSupply, 1); grownSupply = totalSupply();
grownSupply = totalSupply; currentRedistribution = redistributions[redistributions.length-1];
currentRedistribution = uint256(redistributions[redistributions.length-1]); currentRedistribution.value = uint72(grownSupply);
currentRedistribution &= (~maskRedistributionValue);
currentRedistribution |= (grownSupply << shiftRedistributionValue);
redistributions[redistributions.length-1] = bytes32(currentRedistribution); redistributions[redistributions.length-1] = currentRedistribution;
return true; return true;
} }
@@ -262,15 +269,16 @@ contract DemurrageTokenSingleCap {
return uint128((block.timestamp - periodStart) / periodDuration + 1); return uint128((block.timestamp - periodStart) / periodDuration + 1);
} }
// Add an entered demurrage period to the redistribution array // Retrieve next redistribution if the period threshold has been crossed
function checkPeriod() private view returns (bytes32) { function checkPeriod() private view returns (redistributionItem memory) {
bytes32 lastRedistribution; redistributionItem memory lastRedistribution;
redistributionItem memory emptyRedistribution;
uint256 currentPeriod; uint256 currentPeriod;
lastRedistribution = redistributions[redistributions.length-1]; lastRedistribution = redistributions[lastPeriod];
currentPeriod = this.actualPeriod(); currentPeriod = this.actualPeriod();
if (currentPeriod <= toRedistributionPeriod(lastRedistribution)) { if (currentPeriod <= toRedistributionPeriod(lastRedistribution)) {
return bytes32(0x00); return emptyRedistribution;
} }
return lastRedistribution; return lastRedistribution;
} }
@@ -278,11 +286,11 @@ contract DemurrageTokenSingleCap {
function getDistribution(uint256 _supply, uint256 _demurrageAmount) public view returns (uint256) { function getDistribution(uint256 _supply, uint256 _demurrageAmount) public view returns (uint256) {
uint256 difference; uint256 difference;
difference = _supply * (resolutionFactor - _demurrageAmount); //(nanoDivider - ((resolutionFactor - _demurrageAmount) / nanoDivider)); difference = _supply * (resolutionFactor - (_demurrageAmount * 10000000000));
return difference / resolutionFactor; return difference / resolutionFactor;
} }
function getDistributionFromRedistribution(bytes32 _redistribution) public returns (uint256) { function getDistributionFromRedistribution(redistributionItem memory _redistribution) public returns (uint256) {
uint256 redistributionSupply; uint256 redistributionSupply;
uint256 redistributionDemurrage; uint256 redistributionDemurrage;
@@ -292,11 +300,15 @@ contract DemurrageTokenSingleCap {
} }
// Returns the amount sent to the sink address // Returns the amount sent to the sink address
function applyDefaultRedistribution(bytes32 _redistribution) private returns (uint256) { function applyDefaultRedistribution(redistributionItem memory _redistribution) private returns (uint256) {
uint256 unit; uint256 unit;
uint256 baseUnit;
unit = getDistributionFromRedistribution(_redistribution); unit = getDistributionFromRedistribution(_redistribution);
increaseBaseBalance(sinkAddress, toBaseAmount(unit)); baseUnit = toBaseAmount(unit) - totalSink;
increaseBaseBalance(sinkAddress, baseUnit);
lastPeriod += 1;
totalSink += baseUnit;
return unit; return unit;
} }
@@ -307,18 +319,25 @@ contract DemurrageTokenSingleCap {
// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call // Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
function applyDemurrage() public returns (bool) { function applyDemurrage() public returns (bool) {
//uint128 epochPeriodCount; return applyDemurrageLimited(0);
}
function applyDemurrageLimited(uint256 _rounds) public returns (bool) {
uint256 periodCount; uint256 periodCount;
uint256 lastDemurrageAmount; uint256 lastDemurrageAmount;
//epochPeriodCount = actualPeriod();
//periodCount = epochPeriodCount - demurragePeriod;
periodCount = getMinutesDelta(demurrageTimestamp); periodCount = getMinutesDelta(demurrageTimestamp);
if (periodCount == 0) { if (periodCount == 0) {
return false; return false;
} }
lastDemurrageAmount = demurrageAmount; lastDemurrageAmount = demurrageAmount;
// safety limit for exponential calculation to ensure that we can always
// execute this code no matter how much time passes.
if (_rounds > 0 && _rounds < periodCount) {
periodCount = _rounds;
}
demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount)); demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount));
//demurragePeriod = epochPeriodCount; //demurragePeriod = epochPeriodCount;
demurrageTimestamp = demurrageTimestamp + (periodCount * 60); demurrageTimestamp = demurrageTimestamp + (periodCount * 60);
@@ -336,37 +355,46 @@ contract DemurrageTokenSingleCap {
return (block.timestamp - _target) / 60; 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 // 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) { function changePeriod() public returns (bool) {
bytes32 currentRedistribution; redistributionItem memory currentRedistribution;
bytes32 nextRedistribution; redistributionItem memory nextRedistribution;
redistributionItem memory lastRedistribution;
uint256 currentPeriod; uint256 currentPeriod;
uint256 currentDemurrageAmount; uint256 lastDemurrageAmount;
uint256 nextRedistributionDemurrage; uint256 nextRedistributionDemurrage;
uint256 demurrageCounts; uint256 demurrageCounts;
uint256 periodTimestamp;
uint256 nextPeriod; uint256 nextPeriod;
applyDemurrage(); applyDemurrage();
currentRedistribution = checkPeriod(); currentRedistribution = checkPeriod();
if (currentRedistribution == bytes32(0x00)) { if (isEmptyRedistribution(currentRedistribution)) {
return false; return false;
} }
// calculate the decay from previous redistributino
lastRedistribution = redistributions[lastPeriod];
currentPeriod = toRedistributionPeriod(currentRedistribution); currentPeriod = toRedistributionPeriod(currentRedistribution);
nextPeriod = currentPeriod + 1; nextPeriod = currentPeriod + 1;
periodTimestamp = getPeriodTimeDelta(currentPeriod); lastDemurrageAmount = toRedistributionDemurrageModifier(lastRedistribution);
demurrageCounts = periodDuration / 60;
nextRedistributionDemurrage = decayBy(lastDemurrageAmount, demurrageCounts);
currentDemurrageAmount = demurrageAmount; nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply(), nextPeriod);
demurrageCounts = demurrageCycles(periodTimestamp);
if (demurrageCounts > 0) {
nextRedistributionDemurrage = growBy(currentDemurrageAmount, demurrageCounts);
} else {
nextRedistributionDemurrage = currentDemurrageAmount;
}
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod);
redistributions.push(nextRedistribution); redistributions.push(nextRedistribution);
applyDefaultRedistribution(nextRedistribution); applyDefaultRedistribution(nextRedistribution);
@@ -375,18 +403,18 @@ contract DemurrageTokenSingleCap {
} }
// Reverse a value reduced by demurrage by the given period to its original value // Reverse a value reduced by demurrage by the given period to its original value
function growBy(uint256 _value, uint256 _period) public view returns (uint256) { // function growBy(uint256 _value, uint256 _period) public view returns (uint256) {
uint256 valueFactor; // uint256 valueFactor;
uint256 truncatedTaxLevel; // uint256 truncatedTaxLevel;
//
valueFactor = growthResolutionFactor; // valueFactor = growthResolutionFactor;
truncatedTaxLevel = taxLevel / nanoDivider; // truncatedTaxLevel = taxLevel / nanoDivider;
//
for (uint256 i = 0; i < _period; i++) { // for (uint256 i = 0; i < _period; i++) {
valueFactor = valueFactor + ((valueFactor * truncatedTaxLevel) / growthResolutionFactor); // valueFactor = valueFactor + ((valueFactor * truncatedTaxLevel) / growthResolutionFactor);
} // }
return (valueFactor * _value) / growthResolutionFactor; // return (valueFactor * _value) / growthResolutionFactor;
} // }
// Calculate a value reduced by demurrage by the given period // Calculate a value reduced by demurrage by the given period
function decayBy(uint256 _value, uint256 _period) public view returns (uint256) { function decayBy(uint256 _value, uint256 _period) public view returns (uint256) {
@@ -404,7 +432,6 @@ contract DemurrageTokenSingleCap {
// Inflates the given amount according to the current demurrage modifier // Inflates the given amount according to the current demurrage modifier
function toBaseAmount(uint256 _value) public view returns (uint256) { function toBaseAmount(uint256 _value) public view returns (uint256) {
//return (_value * resolutionFactor) / demurrageAmount;
return (_value * resolutionFactor) / (demurrageAmount * 10000000000); return (_value * resolutionFactor) / (demurrageAmount * 10000000000);
} }
@@ -412,14 +439,45 @@ contract DemurrageTokenSingleCap {
function approve(address _spender, uint256 _value) public returns (bool) { function approve(address _spender, uint256 _value) public returns (bool) {
uint256 baseValue; uint256 baseValue;
if (allowance[msg.sender][_spender] > 0) {
require(_value == 0, 'ZERO_FIRST');
}
changePeriod(); changePeriod();
baseValue = toBaseAmount(_value); baseValue = toBaseAmount(_value);
allowance[msg.sender][_spender] += baseValue; allowance[msg.sender][_spender] = baseValue;
emit Approval(msg.sender, _spender, _value); emit Approval(msg.sender, _spender, _value);
return true; 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 // Implements ERC20, triggers tax and/or redistribution
function transfer(address _to, uint256 _value) public returns (bool) { function transfer(address _to, uint256 _value) public returns (bool) {
uint256 baseValue; uint256 baseValue;
@@ -443,7 +501,9 @@ contract DemurrageTokenSingleCap {
baseValue = toBaseAmount(_value); baseValue = toBaseAmount(_value);
require(allowance[_from][msg.sender] >= baseValue); require(allowance[_from][msg.sender] >= baseValue);
allowance[_from][msg.sender] -= baseValue;
result = transferBase(_from, _to, baseValue); result = transferBase(_from, _to, baseValue);
emit Transfer(_from, _to, _value); emit Transfer(_from, _to, _value);
return result; return result;
} }
@@ -455,7 +515,6 @@ contract DemurrageTokenSingleCap {
decreaseBaseBalance(_from, _value); decreaseBaseBalance(_from, _value);
increaseBaseBalance(_to, _value); increaseBaseBalance(_to, _value);
//period = actualPeriod();
return true; return true;
} }
@@ -476,6 +535,29 @@ contract DemurrageTokenSingleCap {
emit OwnershipTransferred(oldOwner, owner); 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 // Implements EIP165
function supportsInterface(bytes4 _sum) public pure returns (bool) { function supportsInterface(bytes4 _sum) public pure returns (bool) {
if (_sum == 0xc6bb4b70) { // ERC20 if (_sum == 0xc6bb4b70) { // ERC20

View File

@@ -1,6 +1,6 @@
SOLC = /usr/bin/solc SOLC = /usr/bin/solc
all: multi single all: single_nocap
multi_nocap: multi_nocap:
$(SOLC) DemurrageTokenMultiNocap.sol --abi --evm-version byzantium | awk 'NR>3' > DemurrageTokenMultiNocap.json $(SOLC) DemurrageTokenMultiNocap.sol --abi --evm-version byzantium | awk 'NR>3' > DemurrageTokenMultiNocap.json
@@ -33,6 +33,7 @@ test: all
python ../python/tests/test_pure.py python ../python/tests/test_pure.py
install: all 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 .PHONY: test install