chainlib

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

commit ca582e09a30a3081542323e2d8cbd5538b884391
parent 8be9b595565bd8d1638390221391fbc710168da4
Author: nolash <dev@holbrook.no>
Date:   Tue,  9 Feb 2021 15:53:58 +0100

Finish erc20 transfer script

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

Diffstat:
Mcic_tools/eth/connection.py | 53+++++++++++++++++++++++++++++++++++++++++++++++++++--
Mcic_tools/eth/erc20.py | 19++++++++++---------
Mcic_tools/eth/error.py | 4++++
Mcic_tools/eth/gas.py | 1+
Mcic_tools/eth/runnable/balance.py | 2+-
Mcic_tools/eth/runnable/gas.py | 16++++++++++------
Mcic_tools/eth/runnable/transfer.py | 20+++++++++++---------
7 files changed, 88 insertions(+), 27 deletions(-)

diff --git a/cic_tools/eth/connection.py b/cic_tools/eth/connection.py @@ -1,12 +1,28 @@ +# standard imports import logging import json +import datetime +import time from urllib.request import ( Request, urlopen, ) -from .error import DefaultErrorParser -from .rpc import jsonrpc_result +# third-party imports +from hexathon import ( + add_0x, + strip_0x, + ) + +# local imports +from .error import ( + DefaultErrorParser, + RevertEthException, + ) +from .rpc import ( + jsonrpc_template, + jsonrpc_result, + ) error_parser = DefaultErrorParser() logg = logging.getLogger(__name__) @@ -29,3 +45,36 @@ class HTTPConnection: res = urlopen(req, data=data.encode('utf-8')) o = json.load(res) return jsonrpc_result(o, error_parser) + + + def wait(self, tx_hash_hex, delay=0.5, timeout=0.0): + t = datetime.datetime.utcnow() + i = 0 + while True: + o = jsonrpc_template() + o['method'] ='eth_getTransactionReceipt' + o['params'].append(add_0x(tx_hash_hex)) + req = Request( + self.url, + method='POST', + ) + req.add_header('Content-Type', 'application/json') + data = json.dumps(o) + logg.debug('(HTTP) receipt attempt {} {}'.format(i, data)) + res = urlopen(req, data=data.encode('utf-8')) + r = json.load(res) + + e = jsonrpc_result(r, error_parser) + if e != None: + logg.debug('e {}'.format(strip_0x(e['status']))) + if strip_0x(e['status']) == '00': + raise RevertEthException(tx_hash_hex) + return e + + if timeout > 0.0: + delta = (datetime.datetime.utcnow() - t) + datetime.timedelta(seconds=delay) + if delta.total_seconds() >= timeout: + raise TimeoutError(tx_hash) + + time.sleep(delay) + i += 1 diff --git a/cic_tools/eth/erc20.py b/cic_tools/eth/erc20.py @@ -2,9 +2,13 @@ import sha3 from hexathon import add_0x from eth_abi import encode_single +from crypto_dev_signer.eth.transaction import EIP155Transaction # local imports -from .hash import keccak256_string_to_hex +from .hash import ( + keccak256_hex_to_hex, + keccak256_string_to_hex, + ) from .constant import ZERO_ADDRESS from .rpc import jsonrpc_template from .tx import TxFactory @@ -13,7 +17,7 @@ from .tx import TxFactory # TODO: move to cic-contracts erc20_balance_signature = keccak256_string_to_hex('balanceOf(address)')[:8] erc20_decimals_signature = keccak256_string_to_hex('decimals()')[:8] -erc20_transfer_signature = keccak256_string_to_hex('transfer(address,uint256')[:8] +erc20_transfer_signature = keccak256_string_to_hex('transfer(address,uint256)')[:8] class ERC20TxFactory(TxFactory): @@ -50,20 +54,17 @@ class ERC20TxFactory(TxFactory): o['method'] = 'eth_call' data = add_0x(erc20_decimals_signature) tx = self.template(sender_address, contract_address) - tx = self.set_code(tx, data, update_fee=False) + tx = self.set_code(tx, data) o['params'].append(self.normalize(tx)) o['params'].append('latest') return o - def erc20_transfer(self, contract_address, sender_address, value): - o = jsonrpc_template() - o['method'] = 'eth_sendRawTransaction' + def erc20_transfer(self, contract_address, sender_address, recipient_address, value): data = erc20_transfer_signature - data += encode_single('address', sender_address).hex() + data += encode_single('address', recipient_address).hex() data += encode_single('uint256', value).hex() data = add_0x(data) tx = self.template(sender_address, contract_address) tx = self.set_code(tx, data) - o['params'].append(tx) - return self.build(o) + return self.build(tx) diff --git a/cic_tools/eth/error.py b/cic_tools/eth/error.py @@ -2,6 +2,10 @@ class EthException(Exception): pass +class RevertEthException(EthException): + pass + + class DefaultErrorParser: def translate(self, error): diff --git a/cic_tools/eth/gas.py b/cic_tools/eth/gas.py @@ -28,6 +28,7 @@ class GasTxFactory(TxFactory): def create(self, sender, recipient, value): tx = self.template(sender, recipient) + tx['value'] = value txe = EIP155Transaction(tx, tx['nonce'], tx['chainId']) self.signer.signTransaction(txe) tx_raw = txe.rlp_serialize() diff --git a/cic_tools/eth/runnable/balance.py b/cic_tools/eth/runnable/balance.py @@ -66,7 +66,7 @@ def main(): r = None decimals = 18 if args.t != None: - g = ERC20TxFactory() + g = ERC20TxFactory(gas_oracle=gas_oracle) # determine decimals decimals_o = g.erc20_decimals(args.t) r = conn.do(decimals_o) diff --git a/cic_tools/eth/runnable/gas.py b/cic_tools/eth/runnable/gas.py @@ -62,8 +62,8 @@ if args.vv: elif args.v: logg.setLevel(logging.INFO) -block_last = args.w block_all = args.ww +block_last = args.w or block_all signer_address = None keystore = DictKeystore() @@ -80,6 +80,10 @@ gas_oracle = DefaultGasOracle(conn) chain_pair = args.i.split(':') chain_id = int(chain_pair[1]) +value = args.amount + +g = GasTxFactory(signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=chain_id) + def balance(address): o = gas_balance(address) @@ -93,17 +97,17 @@ def main(): if not args.u and recipient != add_0x(args.recipient): raise ValueError('invalid checksum address') - value = args.amount - + logg.info('gas transfer from {} to {} value {}'.format(signer_address, recipient, value)) logg.debug('sender {} balance before: {}'.format(signer_address, balance(signer_address))) logg.debug('recipient {} balance before: {}'.format(recipient, balance(recipient))) - g = GasTxFactory(signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=chain_id) (tx_hash_hex, o) = g.create(signer_address, recipient, value) conn.do(o) - logg.debug('sender {} balance after: {}'.format(signer_address, balance(signer_address))) - logg.debug('recipient {} balance after: {}'.format(recipient, balance(recipient))) + if block_last: + conn.wait(tx_hash_hex) + logg.debug('sender {} balance after: {}'.format(signer_address, balance(signer_address))) + logg.debug('recipient {} balance after: {}'.format(recipient, balance(recipient))) print(tx_hash_hex) diff --git a/cic_tools/eth/runnable/transfer.py b/cic_tools/eth/runnable/transfer.py @@ -79,8 +79,11 @@ gas_oracle = DefaultGasOracle(conn) chain_pair = args.i.split(':') chain_id = int(chain_pair[1]) -#g = ERC20TxFactory(signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=chain_id) -g = ERC20TxFactory() + +value = args.amount + +g = ERC20TxFactory(signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=chain_id) + def balance(token_address, address): o = g.erc20_balance(token_address, address) @@ -94,19 +97,18 @@ def main(): if not args.u and recipient != add_0x(args.recipient): raise ValueError('invalid checksum address') - value = args.amount - logg.debug('sender {} balance before: {}'.format(signer_address, balance(args.t, signer_address))) logg.debug('recipient {} balance before: {}'.format(recipient, balance(args.t, recipient))) - logg.debug('sender {} balance after: {}'.format(signer_address, balance(args.t, signer_address))) - logg.debug('recipient {} balance after: {}'.format(recipient, balance(args.t, recipient))) - + (tx_hash_hex, o) = g.erc20_transfer(args.t, signer_address, recipient, value) + conn.do(o) if block_last: - helper.wait_for() + conn.wait(tx_hash_hex) + logg.debug('sender {} balance after: {}'.format(signer_address, balance(args.t, signer_address))) + logg.debug('recipient {} balance after: {}'.format(recipient, balance(args.t, recipient))) - print(tx_hash) + print(tx_hash_hex) if __name__ == '__main__':