lib/tx_build/invoke_host_function.ex

defmodule Stellar.TxBuild.InvokeHostFunction do
  @moduledoc """
  Performs the following operations:
  - Invokes contract functions.
  - Upload WASM of the new contracts.
  - Deploys new contracts using the uploaded WASM or built-in implementations.
  """

  import Stellar.TxBuild.Validations, only: [validate_optional_account: 1]

  alias Stellar.TxBuild.{HostFunction, OptionalAccount}

  alias StellarBase.XDR.Operations.InvokeHostFunction

  alias StellarBase.XDR.{
    HostFunctionList100,
    OperationBody,
    OperationType
  }

  @behaviour Stellar.TxBuild.XDR

  @type functions() :: list(HostFunction.t())
  @type validation :: {:ok, any()} | {:error, atom()}

  @type t :: %__MODULE__{
          functions: functions(),
          source_account: OptionalAccount.t()
        }

  defstruct [:functions, :source_account]

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

  def new(args, _opts) when is_list(args) do
    functions = Keyword.get(args, :functions)
    source_account = Keyword.get(args, :source_account)

    with {:ok, functions} <- validate_host_function_list(functions),
         {:ok, source_account} <- validate_optional_account({:source_account, source_account}) do
      %__MODULE__{
        functions: functions,
        source_account: source_account
      }
    end
  end

  def new(_args, _opts), do: {:error, :invalid_operation_attributes}

  @impl true
  def to_xdr(%__MODULE__{
        functions: functions
      }) do
    op_type = OperationType.new(:INVOKE_HOST_FUNCTION)

    functions
    |> Enum.map(&HostFunction.to_xdr/1)
    |> HostFunctionList100.new()
    |> InvokeHostFunction.new()
    |> OperationBody.new(op_type)
  end

  @spec validate_host_function_list(functions :: functions()) :: validation()
  defp validate_host_function_list(functions) do
    if Enum.all?(functions, &is_host_function?/1),
      do: {:ok, functions},
      else: {:error, :invalid_host_function_list}
  end

  @spec is_host_function?(function :: HostFunction.t()) :: boolean()
  defp is_host_function?(%HostFunction{}), do: true
  defp is_host_function?(_function), do: false
end