diff --git a/apps/ewallet_db/lib/ewallet_db/transaction_request.ex b/apps/ewallet_db/lib/ewallet_db/transaction_request.ex index a8533818d..bcb96b817 100644 --- a/apps/ewallet_db/lib/ewallet_db/transaction_request.ex +++ b/apps/ewallet_db/lib/ewallet_db/transaction_request.ex @@ -188,6 +188,17 @@ defmodule EWalletDB.TransactionRequest do def get(_id, _opts), do: nil + @doc """ + Retrieves a transaction request using one or more fields. + """ + @spec get_by(map() | keyword(), keyword()) :: %__MODULE__{} | nil | no_return() + def get_by(fields, opts \\ []) do + TransactionRequest + |> Repo.get_by(fields) + |> preload_option(opts) + end + + @spec query_all_for_account_and_user_uuids([String.t()], [String.t()]) :: Ecto.Queryable.t() def query_all_for_account_and_user_uuids(account_uuids, user_uuids) do from( t in TransactionRequest, @@ -234,9 +245,7 @@ defmodule EWalletDB.TransactionRequest do @doc """ Touches a request by updating the `updated_at` field. """ - @spec touch(%TransactionRequest{}) :: - {:ok, %TransactionRequest{}} - | {:error, Map.t()} + @spec touch(%TransactionRequest{}) :: {:ok, %TransactionRequest{}} | {:error, Map.t()} def touch(request) do request |> touch_changeset(%{updated_at: NaiveDateTime.utc_now()}) diff --git a/apps/ewallet_db/priv/repo/seeds/01_user.exs b/apps/ewallet_db/priv/repo/seeds/01_user.exs index 5178c7971..68de357d8 100644 --- a/apps/ewallet_db/priv/repo/seeds/01_user.exs +++ b/apps/ewallet_db/priv/repo/seeds/01_user.exs @@ -1,6 +1,6 @@ defmodule EWalletDB.Repo.Seeds.UserSeed do alias EWalletDB.Helpers.Crypto - alias EWalletDB.User + alias EWalletDB.{Account, AccountUser, User} @argsline_desc """ This email and password combination is required for logging into the admin panel. @@ -25,12 +25,15 @@ defmodule EWalletDB.Repo.Seeds.UserSeed do email: args[:admin_email], password: args[:admin_password], metadata: %{}, + account_uuid: Account.get_master_account().uuid } case User.get_by_email(data.email) do nil -> case User.insert(data) do {:ok, user} -> + {:ok, _} = AccountUser.link(data.account_uuid, user.uuid) + writer.success(""" ID : #{user.id} Email : #{user.email} @@ -42,13 +45,16 @@ defmodule EWalletDB.Repo.Seeds.UserSeed do {:seeded_admin_user_email, user.email}, {:seeded_admin_user_password, user.password}, ] + {:error, changeset} -> writer.error(" Admin Panel user #{data.email} could not be inserted:") writer.print_errors(changeset) + _ -> writer.error(" Admin Panel user #{data.email} could not be inserted:") writer.error(" Unknown error.") end + %User{} = user -> writer.warn(""" ID : #{user.id} diff --git a/apps/ewallet_db/priv/repo/seeds_sample/00_token.exs b/apps/ewallet_db/priv/repo/seeds_sample/00_token.exs index c52dd4641..95201bb1d 100644 --- a/apps/ewallet_db/priv/repo/seeds_sample/00_token.exs +++ b/apps/ewallet_db/priv/repo/seeds_sample/00_token.exs @@ -8,29 +8,29 @@ defmodule EWalletDB.Repo.Seeds.TokenSampleSeed do %{ symbol: "OMG", name: "OmiseGO", - subunit_to_unit: 10_000, - genesis_amount: 10_000_000_000, # 1,000,000 OMG + subunit_to_unit: 1_000_000_000_000_000_000, + genesis_amount: 1_000_000_000_000_000_000_000_000, # 1,000,000 OMG account_name: "master_account" }, %{ symbol: "KNC", name: "Kyber", - subunit_to_unit: 1_000, - genesis_amount: 1_000_000_000, # 1,000,000 KNC + subunit_to_unit: 1_000_000_000_000_000_000, + genesis_amount: 1_000_000_000_000_000_000_000_000, # 1,000,000 KNC account_name: "master_account" }, %{ symbol: "BTC", name: "Bitcoin", - subunit_to_unit: 10_000, - genesis_amount: 10_000_000_000, # 1,000,000 BTC + subunit_to_unit: 1_000_000_000_000_000_000, + genesis_amount: 1_000_000_000_000_000_000_000_000, # 1,000,000 BTC account_name: "master_account" }, %{ symbol: "OEM", name: "One EM", - subunit_to_unit: 100, - genesis_amount: 100_000_000, # 1,000,000 OEM + subunit_to_unit: 1_000_000_000_000_000_000, + genesis_amount: 1_000_000_000_000_000_000_000_000, # 1,000,000 OEM account_name: "master_account" }, %{ diff --git a/apps/ewallet_db/priv/repo/seeds_sample/00_user.exs b/apps/ewallet_db/priv/repo/seeds_sample/00_user.exs index 9abe1b3ca..d3efe749e 100644 --- a/apps/ewallet_db/priv/repo/seeds_sample/00_user.exs +++ b/apps/ewallet_db/priv/repo/seeds_sample/00_user.exs @@ -1,9 +1,12 @@ defmodule EWalletDB.Repo.Seeds.UserSampleSeed do - alias EWalletDB.User + alias Ecto.UUID + alias EWallet.TransactionGate + alias EWalletDB.{Account, AccountUser, Token, User} @users_count 5 @username_prefix "user" @provider_prefix "provider_user_id" + @minimum_token_amount 1_000 def seed do [ @@ -24,25 +27,32 @@ defmodule EWalletDB.Repo.Seeds.UserSampleSeed do data = %{ provider_user_id: @provider_prefix <> running_string, username: @username_prefix <> running_string, - metadata: %{} + metadata: %{}, + account_uuid: Account.get_master_account().uuid } case User.get_by_provider_user_id(data.provider_user_id) do nil -> case User.insert(data) do {:ok, user} -> + :ok = give_token(user, Token.all(), @minimum_token_amount) + {:ok, _} = AccountUser.link(data.account_uuid, user.uuid) + writer.success(""" User ID : #{user.id} Provider user ID : #{user.provider_user_id} Username : #{user.username} """) + {:error, changeset} -> writer.error(" eWallet user #{data.email} could not be inserted:") writer.print_errors(changeset) + _ -> writer.error(" eWallet user #{data.email} could not be inserted:") writer.error(" Unknown error.") end + %User{} = user -> writer.warn(""" User ID : #{user.id} @@ -51,4 +61,23 @@ defmodule EWalletDB.Repo.Seeds.UserSampleSeed do """) end end + + defp give_token(user, tokens, minimum_amount) when is_list(tokens) do + Enum.each(tokens, fn token -> + give_token(user, token, minimum_amount) + end) + end + + defp give_token(user, token, minimum_amount) do + master_account = Account.get_master_account() + + TransactionGate.create(%{ + "from_address" => Account.get_primary_wallet(master_account).address, + "to_address" => User.get_primary_wallet(user).address, + "token_id" => token.id, + "amount" => :rand.uniform(10) * minimum_amount * token.subunit_to_unit, + "metadata" => %{}, + "idempotency_token" => UUID.generate() + }) + end end diff --git a/apps/ewallet_db/priv/repo/seeds_sample/01_exchange_pair.exs b/apps/ewallet_db/priv/repo/seeds_sample/01_exchange_pair.exs new file mode 100644 index 000000000..d4f0f7302 --- /dev/null +++ b/apps/ewallet_db/priv/repo/seeds_sample/01_exchange_pair.exs @@ -0,0 +1,63 @@ +defmodule EWalletDB.Repo.Seeds.ExchangePairSeed do + alias EWallet.Web.Preloader + alias EWalletDB.{Token, ExchangePair} + + @pairs [ + %{from_token_symbol: "BTC", to_token_symbol: "OEM", rate: 1_000_000}, + %{from_token_symbol: "ETH", to_token_symbol: "OMG", rate: 400}, + + %{from_token_symbol: "OEM", to_token_symbol: "BTC", rate: 0.000001}, + %{from_token_symbol: "OEM", to_token_symbol: "OMG", rate: 0.001}, + + %{from_token_symbol: "OMG", to_token_symbol: "OEM", rate: 1_000}, + %{from_token_symbol: "OMG", to_token_symbol: "BTC", rate: 0.0010}, + %{from_token_symbol: "OMG", to_token_symbol: "ETH", rate: 0.0025} + ] + + def seed do + [ + run_banner: "Seeding sample exchange pairs:", + argsline: [], + ] + end + + def run(writer, _args) do + Enum.each @pairs, fn args -> + run_with(writer, args) + end + end + + def run_with(writer, args) do + from_token = Token.get_by(symbol: args.from_token_symbol) + to_token = Token.get_by(symbol: args.to_token_symbol) + + case ExchangePair.get_by([from_token_uuid: from_token.uuid, to_token_uuid: to_token.uuid]) do + nil -> + {:ok, pair} = + ExchangePair.insert(%{ + from_token_uuid: from_token.uuid, + to_token_uuid: to_token.uuid, + rate: args.rate + }) + + {:ok, pair} = Preloader.preload_one(pair, [:from_token, :to_token]) + + writer.success(""" + Exchange Pair ID : #{pair.id} + From Token ID : #{pair.from_token.id} + To Token ID : #{pair.to_token.id} + Rate : #{pair.rate} + """) + + %ExchangePair{} = pair -> + {:ok, pair} = Preloader.preload_one(pair, [:from_token, :to_token]) + + writer.warn(""" + Exchange Pair ID : #{pair.id} + From Token ID : #{pair.from_token.id} + To Token ID : #{pair.to_token.id} + Rate : #{pair.rate} + """) + end + end +end diff --git a/apps/ewallet_db/priv/repo/seeds_sample/30_transaction_request_and_consumption.exs b/apps/ewallet_db/priv/repo/seeds_sample/30_transaction_request_and_consumption.exs new file mode 100644 index 000000000..e39c24fc1 --- /dev/null +++ b/apps/ewallet_db/priv/repo/seeds_sample/30_transaction_request_and_consumption.exs @@ -0,0 +1,165 @@ +defmodule EWalletDB.Repo.Seeds.TransactionRequestSeed do + alias Ecto.UUID + alias EWallet.TransactionConsumptionConfirmerGate + alias EWallet.Web.Preloader + alias EWalletDB.{Account, Token, TransactionRequest, TransactionConsumption, User} + + @num_requests 20 + @request_correlation_id_prefix "transaction_request_" + @request_values %{ + types: ["send", "receive"], + token_symbols: ["OMG", "ETH", "OEM", "BTC"], + allow_amount_overrides: [false], + require_confirmations: [true, false], + consumption_lifetimes: [nil, 10_000, 2_000_000], + max_consumptions: [nil, 1, 10, 100], + max_consumptions_per_user: [nil, 1, 10], + consumed: [true, false] + } + + @consumption_values %{ + provider_user_ids: ["provider_user_id01", "provider_user_id02", "provider_user_id03"] + } + + def seed do + [ + run_banner: "Seeding sample transaction requests:", + argsline: [], + ] + end + + def run(writer, _args) do + Enum.each 1..@num_requests, fn num -> + run_with(writer, num) + end + end + + def run_with(writer, num) do + correlation_id = @request_correlation_id_prefix <> to_string(num) + + case TransactionRequest.get_by(correlation_id: correlation_id) do + nil -> + {:ok, request} = request(correlation_id, @request_values, writer) + + if Enum.random(@request_values.consumed) == true do + consume(request, @consumption_values, writer) + end + + %TransactionRequest{} = request -> + {:ok, request} = Preloader.preload_one(request, [:token, :wallet]) + + writer.warn(""" + Transaction Request ID : #{request.id} + Correlation ID : #{request.correlation_id} + Type : #{request.type} + Amount (subunit) : #{request.amount} + Token : #{request.token.symbol} + Wallet address : #{request.wallet.address} + """) + end + end + + defp request(correlation_id, request_values, writer) do + correlation_id + |> prepare_request(request_values) + |> TransactionRequest.insert() + |> case do + {:ok, request} -> + {:ok, request} = Preloader.preload_one(request, [:token, :wallet]) + + writer.success(""" + Transaction Request ID : #{request.id} + Correlation ID : #{request.correlation_id} + Type : #{request.type} + Amount (subunit) : #{request.amount} + Token : #{request.token.symbol} + Wallet address : #{request.wallet.address} + """) + + {:ok, request} + + {:error, changeset} -> + writer.error(" Transaction request could not be inserted:") + writer.print_errors(changeset) + + _ -> + writer.error(" Transaction request could not be inserted:") + writer.error(" Unknown error.") + end + end + + defp consume(request, consumption_values, writer) do + request + |> prepare_consumption(consumption_values) + |> TransactionConsumption.insert() + |> case do + {:ok, consumption} -> + consumption = TransactionConsumption.approve(consumption) + + {:ok, consumption} = + Preloader.preload_one(consumption, [:token, :wallet, :transaction_request]) + + {:ok, consumption} = + TransactionConsumptionConfirmerGate.approve_and_confirm(request, consumption) + + writer.success(""" + Transaction Consumption ID : #{consumption.id} + Amount (subunit) : #{consumption.amount} + Token : #{consumption.token.symbol} + Wallet address : #{consumption.wallet.address} + """) + + {:ok, consumption} + + {:error, changeset} -> + writer.error(" Transaction consumption could not be inserted:") + writer.print_errors(changeset) + + _ -> + writer.error(" Transaction consumption could not be inserted:") + writer.error(" Unknown error.") + end + end + + defp prepare_request(correlation_id, attrs) do + account = Account.get_master_account() + token_symbol = rand(attrs.token_symbols) + + %{ + type: rand(attrs.types), + amount: :rand.uniform(100) * 1_000_000_000_000_000_000, + account_uuid: account.uuid, + correlation_id: correlation_id, + token_uuid: Token.get_by(symbol: token_symbol).uuid, + wallet_address: Account.get_primary_wallet(account).address, + allow_amount_override: rand(attrs.allow_amount_overrides), + require_confirmation: rand(attrs.require_confirmations), + consumption_lifetime: rand(attrs.consumption_lifetimes), + metadata: %{}, + encrypted_metadata: %{}, + expiration_date: nil, + max_consumptions: rand(attrs.max_consumptions), + max_consumptions_per_user: rand(attrs.max_consumptions_per_user), + exchange_account_id: nil, + exchange_wallet_address: nil, + } + end + + defp prepare_consumption(request, attrs) do + user = User.get_by(provider_user_id: Enum.random(attrs.provider_user_ids)) + user_wallet = User.get_primary_wallet(user) + + %{ + idempotency_token: UUID.generate(), + amount: request.amount, + user_uuid: user.uuid, + token_uuid: request.token.uuid, + transaction_request_uuid: request.uuid, + wallet_address: user_wallet.address, + estimated_request_amount: request.amount, + estimated_consumption_amount: request.amount, + } + end + + defp rand(list), do: Enum.random(list) +end diff --git a/apps/ewallet_db/priv/repo/seeds_test/01_user.exs b/apps/ewallet_db/priv/repo/seeds_test/01_user.exs index 336d3db24..a9f363acf 100644 --- a/apps/ewallet_db/priv/repo/seeds_test/01_user.exs +++ b/apps/ewallet_db/priv/repo/seeds_test/01_user.exs @@ -1,16 +1,18 @@ defmodule EWalletDB.Repo.Seeds.UserSeed do - alias EWalletDB.User + alias EWalletDB.{Account, AccountUser, User} @seed_data [ %{ email: System.get_env("E2E_TEST_ADMIN_EMAIL") || "test_admin@example.com", password: System.get_env("E2E_TEST_ADMIN_PASSWORD") || "password", metadata: %{}, + account_uuid: Account.get_master_account().uuid }, %{ email: System.get_env("E2E_TEST_ADMIN_1_EMAIL") || "test_admin_1@example.com", password: System.get_env("E2E_TEST_ADMIN_1_PASSWORD") || "password", metadata: %{}, + account_uuid: Account.get_master_account().uuid }, ] @@ -32,18 +34,23 @@ defmodule EWalletDB.Repo.Seeds.UserSeed do nil -> case User.insert(data) do {:ok, user} -> + {:ok, _} = AccountUser.link(data.account_uuid, user.uuid) + writer.success(""" ID : #{user.id} Email : #{user.email} Password : """) + {:error, changeset} -> writer.error(" Admin Panel user #{data.email} could not be inserted:") writer.print_errors(changeset) + _ -> writer.error(" Admin Panel user #{data.email} could not be inserted:") writer.error(" Unknown error.") end + %User{} = user -> writer.warn(""" ID : #{user.id} diff --git a/apps/ewallet_db/test/ewallet_db/transaction_request_test.exs b/apps/ewallet_db/test/ewallet_db/transaction_request_test.exs index 9f3b2bedf..27fa5ea49 100644 --- a/apps/ewallet_db/test/ewallet_db/transaction_request_test.exs +++ b/apps/ewallet_db/test/ewallet_db/transaction_request_test.exs @@ -33,6 +33,10 @@ defmodule EWalletDB.TransactionRequestTest do end end + describe "get_by/2" do + test_schema_get_by_allows_search_by(TransactionRequest, :correlation_id) + end + describe "expire_all/0" do test "expires all requests past their expiration date" do now = NaiveDateTime.utc_now()