From 6d676cfc995f8b3e5a0105d945190ee3ae791a71 Mon Sep 17 00:00:00 2001 From: Sebastian Borrazas Date: Wed, 12 Jun 2024 08:41:42 -0300 Subject: [PATCH] chore: move aexn tokens rendering to contract modules (#1807) --- lib/ae_mdw/aex141.ex | 79 ++++++----- lib/ae_mdw/aexn_tokens.ex | 128 ++++++++++++++++-- .../controllers/aex141_controller.ex | 20 +-- lib/ae_mdw_web/controllers/aex9_controller.ex | 12 +- .../controllers/aexn_token_controller.ex | 15 +- lib/ae_mdw_web/views/aexn_view.ex | 92 ------------- test/ae_mdw/aexn_tokens_test.exs | 123 ++++++++++++----- test/ae_mdw_web/views/aexn_view_test.exs | 81 ----------- 8 files changed, 264 insertions(+), 286 deletions(-) delete mode 100644 test/ae_mdw_web/views/aexn_view_test.exs diff --git a/lib/ae_mdw/aex141.ex b/lib/ae_mdw/aex141.ex index 8e688b0fe..caf0ed64e 100644 --- a/lib/ae_mdw/aex141.ex +++ b/lib/ae_mdw/aex141.ex @@ -13,6 +13,7 @@ defmodule AeMdw.Aex141 do alias AeMdw.Stats alias AeMdw.Txs alias AeMdw.Util + alias AeMdw.Validate import AeMdw.Util.Encoding @@ -72,15 +73,16 @@ defmodule AeMdw.Aex141 do end end - @spec fetch_owned_nfts(State.t(), pubkey(), pubkey(), cursor(), pagination()) :: + @spec fetch_owned_tokens(State.t(), binary(), cursor(), pagination(), map()) :: {:ok, {page_cursor(), [nft()], page_cursor()}} | {:error, Error.t()} - def fetch_owned_nfts(state, account_pk, contract_pk, cursor, pagination) do - with {:ok, cursor_key} <- deserialize_cursor(@ownership_table, cursor), - cursor_key <- update_cursor(cursor_key, account_pk, contract_pk) do + def fetch_owned_tokens(state, account_id, cursor, pagination, params) do + with {:ok, account_pk} <- Validate.id(account_id), + {:ok, filters} <- Util.convert_params(params, &convert_owned_tokens_param/1), + {:ok, cursor} <- deserialize_ownership_cursor(account_pk, cursor) do paginated_nfts = state - |> build_streamer(@ownership_table, cursor_key, account_pk, contract_pk) - |> Collection.paginate(pagination, &render_owned_nft/1, &serialize_cursor/1) + |> build_owned_tokens_streamer(account_pk, cursor, filters) + |> Collection.paginate(pagination, &render_owned_nft/1, &serialize_ownership_cursor/1) {:ok, paginated_nfts} end @@ -166,13 +168,20 @@ defmodule AeMdw.Aex141 do # # Private function # - defp build_streamer(state, Model.NftOwnership, cursor_key, account_pk, contract_pk) do + defp build_owned_tokens_streamer(state, account_pk, cursor, %{contract: contract_pk}) do key_boundary = - {{account_pk, contract_pk || <<>>, 0}, - {account_pk, contract_pk || Util.max_256bit_bin(), Util.max_int()}} + {{account_pk, contract_pk, Util.min_int()}, {account_pk, contract_pk, Util.max_int()}} fn direction -> - Collection.stream(state, Model.NftOwnership, direction, key_boundary, cursor_key) + Collection.stream(state, @ownership_table, direction, key_boundary, cursor) + end + end + + defp build_owned_tokens_streamer(state, account_pk, cursor, _params) do + key_boundary = {{account_pk, <<>>, 0}, {account_pk, Util.max_256bit_bin(), Util.max_int()}} + + fn direction -> + Collection.stream(state, @ownership_table, direction, key_boundary, cursor) end end @@ -196,16 +205,6 @@ defmodule AeMdw.Aex141 do defp deserialize_cursor(_table, nil), do: {:ok, nil} - defp deserialize_cursor(Model.NftOwnership, cursor_bin64) do - with {:ok, cursor_bin} <- Base.decode64(cursor_bin64), - {<<_pk1::256>>, <<_pk2::256>>, token_id} = cursor when is_integer(token_id) <- - :erlang.binary_to_term(cursor_bin) do - {:ok, cursor} - else - _invalid -> {:error, ErrInput.Cursor.exception(value: cursor_bin64)} - end - end - defp deserialize_cursor(Model.NftTemplate, cursor_bin64) do with {:ok, cursor_bin} <- Base.decode64(cursor_bin64), {<<_pk::256>>, template_id} = cursor when is_integer(template_id) <- @@ -237,6 +236,30 @@ defmodule AeMdw.Aex141 do end end + defp serialize_ownership_cursor({_account_pk, contract_pk, token_id}), + do: serialize_cursor({contract_pk, token_id}) + + defp deserialize_ownership_cursor(_account_pk, nil), do: {:ok, nil} + + defp deserialize_ownership_cursor(account_pk, cursor_bin64) do + with {:ok, cursor_bin} <- Base.decode64(cursor_bin64), + {<<_pk::256>> = contract_pk, token_id} when is_integer(token_id) <- + :erlang.binary_to_term(cursor_bin) do + {:ok, {account_pk, contract_pk, token_id}} + else + _invalid -> {:error, ErrInput.Cursor.exception(value: cursor_bin64)} + end + end + + defp convert_owned_tokens_param({"contract", contract_id}) do + with {:ok, contract_pk} <- Validate.id(contract_id) do + {:ok, {:contract, contract_pk}} + end + end + + defp convert_owned_tokens_param(other_param), + do: {:error, ErrInput.Query.exception(value: other_param)} + defp call_contract(state, contract_pk, entrypoint, args) do with true <- State.exists?(state, Model.AexnContract, {:aex141, contract_pk}), {:ok, {:variant, [0, 1], 1, {result}}} <- @@ -267,22 +290,6 @@ defmodule AeMdw.Aex141 do end end - defp update_cursor(nil, _account_pk, _contract_pk), do: nil - - defp update_cursor({account_pk, _contract_pk, _token} = cursor_key, account_pk, nil), - do: cursor_key - - defp update_cursor({account_pk, contract_pk, _token} = cursor_key, account_pk, contract_pk), - do: cursor_key - - defp update_cursor({account_pk, _contract_pk1, _token}, account_pk, contract_pk2), - do: {account_pk, contract_pk2, 0} - - defp update_cursor({_account_pk1, _contract_pk, _token}, account_pk2, contract_pk), - do: {account_pk2, contract_pk || <<>>, 0} - - defp update_cursor(cursor_key, _account_pk, _contract_pk), do: cursor_key - defp render_owned_nft({owner_pk, contract_pk, token_id}) do %{ contract_id: encode_contract(contract_pk), diff --git a/lib/ae_mdw/aexn_tokens.ex b/lib/ae_mdw/aexn_tokens.ex index 4e80ace98..7850c5dc2 100644 --- a/lib/ae_mdw/aexn_tokens.ex +++ b/lib/ae_mdw/aexn_tokens.ex @@ -3,12 +3,16 @@ defmodule AeMdw.AexnTokens do Context module for AEX-N tokens. """ + alias :aeser_api_encoder, as: Enc + alias AeMdw.Aex141 + alias AeMdw.Aex9 alias AeMdw.Collection alias AeMdw.Db.Model alias AeMdw.Db.State alias AeMdw.Error alias AeMdw.Error.Input, as: ErrInput - alias AeMdw.Node.Db + alias AeMdw.Stats + alias AeMdw.Txs alias AeMdw.Util alias AeMdw.Validate @@ -24,6 +28,7 @@ defmodule AeMdw.AexnTokens do @typep pagination :: Collection.direction_limit() @typep order_by() :: :name | :symbol | :creation @typep query :: %{binary() => binary()} + @typep aexn_contract() :: map() @max_sort_field_length 100 @@ -37,25 +42,37 @@ defmodule AeMdw.AexnTokens do creation: @aexn_creation_table } - @spec fetch_contract(State.t(), {aexn_type(), Db.pubkey()}) :: - {:ok, Model.aexn_contract()} | {:error, Error.t()} - def fetch_contract(state, {aexn_type, contract_pk}) do - with {:ok, m_aexn} <- State.get(state, Model.AexnContract, {aexn_type, contract_pk}), + @spec fetch_contract(State.t(), aexn_type(), binary(), boolean()) :: + {:ok, aexn_contract()} | {:error, Error.t()} + def fetch_contract(state, aexn_type, contract_id, v3?) do + with {:ok, contract_pk} <- Validate.id(contract_id), + {:ok, aexn_contract} <- State.get(state, Model.AexnContract, {aexn_type, contract_pk}), {:invalid, false} <- {:invalid, State.exists?(state, Model.AexnInvalidContract, {aexn_type, contract_pk})} do - {:ok, m_aexn} + {:ok, render_contract(state, aexn_contract, v3?)} else - :not_found -> - {:error, ErrInput.NotFound.exception(value: encode_contract(contract_pk))} - {:invalid, true} -> - {:error, ErrInput.AexnContractInvalid.exception(value: encode_contract(contract_pk))} + {:error, ErrInput.AexnContractInvalid.exception(value: contract_id)} + + {:error, reason} -> + {:error, reason} + + :not_found -> + {:error, ErrInput.NotFound.exception(value: contract_id)} end end - @spec fetch_contracts(State.t(), pagination(), aexn_type(), query(), order_by(), cursor() | nil) :: + @spec fetch_contracts( + State.t(), + pagination(), + aexn_type(), + query(), + order_by(), + cursor() | nil, + boolean() + ) :: {:ok, {cursor() | nil, [Model.aexn_contract()], cursor() | nil}} | {:error, Error.t()} - def fetch_contracts(state, pagination, aexn_type, query, order_by, cursor) do + def fetch_contracts(state, pagination, aexn_type, query, order_by, cursor, v3?) do with {:ok, cursor} <- deserialize_aexn_cursor(cursor), {:ok, params} <- validate_params(query), {:ok, filters} <- Util.convert_params(params, &convert_param/1) do @@ -64,7 +81,11 @@ defmodule AeMdw.AexnTokens do paginated_aexn_contracts = filters |> build_tokens_streamer(state, aexn_type, sorting_table, cursor) - |> Collection.paginate(pagination, & &1, &serialize_aexn_cursor(order_by, &1)) + |> Collection.paginate( + pagination, + &render_contract(state, &1, v3?), + &serialize_aexn_cursor(order_by, &1) + ) {:ok, paginated_aexn_contracts} end @@ -179,4 +200,85 @@ defmodule AeMdw.AexnTokens do end defp is_valid_cursor_term?(_other_term), do: false + + defp render_contract( + state, + Model.aexn_contract( + index: {:aex9, contract_pk} = index, + txi_idx: {txi, _idx}, + meta_info: {name, symbol, decimals}, + extensions: extensions + ), + v3? + ) do + initial_supply = + case State.get(state, Model.Aex9InitialSupply, contract_pk) do + {:ok, Model.aex9_initial_supply(amount: amount)} -> amount + :not_found -> 0 + end + + event_supply = + case State.get(state, Model.Aex9ContractBalance, contract_pk) do + {:ok, Model.aex9_contract_balance(amount: amount)} -> amount + :not_found -> 0 + end + + num_holders = Aex9.fetch_holders_count(state, contract_pk) + + response = %{ + name: name, + symbol: symbol, + decimals: decimals, + contract_id: encode_contract(contract_pk), + extensions: extensions, + initial_supply: initial_supply, + event_supply: event_supply, + holders: num_holders, + invalid: State.exists?(state, Model.AexnInvalidContract, index), + logs_count: Stats.fetch_aex9_logs_count(state, contract_pk) + } + + case v3? do + true -> + Map.put(response, :contract_tx_hash, Enc.encode(:tx_hash, Txs.txi_to_hash(state, txi))) + + false -> + Map.put(response, :contract_txi, txi) + end + end + + defp render_contract( + state, + Model.aexn_contract( + index: {:aex141, contract_pk} = index, + txi_idx: {txi, _idx}, + meta_info: {name, symbol, base_url, metadata_type}, + extensions: extensions + ), + v3? + ) do + %{ + name: name, + symbol: symbol, + base_url: base_url, + contract_txi: txi, + contract_id: encode_contract(contract_pk), + metadata_type: metadata_type, + extensions: extensions, + limits: Aex141.fetch_limits(state, contract_pk, v3?), + invalid: State.exists?(state, Model.AexnInvalidContract, index) + } + |> maybe_put_contract_tx_hash(state, txi, v3?) + |> Map.merge(Stats.fetch_nft_stats(state, contract_pk)) + end + + defp maybe_put_contract_tx_hash(data, state, txi, v3?) do + if v3? do + data + |> Map.put(:contract_tx_hash, Enc.encode(:tx_hash, Txs.txi_to_hash(state, txi))) + |> Map.delete(:contract_txi) + else + data + end + end end diff --git a/lib/ae_mdw_web/controllers/aex141_controller.ex b/lib/ae_mdw_web/controllers/aex141_controller.ex index 6018bc123..8876a95b2 100644 --- a/lib/ae_mdw_web/controllers/aex141_controller.ex +++ b/lib/ae_mdw_web/controllers/aex141_controller.ex @@ -108,25 +108,17 @@ defmodule AeMdwWeb.Aex141Controller do end @spec owned_nfts(Conn.t(), map()) :: Conn.t() | {:error, ErrInput.t()} - def owned_nfts(%Conn{assigns: assigns} = conn, %{"account_id" => account_id} = params) do + def owned_nfts(%Conn{assigns: assigns} = conn, %{"account_id" => account_id}) do %{ state: state, pagination: pagination, - cursor: cursor + cursor: cursor, + query: query } = assigns - with {:ok, account_pk} <- Validate.id(account_id), - {:ok, contract_pk} <- validate_optional_pubkey(params, "contract"), - {:ok, {prev_cursor, nfts, next_cursor}} <- - Aex141.fetch_owned_nfts(state, account_pk, contract_pk, cursor, pagination) do - Util.render(conn, prev_cursor, nfts, next_cursor) - end - end - - defp validate_optional_pubkey(params, param_name) do - case Map.fetch(params, param_name) do - {:ok, id} -> Validate.id(id) - :error -> {:ok, nil} + with {:ok, paginated_tokens} <- + Aex141.fetch_owned_tokens(state, account_id, cursor, pagination, query) do + Util.render(conn, paginated_tokens) end end end diff --git a/lib/ae_mdw_web/controllers/aex9_controller.ex b/lib/ae_mdw_web/controllers/aex9_controller.ex index 29ad58c08..83e68de16 100644 --- a/lib/ae_mdw_web/controllers/aex9_controller.ex +++ b/lib/ae_mdw_web/controllers/aex9_controller.ex @@ -196,8 +196,8 @@ defmodule AeMdwWeb.Aex9Controller do # defp by_contract_reply(%Conn{assigns: %{state: state}} = conn, contract_id) do with {:ok, contract_pk} <- Validate.id(contract_id, [:contract_pubkey]), - {:ok, m_aex9} <- AexnTokens.fetch_contract(state, {:aex9, contract_pk}) do - format_json(conn, %{data: render_contract(state, m_aex9, false)}) + {:ok, contract} <- AexnTokens.fetch_contract(state, :aex9, contract_pk, false) do + format_json(conn, %{data: contract}) end end @@ -205,8 +205,8 @@ defmodule AeMdwWeb.Aex9Controller do pagination = {:forward, false, 32_000, false} with {:ok, {_prev_cursor, aex9_tokens, _next_cursor}} <- - AexnTokens.fetch_contracts(state, pagination, :aex9, params, :name, nil) do - format_json(conn, render_contracts(state, aex9_tokens, false)) + AexnTokens.fetch_contracts(state, pagination, :aex9, params, :name, nil, false) do + format_json(conn, aex9_tokens) end end @@ -214,8 +214,8 @@ defmodule AeMdwWeb.Aex9Controller do pagination = {:forward, false, 32_000, false} with {:ok, {_prev_cursor, aex9_tokens, _next_cursor}} <- - AexnTokens.fetch_contracts(state, pagination, :aex9, params, :symbol, nil) do - format_json(conn, render_contracts(state, aex9_tokens, false)) + AexnTokens.fetch_contracts(state, pagination, :aex9, params, :symbol, nil, false) do + format_json(conn, aex9_tokens) end end diff --git a/lib/ae_mdw_web/controllers/aexn_token_controller.ex b/lib/ae_mdw_web/controllers/aexn_token_controller.ex index 851adde60..b3f16ccfb 100644 --- a/lib/ae_mdw_web/controllers/aexn_token_controller.ex +++ b/lib/ae_mdw_web/controllers/aexn_token_controller.ex @@ -14,8 +14,6 @@ defmodule AeMdwWeb.AexnTokenController do alias AeMdwWeb.Util alias Plug.Conn - import AeMdwWeb.AexnView - use AeMdwWeb, :controller require Model @@ -174,17 +172,18 @@ defmodule AeMdwWeb.AexnTokenController do aexn_type, query, order_by, - cursor + cursor, + v3? ) do - Util.render(conn, paginated_contracts, &render_contract(state, &1, v3?)) + Util.render(conn, paginated_contracts) end end defp aexn_contract(%Conn{assigns: %{state: state} = assigns} = conn, contract_id, aexn_type) do - with {:ok, contract_pk} <- Validate.id(contract_id, [:contract_pubkey]), - {:ok, m_aexn} <- AexnTokens.fetch_contract(state, {aexn_type, contract_pk}) do - v3? = Map.get(assigns, :v3?, true) - format_json(conn, render_contract(state, m_aexn, v3?)) + v3? = Map.get(assigns, :v3?, true) + + with {:ok, aexn_contract} <- AexnTokens.fetch_contract(state, aexn_type, contract_id, v3?) do + format_json(conn, aexn_contract) end end diff --git a/lib/ae_mdw_web/views/aexn_view.ex b/lib/ae_mdw_web/views/aexn_view.ex index 26c9a060c..0dcff02a8 100644 --- a/lib/ae_mdw_web/views/aexn_view.ex +++ b/lib/ae_mdw_web/views/aexn_view.ex @@ -3,14 +3,10 @@ defmodule AeMdwWeb.AexnView do Renders data for balance(s) endpoints. """ - alias :aeser_api_encoder, as: Enc alias AeMdw.Db.Model alias AeMdw.Db.State alias AeMdw.Db.Util alias AeMdw.Node.Db, as: NodeDb - alias AeMdw.Stats - alias AeMdw.Aex9 - alias AeMdw.Aex141 alias AeMdw.Sync.DexCache alias AeMdw.Txs @@ -118,84 +114,6 @@ defmodule AeMdwWeb.AexnView do do_render_event_balance(state, contract_pk, account_pk, txi, log_idx, amount) end - @spec render_contract(State.t(), Model.aexn_contract(), boolean()) :: aexn_contract() - def render_contract( - state, - Model.aexn_contract( - index: {_type, contract_pk} = index, - txi_idx: {txi, _idx}, - meta_info: {name, symbol, decimals}, - extensions: extensions - ), - v3? - ) do - initial_supply = - case State.get(state, Model.Aex9InitialSupply, contract_pk) do - {:ok, Model.aex9_initial_supply(amount: amount)} -> amount - :not_found -> 0 - end - - event_supply = - case State.get(state, Model.Aex9ContractBalance, contract_pk) do - {:ok, Model.aex9_contract_balance(amount: amount)} -> amount - :not_found -> 0 - end - - invalid? = State.exists?(state, Model.AexnInvalidContract, index) - - num_holders = Aex9.fetch_holders_count(state, contract_pk) - - response = %{ - name: name, - symbol: symbol, - decimals: decimals, - contract_id: encode_contract(contract_pk), - extensions: extensions, - initial_supply: initial_supply, - event_supply: event_supply, - holders: num_holders, - invalid: invalid? - } - - case v3? do - true -> - Map.put(response, :contract_tx_hash, Enc.encode(:tx_hash, Txs.txi_to_hash(state, txi))) - - false -> - Map.put(response, :contract_txi, txi) - end - end - - def render_contract( - state, - Model.aexn_contract( - index: {_type, contract_pk} = index, - txi_idx: {txi, _idx}, - meta_info: {name, symbol, base_url, metadata_type}, - extensions: extensions - ), - v3? - ) do - %{ - name: name, - symbol: symbol, - base_url: base_url, - contract_txi: txi, - contract_id: encode_contract(contract_pk), - metadata_type: metadata_type, - extensions: extensions, - limits: Aex141.fetch_limits(state, contract_pk, v3?), - invalid: State.exists?(state, Model.AexnInvalidContract, index) - } - |> maybe_put_contract_tx_hash(state, txi, v3?) - |> Map.merge(Stats.fetch_nft_stats(state, contract_pk)) - end - - @spec render_contracts(State.t(), [Model.aexn_contract()], boolean()) :: [aexn_contract()] - def render_contracts(state, aexn_contracts, v3?) do - Enum.map(aexn_contracts, &render_contract(state, &1, v3?)) - end - @spec sender_transfer_to_map(State.t(), account_transfer_key()) :: map() def sender_transfer_to_map(state, key), do: do_transfer_to_map(state, key) @@ -336,14 +254,4 @@ defmodule AeMdwWeb.AexnView do Map.put(json, :call_txi, call_txi) end end - - defp maybe_put_contract_tx_hash(data, state, txi, v3?) do - if v3? do - data - |> Map.put(:contract_tx_hash, Enc.encode(:tx_hash, Txs.txi_to_hash(state, txi))) - |> Map.delete(:contract_txi) - else - data - end - end end diff --git a/test/ae_mdw/aexn_tokens_test.exs b/test/ae_mdw/aexn_tokens_test.exs index b1a29ce72..5cc19e0c4 100644 --- a/test/ae_mdw/aexn_tokens_test.exs +++ b/test/ae_mdw/aexn_tokens_test.exs @@ -11,7 +11,6 @@ defmodule AeMdw.AexnTokensTest do import AeMdw.Util.Encoding, only: [encode_contract: 1] import AeMdw.TestUtil, only: [empty_state: 0] - import AeMdwWeb.AexnView require Model @@ -35,21 +34,14 @@ defmodule AeMdw.AexnTokensTest do m_tx = Model.tx(index: txi, id: decoded_tx_hash) - Database.dirty_write(Model.AexnContract, m_aexn) - Database.dirty_write(Model.Tx, m_tx) + state = + state + |> State.put(Model.AexnContract, m_aexn) + |> State.put(Model.Tx, m_tx) contract_id = encode_contract(contract_pk) - assert {:ok, m_aex9} = AexnTokens.fetch_contract(state, {:aex9, contract_pk}) - - assert %{ - name: ^name, - symbol: ^symbol, - decimals: ^decimals, - contract_txi: ^txi, - contract_id: ^contract_id, - extensions: ^extensions - } = render_contract(state, m_aex9, false) + assert {:ok, aex9_contract} = AexnTokens.fetch_contract(state, :aex9, contract_pk, true) assert %{ name: ^name, @@ -58,7 +50,7 @@ defmodule AeMdw.AexnTokensTest do contract_tx_hash: ^tx_hash, contract_id: ^contract_id, extensions: ^extensions - } = render_contract(state, m_aex9, true) + } = aex9_contract end test "returns a AEX-141 contract meta info" do @@ -89,17 +81,7 @@ defmodule AeMdw.AexnTokensTest do contract_id = encode_contract(contract_pk) - assert {:ok, m_aex141} = AexnTokens.fetch_contract(state, {:aex141, contract_pk}) - - assert %{ - name: ^name, - symbol: ^symbol, - base_url: ^base_url, - contract_txi: ^txi, - contract_id: ^contract_id, - metadata_type: ^type, - extensions: ^extensions - } = render_contract(state, m_aex141, false) + assert {:ok, aex141_contract} = AexnTokens.fetch_contract(state, :aex141, contract_pk, true) assert %{ name: ^name, @@ -109,7 +91,7 @@ defmodule AeMdw.AexnTokensTest do contract_id: ^contract_id, metadata_type: ^type, extensions: ^extensions - } = render_contract(state, m_aex141, true) + } = aex141_contract end test "returns input error on AEX9 not found" do @@ -117,7 +99,7 @@ defmodule AeMdw.AexnTokensTest do contract_pk = Validate.id!(contract_id) assert {:error, %ErrInput{reason: ErrInput.NotFound}} = - AexnTokens.fetch_contract(State.new(), {:aex9, contract_pk}) + AexnTokens.fetch_contract(State.new(), :aex9, contract_pk, false) end test "returns input error AEX141 not found" do @@ -125,7 +107,7 @@ defmodule AeMdw.AexnTokensTest do contract_pk = Validate.id!(contract_id) assert {:error, %ErrInput{reason: ErrInput.NotFound}} = - AexnTokens.fetch_contract(State.new(), {:aex141, contract_pk}) + AexnTokens.fetch_contract(State.new(), :aex141, contract_pk, false) end end @@ -175,11 +157,36 @@ defmodule AeMdw.AexnTokensTest do pagination = {:forward, false, 10, false} - assert {:ok, {nil, [^m2, ^m1], nil}} = - AexnTokens.fetch_contracts(state, pagination, :aex9, %{}, :name, nil) + assert {:ok, {nil, [contract1, contract2], nil}} = + AexnTokens.fetch_contracts(state, pagination, :aex9, %{}, :name, nil, false) + + assert %{ + contract_id: "ct_11111111111111111111111111111118qjnEr", + contract_txi: 123_456_789, + decimals: 18, + event_supply: 0, + extensions: ["extA1"], + holders: 0, + initial_supply: 0, + invalid: false, + logs_count: 0, + name: "TokenA1", + symbol: "TKA1" + } = contract1 - assert {:ok, {nil, [^m1, ^m2], nil}} = - AexnTokens.fetch_contracts(state, pagination, :aex9, %{}, :creation, nil) + assert %{ + contract_id: "ct_1111111111111111111111111111111Hrt6FG", + contract_txi: 123_456_788, + decimals: 18, + event_supply: 0, + extensions: ["extB1"], + holders: 0, + initial_supply: 0, + invalid: false, + logs_count: 0, + name: "TokenB1", + symbol: "TKB1" + } = contract2 end test "returns AEX-141 contracts sorted by creation" do @@ -224,14 +231,58 @@ defmodule AeMdw.AexnTokensTest do Model.AexnContractName, Model.aexn_contract_name(index: {:aex141, "TokenA1", contract_pk2}) ) + |> State.put(Model.Tx, Model.tx(index: txi1, id: <<1::256>>)) + |> State.put(Model.Tx, Model.tx(index: txi2, id: <<2::256>>)) pagination = {:forward, false, 10, false} - assert {:ok, {nil, [^m2, ^m1], nil}} = - AexnTokens.fetch_contracts(state, pagination, :aex141, %{}, :name, nil) + assert {:ok, {nil, [contract2, contract1], nil}} = + AexnTokens.fetch_contracts(state, pagination, :aex141, %{}, :name, nil, false) + + assert %{ + base_url: "", + contract_id: "ct_1111111111111111111111111111111Hrt6FG", + contract_txi: 123_456_788, + extensions: ["extB1"], + invalid: false, + limits: nil, + metadata_type: :url, + name: "TokenB1", + nft_owners: 0, + nfts_amount: 0, + symbol: "TKB1" + } = contract1 + + assert %{ + base_url: "", + contract_id: "ct_11111111111111111111111111111118qjnEr", + contract_txi: 123_456_789, + extensions: ["extA1"], + invalid: false, + limits: nil, + metadata_type: :url, + name: "TokenA1", + nft_owners: 0, + nfts_amount: 0, + symbol: "TKA1" + } = contract2 + + assert {:ok, {nil, [contract1, _contract2], nil}} = + AexnTokens.fetch_contracts(state, pagination, :aex141, %{}, :creation, nil, true) - assert {:ok, {nil, [^m1, ^m2], nil}} = - AexnTokens.fetch_contracts(state, pagination, :aex141, %{}, :creation, nil) + assert %{ + base_url: "", + contract_id: "ct_1111111111111111111111111111111Hrt6FG", + contract_tx_hash: "th_11111111111111111111111111111118qjnEr", + extensions: ["extB1"], + invalid: false, + limits: nil, + metadata_type: :url, + name: "TokenB1", + nft_owners: 0, + nfts_amount: 0, + symbol: "TKB1" + } = contract1 end end end diff --git a/test/ae_mdw_web/views/aexn_view_test.exs b/test/ae_mdw_web/views/aexn_view_test.exs deleted file mode 100644 index f481e0f2f..000000000 --- a/test/ae_mdw_web/views/aexn_view_test.exs +++ /dev/null @@ -1,81 +0,0 @@ -defmodule AeMdwWeb.AexnViewTest do - use ExUnit.Case - - alias :aeser_api_encoder, as: Enc - alias AeMdw.Db.Model - alias AeMdw.Db.MemStore - alias AeMdw.Db.NullStore - alias AeMdw.Db.State - alias AeMdwWeb.AexnView - - import AeMdw.Util.Encoding - - require Model - - describe "render_contract/3" do - test "returns NA for invalid amount of holders" do - contract_pk = :crypto.strong_rand_bytes(32) - contract_id = encode_contract(contract_pk) - aex9_meta_info = {name, symbol, decimals} = {"Token1", "TK1", 18} - txi = 1_123_456_789 - decoded_tx_hash = <> - tx_hash = Enc.encode(:tx_hash, decoded_tx_hash) - - extensions = ["ext1", "ext2"] - - m_tx = Model.tx(index: txi, id: decoded_tx_hash) - - m_aex9 = - Model.aexn_contract( - index: {:aex9, contract_pk}, - txi_idx: {txi, -1}, - meta_info: aex9_meta_info, - extensions: extensions - ) - - state = - NullStore.new() - |> MemStore.new() - |> State.new() - |> State.put(Model.Tx, m_tx) - - assert %{ - name: ^name, - symbol: ^symbol, - decimals: ^decimals, - contract_txi: ^txi, - contract_id: ^contract_id, - extensions: ^extensions, - holders: 0, - invalid: false - } = AexnView.render_contract(state, m_aex9, false) - - assert %{ - name: ^name, - symbol: ^symbol, - decimals: ^decimals, - contract_tx_hash: ^tx_hash, - contract_id: ^contract_id, - extensions: ^extensions, - invalid: false - } = AexnView.render_contract(state, m_aex9, true) - - State.put( - state, - Model.AexnInvalidContract, - Model.aexn_invalid_contract(index: {:aex9, contract_pk}) - ) - - assert %{ - name: ^name, - symbol: ^symbol, - decimals: ^decimals, - contract_txi: ^txi, - contract_id: ^contract_id, - extensions: ^extensions, - holders: 0, - invalid: true - } = AexnView.render_contract(state, m_aex9, false) - end - end -end