Repository: ApeWorX/ape

Issue:
Show first 4 bytes of `data`, not just 3
### Have you ensured that all of these are up to date?

- [x] Ape
- [x] Ape plugins

### What version of Ape are you on?

0.8.26

### What command(s) is the bug in?

_No response_

### Operating System

Linux

### Ape Configuration

_No response_

### Description

Noticed that when submitting a contract transaction, it shows the first 3 bytes of `data` when I am signing, and it should show 4 (because most methods are 4 bytes method_id + args)

### Reproduction code

_No response_

Discussion:
@linear: <p><a href="https://linear.app/apeworx/issue/APE-1905/show-first-4-bytes-of-data-not-just-3">APE-1905 Show first 4 bytes of `data`, not just 3</a></p>@antazoey: I'm guessing this is referring to the keyfile account's sign?

Solution patch:
diff --git a/src/ape/api/transactions.py b/src/ape/api/transactions.py
index e6e12c121..fc7f908cc 100644
--- a/src/ape/api/transactions.py
+++ b/src/ape/api/transactions.py
@@ -190,13 +190,13 @@ class TransactionAPI(BaseInterfaceModel):
             if isinstance(data["data"], str):
                 data["data"] = (
                     "0x"
-                    + bytes(data["data"][:3], encoding="utf8").hex()
+                    + bytes(data["data"][:4], encoding="utf8").hex()
                     + "..."
-                    + bytes(data["data"][-3:], encoding="utf8").hex()
+                    + bytes(data["data"][-4:], encoding="utf8").hex()
                 )
             else:
                 data["data"] = (
-                    to_hex(bytes(data["data"][:3])) + "..." + to_hex(bytes(data["data"][-3:]))
+                    to_hex(bytes(data["data"][:4])) + "..." + to_hex(bytes(data["data"][-4:]))
                 )
         else:
             if isinstance(data["data"], str):


Test patch:
diff --git a/tests/functional/test_accounts.py b/tests/functional/test_accounts.py
index 4373394e5..7f1bd215e 100644
--- a/tests/functional/test_accounts.py
+++ b/tests/functional/test_accounts.py
@@ -71,6 +71,23 @@ def test_sign_message(signer, message):
     assert signer.check_signature(message, signature)
 
 
+def test_sign_transaction(signer, message, ethereum):
+    transaction = ethereum.create_transaction(nonce=0, max_fee=0, max_priority_fee=0)
+    signed_transaction = signer.sign_transaction(transaction)
+    assert signed_transaction.signature is not None
+
+
+def test_sign_transaction_using_keyfile_account(keyfile_account, message, ethereum, runner):
+    transaction = ethereum.create_transaction(
+        nonce=0, max_fee=0, max_priority_fee=0, data="0x21314135413451"
+    )
+
+    with runner.isolation(f"y\n{PASSPHRASE}\ny"):
+        signed_transaction = keyfile_account.sign_transaction(transaction)
+
+    assert signed_transaction.signature is not None
+
+
 def test_sign_string(signer):
     message = "Hello Apes!"
     signature = signer.sign_message(message)
diff --git a/tests/functional/test_transaction.py b/tests/functional/test_transaction.py
index e9606789d..9f786fdef 100644
--- a/tests/functional/test_transaction.py
+++ b/tests/functional/test_transaction.py
@@ -307,6 +307,17 @@ def test_str_when_data_is_bytes(ethereum):
     assert isinstance(actual, str)
 
 
+def test_str_when_data_is_long_shows_first_4_bytes(vyper_contract_instance):
+    """
+    Tests against a condition that would cause transactions to
+    fail with string-encoding errors.
+    """
+    txn = vyper_contract_instance.setNumber.as_transaction(123)
+    actual = str(txn)
+    assert isinstance(actual, str)
+    assert "data: 0x30783366..." in actual
+
+
 def test_receipt_when_none(ethereum):
     txn = ethereum.create_transaction(data=HexBytes("0x123"))
     assert txn.receipt is None