18 Commits

Author SHA1 Message Date
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
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
22 changed files with 631 additions and 269 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

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

@@ -325,8 +325,19 @@ class DemurrageToken(ERC20):
return o return o
def apply_demurrage(self, contract_address, sender_address): def apply_demurrage(self, contract_address, sender_address, limit=0, tx_format=TxFormat.JSONRPC):
return self.transact_noarg('applyDemurrage', contract_address, sender_address) if limit == 0:
return self.transact_noarg('applyDemurrage', contract_address, sender_address)
enc = ABIContractEncoder()
enc.method('applyDemurrageLimited')
enc.typ(ABIContractType.UINT256)
enc.uint256(limit)
data = enc.get()
tx = self.template(sender_address, contract_address, use_nonce=True)
tx = self.set_code(tx, data)
tx = self.finalize(tx, tx_format)
return tx
def change_period(self, contract_address, sender_address): def change_period(self, contract_address, sender_address):
@@ -369,6 +380,10 @@ 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)

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

@@ -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,78 @@ 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_transfer_from(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.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.approve(self.address, self.accounts[1], self.accounts[2], 500) (tx_hash, o) = c.approve(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, 1024) self.assertEqual(balance, 1024)
#
# 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_from(self.address, self.accounts[2], self.accounts[1], self.accounts[3], 500) (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)
#
# 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[3], sender_address=self.accounts[0]) o = c.balance_of(self.address, self.accounts[3], 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)
if __name__ == '__main__': if __name__ == '__main__':

View File

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

View File

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

View File

@@ -124,6 +124,13 @@ contract DemurrageTokenSingleCap {
minimumParticipantSpend = 10 ** uint256(_decimals); minimumParticipantSpend = 10 ** uint256(_decimals);
} }
// Change sink address for redistribution
function setSinkAddress(address _sinkAddress) public {
require(msg.sender == owner);
sinkAddress = _sinkAddress;
}
// Given address will be allowed to call the mintTo() function // Given address will be allowed to call the mintTo() function
function addMinter(address _minter) public returns (bool) { function addMinter(address _minter) public returns (bool) {
require(msg.sender == owner); require(msg.sender == owner);
@@ -278,7 +285,8 @@ contract DemurrageTokenSingleCap {
function getDistribution(uint256 _supply, uint256 _demurrageAmount) public view returns (uint256) { function getDistribution(uint256 _supply, uint256 _demurrageAmount) public view returns (uint256) {
uint256 difference; uint256 difference;
difference = _supply * (resolutionFactor - _demurrageAmount); //(nanoDivider - ((resolutionFactor - _demurrageAmount) / nanoDivider)); //difference = _supply * (resolutionFactor - _demurrageAmount); //(nanoDivider - ((resolutionFactor - _demurrageAmount) / nanoDivider));
difference = _supply * (resolutionFactor - (_demurrageAmount * 10000000000)); //(nanoDivider - ((resolutionFactor - _demurrageAmount) / nanoDivider));
return difference / resolutionFactor; return difference / resolutionFactor;
} }
@@ -307,6 +315,10 @@ contract DemurrageTokenSingleCap {
// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call // Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
function applyDemurrage() public returns (bool) { function applyDemurrage() public returns (bool) {
return applyDemurrageLimited(0);
}
function applyDemurrageLimited(uint256 _rounds) public returns (bool) {
//uint128 epochPeriodCount; //uint128 epochPeriodCount;
uint256 periodCount; uint256 periodCount;
uint256 lastDemurrageAmount; uint256 lastDemurrageAmount;
@@ -319,6 +331,13 @@ contract DemurrageTokenSingleCap {
return false; return false;
} }
lastDemurrageAmount = demurrageAmount; lastDemurrageAmount = demurrageAmount;
// safety limit for exponential calculation to ensure that we can always
// execute this code no matter how much time passes.
if (_rounds > 0 && _rounds < periodCount) {
periodCount = _rounds;
}
demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount)); demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount));
//demurragePeriod = epochPeriodCount; //demurragePeriod = epochPeriodCount;
demurrageTimestamp = demurrageTimestamp + (periodCount * 60); demurrageTimestamp = demurrageTimestamp + (periodCount * 60);