def _transact(self, tx: Dict, allow_revert: bool) -> None: if allow_revert is None: allow_revert = bool(CONFIG.network_type == "development") if not allow_revert: self._check_for_revert(tx) formatters = { "nonce": web3.toHex, "gasPrice": web3.toHex, "gas": web3.toHex, "value": web3.toHex, "chainId": web3.toHex, "data": web3.toHex, "from": to_address, } if "to" in tx: formatters["to"] = to_address tx["chainId"] = web3.chain_id tx = apply_formatters_to_dict(formatters, tx) response = self._provider.make_request("account_signTransaction", [tx]) if "error" in response: raise ValueError(response["error"]["message"]) return web3.eth.send_raw_transaction(response["result"]["raw"])
def sign_transaction(self, transaction_dict: dict) -> HexBytes: formatters = { 'nonce': Web3.toHex, 'gasPrice': Web3.toHex, 'gas': Web3.toHex, 'value': Web3.toHex, 'chainId': Web3.toHex, 'from': to_checksum_address } # Workaround for contract creation TXs if transaction_dict['to'] == b'': transaction_dict['to'] = None elif transaction_dict['to']: formatters['to'] = to_checksum_address formatted_transaction = apply_formatters_to_dict( formatters, transaction_dict) signed = self.__ipc_request("account_signTransaction", formatted_transaction) return HexBytes(signed.raw)
def sign_transaction(self, transaction_dict: dict, rlp_encoded: bool = True ) -> Union[HexBytes, Transaction]: """ Sign a transaction with a trezor hardware wallet. This non-mutative method handles transaction validation, field formatting, signing, and outgoing serialization. Accepts a standard transaction dictionary as input, and produces an RLP encoded raw signed transaction by default. Internally the standard transaction dictionary is reformatted for trezor API consumption via calls `trezorlib.client.ethereum.sign_tx`. WARNING: This function returns a raw signed transaction which can be broadcast by anyone with a connection to the ethereum network. ***Treat pre-signed raw transactions produced by this function like money.*** """ # Eager enforcement of EIP-155 # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md # # Leave the chain ID in tact for the trezor signing request so that an EIP-155 transaction signature will be applied # https://github.com/trezor/trezor-core/pull/311 if 'chainId' not in transaction_dict: raise self.SignerError('Invalid EIP-155 transaction - "chain_id" field is missing in trezor signing request.') # Consume the sender inside the transaction request's 'from field. try: sender_address = transaction_dict['from'] except KeyError: raise self.SignerError("'from' field is missing from trezor signing request.") transaction_dict = dissoc(transaction_dict, 'from') # Format contract data field for both trezor and eth_account's Transaction formatters = {'data': lambda data: Web3.toBytes(HexBytes(data))} transaction_dict = dict(apply_formatters_to_dict(formatters, transaction_dict)) # Format transaction fields for Trezor, Lookup HD path trezor_transaction = self._format_transaction(transaction_dict=transaction_dict) # Note that the derivation path on the trezor must correlate with the chain id # in the transaction. Since Trezor firmware version 2.3.1 mismatched chain_id # and derivation path will fail to sign with 'forbidden key path'. # https://github.com/trezor/trezor-firmware/issues/1050#issuecomment-640718622 hd_path = self.__get_address_path(checksum_address=sender_address) # from cache # Trezor signing request _v, _r, _s = self.__sign_transaction(n=hd_path, trezor_transaction=trezor_transaction) # Create RLP serializable Transaction instance with eth_account # chainId is not longer needed since it can later be derived from v transaction_dict = dissoc(transaction_dict, 'chainId') # 'to' may be blank if this transaction is contract creation formatters = {'to': to_canonical_address} transaction_dict = dict(apply_formatters_to_dict(formatters, transaction_dict)) signed_transaction = Transaction(v=to_int(_v), # type: int r=to_int(_r), # bytes -> int s=to_int(_s), # bytes -> int **transaction_dict) # Optionally encode as RLP for broadcasting if rlp_encoded: signed_transaction = HexBytes(rlp.encode(signed_transaction)) return signed_transaction