lib/tx_build/create_claimable_balance.ex

defmodule Stellar.TxBuild.CreateClaimableBalance do
  @moduledoc """
  `CreateClaimableBalance` struct definition.
  """
  import Stellar.TxBuild.Validations,
    only: [validate_asset: 1, validate_amount: 1, validate_optional_account: 1]

  alias Stellar.TxBuild.{Amount, Asset, Claimants, OptionalAccount}
  alias StellarBase.XDR.{OperationBody, OperationType, Operations.CreateClaimableBalance}

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

  @behaviour Stellar.TxBuild.XDR

  @type t :: %__MODULE__{
          asset: Asset.t(),
          amount: Amount.t(),
          claimants: Claimants.t(),
          source_account: OptionalAccount.t()
        }

  defstruct [:asset, :amount, :claimants, :source_account]

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

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

    with {:ok, asset} <- validate_asset({:asset, asset}),
         {:ok, amount} <- validate_amount({:amount, amount}),
         {:ok, claimants} <- validate_claimants(claimants),
         {:ok, source_account} <- validate_optional_account({:source_account, source_account}) do
      %__MODULE__{
        asset: asset,
        amount: amount,
        claimants: claimants,
        source_account: source_account
      }
    end
  end

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

  @impl true
  def to_xdr(%__MODULE__{
        asset: asset,
        amount: amount,
        claimants: claimants
      }) do
    op_type = OperationType.new(:CREATE_CLAIMABLE_BALANCE)
    asset = Asset.to_xdr(asset)
    amount = Amount.to_xdr(amount)
    claimants = Claimants.to_xdr(claimants)

    claimable_balance =
      CreateClaimableBalance.new(
        asset,
        amount,
        claimants
      )

    OperationBody.new(claimable_balance, op_type)
  end

  @spec validate_claimants(claimants :: Claimants.t()) :: validation()
  defp validate_claimants(claimants) do
    case claimants do
      %Claimants{} = claimants -> {:ok, claimants}
      _error -> {:error, :invalid_claimant_list}
    end
  end
end