Compare commits

..

6 Commits

Author SHA1 Message Date
nolash
2cb8f744e8 Add transfer from 2021-11-15 14:34:12 +01:00
nolash
edb35b3ead Improve test runner 2021-10-24 16:13:45 +02:00
nolash
fde30511de Add approve runable 2021-10-24 15:29:00 +02:00
nolash
84ce22e8b6 Add id generator for approve, transfer from 2021-09-30 17:18:27 +02:00
nolash
7ea1aa21ca Use explicit pre-release signer 2021-08-24 21:37:53 +02:00
nolash
e007d0754e Implement remaining cli tools on chainlib cli util 2021-07-29 20:25:10 +02:00
15 changed files with 356 additions and 402 deletions

View File

@@ -1 +1 @@
include **/data/ERC20.json **/data/GiftableToken.json **/data/GiftableToken.bin requirements.txt test_requirements.txt
include **/data/ERC20.json **/data/GiftableToken.json **/data/GiftableToken.bin *requirements.txt

View File

@@ -7,7 +7,6 @@ from hexathon import (
add_0x,
strip_0x,
)
from crypto_dev_signer.eth.transaction import EIP155Transaction
# external imports
from chainlib.hash import (
@@ -148,7 +147,7 @@ class ERC20(TxFactory):
return tx
def transfer_from(self, contract_address, sender_address, holder_address, recipient_address, value, tx_format=TxFormat.JSONRPC):
def transfer_from(self, contract_address, sender_address, holder_address, recipient_address, value, tx_format=TxFormat.JSONRPC, id_generator=None):
enc = ABIContractEncoder()
enc.method('transferFrom')
enc.typ(ABIContractType.ADDRESS)
@@ -164,7 +163,7 @@ class ERC20(TxFactory):
return tx
def approve(self, contract_address, sender_address, spender_address, value, tx_format=TxFormat.JSONRPC):
def approve(self, contract_address, sender_address, spender_address, value, tx_format=TxFormat.JSONRPC, id_generator=None):
enc = ABIContractEncoder()
enc.method('approve')
enc.typ(ABIContractType.ADDRESS)
@@ -198,6 +197,11 @@ class ERC20(TxFactory):
return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
def parse_balance_of(self, v):
return self.parse_balance(v)
@classmethod
def parse_total_supply(self, v):
return abi_decode_single(ABIContractType.UINT256, v)

View File

@@ -0,0 +1,99 @@
#!python3
"""Token transfer script
.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
"""
# SPDX-License-Identifier: GPL-3.0-or-later
# standard imports
import os
import io
import json
import argparse
import logging
# external imports
from hexathon import (
add_0x,
strip_0x,
)
from chainlib.eth.connection import EthHTTPConnection
from chainlib.chain import ChainSpec
from chainlib.eth.runnable.util import decode_for_puny_humans
from chainlib.eth.address import to_checksum_address
import chainlib.eth.cli
# local imports
from eth_erc20 import ERC20
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC | chainlib.eth.cli.Flag.WALLET
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_positional('amount', type=int, help='Token amount to send')
args = argparser.parse_args()
extra_args = {
'amount': None,
}
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=100000)
block_all = args.ww
block_last = args.w or block_all
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'))
value = config.get('_AMOUNT')
send = config.true('_RPC_SEND')
def main():
signer = rpc.get_signer()
signer_address = rpc.get_sender_address()
gas_oracle = rpc.get_gas_oracle()
nonce_oracle = rpc.get_nonce_oracle()
g = ERC20(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
recipient = to_checksum_address(config.get('_RECIPIENT'))
if not config.true('_UNSAFE') and recipient != add_0x(config.get('_RECIPIENT')):
raise ValueError('invalid checksum address for recipient')
token_address = to_checksum_address(config.get('_EXEC_ADDRESS'))
if not config.true('_UNSAFE') and token_address != add_0x(config.get('_EXEC_ADDRESS')):
raise ValueError('invalid checksum address for contract')
(tx_hash_hex, o) = g.approve(token_address, signer_address, recipient, value, id_generator=rpc.id_generator)
if send:
conn.do(o)
if block_last:
r = conn.wait(tx_hash_hex)
if logg.isEnabledFor(logging.DEBUG):
sender_balance = balance(g, token_address, signer_address, id_generator=rpc.id_generator)
recipient_balance = balance(g, token_address, recipient, id_generator=rpc.id_generator)
logg.debug('sender {} balance after: {}'.format(signer_address, sender_balance))
logg.debug('recipient {} balance after: {}'.format(recipient, recipient_balance))
if r['status'] == 0:
logg.critical('VM revert. Wish I could tell you more')
sys.exit(1)
print(tx_hash_hex)
else:
print(o['params'][0])
if __name__ == '__main__':
main()

View File

@@ -16,17 +16,17 @@ import json
import argparse
import logging
# third-party imports
# external imports
from hexathon import (
add_0x,
strip_0x,
even,
)
import sha3
from eth_abi import encode_single
# external imports
from chainlib.eth.address import to_checksum
import chainlib.eth.cli
from chainlib.eth.address import to_checksum_address
from chainlib.eth.connection import EthHTTPConnection
from chainlib.eth.gas import (
OverrideGasOracle,
@@ -40,46 +40,29 @@ from eth_erc20 import ERC20
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
default_eth_provider = os.environ.get('RPC_PROVIDER')
if default_eth_provider == None:
default_eth_provider = os.environ.get('ETH_PROVIDER', 'http://localhost:8545')
argparser = argparse.ArgumentParser()
argparser.add_argument('-p', '--provider', dest='p', default=default_eth_provider, type=str, help='Web3 provider url (http only)')
argparser.add_argument('-a', '--token-address', dest='a', required=True, type=str, help='Token address. If not set, will return gas balance')
argparser.add_argument('-f', '--format', dest='f', type=str, default='terminal', help='Output format [terminal (default), raw, brief]')
argparser.add_argument('-i', '--chain-spec', dest='i', type=str, default='evm:ethereum:1', help='Chain specification string')
argparser.add_argument('-u', '--unsafe', dest='u', action='store_true', help='Auto-convert address to checksum adddress')
argparser.add_argument('--seq', action='store_true', help='Use sequential rpc ids')
argparser.add_argument('-v', action='store_true', help='Be verbose')
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
argparser.add_argument('address', type=str, help='Account address')
arg_flags = chainlib.eth.cli.argflag_std_read | chainlib.eth.cli.Flag.EXEC
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_positional('address', type=str, help='Ethereum address of recipient')
args = argparser.parse_args()
config = chainlib.eth.cli.Config.from_args(args, arg_flags)
wallet = chainlib.eth.cli.Wallet()
wallet.from_config(config)
holder_address = args.address
if wallet.get_signer_address() == None and holder_address != None:
holder_address = wallet.from_address(holder_address)
if args.vv:
logg.setLevel(logging.DEBUG)
elif args.v:
logg.setLevel(logging.INFO)
rpc = chainlib.eth.cli.Rpc()
conn = rpc.connect_by_config(config)
conn = EthHTTPConnection(args.p)
gas_oracle = OverrideGasOracle(conn)
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
address = to_checksum(args.address)
if not args.u and address != add_0x(args.address):
raise ValueError('invalid checksum address')
token_address = args.a
fmt = args.f
chain_spec = ChainSpec.from_chain_str(args.i)
token_address = config.get('_EXEC_ADDRESS')
def main():
r = None
decimals = 18
g = ERC20(chain_spec=chain_spec)
g = ERC20(chain_spec=chain_spec, gas_oracle=rpc.get_gas_oracle())
# determine decimals
decimals_o = g.decimals(token_address)
r = conn.do(decimals_o)
@@ -96,9 +79,8 @@ def main():
token_symbol = g.parse_symbol(r)
logg.info('symbol {}'.format(token_symbol))
# get balance
balance_o = g.balance(token_address, address)
balance_o = g.balance(token_address, holder_address)
r = conn.do(balance_o)
hx = strip_0x(r)
@@ -107,9 +89,7 @@ def main():
balance_str = str(balance_value)
balance_len = len(balance_str)
if fmt == 'terminal':
sys.stdout.write('{} ({}): '.format(token_name, token_symbol))
if fmt == 'raw':
if config.get('_RAW'):
print(balance_str)
else:
if balance_len < decimals + 1:

View File

@@ -16,27 +16,19 @@ 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
# external imports
from hexathon import (
add_0x,
strip_0x,
)
from chainlib.eth.connection import EthHTTPConnection
from chainlib.jsonrpc import (
IntSequenceGenerator,
)
from chainlib.eth.nonce import (
RPCNonceOracle,
OverrideNonceOracle,
)
from chainlib.eth.gas import (
RPCGasOracle,
OverrideGasOracle,
)
from chainlib.chain import ChainSpec
from chainlib.eth.runnable.util import decode_for_puny_humans
from chainlib.eth.address import (
to_checksum_address,
is_same_address,
)
import chainlib.eth.cli
# local imports
from eth_erc20 import ERC20
@@ -44,125 +36,88 @@ from eth_erc20 import ERC20
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
logging.getLogger('web3').setLevel(logging.WARNING)
logging.getLogger('urllib3').setLevel(logging.WARNING)
default_eth_provider = os.environ.get('RPC_PROVIDER')
if default_eth_provider == None:
default_eth_provider = os.environ.get('ETH_PROVIDER', 'http://localhost:8545')
argparser = argparse.ArgumentParser()
argparser.add_argument('-p', '--provider', dest='p', default=default_eth_provider, 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('-a', '--token-address', required='True', dest='a', type=str, help='Token address')
argparser.add_argument('-y', '--key-file', dest='y', type=str, help='Ethereum keystore file to use for signing')
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('-u', '--unsafe', dest='u', action='store_true', help='Auto-convert address to checksum adddress')
argparser.add_argument('-s', '--send', dest='s', action='store_true', help='Send to network')
argparser.add_argument('--nonce', type=int, help='Override nonce')
argparser.add_argument('--gas-price', dest='gas_price', type=int, help='Override gas price')
argparser.add_argument('--gas-limit', dest='gas_limit', type=int, help='Override gas limit')
argparser.add_argument('--seq', action='store_true', help='Use sequential rpc ids')
argparser.add_argument('-v', action='store_true', help='Be verbose')
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
argparser.add_argument('recipient', type=str, help='Recipient account address')
argparser.add_argument('amount', type=int, help='Amount of tokens to mint and gift')
arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC | chainlib.eth.cli.Flag.WALLET
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_argument('--from', type=str, help='Address to send on behalf of')
argparser.add_positional('amount', type=int, help='Token amount to send')
args = argparser.parse_args()
extra_args = {
'amount': None,
'from': None,
}
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=100000)
if args.vv:
logg.setLevel(logging.DEBUG)
elif args.v:
logg.setLevel(logging.INFO)
block_all = args.ww
block_all = args.ww
block_last = args.w or block_all
passphrase_env = 'ETH_PASSPHRASE'
if args.env_prefix != None:
passphrase_env = args.env_prefix + '_' + passphrase_env
passphrase = os.environ.get(passphrase_env)
logg.error('pass {}'.format(passphrase_env))
if passphrase == None:
logg.warning('no passphrase given')
passphrase=''
wallet = chainlib.eth.cli.Wallet()
wallet.from_config(config)
signer_address = None
keystore = DictKeystore()
if args.y != None:
logg.debug('loading keystore file {}'.format(args.y))
signer_address = keystore.import_keystore_file(args.y, password=passphrase)
logg.debug('now have key for signer address {}'.format(signer_address))
signer = EIP155Signer(keystore)
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
conn = rpc.connect_by_config(config)
rpc_id_generator = None
if args.seq:
rpc_id_generator = IntSequenceGenerator()
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
auth = None
#if os.environ.get('RPC_AUTHENTICATION') == 'custom_token':
value = config.get('_AMOUNT')
if os.environ.get('RPC_AUTHENTICATION') == 'basic':
from chainlib.auth import BasicAuth
#auth = CustomHeaderTokenAuth('x-api-key', os.environ['RPC_AUTHENTICATION_STRING'])
auth = BasicAuth(os.environ['RPC_USERNAME'], os.environ['RPC_PASSWORD'])
conn = EthHTTPConnection(args.p)
nonce_oracle = None
if args.nonce != None:
nonce_oracle = OverrideNonceOracle(signer_address, args.nonce, id_generator=rpc_id_generator)
else:
nonce_oracle = RPCNonceOracle(signer_address, conn, id_generator=rpc_id_generator)
#def _max_gas(code=None):
# return 8000000
gas_oracle = None
if args.gas_price != None or args.gas_limit != None:
gas_oracle = OverrideGasOracle(price=args.gas_price, limit=args.gas_limit, id_generator=rpc_id_generator, conn=conn)
else:
gas_oracle = RPCGasOracle(conn, code_callback=ERC20.gas, id_generator=rpc_id_generator)
chain_spec = ChainSpec.from_chain_str(args.i)
chain_id = chain_spec.network_id()
value = args.amount
send = args.s
g = ERC20(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
send = config.true('_RPC_SEND')
def balance(token_address, address, id_generator=None):
o = g.balance(token_address, address, id_generator=id_generator)
def balance(generator, token_address, address, id_generator=None):
o = generator.balance(token_address, address, id_generator=id_generator)
r = conn.do(o)
hx = strip_0x(r)
return int(hx, 16)
token_balance = generator.parse_balance(r)
return token_balance
def main():
recipient = args.recipient
if not args.u and recipient != add_0x(args.recipient):
raise ValueError('invalid checksum address')
signer = rpc.get_signer()
signer_address = rpc.get_sender_address()
gas_oracle = rpc.get_gas_oracle()
nonce_oracle = rpc.get_nonce_oracle()
g = ERC20(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
recipient = to_checksum_address(config.get('_RECIPIENT'))
if not config.true('_UNSAFE') and recipient != add_0x(config.get('_RECIPIENT')):
raise ValueError('invalid checksum address for recipient')
token_address = to_checksum_address(config.get('_EXEC_ADDRESS'))
if not config.true('_UNSAFE') and token_address != add_0x(config.get('_EXEC_ADDRESS')):
raise ValueError('invalid checksum address for contract')
transfer_from_address = None
if config.get('_FROM'):
transfer_from_address = to_checksum_address(config.get('_FROM'))
if not config.true('_UNSAFE') and transfer_from_address != add_0x(config.get('_FROM')):
raise ValueError('invalid checksum address for "from" argument')
check_balance_address = None
if logg.isEnabledFor(logging.DEBUG):
sender_balance = balance(args.a, signer_address, id_generator=rpc_id_generator)
recipient_balance = balance(args.a, recipient, id_generator=rpc_id_generator)
logg.debug('sender {} balance after: {}'.format(signer_address, sender_balance))
logg.debug('recipient {} balance after: {}'.format(recipient, recipient_balance))
if transfer_from_address:
check_balance_address = transfer_from_address
else:
check_balance_address = signer_address
sender_balance = balance(g, token_address, check_balance_address, id_generator=rpc.id_generator)
recipient_balance = balance(g, token_address, recipient, id_generator=rpc.id_generator)
logg.debug('sender {} balance before: {}'.format(check_balance_address, sender_balance))
logg.debug('recipient {} balance before: {}'.format(recipient, recipient_balance))
(tx_hash_hex, o) = g.transfer(args.a, signer_address, recipient, value, id_generator=rpc_id_generator)
if transfer_from_address and not is_same_address(transfer_from_address, signer_address):
(tx_hash_hex, o) = g.transfer_from(token_address, signer_address, transfer_from_address, recipient, value, id_generator=rpc.id_generator)
else:
(tx_hash_hex, o) = g.transfer(token_address, signer_address, recipient, value, id_generator=rpc.id_generator)
if send:
conn.do(o)
if block_last:
r = conn.wait(tx_hash_hex)
if logg.isEnabledFor(logging.DEBUG):
sender_balance = balance(args.a, signer_address, id_generator=rpc_id_generator)
recipient_balance = balance(args.a, recipient, id_generator=rpc_id_generator)
logg.debug('sender {} balance after: {}'.format(signer_address, sender_balance))
sender_balance = balance(g, token_address, check_balance_address, id_generator=rpc.id_generator)
recipient_balance = balance(g, token_address, recipient, id_generator=rpc.id_generator)
logg.debug('sender {} balance after: {}'.format(check_balance_address, sender_balance))
logg.debug('recipient {} balance after: {}'.format(recipient, recipient_balance))
if r['status'] == 0:
logg.critical('VM revert. Wish I could tell you more')
@@ -170,12 +125,7 @@ def main():
print(tx_hash_hex)
else:
if logg.isEnabledFor(logging.INFO):
io_str = io.StringIO()
decode_for_puny_humans(o['params'][0], chain_spec, io_str)
print(io_str.getvalue())
else:
print(o['params'][0])
print(o['params'][0])
if __name__ == '__main__':

File diff suppressed because one or more lines are too long

View File

@@ -38,7 +38,7 @@ class GiftableToken(TxFactory):
@staticmethod
def gas(code=None):
return 1500000
return 2000000
@staticmethod
@@ -71,6 +71,18 @@ class GiftableToken(TxFactory):
return tx
def remove_minter(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
enc = ABIContractEncoder()
enc.method('removeMinter')
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')

View File

@@ -17,17 +17,8 @@ import time
from enum import Enum
# external imports
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from crypto_dev_signer.keystore.dict import DictKeystore
import chainlib.eth.cli
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
@@ -37,83 +28,47 @@ from giftable_erc20_token import GiftableToken
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('--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('--name', default='Giftable Token', type=str, help='Token name')
argparser.add_argument('--symbol', default='GFT', type=str, help='Token symbol')
argparser.add_argument('--decimals', default=18, type=int, help='Token decimals')
argparser.add_argument('-d', action='store_true', help='Dump RPC calls to terminal and do not send')
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('-v', action='store_true', help='Be verbose')
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
arg_flags = chainlib.eth.cli.argflag_std_write
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_argument('--name', dest='token_name', required=True, 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', default=18, type=int, help='Token decimals')
args = argparser.parse_args()
if args.vv:
logg.setLevel(logging.DEBUG)
elif args.v:
logg.setLevel(logging.INFO)
extra_args = {
'token_name': None,
'token_symbol': None,
'token_decimals': None,
}
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=GiftableToken.gas())
block_all = args.ww
block_last = args.w or block_all
wallet = chainlib.eth.cli.Wallet()
wallet.from_config(config)
passphrase_env = 'ETH_PASSPHRASE'
if args.env_prefix != None:
passphrase_env = args.env_prefix + '_' + passphrase_env
passphrase = os.environ.get(passphrase_env)
if passphrase == None:
logg.warning('no passphrase given')
passphrase=''
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
conn = rpc.connect_by_config(config)
signer_address = None
keystore = DictKeystore()
if args.y != None:
logg.debug('loading keystore file {}'.format(args.y))
signer_address = keystore.import_keystore_file(args.y, password=passphrase)
logg.debug('now have key for signer address {}'.format(signer_address))
signer = EIP155Signer(keystore)
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=GiftableToken.gas)
else:
gas_oracle = RPCGasOracle(rpc, code_callback=GiftableToken.gas)
dummy = args.d
token_name = args.name
token_symbol = args.symbol
token_decimals = args.decimals
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
def main():
signer = rpc.get_signer()
signer_address = rpc.get_sender_address()
token_name = config.get('_TOKEN_NAME')
token_symbol = config.get('_TOKEN_SYMBOL')
token_decimals = config.get('_TOKEN_DECIMALS')
gas_oracle = rpc.get_gas_oracle()
nonce_oracle = rpc.get_nonce_oracle()
c = GiftableToken(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
(tx_hash_hex, o) = c.constructor(signer_address, token_name, token_symbol, token_decimals)
if dummy:
print(tx_hash_hex)
print(o)
else:
rpc.do(o)
if block_last:
r = rpc.wait(tx_hash_hex)
if config.get('_RPC_SEND'):
conn.do(o)
if config.get('_WAIT'):
r = conn.wait(tx_hash_hex)
if r['status'] == 0:
sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you')
sys.exit(1)
@@ -123,6 +78,8 @@ def main():
print(address)
else:
print(tx_hash_hex)
else:
print(o)
if __name__ == '__main__':

View File

@@ -15,20 +15,16 @@ import argparse
import logging
import time
# third-party imports
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from crypto_dev_signer.keystore.dict import DictKeystore
# external imports
import chainlib.eth.cli
from chainlib.eth.tx import receipt
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.address import to_checksum_address
from hexathon import (
strip_0x,
add_0x,
)
# local imports
from giftable_erc20_token import GiftableToken
@@ -36,85 +32,50 @@ from giftable_erc20_token import GiftableToken
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('-e', action='store_true', help='Treat all transactions as essential')
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('-a', '--token-address', required='True', dest='a', type=str, help='Giftable token address')
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('--gas-price', type=int, dest='gas_price', help='Override gas price')
argparser.add_argument('--nonce', type=int, help='Override transaction nonce')
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('--recipient', type=str, help='Recipient account address. If not set, tokens will be gifted to the keystore account')
argparser.add_argument('value', type=int, help='Value of tokens to mint and gift')
arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC | chainlib.eth.cli.Flag.WALLET
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_positional('amount', type=int, help='Token amount to gift')
args = argparser.parse_args()
extra_args = {
'amount': None,
}
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=GiftableToken.gas())
if args.vv:
logg.setLevel(logging.DEBUG)
elif args.v:
logg.setLevel(logging.INFO)
wallet = chainlib.eth.cli.Wallet()
wallet.from_config(config)
block_all = args.ww
block_last = args.w or block_all
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
conn = rpc.connect_by_config(config)
passphrase_env = 'ETH_PASSPHRASE'
if args.env_prefix != None:
passphrase_env = args.env_prefix + '_' + passphrase_env
passphrase = os.environ.get(passphrase_env)
if passphrase == None:
logg.warning('no passphrase given')
passphrase=''
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=GiftableToken.gas)
else:
gas_oracle = RPCGasOracle(rpc, code_callback=GiftableToken.gas)
dummy = args.d
token_address = args.a
recipient_address = args.recipient
if recipient_address == None:
recipient_address = signer_address
token_value = args.value
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
def main():
signer = rpc.get_signer()
signer_address = rpc.get_sender_address()
gas_oracle = rpc.get_gas_oracle()
nonce_oracle = rpc.get_nonce_oracle()
recipient_address_input = config.get('_RECIPIENT')
if recipient_address_input == None:
recipient_address_input = signer_address
recipient_address = add_0x(to_checksum_address(recipient_address_input))
if not config.true('_UNSAFE') and recipient_address != add_0x(recipient_address_input):
raise ValueError('invalid checksum address for recipient')
token_address = add_0x(to_checksum_address(config.get('_EXEC_ADDRESS')))
if not config.true('_UNSAFE') and token_address != add_0x(config.get('_EXEC_ADDRESS')):
raise ValueError('invalid checksum address for contract')
token_value = config.get('_AMOUNT')
c = GiftableToken(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
(tx_hash_hex, o) = c.mint_to(token_address, signer_address, recipient_address, token_value)
if dummy:
print(tx_hash_hex)
print(o)
else:
rpc.do(o)
if block_last:
r = rpc.wait(tx_hash_hex)
if config.get('_RPC_SEND'):
conn.do(o)
if config.get('_WAIT'):
r = conn.wait(tx_hash_hex)
if r['status'] == 0:
sys.stderr.write('EVM revert. Wish I had more to tell you')
sys.exit(1)
@@ -122,6 +83,8 @@ def main():
logg.info('mint to {} tx {}'.format(recipient_address, tx_hash_hex))
print(tx_hash_hex)
else:
print(o)
if __name__ == '__main__':

View File

@@ -16,19 +16,15 @@ import logging
import time
# external imports
import chainlib.eth.cli
from chainlib.eth.connection import EthHTTPConnection
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from crypto_dev_signer.keystore.dict import DictKeystore
from chainlib.eth.nonce import (
RPCNonceOracle,
OverrideNonceOracle,
)
from chainlib.eth.gas import (
RPCGasOracle,
OverrideGasOracle,
)
from chainlib.chain import ChainSpec
from chainlib.eth.tx import receipt
from chainlib.eth.address import to_checksum_address
from hexathon import (
strip_0x,
add_0x,
)
# local imports
from giftable_erc20_token import GiftableToken
@@ -36,80 +32,56 @@ from giftable_erc20_token import GiftableToken
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('-a', '--token-address', required='True', dest='a', type=str, help='Giftable token address')
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('--gas-price', type=int, dest='gas_price', help='Override gas price')
argparser.add_argument('--nonce', type=int, help='Override transaction nonce')
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('minter_address', type=str, help='Minter address to add')
arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC | chainlib.eth.cli.Flag.WALLET
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_argument('--rm', action='store_true', help='Remove entry')
argparser.add_positional('minter_address', type=str, help='Address to add or remove as minter')
args = argparser.parse_args()
extra_args = {
'rm': None,
'minter_address': None,
}
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_fee_limit=GiftableToken.gas())
if args.vv:
logg.setLevel(logging.DEBUG)
elif args.v:
logg.setLevel(logging.INFO)
wallet = chainlib.eth.cli.Wallet()
wallet.from_config(config)
block_all = args.ww
block_last = args.w or block_all
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
conn = rpc.connect_by_config(config)
passphrase_env = 'ETH_PASSPHRASE'
if args.env_prefix != None:
passphrase_env = args.env_prefix + '_' + passphrase_env
passphrase = os.environ.get(passphrase_env)
if passphrase == None:
logg.warning('no passphrase given')
passphrase=''
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=GiftableToken.gas)
else:
gas_oracle = RPCGasOracle(rpc, code_callback=GiftableToken.gas)
dummy = args.d
token_address = args.a
minter_address = args.minter_address
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
def main():
signer = rpc.get_signer()
signer_address = rpc.get_sender_address()
gas_oracle = rpc.get_gas_oracle()
nonce_oracle = rpc.get_nonce_oracle()
recipient_address_input = config.get('_RECIPIENT')
if recipient_address_input == None:
recipient_address_input = signer_address
recipient_address = add_0x(to_checksum_address(recipient_address_input))
if not config.true('_UNSAFE') and recipient_address != add_0x(recipient_address_input):
raise ValueError('invalid checksum address for recipient')
token_address = add_0x(to_checksum_address(config.get('_EXEC_ADDRESS')))
if not config.true('_UNSAFE') and token_address != add_0x(config.get('_EXEC_ADDRESS')):
raise ValueError('invalid checksum address for contract')
minter_address = config.get('_MINTER_ADDRESS')
c = GiftableToken(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
(tx_hash_hex, o) = c.add_minter(token_address, signer_address, minter_address)
if dummy:
print(tx_hash_hex)
print(o)
if config.get('_RM'):
(tx_hash_hex, o) = c.remove_minter(token_address, signer_address, minter_address)
else:
rpc.do(o)
if block_last:
r = rpc.wait(tx_hash_hex)
(tx_hash_hex, o) = c.add_minter(token_address, signer_address, minter_address)
if config.get('_RPC_SEND'):
conn.do(o)
if config.get('_WAIT'):
r = conn.wait(tx_hash_hex)
if r['status'] == 0:
sys.stderr.write('EVM revert. Wish I had more to tell you')
sys.exit(1)
@@ -117,6 +89,8 @@ def main():
logg.info('add minter {} to {} tx {}'.format(minter_address, token_address, tx_hash_hex))
print(tx_hash_hex)
else:
print(o)
if __name__ == '__main__':

View File

@@ -1,4 +1,3 @@
confini~=0.3.6rc3
crypto-dev-signer~=0.4.14b6
chainlib-eth~=0.0.5a1
potaahto~=0.0.1a2
confini~=0.5.1
chainlib-eth~=0.0.12
potaahto~=0.1.0

View File

@@ -2,6 +2,8 @@
set -e
set -x
default_pythonpath=$PYTHONPATH:.
export PYTHONPATH=${default_pythonpath:-.}
for f in `ls tests/*.py`; do
python $f
if [ $? -gt 0 ]; then

View File

@@ -1,6 +1,6 @@
[metadata]
name = eth-erc20
version = 0.0.10a2
version = 0.1.2
description = ERC20 interface and simple contract with deployment script that lets any address mint and gift itself tokens.
author = Louis Holbrook
author_email = dev@holbrook.no

File diff suppressed because one or more lines are too long

View File

@@ -23,7 +23,7 @@ contract GiftableToken {
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event TransferFrom(address indexed _from, address indexed _to, address indexed _spender, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
event Mint(address indexed _minter, address indexed _beneficiary, uint256 _value);
event Mint(address indexed _minter, address indexed _beneficiary, uint256 _value); // Minter
constructor(string memory _name, string memory _symbol, uint8 _decimals) public {
owner = msg.sender;
@@ -33,6 +33,7 @@ contract GiftableToken {
minters[msg.sender] = true;
}
// Implements Minter
function mintTo(address _to, uint256 _value) public returns (bool) {
require(minters[msg.sender]);
@@ -52,6 +53,11 @@ contract GiftableToken {
return true;
}
// Implements Writer
function addWriter(address _minter) public returns (bool) {
return addMinter(_minter);
}
function removeMinter(address _minter) public returns (bool) {
require(msg.sender == owner || msg.sender == _minter);
@@ -60,6 +66,11 @@ contract GiftableToken {
return true;
}
// Implements Writer
function deleteWriter(address _minter) public returns (bool) {
return removeMinter(_minter);
}
// Implements ERC20
function transfer(address _to, uint256 _value) public returns (bool) {
require(balanceOf[msg.sender] >= _value);
@@ -101,6 +112,9 @@ contract GiftableToken {
if (_sum == 0x01ffc9a7) { // EIP165
return true;
}
if (_sum == 0x80c84bd6) { // Writer
return true;
}
return false;
}
}