lib/tx_build/soroban_address_credentials.ex

defmodule Stellar.TxBuild.SorobanAddressCredentials do
  @moduledoc """
  `SorobanAddressCredentials` struct definition.
  """
  import Stellar.TxBuild.Validations,
    only: [validate_address: 1, validate_vec: 1]

  alias StellarBase.XDR.{Int64, SorobanAddressCredentials, UInt32}
  alias Stellar.TxBuild.{SCAddress, SCVec}

  @behaviour Stellar.TxBuild.XDR

  @type error :: {:error, atom()}
  @type validation :: {:ok, any()} | error()
  @type address :: SCAddress.t()
  @type nonce :: non_neg_integer()
  @type signature_expiration_ledger :: non_neg_integer()
  @type signature_args :: SCVec.t()

  @type t :: %__MODULE__{
          address: address(),
          nonce: nonce(),
          signature_expiration_ledger: signature_expiration_ledger(),
          signature_args: signature_args()
        }

  defstruct [
    :address,
    :nonce,
    :signature_expiration_ledger,
    :signature_args
  ]

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

  def new(args, _opts) when is_list(args) do
    address = Keyword.get(args, :address)
    nonce = Keyword.get(args, :nonce)
    signature_expiration_ledger = Keyword.get(args, :signature_expiration_ledger)
    signature_args = Keyword.get(args, :signature_args)

    with {:ok, address} <- validate_address(address),
         {:ok, nonce} <- validate_integer({nonce, :nonce}),
         {:ok, signature_expiration_ledger} <-
           validate_integer({signature_expiration_ledger, :signature_expiration_ledger}),
         {:ok, signature_args} <- validate_vec(signature_args) do
      %__MODULE__{
        address: address,
        nonce: nonce,
        signature_expiration_ledger: signature_expiration_ledger,
        signature_args: signature_args
      }
    end
  end

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

  @impl true
  def to_xdr(%__MODULE__{
        address: address,
        nonce: nonce,
        signature_expiration_ledger: signature_expiration_ledger,
        signature_args: signature_args
      }) do
    nonce = Int64.new(nonce)
    signature_expiration_ledger = UInt32.new(signature_expiration_ledger)
    signature_args = SCVec.to_xdr(signature_args)

    address
    |> SCAddress.to_xdr()
    |> SorobanAddressCredentials.new(nonce, signature_expiration_ledger, signature_args)
  end

  def to_xdr(_struct), do: {:error, :invalid_struct}

  @spec validate_integer(tuple()) :: validation()
  defp validate_integer({integer, _type}) when is_integer(integer), do: {:ok, integer}
  defp validate_integer({_integer, type}), do: {:error, :"invalid_#{type}"}
end