def check_block(self, block_info, operator): """检查区块是否回滚""" if not block_info: return False info = operator.get_block(int(block_info.block_num) + 1) if not info: return False if hexstr_if_str(to_hex, info['parentHash']) == block_info.block_hash: checked_block = { 'block_num': int(info['number']), 'block_hash': hexstr_if_str(to_hex, info['hash']), 'coin_type': self.coin_category } else: new_block_info = { 'coin': self.coin_category, 'block_num': int(block_info.block_num) - int(config.etc_confirmations), } roll_back_info = operator.get_block(new_block_info['block_num']) new_block_info['block_hash'] = hexstr_if_str( to_hex, roll_back_info['hash']) # self.block_info_coll.update_one({'coin_type': self.coin_category}, # {'$set': new_block_info}) checked_block = { 'block_num': int(roll_back_info['number']), 'block_hash': hexstr_if_str(to_hex, roll_back_info['hash']), 'coin_type': self.coin_category } return checked_block
def check_block(block_info, operator: ERC20Token): """检查区块是否回滚""" if not block_info: return False info = operator.get_block(int(block_info.block_num) + 1) if not info: return False if hexstr_if_str(to_hex, info['parentHash']) == block_info.block_hash: new_block_info = { 'block_num': int(info['number']), 'block_hash': hexstr_if_str(to_hex, info['hash']), 'coin_type': coin_type } else: new_block_info = { 'coin_type': coin_type, 'block_num': int(block_info.block_num) - int(config.eth_confirmations), } roll_back_info = operator.get_block(new_block_info['block_num']) new_block_info['block_hash'] = hexstr_if_str(to_hex, roll_back_info['hash']) new_block_info = { 'block_num': int(roll_back_info['number']), 'block_hash': hexstr_if_str(to_hex, roll_back_info['hash']), 'coin_type': coin_type } return new_block_info
def test_hexstr_if_str_invalid_hex_py2(val, converter): try: is_hexstr = (is_hex(val) or val == '') except ValueError: is_hexstr = False if not is_hexstr: with pytest.raises(ValueError): hexstr_if_str(converter, val)
def test_hexstr_if_str_on_invalid_hex(val): try: is_hexstr = (is_hex(val) or val == '') except ValueError: is_hexstr = False if not is_hexstr: with pytest.raises(ValueError): hexstr_if_str(Mock(), val)
def withdraw_eth(cls, address, amount): private_key = hexstr_if_str(to_hex, load_keyfile(config.eth_private_key_file, config.eth_password) ) eth = ERC20Token(provider_endpoint=config.eth_wallet_url, private_key=private_key, password=config.eth_password) checksum_address = eth.web3.toChecksumAddress(address) try: tx_id = eth.send_ether(checksum_address, amount) tb = { 'amount': amount, 'txid': tx_id, 'coin_type': 'ETH', 'from_address': config.eth_tb_address, 'to_address': address, 'status': TbStatusEnum.TRANSFER } except Exception as e: logger.exception(e) else: try: document = TbRecord(**tb) document.insert() return tx_id except Exception as e: logger.exception(e) logger.error("提币行为已经发生,但数据没有插入数据库{}".format(tb))
def send2address(self, address, amount, *args, **kwargs): password = config.etc_password private_key = hexstr_if_str( to_hex, load_keyfile(config.etc_private_key_file, password)) eth = ERC20Token(ipc_path=config.etc_ipc_path, private_key=private_key, password=password) checksum_address = eth.web3.toChecksumAddress(address) try: tx_id = eth.send_ether(checksum_address, amount) tb = { 'amount': amount, 'txid': tx_id, 'coin_type': 'ETC', 'from_address': config.etc_tb_address, 'to_address': address, } except Exception as e: logger.exception(e) else: try: document = TbRecord(**tb) document.insert() return tx_id except Exception as e: logger.exception(e) logger.error("提币行为已经发生,但数据没有插入数据库{}".format(tb))
def poll(self): logger.info('-----------etc_recharge start-----------') session = Session() self.init_block_info(session) while True: try: operator = self.erc_op_class(ipc_path=config.etc_ipc_path) # 获取区块信息及交易列表 block_info = session.query(self.block_info_coll).filter_by( coin_type=self.coin_category).first() checked_block_info = self.check_block(block_info, operator) if not checked_block_info: continue tx_list = operator.get_block_tx_id_list( checked_block_info['block_num']) # 遍历交易列表 for tx_id in tx_list: self.tx_process(session, hexstr_if_str(to_hex, tx_id), operator) logger.info( f'pull block finished: {checked_block_info["block_num"]}') except Exception as e: logger.exception(e) else: # 存储交易区块信息 block_info.block_num = checked_block_info['block_num'] block_info.block_hash = checked_block_info['block_hash'] try: session.commit() except Exception as e: logger.exception("检查回滚区块时发生错误{}".format(e)) session.rollback() time.sleep(3)
def ask_fee(self, session, item): password = config.eth_password private_key = hexstr_if_str(to_hex, load_keyfile(config.eth_private_key_file, password) ) now = datetime.datetime.utcnow() eth = self.erc_op_class(provider_endpoint=config.eth_wallet_url, private_key=private_key, password=password) try: amount = item.amount tx_id = eth.send_ether(item.to_address, amount) logger.info(f'ETH withdraw ask fee done: {item.to_address} ' f'{amount}') except Exception as e: logger.exception("缴费转账时发生错误{}".format(e)) item.status = AskFeeStatusEnum.ASK_FAILED item.updated_at = now item.error_msg = e try: session.commit() except Exception as e: logger.exception("缴费发生错误时更改状态也错误{}".format(e)) session.rollback() else: item.status = AskFeeStatusEnum.WAIT_CONFIRM item.updated_at = now item.txid = tx_id try: session.commit() except Exception as e: logger.exception("缴费成功时更改状态错误{}".format(e)) session.rollback()
def main(): session = Session() init_block_info(session) while True: try: operator = ERC20Token(provider_endpoint=config.eth_wallet_url) # 获取区块信息及交易列表 block_info = session.query(block_info_document).filter_by( coin_type=coin_type).first() checked_block_info = check_block(block_info, operator) if not checked_block_info: continue tx_list = operator.get_block_tx_id_list( checked_block_info['block_num']) # 遍历交易列表 for tx_id in tx_list: tx_process(session, hexstr_if_str(to_hex, tx_id), operator) logger.info( f'pull block finished: {checked_block_info["block_num"]}') except Exception as e: logger.exception(e) else: block_info.block_hash = checked_block_info['block_hash'] block_info.block_num = checked_block_info['block_num'] try: session.commit() except Exception as e: logger.exception("更新区块发生错误{}".format(e)) session.rollback() time.sleep(3)
def init_block_info(session): """第一次初始化数据库区块信息""" block_info = session.query(BlockInfo).filter_by( coin_type=coin_type).first() if block_info: return while True: try: operator = ERC20Token(provider_endpoint=config.eth_wallet_url) info = operator.get_block(int(operator.get_block_number())) block_info = BlockInfo( **{ 'block_num': int(info['number']), 'block_hash': hexstr_if_str(to_hex, info['hash']), 'coin_type': coin_type }) session.add(block_info) session.commit() logger.info('block_info init success') break except Exception as e: logger.exception("初始化区块失败{}".format(e)) session.rollback() time.sleep(15)
def sign(self, message=None, private_key=None, message_hexstr=None, message_text=None): ''' @param private_key in bytes, str, or int. In Python 2, a bytes, unicode or str object will be interpreted as hexstr In Python 3, only a str object will be interpreted as hexstr ''' msg_bytes = to_bytes(message, hexstr=message_hexstr, text=message_text) msg_hash = self.hashMessage(msg_bytes) key_bytes = hexstr_if_str(to_bytes, private_key) key = self._keys.PrivateKey(key_bytes) (v, r, s, eth_signature_bytes) = sign_message_hash(key, msg_hash) (r_hex, s_hex, eth_signature_hex) = map(to_hex, (r, s, eth_signature_bytes)) return AttributeDict({ 'message': msg_bytes, 'messageHash': msg_hash, 'r': r_hex, 's': s_hex, 'v': v, 'signature': eth_signature_hex, })
def recoverTransaction(self, serialized_transaction): txn_bytes = hexstr_if_str(to_bytes, serialized_transaction) txn = Transaction.from_bytes(txn_bytes) msg_hash = hash_of_signed_transaction(txn) if sys.version_info.major < 3: msg_hash = to_hex(msg_hash) return self.recover(msg_hash, vrs=vrs_from(txn))
def send_tokens(self, address, amount): """Send tokens from my wallet to address. :param str address: the address to send tokens to. :param Decimal amount: the amount of tokens to transfer. :returns: transaction id (hash) :rtype: str :raises: :class:`~erc20token.exceptions.SdkConfigurationError`: if the SDK was not configured with a private key. :raises: ValueError: if the amount is not positive. :raises: ValueError: if the address has a wrong format. :raises: ValueError: if the nonce is incorrect. :raises: ValueError: if insufficient funds for for gas * gas_price. """ if not self.address: raise SdkNotConfiguredError('private key not configured') validate_address(address) if amount <= 0: raise ValueError('amount must be positive') hex_data = self.token_contract._encode_transaction_data( 'transfer', args=(address, self.web3.toWei(amount, 'ether'))) data = hexstr_if_str(to_bytes, hex_data) return self._tx_manager.send_transaction(self.token_contract.address, 0, data)
def recover(self, msghash, vrs=None, signature=None): hash_bytes = hexstr_if_str(to_bytes, msghash) if vrs is not None: v, r, s = map(hexstr_if_str(to_decimal), vrs) v_standard = to_standard_v(v) signature_obj = self._keys.Signature(vrs=(v_standard, r, s)) elif signature is not None: signature_bytes = hexstr_if_str(to_bytes, signature) signature_bytes_standard = to_standard_signature_bytes( signature_bytes) signature_obj = self._keys.Signature( signature_bytes=signature_bytes_standard) else: raise TypeError( "You must supply the vrs tuple or the signature bytes") pubkey = signature_obj.recover_public_key_from_msg_hash(hash_bytes) return pubkey.to_checksum_address()
def abi_bytes_to_hex(abi_type, data): base, sub, arrlist = process_type(abi_type) if base == 'bytes' and not arrlist: bytes_data = hexstr_if_str(to_bytes, data) if len(bytes_data) != int(sub): raise ValueError( "This value was expected to be %d bytes, but instead was %d: %r" % ((sub, len(bytes_data), data))) return abi_type, to_hex(bytes_data)
def privateKeyToAccount(self, private_key): key_bytes = hexstr_if_str(to_bytes, private_key) try: key_obj = self._keys.PrivateKey(key_bytes) return LocalAccount(key_obj, self) except ValidationError as original_exception: raise_from( ValueError( "The private key must be exactly 32 bytes long, instead of " "%d bytes." % len(key_bytes)), original_exception)
def _retrieve_head_block(self): while True: try: block_num = self.wallet_op.get_block_number() block_ = self.wallet_op.get_block(block_num) except Exception as e: logger.exception(e) else: block_['hash'] = hexstr_if_str(to_hex, block_['hash']) return block_
def list_transactions(self): while True: next_ = self.wallet_op.get_block(self.block_num + 1) if not next_: raise StopIteration # check chain block_record conformity if hexstr_if_str(to_hex, next_['parentHash']) == self.block_hash: self.block_num = int(next_['number']) self.block_hash = int(next_['hash']) else: rollback_num = self.block_num - self.confirmation_count rollback_ = self.wallet_op.get_block(rollback_num) self.block_num = hexstr_if_str(to_hex, rollback_['number']) self.block_hash = hexstr_if_str(to_hex, rollback_['hash']) txids = self.wallet_op.get_block_tx_id_list(self.block_num) for txid in txids: tran = self.wallet_op.get_transaction_data(txid) yield tran self.block_record.save_head_block( dict(block_num=self.block_num, block_hash=self.block_hash))
def recover(self, msghash, vrs=None, signature=None): hash_bytes = HexBytes(msghash) if vrs is not None: v, r, s = map(hexstr_if_str(to_int), vrs) v_standard = to_standard_v(v) signature_obj = self._keys.Signature(vrs=(v_standard, r, s)) elif signature is not None: signature_bytes = HexBytes(signature) signature_bytes_standard = to_standard_signature_bytes(signature_bytes) signature_obj = self._keys.Signature(signature_bytes=signature_bytes_standard) else: raise TypeError("You must supply the vrs tuple or the signature bytes") pubkey = signature_obj.recover_public_key_from_msg_hash(hash_bytes) return pubkey.to_checksum_address()
def abi_bytes_to_hex(abi_type, data): base, sub, arrlist = process_type(abi_type) if base == 'bytes' and not arrlist: bytes_data = hexstr_if_str(to_bytes, data) if not sub: return abi_type, to_hex(bytes_data) else: num_bytes = int(sub) if len(bytes_data) <= num_bytes: padded = bytes_data.ljust(num_bytes, b'\0') return abi_type, to_hex(padded) else: raise ValueError( "This value was expected to be at most %d bytes, but instead was %d: %r" % ((num_bytes, len(bytes_data), data)))
def abi_bytes_to_hex(abi_type, data): base, sub, arrlist = process_type(abi_type) if base == 'bytes' and not arrlist: bytes_data = hexstr_if_str(to_bytes, data) if not sub: return abi_type, to_hex(bytes_data) else: num_bytes = int(sub) if len(bytes_data) <= num_bytes: padded = bytes_data.ljust(num_bytes, b'\0') return abi_type, to_hex(padded) else: raise ValueError( "This value was expected to be at most %d bytes, but instead was %d: %r" % ( (num_bytes, len(bytes_data), data) ) )
def withdraw_token(cls, to_address, amount, coin_type): token_info = CoinSetting.find_one({'id': coin_type, 'main_coin': 'ETH'}) if not token_info: logger.warning('不存在的币种类型{}'.format(coin_type)) return token_address = token_info['token_address'] private_key = hexstr_if_str(to_hex, load_keyfile(config.eth_private_key_file, config.eth_password) ) token = ERC20Token( provider_endpoint=config.eth_wallet_url, contract_address=token_address, password=config.eth_password, private_key=private_key) try: tx_id = token.send_tokens(to_address, amount, token_info['token_unit']) data = { 'amount': amount, 'txid': tx_id, 'from_address': getattr(config, f'{coin_type}_tb_address'), 'coin_type': coin_type, 'to_address': to_address, 'status': TbStatusEnum.TRANSFER } logger.info('withdraw {} to {}'.format(coin_type, to_address)) except Exception as e: logger.exception(e) else: try: tb = TbRecord(**data) tb.insert() return tx_id except Exception as e: logger.exception(e) logger.error("提币行为已经发生,但数据没有插入数据库{}".format(data))
def init_block_info(self, session): """第一次初始化数据库区块信息""" block_info = session.query(self.block_info_coll).filter_by( coin_type=self.coin_category).first() if block_info: return while True: try: operator = self.erc_op_class(ipc_path=config.etc_ipc_path) info = operator.get_block(int(operator.get_block_number())) block_info = { 'block_num': int(info['number']), 'block_hash': hexstr_if_str(to_hex, info['hash']), 'coin_type': self.coin_category } block = self.block_info_coll(**block_info) session.add(block) session.commit() logger.info('etc_block_info init success') return except Exception as e: logger.error("etc初始化区块失败{}".format(e)) session.rollback() time.sleep(15)
def send_tokens(self, address, amount): """Send tokens from my wallet to address. :param str address: the address to send tokens to. :param Decimal amount: the amount of tokens to transfer. :returns: transaction id (hash) :rtype: str :raises: :class:`~erc20token.exceptions.SdkConfigurationError`: if the SDK was not configured with a private key. :raises: ValueError: if the amount is not positive. :raises: ValueError: if the address has a wrong format. :raises: ValueError: if the nonce is incorrect. :raises: ValueError: if insufficient funds for for gas * gas_price. """ if not self.address: raise SdkNotConfiguredError('private key not configured') validate_address(address) if amount <= 0: raise ValueError('amount must be positive') hex_data = self.token_contract._encode_transaction_data('transfer', args=(address, self.web3.toWei(amount, 'ether'))) data = hexstr_if_str(to_bytes, hex_data) return self._tx_manager.send_transaction(self.token_contract.address, 0, data)
def __init__(self, keyfile='', password='', private_key='', provider='', provider_endpoint_uri='', contract_address='', contract_abi={}, gas_price=None, gas_limit=None): """Create a new instance of the Token SDK. The SDK needs a JSON-RPC provider, contract definitions and (optionally) a wallet private key. The user may pass either a provider or a provider endpoint URI, in which case a default :class:`web3:providers:HTTPProvider` will be created. If neither private_key nor keyfile+password are provided, the SDK can still be used in "anonymous" mode, with only the following functions available: - get_address_ether_balance - get_transaction_status - get_transaction_data - monitor_ether_transactions :param str private_key: a private key to initialize the wallet with. If either private key or keyfile are not provided, the wallet will not be initialized and methods needing the wallet will raise exception. :param str keyfile: the path to the keyfile to initialize the wallet with. You will also need to supply a password for this keyfile. :param str password: a password for the keyfile. :param provider: JSON-RPC provider to work with. If not given, a default `web3:providers:HTTPProvider` is used, inited with provider_endpoint_uri. :type provider: :class:`web3:providers:BaseProvider` :param str provider_endpoint_uri: a URI to use with a default HTTPProvider. :param str contract_address: the address of the token contract. :param list contract_abi: The contract ABI json. :param number gas_price: The price of gas in Gwei. :param number gas_limit: Transaction gas limit. :returns: An instance of the SDK. :rtype: :class:`~erc20tokensdk.SDK` :raises: :class:`~erc20tokensdk.exceptions.SdkConfigurationError` if some of the configuration parameters are invalid. """ if not provider and not provider_endpoint_uri: raise SdkConfigurationError('either provider or provider endpoint must be provided') try: validate_address(contract_address) except ValueError as ve: raise SdkConfigurationError('invalid token contract address: ' + str(ve)) try: validate_abi(contract_abi) except Exception as e: raise SdkConfigurationError('invalid token contract abi: ' + str(e)) if gas_price and not (isinstance(gas_price, int) or isinstance(gas_price, float)): raise SdkConfigurationError('gas price must be either integer of float') if gas_limit and not isinstance(gas_limit, int): raise SdkConfigurationError('gas limit must be integer') if provider: self.web3 = Web3(provider) else: self.web3 = Web3(RetryHTTPProvider(provider_endpoint_uri)) if not self.web3.isConnected(): raise SdkConfigurationError('cannot connect to provider endpoint') self.token_contract = self.web3.eth.contract(contract_address, abi=contract_abi) self.private_key = None self.address = None if keyfile: try: self.private_key = load_keyfile(keyfile, password) except Exception as e: raise SdkConfigurationError('cannot load keyfile: ' + str(e)) elif private_key: self.private_key = private_key if self.private_key: try: private_key_bytes = hexstr_if_str(to_bytes, self.private_key) pk = keys.PrivateKey(private_key_bytes) self.address = self.web3.eth.defaultAccount = pk.public_key.to_checksum_address() except ValidationError as e: raise SdkConfigurationError('cannot load private key: ' + str(e)) # init transaction manager self._tx_manager = TransactionManager(self.web3, self.private_key, self.address, self.token_contract, gas_price, gas_limit) # monitoring filter manager self._filter_mgr = FilterManager(self.web3)
def __init__(self, keyfile='', password='', private_key='', provider='', provider_endpoint_uri='', contract_address='', contract_abi={}, gas_price=None, gas_limit=None): """Create a new instance of the Token SDK. The SDK needs a JSON-RPC provider, contract definitions and (optionally) a wallet private key. The user may pass either a provider or a provider endpoint URI, in which case a default :class:`web3:providers:HTTPProvider` will be created. If neither private_key nor keyfile+password are provided, the SDK can still be used in "anonymous" mode, with only the following functions available: - get_address_ether_balance - get_transaction_status - get_transaction_data - monitor_ether_transactions :param str private_key: a private key to initialize the wallet with. If either private key or keyfile are not provided, the wallet will not be initialized and methods needing the wallet will raise exception. :param str keyfile: the path to the keyfile to initialize the wallet with. You will also need to supply a password for this keyfile. :param str password: a password for the keyfile. :param provider: JSON-RPC provider to work with. If not given, a default `web3:providers:HTTPProvider` is used, inited with provider_endpoint_uri. :type provider: :class:`web3:providers:BaseProvider` :param str provider_endpoint_uri: a URI to use with a default HTTPProvider. :param str contract_address: the address of the token contract. :param list contract_abi: The contract ABI json. :param number gas_price: The price of gas in Gwei. :param number gas_limit: Transaction gas limit. :returns: An instance of the SDK. :rtype: :class:`~erc20token.SDK` :raises: :class:`~erc20token.exceptions.SdkConfigurationError` if some of the configuration parameters are invalid. """ if not provider and not provider_endpoint_uri: raise SdkConfigurationError('either provider or provider endpoint must be provided') try: validate_address(contract_address) except ValueError as ve: raise SdkConfigurationError('invalid token contract address: ' + str(ve)) try: validate_abi(contract_abi) except Exception as e: raise SdkConfigurationError('invalid token contract abi: ' + str(e)) if gas_price and not (isinstance(gas_price, int) or isinstance(gas_price, float)): raise SdkConfigurationError('gas price must be either integer of float') if gas_limit and not isinstance(gas_limit, int): raise SdkConfigurationError('gas limit must be integer') if provider: self.web3 = Web3(provider) else: self.web3 = Web3(RetryHTTPProvider(provider_endpoint_uri)) if not self.web3.isConnected(): raise SdkConfigurationError('cannot connect to provider endpoint') self.token_contract = self.web3.eth.contract(contract_address, abi=contract_abi) self.private_key = None self.address = None if keyfile: try: self.private_key = load_keyfile(keyfile, password) except Exception as e: raise SdkConfigurationError('cannot load keyfile: ' + str(e)) elif private_key: self.private_key = private_key if self.private_key: try: private_key_bytes = hexstr_if_str(to_bytes, self.private_key) pk = keys.PrivateKey(private_key_bytes) self.address = self.web3.eth.defaultAccount = pk.public_key.to_checksum_address() except ValidationError as e: raise SdkConfigurationError('cannot load private key: ' + str(e)) # init transaction manager self._tx_manager = TransactionManager(self.web3, self.private_key, self.address, self.token_contract, gas_price, gas_limit) # monitoring filter manager self._filter_mgr = FilterManager(self.web3)
compose( apply_formatter_at_index(block_number_formatter, 0), apply_formatter_at_index(integer_to_hex, 1), ), 'eth_getTransactionCount': apply_formatter_at_index(block_number_formatter, 1), 'eth_getUncleCountByBlockNumber': apply_formatter_at_index(block_number_formatter, 0), 'eth_newFilter': apply_formatter_at_index(filter_params_formatter, 0), 'eth_getLogs': apply_formatter_at_index(filter_params_formatter, 0), # personal 'personal_importRawKey': apply_formatter_at_index( compose(remove_0x_prefix, hexstr_if_str(to_hex)), 0, ), 'personal_sign': apply_formatter_at_index(encode_hex, 0), 'personal_ecRecover': apply_formatter_at_index(encode_hex, 0), # Snapshot and Revert 'evm_revert': apply_formatter_at_index(integer_to_hex, 0), }, result_formatters={ # Eth 'eth_accounts': apply_formatter_to_array(to_checksum_address), 'eth_blockNumber':
def abi_bytes_to_bytes(abi_type, data): base, sub, arrlist = process_type(abi_type) if base == 'bytes' and not arrlist: return abi_type, hexstr_if_str(to_bytes, data)
def abi_int_to_hex(abi_type, data): base, _sub, arrlist = process_type(abi_type) if base == 'uint' and not arrlist: return abi_type, hexstr_if_str(to_hex, data)
def encode_transaction(unsigned_transaction, vrs): (v, r, s) = vrs chain_naive_transaction = dissoc(vars(unsigned_transaction), 'v', 'r', 's') signed_transaction = Transaction(v=v, r=r, s=s, **chain_naive_transaction) return rlp.encode(signed_transaction) TRANSACTION_DEFAULTS = { 'gasPrice': lambda web3: web3.eth.gasPrice, 'value': 0, 'data': b'', 'chainId': lambda web3: int(web3.net.version), } TRANSACTION_FORMATTERS = { 'nonce': hexstr_if_str(to_int), 'gasPrice': hexstr_if_str(to_int), 'gas': hexstr_if_str(to_int), 'to': apply_formatter_if(is_string, decode_hex), 'value': hexstr_if_str(to_int), 'data': apply_formatter_if(is_string, decode_hex), 'v': hexstr_if_str(to_int), 'r': hexstr_if_str(to_int), 's': hexstr_if_str(to_int), } def chain_id_to_v(transaction_dict): # See EIP 155 chain_id = transaction_dict.pop('chainId') if chain_id is None:
def test_hexstr_if_str_passthrough(val): to_type = Mock(return_value='zoot') assert hexstr_if_str(to_type, val) == 'zoot' assert to_type.call_args == ((val, ), {'hexstr': None})
def test_hexstr_if_str_on_valid_hex(val): to_type = Mock(return_value='zoot') assert hexstr_if_str(to_type, val) == 'zoot' assert to_type.call_args == ((None, ), {'hexstr': val})
def test_hexstr_if_str_curried(): converter = hexstr_if_str(to_hex) assert converter(255) == '0xff'
def __new__(cls, val): bytesval = hexstr_if_str(to_bytes, val) return super().__new__(cls, bytesval)
(v, r, s) = vrs chain_naive_transaction = dissoc(vars(unsigned_transaction), 'v', 'r', 's') signed_transaction = Transaction(v=v, r=r, s=s, **chain_naive_transaction) return rlp.encode(signed_transaction) TRANSACTION_DEFAULTS = { 'value': 0, 'data': b'', 'gas': lambda web3, tx: web3.eth.estimateGas(tx), 'gasPrice': lambda web3, tx: web3.eth.generateGasPrice(tx) or web3.eth.gasPrice, 'chainId': lambda web3, tx: int(web3.net.version), } TRANSACTION_FORMATTERS = { 'nonce': hexstr_if_str(to_int), 'gasPrice': hexstr_if_str(to_int), 'gas': hexstr_if_str(to_int), 'to': apply_formatter_if(is_string, decode_hex), 'value': hexstr_if_str(to_int), 'data': apply_formatter_if(is_string, decode_hex), 'v': hexstr_if_str(to_int), 'r': hexstr_if_str(to_int), 's': hexstr_if_str(to_int), } def chain_id_to_v(transaction_dict): # See EIP 155 chain_id = transaction_dict.pop('chainId') if chain_id is None:
def get_event_data(event_abi, log_entry): """ Given an event ABI and a log entry for that event, return the decoded event data """ if event_abi['anonymous']: log_topics = log_entry['topics'] elif not log_entry['topics']: raise MismatchedABI("Expected non-anonymous event to have 1 or more topics") elif event_abi_to_log_topic(event_abi) != log_entry['topics'][0]: raise MismatchedABI("The event signature did not match the provided ABI") else: log_topics = log_entry['topics'][1:] log_topics_abi = get_indexed_event_inputs(event_abi) log_topic_normalized_inputs = normalize_event_input_types(log_topics_abi) log_topic_types = get_event_abi_types_for_decoding(log_topic_normalized_inputs) log_topic_names = get_abi_input_names({'inputs': log_topics_abi}) if len(log_topics) != len(log_topic_types): raise ValueError("Expected {0} log topics. Got {1}".format( len(log_topic_types), len(log_topics), )) log_data = hexstr_if_str(to_bytes, log_entry['data']) log_data_abi = exclude_indexed_event_inputs(event_abi) log_data_normalized_inputs = normalize_event_input_types(log_data_abi) log_data_types = get_event_abi_types_for_decoding(log_data_normalized_inputs) log_data_names = get_abi_input_names({'inputs': log_data_abi}) # sanity check that there are not name intersections between the topic # names and the data argument names. duplicate_names = set(log_topic_names).intersection(log_data_names) if duplicate_names: raise ValueError( "Invalid Event ABI: The following argument names are duplicated " "between event inputs: '{0}'".format(', '.join(duplicate_names)) ) decoded_log_data = decode_abi(log_data_types, log_data) normalized_log_data = map_abi_data( BASE_RETURN_NORMALIZERS, log_data_types, decoded_log_data ) decoded_topic_data = [ decode_single(topic_type, topic_data) for topic_type, topic_data in zip(log_topic_types, log_topics) ] normalized_topic_data = map_abi_data( BASE_RETURN_NORMALIZERS, log_topic_types, decoded_topic_data ) event_args = dict(itertools.chain( zip(log_topic_names, normalized_topic_data), zip(log_data_names, normalized_log_data), )) event_data = { 'args': event_args, 'event': event_abi['name'], 'logIndex': log_entry['logIndex'], 'transactionIndex': log_entry['transactionIndex'], 'transactionHash': log_entry['transactionHash'], 'address': log_entry['address'], 'blockHash': log_entry['blockHash'], 'blockNumber': log_entry['blockNumber'], } return AttributeDict.recursive(event_data)
block_number_formatter, 0, ), 'eth_getCode': apply_formatter_at_index(block_number_formatter, 1), 'eth_getStorageAt': apply_formatter_at_index(block_number_formatter, 2), 'eth_getTransactionByBlockNumberAndIndex': compose( apply_formatter_at_index(block_number_formatter, 0), apply_formatter_at_index(integer_to_hex, 1), ), 'eth_getTransactionCount': apply_formatter_at_index(block_number_formatter, 1), 'eth_getUncleCountByBlockNumber': apply_formatter_at_index(block_number_formatter, 0), 'eth_newFilter': apply_formatter_at_index(filter_params_formatter, 0), 'eth_getLogs': apply_formatter_at_index(filter_params_formatter, 0), # personal 'personal_importRawKey': apply_formatter_at_index( compose(remove_0x_prefix, hexstr_if_str(to_hex)), 0, ), 'personal_sign': apply_formatter_at_index(encode_hex, 0), 'personal_ecRecover': apply_formatter_at_index(encode_hex, 0), # Snapshot and Revert 'evm_revert': apply_formatter_if( bool, apply_formatter_at_index(to_integer_if_hex, 0), ) }, result_formatters={ # Eth 'eth_accounts': apply_formatter_to_array(to_checksum_address), 'eth_blockNumber': to_integer_if_hex, 'eth_coinbase': to_checksum_address,
def hexstrs_to_bytes(abi_type, data): base, sub, arrlist = process_type(abi_type) if base in {'string', 'bytes'}: return abi_type, hexstr_if_str(to_bytes, data)