Merge pull request #1045 from poanetwork/sa-graphql-contracts

Add smart_contract field to address object
pull/1052/head
Luke Imhoff 6 years ago committed by GitHub
commit 88a95150ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      apps/block_scout_web/lib/block_scout_web/schema.ex
  2. 2
      apps/block_scout_web/lib/block_scout_web/schema/scalars.ex
  3. 33
      apps/block_scout_web/lib/block_scout_web/schema/scalars/JSON.ex
  4. 23
      apps/block_scout_web/lib/block_scout_web/schema/types.ex
  5. 4
      apps/block_scout_web/mix.exs
  6. 43
      apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs
  7. 5
      apps/explorer/lib/explorer/chain.ex
  8. 1
      mix.lock

@ -3,7 +3,10 @@ defmodule BlockScoutWeb.Schema do
use Absinthe.Schema
alias Absinthe.Middleware.Dataloader, as: AbsintheMiddlewareDataloader
alias Absinthe.Plugin, as: AbsinthePlugin
alias BlockScoutWeb.Resolvers.{Address, Block, Transaction}
alias Explorer.Chain
import_types(BlockScoutWeb.Schema.Types)
@ -37,4 +40,14 @@ defmodule BlockScoutWeb.Schema do
end)
end
end
def context(context) do
loader = Dataloader.add_source(Dataloader.new(), :db, Chain.data())
Map.put(context, :loader, loader)
end
def plugins do
[AbsintheMiddlewareDataloader] ++ AbsinthePlugin.defaults()
end
end

@ -6,6 +6,8 @@ defmodule BlockScoutWeb.Schema.Scalars do
alias Explorer.Chain.{Data, Hash, Wei}
alias Explorer.Chain.Hash.{Address, Full, Nonce}
import_types(BlockScoutWeb.Schema.Scalars.JSON)
@desc """
The address (40 (hex) characters / 160 bits / 20 bytes) is derived from the public key (128 (hex) characters /
512 bits / 64 bytes) which is derived from the private key (64 (hex) characters / 256 bits / 32 bytes).

@ -0,0 +1,33 @@
defmodule BlockScoutWeb.Schema.Scalars.JSON do
@moduledoc """
The JSON scalar type allows arbitrary JSON values to be passed in and out.
"""
use Absinthe.Schema.Notation
@desc """
The `JSON` scalar type represents arbitrary JSON string data, represented as UTF-8
character sequences. The JSON type is most often used to represent a free-form
human-readable JSON string.
"""
scalar :json do
parse(&decode/1)
serialize(&encode/1)
end
defp decode(%Absinthe.Blueprint.Input.String{value: value}) do
case Jason.decode(value) do
{:ok, result} -> {:ok, result}
_ -> :error
end
end
defp decode(%Absinthe.Blueprint.Input.Null{}) do
{:ok, nil}
end
defp decode(_) do
:error
end
defp encode(value), do: Jason.encode!(value)
end

@ -3,6 +3,8 @@ defmodule BlockScoutWeb.Schema.Types do
use Absinthe.Schema.Notation
import Absinthe.Resolution.Helpers
import_types(Absinthe.Type.Custom)
import_types(BlockScoutWeb.Schema.Scalars)
@ -14,6 +16,10 @@ defmodule BlockScoutWeb.Schema.Types do
field(:fetched_coin_balance, :wei)
field(:fetched_coin_balance_block_number, :integer)
field(:contract_code, :data)
field :smart_contract, :smart_contract do
resolve(dataloader(:db, :smart_contract))
end
end
@desc """
@ -36,6 +42,23 @@ defmodule BlockScoutWeb.Schema.Types do
field(:parent_hash, :full_hash)
end
@desc """
The representation of a verified Smart Contract.
"A contract in the sense of Solidity is a collection of code (its functions)
and data (its state) that resides at a specific address on the Ethereum
blockchain."
http://solidity.readthedocs.io/en/v0.4.24/introduction-to-smart-contracts.html
"""
object :smart_contract do
field(:name, :string)
field(:compiler_version, :string)
field(:optimization, :boolean)
field(:contract_source_code, :string)
field(:abi, :json)
field(:address_hash, :address_hash)
end
@desc """
Models a Web3 transaction.
"""

@ -67,6 +67,8 @@ defmodule BlockScoutWeb.Mixfile do
{:absinthe_plug, "~> 1.4"},
{:bypass, "~> 0.8", only: :test},
{:credo, "0.10.2", only: [:dev, :test], runtime: false},
# For Absinthe to load data in batches
{:dataloader, "~> 1.0.0"},
{:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false},
{:ex_cldr_numbers, "~> 1.0"},
{:ex_cldr_units, "~> 1.0"},
@ -80,6 +82,8 @@ defmodule BlockScoutWeb.Mixfile do
{:flow, "~> 0.12"},
{:gettext, "~> 0.14.1"},
{:httpoison, "~> 1.0", override: true},
# JSON parser and generator
{:jason, "~> 1.0"},
{:junit_formatter, ">= 0.0.0", only: [:test], runtime: false},
# Log errors and application output to separate files
{:logger_file_backend, "~> 0.0.10"},

@ -60,6 +60,49 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do
}
end
test "smart_contract returns all expected fields", %{conn: conn} do
address = insert(:address, fetched_coin_balance: 100)
smart_contract = insert(:smart_contract, address_hash: address.hash)
query = """
query ($hashes: [AddressHash!]!) {
addresses(hashes: $hashes) {
fetched_coin_balance
smart_contract {
name
compiler_version
optimization
contract_source_code
abi
address_hash
}
}
}
"""
variables = %{"hashes" => to_string(address.hash)}
conn = get(conn, "/graphql", query: query, variables: variables)
assert json_response(conn, 200) == %{
"data" => %{
"addresses" => [
%{
"fetched_coin_balance" => to_string(address.fetched_coin_balance.value),
"smart_contract" => %{
"name" => smart_contract.name,
"compiler_version" => smart_contract.compiler_version,
"optimization" => smart_contract.optimization,
"contract_source_code" => smart_contract.contract_source_code,
"abi" => Jason.encode!(smart_contract.abi),
"address_hash" => to_string(address.hash)
}
}
]
}
}
end
test "errors for non-existent address hashes", %{conn: conn} do
address = build(:address)

@ -42,6 +42,8 @@ defmodule Explorer.Chain do
alias Explorer.{PagingOptions, Repo}
alias Explorer.Counters.{BlockValidationCounter, TokenHoldersCounter, TokenTransferCounter}
alias Dataloader.Ecto, as: DataloaderEcto
@default_paging_options %PagingOptions{page_size: 50}
@typedoc """
@ -2086,4 +2088,7 @@ defmodule Explorer.Chain do
|> limit(^paging_options.page_size)
|> Repo.all()
end
@spec data() :: Dataloader.Ecto.t()
def data, do: DataloaderEcto.new(Repo)
end

@ -19,6 +19,7 @@
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
"credo": {:hex, :credo, "0.10.2", "03ad3a1eff79a16664ed42fc2975b5e5d0ce243d69318060c626c34720a49512", [: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.4", "7c2345c53c9e5b61420013fc53c8463ba347a938b61f66677eb47d9c4a53ac5d", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]},
"decimal": {:hex, :decimal, "1.5.0", "b0433a36d0e2430e3d50291b1c65f53c37d56f83665b43d79963684865beab68", [:mix], []},
"deep_merge": {:hex, :deep_merge, "0.2.0", "c1050fa2edf4848b9f556fba1b75afc66608a4219659e3311d9c9427b5b680b3", [:mix], [], "hexpm"},

Loading…
Cancel
Save