pull/3143/head
Victor Baranov 4 years ago
parent 46f2e8bb68
commit f359c81b48
  1. 1
      CHANGELOG.md
  2. 2
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/address_controller.ex
  3. 8
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex
  4. 6
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/token_controller.ex
  5. 2
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  6. 4
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/stats_view.ex
  7. 14
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs
  8. 12
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs
  9. 10
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/token_controller_test.exs
  10. 18
      apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs
  11. 4
      apps/block_scout_web/test/block_scout_web/schema/query/block_test.exs
  12. 18
      apps/block_scout_web/test/block_scout_web/schema/query/token_transfers_test.exs
  13. 22
      apps/block_scout_web/test/block_scout_web/schema/query/transaction_test.exs
  14. 2
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/log.ex
  15. 6
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs
  16. 1
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/receipts_test.exs
  17. 2
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket/web_socket_client_test.exs
  18. 12
      apps/explorer/package-lock.json
  19. 2
      apps/explorer/package.json
  20. 3
      apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs
  21. 5
      apps/explorer/test/explorer/smart_contract/verifier_test.exs
  22. 2
      apps/explorer/test/explorer/smart_contract/writer_test.exs
  23. 2
      mix.lock

@ -7,7 +7,6 @@
- [#3145](https://github.com/poanetwork/blockscout/pull/3145) - Pending txs per address API endpoint - [#3145](https://github.com/poanetwork/blockscout/pull/3145) - Pending txs per address API endpoint
### Fixes ### Fixes
- [#3143](https://github.com/poanetwork/blockscout/pull/3143) - Update Phoenix 1.4 -> 1.5
- [#3209](https://github.com/poanetwork/blockscout/pull/3209) - GraphQL: fix internal server error at request of internal transactions at address - [#3209](https://github.com/poanetwork/blockscout/pull/3209) - GraphQL: fix internal server error at request of internal transactions at address
- [#3207](https://github.com/poanetwork/blockscout/pull/3207) - Fix read contract bytes array type output - [#3207](https://github.com/poanetwork/blockscout/pull/3207) - Fix read contract bytes array type output
- [#3203](https://github.com/poanetwork/blockscout/pull/3203) - Improve "get mined blocks" query performance - [#3203](https://github.com/poanetwork/blockscout/pull/3203) - Improve "get mined blocks" query performance

@ -174,7 +174,7 @@ defmodule BlockScoutWeb.API.RPC.AddressController do
render(conn, :error, error: "Invalid address format") render(conn, :error, error: "Invalid address format")
{:contract_address, :error} -> {:contract_address, :error} ->
render(conn, :error, error: "Invalid contractaddress format") render(conn, :error, error: "Invalid contract address format")
{:error, :not_found} -> {:error, :not_found} ->
render(conn, :error, error: "No token transfers found", data: []) render(conn, :error, error: "No token transfers found", data: [])

@ -11,16 +11,16 @@ defmodule BlockScoutWeb.API.RPC.StatsController do
with {:contractaddress_param, {:ok, contractaddress_param}} <- fetch_contractaddress(params), with {:contractaddress_param, {:ok, contractaddress_param}} <- fetch_contractaddress(params),
{:format, {:ok, address_hash}} <- to_address_hash(contractaddress_param), {:format, {:ok, address_hash}} <- to_address_hash(contractaddress_param),
{:token, {:ok, token}} <- {:token, Chain.token_from_address_hash(address_hash)} do {:token, {:ok, token}} <- {:token, Chain.token_from_address_hash(address_hash)} do
render(conn, "tokensupply.json", token.total_supply) render(conn, "tokensupply.json", total_supply: Decimal.to_string(token.total_supply))
else else
{:contractaddress_param, :error} -> {:contractaddress_param, :error} ->
render(conn, :error, error: "Query parameter contractaddress is required") render(conn, :error, error: "Query parameter contract address is required")
{:format, :error} -> {:format, :error} ->
render(conn, :error, error: "Invalid contractaddress format") render(conn, :error, error: "Invalid contract address format")
{:token, {:error, :not_found}} -> {:token, {:error, :not_found}} ->
render(conn, :error, error: "contractaddress not found") render(conn, :error, error: "contract address not found")
end end
end end

@ -10,13 +10,13 @@ defmodule BlockScoutWeb.API.RPC.TokenController do
render(conn, "gettoken.json", %{token: token}) render(conn, "gettoken.json", %{token: token})
else else
{:contractaddress_param, :error} -> {:contractaddress_param, :error} ->
render(conn, :error, error: "Query parameter contractaddress is required") render(conn, :error, error: "Query parameter contract address is required")
{:format, :error} -> {:format, :error} ->
render(conn, :error, error: "Invalid contractaddress hash") render(conn, :error, error: "Invalid contract address hash")
{:token, {:error, :not_found}} -> {:token, {:error, :not_found}} ->
render(conn, :error, error: "contractaddress not found") render(conn, :error, error: "contract address not found")
end end
end end

@ -272,7 +272,7 @@ defmodule BlockScoutWeb.Etherscan do
@token_gettoken_example_value_error %{ @token_gettoken_example_value_error %{
"status" => "0", "status" => "0",
"message" => "Invalid contractaddress format", "message" => "Invalid contract address format",
"result" => nil "result" => nil
} }

@ -3,8 +3,8 @@ defmodule BlockScoutWeb.API.RPC.StatsView do
alias BlockScoutWeb.API.RPC.RPCView alias BlockScoutWeb.API.RPC.RPCView
def render("tokensupply.json", token_supply) do def render("tokensupply.json", %{total_supply: token_supply}) do
RPCView.render("show.json", data: Decimal.to_string(token_supply)) RPCView.render("show.json", data: token_supply)
end end
def render("ethsupplyexchange.json", %{total_supply: total_supply}) do def render("ethsupplyexchange.json", %{total_supply: total_supply}) do

@ -2269,7 +2269,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
|> get("/api", params) |> get("/api", params)
|> json_response(200) |> json_response(200)
assert response["message"] =~ "Invalid contractaddress format" assert response["message"] =~ "Invalid contract address format"
assert response["status"] == "0" assert response["status"] == "0"
assert Map.has_key?(response, "result") assert Map.has_key?(response, "result")
refute response["result"] refute response["result"]
@ -2342,7 +2342,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response)
end end
test "with contractaddress but without address", %{conn: conn} do test "with contract address but without address", %{conn: conn} do
params = %{ params = %{
"module" => "account", "module" => "account",
"action" => "tokenbalance", "action" => "tokenbalance",
@ -2361,7 +2361,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response)
end end
test "with address but without contractaddress", %{conn: conn} do test "with address but without contract address", %{conn: conn} do
params = %{ params = %{
"module" => "account", "module" => "account",
"action" => "tokenbalance", "action" => "tokenbalance",
@ -2380,7 +2380,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response)
end end
test "with an invalid contractaddress hash", %{conn: conn} do test "with an invalid contract address hash", %{conn: conn} do
params = %{ params = %{
"module" => "account", "module" => "account",
"action" => "tokenbalance", "action" => "tokenbalance",
@ -2420,7 +2420,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response)
end end
test "with a contractaddress and address that doesn't exist", %{conn: conn} do test "with a contract address and address that doesn't exist", %{conn: conn} do
params = %{ params = %{
"module" => "account", "module" => "account",
"action" => "tokenbalance", "action" => "tokenbalance",
@ -2439,7 +2439,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response)
end end
test "with contractaddress and address without row in token_balances table", %{conn: conn} do test "with contract address and address without row in token_balances table", %{conn: conn} do
token = insert(:token) token = insert(:token)
address = insert(:address) address = insert(:address)
@ -2461,7 +2461,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response)
end end
test "with contractaddress and address with existing balance in token_balances table", %{conn: conn} do test "with contract address and address with existing balance in token_balances table", %{conn: conn} do
token_balance = insert(:token_balance) token_balance = insert(:token_balance)
params = %{ params = %{

@ -19,14 +19,14 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do
|> get("/api", params) |> get("/api", params)
|> json_response(200) |> json_response(200)
assert response["message"] =~ "contractaddress is required" assert response["message"] =~ "contract address is required"
assert response["status"] == "0" assert response["status"] == "0"
assert Map.has_key?(response, "result") assert Map.has_key?(response, "result")
refute response["result"] refute response["result"]
assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response)
end end
test "with an invalid contractaddress hash", %{conn: conn} do test "with an invalid contract address hash", %{conn: conn} do
params = %{ params = %{
"module" => "stats", "module" => "stats",
"action" => "tokensupply", "action" => "tokensupply",
@ -38,14 +38,14 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do
|> get("/api", params) |> get("/api", params)
|> json_response(200) |> json_response(200)
assert response["message"] =~ "Invalid contractaddress format" assert response["message"] =~ "Invalid contract address format"
assert response["status"] == "0" assert response["status"] == "0"
assert Map.has_key?(response, "result") assert Map.has_key?(response, "result")
refute response["result"] refute response["result"]
assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response)
end end
test "with a contractaddress that doesn't exist", %{conn: conn} do test "with a contract address that doesn't exist", %{conn: conn} do
params = %{ params = %{
"module" => "stats", "module" => "stats",
"action" => "tokensupply", "action" => "tokensupply",
@ -57,14 +57,14 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do
|> get("/api", params) |> get("/api", params)
|> json_response(200) |> json_response(200)
assert response["message"] =~ "contractaddress not found" assert response["message"] =~ "contract address not found"
assert response["status"] == "0" assert response["status"] == "0"
assert Map.has_key?(response, "result") assert Map.has_key?(response, "result")
refute response["result"] refute response["result"]
assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response)
end end
test "with valid contractaddress", %{conn: conn} do test "with valid contract address", %{conn: conn} do
token = insert(:token) token = insert(:token)
params = %{ params = %{

@ -13,13 +13,13 @@ defmodule BlockScoutWeb.API.RPC.TokenControllerTest do
|> get("/api", params) |> get("/api", params)
|> json_response(200) |> json_response(200)
assert response["message"] =~ "contractaddress is required" assert response["message"] =~ "contract address is required"
assert response["status"] == "0" assert response["status"] == "0"
assert Map.has_key?(response, "result") assert Map.has_key?(response, "result")
refute response["result"] refute response["result"]
end end
test "with an invalid contractaddress hash", %{conn: conn} do test "with an invalid contract address hash", %{conn: conn} do
params = %{ params = %{
"module" => "token", "module" => "token",
"action" => "getToken", "action" => "getToken",
@ -31,13 +31,13 @@ defmodule BlockScoutWeb.API.RPC.TokenControllerTest do
|> get("/api", params) |> get("/api", params)
|> json_response(200) |> json_response(200)
assert response["message"] =~ "Invalid contractaddress hash" assert response["message"] =~ "Invalid contract address hash"
assert response["status"] == "0" assert response["status"] == "0"
assert Map.has_key?(response, "result") assert Map.has_key?(response, "result")
refute response["result"] refute response["result"]
end end
test "with a contractaddress that doesn't exist", %{conn: conn} do test "with a contract address that doesn't exist", %{conn: conn} do
params = %{ params = %{
"module" => "token", "module" => "token",
"action" => "getToken", "action" => "getToken",
@ -49,7 +49,7 @@ defmodule BlockScoutWeb.API.RPC.TokenControllerTest do
|> get("/api", params) |> get("/api", params)
|> json_response(200) |> json_response(200)
assert response["message"] =~ "contractaddress not found" assert response["message"] =~ "contract address not found"
assert response["status"] == "0" assert response["status"] == "0"
assert Map.has_key?(response, "result") assert Map.has_key?(response, "result")
refute response["result"] refute response["result"]

@ -193,7 +193,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do
"first" => 1 "first" => 1
} }
conn = get(conn, "/graphql", query: query, variables: variables) conn = post(conn, "/graphql", query: query, variables: variables)
assert json_response(conn, 200) == %{ assert json_response(conn, 200) == %{
"data" => %{ "data" => %{
@ -251,7 +251,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do
"first" => 1 "first" => 1
} }
conn = get(conn, "/graphql", query: query, variables: variables) conn = post(conn, "/graphql", query: query, variables: variables)
assert json_response(conn, 200) == %{ assert json_response(conn, 200) == %{
"data" => %{ "data" => %{
@ -304,7 +304,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do
"first" => 3 "first" => 3
} }
conn = get(conn, "/graphql", query: query, variables: variables) conn = post(conn, "/graphql", query: query, variables: variables)
%{ %{
"data" => %{ "data" => %{
@ -348,7 +348,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do
"first" => 67 "first" => 67
} }
conn = get(conn, "/graphql", query: query, variables: variables) conn = post(conn, "/graphql", query: query, variables: variables)
assert %{"errors" => [error1, error2, error3]} = json_response(conn, 200) assert %{"errors" => [error1, error2, error3]} = json_response(conn, 200)
assert error1["message"] =~ ~s(Field transactions is too complex) assert error1["message"] =~ ~s(Field transactions is too complex)
@ -403,7 +403,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do
"count" => 9 "count" => 9
} }
conn = get(conn, "/graphql", query: query, variables: variables) conn = post(conn, "/graphql", query: query, variables: variables)
%{ %{
"data" => %{ "data" => %{
@ -463,7 +463,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do
"first" => 3 "first" => 3
} }
conn = get(conn, "/graphql", query: query1, variables: variables1) conn = post(conn, "/graphql", query: query1, variables: variables1)
%{"data" => %{"address" => %{"transactions" => page1}}} = json_response(conn, 200) %{"data" => %{"address" => %{"transactions" => page1}}} = json_response(conn, 200)
@ -477,7 +477,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do
|> Map.get("cursor") |> Map.get("cursor")
query2 = """ query2 = """
query ($hash: AddressHash!, $first: Int!, $after: Int!) { query ($hash: AddressHash!, $first: Int!, $after: String!) {
address(hash: $hash) { address(hash: $hash) {
transactions(first: $first, after: $after) { transactions(first: $first, after: $after) {
page_info { page_info {
@ -502,7 +502,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do
"after" => last_cursor_page1 "after" => last_cursor_page1
} }
conn = get(conn, "/graphql", query: query2, variables: variables2) conn = post(conn, "/graphql", query: query2, variables: variables2)
%{"data" => %{"address" => %{"transactions" => page2}}} = json_response(conn, 200) %{"data" => %{"address" => %{"transactions" => page2}}} = json_response(conn, 200)
@ -521,7 +521,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do
"after" => last_cursor_page2 "after" => last_cursor_page2
} }
conn = get(conn, "/graphql", query: query2, variables: variables3) conn = post(conn, "/graphql", query: query2, variables: variables3)
%{"data" => %{"address" => %{"transactions" => page3}}} = json_response(conn, 200) %{"data" => %{"address" => %{"transactions" => page3}}} = json_response(conn, 200)

@ -27,7 +27,7 @@ defmodule BlockScoutWeb.Schema.Query.BlockTest do
variables = %{"number" => block.number} variables = %{"number" => block.number}
conn = get(conn, "/graphql", query: query, variables: variables) conn = post(conn, "/graphql", query: query, variables: variables)
assert json_response(conn, 200) == %{ assert json_response(conn, 200) == %{
"data" => %{ "data" => %{
@ -63,7 +63,7 @@ defmodule BlockScoutWeb.Schema.Query.BlockTest do
variables = %{"number" => non_existent_block_number} variables = %{"number" => non_existent_block_number}
conn = get(conn, "/graphql", query: query, variables: variables) conn = post(conn, "/graphql", query: query, variables: variables)
assert %{"errors" => [error]} = json_response(conn, 200) assert %{"errors" => [error]} = json_response(conn, 200)
assert error["message"] =~ ~s(Block number #{non_existent_block_number} was not found) assert error["message"] =~ ~s(Block number #{non_existent_block_number} was not found)

@ -31,7 +31,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do
"first" => 1 "first" => 1
} }
conn = get(conn, "/graphql", query: query, variables: variables) conn = post(conn, "/graphql", query: query, variables: variables)
assert json_response(conn, 200) == %{ assert json_response(conn, 200) == %{
"data" => %{ "data" => %{
@ -82,7 +82,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do
"first" => 10 "first" => 10
} }
conn = get(conn, "/graphql", query: query, variables: variables) conn = post(conn, "/graphql", query: query, variables: variables)
assert json_response(conn, 200) == %{ assert json_response(conn, 200) == %{
"data" => %{ "data" => %{
@ -117,7 +117,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do
response1 = response1 =
conn conn
|> get("/graphql", query: query1, variables: variables1) |> post("/graphql", query: query1, variables: variables1)
|> json_response(200) |> json_response(200)
%{"errors" => [response1_error1, response1_error2]} = response1 %{"errors" => [response1_error1, response1_error2]} = response1
@ -146,7 +146,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do
response2 = response2 =
conn conn
|> get("/graphql", query: query2, variables: variables2) |> post("/graphql", query: query2, variables: variables2)
|> json_response(200) |> json_response(200)
%{"errors" => [response2_error1, response2_error2]} = response2 %{"errors" => [response2_error1, response2_error2]} = response2
@ -208,7 +208,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do
[token_transfer] = [token_transfer] =
conn conn
|> get("/graphql", query: query, variables: variables) |> post("/graphql", query: query, variables: variables)
|> json_response(200) |> json_response(200)
|> get_in(["data", "token_transfers", "edges"]) |> get_in(["data", "token_transfers", "edges"])
@ -260,7 +260,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do
"first" => 1 "first" => 1
} }
conn = get(conn, "/graphql", query: query1, variables: variables1) conn = post(conn, "/graphql", query: query1, variables: variables1)
%{"data" => %{"token_transfers" => page1}} = json_response(conn, 200) %{"data" => %{"token_transfers" => page1}} = json_response(conn, 200)
@ -274,7 +274,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do
|> Map.get("cursor") |> Map.get("cursor")
query2 = """ query2 = """
query ($token_contract_address_hash: AddressHash!, $first: Int!, $after: ID!) { query ($token_contract_address_hash: AddressHash!, $first: Int!, $after: String!) {
token_transfers(token_contract_address_hash: $token_contract_address_hash, first: $first, after: $after) { token_transfers(token_contract_address_hash: $token_contract_address_hash, first: $first, after: $after) {
page_info { page_info {
has_next_page has_next_page
@ -296,7 +296,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do
"after" => last_cursor_page1 "after" => last_cursor_page1
} }
conn = get(conn, "/graphql", query: query2, variables: variables2) conn = post(conn, "/graphql", query: query2, variables: variables2)
%{"data" => %{"token_transfers" => page2}} = json_response(conn, 200) %{"data" => %{"token_transfers" => page2}} = json_response(conn, 200)
@ -315,7 +315,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do
"after" => last_cursor_page2 "after" => last_cursor_page2
} }
conn = get(conn, "/graphql", query: query2, variables: variables3) conn = post(conn, "/graphql", query: query2, variables: variables3)
%{"data" => %{"token_transfers" => page3}} = json_response(conn, 200) %{"data" => %{"token_transfers" => page3}} = json_response(conn, 200)

@ -180,7 +180,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
"first" => 1 "first" => 1
} }
conn = get(conn, "/graphql", query: query, variables: variables) conn = post(conn, "/graphql", query: query, variables: variables)
assert json_response(conn, 200) == %{ assert json_response(conn, 200) == %{
"data" => %{ "data" => %{
@ -247,7 +247,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
"first" => 1 "first" => 1
} }
conn = get(conn, "/graphql", query: query, variables: variables) conn = post(conn, "/graphql", query: query, variables: variables)
assert json_response(conn, 200) == %{ assert json_response(conn, 200) == %{
"data" => %{ "data" => %{
@ -306,7 +306,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
response = response =
conn conn
|> get("/graphql", query: query, variables: variables) |> post("/graphql", query: query, variables: variables)
|> json_response(200) |> json_response(200)
internal_transactions = get_in(response, ["data", "transaction", "internal_transactions", "edges"]) internal_transactions = get_in(response, ["data", "transaction", "internal_transactions", "edges"])
@ -341,7 +341,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
response1 = response1 =
conn conn
|> get("/graphql", query: query1, variables: variables1) |> post("/graphql", query: query1, variables: variables1)
|> json_response(200) |> json_response(200)
assert %{"errors" => [error1, error2, error3]} = response1 assert %{"errors" => [error1, error2, error3]} = response1
@ -372,7 +372,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
response2 = response2 =
conn conn
|> get("/graphql", query: query2, variables: variables2) |> post("/graphql", query: query2, variables: variables2)
|> json_response(200) |> json_response(200)
assert %{"errors" => [error1, error2, error3]} = response2 assert %{"errors" => [error1, error2, error3]} = response2
@ -435,7 +435,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
[internal_transaction] = [internal_transaction] =
conn conn
|> get("/graphql", query: query, variables: variables) |> post("/graphql", query: query, variables: variables)
|> json_response(200) |> json_response(200)
|> get_in(["data", "transaction", "internal_transactions", "edges"]) |> get_in(["data", "transaction", "internal_transactions", "edges"])
@ -455,7 +455,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
end end
query1 = """ query1 = """
query ($hash: AddressHash!, $first: Int!) { query ($hash: FullHash!, $first: Int!) {
transaction(hash: $hash) { transaction(hash: $hash) {
internal_transactions(first: $first) { internal_transactions(first: $first) {
page_info { page_info {
@ -479,7 +479,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
"first" => 2 "first" => 2
} }
conn = get(conn, "/graphql", query: query1, variables: variables1) conn = post(conn, "/graphql", query: query1, variables: variables1)
%{"data" => %{"transaction" => %{"internal_transactions" => page1}}} = json_response(conn, 200) %{"data" => %{"transaction" => %{"internal_transactions" => page1}}} = json_response(conn, 200)
@ -493,7 +493,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
|> Map.get("cursor") |> Map.get("cursor")
query2 = """ query2 = """
query ($hash: AddressHash!, $first: Int!, $after: ID!) { query ($hash: FullHash!, $first: Int!, $after: String!) {
transaction(hash: $hash) { transaction(hash: $hash) {
internal_transactions(first: $first, after: $after) { internal_transactions(first: $first, after: $after) {
page_info { page_info {
@ -520,7 +520,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
page2 = page2 =
conn conn
|> get("/graphql", query: query2, variables: variables2) |> post("/graphql", query: query2, variables: variables2)
|> json_response(200) |> json_response(200)
|> get_in(["data", "transaction", "internal_transactions"]) |> get_in(["data", "transaction", "internal_transactions"])
@ -541,7 +541,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do
page3 = page3 =
conn conn
|> get("/graphql", query: query2, variables: variables3) |> post("/graphql", query: query2, variables: variables3)
|> json_response(200) |> json_response(200)
|> get_in(["data", "transaction", "internal_transactions"]) |> get_in(["data", "transaction", "internal_transactions"])

@ -48,7 +48,6 @@ defmodule EthereumJSONRPC.Log do
second_topic: nil, second_topic: nil,
third_topic: nil, third_topic: nil,
transaction_hash: "0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5", transaction_hash: "0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5",
block_hash: "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
type: "mined" type: "mined"
} }
@ -73,7 +72,6 @@ defmodule EthereumJSONRPC.Log do
address_hash: "0xda8b3276cde6d768a44b9dac659faa339a41ac55", address_hash: "0xda8b3276cde6d768a44b9dac659faa339a41ac55",
block_hash: "0x0b89f7f894f5d8ba941e16b61490e999a0fcaaf92dfcc70aee2ac5ddb5f243e1", block_hash: "0x0b89f7f894f5d8ba941e16b61490e999a0fcaaf92dfcc70aee2ac5ddb5f243e1",
block_number: 4448, block_number: 4448,
block_hash: "0x0b89f7f894f5d8ba941e16b61490e999a0fcaaf92dfcc70aee2ac5ddb5f243e1",
data: "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563", data: "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563",
first_topic: "0xadc1e8a294f8415511303acc4a8c0c5906c7eb0bf2a71043d7f4b03b46a39130", first_topic: "0xadc1e8a294f8415511303acc4a8c0c5906c7eb0bf2a71043d7f4b03b46a39130",
fourth_topic: nil, fourth_topic: nil,

@ -157,7 +157,7 @@ defmodule EthereumJSONRPC.GethTest do
gas: 4_000_000, gas: 4_000_000,
gas_price: 1_000_000_000, gas_price: 1_000_000_000,
hash: "0x2b8cfd76a31b942e51b6265c791c860e2840b11f8c2fcfa1c9dfe53dea4c3102", hash: "0x2b8cfd76a31b942e51b6265c791c860e2840b11f8c2fcfa1c9dfe53dea4c3102",
index: 0, index: nil,
input: input:
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030af6932dec7c4eaf4b966059e74cc7a1767ba93e62f2d83a7dba5bb785b6efd25e8ab7d2e8798e7ecc27df96380d77a0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000b29e5229b86fbb3a8e45e406b64226c3d49389804a6f7449325fae232d6623000000000000000000000000000000000000000000000000000000000000006097e4c1ed443f430b1d8ad66e565a960fade76e3e177b4120186bdad2fcfa43e134de3abdc0272c9433af94833fec73260c261cf41422e83d958787b62144478bc44ab84d1ddba7a462d355057f3be8ab914a195ac1a637c4fb8503c441dadb45", "0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030af6932dec7c4eaf4b966059e74cc7a1767ba93e62f2d83a7dba5bb785b6efd25e8ab7d2e8798e7ecc27df96380d77a0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000b29e5229b86fbb3a8e45e406b64226c3d49389804a6f7449325fae232d6623000000000000000000000000000000000000000000000000000000000000006097e4c1ed443f430b1d8ad66e565a960fade76e3e177b4120186bdad2fcfa43e134de3abdc0272c9433af94833fec73260c261cf41422e83d958787b62144478bc44ab84d1ddba7a462d355057f3be8ab914a195ac1a637c4fb8503c441dadb45",
nonce: 4656, nonce: 4656,
@ -177,7 +177,7 @@ defmodule EthereumJSONRPC.GethTest do
gas: 4_000_000, gas: 4_000_000,
gas_price: 1_000_000_000, gas_price: 1_000_000_000,
hash: "0x7c3ea924740e996bf552a8dded903ba4258b69d30bf5e6dca6ec86ebc60b8151", hash: "0x7c3ea924740e996bf552a8dded903ba4258b69d30bf5e6dca6ec86ebc60b8151",
index: 0, index: nil,
input: input:
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030a25723bca32f88a73abc7eb153cee248effd563d87efe12e08e8a33f74047afc28c30ab9c74bddeb6f0558628b8bf200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020009c56025b2796cdc72f91836278a471590b774462adddd1c87a0b6f84b787990000000000000000000000000000000000000000000000000000000000000060aa53b46c8b57aed7c4c0fdf3f650ec3bb330591929bc813610656882e3203157c22b50d0d0b0316a8712c00fe4f0e0c509613114f5d24c0419a4e8188f2489678b05dccf72a67957785e8e250092c8787f049f7e20b1414a633595a56c98ff82", "0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000030a25723bca32f88a73abc7eb153cee248effd563d87efe12e08e8a33f74047afc28c30ab9c74bddeb6f0558628b8bf200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020009c56025b2796cdc72f91836278a471590b774462adddd1c87a0b6f84b787990000000000000000000000000000000000000000000000000000000000000060aa53b46c8b57aed7c4c0fdf3f650ec3bb330591929bc813610656882e3203157c22b50d0d0b0316a8712c00fe4f0e0c509613114f5d24c0419a4e8188f2489678b05dccf72a67957785e8e250092c8787f049f7e20b1414a633595a56c98ff82",
nonce: 4657, nonce: 4657,
@ -197,7 +197,7 @@ defmodule EthereumJSONRPC.GethTest do
gas: 4_000_000, gas: 4_000_000,
gas_price: 1_000_000_000, gas_price: 1_000_000_000,
hash: "0xe699a58ef4986f2dbdc102acf73b35392aff9ce43fd226000526955e19c0b06e", hash: "0xe699a58ef4986f2dbdc102acf73b35392aff9ce43fd226000526955e19c0b06e",
index: 0, index: nil,
input: input:
"0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000308eb3ed9e686f6bc1fe2d8ce3fea37fb3a66a9c67b91ef15ba6bd7da0eed73288f72577edea2b7ded5855ca8a56b1e01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000051afe6c51e2175a62afbd66d293e8a7509943d5cd6f851f59923a61a186e80000000000000000000000000000000000000000000000000000000000000060a063498e8db2e75e0a193de89ad2947111d677c9501e75c34a64fcee8fe5a7c7607929fc6bce943d64f1039e1d1f325f02d1e5d71f86ca976c9ab79d19f0fd0e530a5210fbe131087ba1f1b3c92abc4a0dd7c8a47c3c276fac3e09bca964fd74", "0xc47e300d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000308eb3ed9e686f6bc1fe2d8ce3fea37fb3a66a9c67b91ef15ba6bd7da0eed73288f72577edea2b7ded5855ca8a56b1e01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000051afe6c51e2175a62afbd66d293e8a7509943d5cd6f851f59923a61a186e80000000000000000000000000000000000000000000000000000000000000060a063498e8db2e75e0a193de89ad2947111d677c9501e75c34a64fcee8fe5a7c7607929fc6bce943d64f1039e1d1f325f02d1e5d71f86ca976c9ab79d19f0fd0e530a5210fbe131087ba1f1b3c92abc4a0dd7c8a47c3c276fac3e09bca964fd74",
nonce: 4658, nonce: 4658,

@ -84,7 +84,6 @@ defmodule EthereumJSONRPC.ReceiptsTest do
"logs" => [ "logs" => [
%{ %{
"address" => address_hash, "address" => address_hash,
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => integer_to_quantity(block_number), "blockNumber" => integer_to_quantity(block_number),
"blockHash" => nil, "blockHash" => nil,
"data" => data, "data" => data,

@ -81,7 +81,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClientTest do
describe "reconnect" do describe "reconnect" do
setup do setup do
dispatch = :cowboy_router.compile([{:_, [{"/websocket", EthereumJSONRPC.WebSocket.Cowboy.WebSocketHandler, []}]}]) dispatch = :cowboy_router.compile([{:_, [{"/websocket", EthereumJSONRPC.WebSocket.Cowboy.WebSocketHandler, []}]}])
{:ok, _} = :cowboy.start_http(EthereumJSONRPC.WebSocket.Cowboy, 100, [], env: [dispatch: dispatch]) {:ok, _} = :cowboy.start_tls(EthereumJSONRPC.WebSocket.Cowboy, [], env: [dispatch: dispatch])
on_exit(fn -> on_exit(fn ->
:ranch.stop_listener(EthereumJSONRPC.WebSocket.Cowboy) :ranch.stop_listener(EthereumJSONRPC.WebSocket.Cowboy)

@ -77,9 +77,9 @@
} }
}, },
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"js-sha3": { "js-sha3": {
"version": "0.8.0", "version": "0.8.0",
@ -152,9 +152,9 @@
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}, },
"solc": { "solc": {
"version": "0.6.7", "version": "0.6.12",
"resolved": "https://registry.npmjs.org/solc/-/solc-0.6.7.tgz", "resolved": "https://registry.npmjs.org/solc/-/solc-0.6.12.tgz",
"integrity": "sha512-a3iocjS1yGzw3Wy7jkqSLX3Vg1lMDCyoKZoVfpOagRGWkh37f11BrcUDO8f73rjdpw2WUBSLJtTQ26i52/0JOg==", "integrity": "sha512-Lm0Ql2G9Qc7yPP2Ba+WNmzw2jwsrd3u4PobHYlSOxaut3TtUbj9+5ZrT6f4DUpNPEoBaFUOEg9Op9C0mk7ge9g==",
"requires": { "requires": {
"command-exists": "^1.2.8", "command-exists": "^1.2.8",
"commander": "3.0.2", "commander": "3.0.2",

@ -13,6 +13,6 @@
}, },
"scripts": {}, "scripts": {},
"dependencies": { "dependencies": {
"solc": "^0.6.7" "solc": "^0.6.12"
} }
} }

@ -191,7 +191,8 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do
optimize: contract_code_info.optimized optimize: contract_code_info.optimized
) )
assert {:error, :compilation} = response assert {:error, :compilation, "Expected pragma, import directive or contract/interface/library definition."} =
response
end end
test "returns constructor in abi" do test "returns constructor in abi" do

@ -288,7 +288,7 @@ defmodule Explorer.SmartContract.VerifierTest do
params = %{ params = %{
"contract_source_code" => different_code, "contract_source_code" => different_code,
"compiler_version" => contract_code_info.version, "compiler_version" => contract_code_info.version,
"evm_version" => "istanbul", "evm_version" => "default",
"name" => contract_code_info.name, "name" => contract_code_info.name,
"optimization" => contract_code_info.optimized "optimization" => contract_code_info.optimized
} }
@ -338,7 +338,8 @@ defmodule Explorer.SmartContract.VerifierTest do
"optimization" => contract_code_info.optimized "optimization" => contract_code_info.optimized
} }
assert {:error, :compilation} = Verifier.evaluate_authenticity(contract_address.hash, params) assert {:error, :compilation, "Function, variable, struct or modifier declaration expected."} =
Verifier.evaluate_authenticity(contract_address.hash, params)
end end
end end

@ -281,7 +281,7 @@ defmodule Explorer.SmartContract.WriterTest do
describe "write_functions_proxy/1" do describe "write_functions_proxy/1" do
test "fetches the smart contract proxy write functions" do test "fetches the smart contract proxy write functions" do
proxy_smart_contract = _proxy_smart_contract =
insert(:smart_contract, insert(:smart_contract,
abi: @abi abi: @abi
) )

@ -85,7 +85,7 @@
"phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [: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", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [: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", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"},
"phoenix_form_awesomplete": {:hex, :phoenix_form_awesomplete, "0.1.5", "d09aade160b584e3428e1e095645482396f17bddda4f566f1118f12d2598d11c", [:mix], [{:phoenix_html, "~> 2.10", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "acef2dbc638b5bcad92c11e41eb2b55d71f2596741a2f936717b8472196456ec"}, "phoenix_form_awesomplete": {:hex, :phoenix_form_awesomplete, "0.1.5", "d09aade160b584e3428e1e095645482396f17bddda4f566f1118f12d2598d11c", [:mix], [{:phoenix_html, "~> 2.10", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "acef2dbc638b5bcad92c11e41eb2b55d71f2596741a2f936717b8472196456ec"},
"phoenix_html": {:hex, :phoenix_html, "2.14.2", "b8a3899a72050f3f48a36430da507dd99caf0ac2d06c77529b1646964f3d563e", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "58061c8dfd25da5df1ea0ca47c972f161beb6c875cd293917045b92ffe1bf617"}, "phoenix_html": {:hex, :phoenix_html, "2.14.2", "b8a3899a72050f3f48a36430da507dd99caf0ac2d06c77529b1646964f3d563e", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "58061c8dfd25da5df1ea0ca47c972f161beb6c875cd293917045b92ffe1bf617"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.2", "38d94c30df5e2ef11000697a4fbe2b38d0fbf79239d492ff1be87bbc33bc3a84", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "a3dec3d28ddb5476c96a7c8a38ea8437923408bc88da43e5c45d97037b396280"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.4", "940c0344b1d66a2e46eef02af3a70e0c5bb45a4db0bf47917add271b76cd3914", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "38f9308357dea4cc77f247e216da99fcb0224e05ada1469167520bed4cb8cccd"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
"plug": {:hex, :plug, "1.10.3", "c9cebe917637d8db0e759039cc106adca069874e1a9034fd6e3fdd427fd3c283", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "01f9037a2a1de1d633b5a881101e6a444bcabb1d386ca1e00bb273a1f1d9d939"}, "plug": {:hex, :plug, "1.10.3", "c9cebe917637d8db0e759039cc106adca069874e1a9034fd6e3fdd427fd3c283", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "01f9037a2a1de1d633b5a881101e6a444bcabb1d386ca1e00bb273a1f1d9d939"},
"plug_cowboy": {:hex, :plug_cowboy, "2.3.0", "149a50e05cb73c12aad6506a371cd75750c0b19a32f81866e1a323dda9e0e99d", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bc595a1870cef13f9c1e03df56d96804db7f702175e4ccacdb8fc75c02a7b97e"}, "plug_cowboy": {:hex, :plug_cowboy, "2.3.0", "149a50e05cb73c12aad6506a371cd75750c0b19a32f81866e1a323dda9e0e99d", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bc595a1870cef13f9c1e03df56d96804db7f702175e4ccacdb8fc75c02a7b97e"},

Loading…
Cancel
Save