defmodule Stellar.TxBuild.Asset do
@moduledoc """
`Asset` struct definition.
"""
alias Stellar.TxBuild.AccountID
alias StellarBase.XDR.{AlphaNum4, AlphaNum12, AssetCode4, AssetCode12, Asset, AssetType, Void}
@behaviour Stellar.TxBuild.XDR
@type issuer :: AccountID.t() | nil
@type validation :: {:ok, any()} | {:error, atom()}
@type t :: %__MODULE__{code: String.t(), issuer: issuer(), type: atom()}
defstruct [:code, :issuer, :type]
@impl true
def new(args, opts \\ [])
def new(:native, _opts) do
%__MODULE__{code: "XLM", issuer: nil, type: :native}
end
def new({code, issuer}, _opts), do: new(code: code, issuer: issuer)
def new([code: code, issuer: issuer], _opts) do
code = code |> to_string() |> String.trim()
with {:ok, code} <- validate_asset_code(code),
{:ok, issuer} <- validate_asset_issuer(issuer) do
%__MODULE__{type: asset_type(code), code: code, issuer: issuer}
end
end
def new(_args, _opts), do: {:error, :invalid_asset}
@impl true
def to_xdr(%__MODULE__{type: :native}) do
Asset.new(Void.new(), AssetType.new(:ASSET_TYPE_NATIVE))
end
def to_xdr(%__MODULE__{code: code, issuer: issuer, type: :alpha_num4}) do
asset_type = AssetType.new(:ASSET_TYPE_CREDIT_ALPHANUM4)
issuer = AccountID.to_xdr(issuer)
code
|> AssetCode4.new()
|> AlphaNum4.new(issuer)
|> Asset.new(asset_type)
end
def to_xdr(%__MODULE__{code: code, issuer: issuer, type: :alpha_num12}) do
asset_type = AssetType.new(:ASSET_TYPE_CREDIT_ALPHANUM12)
issuer = AccountID.to_xdr(issuer)
code
|> AssetCode12.new()
|> AlphaNum12.new(issuer)
|> Asset.new(asset_type)
end
@spec validate_asset_code(code :: String.t()) :: validation()
defp validate_asset_code(code) when is_bitstring(code) and byte_size(code) < 13, do: {:ok, code}
defp validate_asset_code(_code), do: {:error, :invalid_asset_code}
@spec validate_asset_issuer(issuer :: String.t()) :: validation()
defp validate_asset_issuer(issuer) do
case AccountID.new(issuer) do
%AccountID{} = issuer -> {:ok, issuer}
_error -> {:error, :invalid_asset_issuer}
end
end
@spec asset_type(code :: String.t()) :: atom()
defp asset_type(code) when byte_size(code) < 5, do: :alpha_num4
defp asset_type(_code), do: :alpha_num12
end