lib/stake_pool.ex

defmodule Cardanoex.StakePool do
  alias Cardanoex.Backend
  alias Cardanoex.Util
  alias Cardanoex.Transaction

  @moduledoc """
  The stake pool module let's you manage stake pools.
  """

  @type stake_pool :: %{
          id: String.t(),
          metrics: %{
            non_myopic_member_rewards: %{
              quantity: non_neg_integer(),
              unit: String.t()
            }
          },
          relative_stake: %{
            quantity: float(),
            unit: String.t()
          },
          saturation: float(),
          produced_blocks: %{
            quantity: non_neg_integer(),
            unit: String.t()
          },
          cost: %{
            quantity: non_neg_integer(),
            unit: String.t()
          },
          margin: %{
            quantity: float(),
            unit: String.t()
          },
          pledge: %{
            quantity: non_neg_integer(),
            unit: String.t()
          },
          metadata: %{
            ticker: String.t(),
            name: String.t(),
            description: String.t(),
            homepage: String.t()
          },
          retirement: %{
            epoch_number: non_neg_integer(),
            epoch_start_time: String.t()
          },
          flags: list(String.t())
        }

  @type stake_key :: %{
          ours:
            list(%{
              index: non_neg_integer(),
              key: String.t(),
              stake: %{quantity: non_neg_integer(), unit: String.t()},
              reward_balance: %{quantity: non_neg_integer(), unit: String.t()},
              delegation: %{
                active: %{
                  status: String.t(),
                  target: String.t()
                },
                next:
                  list(%{
                    status: String.t(),
                    target: String.t(),
                    changes_at: %{
                      epoch_number: non_neg_integer(),
                      epoch_start_time: String.t()
                    }
                  })
              }
            }),
          foreign:
            list(%{
              key: String.t(),
              stake: %{
                quantity: non_neg_integer(),
                unit: String.t()
              },
              reward_balance: %{
                quantity: non_neg_integer(),
                unit: String.t()
              }
            }),
          none: %{
            stake: %{
              quantity: non_neg_integer(),
              unit: String.t()
            }
          }
        }

  @type fee_estimation :: %{
          estimated_min: %{quantity: non_neg_integer(), unit: String.t()},
          estimated_max: %{quantity: non_neg_integer(), unit: String.t()},
          minimum_coins:
            list(%{
              quantity: non_neg_integer(),
              unit: String.t()
            }),
          deposit: %{quantity: non_neg_integer(), unit: String.t()}
        }

  @spec list(non_neg_integer()) :: {:error, String.t()} | {:ok, list(stake_pool)}
  @doc """
  List all known stake pools ordered by descending non_myopic_member_rewards. The non_myopic_member_rewards — and thus the ordering — depends on the `stake` parameter.

  Some pools may also have metadata attached to them.

  ### Options
    * stake - The stake the user intends to delegate in Lovelace. Required.
  """
  def list(stake) do
    case Backend.list_stake_pools(stake) do
      {:ok, stake_pools} -> {:ok, Enum.map(stake_pools, fn s -> Util.keys_to_atom(s) end)}
      {:error, message} -> {:error, message}
    end
  end

  @spec list_stake_keys(String.t()) :: {:error, String.t()} | {:ok, stake_key()}
  @doc """
  List stake-keys relevant to the wallet, and how much ada is associated with each.

  ### Options
    * `wallet_id` - hex based string. 40 characters
  """
  def list_stake_keys(wallet_id) do
    case Backend.list_stake_keys(wallet_id) do
      {:ok, stake_keys} -> {:ok, Util.keys_to_atom(stake_keys)}
      {:error, message} -> {:error, message}
    end
  end

  @spec view_maintenance_actions ::
          {:error, String.t()}
          | {:ok,
             %{
               gc_stake_pools: %{
                 status: String.t(),
                 last_run: String.t()
               }
             }}
  @doc """
  Returns the current status of the stake pools maintenance actions.
  """
  def view_maintenance_actions do
    case Backend.view_maintenance_actions() do
      {:ok, actions} -> {:ok, Util.keys_to_atom(actions)}
      {:error, message} -> {:error, message}
    end
  end

  @spec trigger_maintenance_action(String.t()) :: :ok | {:error, String.t()}
  @doc """
  Performs maintenance actions on stake pools, such as triggering metadata garbage collection.

  Actions may not be instantaneous.
  """
  def trigger_maintenance_action(action) do
    case Backend.trigger_maintenance_action(action) do
      {:ok, _result} -> :ok
      {:error, message} -> {:error, message}
    end
  end

  @spec estimate_fee(String.t()) :: {:error, String.t()} | {:ok, fee_estimation}
  @doc """
  Estimate fee for joining or leaving a stake pool. Note that it is an estimation because a delegation induces a transaction for which coins have to be selected randomly within the wallet. Because of this randomness, fees can only be estimated.

  ### Options
    * `wallet_id` - hex based string. 40 characters
  """
  def estimate_fee(wallet_id) do
    case Backend.delegation_fees(wallet_id) do
      {:ok, estimated_fees} -> {:ok, Util.keys_to_atom(estimated_fees)}
      {:error, message} -> {:error, message}
    end
  end

  @spec join(String.t(), String.t(), String.t()) ::
          {:error, String.t()} | {:ok, Transaction.transaction()}
  @doc """
  Delegate all (current and future) addresses from the given wallet to the given stake pool.

  *Note:* Bech32-encoded stake pool identifiers can vary in length.

  ### Options
    * `wallet_id` - hex based string. 40 characters
    * `stake_pool_id` - hex|bech32 string
    * `passphrase` - Wallet passphrase
  """
  def join(wallet_id, stake_pool_id, passphrase) do
    case Backend.join_stake_pool(wallet_id, stake_pool_id, passphrase) do
      {:ok, result} -> {:ok, Util.keys_to_atom(result)}
      {:error, message} -> {:error, message}
    end
  end

  @spec quit(String.t(), String.t()) :: {:error, String.t()} | {:ok, Transaction.transaction()}
  @doc """
  Stop delegating completely. The wallet's stake will become inactive.

  ### Options
    * `wallet_id` - hex based string. 40 characters
    * `passphrase` - Wallet passphrase
  """
  def quit(wallet_id, passphrase) do
    case Backend.quit_staking(wallet_id, passphrase) do
      {:ok, result} -> {:ok, Util.keys_to_atom(result)}
      {:error, message} -> {:error, message}
    end
  end
end