Bridged tokens API endpoint

pull/4989/head
Viktor Baranov 3 years ago
parent 37d4de6f12
commit cc1c7b4f34
  1. 1
      CHANGELOG.md
  2. 28
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/token_controller.ex
  3. 112
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  4. 23
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/token_view.ex
  5. 6
      apps/block_scout_web/lib/block_scout_web/views/bridged_tokens_view.ex
  6. 20
      apps/explorer/lib/explorer/chain.ex

@ -1,6 +1,7 @@
## Current
### Features
- [#4989](https://github.com/blockscout/blockscout/pull/4989) - Bridged tokens list API endpoint
- [#4931](https://github.com/blockscout/blockscout/pull/4931) - Web3 modal with Wallet Connect for Write contract page and Staking Dapp
### Fixes

@ -49,10 +49,38 @@ defmodule BlockScoutWeb.API.RPC.TokenController do
end
end
def bridgedtokenlist(conn, params) do
chainid = params |> Map.get("chainid")
destination = translate_chain_id_to_destination(chainid)
params_with_paging_options = Helpers.put_pagination_options(%{}, params)
options = [
paging_options: %PagingOptions{
key: nil,
page_number: params_with_paging_options.page_number,
page_size: params_with_paging_options.page_size
}
]
bridged_tokens = Chain.list_top_bridged_tokens(destination, nil, options)
render(conn, "bridgedtokenlist.json", %{bridged_tokens: bridged_tokens})
end
defp fetch_contractaddress(params) do
{:contractaddress_param, Map.fetch(params, "contractaddress")}
end
defp translate_chain_id_to_destination(destination) do
case destination do
"1" -> :eth
"42" -> :kovan
"56" -> :bsc
"99" -> :poa
wrong_chain_id -> wrong_chain_id
end
end
defp to_address_hash(address_hash_string) do
{:format, Chain.string_to_address_hash(address_hash_string)}
end

@ -293,6 +293,35 @@ defmodule BlockScoutWeb.Etherscan do
"result" => nil
}
@token_bridgedtokenlist_example_value %{
"status" => "1",
"message" => "OK",
"result" => [
%{
"foreignChainId" => "1",
"foreignTokenContractAddressHash" => "0x0ae055097c6d159879521c384f1d2123d1f195e6",
"homeContractAddressHash" => "0xb7d311e2eb55f2f68a9440da38e7989210b9a05e",
"homeDecimals" => "18",
"homeHolderCount" => 393,
"homeName" => "STAKE on xDai",
"homeSymbol" => "STAKE",
"homeTotalSupply" => "1484374.775044204093387391",
"homeUsdValue" => "18807028.39981006586321824397"
},
%{
"foreignChainId" => "1",
"foreignTokenContractAddressHash" => "0xf5581dfefd8fb0e4aec526be659cfab1f8c781da",
"homeContractAddressHash" => "0xd057604a14982fe8d88c5fc25aac3267ea142a08",
"homeDecimals" => "18",
"homeHolderCount" => 73,
"homeName" => "HOPR Token on xDai",
"homeSymbol" => "HOPR",
"homeTotalSupply" => "26600449.86076749062791602",
"homeUsdValue" => "6638727.472651464170990256943"
}
]
}
@stats_tokensupply_example_value %{
"status" => "1",
"message" => "OK",
@ -577,6 +606,12 @@ defmodule BlockScoutWeb.Etherscan do
enum_interpretation: %{"0" => "error", "1" => "ok"}
}
@success_status_type %{
type: "status",
enum: ~s(["1"]),
enum_interpretation: %{"1" => "ok"}
}
@jsonrpc_version_type %{
type: "string",
example: ~s("2.0")
@ -722,6 +757,37 @@ defmodule BlockScoutWeb.Etherscan do
}
}
@bridged_token_details %{
name: "Bridged Token Detail",
fields: %{
foreignChainId: %{
type: "value",
definition: "Chain ID of the chain where original token exists.",
example: ~s("1")
},
foreignTokenContractAddressHash: @address_hash_type,
homeContractAddressHash: @address_hash_type,
homeDecimals: @token_decimal_type,
homeHolderCount: %{
type: "value",
definition: "Token holders count.",
example: ~s("393")
},
homeName: @token_name_type,
homeSymbol: @token_symbol_type,
homeTotalSupply: %{
type: "value",
definition: "Total supply of the token on the home side (where token was bridged).",
example: ~s("1484374.775044204093387391")
},
homeUsdValue: %{
type: "value",
definition: "Total supply of the token on the home side (where token was bridged) in USD.",
example: ~s("6638727.472651464170990256943")
}
}
}
@address_balance %{
name: "AddressBalance",
fields: %{
@ -1951,6 +2017,49 @@ defmodule BlockScoutWeb.Etherscan do
]
}
@token_bridgedtokenlist_action %{
name: "bridgedTokenList",
description: "Get bridged tokens list.",
required_params: [],
optional_params: [
%{
key: "chainid",
type: "integer",
description: "A nonnegative integer that represents the chain id, where original token exists."
},
%{
key: "page",
type: "integer",
description:
"A nonnegative integer that represents the page number to be used for pagination. 'offset' must be provided in conjunction."
},
%{
key: "offset",
type: "integer",
description:
"A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction."
}
],
responses: [
%{
code: "200",
description: "successful operation",
example_value: Jason.encode!(@token_bridgedtokenlist_example_value),
model: %{
name: "Result",
fields: %{
status: @success_status_type,
message: @message_type,
result: %{
type: "array",
array_type: @bridged_token_details
}
}
}
}
]
}
@stats_tokensupply_action %{
name: "tokensupply",
description:
@ -2792,7 +2901,8 @@ defmodule BlockScoutWeb.Etherscan do
name: "token",
actions: [
@token_gettoken_action,
@token_gettokenholders_action
@token_gettokenholders_action,
@token_bridgedtokenlist_action
]
}

@ -2,6 +2,7 @@ defmodule BlockScoutWeb.API.RPC.TokenView do
use BlockScoutWeb, :view
alias BlockScoutWeb.API.RPC.RPCView
alias BlockScoutWeb.BridgedTokensView
def render("gettoken.json", %{token: token}) do
RPCView.render("show.json", data: prepare_token(token))
@ -12,6 +13,11 @@ defmodule BlockScoutWeb.API.RPC.TokenView do
RPCView.render("show.json", data: data)
end
def render("bridgedtokenlist.json", %{bridged_tokens: bridged_tokens}) do
data = Enum.map(bridged_tokens, &prepare_bridged_token/1)
RPCView.render("show.json", data: data)
end
def render("error.json", assigns) do
RPCView.render("error.json", assigns)
end
@ -34,4 +40,21 @@ defmodule BlockScoutWeb.API.RPC.TokenView do
"value" => token_holder.value
}
end
defp prepare_bridged_token([token, bridged_token]) do
total_supply = divide_decimals(token.total_supply, token.decimals)
usd_value = BridgedTokensView.bridged_token_usd_cap(bridged_token, token)
%{
"foreignChainId" => bridged_token.foreign_chain_id,
"foreignTokenContractAddressHash" => bridged_token.foreign_token_contract_address_hash,
"homeContractAddressHash" => token.contract_address_hash,
"homeDecimals" => token.decimals,
"homeHolderCount" => if(token.holder_count, do: to_string(token.holder_count), else: "0"),
"homeName" => token.name,
"homeSymbol" => token.symbol,
"homeTotalSupply" => total_supply,
"homeUsdValue" => usd_value
}
end
end

@ -3,7 +3,7 @@ defmodule BlockScoutWeb.BridgedTokensView do
alias BlockScoutWeb.ChainView
alias Explorer.Chain
alias Explorer.Chain.{Address, Token}
alias Explorer.Chain.{Address, BridgedToken, Token}
@owl_token_amb "0x0905Ab807F8FD040255F0cF8fa14756c1D824931"
@owl_token_omni "0x750eCf8c11867Ce5Dbc556592c5bb1E0C6d16538"
@ -31,6 +31,10 @@ defmodule BlockScoutWeb.BridgedTokensView do
"<div class='custom-tooltip-header'>OWL token bridged through OmniBridge without support of <i>burnOWL</i> method. It is not recommended to use.</div>"
end
@doc """
Calculates capitalization of the bridged token in USD.
"""
@spec bridged_token_usd_cap(%BridgedToken{}, %Token{}) :: any()
def bridged_token_usd_cap(bridged_token, token) do
if bridged_token.custom_cap do
bridged_token.custom_cap

@ -10,6 +10,7 @@ defmodule Explorer.Chain do
join: 5,
limit: 2,
lock: 2,
offset: 2,
order_by: 2,
order_by: 3,
preload: 2,
@ -2305,7 +2306,7 @@ defmodule Explorer.Chain do
fetch_top_tokens(filter, paging_options)
end
@spec list_top_bridged_tokens(atom(), String.t(), [paging_options | necessity_by_association_option]) :: [
@spec list_top_bridged_tokens(atom(), String.t() | nil, [paging_options | necessity_by_association_option]) :: [
{Token.t(), non_neg_integer()}
]
def list_top_bridged_tokens(destination, filter, options \\ []) do
@ -2340,13 +2341,23 @@ defmodule Explorer.Chain do
end
defp fetch_top_bridged_tokens(destination, paging_options, filter) do
offset = (max(paging_options.page_number, 1) - 1) * paging_options.page_size
chain_id = translate_destination_to_chain_id(destination)
if chain_id == :undefined do
[]
else
bridged_tokens_query =
if chain_id do
from(bt in BridgedToken,
select: bt,
where: bt.foreign_chain_id == ^chain_id
)
else
from(bt in BridgedToken,
select: bt
)
end
base_query =
from(t in Token,
@ -2363,6 +2374,7 @@ defmodule Explorer.Chain do
base_query
|> page_tokens(paging_options)
|> limit(^paging_options.page_size)
|> offset(^offset)
query =
if filter && filter !== "" do
@ -2375,12 +2387,16 @@ defmodule Explorer.Chain do
query
|> Repo.all()
end
end
defp translate_destination_to_chain_id(destination) do
case destination do
:eth -> 1
:kovan -> 42
:bsc -> 56
_ -> 1
:poa -> 99
nil -> nil
_ -> :undefined
end
end

Loading…
Cancel
Save