Compare commits

..

1 Commits

Author SHA1 Message Date
lash
5189364fda Add info cli command 2022-01-22 12:01:56 +00:00
28 changed files with 219 additions and 508 deletions

1
.gitignore vendored
View File

@@ -5,4 +5,3 @@ build/
gmon.out gmon.out
*.egg-info *.egg-info
.venv/ .venv/
.idea

View File

@@ -1,67 +1,26 @@
stages: stages:
- test - test
- run-coverage
- slither-analyzer - slither-analyzer
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
cache:
paths:
- .cache/pip
- venv/
before_script:
- python -V # Print out python version for debugging
- pip install virtualenv
- virtualenv venv
- source venv/bin/activate
test: test:
image: registry.gitlab.com/grassrootseconomics/cic-base-images/ci-solc-python:latest image: registry.gitlab.com/grassrootseconomics/cic-base-images/ci-solc-py-alpine:latest
cache:
- key:
files:
- requirements.txt
- test_requirements.txt
paths:
- /root/.cache/pip
script: script:
# build abi
- 'echo "Running Solc Version: $(solc --version)"'
- cd solidity
- solc --evm-version=byzantium GiftableToken.sol --abi | awk 'NR>3' > GiftableToken.abi.json
# build bin
- solc GiftableToken.sol --bin | awk 'NR>3' > GiftableToken.bin &&
truncate -s "$((`stat -t -c "%s" GiftableToken.bin`-1))" GiftableToken.bin
# install test dependencies
- cd ../python
- export PYTHONPATH=.
- pip install --extra-index-url https://pip.grassrootseconomics.net
--extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple
-r requirements.txt -r test_requirements.txt
# run tests
- bash run_tests.sh
run-coverage:
stage: test
image: registry.gitlab.com/grassrootseconomics/cic-base-images/ci-solc-python:latest
script:
- cd python - cd python
- export PYTHONPATH=. - pip install --extra-index-url https://pip.grassrootseconomics.net:8433 -r test_requirements.txt
- pip install --extra-index-url https://pip.grassrootseconomics.net - pip install --extra-index-url https://pip.grassrootseconomics.net:8433 -r test_requirements.txt
--extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple - bash run_tests.sh
-r requirements.txt -r test_requirements.txt
- pip install pytest pytest-cov
- coverage run -m pytest
- coverage html
- coverage report --fail-under=90
coverage: '/^TOTAL.+?(\d+\%)$/'
artifacts:
reports:
cobertura: python/htmlcov/index.html
slither-analyzer:
image: registry.gitlab.com/grassrootseconomics/cic-base-images/ci-solc-python:latest slither-analize:
allow_failure: true image: registry.gitlab.com/grassrootseconomics/cic-base-images/ci-solc-py-alpine:latest
script: script:
- cd solidity - pip install slither-analyzer
- slither GiftableToken.sol - slither solidity/
- slither GiftableToken.sol --print human-summary

33
Dockerfile Normal file
View File

@@ -0,0 +1,33 @@
FROM ethereum/solc:0.6.12
FROM python:3.8.6-alpine
COPY --from=0 /usr/bin/solc /usr/bin/solc
RUN apk update &&\
apk add gcc bash musl-dev libffi-dev openssl-dev autoconf automake build-base \
libtool pkgconfig python3-dev cargo
WORKDIR /usr/src
# Try to keep everything above here re-usable!
COPY ./solidity/ /usr/src/giftable_erc20_token/solidity/
COPY ./python/ /usr/src/giftable_erc20_token/python/
RUN chmod +x ./python/run_tests.sh
RUN cd giftable_erc20_token/solidity && \
solc GiftableToken.sol --abi | awk 'NR>3' > GiftableToken.abi.json
RUN cd giftable_erc20_token/solidity && \
solc GiftableToken.sol --bin | awk 'NR>3' > GiftableToken.bin && \
truncate -s "$((`stat -t -c "%s" GiftableToken.bin`-1))" GiftableToken.bin
RUN cd giftable_erc20_token/python && \
pip install --extra-index-url https://pip.grassrootseconomics.net:8433 .
RUN pip install slither-analyzer
# To deploy:
# giftable-token-deploy --contracts-dir giftable_erc20_token/solidity/ <amount>

View File

@@ -1,25 +0,0 @@
# ETH-ERC20 Solidity Contract
### solidity
To generate bytecode and tests install solc 8.x.x and run the solc bits below or execute the following:
```
docker run -v `pwd`:/src registry.gitlab.com/grassrootseconomics/cic-base-images/ci-solc-python:latest solc --evm-version=byzantium solidity/GiftableToken.sol --abi
```
```
docker run -v `pwd`:/src registry.gitlab.com/grassrootseconomics/cic-base-images/ci-solc-python:latest solc GiftableToken.sol --bin | awk 'NR>3' > GiftableToken.bin
```
### python unit tests
```
pip install --extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple \
--extra-index-url https://pip.grassrootseconomics.net \
-r requirements.txt -r test_requirements.txt
bash python/run_tests.sh
```

Binary file not shown.

View File

@@ -1,7 +0,0 @@
[run]
branch = True
[report]
omit = .venv/*
**/runnable/*.py
[html]

View File

@@ -1,18 +1,9 @@
* 0.4.1 * 0.0.10-pending
- Implement chainlib 0.4.x for giftable token
- Fix broken inputs in erc20 cli tools
* 0.4.0
- Implement chainlib 0.3.0
* 0.3.1
- Upgrade chainlib to 0.2.0
* 0.1.10
- Apply transfer ownership protection in GiftableToken contract
* 0.1.9
- Add statictoken contract
* 0.0.10
- Upgrade chainlib dependency, providing customizable jsonrpc id - Upgrade chainlib dependency, providing customizable jsonrpc id
- Move to chainlib-eth - Move to chainlib-eth
* 0.0.7 * 0.0.9-unreleased
* 0.0.8-unreleased
* 0.0.7-unreleased
- Implement keystore signing. - Implement keystore signing.
- Change makefile to compile contract for Byzantium. - Change makefile to compile contract for Byzantium.
- Change mint method name to mintTo - Change mint method name to mintTo

View File

@@ -1,3 +0,0 @@
On arch, Python.h is in non-standard location, which may cause pip install -r test_requirements to fail.
If so, set env CFLAGS=`pkg-config --cflags python3`

View File

@@ -217,6 +217,7 @@ class ERC20(TxFactory):
l = len(r) l = len(r)
m = v[:l] m = v[:l]
if m != r: if m != r:
logg.error('method mismatch, expected {}, got {}'.format(r, m))
raise RequestMismatchException(v) raise RequestMismatchException(v)
cursor += l cursor += l
@@ -243,6 +244,7 @@ class ERC20(TxFactory):
l = len(r) l = len(r)
m = v[:l] m = v[:l]
if m != r: if m != r:
logg.error('method mismatch, expected {}, got {}'.format(r, m))
raise RequestMismatchException(v) raise RequestMismatchException(v)
cursor += l cursor += l
@@ -271,6 +273,7 @@ class ERC20(TxFactory):
l = len(r) l = len(r)
m = v[:l] m = v[:l]
if m != r: if m != r:
logg.error('method mismatch, expected {}, got {}'.format(r, m))
raise RequestMismatchException(v) raise RequestMismatchException(v)
cursor += l cursor += l

View File

@@ -26,15 +26,6 @@ import sha3
# external imports # external imports
import chainlib.eth.cli 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.address import to_checksum_address from chainlib.eth.address import to_checksum_address
from chainlib.eth.connection import EthHTTPConnection from chainlib.eth.connection import EthHTTPConnection
from chainlib.eth.gas import ( from chainlib.eth.gas import (
@@ -42,55 +33,35 @@ from chainlib.eth.gas import (
balance, balance,
) )
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
from chainlib.eth.cli.log import process_log
from chainlib.eth.settings import process_settings
from chainlib.settings import ChainSettings
# local imports # local imports
from eth_erc20 import ERC20 from eth_erc20 import ERC20
logging.basicConfig(level=logging.WARNING)
def process_config_local(config, arg, args, flags):
recipient = None
address = config.get('_POSARG')
if address:
recipient = add_0x(address)
else:
recipient = stdin_arg()
config.add(recipient, '_RECIPIENT', False)
return config
logg = logging.getLogger() logg = logging.getLogger()
arg_flags = ArgFlag() arg_flags = chainlib.eth.cli.argflag_std_read | chainlib.eth.cli.Flag.EXEC
arg = Arg(arg_flags) argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
flags = arg_flags.STD_READ | arg_flags.EXEC argparser.add_positional('address', type=str, help='Ethereum address of recipient')
argparser = chainlib.eth.cli.ArgumentParser()
argparser = process_args(argparser, arg, flags)
argparser.add_argument('address', type=str, help='Ethereum address of recipient')
args = argparser.parse_args() args = argparser.parse_args()
config = chainlib.eth.cli.Config.from_args(args, arg_flags)
logg = process_log(args, logg) 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)
config = Config() rpc = chainlib.eth.cli.Rpc()
config = process_config(config, arg, args, flags, positional_name='address') conn = rpc.connect_by_config(config)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
settings = ChainSettings() chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
settings = process_settings(settings, config)
logg.debug('settings loaded:\n{}'.format(settings)) token_address = config.get('_EXEC_ADDRESS')
def main(): def main():
token_address = settings.get('EXEC') g = ERC20(chain_spec=chain_spec, gas_oracle=rpc.get_gas_oracle())
conn = settings.get('CONN')
g = ERC20(
chain_spec=settings.get('CHAIN_SPEC'),
gas_oracle=settings.get('GAS_ORACLE'),
)
# determine decimals # determine decimals
decimals_o = g.decimals(token_address) decimals_o = g.decimals(token_address)
@@ -109,7 +80,7 @@ def main():
logg.info('symbol {}'.format(token_symbol)) logg.info('symbol {}'.format(token_symbol))
# get balance # get balance
balance_o = g.balance(token_address, settings.get('RECIPIENT')) balance_o = g.balance(token_address, holder_address)
r = conn.do(balance_o) r = conn.do(balance_o)
hx = strip_0x(r) hx = strip_0x(r)

View File

@@ -26,16 +26,6 @@ import sha3
# external imports # external imports
import chainlib.eth.cli import chainlib.eth.cli
from chainlib.eth.cli.arg import (
Arg,
ArgFlag,
process_args,
stdin_arg,
)
from chainlib.eth.cli.config import (
Config,
process_config,
)
from chainlib.eth.address import to_checksum_address from chainlib.eth.address import to_checksum_address
from chainlib.eth.connection import EthHTTPConnection from chainlib.eth.connection import EthHTTPConnection
from chainlib.eth.gas import ( from chainlib.eth.gas import (
@@ -43,105 +33,48 @@ from chainlib.eth.gas import (
balance, balance,
) )
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
from chainlib.eth.settings import process_settings
from chainlib.settings import ChainSettings
from chainlib.eth.cli.log import process_log
# local imports # local imports
from eth_erc20 import ERC20 from eth_erc20 import ERC20
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger() logg = logging.getLogger()
arg_flags = chainlib.eth.cli.argflag_std_read | chainlib.eth.cli.Flag.EXEC
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
def process_config_local(config, arg, args, flags):
contract = None
try:
contract = config.get('_EXEC_ADDRESS')
except KeyError:
pass
if contract == None:
address = config.get('_POSARG')
if address:
contract = add_0x(address)
else:
contract = stdin_arg()
config.add(contract, '_CONTRACT', False)
return config
arg_flags = ArgFlag()
arg = Arg(arg_flags)
flags = arg_flags.STD_READ | arg_flags.EXEC | arg_flags.TAB
argparser = chainlib.eth.cli.ArgumentParser()
argparser = process_args(argparser, arg, flags)
argparser.add_argument('contract_address', type=str, help='Token contract address (may also be specified by -e)')
args = argparser.parse_args() args = argparser.parse_args()
config = chainlib.eth.cli.Config.from_args(args, arg_flags)
logg = process_log(args, logg) rpc = chainlib.eth.cli.Rpc()
conn = rpc.connect_by_config(config)
config = Config() chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
config = process_config(config, arg, args, flags, positional_name='contract_address')
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
settings = ChainSettings() token_address = config.get('_EXEC_ADDRESS')
settings = process_settings(settings, config)
logg.debug('settings loaded:\n{}'.format(settings))
def main(): def main():
token_address = config.get('_CONTRACT') g = ERC20(chain_spec=chain_spec, gas_oracle=rpc.get_gas_oracle())
conn = settings.get('CONN')
g = ERC20(
chain_spec=settings.get('CHAIN_SPEC'),
gas_oracle=settings.get('GAS_ORACLE'),
)
outkeys = config.get('_OUTARG') name_o = g.name(token_address)
r = conn.do(name_o)
token_name = g.parse_name(r)
print('Name: {}'.format(token_name))
if not outkeys or 'address' in outkeys: symbol_o = g.symbol(token_address)
name_o = g.name(token_address) r = conn.do(symbol_o)
r = conn.do(name_o) token_symbol = g.parse_symbol(r)
token_name = g.parse_name(r) print('Symbol: {}'.format(token_symbol))
s = ''
if not config.true('_RAW'):
s = 'Name: '
s += token_name
print(s)
if not outkeys or 'symbol' in outkeys: decimals_o = g.decimals(token_address)
symbol_o = g.symbol(token_address) r = conn.do(decimals_o)
r = conn.do(symbol_o) decimals = int(strip_0x(r), 16)
token_symbol = g.parse_symbol(r) print('Decimals: {}'.format(decimals))
s = ''
if not config.true('_RAW'):
s = 'Symbol: '
s += token_symbol
print(s)
if not outkeys or 'decimals' in outkeys: supply_o = g.total_supply(token_address)
decimals_o = g.decimals(token_address) r = conn.do(supply_o)
r = conn.do(decimals_o) supply = int(strip_0x(r), 16)
decimals = int(strip_0x(r), 16) print('Total supply: {}'.format(supply))
s = ''
if not config.true('_RAW'):
s = 'Decimals: '
s += str(decimals)
print(s)
if not outkeys or 'supply' in outkeys:
supply_o = g.total_supply(token_address)
r = conn.do(supply_o)
supply = int(strip_0x(r), 16)
s = ''
if not config.true('_RAW'):
s = 'Supply: '
s += str(supply)
print(s)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -21,53 +21,44 @@ from hexathon import (
add_0x, add_0x,
strip_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 import chainlib.eth.cli
from chainlib.eth.cli.log import process_log
from chainlib.eth.settings import process_settings
from chainlib.settings import ChainSettings
from chainlib.eth.cli.arg import (
Arg,
ArgFlag,
process_args,
)
from chainlib.eth.cli.config import (
Config,
process_config,
)
# local imports # local imports
from eth_erc20 import ERC20 from eth_erc20 import ERC20
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger() logg = logging.getLogger()
arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC | chainlib.eth.cli.Flag.WALLET
def process_config_local(config, arg, args, flags): argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
config.add(config.get('_POSARG'), '_VALUE', False) argparser.add_positional('amount', type=int, help='Token amount to send')
return config
arg_flags = ArgFlag()
arg = Arg(arg_flags)
flags = arg_flags.STD_WRITE | arg_flags.EXEC | arg_flags.WALLET
argparser = chainlib.eth.cli.ArgumentParser()
argparser = process_args(argparser, arg, flags)
argparser.add_argument('value', type=str, help='Token value to send')
args = argparser.parse_args() 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)
logg = process_log(args, logg) block_all = args.ww
block_last = args.w or block_all
config = Config() wallet = chainlib.eth.cli.Wallet()
config = process_config(config, arg, args, flags, positional_name='value') wallet.from_config(config)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
settings = ChainSettings() rpc = chainlib.eth.cli.Rpc(wallet=wallet)
settings = process_settings(settings, config) conn = rpc.connect_by_config(config)
logg.debug('settings loaded:\n{}'.format(settings))
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
value = config.get('_AMOUNT')
send = config.true('_RPC_SEND')
def balance(conn, generator, token_address, address, id_generator=None): def balance(generator, token_address, address, id_generator=None):
o = generator.balance(token_address, address, id_generator=id_generator) o = generator.balance(token_address, address, id_generator=id_generator)
r = conn.do(o) r = conn.do(o)
token_balance = generator.parse_balance(r) token_balance = generator.parse_balance(r)
@@ -75,32 +66,37 @@ def balance(conn, generator, token_address, address, id_generator=None):
def main(): def main():
token_address = settings.get('EXEC') signer = rpc.get_signer()
signer_address = settings.get('SENDER_ADDRESS') signer_address = rpc.get_sender_address()
recipient = settings.get('RECIPIENT')
value = settings.get('VALUE') gas_oracle = rpc.get_gas_oracle()
conn = settings.get('CONN') nonce_oracle = rpc.get_nonce_oracle()
g = ERC20(
settings.get('CHAIN_SPEC'), g = ERC20(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
signer=settings.get('SIGNER'),
gas_oracle=settings.get('GAS_ORACLE'), recipient = to_checksum_address(config.get('_RECIPIENT'))
nonce_oracle=settings.get('NONCE_ORACLE'), 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')
if logg.isEnabledFor(logging.DEBUG): if logg.isEnabledFor(logging.DEBUG):
sender_balance = balance(conn, g, token_address, signer_address, id_generator=settings.get('RPC_ID_GENERATOR')) sender_balance = balance(g, token_address, signer_address, id_generator=rpc.id_generator)
recipient_balance = balance(conn, g, token_address, recipient, id_generator=settings.get('RPC_ID_GENERATOR')) recipient_balance = balance(g, token_address, recipient, id_generator=rpc.id_generator)
logg.debug('sender {} balance before: {}'.format(signer_address, sender_balance)) logg.debug('sender {} balance before: {}'.format(signer_address, sender_balance))
logg.debug('recipient {} balance before: {}'.format(recipient, recipient_balance)) logg.debug('recipient {} balance before: {}'.format(recipient, recipient_balance))
(tx_hash_hex, o) = g.transfer(token_address, signer_address, recipient, value, id_generator=settings.get('RPC_ID_GENERATOR')) (tx_hash_hex, o) = g.transfer(token_address, signer_address, recipient, value, id_generator=rpc.id_generator)
if settings.get('RPC_SEND'): if send:
conn.do(o) conn.do(o)
if settings.get('WAIT'): if block_last:
r = conn.wait(tx_hash_hex) r = conn.wait(tx_hash_hex)
if logg.isEnabledFor(logging.DEBUG): if logg.isEnabledFor(logging.DEBUG):
sender_balance = balance(conn, g, token_address, signer_address, id_generator=settings.get('RPC_ID_GENERATOR')) sender_balance = balance(g, token_address, signer_address, id_generator=rpc.id_generator)
recipient_balance = balance(conn, g, token_address, recipient, id_generator=settings.get('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('sender {} balance after: {}'.format(signer_address, sender_balance))
logg.debug('recipient {} balance after: {}'.format(recipient, recipient_balance)) logg.debug('recipient {} balance after: {}'.format(recipient, recipient_balance))
if r['status'] == 0: if r['status'] == 0:

File diff suppressed because one or more lines are too long

View File

@@ -95,5 +95,3 @@ class GiftableToken(TxFactory):
tx = self.set_code(tx, data) tx = self.set_code(tx, data)
tx = self.finalize(tx, tx_format) tx = self.finalize(tx, tx_format)
return tx return tx

View File

@@ -1,5 +1,3 @@
#!python3
"""Deploys giftable token """Deploys giftable token
.. moduleauthor:: Louis Holbrook <dev@holbrook.no> .. moduleauthor:: Louis Holbrook <dev@holbrook.no>
@@ -20,76 +18,56 @@ from enum import Enum
# external imports # external imports
import chainlib.eth.cli import chainlib.eth.cli
from chainlib.chain import ChainSpec
from chainlib.eth.connection import EthHTTPConnection
from chainlib.eth.tx import receipt from chainlib.eth.tx import receipt
from chainlib.settings import ChainSettings
from chainlib.eth.cli.log import process_log
from chainlib.eth.settings import process_settings
from chainlib.eth.cli.arg import (
Arg,
ArgFlag,
process_args,
)
from chainlib.eth.cli.config import (
Config,
process_config,
)
# local imports # local imports
from giftable_erc20_token import GiftableToken from giftable_erc20_token import GiftableToken
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger() logg = logging.getLogger()
arg_flags = chainlib.eth.cli.argflag_std_write
def process_config_local(config, arg, args, flags): argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
config.add(args.token_name, '_TOKEN_NAME', False)
config.add(args.token_symbol, '_TOKEN_SYMBOL', False)
config.add(args.token_decimals, '_TOKEN_DECIMALS', False)
return config
arg_flags = ArgFlag()
arg = Arg(arg_flags)
flags = arg_flags.STD_WRITE | arg_flags.WALLET
argparser = chainlib.eth.cli.ArgumentParser()
argparser = process_args(argparser, arg, flags)
argparser.add_argument('--name', dest='token_name', required=True, type=str, help='Token name') 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('--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', default=18, type=int, help='Token decimals')
args = argparser.parse_args() args = argparser.parse_args()
logg = process_log(args, logg) 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())
config = Config() wallet = chainlib.eth.cli.Wallet()
config = process_config(config, arg, args, flags) wallet.from_config(config)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
settings = ChainSettings() rpc = chainlib.eth.cli.Rpc(wallet=wallet)
settings = process_settings(settings, config) conn = rpc.connect_by_config(config)
logg.debug('settings loaded:\n{}'.format(settings))
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
def main(): def main():
signer_address = settings.get('SENDER_ADDRESS') signer = rpc.get_signer()
signer_address = rpc.get_sender_address()
c = GiftableToken( token_name = config.get('_TOKEN_NAME')
settings.get('CHAIN_SPEC'), token_symbol = config.get('_TOKEN_SYMBOL')
signer=settings.get('SIGNER'), token_decimals = config.get('_TOKEN_DECIMALS')
gas_oracle=settings.get('GAS_ORACLE'),
nonce_oracle=settings.get('NONCE_ORACLE'),
)
(tx_hash_hex, o) = c.constructor( gas_oracle = rpc.get_gas_oracle()
signer_address, nonce_oracle = rpc.get_nonce_oracle()
config.get('_TOKEN_NAME'),
config.get('_TOKEN_SYMBOL'), c = GiftableToken(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
config.get('_TOKEN_DECIMALS'),
) (tx_hash_hex, o) = c.constructor(signer_address, token_name, token_symbol, token_decimals)
if settings.get('RPC_SEND'): if config.get('_RPC_SEND'):
conn.do(o) conn.do(o)
if settings.get('WAIT'): if config.get('_WAIT'):
r = conn.wait(tx_hash_hex) r = conn.wait(tx_hash_hex)
if r['status'] == 0: if r['status'] == 0:
sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you') sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you')

View File

@@ -17,21 +17,10 @@ import time
# external imports # external imports
import chainlib.eth.cli import chainlib.eth.cli
from chainlib.eth.connection import EthHTTPConnection
from chainlib.chain import ChainSpec
from chainlib.eth.tx import receipt from chainlib.eth.tx import receipt
from chainlib.settings import ChainSettings
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.eth.address import to_checksum_address
from chainlib.eth.cli.arg import (
Arg,
ArgFlag,
process_args,
)
from chainlib.eth.cli.config import (
Config,
process_config,
)
from hexathon import ( from hexathon import (
strip_0x, strip_0x,
add_0x, add_0x,
@@ -40,41 +29,37 @@ from hexathon import (
# local imports # local imports
from giftable_erc20_token import GiftableToken from giftable_erc20_token import GiftableToken
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger() logg = logging.getLogger()
arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC | chainlib.eth.cli.Flag.WALLET
def process_config_local(config, arg, args, flags): argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
config.add(args.rm, '_RM', False)
config.add(add_0x(args.minter_address[0]), '_MINTER_ADDRESS', False)
return config
arg_flags = ArgFlag()
arg = Arg(arg_flags)
flags = arg_flags.STD_WRITE | arg_flags.EXEC | arg_flags.WALLET
argparser = chainlib.eth.cli.ArgumentParser()
argparser = process_args(argparser, arg, flags)
argparser.add_argument('--rm', action='store_true', help='Remove entry') argparser.add_argument('--rm', action='store_true', help='Remove entry')
argparser.add_argument('minter_address', type=str, help='Address to add or remove as minter') argparser.add_positional('minter_address', type=str, help='Address to add or remove as minter')
args = argparser.parse_args() 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())
logg = process_log(args, logg) wallet = chainlib.eth.cli.Wallet()
wallet.from_config(config)
config = Config() rpc = chainlib.eth.cli.Rpc(wallet=wallet)
config = process_config(config, arg, args, flags) conn = rpc.connect_by_config(config)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
settings = ChainSettings() chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
settings = process_settings(settings, config)
logg.debug('settings loaded:\n{}'.format(settings))
def main(): def main():
signer_address = settings.get('SENDER_ADDRESS') signer = rpc.get_signer()
signer_address = rpc.get_sender_address()
recipient_address_input = settings.get('RECIPIENT') gas_oracle = rpc.get_gas_oracle()
nonce_oracle = rpc.get_nonce_oracle()
recipient_address_input = config.get('_RECIPIENT')
if recipient_address_input == None: if recipient_address_input == None:
recipient_address_input = signer_address recipient_address_input = signer_address
@@ -82,30 +67,20 @@ def main():
if not config.true('_UNSAFE') and recipient_address != add_0x(recipient_address_input): if not config.true('_UNSAFE') and recipient_address != add_0x(recipient_address_input):
raise ValueError('invalid checksum address for recipient') 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') minter_address = config.get('_MINTER_ADDRESS')
c = GiftableToken( c = GiftableToken(chain_spec, signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle)
settings.get('CHAIN_SPEC'),
signer=settings.get('SIGNER'),
gas_oracle=settings.get('GAS_ORACLE'),
nonce_oracle=settings.get('NONCE_ORACLE'),
)
if config.get('_RM'): if config.get('_RM'):
(tx_hash_hex, o) = c.remove_minter( (tx_hash_hex, o) = c.remove_minter(token_address, signer_address, minter_address)
settings.get('EXEC'),
signer_address,
minter_address,
)
else: else:
(tx_hash_hex, o) = c.add_minter( (tx_hash_hex, o) = c.add_minter(token_address, signer_address, minter_address)
settings.get('EXEC'),
signer_address,
minter_address,
)
if settings.get('RPC_SEND'): if config.get('_RPC_SEND'):
conn.do(o) conn.do(o)
if settings.get('WAIT'): if config.get('_WAIT'):
r = conn.wait(tx_hash_hex) r = conn.wait(tx_hash_hex)
if r['status'] == 0: if r['status'] == 0:
sys.stderr.write('EVM revert. Wish I had more to tell you') sys.stderr.write('EVM revert. Wish I had more to tell you')

View File

@@ -1,3 +1,3 @@
confini~=0.6.1 confini~=0.5.2
chainlib-eth~=0.4.2 chainlib-eth~=0.0.14
potaahto~=0.1.1 potaahto==0.1.0

0
python/run_tests.sh Executable file → Normal file
View File

View File

@@ -1,6 +1,6 @@
[metadata] [metadata]
name = eth-erc20 name = eth-erc20
version = 0.5.0 version = 0.1.5
description = ERC20 interface and simple contract with deployment script that lets any address mint and gift itself tokens. description = ERC20 interface and simple contract with deployment script that lets any address mint and gift itself tokens.
author = Louis Holbrook author = Louis Holbrook
author_email = dev@holbrook.no author_email = dev@holbrook.no
@@ -30,14 +30,11 @@ packages =
eth_erc20 eth_erc20
eth_erc20.data eth_erc20.data
eth_erc20.runnable eth_erc20.runnable
static_token.data
[options.package_data] [options.package_data]
* = * =
data/GiftableToken.json data/GiftableToken.json
data/GiftableToken.bin data/GiftableToken.bin
data/StaticToken.json
data/StaticToken.bin
data/ERC20.json data/ERC20.json
[options.entry_points] [options.entry_points]
@@ -47,4 +44,3 @@ console_scripts =
giftable-token-minter = giftable_erc20_token.runnable.minter:main giftable-token-minter = giftable_erc20_token.runnable.minter:main
erc20-transfer = eth_erc20.runnable.transfer:main erc20-transfer = eth_erc20.runnable.transfer:main
erc20-balance = eth_erc20.runnable.balance:main erc20-balance = eth_erc20.runnable.balance:main
erc20-info = eth_erc20.runnable.info:main

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"uint256","name":"_supply","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"TransferFrom","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

View File

@@ -12,7 +12,6 @@ from chainlib.eth.tx import (
receipt, receipt,
) )
from chainlib.eth.address import to_checksum_address from chainlib.eth.address import to_checksum_address
from hexathon import strip_0x
# local imports # local imports
from giftable_erc20_token import GiftableToken from giftable_erc20_token import GiftableToken
@@ -103,7 +102,7 @@ class TestToken(EthTesterCase):
o = transaction(tx_hash) o = transaction(tx_hash)
r = self.rpc.do(o) r = self.rpc.do(o)
data = c.parse_transfer_request(r['data']) data = c.parse_transfer_request(r['data'])
self.assertEqual(data[0], strip_0x(self.accounts[1])) self.assertEqual(data[0], self.accounts[1])
self.assertEqual(data[1], 1000) self.assertEqual(data[1], 1000)
@@ -125,7 +124,7 @@ class TestToken(EthTesterCase):
o = transaction(tx_hash) o = transaction(tx_hash)
r = self.rpc.do(o) r = self.rpc.do(o)
data = c.parse_approve_request(r['data']) data = c.parse_approve_request(r['data'])
self.assertEqual(data[0], strip_0x(self.accounts[1])) self.assertEqual(data[0], self.accounts[1])
self.assertEqual(data[1], 1000) self.assertEqual(data[1], 1000)
nonce_oracle = RPCNonceOracle(self.accounts[1], conn=self.conn) nonce_oracle = RPCNonceOracle(self.accounts[1], conn=self.conn)
@@ -139,8 +138,8 @@ class TestToken(EthTesterCase):
o = transaction(tx_hash) o = transaction(tx_hash)
r = self.rpc.do(o) r = self.rpc.do(o)
data = c.parse_transfer_from_request(r['data']) data = c.parse_transfer_from_request(r['data'])
self.assertEqual(data[0], strip_0x(self.accounts[0])) self.assertEqual(data[0], self.accounts[0])
self.assertEqual(data[1], strip_0x(self.accounts[2])) self.assertEqual(data[1], self.accounts[2])
self.assertEqual(data[2], 1001) self.assertEqual(data[2], 1001)
(tx_hash, o) = c.transfer_from(self.address, self.accounts[1], self.accounts[0], self.accounts[2], 1000) (tx_hash, o) = c.transfer_from(self.address, self.accounts[1], self.accounts[0], self.accounts[2], 1000)

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
pragma solidity >=0.6.11; pragma solidity >0.6.11;
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// File-Version: 2 // File-Version: 2
@@ -93,7 +93,6 @@ contract GiftableToken {
// Implements EIP173 // Implements EIP173
function transferOwnership(address _newOwner) public returns (bool) { function transferOwnership(address _newOwner) public returns (bool) {
require(msg.sender == owner);
owner = _newOwner; owner = _newOwner;
} }

View File

@@ -4,11 +4,7 @@ all:
$(SOLC) --bin GiftableToken.sol --evm-version byzantium | awk 'NR>3' > GiftableToken.bin $(SOLC) --bin GiftableToken.sol --evm-version byzantium | awk 'NR>3' > GiftableToken.bin
truncate -s -1 GiftableToken.bin truncate -s -1 GiftableToken.bin
$(SOLC) --abi GiftableToken.sol --evm-version byzantium | awk 'NR>3' > GiftableToken.json $(SOLC) --abi GiftableToken.sol --evm-version byzantium | awk 'NR>3' > GiftableToken.json
$(SOLC) --bin StaticToken.sol --evm-version byzantium | awk 'NR>3' > StaticToken.bin
truncate -s -1 StaticToken.bin
$(SOLC) --abi StaticToken.sol --evm-version byzantium | awk 'NR>3' > StaticToken.json
install: all install: all
cp -v Giftable*{json,bin} ../python/giftable_erc20_token/data/ cp -v *{json,bin} ../python/giftable_erc20_token/data/
cp -v Static*{json,bin} ../python/static_token/data/

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"uint256","name":"_supply","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"TransferFrom","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

View File

@@ -1,76 +0,0 @@
pragma solidity >0.6.11;
// SPDX-License-Identifier: GPL-3.0-or-later
contract StaticToken {
address owner;
mapping(address => bool) minters;
// Implements ERC20
string public name;
// Implements ERC20
string public symbol;
// Implements ERC20
uint8 public decimals;
// Implements ERC20
uint256 public totalSupply;
// Implements ERC20
mapping (address => uint256) public balanceOf;
// Implements ERC20
mapping (address => mapping (address => uint256)) public allowance;
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);
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _supply) public {
owner = msg.sender;
name = _name;
symbol = _symbol;
decimals = _decimals;
totalSupply = _supply;
balanceOf[owner] = _supply;
}
// Implements ERC20
function transfer(address _to, uint256 _value) public returns (bool) {
require(balanceOf[msg.sender] >= _value);
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
// Implements ERC20
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(allowance[_from][msg.sender] >= _value);
require(balanceOf[_from] >= _value);
allowance[_from][msg.sender] = allowance[_from][msg.sender] - _value;
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
emit TransferFrom(_from, _to, msg.sender, _value);
return true;
}
// Implements ERC20
function approve(address _spender, uint256 _value) public returns (bool) {
if (_value > 0) {
require(allowance[msg.sender][_spender] == 0);
}
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
// Implements EIP165
function supportsInterface(bytes4 _sum) public returns (bool) {
if (_sum == 0xc6bb4b70) { // ERC20
return true;
}
if (_sum == 0x01ffc9a7) { // EIP165
return true;
}
return false;
}
}