ApeWorX / ape-ledger

Ledger Nano S and X account plugin for the Ape Framework
https://www.apeworx.io/
Apache License 2.0
6 stars 7 forks source link

Serialization error for TypedTransaction type #16

Closed fubuloubu closed 2 years ago

fubuloubu commented 2 years ago

Environment information

$ ape --version
0.1.0b5

$ ape plugins list
Installed Plugins:
  ledger           0.1.0b2

What went wrong?

File site-packages/ape/contracts/base.py:206, in ContractTransactionHandler.__call__(self, *args, **kwargs)
    203 if not selected_abi:
    204     raise ArgumentsLengthError(len(args))
--> 206 return ContractTransaction(  # type: ignore
    207     abi=selected_abi,
    208     address=self.contract.address,
    209     provider=self.provider,
    210     converter=self.converter,
    211 )(*args, **kwargs)

File site-packages/ape/contracts/base.py:177, in ContractTransaction.__call__(self, *args, **kwargs)
    175     sender = kwargs["sender"]
    176     txn = self.encode(*args, **kwargs)
--> 177     return sender.call(txn)
    179 raise TransactionError(message="Must specify a `sender`.")

File site-packages/ape/api/accounts.py:135, in AccountAPI.call(self, txn, send_everything)
    132 elif not isinstance(txn.required_confirmations, int) or txn.required_confirmations < 0:
    133     raise TransactionError(message="'required_confirmations' must be a positive integer.")
--> 135 txn.signature = self.sign_transaction(txn)
    136 if not txn.signature:
    137     raise SignatureError("The transaction was not signed.")

File site-packages/ape_ledger/accounts.py:93, in LedgerAccount.sign_transaction(self, txn)
     92 def sign_transaction(self, txn: TransactionAPI) -> Optional[TransactionSignature]:
---> 93     signed_txn = self._client.sign_transaction(txn.as_dict())
     95     return TransactionSignature(*signed_txn)

File site-packages/ape_ledger/client.py:366, in LedgerEthereumAccountClient.sign_transaction(self, txn)
    353 """
    354 Sign a transaction using your Ledger device. You will need to follow
    355 the prompts on the device to validate the transaction data.
   (...)
    362   RLP transaction chunk                            - arbitrary
    363 """
    365 unsigned_transaction = serializable_unsigned_transaction_from_dict(txn)
--> 366 rlp_encoded_tx = rlp.encode(unsigned_transaction)
    367 payload = self.path_bytes + rlp_encoded_tx
    368 chunks = [payload[i : i + 255] for i in range(0, len(payload), 255)]  # noqa: E203

File site-packages/rlp/codec.py:120, in encode(obj, sedes, infer_serializer, cache)
    118     item = sedes.serialize(obj)
    119 elif infer_serializer:
--> 120     item = infer_sedes(obj).serialize(obj)
    121 else:
    122     item = obj

File site-packages/rlp/codec.py:318, in infer_sedes(obj)
    316     return text
    317 msg = 'Did not find sedes handling type {}'.format(type(obj).__name__)
--> 318 raise TypeError(msg)

TypeError: Did not find sedes handling type TypedTransaction

How can it be fixed?

When we added EIP1559 support, we probably forgot to update these plugins

fubuloubu commented 2 years ago

Different issue when I attempt to use type="0" to switch to "legacy txns":

site-packages/ape/contracts/base.py:206, in ContractTransactionHandler.__call__(self, *args, **kwargs)
    203 if not selected_abi:
    204     raise ArgumentsLengthError(len(args))
--> 206 return ContractTransaction(  # type: ignore
    207     abi=selected_abi,
    208     address=self.contract.address,
    209     provider=self.provider,
    210     converter=self.converter,
    211 )(*args, **kwargs)

site-packages/ape/contracts/base.py:177, in ContractTransaction.__call__(self, *args, **kwargs)
    175     sender = kwargs["sender"]
    176     txn = self.encode(*args, **kwargs)
--> 177     return sender.call(txn)
    179 raise TransactionError(message="Must specify a `sender`.")

site-packages/ape/api/accounts.py:135, in AccountAPI.call(self, txn, send_everything)
    132 elif not isinstance(txn.required_confirmations, int) or txn.required_confirmations < 0:
    133     raise TransactionError(message="'required_confirmations' must be a positive integer.")
--> 135 txn.signature = self.sign_transaction(txn)
    136 if not txn.signature:
    137     raise SignatureError("The transaction was not signed.")

site-packages/ape_ledger/accounts.py:93, in LedgerAccount.sign_transaction(self, txn)
     92 def sign_transaction(self, txn: TransactionAPI) -> Optional[TransactionSignature]:
---> 93     signed_txn = self._client.sign_transaction(txn.as_dict())
     95     return TransactionSignature(*signed_txn)

site-packages/ape_ledger/client.py:365, in LedgerEthereumAccountClient.sign_transaction(self, txn)
    352 def sign_transaction(self, txn: Dict) -> Optional[Tuple[int, bytes, bytes]]:
    353     """
    354     Sign a transaction using your Ledger device. You will need to follow
    355     the prompts on the device to validate the transaction data.
   (...)
    362       RLP transaction chunk                            - arbitrary
    363     """
--> 365     unsigned_transaction = serializable_unsigned_transaction_from_dict(txn)
    366     rlp_encoded_tx = rlp.encode(unsigned_transaction)
    367     payload = self.path_bytes + rlp_encoded_tx

site-packages/eth_account/_utils/legacy_transactions.py:44, in serializable_unsigned_transaction_from_dict(transaction_dict)
     40 if 'type' in transaction_dict:
     41     # We delegate to TypedTransaction, which will carry out validation & formatting.
     42     return TypedTransaction.from_dict(transaction_dict)
---> 44 assert_valid_fields(transaction_dict)
     45 filled_transaction = pipe(
     46     transaction_dict,
     47     dict,
   (...)
     50     apply_formatters_to_dict(LEGACY_TRANSACTION_FORMATTERS),
     51 )
     52 if 'v' in filled_transaction:

site-packages/eth_account/_utils/legacy_transactions.py:102, in assert_valid_fields(transaction_dict)
    100 superfluous_keys = set(transaction_dict.keys()).difference(ALLOWED_TRANSACTION_KEYS)
    101 if superfluous_keys:
--> 102     raise TypeError("Transaction must not include unrecognized fields: %r" % superfluous_keys)
    104 # check for valid types in each field
    105 valid_fields: Dict[str, bool]

TypeError: Transaction must not include unrecognized fields: {'from'}
antazoey commented 2 years ago

Having a tough time with eth-accounts but good news! I put a feature request in the ledgereth repo for eip-1559 support because that guy is really smart, and he figured it out! https://github.com/mikeshultz/ledger-eth-lib/pull/10/files

We can either copy that or even consider using the ledgereth library.

antazoey commented 2 years ago

Fixed in #17 (forgot to do the fixes comment in the PR)