erc20-demurrage-token

ERC20 token with redistributed continual demurrage
Log | Files | Refs | README

commit 65a91cc44d7062e24deb14f541109404b8b9bd13
parent e39b2597497dfae0143ff6ff7806887fb7374547
Author: Will Ruddick <willruddick@gmail.com>
Date:   Sun, 19 Feb 2023 14:16:39 +0300

updated README example

Signed-off-by: lash <dev@holbrook.no>

Diffstat:
MREADME.md | 26+++++++++++++-------------
Apython/erc20_demurrage_token/runnable/#publish.py# | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apython/erc20_demurrage_token/runnable/.#publish.py | 2++
3 files changed, 191 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md @@ -1,24 +1,24 @@ # RedistributedDemurrageToken -**this documentation is obsolete, will rewrite asap** +# RedistributedDemurrageToken + +**Last edit: Will Ruddick Feburary 19 2023** ## Use Case -* Network / Basic Income Token - * 100 Sarafu is distributed to anyone in Kenya after user validation by the owner of a faucet which mints new Sarafu. - * Validated users are those that validate their phone number in Kenya. - * A Sarafu holding tax aka ([demurrage](https://en.wikipedia.org/wiki/Demurrage_(currency))) of 0.000050105908373373% is charged from users per minute - such that over 1 month to total tax would be 2%. - * After 1 week the total amount tax is distributed evenly out to _active_ users. - * any single transaction by a user within that week is considered _active_ (heartbeat) - * This is meant to result in a disincentivization to hold (hodl) the Sarafu token and increase its usage as a medium of exchange rather than a store of value. - * This token can be added to liquidity pools with other ERC20 tokens and or Community Inclusion Currencies (CICs) - and thereby act as a central network token and connect various tokens and CICs together. +* Vouchers + * A Publisher may publish a RedistributedDemurrageToken (Voucher) representing a credit obligation of an Issuer or Association of Issuers that can be redeemed as payment for the products of the Issuer. The Issuer is the entity legally obligated to redeem the voucher as payment. + * Decay: The Publisher can specify an decay rate such as 2% as well as a redistribution period. After the redistribution period such as a month. Assuming an account holder has not had any transfers they will have a new balance of their original balance*2%. Note that the numeric decay will happen continuously by the minute. + * Redistribution: The missing (demurraged) balances will be added to the balance of the SINK address. So once a redistribution period (e.g. once a month) the total supply of all holders including the SINK will return to the minted supply. + * This is meant to result as a disincentivization to hold (hodl) the Voucher without causing price inflation, as the total supply is stable. * Example - - With a demurrage of 2% (net per month) and a reward period of 1 month - If there are 10 users all with balances of 1000 Sarafu and only 2 of them trade that month (assume they trade back and forth with no net balance change). - - Then the resulting balances after one tax period of those two trading would be 1080 Sarafu while the remaining non-active users would be 980 Sarafu. If this behaviour continued in the next tax period, with the same two users only trading (with no net balance changes), they would have 1158.39999968 Sarafu and those users that are not trading would have their balances further reduced to 960.40 Sarafu. If this continued on ~forever those two active trading users would have the entire token supply and the non-trading users would eventually reach a zero balance. - - this example calculation for 3 tax periods can be found here: https://gitlab.com/grassrootseconomics/cic-docs/-/blob/master/demurrage-redist-sarafu.ods + - With a demurrage of 2% (and redistribution period of 1 month) - If there are 10 users all with balances of 100 Vouchers (and only 2 of them trade that month (assume they trade back and forth with no net balance change)). + - Then the resulting balances after one redistribution period of ALL users (regardless of their trading) would be 98 Vouchers and 20 Voucher would be the balance of the SINK address. Assuming the SINK address is redistributed (as a Community Fund) back to users, it’s balance would again reach 20 the next redistribution period. + - Note that after the redistribution the total of all balances will equal the total minted amount. + - Note that all accounts holding such Vouchers are effected by demurrage. ## Nomenclature -* `Demurrage` aka Decay amount: A percentage of token supply that will be charged once per minute and evenly redistributed to _active_ users every Demurrage Period (minutes) +* `Demurrage` aka Decay amount: A percentage of token supply that will gradually be removed over a redstribution period and then redistributed to the SINK account. * Base balance: The inflated balance of each user is stored for bookkeeping. * Sink Token Address: Rounding errors and if no one trades the tax goes to this address * Demurrage Period (minutes)- aka `period`: The number of minutes over which a user must be _active_ to receive tax-redistibution. diff --git a/python/erc20_demurrage_token/runnable/#publish.py# b/python/erc20_demurrage_token/runnable/#publish.py# @@ -0,0 +1,176 @@ +"""Deploy sarafu token + +.. moduleauthor:: Louis Holbrook <dev@holbrook.no> +.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 + +""" + +# standard imports +import sys +import os +import json +import argparse +import logging + +# external imports +import confini +from funga.eth.signer import EIP155Signer +from funga.eth.keystore.dict import DictKeystore +from chainlib.chain import ChainSpec +from chainlib.eth.nonce import ( + RPCNonceOracle, + OverrideNonceOracle, + ) +from chainlib.eth.gas import ( + RPCGasOracle, + OverrideGasOracle, + ) +from chainlib.eth.connection import EthHTTPConnection +from chainlib.eth.tx import receipt +from chainlib.eth.constant import ZERO_ADDRESS +import chainlib.eth.cli +from chainlib.eth.cli.arg import ( + Arg, + ArgFlag, + process_args, + ) +from chainlib.eth.cli.config import ( + Config, + process_config, + ) +from chainlib.eth.cli.log import process_log +from chainlib.eth.settings import process_settings +from chainlib.eth.address import to_checksum_address +from chainlib.settings import ChainSettings + +from dexif import to_fixed + +# local imports +import erc20_demurrage_token +from erc20_demurrage_token import ( + DemurrageToken, + DemurrageTokenSettings, + ) + +logg = logging.getLogger() + +script_dir = os.path.dirname(__file__) +data_dir = os.path.join(script_dir, '..', 'data') + +config_dir = os.path.join(data_dir, 'config') + + +def process_config_local(config, arg, args, flags): + config.add(args.token_name, 'TOKEN_NAME') + config.add(args.token_symbol, 'TOKEN_SYMBOL') + config.add(args.token_decimals, 'TOKEN_DECIMALS') + sink_address = to_checksum_address(args.sink_address) + config.add(sink_address, 'TOKEN_SINK_ADDRESS') + config.add(args.redistribution_period, 'TOKEN_REDISTRIBUTION_PERIOD') + + v = (1 - (args.demurrage_level / 1000000)) ** (1 / config.get('TOKEN_REDISTRIBUTION_PERIOD')) + if v >= 1.0: + raise ValueError('demurrage level must be less than 100%') + demurrage_level = to_fixed(v) + logg.info('v {} demurrage level {}'.format(v, demurrage_level)) + config.add(demurrage_level, 'TOKEN_DEMURRAGE_LEVEL') + return config + + +arg_flags = ArgFlag() +arg = Arg(arg_flags) +flags = arg_flags.STD_WRITE | arg_flags.WALLET + +argparser = chainlib.eth.cli.ArgumentParser(arg_flags) +argparser = process_args(argparser, arg, flags) +argparser.add_argument('--name', dest='token_name', type=str, help='Token name') +argparser.add_argument('--symbol', dest='token_symbol', required=True, type=str, help='Token symbol') +argparser.add_argument('--decimals', dest='token_decimals', type=int, help='Token decimals') +argparser.add_argument('--sink-address', dest='sink_address', type=str, help='demurrage level,ppm per minute') +argparser.add_argument('--redistribution-period', type=int, help='redistribution period, minutes (0 = deactivate)') # default 10080 = week +argparser.add_argument('--demurrage-level', dest='demurrage_level', type=int, help='demurrage level, ppm per period') +args = argparser.parse_args() + +logg = process_log(args, logg) + +config = Config() +config = process_config(config, arg, args, flags) +config = process_config_local(config, arg, args, flags) +logg.debug('config loaded:\n{}'.format(config)) + +settings = ChainSettings() +settings = process_settings(settings, config) +logg.debug('settings loaded:\n{}'.format(settings)) + + +#extra_args = { +# 'redistribution_period': 'TOKEN_REDISTRIBUTION_PERIOD', +# 'demurrage_level': 'TOKEN_DEMURRAGE_LEVEL', +# 'supply_limit': 'TOKEN_SUPPLY_LIMIT', +# 'token_name': 'TOKEN_NAME', +# 'token_symbol': 'TOKEN_SYMBOL', +# 'token_decimals': 'TOKEN_DECIMALS', +# 'sink_address': 'TOKEN_SINK_ADDRESS', +# 'multi': None, +# } +#config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=DemurrageToken.gas(), base_config_dir=config_dir) +# +#if not bool(config.get('TOKEN_NAME')): +# logg.info('token name not set, using symbol {} as name'.format(config.get('TOKEN_SYMBOL'))) +# config.add(config.get('TOKEN_SYMBOL'), 'TOKEN_NAME', True) +# +#if config.get('TOKEN_SUPPLY_LIMIT') == None: +# config.add(0, 'TOKEN_SUPPLY_LIMIT', True) +# +#if config.get('TOKEN_REDISTRIBUTION_PERIOD') == None: +# config.add(10800, 'TOKEN_REDISTRIBUTION_PERIOD', True) +#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(): + conn = settings.get('CONN') + c = DemurrageToken( + settings.get('CHAIN_SPEC'), + signer=settings.get('SIGNER'), + gas_oracle=settings.get('FEE_ORACLE'), + nonce_oracle=settings.get('NONCE_ORACLE'), + ) + token_settings = DemurrageTokenSettings() + token_settings.name = config.get('TOKEN_NAME') + token_settings.symbol = config.get('TOKEN_SYMBOL') + token_settings.decimals = int(config.get('TOKEN_DECIMALS')) + token_settings.demurrage_level = int(config.get('TOKEN_DEMURRAGE_LEVEL')) + token_settings.period_minutes = int(config.get('TOKEN_REDISTRIBUTION_PERIOD')) + token_settings.sink_address = config.get('TOKEN_SINK_ADDRESS') + + (tx_hash_hex, o) = c.constructor( + settings.get('SENDER_ADDRESS'), + token_settings, + ) + if settings.get('RPC_SEND'): + conn.do(o) + if config.true('_WAIT'): + r = conn.wait(tx_hash_hex) + if r['status'] == 0: + sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you') + sys.exit(1) + # TODO: pass through translator for keys (evm tester uses underscore instead of camelcase) + address = r['contractAddress'] + + print(address) + else: + print(tx_hash_hex) + + else: + print(o) + + +if __name__ == '__main__': + main() diff --git a/python/erc20_demurrage_token/runnable/.#publish.py b/python/erc20_demurrage_token/runnable/.#publish.py @@ -0,0 +1 @@ +wor@gecon.733148:1676287007 +\ No newline at end of file