Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

### Bug Fixes

- Fix decoding of `input` values from RPC results
- Handle the case when the RPC result does not have a transaction signature

## 0.6.8 (2025-10-13)

### Enhancements
Expand Down
14 changes: 7 additions & 7 deletions lib/ethers/transaction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@ defmodule Ethers.Transaction do
end
end

defp maybe_wrap_signed({:ok, transaction}, params) do
case Map.fetch(params, :signature_r) do
{:ok, sig_r} when not is_nil(sig_r) ->
defp maybe_wrap_signed({:ok, transaction}, params) when not is_nil(transaction) do
case Map.get(params, :signature_r) do
nil ->
{:ok, transaction}

_sig_r ->
params
|> Map.put(:payload, transaction)
|> Signed.new()

:error ->
{:ok, transaction}
end
end

Expand Down Expand Up @@ -265,7 +265,7 @@ defmodule Ethers.Transaction do
block_hash: from_map_value(tx, :blockHash),
block_number: from_map_value_int(tx, :blockNumber),
chain_id: from_map_value_int(tx, :chainId),
input: from_map_value(tx, :input) || from_map_value(tx, :data),
input: from_map_value_bin(tx, :input) || from_map_value_bin(tx, :data) || "",
from: from_map_value(tx, :from),
gas: from_map_value_int(tx, :gas),
gas_price: from_map_value_int(tx, :gasPrice),
Expand Down
141 changes: 141 additions & 0 deletions test/ethers/transaction_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,145 @@ defmodule Ethers.TransactionTest do
assert Transaction.Protocol.type_envelope(signed_tx) == type.type_envelope()
end
end

describe "from_rpc_map/1" do
test "handles transaction with input field" do
tx_map = %{
"type" => "0x2",
"chainId" => "0x1",
"nonce" => "0x0",
"to" => "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"value" => "0x0",
"input" => "0x1234567890",
"gas" => "0x5208",
"maxFeePerGas" => "0x3b9aca00",
"maxPriorityFeePerGas" => "0x0"
}

assert {:ok, transaction} = Transaction.from_rpc_map(tx_map)
assert transaction.input == Utils.hex_decode!("0x1234567890")
end

test "handles transaction with data field instead of input" do
tx_map = %{
"type" => "0x2",
"chainId" => "0x1",
"nonce" => "0x0",
"to" => "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"value" => "0x0",
"data" => "0xabcdef",
"gas" => "0x5208",
"maxFeePerGas" => "0x3b9aca00",
"maxPriorityFeePerGas" => "0x0"
}

assert {:ok, transaction} = Transaction.from_rpc_map(tx_map)
assert transaction.input == Utils.hex_decode!("0xabcdef")
end

test "handles transaction with both input and data fields (input takes precedence)" do
tx_map = %{
"type" => "0x2",
"chainId" => "0x1",
"nonce" => "0x0",
"to" => "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"value" => "0x0",
"input" => "0x1234",
"data" => "0x5678",
"gas" => "0x5208",
"maxFeePerGas" => "0x3b9aca00",
"maxPriorityFeePerGas" => "0x0"
}

assert {:ok, transaction} = Transaction.from_rpc_map(tx_map)
assert transaction.input == Utils.hex_decode!("0x1234")
end

test "handles transaction with neither input nor data field (defaults to empty string)" do
tx_map = %{
"type" => "0x2",
"chainId" => "0x1",
"nonce" => "0x0",
"to" => "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"value" => "0x0",
"gas" => "0x5208",
"maxFeePerGas" => "0x3b9aca00",
"maxPriorityFeePerGas" => "0x0"
}

assert {:ok, transaction} = Transaction.from_rpc_map(tx_map)
assert transaction.input == ""
end

test "handles transaction with nil input and data fields" do
tx_map = %{
"type" => "0x2",
"chainId" => "0x1",
"nonce" => "0x0",
"to" => "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"value" => "0x0",
"input" => nil,
"data" => nil,
"gas" => "0x5208",
"maxFeePerGas" => "0x3b9aca00",
"maxPriorityFeePerGas" => "0x0"
}

assert {:ok, transaction} = Transaction.from_rpc_map(tx_map)
assert transaction.input == ""
end

test "handles transaction with atom keys" do
tx_map = %{
type: "0x2",
chainId: "0x1",
nonce: "0x0",
to: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
value: "0x0",
input: "0xdeadbeef",
gas: "0x5208",
maxFeePerGas: "0x3b9aca00",
maxPriorityFeePerGas: "0x0"
}

assert {:ok, transaction} = Transaction.from_rpc_map(tx_map)
assert transaction.input == Utils.hex_decode!("0xdeadbeef")
assert %Transaction.Eip1559{} = transaction
end

test "handles legacy transaction type" do
tx_map = %{
"type" => "0x0",
"chainId" => "0x1",
"nonce" => "0x0",
"to" => "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"value" => "0x0",
"data" => "0xaabbcc",
"gas" => "0x5208",
"gasPrice" => "0x3b9aca00"
}

assert {:ok, transaction} = Transaction.from_rpc_map(tx_map)
assert transaction.input == Utils.hex_decode!("0xaabbcc")
assert %Transaction.Legacy{} = transaction
end

test "handles EIP-2930 transaction type" do
tx_map = %{
"type" => "0x1",
"chainId" => "0x1",
"nonce" => "0x0",
"to" => "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5",
"value" => "0x0",
"input" => "0x112233",
"gas" => "0x5208",
"gasPrice" => "0x3b9aca00",
"accessList" => []
}

assert {:ok, transaction} = Transaction.from_rpc_map(tx_map)
assert transaction.input == Utils.hex_decode!("0x112233")
assert %Transaction.Eip2930{} = transaction
end
end
end
13 changes: 9 additions & 4 deletions test/ethers_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule EthersTest do

import Ethers.TestHelpers

alias Ethers.Transaction
alias Ethers.Utils

alias Ethers.Contract.Test.HelloWorldContract
Expand Down Expand Up @@ -122,9 +123,10 @@ defmodule EthersTest do

describe "get_transaction" do
test "returns correct transaction by tx_hash" do
%{data: input} = call = HelloWorldContract.set_hello("hello local signer")

{:ok, tx_hash} =
HelloWorldContract.set_hello("hello local signer")
|> Ethers.send_transaction(
Ethers.send_transaction(call,
from: @from,
to: @to,
signer: Ethers.Signer.Local,
Expand All @@ -140,14 +142,17 @@ defmodule EthersTest do
assert {:ok,
%Ethers.Transaction.Signed{
payload: %Ethers.Transaction.Eip1559{
to: ^checksum_to_addr
to: ^checksum_to_addr,
input: ^input
},
metadata: %Ethers.Transaction.Metadata{
block_hash: "0x" <> _,
block_number: block_number,
transaction_index: 0
}
}} = Ethers.get_transaction(tx_hash)
} = transaction} = Ethers.get_transaction(tx_hash)

assert Transaction.transaction_hash(transaction) == tx_hash

assert is_integer(block_number) and block_number >= 0
end
Expand Down