chainlib

Generic blockchain access library and tooling
Log | Files | Refs | README | LICENSE

commit 4788326c4e7348da27c945a5c576582eeacfc14b
Author: nolash <dev@holbrook.no>
Date:   Mon,  8 Feb 2021 10:39:42 +0100

Initial commit

Diffstat:
Acic_tools/eth/checksum.py | 28++++++++++++++++++++++++++++
Acic_tools/eth/connection.py | 31+++++++++++++++++++++++++++++++
Acic_tools/eth/constant.py | 2++
Acic_tools/eth/error.py | 8++++++++
Acic_tools/eth/hash.py | 7+++++++
Acic_tools/eth/method.py | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acic_tools/eth/runnable/__init__.py | 0
Acic_tools/eth/runnable/balance.py | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acic_tools/eth/runnable/checksum.py | 8++++++++
Arequirements.txt | 13+++++++++++++
Asetup.cfg | 36++++++++++++++++++++++++++++++++++++
Asetup.py | 16++++++++++++++++
12 files changed, 309 insertions(+), 0 deletions(-)

diff --git a/cic_tools/eth/checksum.py b/cic_tools/eth/checksum.py @@ -0,0 +1,28 @@ +# third-party imports +import sha3 +from hexathon import ( + strip_0x, + uniform, + ) + + +def to_checksum(address_hex): + + address_hex = strip_0x(address_hex) + address_hex = uniform(address_hex) + h = sha3.keccak_256() + h.update(address_hex.encode('utf-8')) + z = h.digest() + + checksum_address_hex = '0x' + + for (i, c) in enumerate(address_hex): + if c in '1234567890': + checksum_address_hex += c + elif c in 'abcdef': + if z[int(i / 2)] & (0x80 >> ((i % 2) * 4)) > 1: + checksum_address_hex += c.upper() + else: + checksum_address_hex += c + + return checksum_address_hex diff --git a/cic_tools/eth/connection.py b/cic_tools/eth/connection.py @@ -0,0 +1,31 @@ +import logging +import json +from urllib.request import ( + Request, + urlopen, + ) + +from .error import DefaultErrorParser +from .method import jsonrpc_result + +error_parser = DefaultErrorParser() +logg = logging.getLogger(__name__) + + +class HTTPConnection: + + def __init__(self, url): + self.url = url + + + def do(self, o, error_parser=error_parser): + req = Request( + self.url, + method='POST', + ) + req.add_header('Content-Type', 'application/json') + data = json.dumps(o) + logg.debug('(HTTP) send {}'.format(data)) + res = urlopen(req, data=data.encode('utf-8')) + o = json.load(res) + return jsonrpc_result(o, error_parser) diff --git a/cic_tools/eth/constant.py b/cic_tools/eth/constant.py @@ -0,0 +1,2 @@ +zero_address = '0x{:040x}'.format(0) +zero_content = '0x{:064x}'.format(0) diff --git a/cic_tools/eth/error.py b/cic_tools/eth/error.py @@ -0,0 +1,8 @@ +class EthException(Exception): + pass + + +class DefaultErrorParser: + + def translate(self, error): + return EthException('default parser codeĀ {}'.format(error)) diff --git a/cic_tools/eth/hash.py b/cic_tools/eth/hash.py @@ -0,0 +1,7 @@ +import sha3 + + +def keccak256_hex(s): + h = sha3.keccak_256() + h.update(s.encode('utf-8')) + return h.digest().hex() diff --git a/cic_tools/eth/method.py b/cic_tools/eth/method.py @@ -0,0 +1,61 @@ +import sha3 +import uuid + +from hexathon import add_0x +from eth_abi import encode_single + +from .hash import keccak256_hex +from .constant import zero_address + + +# TODO: move to cic-contracts +erc20_balance_signature = keccak256_hex('balanceOf(address)')[:8] +erc20_decimals_signature = keccak256_hex('decimals()')[:8] + + +def jsonrpc_template(): + return { + 'jsonrpc': '2.0', + 'id': str(uuid.uuid4()), + 'method': None, + 'params': [], + } + + +def erc20_balance(contract_address, address, sender_address=zero_address): + o = jsonrpc_template() + o['method'] = 'eth_call' + data = erc20_balance_signature + data += encode_single('address', address).hex() + data = add_0x(data) + a = call(contract_address, data=data) + o['params'].append(a) + o['params'].append('latest') + return o + + +def erc20_decimals(contract_address, sender_address=zero_address): + o = jsonrpc_template() + o['method'] = 'eth_call' + arg = add_0x(erc20_decimals_signature) + #o['params'].append(arg) + a = call(contract_address, arg) + o['params'].append(a) + o['params'].append('latest') + return o + + +def call(contract_address, data, sender_address=zero_address): + return { + 'from': sender_address, + 'to': contract_address, + 'data': data, + } + + +def jsonrpc_result(o, ep): + if o.get('error') != None: + raise ep.translate(o) + return o['result'] + + diff --git a/cic_tools/eth/runnable/__init__.py b/cic_tools/eth/runnable/__init__.py diff --git a/cic_tools/eth/runnable/balance.py b/cic_tools/eth/runnable/balance.py @@ -0,0 +1,99 @@ +#!python3 + +"""Token balance query script + +.. moduleauthor:: Louis Holbrook <dev@holbrook.no> +.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 + +""" + +# SPDX-License-Identifier: GPL-3.0-or-later + +# standard imports +import os +import json +import argparse +import logging + +# third-party imports +from hexathon import ( + add_0x, + strip_0x, + even, + ) +import sha3 +from eth_abi import encode_single + +# local imports +from cic_tools.eth.checksum import to_checksum +from cic_tools.eth.method import ( + jsonrpc_template, + erc20_balance, + erc20_decimals, + jsonrpc_result, + ) +from cic_tools.eth.connection import HTTPConnection + + +logging.basicConfig(level=logging.WARNING) +logg = logging.getLogger() + +default_abi_dir = os.environ.get('ETH_ABI_DIR', '/usr/share/local/cic/solidity/abi') +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('-t', '--token-address', dest='t', type=str, help='Token address. If not set, will return gas balance') +argparser.add_argument('-u', '--unsafe', dest='u', action='store_true', help='Auto-convert address to checksum adddress') +argparser.add_argument('--abi-dir', dest='abi_dir', type=str, default=default_abi_dir, help='Directory containing bytecode and abi (default {})'.format(default_abi_dir)) +argparser.add_argument('-v', action='store_true', help='Be verbose') +argparser.add_argument('account', type=str, help='Account address') +args = argparser.parse_args() + + +if args.v: + logg.setLevel(logging.DEBUG) + +conn = HTTPConnection(args.p) + +def main(): +# w3 = web3.Web3(web3.Web3.HTTPProvider(args.p)) +# REPLACE WITH URLLIB + + account = to_checksum(args.account) + if not args.u and account != add_0x(args.account): + raise ValueError('invalid checksum address') + + r = None + decimals = 18 + if args.t != None: + # determine decimals + decimals_o = erc20_decimals(args.t) + r = conn.do(decimals_o) + decimals = int(strip_0x(r), 16) + + # get balance + balance_o = erc20_balance(args.t, account) + r = conn.do(balance_o) + + else: + o = jsonrpc_template() + o['method'] = 'eth_getBalance' + o['params'].append(account) + r = conn.do(o) + + hx = strip_0x(r) + balance = int(hx, 16) + logg.debug('balance {} = {} decimals {}'.format(even(hx), balance, decimals)) + + balance_str = str(balance) + balance_len = len(balance_str) + if balance_len < 19: + print('0.{}'.format(balance_str.zfill(decimals))) + else: + offset = balance_len-decimals + print('{}.{}'.format(balance_str[:offset],balance_str[offset:])) + + +if __name__ == '__main__': + main() diff --git a/cic_tools/eth/runnable/checksum.py b/cic_tools/eth/runnable/checksum.py @@ -0,0 +1,8 @@ +# standard imports +import sys + +# local imports +from cic_tools.eth.checksum import to_checksum + + +print(to_checksum(sys.argv[1])) diff --git a/requirements.txt b/requirements.txt @@ -0,0 +1,13 @@ +cryptocurrency-cli-tools==0.0.4 +giftable-erc20-token==0.0.7b6 +eth-accounts-index==0.0.10a6 +erc20-single-shot-faucet==0.2.0a5 +erc20-approval-escrow==0.3.0a4 +cic-eth==0.10.0a21+build.e4161b3e +vobject==0.9.6.1 +faker==4.17.1 +eth-address-index==0.1.0a5 +crypto-dev-signer==0.4.13rc2 +pysha3==1.0.2 +hexathon==0.0.1a2 +eth-abi==2.1.1 diff --git a/setup.cfg b/setup.cfg @@ -0,0 +1,36 @@ +[metadata] +name = cic-tools +version = 0.0.1a1 +description = Executable tools for CIC network +author = Louis Holbrook +author_email = dev@holbrook.no +url = https://gitlab.com/grassrootseconomics/cic-tools +keywords = + cic + cryptocurrency + ethereum + solidarity + mutual_credit +classifiers = + Programming Language :: Python :: 3 + Operating System :: OS Independent + Development Status :: 3 - Alpha + Environment :: Console + Intended Audience :: Developers + License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) + Topic :: Internet +# Topic :: Blockchain :: EVM +license = GPL3 +licence_files = + LICENSE.txt + +[options] +python_requires = >= 3.6 +packages = + cic_tools.eth + cic_tools.eth.runnable + +[options.entry_points] +console_scripts = + eth-balance = cic_tools.eth.runnable.balance:main + eth-checksum = cic_tools.eth.runnable.checksum:main diff --git a/setup.py b/setup.py @@ -0,0 +1,16 @@ +from setuptools import setup +import configparser +import os + +requirements = [] +f = open('requirements.txt', 'r') +while True: + l = f.readline() + if l == '': + break + requirements.append(l.rstrip()) +f.close() + +setup( + install_requires=requirements, + )