lib/tx_build/transaction.ex

defmodule Stellar.TxBuild.Transaction do
  @moduledoc """
  `Transaction` struct definition.
  """

  alias Stellar.TxBuild.{
    Account,
    BaseFee,
    Operations,
    SequenceNumber,
    Memo,
    Preconditions
  }

  alias StellarBase.XDR.{Ext, Transaction}

  @behaviour Stellar.TxBuild.XDR

  @type validation :: {:ok, struct()} | {:error, atom()}

  @type t :: %__MODULE__{
          source_account: Account.t(),
          sequence_number: SequenceNumber.t(),
          base_fee: BaseFee.t(),
          memo: Memo.t(),
          preconditions: Preconditions.t(),
          operations: Operations.t()
        }

  defstruct [:source_account, :sequence_number, :base_fee, :memo, :preconditions, :operations]

  @impl true
  def new(args, opts \\ [])

  def new(args, _opts) do
    source_account = Keyword.get(args, :source_account)
    sequence_number = Keyword.get(args, :sequence_number)
    base_fee = Keyword.get(args, :base_fee)
    preconditions = Keyword.get(args, :preconditions)
    memo = Keyword.get(args, :memo)
    operations = Keyword.get(args, :operations)

    with {:ok, source_account} <- validate_source_account(source_account),
         {:ok, sequence_number} <- validate_sequence_number(sequence_number),
         {:ok, base_fee} <- validate_base_fee(base_fee),
         {:ok, preconditions} <- validate_preconditions(preconditions),
         {:ok, memo} <- validate_memo(memo),
         {:ok, operations} <- validate_operations(operations) do
      %__MODULE__{
        source_account: source_account,
        sequence_number: sequence_number,
        base_fee: base_fee,
        preconditions: preconditions,
        memo: memo,
        operations: operations
      }
    end
  end

  @impl true
  def to_xdr(%__MODULE__{
        source_account: source_account,
        sequence_number: sequence_number,
        base_fee: base_fee,
        preconditions: preconditions,
        memo: memo,
        operations: operations
      }) do
    Transaction.new(
      Account.to_xdr(source_account),
      BaseFee.to_xdr(base_fee),
      SequenceNumber.to_xdr(sequence_number),
      Preconditions.to_xdr(preconditions),
      Memo.to_xdr(memo),
      Operations.to_xdr(operations),
      Ext.new()
    )
  end

  @spec validate_source_account(source_account :: Account.t()) :: validation()
  defp validate_source_account(%Account{} = source_account), do: {:ok, source_account}
  defp validate_source_account(_source_account), do: {:error, :invalid_source_account}

  @spec validate_sequence_number(sequence_number :: SequenceNumber.t()) :: validation()
  defp validate_sequence_number(%SequenceNumber{} = sequence_number), do: {:ok, sequence_number}
  defp validate_sequence_number(_sequence_number), do: {:error, :invalid_sequence_number}

  @spec validate_base_fee(base_fee :: BaseFee.t()) :: validation()
  defp validate_base_fee(%BaseFee{} = base_fee), do: {:ok, base_fee}
  defp validate_base_fee(_base_fee), do: {:error, :invalid_base_fee}

  @spec validate_operations(preconditions :: Preconditions.t()) :: validation()
  defp validate_preconditions(%Preconditions{} = preconditions), do: {:ok, preconditions}
  defp validate_preconditions(_preconditions), do: {:error, :invalid_preconditions}

  @spec validate_memo(memo :: Memo.t()) :: validation()
  defp validate_memo(%Memo{} = memo), do: {:ok, memo}
  defp validate_memo(_memo), do: {:error, :invalid_memo}

  @spec validate_operations(operations :: Operations.t()) :: validation()
  defp validate_operations(%Operations{} = operations), do: {:ok, operations}
  defp validate_operations(_operations), do: {:error, :invalid_operations}
end