Merge pull request #6187 from blockscout/vb-filter-by-time-created-listcontracts-api

Filter by created time of verified contracts in listcontracts API endpoint
pull/6185/head
Victor Baranov 2 years ago committed by GitHub
commit 0733d20287
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 55
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/address_controller.ex
  3. 16
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
  4. 30
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  5. 76
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs
  6. 209
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs
  7. 29
      apps/explorer/lib/explorer/etherscan/contracts.ex

@ -2,6 +2,7 @@
### Features ### Features
- [#6187](https://github.com/blockscout/blockscout/pull/6187) - Filter by created time of verified contracts in listcontracts API endpoint
- [#6092](https://github.com/blockscout/blockscout/pull/6092) - Blockscout Account functionality - [#6092](https://github.com/blockscout/blockscout/pull/6092) - Blockscout Account functionality
- [#6073](https://github.com/blockscout/blockscout/pull/6073) - Add vyper support for rust verifier microservice integration - [#6073](https://github.com/blockscout/blockscout/pull/6073) - Add vyper support for rust verifier microservice integration
- [#6111](https://github.com/blockscout/blockscout/pull/6111) - Add Prometheus metrics to indexer - [#6111](https://github.com/blockscout/blockscout/pull/6111) - Add Prometheus metrics to indexer

@ -249,11 +249,11 @@ defmodule BlockScoutWeb.API.RPC.AddressController do
%{} %{}
|> put_order_by_direction(params) |> put_order_by_direction(params)
|> Helpers.put_pagination_options(params) |> Helpers.put_pagination_options(params)
|> put_start_block(params) |> put_block(params, "start_block")
|> put_end_block(params) |> put_block(params, "end_block")
|> put_filter_by(params) |> put_filter_by(params)
|> put_start_timestamp(params) |> put_timestamp(params, "start_timestamp")
|> put_end_timestamp(params) |> put_timestamp(params, "end_timestamp")
end end
@doc """ @doc """
@ -427,52 +427,39 @@ defmodule BlockScoutWeb.API.RPC.AddressController do
end end
end end
defp put_start_block(options, params) do # sobelow_skip ["DOS.StringToAtom"]
with %{"startblock" => startblock_param} <- params, defp put_block(options, params, key) do
{start_block, ""} <- Integer.parse(startblock_param) do with %{^key => block_param} <- params,
Map.put(options, :start_block, start_block) {block_number, ""} <- Integer.parse(block_param) do
else Map.put(options, String.to_atom(key), block_number)
_ ->
options
end
end
defp put_end_block(options, params) do
with %{"endblock" => endblock_param} <- params,
{end_block, ""} <- Integer.parse(endblock_param) do
Map.put(options, :end_block, end_block)
else else
_ -> _ ->
options options
end end
end end
# sobelow_skip ["DOS.StringToAtom"]
defp put_filter_by(options, params) do defp put_filter_by(options, params) do
case params do case params do
%{"filterby" => filter_by} when filter_by in ["from", "to"] -> %{"filter_by" => filter_by} when filter_by in ["from", "to"] ->
Map.put(options, :filter_by, filter_by) Map.put(options, String.to_atom("filter_by"), filter_by)
_ -> _ ->
options options
end end
end end
defp put_start_timestamp(options, params) do def put_timestamp({:ok, options}, params, timestamp_param_key) do
with %{"starttimestamp" => starttimestamp_param} <- params, options = put_timestamp(options, params, timestamp_param_key)
{unix_timestamp, ""} <- Integer.parse(starttimestamp_param), {:ok, options}
{:ok, start_timestamp} <- DateTime.from_unix(unix_timestamp) do
Map.put(options, :start_timestamp, start_timestamp)
else
_ ->
options
end
end end
defp put_end_timestamp(options, params) do # sobelow_skip ["DOS.StringToAtom"]
with %{"endtimestamp" => endtimestamp_param} <- params, def put_timestamp(options, params, timestamp_param_key) do
{unix_timestamp, ""} <- Integer.parse(endtimestamp_param), with %{^timestamp_param_key => timestamp_param} <- params,
{:ok, end_timestamp} <- DateTime.from_unix(unix_timestamp) do {unix_timestamp, ""} <- Integer.parse(timestamp_param),
Map.put(options, :end_timestamp, end_timestamp) {:ok, timestamp} <- DateTime.from_unix(unix_timestamp) do
Map.put(options, String.to_atom(timestamp_param_key), timestamp)
else else
_ -> _ ->
options options

@ -4,7 +4,7 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
require Logger require Logger
alias BlockScoutWeb.AddressContractVerificationController, as: VerificationController alias BlockScoutWeb.AddressContractVerificationController, as: VerificationController
alias BlockScoutWeb.API.RPC.Helpers alias BlockScoutWeb.API.RPC.{AddressController, Helpers}
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.Events.Publisher, as: EventsPublisher alias Explorer.Chain.Events.Publisher, as: EventsPublisher
alias Explorer.Chain.{Address, Hash, SmartContract} alias Explorer.Chain.{Address, Hash, SmartContract}
@ -420,7 +420,7 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
case Map.get(opts, :filter) do case Map.get(opts, :filter) do
:verified -> :verified ->
Contracts.list_verified_contracts(page_size, offset) Contracts.list_verified_contracts(page_size, offset, opts)
:decompiled -> :decompiled ->
not_decompiled_with_version = Map.get(opts, :not_decompiled_with_version) not_decompiled_with_version = Map.get(opts, :not_decompiled_with_version)
@ -443,7 +443,9 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
defp add_filters(options, params) do defp add_filters(options, params) do
options options
|> add_filter(params) |> add_filter(params)
|> add_not_decompiled_with_version(params) |> add_param(params, :not_decompiled_with_version)
|> AddressController.put_timestamp(params, "verified_at_start_timestamp")
|> AddressController.put_timestamp(params, "verified_at_end_timestamp")
end end
defp add_filter(options, params) do defp add_filter(options, params) do
@ -456,14 +458,14 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
end end
end end
defp add_not_decompiled_with_version({:ok, options}, params) do defp add_param({:ok, options}, params, key) do
case Map.fetch(params, "not_decompiled_with_version") do case Map.fetch(params, Atom.to_string(key)) do
{:ok, value} -> {:ok, Map.put(options, :not_decompiled_with_version, value)} {:ok, value} -> {:ok, Map.put(options, key, value)}
:error -> {:ok, options} :error -> {:ok, options}
end end
end end
defp add_not_decompiled_with_version(options, _params) do defp add_param(options, _params, _key) do
options options
end end

@ -1406,12 +1406,12 @@ defmodule BlockScoutWeb.Etherscan do
"A string representing the order by block number direction. Defaults to descending order. Available values: asc, desc" "A string representing the order by block number direction. Defaults to descending order. Available values: asc, desc"
}, },
%{ %{
key: "startblock", key: "start_block",
type: "integer", type: "integer",
description: "A nonnegative integer that represents the starting block number." description: "A nonnegative integer that represents the starting block number."
}, },
%{ %{
key: "endblock", key: "end_block",
type: "integer", type: "integer",
description: "A nonnegative integer that represents the ending block number." description: "A nonnegative integer that represents the ending block number."
}, },
@ -1428,7 +1428,7 @@ defmodule BlockScoutWeb.Etherscan do
"A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction." "A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction."
}, },
%{ %{
key: "filterby", key: "filter_by",
type: "string", type: "string",
description: """ description: """
A string representing the field to filter by. If none is given A string representing the field to filter by. If none is given
@ -1437,12 +1437,12 @@ defmodule BlockScoutWeb.Etherscan do
""" """
}, },
%{ %{
key: "starttimestamp", key: "start_timestamp",
type: "unix timestamp", type: "unix timestamp",
description: "Represents the starting block timestamp." description: "Represents the starting block timestamp."
}, },
%{ %{
key: "endtimestamp", key: "end_timestamp",
type: "unix timestamp", type: "unix timestamp",
description: "Represents the ending block timestamp." description: "Represents the ending block timestamp."
} }
@ -1499,13 +1499,13 @@ defmodule BlockScoutWeb.Etherscan do
"A string representing the order by block number direction. Defaults to ascending order. Available values: asc, desc. WARNING: Only available if 'address' is provided." "A string representing the order by block number direction. Defaults to ascending order. Available values: asc, desc. WARNING: Only available if 'address' is provided."
}, },
%{ %{
key: "startblock", key: "start_block",
type: "integer", type: "integer",
description: description:
"A nonnegative integer that represents the starting block number. WARNING: Only available if 'address' is provided." "A nonnegative integer that represents the starting block number. WARNING: Only available if 'address' is provided."
}, },
%{ %{
key: "endblock", key: "end_block",
type: "integer", type: "integer",
description: description:
"A nonnegative integer that represents the ending block number. WARNING: Only available if 'address' is provided." "A nonnegative integer that represents the ending block number. WARNING: Only available if 'address' is provided."
@ -1574,12 +1574,12 @@ defmodule BlockScoutWeb.Etherscan do
"A string representing the order by block number direction. Defaults to ascending order. Available values: asc, desc" "A string representing the order by block number direction. Defaults to ascending order. Available values: asc, desc"
}, },
%{ %{
key: "startblock", key: "start_block",
type: "integer", type: "integer",
description: "A nonnegative integer that represents the starting block number." description: "A nonnegative integer that represents the starting block number."
}, },
%{ %{
key: "endblock", key: "end_block",
type: "integer", type: "integer",
description: "A nonnegative integer that represents the ending block number." description: "A nonnegative integer that represents the ending block number."
}, },
@ -2316,6 +2316,18 @@ defmodule BlockScoutWeb.Etherscan do
type: "string", type: "string",
description: description:
"Ensures that none of the returned contracts were decompiled with the provided version. Ignored unless filtering for decompiled contracts." "Ensures that none of the returned contracts were decompiled with the provided version. Ignored unless filtering for decompiled contracts."
},
%{
key: "verified_at_start_timestamp",
type: "unix timestamp",
description:
"Represents the starting timestamp when contracts verified. Taking into account only with `verified` filter."
},
%{
key: "verified_at_end_timestamp",
type: "unix timestamp",
description:
"Represents the ending timestamp when contracts verified. Taking into account only with `verified` filter."
} }
], ],
responses: [ responses: [

@ -1090,7 +1090,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end end
test "with startblock and endblock params", %{conn: conn} do test "with start_block and end_block params", %{conn: conn} do
blocks = [_, second_block, third_block, _] = insert_list(4, :block) blocks = [_, second_block, third_block, _] = insert_list(4, :block)
address = insert(:address) address = insert(:address)
@ -1104,8 +1104,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
"module" => "account", "module" => "account",
"action" => "txlist", "action" => "txlist",
"address" => "#{address.hash}", "address" => "#{address.hash}",
"startblock" => "#{second_block.number}", "start_block" => "#{second_block.number}",
"endblock" => "#{third_block.number}" "end_block" => "#{third_block.number}"
} }
expected_block_numbers = [ expected_block_numbers = [
@ -1129,7 +1129,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end end
test "with startblock but without endblock", %{conn: conn} do test "with start_block but without end_block", %{conn: conn} do
blocks = [_, _, third_block, fourth_block] = insert_list(4, :block) blocks = [_, _, third_block, fourth_block] = insert_list(4, :block)
address = insert(:address) address = insert(:address)
@ -1143,7 +1143,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
"module" => "account", "module" => "account",
"action" => "txlist", "action" => "txlist",
"address" => "#{address.hash}", "address" => "#{address.hash}",
"startblock" => "#{third_block.number}" "start_block" => "#{third_block.number}"
} }
expected_block_numbers = [ expected_block_numbers = [
@ -1167,7 +1167,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end end
test "with endblock but without startblock", %{conn: conn} do test "with end_block but without start_block", %{conn: conn} do
blocks = [first_block, second_block, _, _] = insert_list(4, :block) blocks = [first_block, second_block, _, _] = insert_list(4, :block)
address = insert(:address) address = insert(:address)
@ -1181,7 +1181,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
"module" => "account", "module" => "account",
"action" => "txlist", "action" => "txlist",
"address" => "#{address.hash}", "address" => "#{address.hash}",
"endblock" => "#{second_block.number}" "end_block" => "#{second_block.number}"
} }
expected_block_numbers = [ expected_block_numbers = [
@ -1205,7 +1205,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end end
test "ignores invalid startblock and endblock", %{conn: conn} do test "ignores invalid start_block and end_block", %{conn: conn} do
blocks = [_, _, _, _] = insert_list(4, :block) blocks = [_, _, _, _] = insert_list(4, :block)
address = insert(:address) address = insert(:address)
@ -1219,8 +1219,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
"module" => "account", "module" => "account",
"action" => "txlist", "action" => "txlist",
"address" => "#{address.hash}", "address" => "#{address.hash}",
"startblock" => "invalidstart", "start_block" => "invalidstart",
"endblock" => "invalidend" "end_block" => "invalidend"
} }
assert response = assert response =
@ -1234,7 +1234,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end end
test "with starttimestamp and endtimestamp params", %{conn: conn} do test "with start_timestamp and end_timestamp params", %{conn: conn} do
now = Timex.now() now = Timex.now()
timestamp1 = Timex.shift(now, hours: -6) timestamp1 = Timex.shift(now, hours: -6)
timestamp2 = Timex.shift(now, hours: -3) timestamp2 = Timex.shift(now, hours: -3)
@ -1257,8 +1257,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
"module" => "account", "module" => "account",
"action" => "txlist", "action" => "txlist",
"address" => "#{address.hash}", "address" => "#{address.hash}",
"starttimestamp" => "#{start_timestamp}", "start_timestamp" => "#{start_timestamp}",
"endtimestamp" => "#{end_timestamp}" "end_timestamp" => "#{end_timestamp}"
} }
expected_block_numbers = [ expected_block_numbers = [
@ -1282,7 +1282,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end end
test "with starttimestamp but without endtimestamp", %{conn: conn} do test "with start_timestamp but without end_timestamp", %{conn: conn} do
now = Timex.now() now = Timex.now()
timestamp1 = Timex.shift(now, hours: -6) timestamp1 = Timex.shift(now, hours: -6)
timestamp2 = Timex.shift(now, hours: -3) timestamp2 = Timex.shift(now, hours: -3)
@ -1304,7 +1304,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
"module" => "account", "module" => "account",
"action" => "txlist", "action" => "txlist",
"address" => "#{address.hash}", "address" => "#{address.hash}",
"starttimestamp" => "#{start_timestamp}" "start_timestamp" => "#{start_timestamp}"
} }
expected_block_numbers = [ expected_block_numbers = [
@ -1330,7 +1330,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end end
test "with endtimestamp but without starttimestamp", %{conn: conn} do test "with end_timestamp but without start_timestamp", %{conn: conn} do
now = Timex.now() now = Timex.now()
timestamp1 = Timex.shift(now, hours: -6) timestamp1 = Timex.shift(now, hours: -6)
timestamp2 = Timex.shift(now, hours: -3) timestamp2 = Timex.shift(now, hours: -3)
@ -1352,7 +1352,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
"module" => "account", "module" => "account",
"action" => "txlist", "action" => "txlist",
"address" => "#{address.hash}", "address" => "#{address.hash}",
"endtimestamp" => "#{end_timestamp}" "end_timestamp" => "#{end_timestamp}"
} }
expected_block_numbers = [ expected_block_numbers = [
@ -1376,7 +1376,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end end
test "with filterby=to option", %{conn: conn} do test "with filter_by=to option", %{conn: conn} do
block = insert(:block) block = insert(:block)
address = insert(:address) address = insert(:address)
@ -1390,7 +1390,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
"module" => "account", "module" => "account",
"action" => "txlist", "action" => "txlist",
"address" => "#{address.hash}", "address" => "#{address.hash}",
"filterby" => "to" "filter_by" => "to"
} }
assert response = assert response =
@ -1404,7 +1404,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response)
end end
test "with filterby=from option", %{conn: conn} do test "with filter_by=from option", %{conn: conn} do
block = insert(:block) block = insert(:block)
address = insert(:address) address = insert(:address)
@ -1421,7 +1421,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
"module" => "account", "module" => "account",
"action" => "txlist", "action" => "txlist",
"address" => "#{address.hash}", "address" => "#{address.hash}",
"filterby" => "from" "filter_by" => "from"
} }
assert response = assert response =
@ -2830,16 +2830,16 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
describe "optional_params/1" do describe "optional_params/1" do
test "includes valid optional params in the required format" do test "includes valid optional params in the required format" do
params = %{ params = %{
"startblock" => "100", "start_block" => "100",
"endblock" => "120", "end_block" => "120",
"sort" => "asc", "sort" => "asc",
# page number # page number
"page" => "1", "page" => "1",
# page size # page size
"offset" => "2", "offset" => "2",
"filterby" => "to", "filter_by" => "to",
"starttimestamp" => "1539186474", "start_timestamp" => "1539186474",
"endtimestamp" => "1539186474" "end_timestamp" => "1539186474"
} }
optional_params = AddressController.optional_params(params) optional_params = AddressController.optional_params(params)
@ -2875,20 +2875,20 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
assert AddressController.optional_params(params3) == %{} assert AddressController.optional_params(params3) == %{}
end end
test "'filterby' value can be 'to' or 'from'" do test "'filter_by' value can be 'to' or 'from'" do
params1 = %{"filterby" => "to"} params1 = %{"filter_by" => "to"}
optional_params1 = AddressController.optional_params(params1) optional_params1 = AddressController.optional_params(params1)
assert optional_params1.filter_by == "to" assert optional_params1.filter_by == "to"
params2 = %{"filterby" => "from"} params2 = %{"filter_by" => "from"}
optional_params2 = AddressController.optional_params(params2) optional_params2 = AddressController.optional_params(params2)
assert optional_params2.filter_by == "from" assert optional_params2.filter_by == "from"
params3 = %{"filterby" => "invalid"} params3 = %{"filter_by" => "invalid"}
assert AddressController.optional_params(params3) == %{} assert AddressController.optional_params(params3) == %{}
end end
@ -2899,25 +2899,25 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
test "ignores invalid optional params, keeps valid ones" do test "ignores invalid optional params, keeps valid ones" do
params1 = %{ params1 = %{
"startblock" => "invalid", "start_block" => "invalid",
"endblock" => "invalid", "end_block" => "invalid",
"sort" => "invalid", "sort" => "invalid",
"page" => "invalid", "page" => "invalid",
"offset" => "invalid", "offset" => "invalid",
"starttimestamp" => "invalid", "start_timestamp" => "invalid",
"endtimestamp" => "invalid" "end_timestamp" => "invalid"
} }
assert AddressController.optional_params(params1) == %{} assert AddressController.optional_params(params1) == %{}
params2 = %{ params2 = %{
"startblock" => "4", "start_block" => "4",
"endblock" => "10", "end_block" => "10",
"sort" => "invalid", "sort" => "invalid",
"page" => "invalid", "page" => "invalid",
"offset" => "invalid", "offset" => "invalid",
"starttimestamp" => "invalid", "start_timestamp" => "invalid",
"endtimestamp" => "invalid" "end_timestamp" => "invalid"
} }
optional_params = AddressController.optional_params(params2) optional_params = AddressController.optional_params(params2)

@ -6,6 +6,66 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
import Mox import Mox
def prepare_contracts do
insert(:contract_address)
{:ok, dt_1, _} = DateTime.from_iso8601("2022-09-20 10:00:00Z")
contract_1 =
insert(:smart_contract,
contract_code_md5: "123",
name: "Test 1",
optimization: "1",
compiler_version: "v0.6.8+commit.0bbfe453",
abi: [%{foo: "bar"}],
inserted_at: dt_1
)
insert(:contract_address)
{:ok, dt_2, _} = DateTime.from_iso8601("2022-09-22 10:00:00Z")
contract_2 =
insert(:smart_contract,
contract_code_md5: "12345",
name: "Test 2",
optimization: "0",
compiler_version: "v0.7.5+commit.eb77ed08",
abi: [%{foo: "bar-2"}],
inserted_at: dt_2
)
insert(:contract_address)
{:ok, dt_3, _} = DateTime.from_iso8601("2022-09-24 10:00:00Z")
contract_3 =
insert(:smart_contract,
contract_code_md5: "1234567",
name: "Test 3",
optimization: "1",
compiler_version: "v0.4.26+commit.4563c3fc",
abi: [%{foo: "bar-3"}],
inserted_at: dt_3
)
[contract_1, contract_2, contract_3]
end
def result(contract) do
%{
"ABI" => Jason.encode!(contract.abi),
"Address" => to_string(contract.address_hash),
"CompilerVersion" => contract.compiler_version,
"ContractName" => contract.name,
"OptimizationUsed" => if(contract.optimization, do: "1", else: "0")
}
end
defp result_not_verified(address_hash) do
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(address_hash)
}
end
describe "listcontracts" do describe "listcontracts" do
setup do setup do
%{params: %{"module" => "contract", "action" => "listcontracts"}} %{params: %{"module" => "contract", "action" => "listcontracts"}}
@ -47,15 +107,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["message"] == "OK" assert response["message"] == "OK"
assert response["status"] == "1" assert response["status"] == "1"
assert response["result"] == [ assert response["result"] == [result(contract)]
%{
"ABI" => Jason.encode!(contract.abi),
"Address" => to_string(contract.address_hash),
"CompilerVersion" => contract.compiler_version,
"ContractName" => contract.name,
"OptimizationUsed" => if(contract.optimization, do: "1", else: "0")
}
]
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
end end
@ -71,12 +123,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["message"] == "OK" assert response["message"] == "OK"
assert response["status"] == "1" assert response["status"] == "1"
assert response["result"] == [ assert response["result"] == [result_not_verified(address.hash)]
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(address.hash)
}
]
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
end end
@ -93,12 +140,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["message"] == "OK" assert response["message"] == "OK"
assert response["status"] == "1" assert response["status"] == "1"
assert response["result"] == [ assert response["result"] == [result_not_verified(address.hash)]
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(address.hash)
}
]
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
end end
@ -119,12 +161,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["message"] == "OK" assert response["message"] == "OK"
assert response["status"] == "1" assert response["status"] == "1"
assert response["result"] == [ assert response["result"] == [result_not_verified(address.hash)]
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(address.hash)
}
]
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
end end
@ -141,15 +178,82 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["message"] == "OK" assert response["message"] == "OK"
assert response["status"] == "1" assert response["status"] == "1"
assert response["result"] == [ assert response["result"] == [result(contract)]
%{
"ABI" => Jason.encode!(contract.abi), assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
"Address" => to_string(contract.address_hash), end
"CompilerVersion" => contract.compiler_version,
"ContractName" => contract.name, test "filtering for only verified contracts in the date range shows only verified contracts in that range", %{
"OptimizationUsed" => if(contract.optimization, do: "1", else: "0") params: params,
} conn: conn
] } do
[contract_1, contract_2, contract_3] = prepare_contracts()
filter_params =
params
|> Map.put("filter", "verified")
|> Map.put("verified_at_start_timestamp", "1663749418")
|> Map.put("verified_at_end_timestamp", "1663922218")
response =
conn
|> get("/api", filter_params)
|> json_response(200)
assert response["message"] == "OK"
assert response["status"] == "1"
assert response["result"] == [result(contract_2)]
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
end
test "filtering for only verified contracts with start created_at timestamp >= given timestamp shows only verified contracts in that range",
%{
params: params,
conn: conn
} do
[contract_1, contract_2, contract_3] = prepare_contracts()
filter_params =
params
|> Map.put("filter", "verified")
|> Map.put("verified_at_start_timestamp", "1663749418")
response =
conn
|> get("/api", filter_params)
|> json_response(200)
assert response["message"] == "OK"
assert response["status"] == "1"
assert response["result"] == [result(contract_2), result(contract_3)]
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
end
test "filtering for only verified contracts with end created_at timestamp < given timestamp shows only verified contracts in that range",
%{
params: params,
conn: conn
} do
[contract_1, contract_2, contract_3] = prepare_contracts()
filter_params =
params
|> Map.put("filter", "verified")
|> Map.put("verified_at_end_timestamp", "1663922218")
response =
conn
|> get("/api", filter_params)
|> json_response(200)
assert response["message"] == "OK"
assert response["status"] == "1"
assert response["result"] == [result(contract_1), result(contract_2)]
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
end end
@ -166,12 +270,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["message"] == "OK" assert response["message"] == "OK"
assert response["status"] == "1" assert response["status"] == "1"
assert response["result"] == [ assert response["result"] == [result_not_verified(decompiled_smart_contract.address_hash)]
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(decompiled_smart_contract.address_hash)
}
]
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
end end
@ -188,12 +287,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["message"] == "OK" assert response["message"] == "OK"
assert response["status"] == "1" assert response["status"] == "1"
assert response["result"] == [ assert response["result"] == [result_not_verified(smart_contract.address_hash)]
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(smart_contract.address_hash)
}
]
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
end end
@ -212,10 +306,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["message"] == "OK" assert response["message"] == "OK"
assert response["status"] == "1" assert response["status"] == "1"
assert %{ assert result_not_verified(smart_contract.address_hash) in response["result"]
"ABI" => "Contract source code not verified",
"Address" => to_string(smart_contract.address_hash)
} in response["result"]
refute to_string(non_match.address_hash) in Enum.map(response["result"], &Map.get(&1, "Address")) refute to_string(non_match.address_hash) in Enum.map(response["result"], &Map.get(&1, "Address"))
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
@ -234,12 +325,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["message"] == "OK" assert response["message"] == "OK"
assert response["status"] == "1" assert response["status"] == "1"
assert response["result"] == [ assert response["result"] == [result_not_verified(contract_address.hash)]
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(contract_address.hash)
}
]
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
end end
@ -261,12 +347,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
assert response["message"] == "OK" assert response["message"] == "OK"
assert response["status"] == "1" assert response["status"] == "1"
assert response["result"] == [ assert response["result"] == [result_not_verified(contract_address.hash)]
%{
"ABI" => "Contract source code not verified",
"Address" => to_string(contract_address.hash)
}
]
assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response)
end end

@ -7,7 +7,8 @@ defmodule Explorer.Etherscan.Contracts do
import Ecto.Query, import Ecto.Query,
only: [ only: [
from: 2 from: 2,
where: 3
] ]
alias Explorer.{Chain, Repo} alias Explorer.{Chain, Repo}
@ -80,7 +81,7 @@ defmodule Explorer.Etherscan.Contracts do
def append_proxy_info(address), do: address def append_proxy_info(address), do: address
def list_verified_contracts(limit, offset) do def list_verified_contracts(limit, offset, opts) do
query = query =
from( from(
smart_contract in SmartContract, smart_contract in SmartContract,
@ -90,7 +91,29 @@ defmodule Explorer.Etherscan.Contracts do
preload: [:address] preload: [:address]
) )
query verified_at_start_timestamp_exist? = Map.has_key?(opts, :verified_at_start_timestamp)
verified_at_end_timestamp_exist? = Map.has_key?(opts, :verified_at_end_timestamp)
query_in_timestamp_range =
cond do
verified_at_start_timestamp_exist? && verified_at_end_timestamp_exist? ->
query
|> where([smart_contract], smart_contract.inserted_at >= ^opts.verified_at_start_timestamp)
|> where([smart_contract], smart_contract.inserted_at < ^opts.verified_at_end_timestamp)
verified_at_start_timestamp_exist? ->
query
|> where([smart_contract], smart_contract.inserted_at >= ^opts.verified_at_start_timestamp)
verified_at_end_timestamp_exist? ->
query
|> where([smart_contract], smart_contract.inserted_at < ^opts.verified_at_end_timestamp)
true ->
query
end
query_in_timestamp_range
|> Repo.replica().all() |> Repo.replica().all()
|> Enum.map(fn smart_contract -> |> Enum.map(fn smart_contract ->
Map.put(smart_contract.address, :smart_contract, smart_contract) Map.put(smart_contract.address, :smart_contract, smart_contract)

Loading…
Cancel
Save