diff --git a/python/requirements.txt b/python/requirements.txt index 3099ced..783d6cd 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -1,2 +1,2 @@ -chainlib~=0.0.1a36 -crypto-dev-signer~=0.4.14a11 +chainlib~=0.0.2a10 +crypto-dev-signer~=0.4.14b1 diff --git a/python/sarafu_token/__init__.py b/python/sarafu_token/__init__.py new file mode 100644 index 0000000..cda5404 --- /dev/null +++ b/python/sarafu_token/__init__.py @@ -0,0 +1 @@ +from .token import RedistributedDemurrageToken diff --git a/python/sarafu_token/data/__init__.py b/python/sarafu_token/data/__init__.py new file mode 100644 index 0000000..01ec837 --- /dev/null +++ b/python/sarafu_token/data/__init__.py @@ -0,0 +1,3 @@ +import os + +data_dir = os.path.realpath(os.path.dirname(__file__)) diff --git a/python/sarafu_token/runnable/deploy.py b/python/sarafu_token/runnable/deploy.py new file mode 100644 index 0000000..480c7e5 --- /dev/null +++ b/python/sarafu_token/runnable/deploy.py @@ -0,0 +1,135 @@ +"""Deploy sarafu token + +.. moduleauthor:: Louis Holbrook +.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 + +""" + +# standard imports +import sys +import os +import json +import argparse +import logging + +# third-party imports +from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer +from crypto_dev_signer.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 + +# local imports +from sarafu_token import RedistributedDemurrageToken + +logging.basicConfig(level=logging.WARNING) +logg = logging.getLogger() + +script_dir = os.path.dirname(__file__) +data_dir = os.path.join(script_dir, '..', 'data') + +argparser = argparse.ArgumentParser() +argparser.add_argument('-p', '--provider', dest='p', default='http://localhost:8545', type=str, help='Web3 provider url (http only)') +argparser.add_argument('-w', action='store_true', help='Wait for the last transaction to be confirmed') +argparser.add_argument('-ww', action='store_true', help='Wait for every transaction to be confirmed') +argparser.add_argument('-i', '--chain-spec', dest='i', type=str, default='evm:ethereum:1', help='Chain specification string') +argparser.add_argument('-y', '--key-file', dest='y', type=str, help='Ethereum keystore file to use for signing') +argparser.add_argument('-v', action='store_true', help='Be verbose') +argparser.add_argument('-vv', action='store_true', help='Be more verbose') +argparser.add_argument('-d', action='store_true', help='Dump RPC calls to terminal and do not send') +argparser.add_argument('--name', type=str, help='Token name') +argparser.add_argument('--decimals', default=6, type=int, help='Token decimals') +argparser.add_argument('--gas-price', type=int, dest='gas_price', help='Override gas price') +argparser.add_argument('--nonce', type=int, help='Override transaction nonce') +argparser.add_argument('--sink-address', default=ZERO_ADDRESS, type=str, help='demurrage level,ppm per minute') +argparser.add_argument('--redistribution-period', default=10080, type=int, help='redistribution period, minutes') # default 10080 = week +argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration') +argparser.add_argument('symbol', default='SRF', type=str, help='Token symbol') +argparser.add_argument('demurrage_level', type=int, help='demurrage level, ppm per minute') +args = argparser.parse_args() + +if args.vv: + logg.setLevel(logging.DEBUG) +elif args.v: + logg.setLevel(logging.INFO) + +block_last = args.w +block_all = args.ww + +passphrase_env = 'ETH_PASSPHRASE' +if args.env_prefix != None: + passphrase_env = args.env_prefix + '_' + passphrase_env +passphrase = os.environ.get(passphrase_env) +if passphrase == None: + logg.warning('no passphrase given') + passphrase='' + +signer_address = None +keystore = DictKeystore() +if args.y != None: + logg.debug('loading keystore file {}'.format(args.y)) + signer_address = keystore.import_keystore_file(args.y, password=passphrase) + logg.debug('now have key for signer address {}'.format(signer_address)) +signer = EIP155Signer(keystore) + +chain_spec = ChainSpec.from_chain_str(args.i) + +rpc = EthHTTPConnection(args.p) +nonce_oracle = None +if args.nonce != None: + nonce_oracle = OverrideNonceOracle(signer_address, args.nonce) +else: + nonce_oracle = RPCNonceOracle(signer_address, rpc) + +gas_oracle = None +if args.gas_price !=None: + gas_oracle = OverrideGasOracle(price=args.gas_price, conn=rpc, code_callback=RedistributedDemurrageToken.gas) +else: + gas_oracle = RPCGasOracle(rpc, code_callback=RedistributedDemurrageToken.gas) + +dummy = args.d + +token_name = args.name +if token_name == None: + token_name = args.symbol + +def main(): + c = RedistributedDemurrageToken(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle) + (tx_hash_hex, o) = c.constructor( + signer_address, + token_name, + args.symbol, + args.decimals, + args.demurrage_level, + args.redistribution_period, + args.sink_address, + ) + if dummy: + print(tx_hash_hex) + print(o) + else: + rpc.do(o) + if block_last: + r = rpc.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) + + +if __name__ == '__main__': + main() diff --git a/python/sarafu_token/token.py b/python/sarafu_token/token.py new file mode 100644 index 0000000..224bbf2 --- /dev/null +++ b/python/sarafu_token/token.py @@ -0,0 +1,87 @@ +# standard imports +import os +import logging + +# external imports +from chainlib.eth.tx import ( + TxFactory, + TxFormat, + ) +from chainlib.hash import keccak256_string_to_hex +from chainlib.eth.contract import ( + ABIContractEncoder, + ABIContractType, + ) + +# local imports +from sarafu_token.data import data_dir + +logg = logging.getLogger(__name__) + + +class RedistributedDemurrageToken(TxFactory): + + __abi = None + __bytecode = None + + def constructor(self, sender_address, name, symbol, decimals, demurrage_level, period_minutes, sink_address, tx_format=TxFormat.JSONRPC): + code = RedistributedDemurrageToken.bytecode() + enc = ABIContractEncoder() + enc.string(name) + enc.string(symbol) + enc.uint256(decimals) + enc.uint256(demurrage_level) + enc.uint256(period_minutes) + enc.address(sink_address) + code += enc.get() + tx = self.template(sender_address, None, use_nonce=True) + tx = self.set_code(tx, code) + return self.finalize(tx, tx_format) + + + @staticmethod + def gas(code=None): + return 3500000 + + @staticmethod + def abi(): + if RedistributedDemurrageToken.__abi == None: + f = open(os.path.join(data_dir, 'RedistributedDemurrageToken.json'), 'r') + RedistributedDemurrageToken.__abi = json.load(f) + f.close() + return RedistributedDemurrageToken.__abi + + + @staticmethod + def bytecode(): + if RedistributedDemurrageToken.__bytecode == None: + f = open(os.path.join(data_dir, 'RedistributedDemurrageToken.bin'), 'r') + RedistributedDemurrageToken.__bytecode = f.read() + f.close() + return RedistributedDemurrageToken.__bytecode + + + def add_minter(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC): + enc = ABIContractEncoder() + enc.method('addMinter') + enc.typ(ABIContractType.ADDRESS) + enc.address(address) + 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 mint_to(self, contract_address, sender_address, address, value, tx_format=TxFormat.JSONRPC): + enc = ABIContractEncoder() + enc.method('mintTo') + 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 diff --git a/python/setup.cfg b/python/setup.cfg index 1eda8eb..b8425e2 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = sarafu-token -version = 0.0.1a4 +version = 0.0.1a5 description = ERC20 token with redistributed continual demurrage author = Louis Holbrook author_email = dev@holbrook.no @@ -38,4 +38,4 @@ install_requires = [options.entry_points] console_scripts = - sarafu-token-deploy = sarafu_faucet.runnable.legacy.deploy:main + sarafu-token-deploy = sarafu_faucet.runnable.deploy:main diff --git a/python/test_requirements.txt b/python/test_requirements.txt index 56f8cb3..ea78714 100644 --- a/python/test_requirements.txt +++ b/python/test_requirements.txt @@ -1,3 +1,4 @@ +web3==5.12.2 eth_tester==0.5.0b3 py-evm==0.3.0a20 giftable-erc20-token==0.0.8a1