Merge branch 'master' into realtime-pubsub-by-postges-notify

pull/2449/head
saneery 5 years ago
commit 13b8a6ac3f
  1. 2
      .credo.exs
  2. 6
      CHANGELOG.md
  3. 2338
      apps/block_scout_web/assets/package-lock.json
  4. 4
      apps/block_scout_web/assets/package.json
  5. 21
      apps/block_scout_web/assets/webpack.config.js
  6. 44
      apps/block_scout_web/lib/block_scout_web/chain.ex
  7. 40
      apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex
  8. 156
      apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex
  9. 4
      apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex
  10. 4
      apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex
  11. 13
      apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex
  12. 2
      apps/block_scout_web/mix.exs
  13. 29
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex
  14. 2
      apps/ethereum_jsonrpc/mix.exs
  15. 129
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs
  16. 20
      apps/explorer/lib/explorer/chain.ex
  17. 2
      apps/explorer/lib/explorer/chain/hash/address.ex
  18. 24
      apps/explorer/lib/explorer/chain/wei.ex
  19. 2
      apps/explorer/mix.exs
  20. 102
      apps/indexer/lib/indexer/fetcher/internal_transaction.ex
  21. 22
      mix.lock

@ -89,7 +89,7 @@
# or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just
# set the `excluded_macros` parameter to `[:schema, :setup, :test]`.
#
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
{Credo.Check.Design.DuplicatedCode, false},
# You can also customize the exit_status of each check.
# If you don't want TODO comments to cause `mix credo` to fail, just

@ -1,15 +1,19 @@
## Current
### Features
- [#2456](https://github.com/poanetwork/blockscout/pull/2456) - fetch pending transactions for geth
### Fixes
- [#2459](https://github.com/poanetwork/blockscout/pull/2459) - fix top addresses query
- [#2425](https://github.com/poanetwork/blockscout/pull/2425) - Force to show address view for checksummed address even if it is not in DB
### Chore
- [#2432](https://github.com/poanetwork/blockscout/pull/2432) - bump credo version
- [#2457](https://github.com/poanetwork/blockscout/pull/2457) - update mix.lock
- [#2435](https://github.com/poanetwork/blockscout/pull/2435) - Replace deprecated extract-text-webpack-plugin with mini-css-extract-plugin
- [#2450](https://github.com/poanetwork/blockscout/pull/2450) - Fix clearance of logs and node_modules folders in clearing script
- [#2434](https://github.com/poanetwork/blockscout/pull/2434) - get rid of timex warnings
## 2.0.2-beta
### Features

File diff suppressed because it is too large Load Diff

@ -54,13 +54,15 @@
"eslint-plugin-node": "^5.2.1",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-standard": "^3.0.1",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^1.1.11",
"jest": "^23.2.0",
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.9.3",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^2.1.4",
"sass-loader": "^7.0.1",
"style-loader": "^0.21.0",
"terser-webpack-plugin": "^1.3.0",
"webpack": "^4.6.0",
"webpack-cli": "^3.0.8"
},

@ -1,5 +1,7 @@
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { ContextReplacementPlugin } = require('webpack');
const glob = require("glob");
@ -32,6 +34,9 @@ const appJs =
filename: 'app.js',
path: path.resolve(__dirname, '../priv/static/js')
},
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
},
module: {
rules: [
{
@ -43,8 +48,9 @@ const appJs =
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
use: [{
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader"
}, {
loader: "postcss-loader"
@ -57,9 +63,8 @@ const appJs =
'node_modules/@fortawesome/fontawesome-free/scss'
]
}
}],
fallback: 'style-loader'
})
}
]
}, {
test: /\.(svg|ttf|eot|woff|woff2)$/,
use: {
@ -74,7 +79,9 @@ const appJs =
]
},
plugins: [
new ExtractTextPlugin('../css/app.css'),
new MiniCssExtractPlugin({
filename: '../css/app.css'
}),
new CopyWebpackPlugin([{ from: 'static/', to: '../' }]),
new ContextReplacementPlugin(/moment[\/\\]locale$/, /en/)
]

@ -124,18 +124,20 @@ defmodule BlockScoutWeb.Chain do
end
def paging_options(%{"block_number" => block_number_string}) do
with {block_number, ""} <- Integer.parse(block_number_string) do
[paging_options: %{@default_paging_options | key: {block_number}}]
else
case Integer.parse(block_number_string) do
{block_number, ""} ->
[paging_options: %{@default_paging_options | key: {block_number}}]
_ ->
[paging_options: @default_paging_options]
end
end
def paging_options(%{"index" => index_string}) when is_binary(index_string) do
with {index, ""} <- Integer.parse(index_string) do
[paging_options: %{@default_paging_options | key: {index}}]
else
case Integer.parse(index_string) do
{index, ""} ->
[paging_options: %{@default_paging_options | key: {index}}]
_ ->
[paging_options: @default_paging_options]
end
@ -174,10 +176,12 @@ defmodule BlockScoutWeb.Chain do
def split_list_by_page(list_plus_one), do: Enum.split(list_plus_one, @page_size)
defp address_from_param(param) do
with {:ok, hash} <- string_to_address_hash(param) do
find_or_insert_address_from_hash(hash)
else
:error -> {:error, :not_found}
case string_to_address_hash(param) do
{:ok, hash} ->
find_or_insert_address_from_hash(hash)
:error ->
{:error, :not_found}
end
end
@ -246,18 +250,22 @@ defmodule BlockScoutWeb.Chain do
end
defp transaction_from_param(param) do
with {:ok, hash} <- string_to_transaction_hash(param) do
hash_to_transaction(hash)
else
:error -> {:error, :not_found}
case string_to_transaction_hash(param) do
{:ok, hash} ->
hash_to_transaction(hash)
:error ->
{:error, :not_found}
end
end
defp hash_string_to_block(hash_string) do
with {:ok, hash} <- string_to_block_hash(hash_string) do
hash_to_block(hash)
else
:error -> {:error, :not_found}
case string_to_block_hash(hash_string) do
{:ok, hash} ->
hash_to_block(hash)
:error ->
{:error, :not_found}
end
end
end

@ -83,10 +83,12 @@ defmodule BlockScoutWeb.AddressTransactionController do
unprocessable_entity(conn)
{:error, :not_found} ->
with {:ok, _} <- Chain.Hash.Address.validate(address_hash_string) do
json(conn, %{items: [], next_page_path: ""})
else
{:error, _} -> not_found(conn)
case Chain.Hash.Address.validate(address_hash_string) do
{:ok, _} ->
json(conn, %{items: [], next_page_path: ""})
_ ->
not_found(conn)
end
end
end
@ -113,20 +115,22 @@ defmodule BlockScoutWeb.AddressTransactionController do
{:ok, address_hash} = Chain.string_to_address_hash(address_hash_string)
address = %Chain.Address{hash: address_hash, smart_contract: nil, token: nil}
with {:ok, _} <- Chain.Hash.Address.validate(address_hash_string) do
render(
conn,
"index.html",
address: address,
coin_balance_status: nil,
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"],
transaction_count: 0,
validation_count: 0,
current_path: current_path(conn)
)
else
{:error, _} -> not_found(conn)
case Chain.Hash.Address.validate(address_hash_string) do
{:ok, _} ->
render(
conn,
"index.html",
address: address,
coin_balance_status: nil,
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"],
transaction_count: 0,
validation_count: 0,
current_path: current_path(conn)
)
_ ->
not_found(conn)
end
end
end

@ -11,57 +11,57 @@ defmodule BlockScoutWeb.BlockTransactionController do
alias Phoenix.View
def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number, "type" => "JSON"} = params) do
with {:ok, block} <-
param_block_hash_or_number_to_block(formatted_block_hash_or_number, []) do
full_options =
Keyword.merge(
[
necessity_by_association: %{
:block => :optional,
[created_contract_address: :names] => :optional,
[from_address: :names] => :required,
[to_address: :names] => :optional
}
],
paging_options(params)
)
transactions_plus_one = Chain.block_to_transactions(block.hash, full_options)
{transactions, next_page} = split_list_by_page(transactions_plus_one)
next_page_path =
case next_page_params(next_page, transactions, params) do
nil ->
nil
case param_block_hash_or_number_to_block(formatted_block_hash_or_number, []) do
{:ok, block} ->
full_options =
Keyword.merge(
[
necessity_by_association: %{
:block => :optional,
[created_contract_address: :names] => :optional,
[from_address: :names] => :required,
[to_address: :names] => :optional
}
],
paging_options(params)
)
next_page_params ->
block_transaction_path(
conn,
:index,
block,
Map.delete(next_page_params, "type")
transactions_plus_one = Chain.block_to_transactions(block.hash, full_options)
{transactions, next_page} = split_list_by_page(transactions_plus_one)
next_page_path =
case next_page_params(next_page, transactions, params) do
nil ->
nil
next_page_params ->
block_transaction_path(
conn,
:index,
block,
Map.delete(next_page_params, "type")
)
end
items =
transactions
|> Enum.map(fn transaction ->
View.render_to_string(
TransactionView,
"_tile.html",
transaction: transaction
)
end
items =
transactions
|> Enum.map(fn transaction ->
View.render_to_string(
TransactionView,
"_tile.html",
transaction: transaction
)
end)
json(
conn,
%{
items: items,
next_page_path: next_page_path
}
)
else
end)
json(
conn,
%{
items: items,
next_page_path: next_page_path
}
)
{:error, {:invalid, :hash}} ->
not_found(conn)
@ -80,25 +80,25 @@ defmodule BlockScoutWeb.BlockTransactionController do
end
def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number}) do
with {:ok, block} <-
param_block_hash_or_number_to_block(formatted_block_hash_or_number,
necessity_by_association: %{
[miner: :names] => :required,
:uncles => :optional,
:nephews => :optional,
:rewards => :optional
}
) do
block_transaction_count = Chain.block_to_transaction_count(block.hash)
render(
conn,
"index.html",
block: block,
block_transaction_count: block_transaction_count,
current_path: current_path(conn)
)
else
case param_block_hash_or_number_to_block(formatted_block_hash_or_number,
necessity_by_association: %{
[miner: :names] => :required,
:uncles => :optional,
:nephews => :optional,
:rewards => :optional
}
) do
{:ok, block} ->
block_transaction_count = Chain.block_to_transaction_count(block.hash)
render(
conn,
"index.html",
block: block,
block_transaction_count: block_transaction_count,
current_path: current_path(conn)
)
{:error, {:invalid, :hash}} ->
not_found(conn)
@ -117,19 +117,23 @@ defmodule BlockScoutWeb.BlockTransactionController do
end
defp param_block_hash_or_number_to_block("0x" <> _ = param, options) do
with {:ok, hash} <- string_to_block_hash(param) do
hash_to_block(hash, options)
else
:error -> {:error, {:invalid, :hash}}
case string_to_block_hash(param) do
{:ok, hash} ->
hash_to_block(hash, options)
:error ->
{:error, {:invalid, :hash}}
end
end
defp param_block_hash_or_number_to_block(number_string, options)
when is_binary(number_string) do
with {:ok, number} <- BlockScoutWeb.Chain.param_to_block_number(number_string) do
number_to_block(number, options)
else
{:error, :invalid} -> {:error, {:invalid, :number}}
case BlockScoutWeb.Chain.param_to_block_number(number_string) do
{:ok, number} ->
number_to_block(number, options)
{:error, :invalid} ->
{:error, {:invalid, :number}}
end
end

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.Chain.MarketHistoryChartController do
alias Explorer.ExchangeRates.Token
def show(conn, _params) do
with true <- ajax?(conn) do
if ajax?(conn) do
exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null()
recent_market_history = Market.fetch_recent_history()
@ -24,7 +24,7 @@ defmodule BlockScoutWeb.Chain.MarketHistoryChartController do
supply_data: available_supply(Chain.supply_for_days(), exchange_rate)
})
else
_ -> unprocessable_entity(conn)
unprocessable_entity(conn)
end
end

@ -6,7 +6,7 @@ defmodule BlockScoutWeb.RecentTransactionsController do
alias Phoenix.View
def index(conn, _params) do
with true <- ajax?(conn) do
if ajax?(conn) do
recent_transactions =
Chain.recent_collated_transactions(
necessity_by_association: %{
@ -29,7 +29,7 @@ defmodule BlockScoutWeb.RecentTransactionsController do
json(conn, %{transactions: transactions})
else
_ -> unprocessable_entity(conn)
unprocessable_entity(conn)
end
end
end

@ -35,12 +35,13 @@ defmodule BlockScoutWeb.SmartContractController do
def show(conn, params) do
with true <- ajax?(conn),
{:ok, address_hash} <- Chain.string_to_address_hash(params["id"]),
:ok <- Chain.check_contract_address_exists(address_hash),
outputs =
Reader.query_function(
address_hash,
%{name: params["function_name"], args: params["args"]}
) do
:ok <- Chain.check_contract_address_exists(address_hash) do
outputs =
Reader.query_function(
address_hash,
%{name: params["function_name"], args: params["args"]}
)
conn
|> put_status(200)
|> put_layout(false)

@ -71,7 +71,7 @@ defmodule BlockScoutWeb.Mixfile do
{:bypass, "~> 1.0", only: :test},
# To add (CORS)(https://www.w3.org/TR/cors/)
{:cors_plug, "~> 2.0"},
{:credo, "1.0.0", only: :test, runtime: false},
{:credo, "~> 1.1", only: :test, runtime: false},
# For Absinthe to load data in batches
{:dataloader, "~> 1.0.0"},
{:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false},

@ -5,7 +5,7 @@ defmodule EthereumJSONRPC.Geth do
import EthereumJSONRPC, only: [id_to_params: 1, integer_to_quantity: 1, json_rpc: 2, request: 1]
alias EthereumJSONRPC.{FetchedBalance, FetchedCode}
alias EthereumJSONRPC.{FetchedBalance, FetchedCode, Transactions}
alias EthereumJSONRPC.Geth.{Calls, Tracer}
@behaviour EthereumJSONRPC.Variant
@ -46,12 +46,31 @@ defmodule EthereumJSONRPC.Geth do
def fetch_block_internal_transactions(_block_range, _json_rpc_named_arguments), do: :ignore
@doc """
Pending transaction fetching is not supported currently for Geth.
To signal to the caller that fetching is not supported, `:ignore` is returned.
Fetches the pending transactions from the Geth node.
"""
@impl EthereumJSONRPC.Variant
def fetch_pending_transactions(_json_rpc_named_arguments), do: :ignore
def fetch_pending_transactions(json_rpc_named_arguments) do
with {:ok, transaction_data} <-
%{id: 1, method: "txpool_content", params: []} |> request() |> json_rpc(json_rpc_named_arguments) do
transactions_params =
transaction_data["pending"]
|> Enum.flat_map(fn {_address, nonce_transactions_map} ->
nonce_transactions_map
|> Enum.map(fn {_nonce, transaction} ->
transaction
end)
end)
|> Transactions.to_elixir()
|> Transactions.elixir_to_params()
|> Enum.map(fn params ->
# txpool_content always returns transaction with 0x0000000000000000000000000000000000000000000000000000000000000000 value in block hash and index is null.
# https://github.com/ethereum/go-ethereum/issues/19897
%{params | block_hash: nil, index: nil}
end)
{:ok, transactions_params}
end
end
defp debug_trace_transaction_requests(id_to_params) when is_map(id_to_params) do
Enum.map(id_to_params, fn {id, %{hash_data: hash_data}} ->

@ -64,7 +64,7 @@ defmodule EthereumJsonrpc.MixProject do
# WebSocket-server for testing `EthereumJSONRPC.WebSocket.WebSocketClient`.
{:cowboy, "~> 2.0", only: [:dev, :test]},
# Style Checking
{:credo, "1.0.0", only: :test, runtime: false},
{:credo, "~> 1.1", only: :test, runtime: false},
# Static Type Checking
{:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false},
# Code coverage

@ -84,8 +84,133 @@ defmodule EthereumJSONRPC.GethTest do
end
describe "fetch_pending_transactions/1" do
test "is not supported", %{json_rpc_named_arguments: json_rpc_named_arguments} do
EthereumJSONRPC.Geth.fetch_pending_transactions(json_rpc_named_arguments)
@tag :no_geth
test "fetches pending transactions", %{json_rpc_named_arguments: json_rpc_named_arguments} do
expect(EthereumJSONRPC.Mox, :json_rpc, fn _, _ ->
{:ok,
%{
"pending" => %{
"0xC99f4e9cFf697ca6717ad9cE8bA4A138e0e55109" => %{
"4656" => %{
"blockHash" => "0x0000000000000000000000000000000000000000000000000000000000000000",
"blockNumber" => nil,
"from" => "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
"gas" => "0x3d0900",
"gasPrice" => "0x3b9aca00",
"hash" => "0x2b8cfd76a31b942e51b6265c791c860e2840b11f8c2fcfa1c9dfe53dea4c3102",
"input" =>
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030af6932dec7c4eaf4b966059e74cc7a1767ba93e62f2d83a7dba5bb785b6efd25e8ab7d2e8798e7ecc27df96380d77a0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000b29e5229b86fbb3a8e45e406b64226c3d49389804a6f7449325fae232d6623000000000000000000000000000000000000000000000000000000000000006097e4c1ed443f430b1d8ad66e565a960fade76e3e177b4120186bdad2fcfa43e134de3abdc0272c9433af94833fec73260c261cf41422e83d958787b62144478bc44ab84d1ddba7a462d355057f3be8ab914a195ac1a637c4fb8503c441dadb45",
"nonce" => "0x1230",
"r" => "0x81345ae149171f4cb4ab868f0ad637d033c96c4659b190b86a39725c8299c947",
"s" => "0x31450678841d7206fa02b564a641420262cc98c8ea0e32c4cb0e97208d3f9feb",
"to" => "0xf003a84d6890202663c0fd80954e836fcf21e004",
"transactionIndex" => "0x0",
"v" => "0x1b",
"value" => "0xb5e620f480000"
},
"4657" => %{
"blockHash" => "0x0000000000000000000000000000000000000000000000000000000000000000",
"blockNumber" => nil,
"from" => "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
"gas" => "0x3d0900",
"gasPrice" => "0x3b9aca00",
"hash" => "0x7c3ea924740e996bf552a8dded903ba4258b69d30bf5e6dca6ec86ebc60b8151",
"input" =>
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030a25723bca32f88a73abc7eb153cee248effd563d87efe12e08e8a33f74047afc28c30ab9c74bddeb6f0558628b8bf200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020009c56025b2796cdc72f91836278a471590b774462adddd1c87a0b6f84b787990000000000000000000000000000000000000000000000000000000000000060aa53b46c8b57aed7c4c0fdf3f650ec3bb330591929bc813610656882e3203157c22b50d0d0b0316a8712c00fe4f0e0c509613114f5d24c0419a4e8188f2489678b05dccf72a67957785e8e250092c8787f049f7e20b1414a633595a56c98ff82",
"nonce" => "0x1231",
"r" => "0xee1eb895262d12ef5c4ee3cbf9b36de3903bc3a1343f0a312bd19edacc4bb877",
"s" => "0xfcb87efe4c3984a3e1d3f4fb10ce41e59f65e21fbd9206a1648ec73fa0a2206",
"to" => "0xf003a84d6890202663c0fd80954e836fcf21e004",
"transactionIndex" => "0x0",
"v" => "0x1b",
"value" => "0xb5e620f480000"
},
"4658" => %{
"blockHash" => "0x0000000000000000000000000000000000000000000000000000000000000000",
"blockNumber" => nil,
"from" => "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
"gas" => "0x3d0900",
"gasPrice" => "0x3b9aca00",
"hash" => "0xe699a58ef4986f2dbdc102acf73b35392aff9ce43fd226000526955e19c0b06e",
"input" =>
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000308eb3ed9e686f6bc1fe2d8ce3fea37fb3a66a9c67b91ef15ba6bd7da0eed73288f72577edea2b7ded5855ca8a56b1e01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000051afe6c51e2175a62afbd66d293e8a7509943d5cd6f851f59923a61a186e80000000000000000000000000000000000000000000000000000000000000060a063498e8db2e75e0a193de89ad2947111d677c9501e75c34a64fcee8fe5a7c7607929fc6bce943d64f1039e1d1f325f02d1e5d71f86ca976c9ab79d19f0fd0e530a5210fbe131087ba1f1b3c92abc4a0dd7c8a47c3c276fac3e09bca964fd74",
"nonce" => "0x1232",
"r" => "0xe95bc86fc32cc591677c7ec9ca49f1dc33a31427235c1c41dbb7a3a957b55599",
"s" => "0xe8b41a6440d0fe6d0ec1f40982394a2d641b19b983aad49e45614e5f3a1abc9",
"to" => "0xf003a84d6890202663c0fd80954e836fcf21e004",
"transactionIndex" => "0x0",
"v" => "0x1c",
"value" => "0xb5e620f480000"
}
}
},
"queued" => %{}
}}
end)
assert {:ok,
[
%{
block_hash: nil,
block_number: nil,
from_address_hash: "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
gas: 4_000_000,
gas_price: 1_000_000_000,
hash: "0x2b8cfd76a31b942e51b6265c791c860e2840b11f8c2fcfa1c9dfe53dea4c3102",
index: 0,
input:
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030af6932dec7c4eaf4b966059e74cc7a1767ba93e62f2d83a7dba5bb785b6efd25e8ab7d2e8798e7ecc27df96380d77a0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000b29e5229b86fbb3a8e45e406b64226c3d49389804a6f7449325fae232d6623000000000000000000000000000000000000000000000000000000000000006097e4c1ed443f430b1d8ad66e565a960fade76e3e177b4120186bdad2fcfa43e134de3abdc0272c9433af94833fec73260c261cf41422e83d958787b62144478bc44ab84d1ddba7a462d355057f3be8ab914a195ac1a637c4fb8503c441dadb45",
nonce: 4656,
r:
58_440_860_745_466_360_584_510_362_592_650_991_653_332_571_230_597_223_185_413_246_840_900_756_818_247,
s:
22_285_286_687_634_777_993_513_656_263_235_057_426_117_768_584_265_280_722_872_863_042_386_096_267_243,
to_address_hash: "0xf003a84d6890202663c0fd80954e836fcf21e004",
transaction_index: 0,
v: 27,
value: 3_200_000_000_000_000
},
%{
block_hash: nil,
block_number: nil,
from_address_hash: "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
gas: 4_000_000,
gas_price: 1_000_000_000,
hash: "0x7c3ea924740e996bf552a8dded903ba4258b69d30bf5e6dca6ec86ebc60b8151",
index: 0,
input:
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030a25723bca32f88a73abc7eb153cee248effd563d87efe12e08e8a33f74047afc28c30ab9c74bddeb6f0558628b8bf200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020009c56025b2796cdc72f91836278a471590b774462adddd1c87a0b6f84b787990000000000000000000000000000000000000000000000000000000000000060aa53b46c8b57aed7c4c0fdf3f650ec3bb330591929bc813610656882e3203157c22b50d0d0b0316a8712c00fe4f0e0c509613114f5d24c0419a4e8188f2489678b05dccf72a67957785e8e250092c8787f049f7e20b1414a633595a56c98ff82",
nonce: 4657,
r:
107_704_737_317_141_024_268_971_404_113_297_355_261_066_880_504_936_960_891_977_784_149_226_505_877_623,
s:
7_144_300_886_174_743_587_831_226_472_052_852_957_529_607_874_128_062_849_708_955_356_153_894_281_734,
to_address_hash: "0xf003a84d6890202663c0fd80954e836fcf21e004",
transaction_index: 0,
v: 27,
value: 3_200_000_000_000_000
},
%{
block_hash: nil,
block_number: nil,
from_address_hash: "0xc99f4e9cff697ca6717ad9ce8ba4a138e0e55109",
gas: 4_000_000,
gas_price: 1_000_000_000,
hash: "0xe699a58ef4986f2dbdc102acf73b35392aff9ce43fd226000526955e19c0b06e",
index: 0,
input:
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000308eb3ed9e686f6bc1fe2d8ce3fea37fb3a66a9c67b91ef15ba6bd7da0eed73288f72577edea2b7ded5855ca8a56b1e01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000051afe6c51e2175a62afbd66d293e8a7509943d5cd6f851f59923a61a186e80000000000000000000000000000000000000000000000000000000000000060a063498e8db2e75e0a193de89ad2947111d677c9501e75c34a64fcee8fe5a7c7607929fc6bce943d64f1039e1d1f325f02d1e5d71f86ca976c9ab79d19f0fd0e530a5210fbe131087ba1f1b3c92abc4a0dd7c8a47c3c276fac3e09bca964fd74",
nonce: 4658,
r:
105_551_060_165_173_654_536_466_245_809_705_255_348_773_503_447_188_823_324_699_103_004_494_755_354_009,
s:
6_578_424_718_200_222_268_891_012_570_118_685_130_111_416_504_340_507_122_286_266_818_507_627_932_617,
to_address_hash: "0xf003a84d6890202663c0fd80954e836fcf21e004",
transaction_index: 0,
v: 28,
value: 3_200_000_000_000_000
}
]} = Geth.fetch_pending_transactions(json_rpc_named_arguments)
end
end
end

@ -2464,9 +2464,10 @@ defmodule Explorer.Chain do
|> Multi.run(:set_address_verified, &set_address_verified/2)
|> Repo.transaction()
with {:ok, %{smart_contract: smart_contract}} <- insert_result do
{:ok, smart_contract}
else
case insert_result do
{:ok, %{smart_contract: smart_contract}} ->
{:ok, smart_contract}
{:error, :smart_contract, changeset, _} ->
{:error, changeset}
@ -2601,7 +2602,11 @@ defmodule Explorer.Chain do
defp page_addresses(query, %PagingOptions{key: nil}), do: query
defp page_addresses(query, %PagingOptions{key: {coin_balance, hash}}) do
where(query, [address], address.fetched_coin_balance <= ^coin_balance and address.hash > ^hash)
from(address in query,
where:
(address.fetched_coin_balance == ^coin_balance and address.hash > ^hash) or
address.fetched_coin_balance < ^coin_balance
)
end
defp page_blocks(query, %PagingOptions{key: nil}), do: query
@ -2936,9 +2941,10 @@ defmodule Explorer.Chain do
)
|> Repo.transaction()
with {:ok, %{token: token}} <- insert_result do
{:ok, token}
else
case insert_result do
{:ok, %{token: token}} ->
{:ok, token}
{:error, :token, changeset, _} ->
{:error, changeset}
end

@ -166,7 +166,7 @@ defmodule Explorer.Chain.Hash.Address do
iex> Explorer.Chain.Hash.Address.validate("0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232H")
{:error, :invalid_characters}
"""
@spec validate(String.t()) :: {:ok, String.t()} | {:error, :invalid_length | :invalid_characters, :invalid_checksum}
@spec validate(String.t()) :: {:ok, String.t()} | {:error, :invalid_length | :invalid_characters | :invalid_checksum}
def validate("0x" <> hash) do
with {:length, true} <- {:length, String.length(hash) == 40},
{:hex, true} <- {:hex, is_hex?(hash)},

@ -31,21 +31,25 @@ defmodule Explorer.Chain.Wei do
@impl Ecto.Type
def cast("0x" <> hex_wei) do
with {int_wei, ""} <- Integer.parse(hex_wei, 16) do
decimal = Decimal.new(int_wei)
{:ok, %__MODULE__{value: decimal}}
else
_ -> :error
case Integer.parse(hex_wei, 16) do
{int_wei, ""} ->
decimal = Decimal.new(int_wei)
{:ok, %__MODULE__{value: decimal}}
_ ->
:error
end
end
@impl Ecto.Type
def cast(string_wei) when is_binary(string_wei) do
with {int_wei, ""} <- Integer.parse(string_wei) do
decimal = Decimal.new(int_wei)
{:ok, %__MODULE__{value: decimal}}
else
_ -> :error
case Integer.parse(string_wei) do
{int_wei, ""} ->
decimal = Decimal.new(int_wei)
{:ok, %__MODULE__{value: decimal}}
_ ->
:error
end
end

@ -68,7 +68,7 @@ defmodule Explorer.Mixfile do
{:bypass, "~> 1.0", only: :test},
{:briefly, "~> 0.4", github: "CargoSense/briefly"},
{:comeonin, "~> 4.0"},
{:credo, "1.0.0", only: :test, runtime: false},
{:credo, "~> 1.1", only: :test, runtime: false},
# For Absinthe to load data in batches
{:dataloader, "~> 1.0.0"},
{:decimal, "~> 1.0"},

@ -152,12 +152,6 @@ defmodule Indexer.Fetcher.InternalTransaction do
unique_entries = unique_entries(entries, variant)
internal_transactions_indexed_at_blocks =
case variant do
EthereumJSONRPC.Parity -> Enum.map(unique_entries, &block_params/1)
_ -> []
end
unique_entries_count = Enum.count(unique_entries)
Logger.metadata(count: unique_entries_count)
@ -176,47 +170,7 @@ defmodule Indexer.Fetcher.InternalTransaction do
end
|> case do
{:ok, internal_transactions_params} ->
internal_transactions_params_without_failed_creations = remove_failed_creations(internal_transactions_params)
addresses_params =
Addresses.extract_addresses(%{
internal_transactions: internal_transactions_params_without_failed_creations
})
address_hash_to_block_number =
Enum.into(addresses_params, %{}, fn %{fetched_coin_balance_block_number: block_number, hash: hash} ->
{hash, block_number}
end)
with {:ok, imported} <-
Chain.import(%{
addresses: %{params: addresses_params},
internal_transactions: %{params: internal_transactions_params_without_failed_creations},
internal_transactions_indexed_at_blocks: %{
params: internal_transactions_indexed_at_blocks,
with: :number_only_changeset
},
timeout: :infinity
}) do
async_import_coin_balances(imported, %{
address_hash_to_fetched_balance_block_number: address_hash_to_block_number
})
else
{:error, step, reason, _changes_so_far} ->
Logger.error(
fn ->
[
"failed to import internal transactions for transactions: ",
inspect(reason)
]
end,
step: step,
error_count: unique_entries_count
)
# re-queue the de-duped entries
{:retry, unique_entries}
end
import_internal_transaction(internal_transactions_params, json_rpc_named_arguments, unique_entries)
{:error, reason} ->
Logger.error(fn -> ["failed to fetch internal transactions for transactions: ", inspect(reason)] end,
@ -231,6 +185,60 @@ defmodule Indexer.Fetcher.InternalTransaction do
end
end
defp import_internal_transaction(internal_transactions_params, json_rpc_named_arguments, unique_entries) do
internal_transactions_indexed_at_blocks =
case Keyword.fetch!(json_rpc_named_arguments, :variant) do
EthereumJSONRPC.Parity -> Enum.map(unique_entries, &block_params/1)
_ -> []
end
unique_entries_count = Enum.count(unique_entries)
internal_transactions_params_without_failed_creations = remove_failed_creations(internal_transactions_params)
addresses_params =
Addresses.extract_addresses(%{
internal_transactions: internal_transactions_params_without_failed_creations
})
address_hash_to_block_number =
Enum.into(addresses_params, %{}, fn %{fetched_coin_balance_block_number: block_number, hash: hash} ->
{hash, block_number}
end)
imports =
Chain.import(%{
addresses: %{params: addresses_params},
internal_transactions: %{params: internal_transactions_params_without_failed_creations},
internal_transactions_indexed_at_blocks: %{
params: internal_transactions_indexed_at_blocks,
with: :number_only_changeset
},
timeout: :infinity
})
case imports do
{:ok, imported} ->
async_import_coin_balances(imported, %{
address_hash_to_fetched_balance_block_number: address_hash_to_block_number
})
{:error, step, reason, _changes_so_far} ->
Logger.error(
fn ->
[
"failed to import internal transactions for transactions: ",
inspect(reason)
]
end,
step: step,
error_count: unique_entries_count
)
# re-queue the de-duped entries
{:retry, unique_entries}
end
end
defp unique_entries(entries, EthereumJSONRPC.Parity), do: Enum.uniq(entries)
# Protection and improved reporting for https://github.com/poanetwork/blockscout/issues/289

@ -14,7 +14,7 @@
"briefly": {:git, "https://github.com/CargoSense/briefly.git", "2526e9674a4e6996137e066a1295ea60962712b8", []},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], []},
"bypass": {:hex, :bypass, "1.0.0", "b78b3dcb832a71aca5259c1a704b2e14b55fd4e1327ff942598b4e7d1a7ad83d", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}], "hexpm"},
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], []},
"comeonin": {:hex, :comeonin, "4.1.2", "3eb5620fd8e35508991664b4c2b04dd41e52f1620b36957be837c1d7784b7592", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
"con_cache": {:hex, :con_cache, "0.13.1", "047e097ab2a8c6876e12d0c29e29a86d487b592df97b98e3e2abedad574e215d", [:mix], [], "hexpm"},
@ -22,11 +22,11 @@
"cors_plug": {:hex, :cors_plug, "2.0.0", "238ddb479f92b38f6dc1ae44b8d81f0387f9519101a6da442d543ab70ee0e482", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"cowboy": {:hex, :cowboy, "2.6.1", "f2e06f757c337b3b311f9437e6e072b678fcd71545a7b2865bdaa154d078593f", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], [], "hexpm"},
"credo": {:hex, :credo, "1.0.0", "aaa40fdd0543a0cf8080e8c5949d8c25f0a24e4fc8c1d83d06c388f5e5e0ea42", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"credo": {:hex, :credo, "1.1.2", "02b6422f3e659eb74b05aca3c20c1d8da0119a05ee82577a82e6c2938bf29f81", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"csv": {:hex, :csv, "2.1.1", "a4c1a7c30d2151b6e4976cb2f52c0a1d49ec965afb737ed84a684bc4284d1627", [:mix], [{:parallel_stream, "~> 1.0.4", [hex: :parallel_stream, optional: false]}]},
"dataloader": {:hex, :dataloader, "1.0.6", "fb724d6d3fb6acb87d27e3b32dea3a307936ad2d245faf9cf5221d1323d6a4ba", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"db_connection": {:hex, :db_connection, "2.0.3", "b4e8aa43c100e16f122ccd6798cd51c48c79fd391c39d411f42b3cd765daccb0", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
"decimal": {:hex, :decimal, "1.6.0", "bfd84d90ff966e1f5d4370bdd3943432d8f65f07d3bab48001aebd7030590dcc", [:mix], [], "hexpm"},
"decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
"decorator": {:hex, :decorator, "1.3.0", "6203dbd6e4e519a21a079e2a74e50fddaf03e80be22711b92eb4cd410173abea", [:mix], [], "hexpm"},
"deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm"},
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], []},
@ -53,10 +53,10 @@
"flow": {:hex, :flow, "0.14.3", "0d92991fe53035894d24aa8dec10dcfccf0ae00c4ed436ace3efa9813a646902", [:mix], [{:gen_stage, "~> 0.14.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm"},
"gen_stage": {:hex, :gen_stage, "0.14.1", "9d46723fda072d4f4bb31a102560013f7960f5d80ea44dcb96fd6304ed61e7a4", [:mix], [], "hexpm"},
"gettext": {:hex, :gettext, "0.16.1", "e2130b25eebcbe02bb343b119a07ae2c7e28bd4b146c4a154da2ffb2b3507af2", [:mix], [], "hexpm"},
"hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"hackney": {:hex, :hackney, "1.13.0", "24edc8cd2b28e1c652593833862435c80661834f6c9344e84b6a2255e7aeef03", [:rebar3], [{:certifi, "2.3.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.2", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], []},
"httpoison": {:hex, :httpoison, "1.5.0", "71ae9f304bdf7f00e9cd1823f275c955bdfc68282bc5eb5c85c3a9ade865d68e", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "5.1.2", "e21cb58a09f0228a9e0b95eaa1217f1bcfc31a1aaa6e1fdf2f53a33f7dbd9494", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], []},
"junit_formatter": {:hex, :junit_formatter, "3.0.0", "13950d944dbd295da7d8cc4798b8faee808a8bb9b637c88069954eac078ac9da", [:mix], [], "hexpm"},
@ -70,7 +70,7 @@
"memento": {:hex, :memento, "0.3.1", "b2909390820550d8b90b68ec96f9e15ff8a45a28b6f97fa4a62ef50e87c2f9d9", [:mix], [], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"},
"mix_erlang_tasks": {:hex, :mix_erlang_tasks, "0.1.0", "36819fec60b80689eb1380938675af215565a89320a9e29c72c70d97512e4649", [:mix], [], "hexpm"},
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
"mock": {:hex, :mock, "0.3.2", "e98e998fd76c191c7e1a9557c8617912c53df3d4a6132f561eb762b699ef59fa", [:mix], [{:meck, "~> 0.8.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
@ -80,7 +80,7 @@
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
"optimal": {:hex, :optimal, "0.3.6", "46bbf52fbbbd238cda81e02560caa84f93a53c75620f1fe19e81e4ae7b07d1dd", [:mix], [], "hexpm"},
"parallel_stream": {:hex, :parallel_stream, "1.0.6", "b967be2b23f0f6787fab7ed681b4c45a215a81481fb62b01a5b750fa8f30f76c", [:mix], []},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.2.0", "2adfa4daf80c14dc36f522cf190eb5c4ee3e28008fc6394397c16f62a26258c2", [:rebar3], [], "hexpm"},
"phoenix": {:hex, :phoenix, "1.4.0", "56fe9a809e0e735f3e3b9b31c1b749d4b436e466d8da627b8d82f90eaae714d2", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_form_awesomplete": {:hex, :phoenix_form_awesomplete, "0.1.4", "4af0603d8d41ca638e70f74d6defff331e4db106dd85f75f125579ca27bd8b64", [:mix], [{:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm"},
@ -108,11 +108,11 @@
"spandex_datadog": {:hex, :spandex_datadog, "0.4.0", "75113a73e843123074886a2e31994af07d6e0632749a8d97e9ca6157b120c287", [:mix], [{:msgpax, "~> 2.2.1", [hex: :msgpax, repo: "hexpm", optional: false]}, {:spandex, "~> 2.3", [hex: :spandex, repo: "hexpm", optional: false]}], "hexpm"},
"spandex_ecto": {:hex, :spandex_ecto, "0.4.0", "deaeaddc11a35f1c551206c53d09bb93a0da5808dbef751430e465c8c7de01d3", [:mix], [{:spandex, "~> 2.2", [hex: :spandex, repo: "hexpm", optional: false]}], "hexpm"},
"spandex_phoenix": {:hex, :spandex_phoenix, "0.3.1", "9cb9a4a9f2161f171d9df9afa1289a0d037abbbeecabae674f959b57f106f201", [:mix], [{:plug, "~> 1.3", [hex: :plug, repo: "hexpm", optional: false]}, {:spandex, "~> 2.2", [hex: :spandex, repo: "hexpm", optional: false]}], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"},
"telemetry": {:hex, :telemetry, "0.3.0", "099a7f3ce31e4780f971b4630a3c22ec66d22208bc090fe33a2a3a6a67754a73", [:rebar3], [], "hexpm"},
"timex": {:hex, :timex, "3.5.0", "b0a23167da02d0fe4f1a4e104d1f929a00d348502b52432c05de875d0b9cffa5", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
"tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
"timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
"tzdata": {:hex, :tzdata, "1.0.1", "f6027a331af7d837471248e62733c6ebee86a72e57c613aa071ebb1f750fc71a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"},
"wallaby": {:hex, :wallaby, "0.22.0", "e5d16bfa7ab23562c8a6e3b0a31445a2fd470ca622082a910114807ba823780d", [:mix], [{:httpoison, "~> 0.12 or ~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, ">= 1.4.0", [hex: :poison, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm"},
"websocket_client": {:hex, :websocket_client, "1.3.0", "2275d7daaa1cdacebf2068891c9844b15f4fdc3de3ec2602420c2fb486db59b6", [:rebar3], [], "hexpm"},
"wobserver": {:git, "https://github.com/poanetwork/wobserver.git", "13bcda30a87f4f0be1878920a79433ad831eefbe", [branch: "support-https"]},

Loading…
Cancel
Save