diff --git a/CHANGELOG.md b/CHANGELOG.md index b5a3cb4..790de67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Bug fixes + +- Fix `to` address trimmed zeros in transaction encoder with unified hex encoding for transaction + ## v0.3.0 (2024-02-05) ### Breaking Changes diff --git a/lib/ethers/signer/local.ex b/lib/ethers/signer/local.ex index 452b991..82bef02 100644 --- a/lib/ethers/signer/local.ex +++ b/lib/ethers/signer/local.ex @@ -41,9 +41,9 @@ defmodule Ethers.Signer.Local do signed = %Ethers.Transaction{ tx - | signature_r: r, - signature_s: s, - signature_y_parity_or_v: y_parity_or_v + | signature_r: Utils.hex_encode(r), + signature_s: Utils.hex_encode(s), + signature_y_parity_or_v: Utils.integer_to_hex(y_parity_or_v) } |> Transaction.encode() |> Utils.hex_encode() diff --git a/lib/ethers/transaction.ex b/lib/ethers/transaction.ex index 147bbf8..b5e5f9c 100644 --- a/lib/ethers/transaction.ex +++ b/lib/ethers/transaction.ex @@ -105,7 +105,6 @@ defmodule Ethers.Transaction do transaction |> to_rlp_list() |> maybe_append_signature(transaction) - |> convert_to_binary() |> ExRLP.encode() |> prepend_type_envelope(type) end @@ -184,11 +183,12 @@ defmodule Ethers.Transaction do case tx do %{signature_r: r, signature_s: s, signature_y_parity_or_v: y_parity} when has_value(r) and has_value(s) and has_value(y_parity) -> - tx_list ++ [y_parity, trim_leading(r), trim_leading(s)] + tx_list ++ + [Utils.hex_to_integer!(y_parity), Utils.hex_to_integer!(r), Utils.hex_to_integer!(s)] %{type: :legacy, chain_id: chain_id} when not is_nil(chain_id) -> # EIP-155 encoding for signature mitigation intra-chain replay attack - tx_list ++ [chain_id, 0, 0] + tx_list ++ [Utils.hex_to_integer!(chain_id), 0, 0] _ -> tx_list @@ -197,26 +197,26 @@ defmodule Ethers.Transaction do defp to_rlp_list(%{type: :eip1559} = tx) do [ - tx.chain_id, - tx.nonce, - tx.max_priority_fee_per_gas, - tx.max_fee_per_gas, - tx.gas, - tx.to, - tx.value, - tx.data, - tx.access_list || [] + Utils.hex_to_integer!(tx.chain_id), + Utils.hex_to_integer!(tx.nonce), + Utils.hex_to_integer!(tx.max_priority_fee_per_gas), + Utils.hex_to_integer!(tx.max_fee_per_gas), + Utils.hex_to_integer!(tx.gas), + hex_decode(tx.to), + Utils.hex_to_integer!(tx.value), + hex_decode(tx.data), + hex_decode(tx.access_list || []) ] end defp to_rlp_list(%{type: :legacy} = tx) do [ - tx.nonce, - tx.gas_price, - tx.gas, - tx.to, - tx.value, - tx.data + Utils.hex_to_integer!(tx.nonce), + Utils.hex_to_integer!(tx.gas_price), + Utils.hex_to_integer!(tx.gas), + hex_decode(tx.to), + Utils.hex_to_integer!(tx.value), + hex_decode(tx.data) ] end @@ -257,22 +257,15 @@ defmodule Ethers.Transaction do defp do_post_process(_key, {:error, reason}), do: {:error, reason} - defp convert_to_binary(list) do - Enum.map(list, fn - "0x" <> _ = bin -> - bin - |> Utils.hex_decode!() - |> trim_leading() + defp hex_decode(""), do: "" + defp hex_decode("0x"), do: "" - l when is_list(l) -> - convert_to_binary(l) - - nil -> - "" + defp hex_decode("0x" <> _ = bin) do + Utils.hex_decode!(bin) + end - item -> - item - end) + defp hex_decode(list) when is_list(list) do + Enum.map(list, &hex_decode/1) end def calculate_y_parity_or_v(tx, recovery_id) when has_value(recovery_id) do @@ -292,9 +285,6 @@ defmodule Ethers.Transaction do end end - defp trim_leading(<<0, rest::binary>>), do: trim_leading(rest) - defp trim_leading(<>), do: bin - defp decode_tx_type(type) do case type do "0x3" -> {:ok, :eip4844} diff --git a/test/ethers/transaction_test.exs b/test/ethers/transaction_test.exs index beec5f0..a6f61fd 100644 --- a/test/ethers/transaction_test.exs +++ b/test/ethers/transaction_test.exs @@ -61,4 +61,44 @@ defmodule Ethers.TransactionTest do Transaction.decode_values(%{@transaction_fixture | signature_y_parity_or_v: ""}) end end + + describe "encode/1" do + test "encodes transaction with address having leading zeros" do + transaction = %Ethers.Transaction{ + type: :eip1559, + chain_id: "0x00539", + from: "0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1", + to: "0x00008FDEE72ac11b5c542428B35EEF5769C409f0", + nonce: "0x1", + gas: "0x1234", + value: "0x0", + data: "0x0006fdde03", + gas_price: "0x10e7467522", + max_fee_per_gas: "0x1448BAF2F5", + max_priority_fee_per_gas: "0x0" + } + + assert "0x02eb8205390180851448baf2f58212349400008fdee72ac11b5c542428b35eef5769c409f080850006fdde03c0" == + Transaction.encode(transaction) |> Ethers.Utils.hex_encode() + end + + test "encodes transaction with empty data" do + transaction = %Ethers.Transaction{ + type: :eip1559, + chain_id: "0x00539", + from: "0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1", + to: "0x00008FDEE72ac11b5c542428B35EEF5769C409f0", + nonce: "0x1", + gas: "0x1234", + value: "0x0", + data: "", + gas_price: "0x10e7467522", + max_fee_per_gas: "0x1448BAF2F5", + max_priority_fee_per_gas: "0x0" + } + + assert "0x02e68205390180851448baf2f58212349400008fdee72ac11b5c542428b35eef5769c409f08080c0" == + Transaction.encode(transaction) |> Ethers.Utils.hex_encode() + end + end end