Repository: ethereumjs/ethereumjs-monorepo
Pull request #4084: Fix outdated hardware wallet signing example
Author: login: vedant-asati
Labels: name: package: tx
Created: 2025-05-17T01:51:54Z | Merged: 2025-05-22T16:07:40Z

Updates the "Signing with a hardware or external wallet" example in the tx package documentation to use the current API.
Fixes #3989


Linked issue #3989: Outdated "Signing with a hardware or external wallet" Tx Example
The above [example](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/tx#signing-with-a-hardware-or-external-wallet) code is outdated (code is not included in `examples` due to dependencies), an update goes beyond the scope of the doc update PRs.

So this should be tested to work with a real dependency setup and then updated.
Linked issue comments: user: login: vedant-asati
created_at: 2025-05-18T11:59:27Z
body: @holgerd77 I've opened a pr with the changes. Please look into it.
author_association: CONTRIBUTOR

Diff:
diff --git a/packages/tx/README.md b/packages/tx/README.md
index e9d3728030f..eab633cf912 100644
--- a/packages/tx/README.md
+++ b/packages/tx/README.md
@@ -441,42 +441,64 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
 
-Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
-
+Here is an example of signing txs with `@ledgerhq/hw-app-eth` with `v6.45.4` and `@ledgerhq/hw-transport-node-hid` with `v6.29.5`:
 ```ts
-import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+// examples/ledgerSigner.mts
+
+import { Chain, Common, Sepolia } from '@ethereumjs/common'
+import { createLegacyTx, createFeeMarket1559Tx, type LegacyTx, type FeeMarket1559Tx, type LegacyTxData, type FeeMarketEIP1559TxData } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
 
-const eth = new Eth(transport)
-const common = new Common({ chain: Chain.Sepolia })
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
 
-let txData: any = { value: 1 }
-let tx: LegacyTransaction | FeeMarketEIP1559Transaction
-let unsignedTx: Uint8Array[] | Uint8Array
-let signedTx: typeof tx
+// Signing with the first key of the derivation path
 const bip32Path = "44'/60'/0'/0/0"
 
+const legacyTxData: LegacyTxData = {
+    nonce: '0x0',
+    gasPrice: '0x09184e72a000',
+    gasLimit: '0x2710',
+    to: '0x0000000000000000000000000000000000000000',
+    value: '0x00',
+    data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+    data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+    gasLimit: '0x02625a00',
+    maxPriorityFeePerGas: '0x01',
+    maxFeePerGas: '0xff',
+    nonce: '0x00',
+    to: '0xcccccccccccccccccccccccccccccccccccccccc',
+    value: '0x0186a0',
+    accessList: [],
+    type: '0x02',
+}
+
 const run = async () => {
-  // Signing a legacy tx
-  tx = LegacyTransaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  // ledger signTransaction API expects it to be serialized
-  let { v, r, s } = await eth.signTransaction(bip32Path, RLP.encode(tx))
-  tx.addSignature(v, r, s, true)
-  let from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
-
-  // Signing a 1559 tx
-  txData = { value: 1 }
-  tx = FeeMarketEIP1559Transaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  ;({ v, r, s } = await eth.signTransaction(bip32Path, unsignedTx)) // this syntax is: object destructuring - assignment without declaration
-  tx.addSignature(v, r, s)
-  from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
+    // Signing a legacy tx
+    const tx1 = createLegacyTx(legacyTxData, { common })
+    const unsignedTx1 = tx1.getMessageToSign()
+    // Ledger signTransaction API expects it to be serialized
+    // Ledger returns unprefixed hex strings without 0x for v, r, s values
+    const { v, r, s } = await eth.signTransaction(bip32Path, bytesToHex(RLP.encode(unsignedTx1)).slice(2), null)
+    const signedTx1 = tx1.addSignature(BigInt(`0x${v}`), BigInt(`0x${r}`), BigInt(`0x${s}`))
+    const from = signedTx1.getSenderAddress().toString()
+    console.log(`signedTx: ${bytesToHex(tx1.serialize())}\nfrom: ${from}`)
+
+    // Signing a 1559 tx
+    const tx2 = createFeeMarket1559Tx(eip1559TxData, { common })
+    // Ledger returns unprefixed hex strings without 0x for v, r, s values
+    const unsignedTx2 = tx2.getMessageToSign()
+    const { v2, r2, s2 } = await eth.signTransaction(bip32Path, bytesToHex(unsignedTx2).slice(2), null)
+    const signedTx2 = tx2.addSignature(BigInt(`0x${v2}`), BigInt(`0x${r2}`), BigInt(`0x${s2}`))
+    const from2 = signedTx2.getSenderAddress().toString()
+    console.log(`signedTx: ${bytesToHex(tx2.serialize())}\nfrom: ${from2}`)
 }
 
 run()
diff --git a/packages/tx/examples/ledgerSigner.mts b/packages/tx/examples/ledgerSigner.mts
new file mode 100644
index 00000000000..3e4447034f3
--- /dev/null
+++ b/packages/tx/examples/ledgerSigner.mts
@@ -0,0 +1,70 @@
+import { Common, Sepolia } from '@ethereumjs/common'
+import { RLP } from '@ethereumjs/rlp'
+import {
+  type FeeMarketEIP1559TxData,
+  type LegacyTxData,
+  createFeeMarket1559Tx,
+  createLegacyTx,
+} from '@ethereumjs/tx'
+import { bytesToHex } from '@ethereumjs/util'
+import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
+
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
+
+// Signing with the first key of the derivation path
+const bip32Path = "44'/60'/0'/0/0"
+
+const legacyTxData: LegacyTxData = {
+  nonce: '0x0',
+  gasPrice: '0x09184e72a000',
+  gasLimit: '0x2710',
+  to: '0x0000000000000000000000000000000000000000',
+  value: '0x00',
+  data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+  data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+  gasLimit: '0x02625a00',
+  maxPriorityFeePerGas: '0x01',
+  maxFeePerGas: '0xff',
+  nonce: '0x00',
+  to: '0xcccccccccccccccccccccccccccccccccccccccc',
+  value: '0x0186a0',
+  accessList: [],
+  type: '0x02',
+}
+
+const run = async () => {
+  // Signing a legacy tx
+  const tx1 = createLegacyTx(legacyTxData, { common })
+  const unsignedTx1 = tx1.getMessageToSign()
+  // Ledger signTransaction API expects it to be serialized
+  // Ledger returns unprefixed hex strings without 0x for v, r, s values
+  const { v, r, s } = await eth.signTransaction(
+    bip32Path,
+    bytesToHex(RLP.encode(unsignedTx1)).slice(2),
+    null,
+  )
+  const signedTx1 = tx1.addSignature(BigInt(`0x${v}`), BigInt(`0x${r}`), BigInt(`0x${s}`))
+  const from = signedTx1.getSenderAddress().toString()
+  console.log(`signedTx: ${bytesToHex(tx1.serialize())}\nfrom: ${from}`)
+
+  // Signing a 1559 tx
+  const tx2 = createFeeMarket1559Tx(eip1559TxData, { common })
+  // Ledger returns unprefixed hex strings without 0x for v, r, s values
+  const unsignedTx2 = tx2.getMessageToSign()
+  const { v2, r2, s2 } = await eth.signTransaction(
+    bip32Path,
+    bytesToHex(unsignedTx2).slice(2),
+    null,
+  )
+  const signedTx2 = tx2.addSignature(BigInt(`0x${v2}`), BigInt(`0x${r2}`), BigInt(`0x${s2}`))
+  const from2 = signedTx2.getSenderAddress().toString()
+  console.log(`signedTx: ${bytesToHex(tx2.serialize())}\nfrom: ${from2}`)
+}
+
+run()


Reviews:
user: login: acolytec3
body: Thanks for the PR.  Did you check this with the ledger signer app to verify this works?  
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T00:49:36Z
user: login: holgerd77
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T10:55:19Z
user: login: vedant-asati
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:36:07Z
user: login: acolytec3
body: > > Thanks for the PR. Did you check this with the ledger signer app to verify this works?
> 
> actually i dont have a hardware wallet but im searching for tools to simulate it.
>  

Don't worry about this one.  I'll do a check today and we can move on once the other feedback is addressed.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T14:11:52Z
user: login: jochem-brouwer
body: Nice work, but some comments or questions :smile: :+1: 
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-22T02:22:17Z
user: login: vedant-asati
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:27:44Z
user: login: vedant-asati
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T09:54:30Z
user: login: acolytec3
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T10:09:46Z
user: login: vedant-asati
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T13:37:57Z
user: login: acolytec3
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T14:09:09Z
user: login: acolytec3
body: LGTM
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T14:47:23Z
user: login: acolytec3
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T15:34:04Z

Review comments:
diff_hunk: @@ -445,34 +445,57 @@ Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
 
 ```ts
 import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+import { createLegacyTx, createFeeMarket1559Tx } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
 
 const eth = new Eth(transport)
 const common = new Common({ chain: Chain.Sepolia })
 
-let txData: any = { value: 1 }
 let tx: LegacyTransaction | FeeMarketEIP1559Transaction
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
 
+const legacyTxData: LegacyTxData = {
+  nonce: '0x0',
+  gasPrice: '0x09184e72a000',
+  gasLimit: '0x2710',
+  to: '0x0000000000000000000000000000000000000000',
+  value: '0x00',
+  data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+  data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+  gasLimit: '0x02625a00',
+  maxPriorityFeePerGas: '0x01',
+  maxFeePerGas: '0xff',
+  nonce: '0x00',
+  to: '0xcccccccccccccccccccccccccccccccccccccccc',
+  value: '0x0186a0',
+  v: '0x01',
+  r: '0xafb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9',
+  s: '0x479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64',
path: packages/tx/README.md
user: login: holgerd77
body: There should not be any signature passed here, this is the dedicated task of the ledger wallet to actually create.
created_at: 2025-05-21T10:55:19Z
author_association: MEMBER
diff_hunk: @@ -445,34 +445,57 @@ Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
 
 ```ts
 import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+import { createLegacyTx, createFeeMarket1559Tx } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
 
 const eth = new Eth(transport)
 const common = new Common({ chain: Chain.Sepolia })
 
-let txData: any = { value: 1 }
 let tx: LegacyTransaction | FeeMarketEIP1559Transaction
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
 
+const legacyTxData: LegacyTxData = {
+  nonce: '0x0',
+  gasPrice: '0x09184e72a000',
+  gasLimit: '0x2710',
+  to: '0x0000000000000000000000000000000000000000',
+  value: '0x00',
+  data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+  data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+  gasLimit: '0x02625a00',
+  maxPriorityFeePerGas: '0x01',
+  maxFeePerGas: '0xff',
+  nonce: '0x00',
+  to: '0xcccccccccccccccccccccccccccccccccccccccc',
+  value: '0x0186a0',
+  v: '0x01',
+  r: '0xafb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9',
+  s: '0x479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64',
path: packages/tx/README.md
user: login: vedant-asati
body: sure. i'll remove it
created_at: 2025-05-21T13:36:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -441,42 +441,67 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
 
-Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
-
+Here is an example of signing txs with `@ledgerhq/hw-app-eth` with `v6.45.4` and `@ledgerhq/hw-transport-node-hid` with `v6.29.5`:
 ```ts
-import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+// examples/ledgerSigner.mts
+
+import { Chain, Common, Sepolia } from '@ethereumjs/common'
+import { createLegacyTx, createFeeMarket1559Tx, type LegacyTx, type FeeMarket1559Tx, type LegacyTxData, type FeeMarketEIP1559TxData } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
 
-const eth = new Eth(transport)
-const common = new Common({ chain: Chain.Sepolia })
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
 
-let txData: any = { value: 1 }
-let tx: LegacyTransaction | FeeMarketEIP1559Transaction
+let tx: LegacyTx | FeeMarket1559Tx
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
 
+const legacyTxData: LegacyTxData = {
+    nonce: '0x0',
+    gasPrice: '0x09184e72a000',
+    gasLimit: '0x2710',
+    to: '0x0000000000000000000000000000000000000000',
+    value: '0x00',
+    data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+    data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+    gasLimit: '0x02625a00',
+    maxPriorityFeePerGas: '0x01',
+    maxFeePerGas: '0xff',
+    nonce: '0x00',
+    to: '0xcccccccccccccccccccccccccccccccccccccccc',
+    value: '0x0186a0',
+    v: '0x01',
+    r: '0xafb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9',
+    s: '0x479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64',
path: packages/tx/README.md
user: login: jochem-brouwer
body: Why are there signed values inside this tx data? I think this is confusing, it should be removed (the `v` `r` `s` values)
created_at: 2025-05-22T02:14:07Z
author_association: MEMBER
diff_hunk: @@ -441,42 +441,67 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
path: packages/tx/README.md
user: login: jochem-brouwer
body: This is not edited by this PR but the note of "Buffer" means that these edits are in very historic parts of the README and that those are also incorrect (we now use `Uint8Array` instead of `Buffer`)
created_at: 2025-05-22T02:16:24Z
author_association: MEMBER
diff_hunk: @@ -441,42 +441,67 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
 
-Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
-
+Here is an example of signing txs with `@ledgerhq/hw-app-eth` with `v6.45.4` and `@ledgerhq/hw-transport-node-hid` with `v6.29.5`:
 ```ts
-import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+// examples/ledgerSigner.mts
+
+import { Chain, Common, Sepolia } from '@ethereumjs/common'
+import { createLegacyTx, createFeeMarket1559Tx, type LegacyTx, type FeeMarket1559Tx, type LegacyTxData, type FeeMarketEIP1559TxData } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
 
-const eth = new Eth(transport)
-const common = new Common({ chain: Chain.Sepolia })
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
 
-let txData: any = { value: 1 }
-let tx: LegacyTransaction | FeeMarketEIP1559Transaction
+let tx: LegacyTx | FeeMarket1559Tx
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
path: packages/tx/README.md
user: login: jochem-brouwer
body: Ok, we only sign with the "first key" it seems (this could use a note to explain what this does)
created_at: 2025-05-22T02:16:53Z
author_association: MEMBER
diff_hunk: @@ -441,42 +441,67 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
 
-Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
-
+Here is an example of signing txs with `@ledgerhq/hw-app-eth` with `v6.45.4` and `@ledgerhq/hw-transport-node-hid` with `v6.29.5`:
 ```ts
-import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+// examples/ledgerSigner.mts
+
+import { Chain, Common, Sepolia } from '@ethereumjs/common'
+import { createLegacyTx, createFeeMarket1559Tx, type LegacyTx, type FeeMarket1559Tx, type LegacyTxData, type FeeMarketEIP1559TxData } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
 
-const eth = new Eth(transport)
-const common = new Common({ chain: Chain.Sepolia })
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
 
-let txData: any = { value: 1 }
-let tx: LegacyTransaction | FeeMarketEIP1559Transaction
+let tx: LegacyTx | FeeMarket1559Tx
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
 
+const legacyTxData: LegacyTxData = {
+    nonce: '0x0',
+    gasPrice: '0x09184e72a000',
+    gasLimit: '0x2710',
+    to: '0x0000000000000000000000000000000000000000',
+    value: '0x00',
+    data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+    data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+    gasLimit: '0x02625a00',
+    maxPriorityFeePerGas: '0x01',
+    maxFeePerGas: '0xff',
+    nonce: '0x00',
+    to: '0xcccccccccccccccccccccccccccccccccccccccc',
+    value: '0x0186a0',
+    v: '0x01',
+    r: '0xafb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9',
+    s: '0x479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64',
+    accessList: [],
+    type: '0x02',
+}
+
 const run = async () => {
-  // Signing a legacy tx
-  tx = LegacyTransaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  // ledger signTransaction API expects it to be serialized
-  let { v, r, s } = await eth.signTransaction(bip32Path, RLP.encode(tx))
-  tx.addSignature(v, r, s, true)
-  let from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
-
-  // Signing a 1559 tx
-  txData = { value: 1 }
-  tx = FeeMarketEIP1559Transaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  ;({ v, r, s } = await eth.signTransaction(bip32Path, unsignedTx)) // this syntax is: object destructuring - assignment without declaration
-  tx.addSignature(v, r, s)
-  from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
+    // Signing a legacy tx
+    tx = createLegacyTx(legacyTxData, { common })
+    unsignedTx = tx.getMessageToSign()
+    // ledger signTransaction API expects it to be serialized
+    let { v, r, s } = await eth.signTransaction(bip32Path, bytesToHex(RLP.encode(unsignedTx)).slice(2), null)
path: packages/tx/README.md
user: login: jochem-brouwer
body: What does this return? Appears to be unprefixed hex strings?
created_at: 2025-05-22T02:19:21Z
author_association: MEMBER
diff_hunk: @@ -441,42 +441,67 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
 
-Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
-
+Here is an example of signing txs with `@ledgerhq/hw-app-eth` with `v6.45.4` and `@ledgerhq/hw-transport-node-hid` with `v6.29.5`:
 ```ts
-import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+// examples/ledgerSigner.mts
+
+import { Chain, Common, Sepolia } from '@ethereumjs/common'
+import { createLegacyTx, createFeeMarket1559Tx, type LegacyTx, type FeeMarket1559Tx, type LegacyTxData, type FeeMarketEIP1559TxData } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
 
-const eth = new Eth(transport)
-const common = new Common({ chain: Chain.Sepolia })
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
 
-let txData: any = { value: 1 }
-let tx: LegacyTransaction | FeeMarketEIP1559Transaction
+let tx: LegacyTx | FeeMarket1559Tx
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
 
+const legacyTxData: LegacyTxData = {
+    nonce: '0x0',
+    gasPrice: '0x09184e72a000',
+    gasLimit: '0x2710',
+    to: '0x0000000000000000000000000000000000000000',
+    value: '0x00',
+    data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+    data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+    gasLimit: '0x02625a00',
+    maxPriorityFeePerGas: '0x01',
+    maxFeePerGas: '0xff',
+    nonce: '0x00',
+    to: '0xcccccccccccccccccccccccccccccccccccccccc',
+    value: '0x0186a0',
+    v: '0x01',
+    r: '0xafb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9',
+    s: '0x479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64',
+    accessList: [],
+    type: '0x02',
+}
+
 const run = async () => {
-  // Signing a legacy tx
-  tx = LegacyTransaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  // ledger signTransaction API expects it to be serialized
-  let { v, r, s } = await eth.signTransaction(bip32Path, RLP.encode(tx))
-  tx.addSignature(v, r, s, true)
-  let from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
-
-  // Signing a 1559 tx
-  txData = { value: 1 }
-  tx = FeeMarketEIP1559Transaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  ;({ v, r, s } = await eth.signTransaction(bip32Path, unsignedTx)) // this syntax is: object destructuring - assignment without declaration
-  tx.addSignature(v, r, s)
-  from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
+    // Signing a legacy tx
+    tx = createLegacyTx(legacyTxData, { common })
+    unsignedTx = tx.getMessageToSign()
+    // ledger signTransaction API expects it to be serialized
+    let { v, r, s } = await eth.signTransaction(bip32Path, bytesToHex(RLP.encode(unsignedTx)).slice(2), null)
+    let signedTx: LegacyTx | FeeMarket1559Tx = tx.addSignature(BigInt(`0x${v}`), BigInt(`0x${r}`), BigInt(`0x${s}`))
path: packages/tx/README.md
user: login: jochem-brouwer
body: Does this have to be cast? It's clear this is a `LegacyTx`, right? Should be auto-casted to `LegacyTx`
created_at: 2025-05-22T02:20:24Z
author_association: MEMBER
diff_hunk: @@ -441,42 +441,67 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
 
-Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
-
+Here is an example of signing txs with `@ledgerhq/hw-app-eth` with `v6.45.4` and `@ledgerhq/hw-transport-node-hid` with `v6.29.5`:
 ```ts
-import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+// examples/ledgerSigner.mts
+
+import { Chain, Common, Sepolia } from '@ethereumjs/common'
+import { createLegacyTx, createFeeMarket1559Tx, type LegacyTx, type FeeMarket1559Tx, type LegacyTxData, type FeeMarketEIP1559TxData } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
 
-const eth = new Eth(transport)
-const common = new Common({ chain: Chain.Sepolia })
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
 
-let txData: any = { value: 1 }
-let tx: LegacyTransaction | FeeMarketEIP1559Transaction
+let tx: LegacyTx | FeeMarket1559Tx
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
 
+const legacyTxData: LegacyTxData = {
+    nonce: '0x0',
+    gasPrice: '0x09184e72a000',
+    gasLimit: '0x2710',
+    to: '0x0000000000000000000000000000000000000000',
+    value: '0x00',
+    data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+    data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+    gasLimit: '0x02625a00',
+    maxPriorityFeePerGas: '0x01',
+    maxFeePerGas: '0xff',
+    nonce: '0x00',
+    to: '0xcccccccccccccccccccccccccccccccccccccccc',
+    value: '0x0186a0',
+    v: '0x01',
+    r: '0xafb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9',
+    s: '0x479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64',
+    accessList: [],
+    type: '0x02',
+}
+
 const run = async () => {
-  // Signing a legacy tx
-  tx = LegacyTransaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  // ledger signTransaction API expects it to be serialized
-  let { v, r, s } = await eth.signTransaction(bip32Path, RLP.encode(tx))
-  tx.addSignature(v, r, s, true)
-  let from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
-
-  // Signing a 1559 tx
-  txData = { value: 1 }
-  tx = FeeMarketEIP1559Transaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  ;({ v, r, s } = await eth.signTransaction(bip32Path, unsignedTx)) // this syntax is: object destructuring - assignment without declaration
-  tx.addSignature(v, r, s)
-  from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
+    // Signing a legacy tx
+    tx = createLegacyTx(legacyTxData, { common })
+    unsignedTx = tx.getMessageToSign()
+    // ledger signTransaction API expects it to be serialized
+    let { v, r, s } = await eth.signTransaction(bip32Path, bytesToHex(RLP.encode(unsignedTx)).slice(2), null)
+    let signedTx: LegacyTx | FeeMarket1559Tx = tx.addSignature(BigInt(`0x${v}`), BigInt(`0x${r}`), BigInt(`0x${s}`))
+    let from = signedTx.getSenderAddress().toString()
+    console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
+
+    // Signing a 1559 tx
+    tx = createFeeMarket1559Tx(eip1559TxData, { common })
+    unsignedTx = tx.getMessageToSign()
+        ; ({ v, r, s } = await eth.signTransaction(bip32Path, bytesToHex(unsignedTx).slice(2), null))
path: packages/tx/README.md
user: login: jochem-brouwer
body: Why this weird `;` syntax here?
created_at: 2025-05-22T02:21:24Z
author_association: MEMBER
diff_hunk: @@ -441,42 +441,67 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
 
-Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
-
+Here is an example of signing txs with `@ledgerhq/hw-app-eth` with `v6.45.4` and `@ledgerhq/hw-transport-node-hid` with `v6.29.5`:
 ```ts
-import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+// examples/ledgerSigner.mts
+
+import { Chain, Common, Sepolia } from '@ethereumjs/common'
+import { createLegacyTx, createFeeMarket1559Tx, type LegacyTx, type FeeMarket1559Tx, type LegacyTxData, type FeeMarketEIP1559TxData } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
 
-const eth = new Eth(transport)
-const common = new Common({ chain: Chain.Sepolia })
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
 
-let txData: any = { value: 1 }
-let tx: LegacyTransaction | FeeMarketEIP1559Transaction
+let tx: LegacyTx | FeeMarket1559Tx
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
 
+const legacyTxData: LegacyTxData = {
+    nonce: '0x0',
+    gasPrice: '0x09184e72a000',
+    gasLimit: '0x2710',
+    to: '0x0000000000000000000000000000000000000000',
+    value: '0x00',
+    data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+    data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+    gasLimit: '0x02625a00',
+    maxPriorityFeePerGas: '0x01',
+    maxFeePerGas: '0xff',
+    nonce: '0x00',
+    to: '0xcccccccccccccccccccccccccccccccccccccccc',
+    value: '0x0186a0',
+    v: '0x01',
+    r: '0xafb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9',
+    s: '0x479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64',
path: packages/tx/README.md
user: login: vedant-asati
body: i've removed it
created_at: 2025-05-22T05:27:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -441,42 +441,67 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
 
-Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
-
+Here is an example of signing txs with `@ledgerhq/hw-app-eth` with `v6.45.4` and `@ledgerhq/hw-transport-node-hid` with `v6.29.5`:
 ```ts
-import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+// examples/ledgerSigner.mts
+
+import { Chain, Common, Sepolia } from '@ethereumjs/common'
+import { createLegacyTx, createFeeMarket1559Tx, type LegacyTx, type FeeMarket1559Tx, type LegacyTxData, type FeeMarketEIP1559TxData } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
 
-const eth = new Eth(transport)
-const common = new Common({ chain: Chain.Sepolia })
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
 
-let txData: any = { value: 1 }
-let tx: LegacyTransaction | FeeMarketEIP1559Transaction
+let tx: LegacyTx | FeeMarket1559Tx
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
path: packages/tx/README.md
user: login: vedant-asati
body: sure, will add a comment
created_at: 2025-05-22T09:54:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -441,42 +441,67 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
 
-Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
-
+Here is an example of signing txs with `@ledgerhq/hw-app-eth` with `v6.45.4` and `@ledgerhq/hw-transport-node-hid` with `v6.29.5`:
 ```ts
-import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+// examples/ledgerSigner.mts
+
+import { Chain, Common, Sepolia } from '@ethereumjs/common'
+import { createLegacyTx, createFeeMarket1559Tx, type LegacyTx, type FeeMarket1559Tx, type LegacyTxData, type FeeMarketEIP1559TxData } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
 
-const eth = new Eth(transport)
-const common = new Common({ chain: Chain.Sepolia })
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
 
-let txData: any = { value: 1 }
-let tx: LegacyTransaction | FeeMarketEIP1559Transaction
+let tx: LegacyTx | FeeMarket1559Tx
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
 
+const legacyTxData: LegacyTxData = {
+    nonce: '0x0',
+    gasPrice: '0x09184e72a000',
+    gasLimit: '0x2710',
+    to: '0x0000000000000000000000000000000000000000',
+    value: '0x00',
+    data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+    data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+    gasLimit: '0x02625a00',
+    maxPriorityFeePerGas: '0x01',
+    maxFeePerGas: '0xff',
+    nonce: '0x00',
+    to: '0xcccccccccccccccccccccccccccccccccccccccc',
+    value: '0x0186a0',
+    v: '0x01',
+    r: '0xafb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9',
+    s: '0x479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64',
+    accessList: [],
+    type: '0x02',
+}
+
 const run = async () => {
-  // Signing a legacy tx
-  tx = LegacyTransaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  // ledger signTransaction API expects it to be serialized
-  let { v, r, s } = await eth.signTransaction(bip32Path, RLP.encode(tx))
-  tx.addSignature(v, r, s, true)
-  let from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
-
-  // Signing a 1559 tx
-  txData = { value: 1 }
-  tx = FeeMarketEIP1559Transaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  ;({ v, r, s } = await eth.signTransaction(bip32Path, unsignedTx)) // this syntax is: object destructuring - assignment without declaration
-  tx.addSignature(v, r, s)
-  from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
+    // Signing a legacy tx
+    tx = createLegacyTx(legacyTxData, { common })
+    unsignedTx = tx.getMessageToSign()
+    // ledger signTransaction API expects it to be serialized
+    let { v, r, s } = await eth.signTransaction(bip32Path, bytesToHex(RLP.encode(unsignedTx)).slice(2), null)
path: packages/tx/README.md
user: login: acolytec3
body: Yes, the ledger sdks return in unprefixed hex strings.
created_at: 2025-05-22T10:09:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -441,42 +441,67 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
 
-Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
-
+Here is an example of signing txs with `@ledgerhq/hw-app-eth` with `v6.45.4` and `@ledgerhq/hw-transport-node-hid` with `v6.29.5`:
 ```ts
-import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+// examples/ledgerSigner.mts
+
+import { Chain, Common, Sepolia } from '@ethereumjs/common'
+import { createLegacyTx, createFeeMarket1559Tx, type LegacyTx, type FeeMarket1559Tx, type LegacyTxData, type FeeMarketEIP1559TxData } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
 
-const eth = new Eth(transport)
-const common = new Common({ chain: Chain.Sepolia })
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
 
-let txData: any = { value: 1 }
-let tx: LegacyTransaction | FeeMarketEIP1559Transaction
+let tx: LegacyTx | FeeMarket1559Tx
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
 
+const legacyTxData: LegacyTxData = {
+    nonce: '0x0',
+    gasPrice: '0x09184e72a000',
+    gasLimit: '0x2710',
+    to: '0x0000000000000000000000000000000000000000',
+    value: '0x00',
+    data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+    data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+    gasLimit: '0x02625a00',
+    maxPriorityFeePerGas: '0x01',
+    maxFeePerGas: '0xff',
+    nonce: '0x00',
+    to: '0xcccccccccccccccccccccccccccccccccccccccc',
+    value: '0x0186a0',
+    v: '0x01',
+    r: '0xafb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9',
+    s: '0x479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64',
+    accessList: [],
+    type: '0x02',
+}
+
 const run = async () => {
-  // Signing a legacy tx
-  tx = LegacyTransaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  // ledger signTransaction API expects it to be serialized
-  let { v, r, s } = await eth.signTransaction(bip32Path, RLP.encode(tx))
-  tx.addSignature(v, r, s, true)
-  let from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
-
-  // Signing a 1559 tx
-  txData = { value: 1 }
-  tx = FeeMarketEIP1559Transaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  ;({ v, r, s } = await eth.signTransaction(bip32Path, unsignedTx)) // this syntax is: object destructuring - assignment without declaration
-  tx.addSignature(v, r, s)
-  from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
+    // Signing a legacy tx
+    tx = createLegacyTx(legacyTxData, { common })
+    unsignedTx = tx.getMessageToSign()
+    // ledger signTransaction API expects it to be serialized
+    let { v, r, s } = await eth.signTransaction(bip32Path, bytesToHex(RLP.encode(unsignedTx)).slice(2), null)
+    let signedTx: LegacyTx | FeeMarket1559Tx = tx.addSignature(BigInt(`0x${v}`), BigInt(`0x${r}`), BigInt(`0x${s}`))
+    let from = signedTx.getSenderAddress().toString()
+    console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
+
+    // Signing a 1559 tx
+    tx = createFeeMarket1559Tx(eip1559TxData, { common })
+    unsignedTx = tx.getMessageToSign()
+        ; ({ v, r, s } = await eth.signTransaction(bip32Path, bytesToHex(unsignedTx).slice(2), null))
path: packages/tx/README.md
user: login: vedant-asati
body: it was already there. we can introduce a temp var and then update v, r, s to make it more clear

created_at: 2025-05-22T13:37:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -441,42 +441,67 @@ To sign a tx with a hardware or external wallet use `tx.getMessageToSign()` to r
 
 A legacy transaction will return a Buffer list of the values, and a Typed Transaction ([EIP-2718](https://eips.ethereum.org/EIPS/eip-2718)) will return the serialized output.
 
-Here is an example of signing txs with `@ledgerhq/hw-app-eth` as of `v6.5.0`:
-
+Here is an example of signing txs with `@ledgerhq/hw-app-eth` with `v6.45.4` and `@ledgerhq/hw-transport-node-hid` with `v6.29.5`:
 ```ts
-import { Chain, Common } from '@ethereumjs/common'
-import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
+// examples/ledgerSigner.mts
+
+import { Chain, Common, Sepolia } from '@ethereumjs/common'
+import { createLegacyTx, createFeeMarket1559Tx, type LegacyTx, type FeeMarket1559Tx, type LegacyTxData, type FeeMarketEIP1559TxData } from '@ethereumjs/tx'
 import { bytesToHex } from '@ethereumjs/util'
 import { RLP } from '@ethereumjs/rlp'
 import Eth from '@ledgerhq/hw-app-eth'
+import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
 
-const eth = new Eth(transport)
-const common = new Common({ chain: Chain.Sepolia })
+const transport = await TransportNodeHid.default.open()
+const eth = new Eth.default(transport)
+const common = new Common({ chain: Sepolia })
 
-let txData: any = { value: 1 }
-let tx: LegacyTransaction | FeeMarketEIP1559Transaction
+let tx: LegacyTx | FeeMarket1559Tx
 let unsignedTx: Uint8Array[] | Uint8Array
 let signedTx: typeof tx
 const bip32Path = "44'/60'/0'/0/0"
 
+const legacyTxData: LegacyTxData = {
+    nonce: '0x0',
+    gasPrice: '0x09184e72a000',
+    gasLimit: '0x2710',
+    to: '0x0000000000000000000000000000000000000000',
+    value: '0x00',
+    data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
+}
+
+const eip1559TxData: FeeMarketEIP1559TxData = {
+    data: '0x1a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+    gasLimit: '0x02625a00',
+    maxPriorityFeePerGas: '0x01',
+    maxFeePerGas: '0xff',
+    nonce: '0x00',
+    to: '0xcccccccccccccccccccccccccccccccccccccccc',
+    value: '0x0186a0',
+    v: '0x01',
+    r: '0xafb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9',
+    s: '0x479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64',
+    accessList: [],
+    type: '0x02',
+}
+
 const run = async () => {
-  // Signing a legacy tx
-  tx = LegacyTransaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  // ledger signTransaction API expects it to be serialized
-  let { v, r, s } = await eth.signTransaction(bip32Path, RLP.encode(tx))
-  tx.addSignature(v, r, s, true)
-  let from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
-
-  // Signing a 1559 tx
-  txData = { value: 1 }
-  tx = FeeMarketEIP1559Transaction.fromTxData(txData, { common })
-  tx = tx.getMessageToSign()
-  ;({ v, r, s } = await eth.signTransaction(bip32Path, unsignedTx)) // this syntax is: object destructuring - assignment without declaration
-  tx.addSignature(v, r, s)
-  from = tx.getSenderAddress().toString()
-  console.log(`signedTx: ${bytesToHex(tx.serialize())}\nfrom: ${from}`)
+    // Signing a legacy tx
+    tx = createLegacyTx(legacyTxData, { common })
+    unsignedTx = tx.getMessageToSign()
+    // ledger signTransaction API expects it to be serialized
+    let { v, r, s } = await eth.signTransaction(bip32Path, bytesToHex(RLP.encode(unsignedTx)).slice(2), null)
+    let signedTx: LegacyTx | FeeMarket1559Tx = tx.addSignature(BigInt(`0x${v}`), BigInt(`0x${r}`), BigInt(`0x${s}`))
path: packages/tx/README.md
user: login: acolytec3
body: The example reuses the same `signedTx` variable for both the legacy transaction and the EIP1559 tx and Typescript was complaining since I didn't provide the type annotation for both.
created_at: 2025-05-22T14:09:08Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-05-20T16:09:50Z
body: ## [Codecov](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 79.39%. Comparing base [(`ee42ec5`)](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/commit/ee42ec5c5a56415304bb9dd544b56d042ed997d7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) to head [(`4f2443e`)](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/commit/4f2443e44430daac07292bec9359d57a6f4a7aff?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs).
> Report is 1 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/graphs/tree.svg?width=650&height=150&src=pr&token=5GTaSDxbii&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs)](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs)

| [Flag](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | Coverage Δ | |
|---|---|---|
| [block](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `84.33% <ø> (ø)` | |
| [blockchain](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `89.32% <ø> (ø)` | |
| [client](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `67.58% <ø> (?)` | |
| [common](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `97.45% <ø> (ø)` | |
| [devp2p](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `86.78% <ø> (ø)` | |
| [evm](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `73.11% <ø> (ø)` | |
| [mpt](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `89.69% <ø> (-0.06%)` | :arrow_down: |
| [statemanager](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `69.06% <ø> (ø)` | |
| [static](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `99.11% <ø> (ø)` | |
| [tx](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `89.89% <ø> (ø)` | |
| [util](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `89.16% <ø> (ø)` | |
| [vm](https://app.codecov.io/gh/ethereumjs/ethereumjs-monorepo/pull/4084/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs) | `55.50% <ø> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ethereumjs#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: vedant-asati
created_at: 2025-05-21T13:40:44Z
body: > Thanks for the PR. Did you check this with the ledger signer app to verify this works?

actually i dont have a hardware wallet but im searching for tools to simulate it.

author_association: CONTRIBUTOR
user: login: vedant-asati
created_at: 2025-05-22T11:09:26Z
body: @acolytec3 i've tested it locally using `@ledgerhq/wallet-api-client` with mock signature params and its passing.
For legacy txns:
```
v: "25",
r: "28EF61340BD939BC2195FE537567866003E1A15D3C71FF63E1590620AA636276",
s: "67CBE9D8997F761AECB703304B3800CCF555C9F3DC64214B297FB1966A3B6D83"
```
For 1559 txns:
```
v: "00",
r: "28EF61340BD939BC2195FE537567866003E1A15D3C71FF63E1590620AA636276",
s: "67CBE9D8997F761AECB703304B3800CCF555C9F3DC64214B297FB1966A3B6D83"
```
With Sepolia there were some validation errors (of `v` value) concerning EIP-155 so I switched to Mainnet and it passed.
You can further test with real ledger so we can be more assured.
author_association: CONTRIBUTOR