def test_safe_multisig_tx_serializer(self): safe = get_eth_address_with_key()[0] to = None value = int(10e18) tx_data = None operation = 0 safe_tx_gas = 1 data_gas = 1 gas_price = 1 gas_token = None refund_receiver = None nonce = 0 data = { "safe": safe, "to": to, "value": value, # 1 ether "data": tx_data, "operation": operation, "safe_tx_gas": safe_tx_gas, "data_gas": data_gas, "gas_price": gas_price, "gas_token": gas_token, "nonce": nonce, "signatures": [{ 'r': 5, 's': 7, 'v': 27 }, { 'r': 17, 's': 29, 'v': 28 }] } serializer = SafeRelayMultisigTxSerializer(data=data) self.assertFalse( serializer.is_valid()) # Less signatures than threshold # Signatures must be sorted! accounts = [Account.create() for _ in range(2)] accounts.sort(key=lambda x: x.address.lower()) safe = get_eth_address_with_key()[0] data['safe'] = safe serializer = SafeRelayMultisigTxSerializer(data=data) self.assertFalse( serializer.is_valid()) # To and data cannot both be null tx_data = HexBytes('0xabcd') data['data'] = tx_data.hex() serializer = SafeRelayMultisigTxSerializer(data=data) self.assertFalse(serializer.is_valid() ) # Operation is not create, but no to provided # Now we fix the signatures to = accounts[-1].address data['to'] = to multisig_tx_hash = SafeTx(None, safe, to, value, tx_data, operation, safe_tx_gas, data_gas, gas_price, gas_token, refund_receiver, safe_nonce=nonce).safe_tx_hash signatures = [ account.signHash(multisig_tx_hash) for account in accounts ] data['signatures'] = signatures serializer = SafeRelayMultisigTxSerializer(data=data) self.assertTrue(serializer.is_valid()) data = { "safe": safe, "to": to, "value": value, # 1 ether "data": tx_data, "operation": operation, "safe_tx_gas": safe_tx_gas, "data_gas": data_gas, "gas_price": gas_price, "gas_token": gas_token, "nonce": nonce, "refund_receiver": accounts[0].address, # Refund must be empty or NULL_ADDRESS "signatures": [{ 'r': 5, 's': 7, 'v': 27 }] } serializer = SafeRelayMultisigTxSerializer(data=data) self.assertFalse(serializer.is_valid()) data['refund_receiver'] = NULL_ADDRESS serializer = SafeRelayMultisigTxSerializer(data=data) self.assertTrue(serializer.is_valid())
def to_python(self, value): return value if value is None else HexBytes(value).hex()
def get_total_transfer_history( self, addresses: List[str], from_block: int = 0, to_block: Optional[int] = None, token_address: Optional[str] = None) -> List[Dict[str, Any]]: """ Get events for erc20 and erc721 transfers from and to an `address`. We decode it manually An example of an erc20 event: {'logIndex': 0, 'transactionIndex': 0, 'transactionHash': HexBytes('0x4d0f25313603e554e3b040667f7f391982babbd195c7ae57a8c84048189f7794'), 'blockHash': HexBytes('0x90fa67d848a0eaf3be625235dae28815389f5292d4465c48d1139f0c207f8d42'), 'blockNumber': 791, 'address': '0xf7d0Bd47BF3214494E7F5B40E392A25cb4788620', 'data': '0x000000000000000000000000000000000000000000000000002001f716742000', 'topics': [HexBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), HexBytes('0x000000000000000000000000f5984365fca2e3bc7d2e020abb2c701df9070eb7'), HexBytes('0x0000000000000000000000001df62f291b2e969fb0849d99d9ce41e2f137006e')], 'type': 'mined' 'args': {'from': '0xf5984365FcA2e3bc7D2E020AbB2c701DF9070eB7', 'to': '0x1dF62f291b2E969fB0849d99D9Ce41e2F137006e', 'value': 9009360000000000 } } An example of an erc721 event {'address': '0x6631FcbB50677DfC6c02CCDcc03a8f68Db427a64', 'blockHash': HexBytes('0x95c71c6c9373e9a8ca2c767dda1cd5083eb6addcce36fc216c9e1f458d6970f9'), 'blockNumber': 5341681, 'data': '0x', 'logIndex': 0, 'removed': False, 'topics': [HexBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000'), HexBytes('0x000000000000000000000000b5239c032ab9fb5abfc3903e770a4b6a9095542c'), HexBytes('0x0000000000000000000000000000000000000000000000000000000000000063')], 'transactionHash': HexBytes('0xce8c8af0503e6f8a421345c10cdf92834c95186916a3f5b1437d2bba63d2db9e'), 'transactionIndex': 0, 'transactionLogIndex': '0x0', 'type': 'mined', 'args': {'from': '0x0000000000000000000000000000000000000000', 'to': '0xb5239C032AB9fB5aBFc3903e770A4B6a9095542C', 'tokenId': 99 } } :param addresses: Search events `from` and `to` these `addresses` :param from_block: Block to start querying from :param to_block: Block to stop querying from :param token_address: Address of the token :return: List of events sorted by blockNumber """ topic_0 = self.TRANSFER_TOPIC.hex() addresses_encoded = [ HexBytes(eth_abi.encode_single('address', address)).hex() for address in addresses ] # Topics for transfer `to` and `from` an address topics_from = [topic_0, addresses_encoded] topics_to = [topic_0, None, addresses_encoded] parameters: Dict[str, Any] = {'fromBlock': from_block} if to_block: parameters['toBlock'] = to_block if token_address: parameters['address'] = token_address all_events: List[Dict] = [] # Do the request to `eth_getLogs` for topics in (topics_to, topics_from): parameters['topics'] = topics all_events.extend(self.slow_w3.eth.getLogs(parameters)) # Decode events. Just pick valid ERC20 Transfer events (ERC721 `Transfer` has the same signature) erc20_events = [] for event in all_events: event['args'] = self._decode_erc20_or_erc721_log( event['data'], event['topics']) if event['args']: erc20_events.append(event) erc20_events.sort(key=lambda x: x['blockNumber']) return erc20_events
def increase(file, address, template, config, offline, style): """ this is staking submodule increase command. """ if template: show_params() return if not os.path.isfile(file): cust_print('file {} not exits! please check!'.format(file), fg='r') sys.exit(1) params = read_json_file(file) wallet_dir = g_dict_dir_config["wallet_dir"] wallet_file_path, private_key, hrp, _ = verify_password( address, wallet_dir) ppos = get_eth_obj(config, 'ppos') _params = {} try: _params['typ'] = params['typ'] _params['node_id'] = params['node_id'] or ppos.admin.nodeInfo.id _params['amount'] = ppos.w3.toWei(str(params['amount']), "ether") _params['pri_key'] = private_key[2:] if isinstance(params['transaction_cfg'], dict): _params['transaction_cfg'] = params['transaction_cfg'] except KeyError as e: cust_print('Key {} does not exist in file {},please check!'.format( e, file), fg='r') sys.exit(1) try: if offline: data = HexBytes( rlp.encode([ rlp.encode(int(1002)), rlp.encode(bytes.fromhex(_params['node_id'])), rlp.encode(_params['typ']), rlp.encode(_params['amount']) ])).hex() params['to_type'] = 'staking' transaction_dict = un_sign_data(data, params, ppos, _params['pri_key']) unsigned_tx_dir = g_dict_dir_config["unsigned_tx_dir"] check_dir_exits(unsigned_tx_dir) unsigned_file_csv_name = "unsigned_staking_increase_{}.csv".format( get_time_stamp()) unsigned_file_path = os.path.join(unsigned_tx_dir, unsigned_file_csv_name) if style == '': write_csv(unsigned_file_path, [transaction_dict]) else: unsigned_file_path = unsigned_file_path.replace('csv', 'jpg') write_QRCode(transaction_dict, unsigned_file_path) cust_print('unsigned_file save to:{}'.format(unsigned_file_path), fg='g') else: tx_hash = ppos.increaseStaking(*_params.values()) cust_print( 'increase staking send transfer transaction successful, tx hash:{}.' .format(tx_hash), fg='g') except ValueError as e: cust_print( 'increase staking send transfer transaction fail,error info:{}'. format(e), fg='r') sys.exit(1)
"signed": "f87f8085e8d4a510008227108080af6025515b525b600a37f260003556601b596020356000355760015b525b54602052f260255860005b525b54602052f21ba05afed0244d0da90b67cf8979b0f246432a5112c0d31e8d5eedd2bc17b171c694a0bb1035c834677c2e1185b8dc90ca6d1fa585ab3d7ef23707e1a497a98e752d1b" # noqa: 501 }, { "chainId": None, "key": "c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4", "nonce": 0, "gasPrice": 1000000000000, "gas": 10000, "to": HexBytes("0x13978aee95f38490e9769C39B2773Ed763d9cd5F"), "value": 10000000000000000, "data": "", "unsigned": "eb8085e8d4a510008227109413978aee95f38490e9769c39b2773ed763d9cd5f872386f26fc1000080808080", # noqa: 501 "signed": "f86b8085e8d4a510008227109413978aee95f38490e9769c39b2773ed763d9cd5f872386f26fc10000801ba0eab47c1a49bf2fe5d40e01d313900e19ca485867d462fe06e139e3a536c6d4f4a014a569d327dcda4b29f74f93c0e9729d2f49ad726e703f9cd90dbb0fbf6649f1" # noqa: 501 }, ] PRIVATE_KEY_AS_BYTES = b'unicorns' * 4 PRIVATE_KEY_AS_HEXSTR = '0x756e69636f726e73756e69636f726e73756e69636f726e73756e69636f726e73' PRIVATE_KEY_AS_INT = 0x756e69636f726e73756e69636f726e73756e69636f726e73756e69636f726e73 PRIVATE_KEY_AS_OBJ = keys.PrivateKey(PRIVATE_KEY_AS_BYTES)
def encrypt(cls, private_key, password, kdf=None, iterations=None): """ Creates a dictionary with an encrypted version of your private key. To import this keyfile into Ethereum clients like geth and parity: encode this dictionary with :func:`json.dumps` and save it to disk where your client keeps key files. :param private_key: The raw private key :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey` :param str password: The password which you will need to unlock the account in your client :param str kdf: The key derivation function to use when encrypting your private key :param int iterations: The work factor for the key derivation function :returns: The data to use in your encrypted file :rtype: dict If kdf is not set, the default key derivation function falls back to the environment variable :envvar:`ETH_ACCOUNT_KDF`. If that is not set, then 'scrypt' will be used as the default. .. code-block:: python >>> import getpass >>> encrypted = Account.encrypt( 0xb25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364, getpass.getpass() ) { 'address': '5ce9454909639d2d17a3f753ce7d93fa0b9ab12e', 'crypto': { 'cipher': 'aes-128-ctr', 'cipherparams': { 'iv': '0b7845a5c3597d3d378bde9b7c7319b7' }, 'ciphertext': 'a494f1feb3c854e99c1ff01e6aaa17d43c0752009073503b908457dc8de5d2a5', # noqa: E501 'kdf': 'scrypt', 'kdfparams': { 'dklen': 32, 'n': 262144, 'p': 8, 'r': 1, 'salt': '13c4a48123affaa29189e9097726c698' }, 'mac': 'f4cfb027eb0af9bd7a320b4374a3fa7bef02cfbafe0ec5d1fd7ad129401de0b1' }, 'id': 'a60e0578-0e5b-4a75-b991-d55ec6451a6f', 'version': 3 } >>> with open('my-keyfile', 'w') as f: f.write(json.dumps(encrypted)) """ if isinstance(private_key, keys.PrivateKey): key_bytes = private_key.to_bytes() else: key_bytes = HexBytes(private_key) if kdf is None: kdf = cls._default_kdf password_bytes = text_if_str(to_bytes, password) assert len(key_bytes) == 32 return create_keyfile_json(key_bytes, password_bytes, kdf=kdf, iterations=iterations)
def test_produce_mpe_events_from_blockchain(self, mock_get_current_block_no, mock_last_block_number, mock_get_contract_instance): mpe_event_producer = MPEEventProducer("wss://ropsten.infura.io/ws", Repository(NETWORKS)) event_repository = EventRepository(Repository(NETWORKS)) deposit_fund_Event_object = Mock() deposit_fund_Event_object.createFilter = Mock(return_value=Mock( get_all_entries=Mock(return_value=[ AttributeDict({ 'args': AttributeDict({ 'sender': '0xabd2cCb3828b4428bBde6C2031A865b0fb272a5A', 'amount': 30000000 }), 'event': 'DepositFunds', 'logIndex': 1, 'transactionIndex': 18, 'transactionHash': HexBytes( '0x562cc2fa59d9c7a4aa56106a19ad9c8078a95ae68416619fc191d86c50c91f12' ), 'address': '0x8FB1dC8df86b388C7e00689d1eCb533A160B4D0C', 'blockHash': HexBytes( '0xe06042a4d471351c0ee9e50056bd4fb6a0e158b2489ba70775d3c06bd29da19b' ), 'blockNumber': 6286405 }) ]))) mock_get_contract_instance.return_value = Mock( events=Mock(DepositFunds=deposit_fund_Event_object, abi=[{ "type": "event", "name": "DepositFunds" }])) mock_last_block_number.return_value = 50 mock_get_current_block_no.return_value = 50 blockchain_events = mpe_event_producer.produce_event(3) assert blockchain_events == [ AttributeDict({ 'args': AttributeDict({ 'sender': '0xabd2cCb3828b4428bBde6C2031A865b0fb272a5A', 'amount': 30000000 }), 'event': 'DepositFunds', 'logIndex': 1, 'transactionIndex': 18, 'transactionHash': HexBytes( '0x562cc2fa59d9c7a4aa56106a19ad9c8078a95ae68416619fc191d86c50c91f12' ), 'address': '0x8FB1dC8df86b388C7e00689d1eCb533A160B4D0C', 'blockHash': HexBytes( '0xe06042a4d471351c0ee9e50056bd4fb6a0e158b2489ba70775d3c06bd29da19b' ), 'blockNumber': 6286405 }) ]
class EthModuleTest: def test_eth_protocolVersion(self, web3: "Web3") -> None: protocol_version = web3.eth.protocolVersion assert is_string(protocol_version) assert protocol_version.isdigit() def test_eth_syncing(self, web3: "Web3") -> None: syncing = web3.eth.syncing assert is_boolean(syncing) or is_dict(syncing) if is_boolean(syncing): assert syncing is False elif is_dict(syncing): sync_dict = cast(SyncStatus, syncing) assert 'startingBlock' in sync_dict assert 'currentBlock' in sync_dict assert 'highestBlock' in sync_dict assert is_integer(sync_dict['startingBlock']) assert is_integer(sync_dict['currentBlock']) assert is_integer(sync_dict['highestBlock']) def test_eth_coinbase(self, web3: "Web3") -> None: coinbase = web3.eth.coinbase assert is_checksum_address(coinbase) def test_eth_mining(self, web3: "Web3") -> None: mining = web3.eth.mining assert is_boolean(mining) def test_eth_hashrate(self, web3: "Web3") -> None: hashrate = web3.eth.hashrate assert is_integer(hashrate) assert hashrate >= 0 def test_eth_chainId(self, web3: "Web3") -> None: chain_id = web3.eth.chainId # chain id value from geth fixture genesis file assert chain_id == 131277322940537 def test_eth_gasPrice(self, web3: "Web3") -> None: gas_price = web3.eth.gasPrice assert is_integer(gas_price) assert gas_price > 0 def test_eth_accounts(self, web3: "Web3") -> None: accounts = web3.eth.accounts assert is_list_like(accounts) assert len(accounts) != 0 assert all((is_checksum_address(account) for account in accounts)) assert web3.eth.coinbase in accounts def test_eth_blockNumber(self, web3: "Web3") -> None: block_number = web3.eth.blockNumber assert is_integer(block_number) assert block_number >= 0 def test_eth_getBalance(self, web3: "Web3") -> None: coinbase = web3.eth.coinbase with pytest.raises(InvalidAddress): web3.eth.getBalance( ChecksumAddress(HexAddress(HexStr(coinbase.lower())))) balance = web3.eth.getBalance(coinbase) assert is_integer(balance) assert balance >= 0 def test_eth_getBalance_with_block_identifier(self, web3: "Web3") -> None: miner_address = web3.eth.getBlock(1)['miner'] genesis_balance = web3.eth.getBalance(miner_address, 0) later_balance = web3.eth.getBalance(miner_address, 1) assert is_integer(genesis_balance) assert is_integer(later_balance) assert later_balance > genesis_balance def test_eth_getStorageAt( self, web3: "Web3", emitter_contract_address: ChecksumAddress) -> None: storage = web3.eth.getStorageAt(emitter_contract_address, 0) assert isinstance(storage, HexBytes) def test_eth_getStorageAt_invalid_address(self, web3: "Web3") -> None: coinbase = web3.eth.coinbase with pytest.raises(InvalidAddress): web3.eth.getStorageAt( ChecksumAddress(HexAddress(HexStr(coinbase.lower()))), 0) def test_eth_getTransactionCount( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: transaction_count = web3.eth.getTransactionCount( unlocked_account_dual_type) assert is_integer(transaction_count) assert transaction_count >= 0 def test_eth_getTransactionCount_invalid_address(self, web3: "Web3") -> None: coinbase = web3.eth.coinbase with pytest.raises(InvalidAddress): web3.eth.getTransactionCount( ChecksumAddress(HexAddress(HexStr(coinbase.lower())))) def test_eth_getBlockTransactionCountByHash_empty_block( self, web3: "Web3", empty_block: BlockData) -> None: transaction_count = web3.eth.getBlockTransactionCount( empty_block['hash']) assert is_integer(transaction_count) assert transaction_count == 0 def test_eth_getBlockTransactionCountByNumber_empty_block( self, web3: "Web3", empty_block: BlockData) -> None: transaction_count = web3.eth.getBlockTransactionCount( empty_block['number']) assert is_integer(transaction_count) assert transaction_count == 0 def test_eth_getBlockTransactionCountByHash_block_with_txn( self, web3: "Web3", block_with_txn: BlockData) -> None: transaction_count = web3.eth.getBlockTransactionCount( block_with_txn['hash']) assert is_integer(transaction_count) assert transaction_count >= 1 def test_eth_getBlockTransactionCountByNumber_block_with_txn( self, web3: "Web3", block_with_txn: BlockData) -> None: transaction_count = web3.eth.getBlockTransactionCount( block_with_txn['number']) assert is_integer(transaction_count) assert transaction_count >= 1 def test_eth_getUncleCountByBlockHash(self, web3: "Web3", empty_block: BlockData) -> None: uncle_count = web3.eth.getUncleCount(empty_block['hash']) assert is_integer(uncle_count) assert uncle_count == 0 def test_eth_getUncleCountByBlockNumber(self, web3: "Web3", empty_block: BlockData) -> None: uncle_count = web3.eth.getUncleCount(empty_block['number']) assert is_integer(uncle_count) assert uncle_count == 0 def test_eth_getCode(self, web3: "Web3", math_contract_address: ChecksumAddress) -> None: code = web3.eth.getCode(math_contract_address) assert isinstance(code, HexBytes) assert len(code) > 0 def test_eth_getCode_invalid_address(self, web3: "Web3", math_contract: "Contract") -> None: with pytest.raises(InvalidAddress): web3.eth.getCode( ChecksumAddress( HexAddress(HexStr(math_contract.address.lower())))) def test_eth_getCode_with_block_identifier( self, web3: "Web3", emitter_contract: "Contract") -> None: code = web3.eth.getCode(emitter_contract.address, block_identifier=web3.eth.blockNumber) assert isinstance(code, HexBytes) assert len(code) > 0 def test_eth_sign(self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: signature = web3.eth.sign(unlocked_account_dual_type, text='Message tö sign. Longer than hash!') assert is_bytes(signature) assert len(signature) == 32 + 32 + 1 # test other formats hexsign = web3.eth.sign( unlocked_account_dual_type, hexstr=HexStr( '0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821' )) assert hexsign == signature intsign = web3.eth.sign( unlocked_account_dual_type, 0x4d6573736167652074c3b6207369676e2e204c6f6e676572207468616e206861736821 ) assert intsign == signature bytessign = web3.eth.sign( unlocked_account_dual_type, b'Message t\xc3\xb6 sign. Longer than hash!') assert bytessign == signature new_signature = web3.eth.sign(unlocked_account_dual_type, text='different message is different') assert new_signature != signature def test_eth_signTypedData( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress, skip_if_testrpc: Callable[["Web3"], None], ) -> None: validJSONMessage = ''' { "types": { "EIP712Domain": [ {"name": "name", "type": "string"}, {"name": "version", "type": "string"}, {"name": "chainId", "type": "uint256"}, {"name": "verifyingContract", "type": "address"} ], "Person": [ {"name": "name", "type": "string"}, {"name": "wallet", "type": "address"} ], "Mail": [ {"name": "from", "type": "Person"}, {"name": "to", "type": "Person"}, {"name": "contents", "type": "string"} ] }, "primaryType": "Mail", "domain": { "name": "Ether Mail", "version": "1", "chainId": "0x01", "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" }, "message": { "from": { "name": "Cow", "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" }, "to": { "name": "Bob", "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" }, "contents": "Hello, Bob!" } } ''' skip_if_testrpc(web3) signature = HexBytes( web3.eth.signTypedData(unlocked_account_dual_type, json.loads(validJSONMessage))) assert len(signature) == 32 + 32 + 1 def test_invalid_eth_signTypedData( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress, skip_if_testrpc: Callable[["Web3"], None]) -> None: skip_if_testrpc(web3) invalid_typed_message = ''' { "types": { "EIP712Domain": [ {"name": "name", "type": "string"}, {"name": "version", "type": "string"}, {"name": "chainId", "type": "uint256"}, {"name": "verifyingContract", "type": "address"} ], "Person": [ {"name": "name", "type": "string"}, {"name": "wallet", "type": "address"} ], "Mail": [ {"name": "from", "type": "Person"}, {"name": "to", "type": "Person[2]"}, {"name": "contents", "type": "string"} ] }, "primaryType": "Mail", "domain": { "name": "Ether Mail", "version": "1", "chainId": "0x01", "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" }, "message": { "from": { "name": "Cow", "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" }, "to": [{ "name": "Bob", "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" }], "contents": "Hello, Bob!" } } ''' with pytest.raises( ValueError, match= r".*Expected 2 items for array type Person\[2\], got 1 items.*" ): web3.eth.signTypedData(unlocked_account_dual_type, json.loads(invalid_typed_message)) def test_eth_signTransaction(self, web3: "Web3", unlocked_account: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account, 'to': unlocked_account, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': web3.eth.gasPrice, 'nonce': Nonce(0), } result = web3.eth.signTransaction(txn_params) signatory_account = web3.eth.account.recover_transaction(result['raw']) assert unlocked_account == signatory_account assert result['tx']['to'] == txn_params['to'] assert result['tx']['value'] == txn_params['value'] assert result['tx']['gas'] == txn_params['gas'] assert result['tx']['gasPrice'] == txn_params['gasPrice'] assert result['tx']['nonce'] == txn_params['nonce'] def test_eth_sendTransaction_addr_checksum_required( self, web3: "Web3", unlocked_account: ChecksumAddress) -> None: non_checksum_addr = unlocked_account.lower() txn_params: TxParams = { 'from': unlocked_account, 'to': unlocked_account, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': web3.eth.gasPrice, } with pytest.raises(InvalidAddress): invalid_params = cast( TxParams, dict(txn_params, **{'from': non_checksum_addr})) web3.eth.sendTransaction(invalid_params) with pytest.raises(InvalidAddress): invalid_params = cast( TxParams, dict(txn_params, **{'to': non_checksum_addr})) web3.eth.sendTransaction(invalid_params) def test_eth_sendTransaction( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account_dual_type, 'to': unlocked_account_dual_type, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': web3.eth.gasPrice, } txn_hash = web3.eth.sendTransaction(txn_params) txn = web3.eth.getTransaction(txn_hash) assert is_same_address(txn['from'], cast(ChecksumAddress, txn_params['from'])) assert is_same_address(txn['to'], cast(ChecksumAddress, txn_params['to'])) assert txn['value'] == 1 assert txn['gas'] == 21000 assert txn['gasPrice'] == txn_params['gasPrice'] def test_eth_sendTransaction_with_nonce( self, web3: "Web3", unlocked_account: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account, 'to': unlocked_account, 'value': Wei(1), 'gas': Wei(21000), # Increased gas price to ensure transaction hash different from other tests 'gasPrice': Wei(web3.eth.gasPrice * 3), 'nonce': web3.eth.getTransactionCount(unlocked_account), } txn_hash = web3.eth.sendTransaction(txn_params) txn = web3.eth.getTransaction(txn_hash) assert is_same_address(txn['from'], cast(ChecksumAddress, txn_params['from'])) assert is_same_address(txn['to'], cast(ChecksumAddress, txn_params['to'])) assert txn['value'] == 1 assert txn['gas'] == 21000 assert txn['gasPrice'] == txn_params['gasPrice'] assert txn['nonce'] == txn_params['nonce'] def test_eth_replaceTransaction( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account_dual_type, 'to': unlocked_account_dual_type, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': web3.eth.gasPrice, } txn_hash = web3.eth.sendTransaction(txn_params) txn_params['gasPrice'] = Wei(web3.eth.gasPrice * 2) replace_txn_hash = web3.eth.replaceTransaction(txn_hash, txn_params) replace_txn = web3.eth.getTransaction(replace_txn_hash) assert is_same_address(replace_txn['from'], cast(ChecksumAddress, txn_params['from'])) assert is_same_address(replace_txn['to'], cast(ChecksumAddress, txn_params['to'])) assert replace_txn['value'] == 1 assert replace_txn['gas'] == 21000 assert replace_txn['gasPrice'] == txn_params['gasPrice'] def test_eth_replaceTransaction_non_existing_transaction( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account_dual_type, 'to': unlocked_account_dual_type, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': web3.eth.gasPrice, } with pytest.raises(TransactionNotFound): web3.eth.replaceTransaction( HexStr( '0x98e8cc09b311583c5079fa600f6c2a3bea8611af168c52e4b60b5b243a441997' ), txn_params) def test_eth_replaceTransaction_already_mined( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account_dual_type, 'to': unlocked_account_dual_type, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': web3.eth.gasPrice, } txn_hash = web3.eth.sendTransaction(txn_params) web3.eth.waitForTransactionReceipt(txn_hash) txn_params['gasPrice'] = Wei(web3.eth.gasPrice * 2) with pytest.raises(ValueError, match="Supplied transaction with hash"): web3.eth.replaceTransaction(txn_hash, txn_params) def test_eth_replaceTransaction_incorrect_nonce( self, web3: "Web3", unlocked_account: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account, 'to': unlocked_account, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': web3.eth.gasPrice, } txn_hash = web3.eth.sendTransaction(txn_params) txn = web3.eth.getTransaction(txn_hash) txn_params['gasPrice'] = Wei(web3.eth.gasPrice * 2) txn_params['nonce'] = Nonce(txn['nonce'] + 1) with pytest.raises(ValueError): web3.eth.replaceTransaction(txn_hash, txn_params) def test_eth_replaceTransaction_gas_price_too_low( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account_dual_type, 'to': unlocked_account_dual_type, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': Wei(10), } txn_hash = web3.eth.sendTransaction(txn_params) txn_params['gasPrice'] = Wei(9) with pytest.raises(ValueError): web3.eth.replaceTransaction(txn_hash, txn_params) def test_eth_replaceTransaction_gas_price_defaulting_minimum( self, web3: "Web3", unlocked_account: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account, 'to': unlocked_account, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': Wei(10), } txn_hash = web3.eth.sendTransaction(txn_params) txn_params.pop('gasPrice') replace_txn_hash = web3.eth.replaceTransaction(txn_hash, txn_params) replace_txn = web3.eth.getTransaction(replace_txn_hash) assert replace_txn['gasPrice'] == 12 # minimum gas price def test_eth_replaceTransaction_gas_price_defaulting_strategy_higher( self, web3: "Web3", unlocked_account: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account, 'to': unlocked_account, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': Wei(10), } txn_hash = web3.eth.sendTransaction(txn_params) def higher_gas_price_strategy(web3: "Web3", txn: TxParams) -> Wei: return Wei(20) web3.eth.setGasPriceStrategy(higher_gas_price_strategy) txn_params.pop('gasPrice') replace_txn_hash = web3.eth.replaceTransaction(txn_hash, txn_params) replace_txn = web3.eth.getTransaction(replace_txn_hash) assert replace_txn[ 'gasPrice'] == 20 # Strategy provides higher gas price def test_eth_replaceTransaction_gas_price_defaulting_strategy_lower( self, web3: "Web3", unlocked_account: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account, 'to': unlocked_account, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': Wei(10), } txn_hash = web3.eth.sendTransaction(txn_params) def lower_gas_price_strategy(web3: "Web3", txn: TxParams) -> Wei: return Wei(5) web3.eth.setGasPriceStrategy(lower_gas_price_strategy) txn_params.pop('gasPrice') replace_txn_hash = web3.eth.replaceTransaction(txn_hash, txn_params) replace_txn = web3.eth.getTransaction(replace_txn_hash) # Strategy provices lower gas price - minimum preferred assert replace_txn['gasPrice'] == 12 def test_eth_modifyTransaction(self, web3: "Web3", unlocked_account: ChecksumAddress) -> None: txn_params: TxParams = { 'from': unlocked_account, 'to': unlocked_account, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': web3.eth.gasPrice, } txn_hash = web3.eth.sendTransaction(txn_params) modified_txn_hash = web3.eth.modifyTransaction( txn_hash, gasPrice=(cast(int, txn_params['gasPrice']) * 2), value=2) modified_txn = web3.eth.getTransaction(modified_txn_hash) assert is_same_address(modified_txn['from'], cast(ChecksumAddress, txn_params['from'])) assert is_same_address(modified_txn['to'], cast(ChecksumAddress, txn_params['to'])) assert modified_txn['value'] == 2 assert modified_txn['gas'] == 21000 assert modified_txn['gasPrice'] == cast(int, txn_params['gasPrice']) * 2 @pytest.mark.parametrize( 'raw_transaction, expected_hash', [ ( # address 0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6 '0xf8648085174876e8008252089439eeed73fb1d3855e90cbd42f348b3d7b340aaa601801ba0ec1295f00936acd0c2cb90ab2cdaacb8bf5e11b3d9957833595aca9ceedb7aada05dfc8937baec0e26029057abd3a1ef8c505dca2cdc07ffacb046d090d2bea06a', # noqa: E501 '0x1f80f8ab5f12a45be218f76404bda64d37270a6f4f86ededd0eb599f80548c13', ), ( # private key 0x3c2ab4e8f17a7dea191b8c991522660126d681039509dc3bb31af7c9bdb63518 # This is an unfunded account, but the transaction has a 0 gas price, so is valid. # It never needs to be mined, we just want the transaction hash back to confirm. HexBytes( '0xf85f808082c35094d898d5e829717c72e7438bad593076686d7d164a80801ba005c2e99ecee98a12fbf28ab9577423f42e9e88f2291b3acc8228de743884c874a077d6bc77a47ad41ec85c96aac2ad27f05a039c4787fca8a1e5ee2d8c7ec1bb6a' ), # noqa: E501 '0x98eeadb99454427f6aad7b558bac13e9d225512a6f5e5c11cf48e8d4067e51b5', ), ]) def test_eth_sendRawTransaction( self, web3: "Web3", raw_transaction: Union[HexStr, bytes], funded_account_for_raw_txn: ChecksumAddress, expected_hash: HexStr, ) -> None: txn_hash = web3.eth.sendRawTransaction(raw_transaction) assert txn_hash == web3.toBytes(hexstr=expected_hash) def test_eth_call(self, web3: "Web3", math_contract: "Contract") -> None: coinbase = web3.eth.coinbase txn_params = math_contract._prepare_transaction( fn_name='add', fn_args=(7, 11), transaction={ 'from': coinbase, 'to': math_contract.address }, ) call_result = web3.eth.call(txn_params) assert is_string(call_result) result = web3.codec.decode_single('uint256', call_result) assert result == 18 def test_eth_call_with_0_result(self, web3: "Web3", math_contract: "Contract") -> None: coinbase = web3.eth.coinbase txn_params = math_contract._prepare_transaction( fn_name='add', fn_args=(0, 0), transaction={ 'from': coinbase, 'to': math_contract.address }, ) call_result = web3.eth.call(txn_params) assert is_string(call_result) result = web3.codec.decode_single('uint256', call_result) assert result == 0 def test_eth_estimateGas( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: gas_estimate = web3.eth.estimateGas({ 'from': unlocked_account_dual_type, 'to': unlocked_account_dual_type, 'value': Wei(1), }) assert is_integer(gas_estimate) assert gas_estimate > 0 def test_eth_estimateGas_with_block( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: gas_estimate = web3.eth.estimateGas( { 'from': unlocked_account_dual_type, 'to': unlocked_account_dual_type, 'value': Wei(1), }, 'latest') assert is_integer(gas_estimate) assert gas_estimate > 0 def test_eth_getBlockByHash(self, web3: "Web3", empty_block: BlockData) -> None: block = web3.eth.getBlock(empty_block['hash']) assert block['hash'] == empty_block['hash'] def test_eth_getBlockByHash_not_found(self, web3: "Web3", empty_block: BlockData) -> None: with pytest.raises(BlockNotFound): web3.eth.getBlock(UNKNOWN_HASH) def test_eth_getBlockByNumber_with_integer(self, web3: "Web3", empty_block: BlockData) -> None: block = web3.eth.getBlock(empty_block['number']) assert block['number'] == empty_block['number'] def test_eth_getBlockByNumber_latest(self, web3: "Web3", empty_block: BlockData) -> None: current_block_number = web3.eth.blockNumber block = web3.eth.getBlock('latest') assert block['number'] == current_block_number def test_eth_getBlockByNumber_not_found(self, web3: "Web3", empty_block: BlockData) -> None: with pytest.raises(BlockNotFound): web3.eth.getBlock(BlockNumber(12345)) def test_eth_getBlockByNumber_pending(self, web3: "Web3", empty_block: BlockData) -> None: current_block_number = web3.eth.blockNumber block = web3.eth.getBlock('pending') assert block['number'] == current_block_number + 1 def test_eth_getBlockByNumber_earliest(self, web3: "Web3", empty_block: BlockData) -> None: genesis_block = web3.eth.getBlock(BlockNumber(0)) block = web3.eth.getBlock('earliest') assert block['number'] == 0 assert block['hash'] == genesis_block['hash'] def test_eth_getBlockByNumber_full_transactions( self, web3: "Web3", block_with_txn: BlockData) -> None: block = web3.eth.getBlock(block_with_txn['number'], True) transaction = block['transactions'][0] assert transaction['hash'] == block_with_txn['transactions'][ 0] # type: ignore def test_eth_getTransactionByHash(self, web3: "Web3", mined_txn_hash: HexStr) -> None: transaction = web3.eth.getTransaction(mined_txn_hash) assert is_dict(transaction) assert transaction['hash'] == HexBytes(mined_txn_hash) def test_eth_getTransactionByHash_contract_creation( self, web3: "Web3", math_contract_deploy_txn_hash: HexStr) -> None: transaction = web3.eth.getTransaction(math_contract_deploy_txn_hash) assert is_dict(transaction) assert transaction['to'] is None, "to field is %r" % transaction['to'] def test_eth_getTransactionByBlockHashAndIndex( self, web3: "Web3", block_with_txn: BlockData, mined_txn_hash: HexStr) -> None: transaction = web3.eth.getTransactionByBlock(block_with_txn['hash'], 0) assert is_dict(transaction) assert transaction['hash'] == HexBytes(mined_txn_hash) def test_eth_getTransactionByBlockNumberAndIndex( self, web3: "Web3", block_with_txn: BlockData, mined_txn_hash: HexStr) -> None: transaction = web3.eth.getTransactionByBlock(block_with_txn['number'], 0) assert is_dict(transaction) assert transaction['hash'] == HexBytes(mined_txn_hash) def test_eth_getTransactionReceipt_mined(self, web3: "Web3", block_with_txn: BlockData, mined_txn_hash: HexStr) -> None: receipt = web3.eth.getTransactionReceipt(mined_txn_hash) assert is_dict(receipt) assert receipt['blockNumber'] == block_with_txn['number'] assert receipt['blockHash'] == block_with_txn['hash'] assert receipt['transactionIndex'] == 0 assert receipt['transactionHash'] == HexBytes(mined_txn_hash) assert is_checksum_address(receipt['to']) assert receipt['from'] is not None assert is_checksum_address(receipt['from']) def test_eth_getTransactionReceipt_unmined( self, web3: "Web3", unlocked_account_dual_type: ChecksumAddress) -> None: txn_hash = web3.eth.sendTransaction({ 'from': unlocked_account_dual_type, 'to': unlocked_account_dual_type, 'value': Wei(1), 'gas': Wei(21000), 'gasPrice': web3.eth.gasPrice, }) with pytest.raises(TransactionNotFound): web3.eth.getTransactionReceipt(txn_hash) def test_eth_getTransactionReceipt_with_log_entry( self, web3: "Web3", block_with_txn_with_log: BlockData, emitter_contract: "Contract", txn_hash_with_log: HexStr, ) -> None: receipt = web3.eth.getTransactionReceipt(txn_hash_with_log) assert is_dict(receipt) assert receipt['blockNumber'] == block_with_txn_with_log['number'] assert receipt['blockHash'] == block_with_txn_with_log['hash'] assert receipt['transactionIndex'] == 0 assert receipt['transactionHash'] == HexBytes(txn_hash_with_log) assert len(receipt['logs']) == 1 log_entry = receipt['logs'][0] assert log_entry['blockNumber'] == block_with_txn_with_log['number'] assert log_entry['blockHash'] == block_with_txn_with_log['hash'] assert log_entry['logIndex'] == 0 assert is_same_address(log_entry['address'], emitter_contract.address) assert log_entry['transactionIndex'] == 0 assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log) def test_eth_getUncleByBlockHashAndIndex(self, web3: "Web3") -> None: # TODO: how do we make uncles.... pass def test_eth_getUncleByBlockNumberAndIndex(self, web3: "Web3") -> None: # TODO: how do we make uncles.... pass def test_eth_newFilter(self, web3: "Web3") -> None: filter = web3.eth.filter({}) changes = web3.eth.getFilterChanges(filter.filter_id) assert is_list_like(changes) assert not changes logs = web3.eth.getFilterLogs(filter.filter_id) assert is_list_like(logs) assert not logs result = web3.eth.uninstallFilter(filter.filter_id) assert result is True def test_eth_newBlockFilter(self, web3: "Web3") -> None: filter = web3.eth.filter('latest') assert is_string(filter.filter_id) changes = web3.eth.getFilterChanges(filter.filter_id) assert is_list_like(changes) assert not changes # TODO: figure out why this fails in go-ethereum # logs = web3.eth.getFilterLogs(filter.filter_id) # assert is_list_like(logs) # assert not logs result = web3.eth.uninstallFilter(filter.filter_id) assert result is True def test_eth_newPendingTransactionFilter(self, web3: "Web3") -> None: filter = web3.eth.filter('pending') assert is_string(filter.filter_id) changes = web3.eth.getFilterChanges(filter.filter_id) assert is_list_like(changes) assert not changes # TODO: figure out why this fails in go-ethereum # logs = web3.eth.getFilterLogs(filter.filter_id) # assert is_list_like(logs) # assert not logs result = web3.eth.uninstallFilter(filter.filter_id) assert result is True def test_eth_getLogs_without_logs( self, web3: "Web3", block_with_txn_with_log: BlockData) -> None: # Test with block range filter_params: FilterParams = { "fromBlock": BlockNumber(0), "toBlock": BlockNumber(block_with_txn_with_log['number'] - 1), } result = web3.eth.getLogs(filter_params) assert len(result) == 0 # the range is wrong filter_params = { "fromBlock": block_with_txn_with_log['number'], "toBlock": BlockNumber(block_with_txn_with_log['number'] - 1), } result = web3.eth.getLogs(filter_params) assert len(result) == 0 # Test with `address` # filter with other address filter_params = { "fromBlock": BlockNumber(0), "address": UNKNOWN_ADDRESS, } result = web3.eth.getLogs(filter_params) assert len(result) == 0 # Test with multiple `address` # filter with other address filter_params = { "fromBlock": BlockNumber(0), "address": [UNKNOWN_ADDRESS, UNKNOWN_ADDRESS], } result = web3.eth.getLogs(filter_params) assert len(result) == 0 def test_eth_getLogs_with_logs( self, web3: "Web3", block_with_txn_with_log: BlockData, emitter_contract_address: ChecksumAddress, txn_hash_with_log: HexStr, ) -> None: def assert_contains_log(result: Sequence[LogReceipt]) -> None: assert len(result) == 1 log_entry = result[0] assert log_entry['blockNumber'] == block_with_txn_with_log[ 'number'] assert log_entry['blockHash'] == block_with_txn_with_log['hash'] assert log_entry['logIndex'] == 0 assert is_same_address(log_entry['address'], emitter_contract_address) assert log_entry['transactionIndex'] == 0 assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log) # Test with block range # the range includes the block where the log resides in filter_params: FilterParams = { "fromBlock": block_with_txn_with_log['number'], "toBlock": block_with_txn_with_log['number'], } result = web3.eth.getLogs(filter_params) assert_contains_log(result) # specify only `from_block`. by default `to_block` should be 'latest' filter_params = { "fromBlock": BlockNumber(0), } result = web3.eth.getLogs(filter_params) assert_contains_log(result) # Test with `address` # filter with emitter_contract.address filter_params = { "fromBlock": BlockNumber(0), "address": emitter_contract_address, } def test_eth_getLogs_with_logs_topic_args( self, web3: "Web3", block_with_txn_with_log: BlockData, emitter_contract_address: ChecksumAddress, txn_hash_with_log: HexStr, ) -> None: def assert_contains_log(result: Sequence[LogReceipt]) -> None: assert len(result) == 1 log_entry = result[0] assert log_entry['blockNumber'] == block_with_txn_with_log[ 'number'] assert log_entry['blockHash'] == block_with_txn_with_log['hash'] assert log_entry['logIndex'] == 0 assert is_same_address(log_entry['address'], emitter_contract_address) assert log_entry['transactionIndex'] == 0 assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log) # Test with None event sig filter_params: FilterParams = { "fromBlock": BlockNumber(0), "topics": [ None, HexStr( '0x000000000000000000000000000000000000000000000000000000000000d431' ) ], } result = web3.eth.getLogs(filter_params) assert_contains_log(result) # Test with None indexed arg filter_params = { "fromBlock": BlockNumber(0), "topics": [ HexStr( '0x057bc32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca' ), None ], } result = web3.eth.getLogs(filter_params) assert_contains_log(result) def test_eth_getLogs_with_logs_none_topic_args(self, web3: "Web3") -> None: # Test with None overflowing filter_params: FilterParams = { "fromBlock": BlockNumber(0), "topics": [None, None, None], } result = web3.eth.getLogs(filter_params) assert len(result) == 0 def test_eth_call_old_contract_state( self, web3: "Web3", math_contract: "Contract", unlocked_account: ChecksumAddress) -> None: start_block = web3.eth.getBlock('latest') block_num = start_block["number"] block_hash = start_block["hash"] math_contract.functions.increment().transact( {'from': unlocked_account}) # This isn't an incredibly convincing test since we can't mine, and # the default resolved block is latest, So if block_identifier was ignored # we would get the same result. For now, we mostly depend on core tests. # Ideas to improve this test: # - Enable on-demand mining in more clients # - Increment the math contract in all of the fixtures, and check the value in an old block block_hash_call_result = math_contract.functions.counter().call( block_identifier=block_hash) block_num_call_result = math_contract.functions.counter().call( block_identifier=block_num) latest_call_result = math_contract.functions.counter().call( block_identifier='latest') default_call_result = math_contract.functions.counter().call() pending_call_result = math_contract.functions.counter().call( block_identifier='pending') assert block_hash_call_result == 0 assert block_num_call_result == 0 assert latest_call_result == 0 assert default_call_result == 0 if pending_call_result != 1: raise AssertionError("pending call result was %d instead of 1" % pending_call_result) def test_eth_uninstallFilter(self, web3: "Web3") -> None: filter = web3.eth.filter({}) assert is_string(filter.filter_id) success = web3.eth.uninstallFilter(filter.filter_id) assert success is True failure = web3.eth.uninstallFilter(filter.filter_id) assert failure is False def test_eth_getTransactionFromBlock_deprecation( self, web3: "Web3", block_with_txn: BlockData) -> None: with pytest.raises(DeprecationWarning): web3.eth.getTransactionFromBlock(block_with_txn['hash'], 0) def test_eth_getCompilers_deprecation(self, web3: "Web3") -> None: with pytest.raises(DeprecationWarning): web3.eth.getCompilers() def test_eth_submitHashrate(self, web3: "Web3") -> None: # node_id from EIP 1474: https://github.com/ethereum/EIPs/pull/1474/files node_id = HexStr( '59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c') result = web3.eth.submitHashrate(5000, node_id) assert result is True def test_eth_submitWork(self, web3: "Web3") -> None: nonce = 1 pow_hash = HexStr( '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' ) mix_digest = HexStr( '0xD1FE5700000000000000000000000000D1FE5700000000000000000000000000' ) result = web3.eth.submitWork(nonce, pow_hash, mix_digest) assert result is False
def test_eth_getTransactionByHash(self, web3: "Web3", mined_txn_hash: HexStr) -> None: transaction = web3.eth.getTransaction(mined_txn_hash) assert is_dict(transaction) assert transaction['hash'] == HexBytes(mined_txn_hash)
def encode_abi(web3, abi, arguments, vmtype, data=None, setabi=None): arguments = list(arguments) if vmtype == 1: inputlength = len(abi['inputs']) if inputlength == len(arguments): if arguments: arrinputs = abi['inputs'] paramabi = encodeparameters(arrinputs, arguments, setabi) else: paramabi = [] else: raise Exception( 'The number of arguments is not matching the methods required number.' 'You need to pass {} arguments.'.format(inputlength)) magicnum = ['00', '61', '73', '6d'] paramabi.insert(0, fnv1_64(bytes(abi['name'], 'utf8'))) if abi['type'] == 'constructor': if data: data1 = bytes.fromhex(str(data, encoding='utf8')) deploydata = rlp.encode([data1, rlp.encode(paramabi)]) encodata = ''.join(magicnum) + deploydata.hex() return '0x' + encodata else: return '0x' + rlp.encode(paramabi).hex() else: encodata = rlp.encode(paramabi).hex() return '0x' + encodata else: argument_types = get_abi_input_types(abi) for j in range(len(argument_types)): if argument_types[j]: if argument_types[j] == 'address': hrpgot, data1 = bech32.decode(arguments[j][:3], arguments[j]) addr = to_checksum_address(bytes(data1)) arguments[j] = addr # .split(",") elif argument_types[j] == 'address[]': for i in range(len(arguments[j])): hrpgot, data1 = bech32.decode(arguments[j][i][:3], arguments[j][i]) addr = to_checksum_address(bytes(data1)) arguments[j][i] = addr if not check_if_arguments_can_be_encoded(abi, arguments, {}): raise TypeError( "One or more arguments could not be encoded to the necessary " "ABI type. Expected types are: {0}".format( ', '.join(argument_types), )) try: normalizers = [ abi_ens_resolver(web3), abi_address_to_hex, abi_bytes_to_bytes, abi_string_to_text, ] normalized_arguments = map_abi_data( normalizers, argument_types, arguments, ) encoded_arguments = eth_abi_encode_abi( argument_types, normalized_arguments, ) except EncodingError as e: raise TypeError( "One or more arguments could not be encoded to the necessary " "ABI type: {0}".format(str(e))) if data: return to_hex(HexBytes(data) + encoded_arguments) else: return encode_hex(encoded_arguments)
def wasmevent_decode(hrp, types, data): if isinstance(data, HexBytes) or isinstance(data, bytes): bufs = detail_decode_data(rlp.decode(data)) else: bufs = data data1 = [] if (not any(bufs)) and ('int' in types): bufs = ['0'] if (not isinstance(bufs, tuple)) and (not isinstance(types, tuple)): buf = bufs type = types if type == 'string': tem = [] if isinstance(buf, list): if not isinstance(buf[0], list): for i in buf: tem.append(bytes.decode(HexBytes(i))) data1 = ''.join(tem) else: data1 = [ wasmevent_decode(hrp, { 'type': type, 'name': '' }, j) for j in buf ] elif isinstance(buf, str): data1 = bytes.decode(HexBytes(buf)) elif isinstance(buf, tuple): data1 = [ wasmevent_decode(hrp, { 'type': type, 'name': '' }, j) for j in buf ] elif type.startswith('uint') and not type.endswith(']'): digit = 0 if len(buf) <= 1: data1 = int(buf[0], 16) else: for i in range(len(buf)): digit += int(buf[i], 16) * (256**(len(buf) - 1 - i)) data1 = digit elif type == 'bool': if not buf: data1 = False elif int(buf[0], 16): data1 = True else: data1 = False elif type in ['int8', 'int16', 'int32', 'int64']: if isinstance(buf, list): buf = ''.join(buf) temp = int(buf, 16) data1 = (temp >> 1) ^ (temp & 1) * (-1) elif type == 'float': data1 = struct.unpack('>f', HexBytes(buf)) elif type == 'double': data1 = struct.unpack('>d', HexBytes(buf)) elif type.endswith(']'): lasti = type.rindex('[') vectype = type[0:lasti] if vectype == 'uint8': data1 = buf else: data1 = [ wasmevent_decode(hrp, { 'type': vectype, 'name': '' }, i) for i in buf ] elif type.startswith('FixedHash'): data1 = '0x' + tostring_hex(buf) if type.endswith('<20>'): temp = [] try: temp = tobech32address(hrp, data1) except: raise ( 'wasmdecode error ! can not match FixedHash<20> type !' ) finally: data1 = temp else: if len(bufs) != len(types): if len(bufs[0]) == len(types): data1 = [] for i in range(len(bufs[0])): buf = bufs[0][i] type = types[i] data1.append(wasmevent_decode(hrp, type, buf)) else: data1 = [] for i in range(len(bufs)): buf = bufs[i] type = types[i] data1.append(wasmevent_decode(hrp, type, buf)) return data1
def wasmdecode_abi(hrp, types, data, setabi=None): if isinstance(data, HexBytes) or isinstance(data, bytes): buf = detail_decode_data(rlp.decode(data)) else: buf = data type = types['type'] name = types['name'] if (not any(buf)) and ('int' in type): buf = ['0'] if type == 'string': tem = [] if isinstance(buf, list): if not isinstance(buf[0], list): for i in buf: tem.append(bytes.decode(HexBytes(i))) data1 = ''.join(tem) else: data1 = [ wasmdecode_abi(hrp, { 'type': type, 'name': '' }, j, setabi) for j in buf ] elif isinstance(buf, str): data1 = bytes.decode(HexBytes(buf)) elif isinstance(buf, tuple): data1 = [ wasmdecode_abi(hrp, { 'type': type, 'name': '' }, j, setabi) for j in buf ] elif type.startswith('uint') and not type.endswith(']'): digit = 0 if len(buf) <= 1: data1 = int(buf[0], 16) else: for i in range(len(buf)): digit += int(buf[i], 16) * (256**(len(buf) - 1 - i)) data1 = digit elif type == 'bool': if not buf: data1 = False elif int(buf[0], 16): data1 = True else: data1 = False elif type in ['int8', 'int16', 'int32', 'int64']: if isinstance(buf, list): buf = ''.join(buf) temp = int(buf, 16) data1 = (temp >> 1) ^ (temp & 1) * (-1) elif type == 'float': data1 = struct.unpack('>f', HexBytes(buf)) elif type == 'double': data1 = struct.unpack('>d', HexBytes(buf)) elif type.endswith(']'): lasti = type.rindex('[') vectype = type[0:lasti] if vectype == 'uint8': data1 = buf else: data1 = [ wasmdecode_abi(hrp, { 'type': vectype, 'name': '' }, i, setabi) for i in buf ] elif type.startswith('list'): i1 = type.index('<') i2 = type.index('>') itype = type[i1 + 1:i2] if isinstance(buf, tuple) and len(buf) <= 1: buf = buf[0] data1 = [ wasmdecode_abi(hrp, { 'type': itype, 'name': '' }, j, setabi) for j in buf ] elif type.startswith('map'): i1 = type.index('<') i2 = type.index(',') i3 = type.index('>') ktype = type[i1 + 1:i2] vtype = type[i2 + 1:i3] data1 = [] for j in range(len(buf)): if len(buf[j]) <= 1 and len(buf[j][0]) >= 2: kvalue = wasmdecode_abi(hrp, { 'type': ktype, 'name': '' }, buf[j][0][0], setabi) vvalue = wasmdecode_abi(hrp, { 'type': vtype, 'name': '' }, buf[j][0][1], setabi) else: kvalue = wasmdecode_abi(hrp, { 'type': ktype, 'name': '' }, buf[j][0], setabi) vvalue = wasmdecode_abi(hrp, { 'type': vtype, 'name': '' }, buf[j][1], setabi) data1.append([kvalue, vvalue]) elif type.startswith('pair'): i1 = type.index('<') i2 = type.index(',') i3 = type.index('>') ktype = type[i1 + 1:i2] vtype = type[i2 + 1:i3] data1 = [ wasmdecode_abi(hrp, { 'type': ktype, 'name': '' }, buf[0], setabi), wasmdecode_abi(hrp, { 'type': vtype, 'name': '' }, buf[1], setabi) ] elif type.startswith('set'): i1 = type.index('<') i2 = type.index('>') stype = type[i1 + 1:i2] if isinstance(buf, tuple) and len(buf) <= 1: buf = buf[0] data1 = [ wasmdecode_abi(hrp, { 'type': stype, 'name': '' }, j, setabi) for j in buf ] data1 = set(data1) elif type == 'struct': structtype = [ item for item in setabi if item['type'] == 'struct' and item['name'] == name ] if not structtype: raise Exception('can not find struct in {} .'.format(name)) else: data1 = [0 for x in range(len(structtype[0]['inputs']))] for i in range(len(structtype[0]['inputs'])): data1[i] = wasmdecode_abi(hrp, structtype[0]['inputs'][i], buf[i], setabi) elif type.startswith('FixedHash'): data1 = '0x' + tostring_hex(buf) if type.endswith('<20>'): temp = [] try: temp = tobech32address(hrp, data1) except: raise ('wasmdecode error ! can not match FixedHash<20> type !') finally: data1 = temp else: structtype = [item for item in setabi if item['name'] == type] if not structtype: raise Exception('can not find struct through {} .'.format(type)) else: data1 = [0 for x in range(len(structtype[0]['inputs']))] for i in range(len(structtype[0]['inputs'])): data1[i] = wasmdecode_abi(hrp, structtype[0]['inputs'][i], buf[i], setabi) return data1
def get_prep_value(self, value: ChecksumAddress) -> Optional[bytes]: if value: return HexBytes(self.to_python(value))
def from_db_value(self, value: memoryview, expression, connection) -> Optional[bytes]: if value: return HexBytes(value.tobytes()).hex()
class Web3ModuleTest: def test_web3_clientVersion(self, web3): client_version = web3.version.node self._check_web3_clientVersion(client_version) def _check_web3_clientVersion(self, client_version): raise NotImplementedError("Must be implemented by subclasses") # Contract that calculated test values can be found at # https://kovan.etherscan.io/address/0xb9be06f5b99372cf9afbccadbbb9954ccaf7f4bb#code @pytest.mark.parametrize( 'types,values,expected', ( ( ['bool'], [True], HexBytes("0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2"), ), ( ['uint8', 'uint8', 'uint8'], [97, 98, 99], HexBytes("0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"), ), ( ['uint248'], [30], HexBytes("0x30f95d210785601eb33ae4d53d405b26f920e765dff87cca8e9a4aec99f82671"), ), ( ['bool', 'uint16'], [True, 299], HexBytes("0xed18599ccd80ee9fae9a28b0e34a5573c3233d7468f808fd659bc171cf0b43bd"), ), ( ['int256'], [-10], HexBytes("0xd6fb717f7e270a360f5093ce6a7a3752183e89c9a9afe5c0cb54b458a304d3d5"), ), ( ['int256'], [10], HexBytes("0xc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8"), ), ( ['int8', 'uint8'], [-10, 18], HexBytes("0x5c6ab1e634c08d9c0f4df4d789e8727943ef010dd7ca8e3c89de197a26d148be"), ), ( ['address'], ["0x49eddd3769c0712032808d86597b84ac5c2f5614"], InvalidAddress, ), ( ['address'], ["0x49EdDD3769c0712032808D86597B84ac5c2F5614"], HexBytes("0x2ff37b5607484cd4eecf6d13292e22bd6e5401eaffcc07e279583bc742c68882"), ), ( ['bytes2'], ['0x5402'], HexBytes("0x4ed9171bda52fca71ab28e7f452bd6eacc3e5a568a47e0fa53b503159a9b8910"), ), ( ['bytes3'], ['0x5402'], HexBytes("0x4ed9171bda52fca71ab28e7f452bd6eacc3e5a568a47e0fa53b503159a9b8910"), ), ( ['bytes'], [ '0x636865636b6c6f6e6762797465737472696e676167' '61696e7374736f6c6964697479736861336861736866756e6374696f6e' ], HexBytes("0xd78a84d65721b67e4011b10c99dafdedcdcd7cb30153064f773e210b4762e22f"), ), ( ['string'], ['testing a string!'], HexBytes("0xe8c275c0b4070a5ec6cfcb83f0ba394b30ddd283de785d43f2eabfb04bd96747"), ), ( ['string', 'bool', 'uint16', 'bytes2', 'address'], [ 'testing a string!', False, 299, '0x5402', "0x49eddd3769c0712032808d86597b84ac5c2f5614", ], InvalidAddress, ), ( ['string', 'bool', 'uint16', 'bytes2', 'address'], [ 'testing a string!', False, 299, '0x5402', "0x49EdDD3769c0712032808D86597B84ac5c2F5614", ], HexBytes("0x8cc6eabb25b842715e8ca39e2524ed946759aa37bfb7d4b81829cf5a7e266103"), ), ( ['bool[2][]'], [[[True, False], [False, True]]], HexBytes("0x1eef261f2eb51a8c736d52be3f91ff79e78a9ec5df2b7f50d0c6f98ed1e2bc06"), ), ( ['bool[]'], [[True, False, True]], HexBytes("0x5c6090c0461491a2941743bda5c3658bf1ea53bbd3edcde54e16205e18b45792"), ), ( ['uint24[]'], [[1, 0, 1]], HexBytes("0x5c6090c0461491a2941743bda5c3658bf1ea53bbd3edcde54e16205e18b45792"), ), ( ['uint8[2]'], [[8, 9]], HexBytes("0xc7694af312c4f286114180fd0ba6a52461fcee8a381636770b19a343af92538a"), ), ( ['uint256[2]'], [[8, 9]], HexBytes("0xc7694af312c4f286114180fd0ba6a52461fcee8a381636770b19a343af92538a"), ), ( ['uint8[]'], [[8]], HexBytes("0xf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3"), ), ( ['address[]'], [[ "0x49EdDD3769c0712032808D86597B84ac5c2F5614", "0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5", ]], HexBytes("0xb98565c0c26a962fd54d93b0ed6fb9296e03e9da29d2281ed3e3473109ef7dde"), ), ( ['address[]'], [[ "0x49EdDD3769c0712032808D86597B84ac5c2F5614", "0xa6b759bbbf4b59d24acf7e06e79f3a5d104fdce5", ]], InvalidAddress, ), ), ) def test_solidityKeccak(self, web3, types, values, expected): if isinstance(expected, type) and issubclass(expected, Exception): with pytest.raises(expected): web3.solidityKeccak(types, values) return actual = web3.solidityKeccak(types, values) assert actual == expected @pytest.mark.parametrize( 'types, values, expected', ( ( ['address'], ['one.eth'], HexBytes("0x2ff37b5607484cd4eecf6d13292e22bd6e5401eaffcc07e279583bc742c68882"), ), ( ['address[]'], [['one.eth', 'two.eth']], HexBytes("0xb98565c0c26a962fd54d93b0ed6fb9296e03e9da29d2281ed3e3473109ef7dde"), ), ), ) def test_solidityKeccak_ens(self, web3, types, values, expected): with ens_addresses(web3, { 'one.eth': "0x49EdDD3769c0712032808D86597B84ac5c2F5614", 'two.eth': "0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5", }): # when called as class method, any name lookup attempt will fail with pytest.raises(InvalidAddress): Web3.solidityKeccak(types, values) # when called as instance method, ens lookups can succeed actual = web3.solidityKeccak(types, values) assert actual == expected @pytest.mark.parametrize( 'types,values', ( (['address'], ['0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5', True]), (['address', 'bool'], ['0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5']), ([], ['0xA6b759bBbf4B59D24acf7E06e79f3a5D104fdCE5']), ) ) def test_solidityKeccak_same_number_of_types_and_values(self, web3, types, values): with pytest.raises(ValueError): web3.solidityKeccak(types, values) def test_is_connected(self, web3): assert web3.isConnected()
def test_eth_getTransactionByBlockHashAndIndex( self, web3: "Web3", block_with_txn: BlockData, mined_txn_hash: HexStr) -> None: transaction = web3.eth.getTransactionByBlock(block_with_txn['hash'], 0) assert is_dict(transaction) assert transaction['hash'] == HexBytes(mined_txn_hash)
def update_eth_pol_status(debug=False): # get active ETH->POL transaction polygon_transactions = PanamaTransaction.objects.filter( type=PanamaTransaction.SWAP_POLYGON, from_network=ETHEREUM_NETWORK, to_network=POLYGON_NETWORK, status__iexact=ETH_POL_STATUS, ) if not polygon_transactions: return 0 # connect to providers # try: # network = Network.displayed_objects.get( # title=ETHEREUM_NETWORK, # testnet=TESTNET, # ) # except Network.DoesNotExist: # logging.error(f"Network {ETHEREUM_NETWORK} with Testnet:{TESTNET} doesn't exist") # return 0 # try: # w3 = get_infura_provider(provider_url=network.provider_url) # except Exception: # logging.error(f'Ethereum->polygon error on provider connection') # return 0 # w3 = get_infura_provider(provider_url=INFURA_URL) # w3 = get_infura_provider(provider_url=INFURA_URL_GOERLI_TESTNET) w3 = get_infura_provider( provider_url=INFURA_URL if debug else INFURA_URL_GOERLI_TESTNET ) # network = Network.displayed_objects.get( # title=POLYGON_NETWORK, # testnet=TESTNET, # ) # try: # pol_provider = get_infura_provider(provider_url=network.provider_url) # except Exception: # return 0 # pol_provider = get_infura_provider(provider_url=POL_PR_URL) # pol_provider = get_infura_provider(provider_url=POL_TEST_PR_URL) pol_provider = get_infura_provider( provider_url=POL_PR_URL if debug else POL_TEST_PR_URL ) # check status for active transaction on blockchain for transaction in polygon_transactions: try: # get transaction receipt receipt = w3.eth.getTransactionReceipt(transaction.transaction_id) # magic code from polygon docs ------ # load some contract some_abi = get_abi_by_filename(SOME_ABI_NAME) some_contract = pol_provider.eth.contract( abi=some_abi, address=SOME_ADDRESS, ) # get pol counter (no one know what is it) polygon_counter = some_contract.functions.lastStateId().call() # get eth counter eth_counter = 0 for log in receipt.logs: if log.topics[0] == HexBytes(LOG_ZERO_TOPIC_HASH): eth_counter = int(bytes.hex(log.topics[1]), 16) # magic counters check if eth_counter: if polygon_counter >= eth_counter: transaction.status = ETH_POL_STATUS_COMPLETED transaction.save() logging.info( 'Ethereum -> Polygon second part updating on {}.'.format( transaction.second_transaction_id ) ) except Exception as exception_error: logging.error( """ Ethereum-> Polygon error on {tx_id}. Error description: {error_message} """.format( tx_id=transaction.second_transaction_id, error_message=exception_error.__str__(), ) ) continue
def normalize_bytecode(bytecode): if bytecode: bytecode = HexBytes(bytecode) return bytecode
def sign_transaction(self, transaction_dict, private_key): """ Sign a transaction using a local private key. Produces signature details and the hex-encoded transaction suitable for broadcast using :meth:`w3.eth.sendRawTransaction() <web3.eth.Eth.sendRawTransaction>`. Create the transaction dict for a contract method with `my_contract.functions.my_function().buildTransaction() <http://web3py.readthedocs.io/en/latest/contracts.html#methods>`_ :param dict transaction_dict: the transaction with keys: nonce, chainId, to, data, value, gas, and gasPrice. :param private_key: the private key to sign the data with :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey` :returns: Various details about the signature - most importantly the fields: v, r, and s :rtype: AttributeDict .. code-block:: python >>> transaction = { # Note that the address must be in checksum format or native bytes: 'to': '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55', 'value': 1000000000, 'gas': 2000000, 'gasPrice': 234567897654321, 'nonce': 0, 'chainId': 1 } >>> key = '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318' >>> signed = Account.sign_transaction(transaction, key) {'hash': HexBytes('0x6893a6ee8df79b0f5d64a180cd1ef35d030f3e296a5361cf04d02ce720d32ec5'), 'r': 4487286261793418179817841024889747115779324305375823110249149479905075174044, 'rawTransaction': HexBytes('0xf86a8086d55698372431831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca008025a009ebb6ca057a0535d6186462bc0b465b561c94a295bdb0621fc19208ab149a9ca0440ffd775ce91a833ab410777204d5341a6f9fa91216a6f3ee2c051fea6a0428'), # noqa: E501 's': 30785525769477805655994251009256770582792548537338581640010273753578382951464, 'v': 37} >>> w3.eth.sendRawTransaction(signed.rawTransaction) """ if not isinstance(transaction_dict, Mapping): raise TypeError("transaction_dict must be dict-like, got %r" % transaction_dict) account = self.from_key(private_key) # allow from field, *only* if it matches the private key if 'from' in transaction_dict: if transaction_dict['from'] == account.address: sanitized_transaction = dissoc(transaction_dict, 'from') else: raise TypeError( "from field must match key's %s, but it was %s" % ( account.address, transaction_dict['from'], )) else: sanitized_transaction = transaction_dict # sign transaction ( v, r, s, rlp_encoded, ) = sign_transaction_dict(account._key_obj, sanitized_transaction) transaction_hash = keccak(rlp_encoded) return AttributeDict({ 'rawTransaction': HexBytes(rlp_encoded), 'hash': HexBytes(transaction_hash), 'r': r, 's': s, 'v': v, })
def recoverHash(self, message_hash, vrs=None, signature=None): ''' Get the address of the account that signed the message with the given hash. You must specify exactly one of: vrs or signature :param message_hash: the hash of the message that you want to verify :type message_hash: hex str or bytes or int :param vrs: the three pieces generated by an elliptic curve signature :type vrs: tuple(v, r, s), each element is hex str, bytes or int :param signature: signature bytes concatenated as r+s+v :type signature: hex str or bytes or int :returns: address of signer, hex-encoded & checksummed :rtype: str .. code-block:: python >>> msg = "I♥SF" >>> msghash = '0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750' >>> vrs = ( 28, '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3', '0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce') >>> Account.recoverHash(msghash, vrs=vrs) '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' # All of these recover calls are equivalent: # variations on msghash >>> msghash = b"\\x14v\\xab\\xb7E\\xd4#\\xbf\\t'?\\x1a\\xfd\\x88}\\x95\\x11\\x81\\xd2Z\\xdcf\\xc4\\x83JpI\\x19\\x11\\xb7\\xf7P" # noqa: E501 >>> Account.recoverHash(msghash, vrs=vrs) >>> msghash = 0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750 >>> Account.recoverHash(msghash, vrs=vrs) # variations on vrs >>> vrs = ( '0x1c', '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3', '0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce') >>> Account.recoverHash(msghash, vrs=vrs) >>> vrs = ( b'\\x1c', b'\\xe6\\xca\\x9b\\xbaX\\xc8\\x86\\x11\\xfa\\xd6jl\\xe8\\xf9\\x96\\x90\\x81\\x95Y8\\x07\\xc4\\xb3\\x8b\\xd5(\\xd2\\xcf\\xf0\\x9dN\\xb3', # noqa: E501 b'>[\\xfb\\xbfM>9\\xb1\\xa2\\xfd\\x81jv\\x80\\xc1\\x9e\\xbe\\xba\\xf3\\xa1A\\xb29\\x93J\\xd4<\\xb3?\\xce\\xc8\\xce') # noqa: E501 >>> Account.recoverHash(msghash, vrs=vrs) >>> vrs = ( 0x1c, 0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb3, 0x3e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce) >>> Account.recoverHash(msghash, vrs=vrs) # variations on signature >>> signature = '0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c' # noqa: E501 >>> Account.recoverHash(msghash, signature=signature) >>> signature = b'\\xe6\\xca\\x9b\\xbaX\\xc8\\x86\\x11\\xfa\\xd6jl\\xe8\\xf9\\x96\\x90\\x81\\x95Y8\\x07\\xc4\\xb3\\x8b\\xd5(\\xd2\\xcf\\xf0\\x9dN\\xb3>[\\xfb\\xbfM>9\\xb1\\xa2\\xfd\\x81jv\\x80\\xc1\\x9e\\xbe\\xba\\xf3\\xa1A\\xb29\\x93J\\xd4<\\xb3?\\xce\\xc8\\xce\\x1c' # noqa: E501 >>> Account.recoverHash(msghash, signature=signature) >>> signature = 0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c # noqa: E501 >>> Account.recoverHash(msghash, signature=signature) ''' hash_bytes = HexBytes(message_hash) if len(hash_bytes) != 32: raise ValueError("The message hash must be exactly 32-bytes") 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 test_produce_registry_events_from_blockchain( self, mock_get_current_block_no, mock_last_block_number, mock_get_contract_instance): registry_event_producer = RegistryEventProducer( "wss://ropsten.infura.io/ws", Repository(NETWORKS)) org_created_event_object = Mock() event_repository = EventRepository(Repository(NETWORKS)) org_created_event_object.createFilter = Mock(return_value=Mock( get_all_entries=Mock(return_value=[ AttributeDict({ 'args': AttributeDict({ 'orgId': b'snet\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' }), 'event': 'OrganizationCreated', 'logIndex': 1, 'transactionIndex': 15, 'transactionHash': HexBytes( '0x7934a42442792f6d5a171df218b66161021c885085187719c991ec58d7459821' ), 'address': '0x663422c6999Ff94933DBCb388623952CF2407F6f', 'blockHash': HexBytes( '0x1da77d63b7d57e0a667ffb9f6d23be92f3ffb5f4b27b39b86c5d75bb167d6779' ), 'blockNumber': 6243627 }) ]))) mock_get_contract_instance.return_value = Mock( events=Mock(organizationCreated=org_created_event_object, abi=[{ "type": "event", "name": "organizationCreated" }])) mock_last_block_number.return_value = 50 mock_get_current_block_no.return_value = 50 blockchain_events = registry_event_producer.produce_event(3) assert blockchain_events == [ AttributeDict({ 'args': AttributeDict({ 'orgId': b'snet\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' }), 'event': 'OrganizationCreated', 'logIndex': 1, 'transactionIndex': 15, 'transactionHash': HexBytes( '0x7934a42442792f6d5a171df218b66161021c885085187719c991ec58d7459821' ), 'address': '0x663422c6999Ff94933DBCb388623952CF2407F6f', 'blockHash': HexBytes( '0x1da77d63b7d57e0a667ffb9f6d23be92f3ffb5f4b27b39b86c5d75bb167d6779' ), 'blockNumber': 6243627 }) ]
def signHash(self, message_hash, private_key): ''' Sign the hash provided. .. WARNING:: *Never* sign a hash that you didn't generate, it can be an arbitrary transaction. For example, it might send all of your account's ether to an attacker. If you would like compatibility with :meth:`w3.eth.sign() <web3.eth.Eth.sign>` you can use :meth:`~eth_account.messages.defunct_hash_message`. Several other message standards are proposed, but none have a clear consensus. You'll need to manually comply with any of those message standards manually. :param message_hash: the 32-byte message hash to be signed :type message_hash: hex str, bytes or int :param private_key: the key to sign the message with :type private_key: hex str, bytes, int or :class:`eth_keys.datatypes.PrivateKey` :returns: Various details about the signature - most importantly the fields: v, r, and s :rtype: ~eth_account.datastructures.AttributeDict .. code-block:: python >>> msg = "I♥SF" >>> from eth_account.messages import defunct_hash_message >>> msghash = defunct_hash_message(text=msg) HexBytes('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750') >>> key = "0xb25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364" >>> Account.signHash(msghash, key) {'messageHash': HexBytes('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750'), # noqa: E501 'r': 104389933075820307925104709181714897380569894203213074526835978196648170704563, 's': 28205917190874851400050446352651915501321657673772411533993420917949420456142, 'signature': HexBytes('0xe6ca9bba58c88611fad66a6ce8f996908195593807c4b38bd528d2cff09d4eb33e5bfbbf4d3e39b1a2fd816a7680c19ebebaf3a141b239934ad43cb33fcec8ce1c'), # noqa: E501 'v': 28} # these are equivalent: >>> Account.signHash( 0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750, key ) >>> Account.signHash( "0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750", key ) ''' msg_hash_bytes = HexBytes(message_hash) if len(msg_hash_bytes) != 32: raise ValueError("The message hash must be exactly 32-bytes") key = self._parsePrivateKey(private_key) (v, r, s, eth_signature_bytes) = sign_message_hash(key, msg_hash_bytes) return AttributeDict({ 'messageHash': msg_hash_bytes, 'r': r, 's': s, 'v': v, 'signature': HexBytes(eth_signature_bytes), })
def convert_id_to_number_or_hash(block_id): try: return int(block_id) except ValueError: return HexBytes(block_id)
from vault.eth import sign_transfer from hexbytes import HexBytes PRIVATE_KEY = b"\xb2\\}\xb3\x1f\xee\xd9\x12''\xbf\t9\xdcv\x9a\x96VK-\xe4\xc4rm\x03[6\xec\xf1\xe5\xb3d" FROM_ADDR = '0x5ce9454909639D2D17A3F753ce7d93fa0b9aB12E' signed = sign_transfer( PRIVATE_KEY, FROM_ADDR, '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', '10') repr(signed.rawTransaction) assert signed.rawTransaction == HexBytes('0xf86c8085202170e40082520894d3cda913deb6f67967b99d67acdfa1712c293601888abcd74d5558e000801ca074cd478946d0599179316473d9c9d04688b8a348b1ecb494509b25f7ba2a1a33a037fc6f9d9f8458c7965f083fdd8946ec9d360176a864c470a4ce9f63c3ef9331')
def __init__(self, receipt): self.raw_receipt = receipt self.transaction_hash = receipt['transactionHash'] self.gas_used = receipt['gasUsed'] self.transfers = [] self.result = None receipt_logs = receipt['logs'] if (receipt_logs is not None) and (len(receipt_logs) > 0): self.successful = True for receipt_log in receipt_logs: if len(receipt_log['topics']) > 0: # $ seth keccak $(seth --from-ascii "Transfer(address,address,uint256)") # 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef if receipt_log['topics'][0] == HexBytes( '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' ): from pymaker.token import ERC20Token transfer_abi = [ abi for abi in ERC20Token.abi if abi.get('name') == 'Transfer' ][0] codec = ABICodec(default_registry) event_data = get_event_data(codec, transfer_abi, receipt_log) self.transfers.append( Transfer( token_address=Address(event_data['address']), from_address=Address( event_data['args']['from']), to_address=Address(event_data['args']['to']), value=Wad(event_data['args']['value']))) # $ seth keccak $(seth --from-ascii "Mint(address,uint256)") # 0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885 if receipt_log['topics'][0] == HexBytes( '0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885' ): from pymaker.token import DSToken transfer_abi = [ abi for abi in DSToken.abi if abi.get('name') == 'Mint' ][0] codec = ABICodec(default_registry) event_data = get_event_data(codec, transfer_abi, receipt_log) self.transfers.append( Transfer( token_address=Address(event_data['address']), from_address=Address( '0x0000000000000000000000000000000000000000' ), to_address=Address(event_data['args']['guy']), value=Wad(event_data['args']['wad']))) # $ seth keccak $(seth --from-ascii "Burn(address,uint256)") # 0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5 if receipt_log['topics'][0] == HexBytes( '0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5' ): from pymaker.token import DSToken transfer_abi = [ abi for abi in DSToken.abi if abi.get('name') == 'Burn' ][0] codec = ABICodec(default_registry) event_data = get_event_data(codec, transfer_abi, receipt_log) self.transfers.append( Transfer( token_address=Address(event_data['address']), from_address=Address( event_data['args']['guy']), to_address=Address( '0x0000000000000000000000000000000000000000' ), value=Wad(event_data['args']['wad']))) else: self.successful = False
address_conversion_func): return deploy(web3, FixedReflectionContract, address_conversion_func) @pytest.fixture() def call_transaction(): return { 'data': '0x61bc221a', 'to': '0xc305c901078781C232A2a521C2aF7980f8385ee9' } @pytest.fixture(params=[ '0x0406040604060406040604060406040604060406040604060406040604060406', '0406040604060406040604060406040604060406040604060406040604060406', HexBytes( '0406040604060406040604060406040604060406040604060406040604060406'), ]) def bytes32_contract(web3, Bytes32Contract, request, address_conversion_func): return deploy(web3, Bytes32Contract, address_conversion_func, args=[request.param]) @pytest.fixture() def undeployed_math_contract(web3, MathContract, address_conversion_func): empty_address = address_conversion_func( "0x000000000000000000000000000000000000dEaD") _undeployed_math_contract = MathContract(address=empty_address) return _undeployed_math_contract
class Erc20Manager: # keccak('Transfer(address,address,uint256)') # ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef TRANSFER_TOPIC = HexBytes(ERC20_721_TRANSFER_TOPIC) def __init__(self, ethereum_client: 'EthereumClient', slow_provider_timeout: int): self.ethereum_client = ethereum_client self.w3 = ethereum_client.w3 self.slow_w3 = Web3( self.ethereum_client.get_slow_provider( timeout=slow_provider_timeout)) def decode_logs(self, logs: List[Dict[str, Any]]): decoded_logs = [] for log in logs: decoded = self._decode_erc20_or_erc721_log(log.data, log.topics) if decoded: log_copy = dict(log) log_copy['args'] = decoded decoded_logs.append(log_copy) return decoded_logs def _decode_erc20_or_erc721_log( self, data: bytes, topics: List[bytes]) -> Optional[Dict[str, Any]]: decoded = self._decode_erc20_log(data, topics) if not decoded: decoded = self._decode_erc721_log(topics) return decoded def _decode_erc20_log(self, data: bytes, topics: List[bytes]) -> Optional[Dict[str, Any]]: if topics and topics[0] == self.TRANSFER_TOPIC and len(topics) == 3: value = eth_abi.decode_single('uint256', HexBytes(data)) _from, to = [ Web3.toChecksumAddress(address) for address in eth_abi.decode_abi(['address', 'address'], b''.join(topics[1:])) ] return {'from': _from, 'to': to, 'value': value} else: # Not compliant ERC20 Transfer(address indexed from, address indexed to, uint256 value) # Maybe ERC712 Transfer(address indexed from, address indexed to, uint256 indexed tokenId) return None def _decode_erc721_log(self, topics: List[bytes]) -> Optional[Dict[str, Any]]: if topics and topics[0] == self.TRANSFER_TOPIC and len(topics) == 4: _from, to, token_id = eth_abi.decode_abi( ['address', 'address', 'uint256'], b''.join(topics[1:])) _from, to = [ Web3.toChecksumAddress(address) for address in (_from, to) ] return {'from': _from, 'to': to, 'tokenId': token_id} else: # Not compliant ERC20 Transfer(address indexed from, address indexed to, uint256 value) # Maybe ERC712 Transfer(address indexed from, address indexed to, uint256 indexed tokenId) return None def get_balance(self, address: str, erc20_address: str) -> int: """ Get balance of address for `erc20_address` :param address: owner address :param erc20_address: erc20 token address :return: balance """ return get_erc20_contract( self.w3, erc20_address).functions.balanceOf(address).call() def get_balances( self, address: str, erc20_addresses: List[str]) -> List[Dict[str, Union[str, int]]]: # Build ether `eth_getBalance` query balance_query = { "jsonrpc": "2.0", "method": "eth_getBalance", "params": [address, "latest"], "id": 0 } queries = [balance_query] # Build tokens `balanceOf` query for i, erc20_address in enumerate(erc20_addresses): queries.append({ "jsonrpc": "2.0", "method": "eth_call", "params": [ { "to": erc20_address, # Balance of "data": "0x70a08231" + '{:0>64}'.format(address.replace('0x', '').lower()) }, "latest" ], "id": i + 1 }) response = requests.post(self.ethereum_client.ethereum_node_url, json=queries) balances = [] for token_address, data in zip([None] + erc20_addresses, response.json()): balances.append({ 'token_address': token_address, 'balance': 0 if data['result'] == '0x' else int(data['result'], 16) }) return balances def get_name(self, erc20_address: str) -> str: erc20 = get_erc20_contract(self.w3, erc20_address) data = erc20.functions.name().buildTransaction({ 'gas': 0, 'gasPrice': 0 })['data'] result = self.w3.eth.call({'to': erc20_address, 'data': data}) return decode_string_or_bytes32(result) def get_symbol(self, erc20_address: str) -> str: erc20 = get_erc20_contract(self.w3, erc20_address) data = erc20.functions.symbol().buildTransaction({ 'gas': 0, 'gasPrice': 0 })['data'] result = self.w3.eth.call({'to': erc20_address, 'data': data}) return decode_string_or_bytes32(result) def get_decimals(self, erc20_address: str) -> int: erc20 = get_erc20_contract(self.w3, erc20_address) return erc20.functions.decimals().call() def get_info(self, erc20_address: str) -> Erc20Info: """ Get erc20 information (`name`, `symbol` and `decimals`) :param erc20_address: :return: Erc20Info """ # We use the `example erc20` as the `erc20 interface` doesn't have `name`, `symbol` nor `decimals` try: name = self.get_name(erc20_address) symbol = self.get_symbol(erc20_address) decimals = self.get_decimals(erc20_address) return Erc20Info(name, symbol, decimals) except (InsufficientDataBytes, ValueError) as e: raise InvalidERC20Info from e def get_total_transfer_history( self, addresses: List[str], from_block: int = 0, to_block: Optional[int] = None, token_address: Optional[str] = None) -> List[Dict[str, Any]]: """ Get events for erc20 and erc721 transfers from and to an `address`. We decode it manually An example of an erc20 event: {'logIndex': 0, 'transactionIndex': 0, 'transactionHash': HexBytes('0x4d0f25313603e554e3b040667f7f391982babbd195c7ae57a8c84048189f7794'), 'blockHash': HexBytes('0x90fa67d848a0eaf3be625235dae28815389f5292d4465c48d1139f0c207f8d42'), 'blockNumber': 791, 'address': '0xf7d0Bd47BF3214494E7F5B40E392A25cb4788620', 'data': '0x000000000000000000000000000000000000000000000000002001f716742000', 'topics': [HexBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), HexBytes('0x000000000000000000000000f5984365fca2e3bc7d2e020abb2c701df9070eb7'), HexBytes('0x0000000000000000000000001df62f291b2e969fb0849d99d9ce41e2f137006e')], 'type': 'mined' 'args': {'from': '0xf5984365FcA2e3bc7D2E020AbB2c701DF9070eB7', 'to': '0x1dF62f291b2E969fB0849d99D9Ce41e2F137006e', 'value': 9009360000000000 } } An example of an erc721 event {'address': '0x6631FcbB50677DfC6c02CCDcc03a8f68Db427a64', 'blockHash': HexBytes('0x95c71c6c9373e9a8ca2c767dda1cd5083eb6addcce36fc216c9e1f458d6970f9'), 'blockNumber': 5341681, 'data': '0x', 'logIndex': 0, 'removed': False, 'topics': [HexBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000'), HexBytes('0x000000000000000000000000b5239c032ab9fb5abfc3903e770a4b6a9095542c'), HexBytes('0x0000000000000000000000000000000000000000000000000000000000000063')], 'transactionHash': HexBytes('0xce8c8af0503e6f8a421345c10cdf92834c95186916a3f5b1437d2bba63d2db9e'), 'transactionIndex': 0, 'transactionLogIndex': '0x0', 'type': 'mined', 'args': {'from': '0x0000000000000000000000000000000000000000', 'to': '0xb5239C032AB9fB5aBFc3903e770A4B6a9095542C', 'tokenId': 99 } } :param addresses: Search events `from` and `to` these `addresses` :param from_block: Block to start querying from :param to_block: Block to stop querying from :param token_address: Address of the token :return: List of events sorted by blockNumber """ topic_0 = self.TRANSFER_TOPIC.hex() addresses_encoded = [ HexBytes(eth_abi.encode_single('address', address)).hex() for address in addresses ] # Topics for transfer `to` and `from` an address topics_from = [topic_0, addresses_encoded] topics_to = [topic_0, None, addresses_encoded] parameters: Dict[str, Any] = {'fromBlock': from_block} if to_block: parameters['toBlock'] = to_block if token_address: parameters['address'] = token_address all_events: List[Dict] = [] # Do the request to `eth_getLogs` for topics in (topics_to, topics_from): parameters['topics'] = topics all_events.extend(self.slow_w3.eth.getLogs(parameters)) # Decode events. Just pick valid ERC20 Transfer events (ERC721 `Transfer` has the same signature) erc20_events = [] for event in all_events: event['args'] = self._decode_erc20_or_erc721_log( event['data'], event['topics']) if event['args']: erc20_events.append(event) erc20_events.sort(key=lambda x: x['blockNumber']) return erc20_events def get_transfer_history( self, from_block: int, to_block: Optional[int] = None, from_address: Optional[str] = None, to_address: Optional[str] = None, token_address: Optional[str] = None) -> List[Dict[str, Any]]: """ DON'T USE, it will fail in some cases until they fix https://github.com/ethereum/web3.py/issues/1351 Get events for erc20/erc721 transfers. At least one of `from_address`, `to_address` or `token_address` must be defined An example of decoded event: { "args": { "from": "0x1Ce67Ea59377A163D47DFFc9BaAB99423BE6EcF1", "to": "0xaE9E15896fd32E59C7d89ce7a95a9352D6ebD70E", "value": 15000000000000000 }, "event": "Transfer", "logIndex": 42, "transactionIndex": 60, "transactionHash": "0x71d6d83fef3347bad848e83dfa0ab28296e2953de946ee152ea81c6dfb42d2b3", "address": "0xfecA834E7da9D437645b474450688DA9327112a5", "blockHash": "0x054de9a496fc7d10303068cbc7ee3e25181a3b26640497859a5e49f0342e7db2", "blockNumber": 7265022 } :param from_block: Block to start querying from :param to_block: Block to stop querying from :param from_address: Address sending the erc20 transfer :param to_address: Address receiving the erc20 transfer :param token_address: Address of the token :return: List of events (decoded) :throws: ReadTimeout """ assert from_address or to_address or token_address, 'At least one parameter must be provided' erc20 = get_erc20_contract(self.slow_w3) argument_filters = {} if from_address: argument_filters['from'] = from_address if to_address: argument_filters['to'] = to_address return erc20.events.Transfer.createFilter( fromBlock=from_block, toBlock=to_block, address=token_address, argument_filters=argument_filters).get_all_entries() def send_tokens(self, to: str, amount: int, erc20_address: str, private_key: str, nonce: Optional[int] = None, gas_price: Optional[int] = None, gas: Optional[int] = None) -> bytes: """ Send tokens to address :param to: :param amount: :param erc20_address: :param private_key: :param nonce: :param gas_price: :param gas: :return: tx_hash """ erc20 = get_erc20_contract(self.w3, erc20_address) account = Account.from_key(private_key) tx_options = {'from': account.address} if nonce: tx_options['nonce'] = nonce if gas_price: tx_options['gasPrice'] = gas_price if gas: tx_options['gas'] = gas tx = erc20.functions.transfer(to, amount).buildTransaction(tx_options) return self.ethereum_client.send_unsigned_transaction( tx, private_key=private_key)
def test_hash_safe_multisig_tx(self): # -------- Old version of the contract -------------------------- expected_hash = HexBytes( '0xc9d69a2350aede7978fdee58e702647e4bbdc82168577aa4a43b66ad815c6d1a' ) tx_hash = SafeTx(self.ethereum_client, '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a', '0x5AC255889882aaB35A2aa939679E3F3d4Cea221E', 5000000, HexBytes('0x00'), 0, 50000, 100, 10000, '0x' + '0' * 40, '0x' + '0' * 40, safe_nonce=67, safe_version='0.1.0').safe_tx_hash self.assertEqual(expected_hash, tx_hash) expected_hash = HexBytes( '0x8ca8db91d72b379193f6e229eb2dff0d0621b6ef452d90638ee3206e9b7349b3' ) tx_hash = SafeTx(self.ethereum_client, '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a', '0x' + '0' * 40, 80000000, HexBytes('0x562944'), 2, 54522, 773, 22000000, '0x' + '0' * 40, '0x' + '0' * 40, safe_nonce=257000, safe_version='0.1.0').safe_tx_hash self.assertEqual(expected_hash, tx_hash) # -------- New version of the contract -------------------------- expected_hash = HexBytes( '0x7c60341f3e1b4483575f38e84e97d6b332a2dd55b9290f39e6e26eef29a04fe7' ) tx_hash = SafeTx(self.ethereum_client, '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a', '0x5AC255889882aaB35A2aa939679E3F3d4Cea221E', 5000000, HexBytes('0x00'), 0, 50000, 100, 10000, '0x' + '0' * 40, '0x' + '0' * 40, safe_nonce=67).safe_tx_hash self.assertEqual(expected_hash, tx_hash) expected_hash = HexBytes( '0xf585279fd867c94738096f4eab964e9e202014d2f0d5155d751099ad85cbe504' ) tx_hash = SafeTx(self.ethereum_client, '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a', '0x' + '0' * 40, 80000000, HexBytes('0x562944'), 2, 54522, 773, 22000000, '0x' + '0' * 40, '0x' + '0' * 40, safe_nonce=257000).safe_tx_hash self.assertEqual(expected_hash, tx_hash) safe_create2_tx = self.deploy_test_safe() safe_address = safe_create2_tx.safe_address # Expected hash must be the same calculated by `getTransactionHash` of the contract expected_hash = get_safe_contract( self.ethereum_client.w3, safe_address).functions.getTransactionHash( '0x5AC255889882aaB35A2aa939679E3F3d4Cea221E', 5212459, HexBytes(0x00), 1, 123456, 122, 12345, '0x' + '2' * 40, '0x' + '2' * 40, 10789).call() safe_tx_hash = SafeTx(self.ethereum_client, safe_address, '0x5AC255889882aaB35A2aa939679E3F3d4Cea221E', 5212459, HexBytes(0x00), 1, 123456, 122, 12345, '0x' + '2' * 40, '0x' + '2' * 40, safe_nonce=10789).safe_tx_hash self.assertEqual(HexBytes(expected_hash), safe_tx_hash)
def test_decode_multisend(self): # Change Safe contract master copy and set fallback manager multisend transaction safe_contract_address = "0x5B9ea52Aaa931D4EEf74C8aEaf0Fe759434FeD74" value = "0" operation = MultiSendOperation.CALL.value data = HexBytes( "0x8d80ff0a0000000000000000000000000000000000000000000000000000000000000020000000000000000000" "00000000000000000000000000000000000000000000f2005b9ea52aaa931d4eef74c8aeaf0fe759434fed740000" "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000247de7edef00000000000000000000000034cfac646f301356faa8b21e9422" "7e3583fe3f5f005b9ea52aaa931d4eef74c8aeaf0fe759434fed7400000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f0" "8a0323000000000000000000000000d5d82b6addc9027b22dca772aa68d5d74cdbdf440000000000000000000000" "000000") change_master_copy_data = HexBytes( "0x7de7edef00000000000000000000000034cfac646f301356faa8b21e94227e3583fe3f5f" ) change_fallback_manager_data = HexBytes( "0xf08a0323000000000000000000000000d5d82b6addc9027b22dca772aa68d5d74cd" "bdf44") tx_decoder = get_tx_decoder() expected = [ { "operation": operation, "to": safe_contract_address, "value": value, "data": change_master_copy_data.hex(), "data_decoded": { "method": "changeMasterCopy", "parameters": [{ "name": "_masterCopy", "type": "address", "value": "0x34CfAC646f301356fAa8B21e94227e3583Fe3F5F", }], }, }, { "operation": operation, "to": safe_contract_address, "value": value, "data": change_fallback_manager_data.hex(), "data_decoded": { "method": "setFallbackHandler", "parameters": [{ "name": "handler", "type": "address", "value": "0xd5D82B6aDDc9027B22dCA772Aa68D5d74cdBdF44", }], }, }, ] # Get just the multisend object self.assertEqual(tx_decoder.decode_multisend_data(data), expected) # Now decode all the data expected = ( "multiSend", [{ "name": "transactions", "type": "bytes", "value": "0x005b9ea52aaa931d4eef74c8aeaf0fe759434fed74000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000247de7edef00000000000000000000000034cfac646f301356faa8b21e94227e3583fe3f5f005b9ea52aaa931d4eef74c8aeaf0fe759434fed7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f08a0323000000000000000000000000d5d82b6addc9027b22dca772aa68d5d74cdbdf44", "value_decoded": [ { "operation": operation, "to": safe_contract_address, "value": value, "data": change_master_copy_data.hex(), "data_decoded": { "method": "changeMasterCopy", "parameters": [{ "name": "_masterCopy", "type": "address", "value": "0x34CfAC646f301356fAa8B21e94227e3583Fe3F5F", }], }, }, { "operation": operation, "to": safe_contract_address, "value": value, "data": change_fallback_manager_data.hex(), "data_decoded": { "method": "setFallbackHandler", "parameters": [{ "name": "handler", "type": "address", "value": "0xd5D82B6aDDc9027B22dCA772Aa68D5d74cdBdF44", }], }, }, ], }], ) self.assertEqual(tx_decoder.decode_transaction_with_types(data), expected) # Safe tx decoder cannot decode it. It would be problematic for the internal tx indexer safe_tx_decoder = get_safe_tx_decoder() with self.assertRaises(CannotDecode): safe_tx_decoder.decode_transaction_with_types(data)
def _get_block_hash(self, key: str) -> Optional[HexBytes]: address = self.db.get(key)[0] return HexBytes(address.decode("utf-8")) if address else None