From e89aa2b39530dedfdca9cdb9f314b10998cf4382 Mon Sep 17 00:00:00 2001 From: Carlos Souza Date: Tue, 21 Oct 2025 18:19:32 -0400 Subject: [PATCH 1/3] Save transaction CBOR --- lib/fester/indexer/db_indexer.ex | 13 ++++++++----- lib/fester/utxo.ex | 1 + .../20251021210246_add_txout_cbor_to_utxos.exs | 9 +++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 priv/repo/migrations/20251021210246_add_txout_cbor_to_utxos.exs diff --git a/lib/fester/indexer/db_indexer.ex b/lib/fester/indexer/db_indexer.ex index 87658ef..dd24e5c 100644 --- a/lib/fester/indexer/db_indexer.ex +++ b/lib/fester/indexer/db_indexer.ex @@ -74,7 +74,7 @@ defmodule Fester.DBIndexer do ) do Logger.info("Processing transaction with collaterals") - %{"collaterals" => collaterals_as_inputs} = transaction + %{"collaterals" => collaterals_as_inputs, "cbor" => cbor} = transaction # Wrapping the collateral return in a list to make it consistent with the # other calling of the process_transaction_outputs function. @@ -82,7 +82,7 @@ defmodule Fester.DBIndexer do if transaction["collateral_return"], do: [transaction["collateral_return"]], else: [] with :ok <- process_transaction_inputs(slot, collaterals_as_inputs), - :ok <- process_transaction_outputs(slot, collateral_return_as_outputs, tx_id) do + :ok <- process_transaction_outputs(slot, collateral_return_as_outputs, tx_id, cbor) do :ok else {:error, reason} -> @@ -95,11 +95,12 @@ defmodule Fester.DBIndexer do %{ "id" => tx_id, "inputs" => inputs, - "outputs" => outputs + "outputs" => outputs, + "cbor" => cbor } = transaction with :ok <- process_transaction_inputs(slot, inputs), - :ok <- process_transaction_outputs(slot, outputs, tx_id) do + :ok <- process_transaction_outputs(slot, outputs, tx_id, cbor) do :ok else {:error, reason} -> @@ -139,18 +140,20 @@ defmodule Fester.DBIndexer do :ok end - defp process_transaction_outputs(slot, outputs, tx_id) do + defp process_transaction_outputs(slot, outputs, tx_id, cbor) do utxo_attrs_list = outputs |> Enum.with_index() |> Enum.map(fn {output, idx} -> output_ref = "#{tx_id}##{idx}" %{"address" => address, "value" => value} = output + IO.inspect(output) %{ utxo_ref: output_ref, address: address, value: value, + txout_cbor: cbor, created_at_slot: slot } end) diff --git a/lib/fester/utxo.ex b/lib/fester/utxo.ex index 0940ceb..caa516f 100644 --- a/lib/fester/utxo.ex +++ b/lib/fester/utxo.ex @@ -9,6 +9,7 @@ defmodule Fester.Utxo do field :created_at_slot, :integer field :consumed_at_slot, :integer field :value, :map + field :txout_cbor, :string timestamps() end diff --git a/priv/repo/migrations/20251021210246_add_txout_cbor_to_utxos.exs b/priv/repo/migrations/20251021210246_add_txout_cbor_to_utxos.exs new file mode 100644 index 0000000..541bd6f --- /dev/null +++ b/priv/repo/migrations/20251021210246_add_txout_cbor_to_utxos.exs @@ -0,0 +1,9 @@ +defmodule Fester.Repo.Migrations.AddTxoutCborToUtxos do + use Ecto.Migration + + def change do + alter table(:utxos) do + add :txout_cbor, :text, null: false + end + end +end From 9af50469062bc7c8a03ca98464638cf8fce827de Mon Sep 17 00:00:00 2001 From: Carlos Souza Date: Tue, 21 Oct 2025 18:21:10 -0400 Subject: [PATCH 2/3] Nit --- lib/fester/indexer/db_indexer.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/fester/indexer/db_indexer.ex b/lib/fester/indexer/db_indexer.ex index dd24e5c..9d34ff5 100644 --- a/lib/fester/indexer/db_indexer.ex +++ b/lib/fester/indexer/db_indexer.ex @@ -147,7 +147,6 @@ defmodule Fester.DBIndexer do |> Enum.map(fn {output, idx} -> output_ref = "#{tx_id}##{idx}" %{"address" => address, "value" => value} = output - IO.inspect(output) %{ utxo_ref: output_ref, From 44f824cea1c1640c485d57aa4047e04e9aa52151 Mon Sep 17 00:00:00 2001 From: Carlos Souza Date: Sun, 26 Oct 2025 14:07:26 -0400 Subject: [PATCH 3/3] Add endpoint for Utxos Optimizes response for blaze-query --- lib/fester/indexer.ex | 12 +++++++++++- lib/fester_web/controllers/utxos_controller.ex | 11 +++++++++++ lib/fester_web/router.ex | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 lib/fester_web/controllers/utxos_controller.ex diff --git a/lib/fester/indexer.ex b/lib/fester/indexer.ex index 6aa86b8..7d3ecf3 100644 --- a/lib/fester/indexer.ex +++ b/lib/fester/indexer.ex @@ -5,11 +5,21 @@ defmodule Fester.Indexer do alias Fester.Utxo @doc """ - Returns the list of utxos for a given address. + Returns the list of utxos for a given address + optimized for consumption by blaze-query getUnspentOutputs """ def list_utxos_by_address(address) do from(u in Utxo, where: u.address == ^address and is_nil(u.consumed_at_slot)) |> Repo.all() + |> Enum.reduce(%{data: []}, &build_response/2) + end + + defp build_response(%Utxo{utxo_ref: utxo_ref, txout_cbor: cbor}, acc) do + [tx_hash, index] = String.split(utxo_ref, "#") + data = Map.get(acc, :data) + data = [%{tx_hash: tx_hash, index: index, txout_cbor: cbor} | data] + + Map.put(acc, :data, data) end @doc """ diff --git a/lib/fester_web/controllers/utxos_controller.ex b/lib/fester_web/controllers/utxos_controller.ex new file mode 100644 index 0000000..8c1dcef --- /dev/null +++ b/lib/fester_web/controllers/utxos_controller.ex @@ -0,0 +1,11 @@ +defmodule FesterWeb.UtxosController do + use FesterWeb, :controller + + alias Fester.Indexer + + def index(conn, %{"address" => address}) do + utxos = Indexer.list_utxos_by_address(address) + + json(conn, utxos) + end +end diff --git a/lib/fester_web/router.ex b/lib/fester_web/router.ex index ab6dea1..f7735f8 100644 --- a/lib/fester_web/router.ex +++ b/lib/fester_web/router.ex @@ -10,6 +10,7 @@ defmodule FesterWeb.Router do # Lists assets for an address get "/address/:address/assets", AssetsController, :index + get "/address/:address/utxos", UtxosController, :index end # Enable Swoosh mailbox preview in development