Migrate account data to separate DB (#67)

* Config changes

* Separate migrations

* Remain to fix preloads

* Some changes

* All is working, need to fix tests migrations

* mix tasks

* Fix tests and tune config

* Add migration script to migrate account data

* Fix after rebase

* Add branch to config.yml

* Add fix for broken sequence id counter

* Add drop dblink

* Add envs to makefile and common-bs.env

* Fix credo
account
nikitosing 2 years ago committed by Viktor Baranov
parent 5f54ea79c7
commit d7eef32074
  1. 25
      .github/workflows/config.yml
  2. 3
      .gitignore
  3. 4
      apps/block_scout_web/config/config.exs
  4. 6
      apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/tags_controller.ex
  5. 27
      apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/user_controller.ex
  6. 5
      apps/block_scout_web/lib/block_scout_web/controllers/account/watchlist_controller.ex
  7. 4
      apps/block_scout_web/lib/block_scout_web/models/get_address_tags.ex
  8. 2
      apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex
  9. 10
      apps/block_scout_web/lib/block_scout_web/models/user_from_auth.ex
  10. 4
      apps/block_scout_web/lib/block_scout_web/templates/account/watchlist_address/row.html.eex
  11. 2
      apps/block_scout_web/lib/block_scout_web/views/account/api/v1/user_view.ex
  12. 6
      apps/block_scout_web/lib/block_scout_web/views/account/watchlist_address_view.ex
  13. 8
      apps/block_scout_web/test/block_scout_web/models/user_from_auth_test.exs
  14. 2
      apps/block_scout_web/test/support/conn_case.ex
  15. 1
      apps/block_scout_web/test/test_helper.exs
  16. 2
      apps/explorer/config/config.exs
  17. 3
      apps/explorer/config/dev.exs
  18. 5
      apps/explorer/config/prod.exs
  19. 10
      apps/explorer/config/test.exs
  20. 16
      apps/explorer/lib/explorer/account/api/key.ex
  21. 38
      apps/explorer/lib/explorer/account/custom_abi.ex
  22. 2
      apps/explorer/lib/explorer/account/identity.ex
  23. 6
      apps/explorer/lib/explorer/account/notifier/email.ex
  24. 6
      apps/explorer/lib/explorer/account/notifier/notify.ex
  25. 17
      apps/explorer/lib/explorer/account/public_tags_request.ex
  26. 34
      apps/explorer/lib/explorer/account/tag_address.ex
  27. 44
      apps/explorer/lib/explorer/account/tag_transaction.ex
  28. 1
      apps/explorer/lib/explorer/account/watchlist.ex
  29. 45
      apps/explorer/lib/explorer/account/watchlist_address.ex
  30. 31
      apps/explorer/lib/explorer/account/watchlist_notification.ex
  31. 1
      apps/explorer/lib/explorer/application.ex
  32. 6
      apps/explorer/lib/explorer/chain/address.ex
  33. 44
      apps/explorer/lib/explorer/repo.ex
  34. 4
      apps/explorer/lib/explorer/third_party_integrations/airtable.ex
  35. 2
      apps/explorer/lib/release_tasks.ex
  36. 2
      apps/explorer/priv/account/migrations/20211031164954_create_account_identities.exs
  37. 2
      apps/explorer/priv/account/migrations/20211105114502_create_account_watchlists.exs
  38. 4
      apps/explorer/priv/account/migrations/20211105130907_create_account_watchlist_addresses.exs
  39. 8
      apps/explorer/priv/account/migrations/20211127212336_create_account_watchlist_notifications.exs
  40. 2
      apps/explorer/priv/account/migrations/20211205220414_add_email_and_name_to_account_identity.exs
  41. 7
      apps/explorer/priv/account/migrations/20220212222222_create_account_tag_addresses.exs
  42. 7
      apps/explorer/priv/account/migrations/20220313133333_create_account_tag_transactions.exs
  43. 2
      apps/explorer/priv/account/migrations/20220324213333_add_subject_to_watchlist_notifications.exs
  44. 2
      apps/explorer/priv/account/migrations/20220407134152_add_api_keys_and_plans_tables.exs
  45. 4
      apps/explorer/priv/account/migrations/20220510094118_add_custom_abis_table.exs
  46. 2
      apps/explorer/priv/account/migrations/20220606194836_add_account_public_tags_requests.exs
  47. 2
      apps/explorer/priv/account/migrations/20220620182600_add_account_identity_fields.exs
  48. 2
      apps/explorer/priv/account/migrations/20220624142547_add_unique_constraints.exs
  49. 2
      apps/explorer/priv/account/migrations/20220705195240_migrate_public_tags_addresses_to_array.exs
  50. 2
      apps/explorer/priv/account/migrations/20220729075714_guardiandb.exs
  51. 92
      apps/explorer/priv/account/migrations/20220901135656_copy_account_data.exs
  52. 1
      apps/explorer/test/explorer/account/notify/email_test.exs
  53. 14
      apps/explorer/test/explorer/account/notify/notify_test.exs
  54. 2
      apps/explorer/test/support/data_case.ex
  55. 5
      apps/explorer/test/support/factory.ex
  56. 1
      apps/explorer/test/test_helper.exs
  57. 28
      config/runtime/dev.exs
  58. 9
      config/runtime/prod.exs
  59. 3
      docker-compose/envs/common-blockscout.env
  60. 7
      docker/Makefile

@ -4,6 +4,7 @@ on:
push:
branches:
- account
- np-separate-account-separate-db
env:
MIX_ENV: test
@ -37,7 +38,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-
@ -97,7 +98,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -121,7 +122,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -144,7 +145,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_13-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -184,7 +185,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -210,7 +211,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -238,7 +239,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -284,7 +285,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -343,7 +344,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -399,7 +400,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -466,7 +467,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -527,7 +528,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_14-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"

3
.gitignore vendored

@ -9,6 +9,9 @@
/*.ez
/logs
# mix dialyzer artifacts
/priv/plts
# Generated on crash by the VM
erl_crash.dump

@ -8,7 +8,7 @@ import Config
# General application configuration
config :block_scout_web,
namespace: BlockScoutWeb,
ecto_repos: [Explorer.Repo]
ecto_repos: [Explorer.Repo, Explorer.Repo.Account]
config :block_scout_web,
admin_panel_enabled: System.get_env("ADMIN_PANEL_ENABLED", "") == "true"
@ -97,7 +97,7 @@ config :hammer,
config :block_scout_web, BlockScoutWeb.Guardian, issuer: "block_scout_web"
config :guardian, Guardian.DB,
repo: Explorer.Repo,
repo: Explorer.Repo.Account,
schema_name: "guardian_tokens",
sweep_interval: 60

@ -17,7 +17,8 @@ defmodule BlockScoutWeb.Account.Api.V1.TagsController do
uid = Plug.current_claims(conn)["sub"]
with {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)},
{:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.preload(identity, :watchlists)},
{:watchlist, %{watchlists: [watchlist | _]}} <-
{:watchlist, Repo.account_repo().preload(identity, :watchlists)},
{:address_hash, {:ok, address_hash}} <- {:address_hash, Address.cast(address_hash)} do
GetAddressTags.get_address_tags(address_hash, %{id: identity.id, watchlist_id: watchlist.id})
else
@ -57,7 +58,8 @@ defmodule BlockScoutWeb.Account.Api.V1.TagsController do
uid = Plug.current_claims(conn)["sub"]
with {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)},
{:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.preload(identity, :watchlists)},
{:watchlist, %{watchlists: [watchlist | _]}} <-
{:watchlist, Repo.account_repo().preload(identity, :watchlists)},
false <- is_nil(transaction) do
GetTransactionTags.get_transaction_with_addresses_tags(transaction, %{
id: identity.id,

@ -30,11 +30,9 @@ defmodule BlockScoutWeb.Account.Api.V1.UserController do
uid = Plug.current_claims(conn)["sub"]
with {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)},
{:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.preload(identity, :watchlists)},
watchlist_with_addresses <-
Repo.preload(watchlist,
watchlist_addresses: {from(wa in WatchlistAddress, order_by: [desc: wa.id]), [:address]}
) do
{:watchlist, %{watchlists: [watchlist | _]}} <-
{:watchlist, Repo.account_repo().preload(identity, :watchlists)},
watchlist_with_addresses <- preload_watchlist_address_fetched_coin_balance(watchlist) do
conn
|> put_status(200)
|> render(:watchlist_addresses, %{
@ -48,7 +46,8 @@ defmodule BlockScoutWeb.Account.Api.V1.UserController do
uid = Plug.current_claims(conn)["sub"]
with {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)},
{:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.preload(identity, :watchlists)},
{:watchlist, %{watchlists: [watchlist | _]}} <-
{:watchlist, Repo.account_repo().preload(identity, :watchlists)},
{count, _} <- WatchlistAddress.delete(watchlist_address_id, watchlist.id),
{:watchlist_delete, true} <- {:watchlist_delete, count > 0} do
conn
@ -100,10 +99,11 @@ defmodule BlockScoutWeb.Account.Api.V1.UserController do
}
with {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)},
{:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.preload(identity, :watchlists)},
{:watchlist, %{watchlists: [watchlist | _]}} <-
{:watchlist, Repo.account_repo().preload(identity, :watchlists)},
{:ok, watchlist_address} <-
WatchlistAddress.create(Map.put(watchlist_params, :watchlist_id, watchlist.id)),
watchlist_address_preloaded <- Repo.preload(watchlist_address, :address) do
watchlist_address_preloaded <- WatchlistAddress.preload_address_fetched_coin_balance(watchlist_address) do
conn
|> put_status(200)
|> render(:watchlist_address, %{
@ -158,10 +158,11 @@ defmodule BlockScoutWeb.Account.Api.V1.UserController do
}
with {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)},
{:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.preload(identity, :watchlists)},
{:watchlist, %{watchlists: [watchlist | _]}} <-
{:watchlist, Repo.account_repo().preload(identity, :watchlists)},
{:ok, watchlist_address} <-
WatchlistAddress.update(Map.put(watchlist_params, :watchlist_id, watchlist.id)),
watchlist_address_preloaded <- Repo.preload(watchlist_address, :address) do
watchlist_address_preloaded <- WatchlistAddress.preload_address_fetched_coin_balance(watchlist_address) do
conn
|> put_status(200)
|> render(:watchlist_address, %{
@ -482,4 +483,10 @@ defmodule BlockScoutWeb.Account.Api.V1.UserController do
defp reject_nil_map_values(map) when is_map(map) do
Map.reject(map, fn {_k, v} -> is_nil(v) end)
end
defp preload_watchlist_address_fetched_coin_balance(watchlist) do
watchlist
|> Repo.account_repo().preload(watchlist_addresses: from(wa in WatchlistAddress, order_by: [desc: wa.id]))
|> WatchlistAddress.preload_address_fetched_coin_balance()
end
end

@ -19,7 +19,8 @@ defmodule BlockScoutWeb.Account.WatchlistController do
defp watchlist_with_addresses(user) do
Watchlist
|> Repo.get(user.watchlist_id)
|> Repo.preload(watchlist_addresses: {from(wa in WatchlistAddress, order_by: [desc: wa.id]), [:address]})
|> Repo.account_repo().get(user.watchlist_id)
|> Repo.account_repo().preload(watchlist_addresses: from(wa in WatchlistAddress, order_by: [desc: wa.id]))
|> WatchlistAddress.preload_address_fetched_coin_balance()
end
end

@ -54,7 +54,7 @@ defmodule BlockScoutWeb.Models.GetAddressTags do
select: %{label: ta.name, display_name: ta.name, address_hash: ta.address_hash}
)
Repo.all(query)
Repo.account_repo().all(query)
end
def get_personal_tags(_, _), do: []
@ -68,7 +68,7 @@ defmodule BlockScoutWeb.Models.GetAddressTags do
select: %{label: wa.name, display_name: wa.name}
)
Repo.all(query)
Repo.account_repo().all(query)
end
def get_watchlist_names_on_address(_, _), do: []

@ -21,7 +21,7 @@ defmodule BlockScoutWeb.Models.GetTransactionTags do
def get_transaction_with_addresses_tags(_, _), do: %{personal_tags: [], watchlist_names: [], personal_tx_tag: nil}
def get_transaction_tags(transaction_hash, %{id: identity_id}) do
Repo.get_by(TagTransaction, tx_hash: transaction_hash, identity_id: identity_id)
Repo.account_repo().get_by(TagTransaction, tx_hash: transaction_hash, identity_id: identity_id)
end
def get_transaction_tags(_, _), do: nil

@ -37,7 +37,7 @@ defmodule BlockScoutWeb.Models.UserFromAuth do
end
defp create_identity(auth) do
with {:ok, %Identity{} = identity} <- Repo.insert(new_identity(auth)),
with {:ok, %Identity{} = identity} <- Repo.account_repo().insert(new_identity(auth)),
{:ok, _watchlist} <- add_watchlist(identity) do
identity
end
@ -46,7 +46,7 @@ defmodule BlockScoutWeb.Models.UserFromAuth do
defp update_identity(identity, attrs) do
identity
|> Identity.changeset(attrs)
|> Repo.update()
|> Repo.account_repo().update()
end
defp new_identity(auth) do
@ -62,12 +62,12 @@ defmodule BlockScoutWeb.Models.UserFromAuth do
defp add_watchlist(identity) do
watchlist = Ecto.build_assoc(identity, :watchlists, %{})
with {:ok, _} <- Repo.insert(watchlist),
with {:ok, _} <- Repo.account_repo().insert(watchlist),
do: {:ok, identity}
end
def find_identity(auth_or_uid) do
Repo.all(query_identity(auth_or_uid))
Repo.account_repo().all(query_identity(auth_or_uid))
end
def query_identity(%Auth{} = auth) do
@ -79,7 +79,7 @@ defmodule BlockScoutWeb.Models.UserFromAuth do
end
defp basic_info(auth, identity) do
%{watchlists: [watchlist | _]} = Repo.preload(identity, :watchlists)
%{watchlists: [watchlist | _]} = Repo.account_repo().preload(identity, :watchlists)
%{
id: identity.id,

@ -8,12 +8,12 @@
</div>
</td>
<td>
<%= balance_ether(@watchlist_address.address) %>
<%= balance_ether(@watchlist_address.fetched_coin_balance) %>
<br>
<span class="address-current-balance">
(<span
data-wei-value="<%= if @watchlist_address.address.fetched_coin_balance, do: @watchlist_address.address.fetched_coin_balance.value %>"
data-wei-value="<%= if @watchlist_address.fetched_coin_balance, do: @watchlist_address.fetched_coin_balance.value %>"
data-usd-exchange-rate="<%= @exchange_rate.usd_value %>"
data-placement="top"
data-toggle="tooltip"

@ -74,7 +74,7 @@ defmodule BlockScoutWeb.Account.Api.V1.UserView do
"id" => watchlist.id,
"address_hash" => watchlist.address_hash,
"name" => watchlist.name,
"address_balance" => if(watchlist.address.fetched_coin_balance, do: watchlist.address.fetched_coin_balance.value),
"address_balance" => if(watchlist.fetched_coin_balance, do: watchlist.fetched_coin_balance.value),
"exchange_rate" => exchange_rate.usd_value,
"notification_settings" => %{
"native" => %{

@ -3,11 +3,9 @@ defmodule BlockScoutWeb.Account.WatchlistAddressView do
import BlockScoutWeb.AddressView, only: [trimmed_hash: 1]
import BlockScoutWeb.WeiHelpers, only: [format_wei_value: 2]
alias Explorer.Chain.Address
def balance_ether(nil), do: ""
def balance_ether(%Address{fetched_coin_balance: nil}), do: ""
def balance_ether(%Address{fetched_coin_balance: balance}) do
def balance_ether(balance) do
format_wei_value(balance, :ether)
end
end

@ -37,13 +37,13 @@ defmodule UserFromAuthTest do
email: "john@blockscout.com",
name: "John Snow",
uid: "github|666666"
} = Identity |> first |> Repo.one()
} = Identity |> first |> Repo.account_repo().one()
%{
id: watchlist_id,
identity_id: ^identity_id,
name: "default"
} = Watchlist |> first |> Repo.one()
} = Watchlist |> first |> Repo.account_repo().one()
assert {:ok,
%{
@ -84,13 +84,13 @@ defmodule UserFromAuthTest do
email: "john@blockscout.com",
name: "John Snow",
uid: "google-oauth2|666666"
} = Identity |> first |> Repo.one()
} = Identity |> first |> Repo.account_repo().one()
%{
id: watchlist_id,
identity_id: ^identity_id,
name: "default"
} = Watchlist |> first |> Repo.one()
} = Watchlist |> first |> Repo.account_repo().one()
assert {:ok,
%{

@ -37,9 +37,11 @@ defmodule BlockScoutWeb.ConnCase do
@dialyzer {:nowarn_function, __ex_unit_setup_0: 1}
setup tags do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Explorer.Repo)
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Explorer.Repo.Account)
unless tags[:async] do
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, {:shared, self()})
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Account, {:shared, self()})
end
Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.Transactions.child_id())

@ -25,6 +25,7 @@ ExUnit.start()
Mox.defmock(Explorer.ExchangeRates.Source.TestSource, for: Explorer.ExchangeRates.Source)
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, :manual)
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Account, :manual)
Absinthe.Test.prime(BlockScoutWeb.Schema)

@ -7,7 +7,7 @@ import Config
# General application configuration
config :explorer,
ecto_repos: [Explorer.Repo],
ecto_repos: [Explorer.Repo, Explorer.Repo.Account],
token_functions_reader_max_retries: 3
config :explorer, Explorer.Counters.AverageBlockTime,

@ -6,6 +6,9 @@ config :explorer, Explorer.Repo, timeout: :timer.seconds(80)
# Configure API database
config :explorer, Explorer.Repo.Replica1, timeout: :timer.seconds(80)
# Configure Account database
config :explorer, Explorer.Repo.Account, timeout: :timer.seconds(80)
config :explorer, Explorer.Tracer, env: "dev", disabled?: true
config :logger, :explorer,

@ -10,6 +10,11 @@ config :explorer, Explorer.Repo.Replica1,
prepare: :unnamed,
timeout: :timer.seconds(60)
# Configures Account database
config :explorer, Explorer.Repo.Account,
prepare: :unnamed,
timeout: :timer.seconds(60)
config :explorer, Explorer.Tracer, env: "production", disabled?: true
config :logger, :explorer,

@ -23,6 +23,16 @@ config :explorer, Explorer.Repo.Replica1,
timeout: :timer.seconds(60),
queue_target: 1000
# Configure API database
config :explorer, Explorer.Repo.Account,
database: "explorer_test_account",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox,
# Default of `5_000` was too low for `BlockFetcher` test
ownership_timeout: :timer.minutes(1),
timeout: :timer.seconds(60),
queue_target: 1000
config :logger, :explorer,
level: :warn,
path: Path.absname("logs/test/explorer.log")

@ -41,14 +41,14 @@ defmodule Explorer.Account.Api.Key do
def create(attrs) do
%__MODULE__{}
|> changeset(Map.put(attrs, :value, generate_api_key()))
|> Repo.insert()
|> Repo.account_repo().insert()
end
def api_key_count_constraint(%Changeset{changes: %{identity_id: identity_id}} = api_key) do
if identity_id
|> api_keys_by_identity_id_query()
|> limit(@max_key_per_account)
|> Repo.aggregate(:count, :value) >= @max_key_per_account do
|> Repo.account_repo().aggregate(:count, :value) >= @max_key_per_account do
api_key
|> add_error(:name, "Max #{@max_key_per_account} keys per account")
else
@ -81,7 +81,7 @@ defmodule Explorer.Account.Api.Key do
def get_api_key_by_value_and_identity_id(value, identity_id) when not is_nil(value) and not is_nil(identity_id) do
value
|> api_key_by_value_and_identity_id_query(identity_id)
|> Repo.one()
|> Repo.account_repo().one()
end
def get_api_key_by_value_and_identity_id(_, _), do: nil
@ -89,7 +89,7 @@ defmodule Explorer.Account.Api.Key do
def update(%{value: api_key_value, identity_id: identity_id} = attrs) do
with api_key <- get_api_key_by_value_and_identity_id(api_key_value, identity_id),
false <- is_nil(api_key) do
api_key |> changeset(attrs) |> Repo.update()
api_key |> changeset(attrs) |> Repo.account_repo().update()
else
true ->
{:error, %{reason: :item_not_found}}
@ -99,7 +99,7 @@ defmodule Explorer.Account.Api.Key do
def delete(api_key_value, identity_id) when not is_nil(api_key_value) and not is_nil(identity_id) do
api_key_value
|> api_key_by_value_and_identity_id_query(identity_id)
|> Repo.delete_all()
|> Repo.account_repo().delete_all()
end
def delete(_, _), do: nil
@ -107,7 +107,7 @@ defmodule Explorer.Account.Api.Key do
def get_api_keys_by_identity_id(id) when not is_nil(id) do
id
|> api_keys_by_identity_id_query()
|> Repo.all()
|> Repo.account_repo().all()
end
def get_api_keys_by_identity_id(_), do: nil
@ -116,8 +116,8 @@ defmodule Explorer.Account.Api.Key do
if match?({:ok, _casted_api_key}, UUID.cast(api_key_value)) do
__MODULE__
|> where([api_key], api_key.value == ^api_key_value)
|> Repo.one()
|> Repo.preload(identity: :plan)
|> Repo.account_repo().one()
|> Repo.account_repo().preload(identity: :plan)
else
nil
end

@ -8,7 +8,7 @@ defmodule Explorer.Account.CustomABI do
alias Ecto.Changeset
alias Explorer.Account.Identity
alias Explorer.{Chain, Repo}
alias Explorer.Chain.{Address, Hash}
alias Explorer.Chain.Hash
import Ecto.Changeset
@ -19,8 +19,9 @@ defmodule Explorer.Account.CustomABI do
field(:abi, {:array, :map})
field(:given_abi, :string, virtual: true)
field(:abi_validating_error, :string, virtual: true)
field(:address_hash, Hash.Address, null: false)
belongs_to(:identity, Identity)
belongs_to(:address, Address, foreign_key: :address_hash, references: :hash, type: Hash.Address)
timestamps()
end
@ -34,7 +35,6 @@ defmodule Explorer.Account.CustomABI do
|> validate_custom_abi()
|> check_smart_contract_address()
|> foreign_key_constraint(:identity_id, message: "User not found")
|> foreign_key_constraint(:address_hash, message: "Address not found")
|> unique_constraint([:identity_id, :address_hash],
message: "Custom ABI for this address has already been added before"
)
@ -48,23 +48,23 @@ defmodule Explorer.Account.CustomABI do
end
defp check_smart_contract_address(%Changeset{changes: %{address_hash: address_hash}} = custom_abi) do
if Chain.is_address_hash_is_smart_contract?(address_hash) do
custom_abi
else
add_error(custom_abi, :address_hash, "Address is not a smart contract")
end
check_smart_contract_address_inner(custom_abi, address_hash)
end
defp check_smart_contract_address(%Changeset{data: %{address_hash: address_hash}} = custom_abi) do
check_smart_contract_address_inner(custom_abi, address_hash)
end
defp check_smart_contract_address(custom_abi), do: custom_abi
defp check_smart_contract_address_inner(changeset, address_hash) do
if Chain.is_address_hash_is_smart_contract?(address_hash) do
custom_abi
changeset
else
add_error(custom_abi, :address_hash, "Address is not a smart contract")
add_error(changeset, :address_hash, "Address is not a smart contract")
end
end
defp check_smart_contract_address(custom_abi), do: custom_abi
defp validate_custom_abi(%Changeset{changes: %{given_abi: given_abi, abi_validating_error: error}} = custom_abi) do
custom_abi
|> add_error(:abi, error)
@ -130,7 +130,7 @@ defmodule Explorer.Account.CustomABI do
if identity_id
|> custom_abis_by_identity_id_query()
|> limit(@max_abis_per_account)
|> Repo.aggregate(:count, :id) >= @max_abis_per_account do
|> Repo.account_repo().aggregate(:count, :id) >= @max_abis_per_account do
add_error(custom_abi, :name, "Max #{@max_abis_per_account} ABIs per account")
else
custom_abi
@ -142,7 +142,7 @@ defmodule Explorer.Account.CustomABI do
def create(attrs) do
%__MODULE__{}
|> changeset(attrs)
|> Repo.insert()
|> Repo.account_repo().insert()
end
def custom_abis_by_identity_id_query(id) when not is_nil(id) do
@ -173,7 +173,7 @@ defmodule Explorer.Account.CustomABI do
when not is_nil(identity_id) and not is_nil(address_hash) do
address_hash
|> custom_abi_by_identity_id_and_address_hash_query(identity_id)
|> Repo.one()
|> Repo.account_repo().one()
end
def get_custom_abi_by_identity_id_and_address_hash(_, _), do: nil
@ -181,7 +181,7 @@ defmodule Explorer.Account.CustomABI do
def get_custom_abi_by_id_and_identity_id(id, identity_id) when not is_nil(id) and not is_nil(identity_id) do
id
|> custom_abi_by_id_and_identity_id_query(identity_id)
|> Repo.one()
|> Repo.account_repo().one()
end
def get_custom_abi_by_id_and_identity_id(_, _), do: nil
@ -189,7 +189,7 @@ defmodule Explorer.Account.CustomABI do
def get_custom_abis_by_identity_id(id) when not is_nil(id) do
id
|> custom_abis_by_identity_id_query()
|> Repo.all()
|> Repo.account_repo().all()
end
def get_custom_abis_by_identity_id(_), do: nil
@ -197,7 +197,7 @@ defmodule Explorer.Account.CustomABI do
def delete(id, identity_id) when not is_nil(id) and not is_nil(identity_id) do
id
|> custom_abi_by_id_and_identity_id_query(identity_id)
|> Repo.delete_all()
|> Repo.account_repo().delete_all()
end
def delete(_, _), do: nil
@ -207,7 +207,7 @@ defmodule Explorer.Account.CustomABI do
false <- is_nil(custom_abi) do
custom_abi
|> changeset(attrs)
|> Repo.update()
|> Repo.account_repo().update()
else
true ->
{:error, %{reason: :item_not_found}}

@ -16,8 +16,10 @@ defmodule Explorer.Account.Identity do
field(:name, :string)
field(:nickname, :string)
field(:avatar, :string)
has_many(:tag_addresses, TagAddress)
has_many(:watchlists, Watchlist)
belongs_to(:plan, Plan)
timestamps()

@ -85,9 +85,9 @@ defmodule Explorer.Account.Notifier.Email do
do: name
defp address_hash_string(%WatchlistNotification{
watchlist_address: %WatchlistAddress{address: address}
watchlist_address: %WatchlistAddress{address_hash: address_hash}
}),
do: hash_string(address.hash)
do: hash_string(address_hash)
defp hash_string(hash) do
"0x" <> Base.encode16(hash.bytes, case: :lower)
@ -114,7 +114,7 @@ defmodule Explorer.Account.Notifier.Email do
end
defp preload(notification) do
Repo.preload(notification, watchlist_address: [:address, watchlist: :identity])
Repo.account_repo().preload(notification, watchlist_address: [watchlist: :identity])
end
defp address_url(address_hash) do

@ -64,7 +64,7 @@ defmodule Explorer.Account.Notifier.Notify do
) do
notification
|> query_notification(address)
|> Repo.all()
|> Repo.account_repo().all()
|> case do
[] -> save_and_send_notification(notification, address)
_ -> :ok
@ -91,7 +91,7 @@ defmodule Explorer.Account.Notifier.Notify do
end
defp save_and_send_notification(%WatchlistNotification{} = notification, %WatchlistAddress{} = address) do
Repo.insert(notification)
Repo.account_repo().insert(notification)
email = Email.compose(notification, address)
@ -143,6 +143,6 @@ defmodule Explorer.Account.Notifier.Notify do
defp find_watchlists_addresses(%Explorer.Chain.Hash{} = address_hash) do
query = from(wa in WatchlistAddress, where: wa.address_hash == ^address_hash)
Repo.all(query)
Repo.account_repo().all(query)
end
end

@ -75,7 +75,7 @@ defmodule Explorer.Account.PublicTagsRequest do
def create(attrs) do
%__MODULE__{}
|> changeset(Map.put(attrs, :request_type, "add"))
|> Repo.insert()
|> Repo.account_repo().insert()
|> AirTable.submit()
end
@ -90,7 +90,7 @@ defmodule Explorer.Account.PublicTagsRequest do
if identity_id
|> public_tags_requests_by_identity_id_query()
|> limit(@max_public_tags_request_per_account)
|> Repo.aggregate(:count, :id) >= @max_public_tags_request_per_account do
|> Repo.account_repo().aggregate(:count, :id) >= @max_public_tags_request_per_account do
request
|> add_error(:tags, "Max #{@max_public_tags_request_per_account} public tags requests per account")
else
@ -113,7 +113,7 @@ defmodule Explorer.Account.PublicTagsRequest do
fragment("? && ?", public_tags_request.addresses, ^Enum.map(prepared_addresses, fn x -> x.bytes end))
)
|> limit(1)
|> Repo.one()
|> Repo.account_repo().one()
now = DateTime.utc_now()
@ -192,7 +192,7 @@ defmodule Explorer.Account.PublicTagsRequest do
def public_tags_request_by_id_and_identity_id_query(_, _), do: nil
def get_public_tags_request_by_id_and_identity_id(id, identity_id) when not is_nil(id) and not is_nil(identity_id) do
id |> public_tags_request_by_id_and_identity_id_query(identity_id) |> Repo.one()
id |> public_tags_request_by_id_and_identity_id_query(identity_id) |> Repo.account_repo().one()
end
def get_public_tags_request_by_id_and_identity_id(_, _), do: nil
@ -200,7 +200,7 @@ defmodule Explorer.Account.PublicTagsRequest do
def get_public_tags_requests_by_identity_id(id) when not is_nil(id) do
id
|> public_tags_requests_by_identity_id_query()
|> Repo.all()
|> Repo.account_repo().all()
end
def get_public_tags_requests_by_identity_id(_), do: nil
@ -208,7 +208,7 @@ defmodule Explorer.Account.PublicTagsRequest do
def delete_public_tags_request(identity_id, id) when not is_nil(id) and not is_nil(identity_id) do
id
|> public_tags_request_by_id_and_identity_id_query(identity_id)
|> Repo.delete_all()
|> Repo.account_repo().delete_all()
end
def delete_public_tags_request(_, _), do: nil
@ -216,7 +216,8 @@ defmodule Explorer.Account.PublicTagsRequest do
def update(%{id: id, identity_id: identity_id} = attrs) do
with public_tags_request <- get_public_tags_request_by_id_and_identity_id(id, identity_id),
false <- is_nil(public_tags_request),
{:ok, changeset} <- public_tags_request |> changeset(Map.put(attrs, :request_type, "edit")) |> Repo.update() do
{:ok, changeset} <-
public_tags_request |> changeset(Map.put(attrs, :request_type, "edit")) |> Repo.account_repo().update() do
AirTable.submit({:ok, changeset})
else
true ->
@ -233,7 +234,7 @@ defmodule Explorer.Account.PublicTagsRequest do
{:ok, changeset} <-
public_tags_request
|> changeset_without_constraints(%{request_type: "delete", remove_reason: remove_reason})
|> Repo.update() do
|> Repo.account_repo().update() do
case AirTable.submit({:ok, changeset}) do
{:error, changeset} ->
changeset

@ -16,14 +16,9 @@ defmodule Explorer.Account.TagAddress do
schema "account_tag_addresses" do
field(:name, :string)
field(:address_hash, Hash.Address, null: false)
belongs_to(:identity, Identity)
belongs_to(:address, Address,
foreign_key: :address_hash,
references: :hash,
type: Hash.Address
)
timestamps()
end
@ -42,30 +37,37 @@ defmodule Explorer.Account.TagAddress do
|> validate_length(:name, min: 1, max: 35)
|> unique_constraint([:identity_id, :address_hash], message: "Address tag already exists")
|> check_existance_or_create_address()
|> foreign_key_constraint(:address_hash, message: "")
|> tag_address_count_constraint()
end
def create(attrs) do
%__MODULE__{}
|> changeset(attrs)
|> Repo.insert()
|> Repo.account_repo().insert()
end
defp check_existance_or_create_address(%Changeset{changes: %{address_hash: address_hash}, valid?: true} = changeset) do
check_existance_or_create_address_inner(changeset, address_hash)
end
defp check_existance_or_create_address(%Changeset{data: %{address_hash: address_hash}, valid?: true} = changeset) do
check_existance_or_create_address_inner(changeset, address_hash)
end
defp check_existance_or_create_address(changeset), do: changeset
defp check_existance_or_create_address_inner(changeset, address_hash) do
with {:ok, hash} <- Hash.Address.cast(address_hash),
{:ok, %Address{}} <- Chain.find_or_insert_address_from_hash(hash, []) do
changeset
end
end
defp check_existance_or_create_address(changeset), do: changeset
def tag_address_count_constraint(%Changeset{changes: %{identity_id: identity_id}} = tag_address) do
if identity_id
|> tags_address_by_identity_id_query()
|> limit(@max_tag_address_per_account)
|> Repo.aggregate(:count, :id) >= @max_tag_address_per_account do
|> Repo.account_repo().aggregate(:count, :id) >= @max_tag_address_per_account do
tag_address
|> add_error(:name, "Max #{@max_tag_address_per_account} tags per account")
else
@ -86,7 +88,7 @@ defmodule Explorer.Account.TagAddress do
def get_tags_address_by_identity_id(id) when not is_nil(id) do
id
|> tags_address_by_identity_id_query()
|> Repo.all()
|> Repo.account_repo().all()
end
def get_tags_address_by_identity_id(_), do: nil
@ -103,7 +105,7 @@ defmodule Explorer.Account.TagAddress do
when not is_nil(address_hash) and not is_nil(identity_id) do
address_hash
|> tag_address_by_address_hash_and_identity_id_query(identity_id)
|> Repo.one()
|> Repo.account_repo().one()
end
def get_tag_address_by_address_hash_and_identity_id(_, _), do: nil
@ -120,7 +122,7 @@ defmodule Explorer.Account.TagAddress do
when not is_nil(tag_id) and not is_nil(identity_id) do
tag_id
|> tag_address_by_id_and_identity_id_query(identity_id)
|> Repo.one()
|> Repo.account_repo().one()
end
def get_tag_address_by_id_and_identity_id_query(_, _), do: nil
@ -128,7 +130,7 @@ defmodule Explorer.Account.TagAddress do
def delete(tag_id, identity_id) when not is_nil(tag_id) and not is_nil(identity_id) do
tag_id
|> tag_address_by_id_and_identity_id_query(identity_id)
|> Repo.delete_all()
|> Repo.account_repo().delete_all()
end
def delete(_, _), do: nil
@ -136,7 +138,7 @@ defmodule Explorer.Account.TagAddress do
def update(%{id: tag_id, identity_id: identity_id} = attrs) do
with tag <- get_tag_address_by_id_and_identity_id_query(tag_id, identity_id),
false <- is_nil(tag) do
tag |> changeset(attrs) |> Repo.update()
tag |> changeset(attrs) |> Repo.account_repo().update()
else
true ->
{:error, %{reason: :item_not_found}}

@ -9,20 +9,16 @@ defmodule Explorer.Account.TagTransaction do
alias Ecto.Changeset
alias Explorer.Account.Identity
alias Explorer.Chain.{Hash, Transaction}
alias Explorer.Repo
alias Explorer.{Chain, Repo}
alias Explorer.Chain.Hash
@max_tag_transaction_per_account 15
schema "account_tag_transactions" do
field(:name, :string)
belongs_to(:identity, Identity)
field(:tx_hash, Hash.Full, null: false)
belongs_to(:transaction, Transaction,
foreign_key: :tx_hash,
references: :hash,
type: Hash.Full
)
belongs_to(:identity, Identity)
timestamps()
end
@ -41,21 +37,37 @@ defmodule Explorer.Account.TagTransaction do
|> validate_required(@attrs, message: "Required")
|> validate_length(:name, min: 1, max: 35)
|> unique_constraint([:identity_id, :tx_hash], message: "Transaction tag already exists")
|> foreign_key_constraint(:tx_hash, message: "Transaction does not exist")
|> tag_transaction_count_constraint()
|> check_transaction_existance()
end
def create(attrs) do
%__MODULE__{}
|> changeset(attrs)
|> Repo.insert()
|> Repo.account_repo().insert()
end
defp check_transaction_existance(%Changeset{changes: %{tx_hash: tx_hash}} = changeset) do
check_transaction_existance_inner(changeset, tx_hash)
end
defp check_transaction_existance(%Changeset{data: %{tx_hash: tx_hash}} = changeset) do
check_transaction_existance_inner(changeset, tx_hash)
end
defp check_transaction_existance_inner(changeset, tx_hash) do
if match?({:ok, _}, Chain.hash_to_transaction(tx_hash)) do
changeset
else
add_error(changeset, :tx_hash, "Transaction does not exist")
end
end
def tag_transaction_count_constraint(%Changeset{changes: %{identity_id: identity_id}} = tag_transaction) do
if identity_id
|> tags_transaction_by_identity_id_query()
|> limit(@max_tag_transaction_per_account)
|> Repo.aggregate(:count, :id) >= @max_tag_transaction_per_account do
|> Repo.account_repo().aggregate(:count, :id) >= @max_tag_transaction_per_account do
tag_transaction
|> add_error(:name, "Max #{@max_tag_transaction_per_account} tags per account")
else
@ -76,7 +88,7 @@ defmodule Explorer.Account.TagTransaction do
def get_tags_transaction_by_identity_id(id) when not is_nil(id) do
id
|> tags_transaction_by_identity_id_query()
|> Repo.all()
|> Repo.account_repo().all()
end
def get_tags_transaction_by_identity_id(_), do: nil
@ -93,7 +105,7 @@ defmodule Explorer.Account.TagTransaction do
when not is_nil(address_hash) and not is_nil(identity_id) do
address_hash
|> tag_transaction_by_address_hash_and_identity_id_query(identity_id)
|> Repo.one()
|> Repo.account_repo().one()
end
def get_tag_transaction_by_address_hash_and_identity_id(_, _), do: nil
@ -110,7 +122,7 @@ defmodule Explorer.Account.TagTransaction do
when not is_nil(tag_id) and not is_nil(identity_id) do
tag_id
|> tag_transaction_by_id_and_identity_id_query(identity_id)
|> Repo.one()
|> Repo.account_repo().one()
end
def get_tag_transaction_by_id_and_identity_id_query(_, _), do: nil
@ -118,7 +130,7 @@ defmodule Explorer.Account.TagTransaction do
def delete(tag_id, identity_id) when not is_nil(tag_id) and not is_nil(identity_id) do
tag_id
|> tag_transaction_by_id_and_identity_id_query(identity_id)
|> Repo.delete_all()
|> Repo.account_repo().delete_all()
end
def delete(_, _), do: nil
@ -126,7 +138,7 @@ defmodule Explorer.Account.TagTransaction do
def update(%{id: tag_id, identity_id: identity_id} = attrs) do
with tag <- get_tag_transaction_by_id_and_identity_id_query(tag_id, identity_id),
false <- is_nil(tag) do
tag |> changeset(attrs) |> Repo.update()
tag |> changeset(attrs) |> Repo.account_repo().update()
else
true ->
{:error, %{reason: :item_not_found}}

@ -14,7 +14,6 @@ defmodule Explorer.Account.Watchlist do
field(:name, :string)
belongs_to(:identity, Identity)
has_many(:watchlist_addresses, WatchlistAddress)
has_many(:addresses, through: [:watchlist_addresses, :address])
timestamps()
end

@ -11,13 +11,14 @@ defmodule Explorer.Account.WatchlistAddress do
alias Explorer.Account.Notifier.ForbiddenAddress
alias Explorer.Account.Watchlist
alias Explorer.{Chain, Repo}
alias Explorer.Chain.{Address, Hash}
alias Explorer.Chain.{Address, Hash, Wei}
@max_watchlist_addresses_per_account 10
schema "account_watchlist_addresses" do
field(:name, :string)
belongs_to(:address, Address, foreign_key: :address_hash, references: :hash, type: Hash.Address)
field(:address_hash, Hash.Address, null: false)
belongs_to(:watchlist, Watchlist)
field(:watch_coin_input, :boolean, default: true)
@ -33,6 +34,8 @@ defmodule Explorer.Account.WatchlistAddress do
field(:notify_feed, :boolean)
field(:notify_inapp, :boolean)
field(:fetched_coin_balance, Wei, virtual: true)
timestamps()
end
@ -51,21 +54,20 @@ defmodule Explorer.Account.WatchlistAddress do
|> validate_required([:name, :address_hash, :watchlist_id], message: "Required")
|> unique_constraint([:watchlist_id, :address_hash], message: "Address already added to the watch list")
|> check_address()
|> foreign_key_constraint(:address_hash, message: "")
|> watchlist_address_count_constraint()
end
def create(attrs) do
%__MODULE__{}
|> changeset(attrs)
|> Repo.insert()
|> Repo.account_repo().insert()
end
def watchlist_address_count_constraint(%Changeset{changes: %{watchlist_id: watchlist_id}} = watchlist_address) do
if watchlist_id
|> watchlist_addresses_by_watchlist_id_query()
|> limit(@max_watchlist_addresses_per_account)
|> Repo.aggregate(:count, :id) >= @max_watchlist_addresses_per_account do
|> Repo.account_repo().aggregate(:count, :id) >= @max_watchlist_addresses_per_account do
watchlist_address
|> add_error(:name, "Max #{@max_watchlist_addresses_per_account} watch list addresses per account")
else
@ -76,6 +78,16 @@ defmodule Explorer.Account.WatchlistAddress do
def watchlist_address_count_constraint(changeset), do: changeset
defp check_address(%Changeset{changes: %{address_hash: address_hash}, valid?: true} = changeset) do
check_address_inner(changeset, address_hash)
end
defp check_address(%Changeset{data: %{address_hash: address_hash}, valid?: true} = changeset) do
check_address_inner(changeset, address_hash)
end
defp check_address(changeset), do: changeset
defp check_address_inner(changeset, address_hash) do
with {:ok, address_hash} <- ForbiddenAddress.check(address_hash),
{:ok, %Address{}} <- Chain.find_or_insert_address_from_hash(address_hash, []) do
changeset
@ -88,8 +100,6 @@ defmodule Explorer.Account.WatchlistAddress do
end
end
defp check_address(changeset), do: changeset
def watchlist_addresses_by_watchlist_id_query(watchlist_id) when not is_nil(watchlist_id) do
__MODULE__
|> where([wl_address], wl_address.watchlist_id == ^watchlist_id)
@ -109,7 +119,7 @@ defmodule Explorer.Account.WatchlistAddress do
when not is_nil(watchlist_address_id) and not is_nil(watchlist_id) do
watchlist_address_id
|> watchlist_address_by_id_and_watchlist_id_query(watchlist_id)
|> Repo.one()
|> Repo.account_repo().one()
end
def get_watchlist_address_by_id_and_watchlist_id(_, _), do: nil
@ -118,7 +128,7 @@ defmodule Explorer.Account.WatchlistAddress do
when not is_nil(watchlist_address_id) and not is_nil(watchlist_id) do
watchlist_address_id
|> watchlist_address_by_id_and_watchlist_id_query(watchlist_id)
|> Repo.delete_all()
|> Repo.account_repo().delete_all()
end
def delete(_, _), do: nil
@ -128,7 +138,7 @@ defmodule Explorer.Account.WatchlistAddress do
false <- is_nil(watchlist_address) do
watchlist_address
|> changeset(attrs)
|> Repo.update()
|> Repo.account_repo().update()
else
true ->
{:error, %{reason: :item_not_found}}
@ -136,4 +146,19 @@ defmodule Explorer.Account.WatchlistAddress do
end
def get_max_watchlist_addresses_count, do: @max_watchlist_addresses_per_account
def preload_address_fetched_coin_balance(%Watchlist{watchlist_addresses: watchlist_addresses} = watchlist) do
w_addresses =
Enum.map(watchlist_addresses, fn wa ->
preload_address_fetched_coin_balance(wa)
end)
%Watchlist{watchlist | watchlist_addresses: w_addresses}
end
def preload_address_fetched_coin_balance(%__MODULE__{address_hash: address_hash} = watchlist_address) do
%__MODULE__{watchlist_address | fetched_coin_balance: address_hash |> Address.fetched_coin_balance() |> Repo.one()}
end
def preload_address_fetched_coin_balance(watchlist), do: watchlist
end

@ -9,12 +9,7 @@ defmodule Explorer.Account.WatchlistNotification do
import Ecto.Changeset
alias Explorer.Account.WatchlistAddress
alias Explorer.Chain.{
Address,
Hash,
Transaction
}
alias Explorer.Chain.Hash
schema "account_watchlist_notifications" do
field(:amount, :decimal)
@ -29,27 +24,9 @@ defmodule Explorer.Account.WatchlistNotification do
belongs_to(:watchlist_address, WatchlistAddress)
belongs_to(
:from_address,
Address,
foreign_key: :from_address_hash,
references: :hash,
type: Hash.Address
)
belongs_to(
:to_address,
Address,
foreign_key: :to_address_hash,
references: :hash,
type: Hash.Address
)
belongs_to(:transaction, Transaction,
foreign_key: :transaction_hash,
references: :hash,
type: Hash.Full
)
field(:from_address_hash, Hash.Address)
field(:to_address_hash, Hash.Address)
field(:transaction_hash, Hash.Full)
timestamps()
end

@ -43,6 +43,7 @@ defmodule Explorer.Application do
base_children = [
Explorer.Repo,
Explorer.Repo.Replica1,
Explorer.Repo.Account,
Supervisor.child_spec({SpandexDatadog.ApiServer, datadog_opts()}, id: SpandexDatadog.ApiServer),
Supervisor.child_spec({Task.Supervisor, name: Explorer.HistoryTaskSupervisor}, id: Explorer.HistoryTaskSupervisor),
Supervisor.child_spec({Task.Supervisor, name: Explorer.MarketTaskSupervisor}, id: Explorer.MarketTaskSupervisor),

@ -259,6 +259,12 @@ defmodule Explorer.Chain.Address do
)
end
def fetched_coin_balance(address_hash) when not is_nil(address_hash) do
Address
|> where([address], address.hash == ^address_hash)
|> select([address], address.fetched_coin_balance)
end
defimpl String.Chars do
@doc """
Uses `hash` as string representation, formatting it according to the eip-55 specification

@ -128,10 +128,54 @@ defmodule Explorer.Repo do
def replica, do: Explorer.Repo.Replica1
end
def account_repo, do: Explorer.Repo.Account
defmodule Replica1 do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres,
read_only: true
def init(_, opts) do
db_url = Application.get_env(:explorer, Explorer.Repo.Replica1)[:url]
repo_conf = Application.get_env(:explorer, Explorer.Repo.Replica1)
merged =
%{url: db_url}
|> ConfigHelper.get_db_config()
|> Keyword.merge(repo_conf, fn
_key, v1, nil -> v1
_key, nil, v2 -> v2
_, _, v2 -> v2
end)
Application.put_env(:explorer, Explorer.Repo.Replica1, merged)
{:ok, Keyword.put(opts, :url, db_url)}
end
end
defmodule Account do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
db_url = Application.get_env(:explorer, Explorer.Repo.Account)[:url]
repo_conf = Application.get_env(:explorer, Explorer.Repo.Account)
merged =
%{url: db_url}
|> ConfigHelper.get_db_config()
|> Keyword.merge(repo_conf, fn
_key, v1, nil -> v1
_key, nil, v2 -> v2
_, _, v2 -> v2
end)
Application.put_env(:explorer, Explorer.Repo.Account, merged)
{:ok, Keyword.put(opts, :url, db_url)}
end
end
end

@ -12,7 +12,7 @@ defmodule Explorer.ThirdPartyIntegrations.AirTable do
if Mix.env() == :test do
new_request
|> PublicTagsRequest.changeset(%{request_id: "123"})
|> Repo.update()
|> Repo.account_repo().update()
input
else
@ -33,7 +33,7 @@ defmodule Explorer.ThirdPartyIntegrations.AirTable do
new_request
|> PublicTagsRequest.changeset(%{request_id: request_id})
|> Repo.update()
|> Repo.account_repo().update()
input

@ -14,7 +14,7 @@ defmodule Explorer.ReleaseTasks do
:ecto_sql
]
@repos Application.compile_env(:blockscout, :ecto_repos, [Explorer.Repo])
@repos Application.compile_env(:blockscout, :ecto_repos, [Explorer.Repo, Explorer.Repo.Account])
def create_and_migrate do
start_services()

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.CreateAccountIdentities do
defmodule Explorer.Repo.Account.Migrations.CreateAccountIdentities do
use Ecto.Migration
def change do

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.CreateAccountWatchlists do
defmodule Explorer.Repo.Account.Migrations.CreateAccountWatchlists do
use Ecto.Migration
def change do

@ -1,10 +1,10 @@
defmodule Explorer.Repo.Migrations.CreateAccountWatchlistAddresses do
defmodule Explorer.Repo.Account.Migrations.CreateAccountWatchlistAddresses do
use Ecto.Migration
def change do
create table(:account_watchlist_addresses) do
add(:name, :string)
add(:address_hash, references(:addresses, column: :hash, type: :bytea, on_delete: :delete_all))
add(:address_hash, :bytea, null: false)
add(:watchlist_id, references(:account_watchlists, on_delete: :delete_all))
add(:watch_coin_input, :boolean, default: true)
add(:watch_coin_output, :boolean, default: true)

@ -1,15 +1,15 @@
defmodule Explorer.Repo.Migrations.CreateAccountWatchlistNotifications do
defmodule Explorer.Repo.Account.Migrations.CreateAccountWatchlistNotifications do
use Ecto.Migration
def change do
create table(:account_watchlist_notifications) do
add(:watchlist_address_id, references(:account_watchlist_addresses, on_delete: :delete_all))
add(:transaction_hash, references(:transactions, column: :hash, type: :bytea, on_delete: :delete_all))
add(:transaction_hash, :bytea)
add(:from_address_hash, references(:addresses, column: :hash, type: :bytea, on_delete: :delete_all))
add(:from_address_hash, :bytea)
add(:to_address_hash, references(:addresses, column: :hash, type: :bytea, on_delete: :delete_all))
add(:to_address_hash, :bytea)
add(:direction, :string)
add(:name, :string)

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.AddEmailToAccountIdentity do
defmodule Explorer.Repo.Account.Migrations.AddEmailToAccountIdentity do
use Ecto.Migration
def change do

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.CreateAccountTagAddresses do
defmodule Explorer.Repo.Account.Migrations.CreateAccountTagAddresses do
use Ecto.Migration
def change do
@ -6,10 +6,7 @@ defmodule Explorer.Repo.Migrations.CreateAccountTagAddresses do
add(:name, :string)
add(:identity_id, references(:account_identities, on_delete: :delete_all))
add(
:address_hash,
references(:addresses, column: :hash, type: :bytea, on_delete: :delete_all)
)
add(:address_hash, :bytea, null: false)
timestamps()
end

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.CreateAccountTagTransactions do
defmodule Explorer.Repo.Account.Migrations.CreateAccountTagTransactions do
use Ecto.Migration
def change do
@ -6,10 +6,7 @@ defmodule Explorer.Repo.Migrations.CreateAccountTagTransactions do
add(:name, :string)
add(:identity_id, references(:account_identities, on_delete: :delete_all))
add(
:tx_hash,
references(:transactions, column: :hash, type: :bytea, on_delete: :delete_all)
)
add(:tx_hash, :bytea, null: false)
timestamps()
end

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.AddSubjectToWatchlistNotifications do
defmodule Explorer.Repo.Account.Migrations.AddSubjectToWatchlistNotifications do
use Ecto.Migration
def change do

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.AddApiKeysAndPlansTables do
defmodule Explorer.Repo.Account.Migrations.AddApiKeysAndPlansTables do
use Ecto.Migration
def change do

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.AddCustomAbisTable do
defmodule Explorer.Repo.Account.Migrations.AddCustomAbisTable do
use Ecto.Migration
def change do
@ -6,7 +6,7 @@ defmodule Explorer.Repo.Migrations.AddCustomAbisTable do
add(:id, :serial, null: false, primary_key: true)
add(:identity_id, references(:account_identities, column: :id, on_delete: :delete_all), null: false)
add(:name, :string, null: false)
add(:address_hash, references(:addresses, column: :hash, type: :bytea, on_delete: :delete_all), null: false)
add(:address_hash, :bytea, null: false)
add(:abi, :jsonb, null: false)
timestamps()

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.AddAccountPublicTagsRequests do
defmodule Explorer.Repo.Account.Migrations.AddAccountPublicTagsRequests do
use Ecto.Migration
def change do

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.AddAccountIdentityFields do
defmodule Explorer.Repo.Account.Migrations.AddAccountIdentityFields do
use Ecto.Migration
def change do

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.AddUniqueConstraints do
defmodule Explorer.Repo.Account.Migrations.AddUniqueConstraints do
use Ecto.Migration
def change do

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.MigratePublicTagsAddressesToArray do
defmodule Explorer.Repo.Account.Migrations.MigratePublicTagsAddressesToArray do
use Ecto.Migration
def change do

@ -1,4 +1,4 @@
defmodule Explorer.Repo.Migrations.CreateGuardianDBTokensTable do
defmodule Explorer.Repo.Account.Migrations.CreateGuardianDBTokensTable do
use Ecto.Migration
def change do

@ -0,0 +1,92 @@
defmodule Explorer.Repo.Account.Migrations.CopyAccountData do
use Ecto.Migration
def change do
execute("CREATE EXTENSION dblink;")
execute("SELECT dblink_connect('db_to_copy_from', '#{System.get_env("DATABASE_URL")}');")
execute(
"INSERT INTO account_identities
SELECT * FROM dblink('db_to_copy_from', 'SELECT * FROM account_identities')
row(id bigint, uid character varying(255), inserted_at timestamp(0) without time zone, updated_at timestamp(0) without time zone, email character varying(255), name character varying(255), plan_id bigint, nickname character varying(255), avatar text);"
)
# execute("INSERT INTO account_api_plans
# SELECT * FROM dblink('db_to_copy_from', 'SELECT * FROM account_api_plans')
# row(id integer, max_req_per_second smallint, name character varying(255), inserted_at timestamp(0) without time zone, updated_at timestamp(0) without time zone);")
execute(
"INSERT INTO account_api_keys
SELECT * FROM dblink('db_to_copy_from', 'SELECT * FROM account_api_keys')
row(identity_id bigint, name character varying(255), value uuid, inserted_at timestamp(0) without time zone, updated_at timestamp(0) without time zone);"
)
execute(
"INSERT INTO account_custom_abis
SELECT * FROM dblink('db_to_copy_from', 'SELECT * FROM account_custom_abis')
row(id integer, identity_id bigint, name character varying(255), address_hash bytea, abi jsonb, inserted_at timestamp(0) without time zone, updated_at timestamp(0) without time zone);"
)
execute(
"INSERT INTO account_public_tags_requests
SELECT * FROM dblink('db_to_copy_from', 'SELECT * FROM account_public_tags_requests')
row(id integer, identity_id bigint, full_name character varying(255), email character varying(255), company character varying(255), website character varying(255), tags character varying(255), description text, additional_comment character varying(255), request_type character varying(255), is_owner boolean, remove_reason text, request_id character varying(255), inserted_at timestamp(0) without time zone, updated_at timestamp(0) without time zone, addresses bytea[]);"
)
execute(
"INSERT INTO account_tag_addresses
SELECT * FROM dblink('db_to_copy_from', 'SELECT * FROM account_tag_addresses')
row(id integer, name character varying(255), identity_id bigint, address_hash bytea, inserted_at timestamp(0) without time zone, updated_at timestamp(0) without time zone);"
)
execute(
"INSERT INTO account_tag_transactions
SELECT * FROM dblink('db_to_copy_from', 'SELECT * FROM account_tag_transactions')
row(id integer, name character varying(255), identity_id bigint, tx_hash bytea, inserted_at timestamp(0) without time zone, updated_at timestamp(0) without time zone);"
)
execute(
"INSERT INTO account_watchlists
SELECT * FROM dblink('db_to_copy_from', 'SELECT * FROM account_watchlists')
row(id bigint, name character varying(255), identity_id bigint, inserted_at timestamp(0) without time zone, updated_at timestamp(0) without time zone);"
)
execute(
"INSERT INTO account_watchlist_addresses
SELECT * FROM dblink('db_to_copy_from', 'SELECT * FROM account_watchlist_addresses')
row(id bigint, name character varying(255), address_hash bytea, watchlist_id bigint, watch_coin_input boolean, watch_coin_output boolean, watch_erc_20_input boolean, watch_erc_20_output boolean, watch_erc_721_input boolean, watch_erc_721_output boolean, watch_erc_1155_input boolean, watch_erc_1155_output boolean, notify_email boolean, notify_epns boolean, notify_feed boolean, notify_inapp boolean, inserted_at timestamp(0) without time zone, updated_at timestamp(0) without time zone);"
)
execute(
"INSERT INTO account_watchlist_notifications
SELECT * FROM dblink('db_to_copy_from', 'SELECT * FROM account_watchlist_notifications')
row(id bigint, watchlist_address_id bigint, transaction_hash bytea, from_address_hash bytea, to_address_hash bytea, direction character varying(255), name character varying(255), type character varying(255), method character varying(255), block_number integer, amount numeric, tx_fee numeric, viewed_at timestamp without time zone, inserted_at timestamp(0) without time zone, updated_at timestamp(0) without time zone, subject character varying(255));"
)
execute(
"INSERT INTO guardian_tokens
SELECT * FROM dblink('db_to_copy_from', 'SELECT * FROM guardian_tokens')
row(jti character varying(255), aud character varying(255), typ character varying(255), iss character varying(255), sub character varying(255), exp bigint, jwt text, claims jsonb, inserted_at timestamp(0) without time zone, updated_at timestamp(0) without time zone);"
)
execute("SELECT dblink_disconnect('db_to_copy_from');")
execute("DROP EXTENSION dblink;")
# update sequence id counter
execute("SELECT setval('account_identities_id_seq', (SELECT MAX(id) FROM account_identities)+1);")
execute("SELECT setval('account_custom_abis_id_seq', (SELECT MAX(id) FROM account_custom_abis)+1);")
execute(
"SELECT setval('account_public_tags_requests_id_seq', (SELECT MAX(id) FROM account_public_tags_requests)+1);"
)
execute("SELECT setval('account_tag_addresses_id_seq', (SELECT MAX(id) FROM account_tag_addresses)+1);")
execute("SELECT setval('account_tag_transactions_id_seq', (SELECT MAX(id) FROM account_tag_transactions)+1);")
execute("SELECT setval('account_watchlists_id_seq', (SELECT MAX(id) FROM account_watchlists)+1);")
execute("SELECT setval('account_watchlist_addresses_id_seq', (SELECT MAX(id) FROM account_watchlist_addresses)+1);")
execute(
"SELECT setval('account_watchlist_notifications_id_seq', (SELECT MAX(id) FROM account_watchlist_notifications)+1);"
)
end
end

@ -60,7 +60,6 @@ defmodule Explorer.Account.Notify.EmailTest do
watchlist_address = %WatchlistAddress{
name: "wallet",
watchlist: watchlist,
address: to_address,
address_hash: to_hash,
watch_coin_input: true,
watch_coin_output: true,

@ -46,7 +46,7 @@ defmodule Explorer.Account.Notify.NotifyTest do
wn =
WatchlistNotification
|> first
|> Repo.one()
|> Repo.account_repo().one()
assert notify == [[:ok]]
@ -55,11 +55,11 @@ defmodule Explorer.Account.Notify.NotifyTest do
test "when address apears in watchlist" do
wa =
%WatchlistAddress{
address: address
} = insert(:account_watchlist_address)
%WatchlistAddress{address_hash: address_hash} =
build(:account_watchlist_address)
|> Repo.account_repo().insert!()
_watchlist_address = Repo.preload(wa, :address, watchlist: :identity)
_watchlist_address = Repo.preload(wa, watchlist: :identity)
tx =
%Transaction{
@ -67,7 +67,7 @@ defmodule Explorer.Account.Notify.NotifyTest do
to_address: _to_address,
block_number: _block_number,
hash: _tx_hash
} = with_block(insert(:transaction, to_address: address))
} = with_block(insert(:transaction, to_address: %Chain.Address{hash: address_hash}))
{_, fee} = Chain.fee(tx, :gwei)
amount = Wei.to(tx.value, :ether)
@ -76,7 +76,7 @@ defmodule Explorer.Account.Notify.NotifyTest do
wn =
WatchlistNotification
|> first
|> Repo.one()
|> Repo.account_repo().one()
assert notify == [[:ok]]

@ -34,9 +34,11 @@ defmodule Explorer.DataCase do
ExVCR.Config.cassette_library_dir("test/support/fixture/vcr_cassettes")
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Explorer.Repo)
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Explorer.Repo.Account)
unless tags[:async] do
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, {:shared, self()})
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Account, {:shared, self()})
end
Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.BlockNumber.child_id())

@ -126,7 +126,6 @@ defmodule Explorer.Factory do
def account_watchlist_factory do
%Watchlist{
name: "default",
identity: build(:account_identity)
}
end
@ -140,10 +139,12 @@ defmodule Explorer.Factory do
end
def account_watchlist_address_factory do
hash = build(:address).hash
%WatchlistAddress{
name: "wallet",
watchlist: build(:account_watchlist),
address: build(:address),
address_hash: hash,
watch_coin_input: true,
watch_coin_output: true,
watch_erc_20_input: true,

@ -12,6 +12,7 @@ ExUnit.start()
{:ok, _} = Application.ensure_all_started(:ex_machina)
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, :auto)
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Account, :auto)
Mox.defmock(Explorer.ExchangeRates.Source.TestSource, for: Explorer.ExchangeRates.Source)
Mox.defmock(Explorer.KnownTokens.Source.TestSource, for: Explorer.KnownTokens.Source)

@ -46,10 +46,11 @@ database_api_url =
do: System.get_env("DATABASE_READ_ONLY_API_URL"),
else: System.get_env("DATABASE_URL")
pool_size =
if System.get_env("DATABASE_READ_ONLY_API_URL"),
do: String.to_integer(System.get_env("POOL_SIZE", "40")),
else: String.to_integer(System.get_env("POOL_SIZE", "50"))
# pool_size =
# if System.get_env("DATABASE_READ_ONLY_API_URL"),
# do: String.to_integer(System.get_env("POOL_SIZE", "40")),
# else: String.to_integer(System.get_env("POOL_SIZE", "50"))
pool_size = String.to_integer(System.get_env("POOL_SIZE", "30"))
# Configure your database
config :explorer, Explorer.Repo,
@ -63,7 +64,7 @@ hostname_api = if System.get_env("DATABASE_READ_ONLY_API_URL"), do: nil, else: h
pool_size_api =
if System.get_env("DATABASE_READ_ONLY_API_URL"),
do: String.to_integer(System.get_env("POOL_SIZE_API", "50")),
do: String.to_integer(System.get_env("POOL_SIZE_API", "10")),
else: String.to_integer(System.get_env("POOL_SIZE_API", "10"))
# Configure API database
@ -73,6 +74,23 @@ config :explorer, Explorer.Repo.Replica1,
url: database_api_url,
pool_size: pool_size_api
database_account_url = System.get_env("DATABASE_ACCOUNT_URL") || System.get_env("DATABASE_URL")
pool_size_account =
if System.get_env("DATABASE_ACCOUNT_URL"),
do: String.to_integer(System.get_env("POOL_SIZE_ACCOUNT", "10")),
else: String.to_integer(System.get_env("POOL_SIZE_ACCOUNT", "10"))
database_account = if System.get_env("DATABASE_ACCOUNT_URL"), do: nil, else: database
hostname_account = if System.get_env("DATABASE_ACCOUNT_URL"), do: nil, else: hostname
# Configure Account database
config :explorer, Explorer.Repo.Account,
database: database_account,
hostname: hostname_account,
url: database_account_url,
pool_size: pool_size_account
variant =
if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do
"ganache"

@ -49,6 +49,15 @@ config :explorer, Explorer.Repo.Replica1,
pool_size: pool_size_api,
ssl: String.equivalent?(System.get_env("ECTO_USE_SSL") || "true", "true")
database_account_url = System.get_env("DATABASE_ACCOUNT_URL")
pool_size_account = String.to_integer(System.get_env("POOL_SIZE_ACCOUNT", "50"))
# Configures Account database
config :explorer, Explorer.Repo.Account,
url: database_account_url,
pool_size: pool_size_account,
ssl: String.equivalent?(System.get_env("ECTO_USE_SSL") || "true", "true")
variant =
if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do
"parity"

@ -124,3 +124,6 @@ API_RATE_LIMIT_STATIC_API_KEY=
FETCH_REWARDS_WAY=trace_block
ENABLE_RUST_VERIFICATION_SERVICE=true
RUST_VERIFICATION_SERVICE_URL=http://host.docker.internal:8043/
# DATABASE_READ_ONLY_API_URL=
# DATABASE_ACCOUNT_URL=
# POOL_SIZE_ACCOUNT=

@ -502,7 +502,12 @@ endif
ifdef RUST_VERIFICATION_SERVICE_URL
BLOCKSCOUT_CONTAINER_PARAMS += -e 'RUST_VERIFICATION_SERVICE_URL=$(RUST_VERIFICATION_SERVICE_URL)'
endif
ifdef DATABASE_ACCOUNT_URL
BLOCKSCOUT_CONTAINER_PARAMS += -e 'DATABASE_ACCOUNT_URL=$(DATABASE_ACCOUNT_URL)'
endif
ifdef POOL_SIZE_ACCOUNT
BLOCKSCOUT_CONTAINER_PARAMS += -e 'POOL_SIZE_ACCOUNT=$(POOL_SIZE_ACCOUNT)'
endif
HAS_BLOCKSCOUT_IMAGE := $(shell docker images | grep -sw "${BS_CONTAINER_IMAGE} ")
build:

Loading…
Cancel
Save