parent
1414a94973
commit
95de1ebcb0
@ -0,0 +1,189 @@ |
|||||||
|
defmodule Explorer.Chain.TokenTransferTest do |
||||||
|
use Explorer.DataCase |
||||||
|
|
||||||
|
import Explorer.Factory |
||||||
|
|
||||||
|
alias Explorer.PagingOptions |
||||||
|
alias Explorer.Chain.TokenTransfer |
||||||
|
|
||||||
|
doctest Explorer.Chain.TokenTransfer |
||||||
|
|
||||||
|
describe "fetch_token_transfers/2" do |
||||||
|
test "returns token transfers for the given address" do |
||||||
|
token_contract_address = insert(:contract_address) |
||||||
|
|
||||||
|
transaction = |
||||||
|
:transaction |
||||||
|
|> insert() |
||||||
|
|> with_block() |
||||||
|
|
||||||
|
token = insert(:token, contract_address: token_contract_address) |
||||||
|
|
||||||
|
token_transfer = |
||||||
|
insert( |
||||||
|
:token_transfer, |
||||||
|
to_address: build(:address), |
||||||
|
transaction: transaction, |
||||||
|
token_contract_address: token_contract_address, |
||||||
|
token: token |
||||||
|
) |
||||||
|
|
||||||
|
another_transfer = |
||||||
|
insert( |
||||||
|
:token_transfer, |
||||||
|
to_address: build(:address), |
||||||
|
transaction: transaction, |
||||||
|
token_contract_address: token_contract_address, |
||||||
|
token: token |
||||||
|
) |
||||||
|
|
||||||
|
insert( |
||||||
|
:token_transfer, |
||||||
|
to_address: build(:address), |
||||||
|
transaction: transaction, |
||||||
|
token_contract_address: build(:address), |
||||||
|
token: token |
||||||
|
) |
||||||
|
|
||||||
|
transfers_ids = |
||||||
|
token_contract_address.hash |
||||||
|
|> TokenTransfer.fetch_token_transfers_from_token_hash([]) |
||||||
|
|> Enum.map(& &1.id) |
||||||
|
|
||||||
|
assert transfers_ids == [another_transfer.id, token_transfer.id] |
||||||
|
end |
||||||
|
|
||||||
|
test "when there isn't token transfers won't show anything" do |
||||||
|
token_contract_address = insert(:contract_address) |
||||||
|
|
||||||
|
insert(:token, contract_address: token_contract_address) |
||||||
|
|
||||||
|
transfers_ids = |
||||||
|
token_contract_address.hash |
||||||
|
|> TokenTransfer.fetch_token_transfers_from_token_hash([]) |
||||||
|
|> Enum.map(& &1.id) |
||||||
|
|
||||||
|
assert transfers_ids == [] |
||||||
|
end |
||||||
|
|
||||||
|
test "token transfers can be paginated" do |
||||||
|
token_contract_address = insert(:contract_address) |
||||||
|
|
||||||
|
transaction = |
||||||
|
:transaction |
||||||
|
|> insert() |
||||||
|
|> with_block() |
||||||
|
|
||||||
|
token = insert(:token) |
||||||
|
|
||||||
|
second_page = |
||||||
|
insert( |
||||||
|
:token_transfer, |
||||||
|
to_address: build(:address), |
||||||
|
transaction: transaction, |
||||||
|
token_contract_address: token_contract_address, |
||||||
|
token: token |
||||||
|
) |
||||||
|
|
||||||
|
first_page = |
||||||
|
insert( |
||||||
|
:token_transfer, |
||||||
|
to_address: build(:address), |
||||||
|
transaction: transaction, |
||||||
|
token_contract_address: token_contract_address, |
||||||
|
token: token |
||||||
|
) |
||||||
|
|
||||||
|
paging_options = %PagingOptions{key: first_page.inserted_at, page_size: 1} |
||||||
|
|
||||||
|
token_transfers_ids_paginated = |
||||||
|
TokenTransfer.fetch_token_transfers_from_token_hash( |
||||||
|
token_contract_address.hash, |
||||||
|
paging_options: paging_options |
||||||
|
) |
||||||
|
|> Enum.map(& &1.id) |
||||||
|
|
||||||
|
assert token_transfers_ids_paginated == [second_page.id] |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "count_token_transfers/1" do |
||||||
|
test "counts how many token transfers a token has" do |
||||||
|
token_contract_address = insert(:contract_address) |
||||||
|
|
||||||
|
transaction = |
||||||
|
:transaction |
||||||
|
|> insert() |
||||||
|
|> with_block() |
||||||
|
|
||||||
|
token = insert(:token) |
||||||
|
|
||||||
|
insert( |
||||||
|
:token_transfer, |
||||||
|
to_address: build(:address), |
||||||
|
transaction: transaction, |
||||||
|
token_contract_address: token_contract_address, |
||||||
|
token: token |
||||||
|
) |
||||||
|
|
||||||
|
insert( |
||||||
|
:token_transfer, |
||||||
|
to_address: build(:address), |
||||||
|
transaction: transaction, |
||||||
|
token_contract_address: token_contract_address, |
||||||
|
token: token |
||||||
|
) |
||||||
|
|
||||||
|
assert TokenTransfer.count_token_transfers_from_token_hash(token_contract_address.hash) == 2 |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "count_addresses_in_transfers/1" do |
||||||
|
test "counts how many unique addresses that appeared at `to` or `from`" do |
||||||
|
token_contract_address = insert(:contract_address) |
||||||
|
|
||||||
|
transaction = |
||||||
|
:transaction |
||||||
|
|> insert() |
||||||
|
|> with_block() |
||||||
|
|
||||||
|
john_address = insert(:address) |
||||||
|
jane_address = insert(:address) |
||||||
|
bob_address = insert(:address) |
||||||
|
|
||||||
|
insert( |
||||||
|
:token_transfer, |
||||||
|
from_address: jane_address, |
||||||
|
to_address: john_address, |
||||||
|
transaction: transaction, |
||||||
|
token_contract_address: token_contract_address |
||||||
|
) |
||||||
|
|
||||||
|
insert( |
||||||
|
:token_transfer, |
||||||
|
from_address: john_address, |
||||||
|
to_address: jane_address, |
||||||
|
transaction: transaction, |
||||||
|
token_contract_address: token_contract_address |
||||||
|
) |
||||||
|
|
||||||
|
insert( |
||||||
|
:token_transfer, |
||||||
|
from_address: bob_address, |
||||||
|
to_address: jane_address, |
||||||
|
transaction: transaction, |
||||||
|
token_contract_address: token_contract_address |
||||||
|
) |
||||||
|
|
||||||
|
insert( |
||||||
|
:token_transfer, |
||||||
|
from_address: jane_address, |
||||||
|
to_address: bob_address, |
||||||
|
transaction: transaction, |
||||||
|
token_contract_address: token_contract_address |
||||||
|
) |
||||||
|
|
||||||
|
assert TokenTransfer.count_addresses_in_token_transfers_from_token_hash(token_contract_address.hash) == 3 |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -1,10 +1,31 @@ |
|||||||
defmodule ExplorerWeb.TokenController do |
defmodule ExplorerWeb.TokenController do |
||||||
use ExplorerWeb, :controller |
use ExplorerWeb, :controller |
||||||
|
|
||||||
def show(conn, %{"id" => id, "locale" => locale}) do |
alias Explorer.Chain |
||||||
|
|
||||||
|
import ExplorerWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3] |
||||||
|
|
||||||
|
def show(conn, %{"id" => address_hash_string} = params) do |
||||||
|
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), |
||||||
|
{:ok, token} <- Chain.token_from_address_hash(address_hash), |
||||||
|
token_transfers <- Chain.fetch_token_transfers_from_token_hash(address_hash, paging_options(params)) do |
||||||
|
{token_transfers_paginated, next_page} = split_list_by_page(token_transfers) |
||||||
|
|
||||||
render( |
render( |
||||||
conn, |
conn, |
||||||
"show.html" |
"show.html", |
||||||
|
transfers: token_transfers_paginated, |
||||||
|
token: token, |
||||||
|
total_token_transfers: Chain.count_token_transfers_from_token_hash(address_hash), |
||||||
|
total_address_in_token_transfers: Chain.count_addresses_in_token_transfers_from_token_hash(address_hash), |
||||||
|
next_page_params: next_page_params(next_page, token_transfers_paginated, params) |
||||||
) |
) |
||||||
|
else |
||||||
|
:error -> |
||||||
|
not_found(conn) |
||||||
|
|
||||||
|
{:error, :not_found} -> |
||||||
|
not_found(conn) |
||||||
|
end |
||||||
end |
end |
||||||
end |
end |
||||||
|
@ -0,0 +1,48 @@ |
|||||||
|
<div class="tile tile-type-token fade-in mb-10"> |
||||||
|
<div class="row"> |
||||||
|
<div class="pl-5 col-md-2 d-flex flex-column align-items-left justify-content-start justify-content-lg-center"> |
||||||
|
<span class="tile-label"><%= gettext "Token Transfer" %></span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="col-md-7 col-lg-8 d-flex flex-column"> |
||||||
|
<p class="tile-title text-truncate"> |
||||||
|
<%= render ExplorerWeb.TransactionView, "_link.html", locale: @locale, transaction_hash: @transfer.transaction_hash %> |
||||||
|
</p> |
||||||
|
<span> |
||||||
|
<%= render ExplorerWeb.AddressView, |
||||||
|
"_link.html", |
||||||
|
address_hash: to_string(@transfer.from_address_hash), |
||||||
|
contract: ExplorerWeb.AddressView.contract?(@transfer.from_address), |
||||||
|
locale: @locale %> |
||||||
|
|
||||||
|
→ |
||||||
|
|
||||||
|
<%= render ExplorerWeb.AddressView, |
||||||
|
"_link.html", |
||||||
|
address_hash: to_string(@transfer.to_address_hash), |
||||||
|
contract: ExplorerWeb.AddressView.contract?(@transfer.to_address), |
||||||
|
locale: @locale %> |
||||||
|
</span> |
||||||
|
|
||||||
|
<span> |
||||||
|
<span> |
||||||
|
<%= token_transfer_amount(@transfer) %> <%= @transfer.token.symbol %></span> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="p-4 col-md-2 col-lg-2 d-flex flex-row flex-md-column justify-content-start align-items-end text-md-right"> |
||||||
|
<span class="ml-1 mr-sm-0 text-muted" data-from-now="<%= @transfer.transaction.block.timestamp %>"></span> |
||||||
|
|
||||||
|
<span class="ml-2"> |
||||||
|
<%= link( |
||||||
|
gettext( |
||||||
|
"Block #%{number}", |
||||||
|
number: @transfer.transaction.block_number |
||||||
|
), |
||||||
|
class: "mr-2 mr-sm-0 text-muted", |
||||||
|
to: block_path(ExplorerWeb.Endpoint, :show, @locale, @transfer.transaction.block_number) |
||||||
|
) %> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
@ -1,3 +1,9 @@ |
|||||||
defmodule ExplorerWeb.TokenView do |
defmodule ExplorerWeb.TokenView do |
||||||
use ExplorerWeb, :view |
use ExplorerWeb, :view |
||||||
|
|
||||||
|
def decimals?(nil), do: false |
||||||
|
def decimals?(_), do: true |
||||||
|
|
||||||
|
def token_name?(nil), do: false |
||||||
|
def token_name?(_), do: true |
||||||
end |
end |
||||||
|
Loading…
Reference in new issue