diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
index a6f58ac33e..402dfde53f 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
@@ -48,30 +48,67 @@ defmodule BlockScoutWeb.TransactionView do
{unquote(TokenTransfer.transfer_function_signature()) <> params, ^zero_wei} ->
types = [:address, {:uint, 256}]
- try do
- [address, value] =
- params
- |> Base.decode16!(case: :mixed)
- |> TypeDecoder.decode_raw(types)
+ [address, value] = decode_params(params, types)
- decimal_value = Decimal.new(value)
+ decimal_value = Decimal.new(value)
- Enum.find(token_transfers, fn token_transfer ->
- token_transfer.to_address_hash.bytes == address && token_transfer.amount == decimal_value
- end)
- rescue
- _ -> nil
- end
+ Enum.find(token_transfers, fn token_transfer ->
+ token_transfer.to_address_hash.bytes == address && token_transfer.amount == decimal_value
+ end)
_ ->
nil
end
+ rescue
+ _ -> nil
end
def erc20_token_transfer(_, _) do
nil
end
+ def erc721_token_transfer(
+ %Transaction{
+ status: :ok,
+ created_contract_address_hash: nil,
+ input: input,
+ value: value
+ },
+ token_transfers
+ ) do
+ zero_wei = %Wei{value: Decimal.new(0)}
+
+ # https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC721/ERC721.sol#L35
+ {from_address, to_address} =
+ case {to_string(input), value} do
+ {"0x23b872dd" <> params, ^zero_wei} ->
+ types = [:address, :address, {:uint, 256}]
+ [from_address, to_address, _value] = decode_params(params, types)
+ {from_address, to_address}
+
+ {"0x42842e0e" <> params, ^zero_wei} ->
+ types = [:address, :address, {:uint, 256}]
+ [from_address, to_address, _value] = decode_params(params, types)
+ {from_address, to_address}
+
+ {"0xb88d4fde" <> params, ^zero_wei} ->
+ types = [:address, :address, {:uint, 256}, :bytes]
+ [from_address, to_address, _value, _data] = decode_params(params, types)
+ {from_address, to_address}
+
+ _ ->
+ nil
+ end
+
+ Enum.find(token_transfers, fn token_transfer ->
+ token_transfer.from_address_hash.bytes == from_address && token_transfer.to_address_hash.bytes == to_address
+ end)
+ rescue
+ _ -> nil
+ end
+
+ def erc721_token_transfer(_, _), do: nil
+
def processing_time_duration(%Transaction{block: nil}) do
:pending
end
@@ -298,4 +335,10 @@ defmodule BlockScoutWeb.TransactionView do
defp tab_name(["token_transfers"]), do: gettext("Token Transfers")
defp tab_name(["internal_transactions"]), do: gettext("Internal Transactions")
defp tab_name(["logs"]), do: gettext("Logs")
+
+ defp decode_params(params, types) do
+ params
+ |> Base.decode16!(case: :mixed)
+ |> TypeDecoder.decode_raw(types)
+ end
end
diff --git a/apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs
index 2a3e5492ed..e8efeff6a0 100644
--- a/apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs
+++ b/apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs
@@ -47,6 +47,30 @@ defmodule BlockScoutWeb.TransactionViewTest do
end
end
+ describe "erc721_token_transfer/2" do
+ test "finds token transfer" do
+ from_address_hash = "0x7a30272c902563b712245696f0a81c5a0e45ddc8"
+ to_address_hash = "0xb544cead8b660aae9f2e37450f7be2ffbc501793"
+ from_address = insert(:address, hash: from_address_hash)
+ to_address = insert(:address, hash: to_address_hash)
+ block = insert(:block)
+
+ transaction =
+ insert(:transaction,
+ input:
+ "0x23b872dd0000000000000000000000007a30272c902563b712245696f0a81c5a0e45ddc8000000000000000000000000b544cead8b660aae9f2e37450f7be2ffbc5017930000000000000000000000000000000000000000000000000000000000000002",
+ value: Decimal.new(0),
+ created_contract_address_hash: nil
+ )
+ |> with_block(block, status: :ok)
+
+ token_transfer =
+ insert(:token_transfer, from_address: from_address, to_address: to_address, transaction: transaction)
+
+ assert TransactionView.erc721_token_transfer(transaction, [token_transfer]) == token_transfer
+ end
+ end
+
describe "processing_time_duration/2" do
test "returns :pending if the transaction has no block" do
transaction = build(:transaction, block: nil)