defmodule Cardanoex.Transaction do
alias Cardanoex.Backend
alias Cardanoex.Util
@type asset :: %{
policy_id: String.t(),
asset_name: String.t(),
quantity: non_neg_integer()
}
@type payment :: %{
address: String.t(),
amount: non_neg_integer(),
assets: list(asset()) | nil
}
@type create_transaction :: %{
passphrase: String.t(),
payments: list(payment()),
withdrawal: String.t() | nil,
metadata: map()
}
@type amount :: %{
quantity: non_neg_integer(),
unit: String.t()
}
@type fee_estimation :: %{
deposit: amount(),
estimated_max: amount(),
estimated_min: amount(),
minimum_coins: list(amount())
}
@type input :: %{
address: String.t(),
amount: amount(),
assets: list(asset()),
id: String.t(),
index: non_neg_integer()
}
@type output :: %{
address: String.t(),
amount: amount(),
assets: list(asset())
}
@type collateral :: %{
address: String.t(),
amount: amount(),
id: String.t(),
index: non_neg_integer()
}
@type withdrawal :: %{
stake_address: String.t(),
amount: amount()
}
@type transaction ::
%{
amount: amount(),
collateral: list(collateral()),
deposit: amount(),
depth: %{quantity: non_neg_integer(), unit: String.t()},
direction: String.t(),
expires_at: %{
absolute_slot_number: non_neg_integer(),
epoch_number: non_neg_integer(),
slot_number: non_neg_integer(),
time: String.t()
},
fee: amount(),
id: String.t(),
inputs: list(input()),
inserted_at: %{
absolute_slot_number: non_neg_integer(),
epoch_number: non_neg_integer(),
height: %{quantity: non_neg_integer(), unit: String.t()},
slot_number: non_neg_integer(),
time: String.t()
},
metadata: map | nil,
mint: list(),
outputs: list(output()),
pending_since: %{
absolute_slot_number: non_neg_integer(),
epoch_number: non_neg_integer(),
height: %{quantity: non_neg_integer(), unit: String.t()},
slot_number: non_neg_integer(),
time: String.t()
},
status: String.t(),
withdrawals: list(withdrawal),
script_validity: String.t() | nil
}
@moduledoc """
The Transaction module lets you work with transactions for a wallet.
"""
@spec estimate_fee(String.t(), create_transaction()) ::
{:error, String.t()} | {:ok, fee_estimation()}
@doc """
Estimate fee for the transaction.
The estimate is made by assembling multiple transactions and analyzing the distribution of their fees.
The `estimated_max` is the highest fee observed, and the `estimated_min` is the fee which is lower than at least 90% of the fees observed.
## Options
* `wallet_id` - hex based string. 40 characters
* `transaction` - A map with the following structure:
```elixir
%{
payments: [
%{
address: "addr_test1qruzy7l5...nq04es9elzy7",
amount: %{quantity: 42_000_000, unit: "lovelace"}
}
]
}
# With asset:
%{
payments: [
%{
address:"addr_test1qruzy7l5...nq04es9elzy7",
amount: %{quantity: 1_407_406, unit: "lovelace"},
assets: [
%{
policy_id: "6b8d07d69639e9413dd637a1a815a7323c69c86abbafb66dbfdb1aa7",
asset_name: "",
quantity: 0
}
]
}
]
}
# With metadata:
%{
payments: [
%{
address: "addr_test1qruzy7l5...nq04es9elzy7",
amount: %{quantity: 1_407_406, unit: "lovelace"}
}
],
metadata: %{"0" => %{"string" => "cardano"}, "1" => %{"int" => 14}}
}
```
"""
def estimate_fee(wallet_id, transaction) do
case Backend.estimate_transaction_fee(wallet_id, transaction) do
{:ok, fee_estimation} -> {:ok, Util.keys_to_atom(fee_estimation)}
{:error, message} -> {:error, message}
end
end
@spec create(String.t(), create_transaction()) :: {:error, String.t()} | {:ok, transaction}
@doc """
Create and send transaction from the wallet.
## Options
* `wallet_id` - hex based string. 40 characters
* `transaction` - A map with the following structure:
```elixir
%{
payments: [
%{
address: "addr_test1qruzy7l5...nq04es9elzy7",
amount: %{quantity: 42_000_000, unit: "lovelace"}
}
]
}
# With asset:
%{
payments: [
%{
address:"addr_test1qruzy7l5...nq04es9elzy7",
amount: %{quantity: 1_407_406, unit: "lovelace"},
assets: [
%{
policy_id: "6b8d07d69639e9413dd637a1a815a7323c69c86abbafb66dbfdb1aa7",
asset_name: "",
quantity: 0
}
]
}
]
}
# With metadata:
%{
payments: [
%{
address: "addr_test1qruzy7l5...nq04es9elzy7",
amount: %{quantity: 1_407_406, unit: "lovelace"}
}
],
metadata: %{"0" => %{"string" => "cardano"}, "1" => %{"int" => 14}}
}
```
"""
def create(wallet_id, transaction) do
case Backend.create_transaction(wallet_id, transaction) do
{:ok, transaction} -> {:ok, Util.keys_to_atom(transaction)}
{:error, message} -> {:error, message}
end
end
@spec list(String.t(),
start: String.t(),
stop: String.t(),
order: atom(),
min_withdrawal: non_neg_integer()
) ::
{:error, String.t()} | {:ok, list(transaction())}
@doc """
Lists all incoming and outgoing wallet's transactions.
## Options
* `start` - An optional start time in ISO 8601 date-and-time format. Basic and extended formats are both accepted. Times can be local (with a timezone offset) or UTC. If both a start time and an end time are specified, then the start time must not be later than the end time. Example: `2008-08-08T08:08:08Z`
* `stop` - An optional end time in ISO 8601 date-and-time format. Basic and extended formats are both accepted. Times can be local (with a timezone offset) or UTC. If both a start time and an end time are specified, then the start time must not be later than the end time.
* `order` - Can be set to `:descending` or `:ascending`. Defaults to `:descending`
* `min_withdrawal` - Returns only transactions that have at least one withdrawal above the given amount. This is particularly useful when set to `1` in order to list the withdrawal history of a wallet.
"""
def list(wallet_id, options \\ []) do
default = [
start: nil,
stop: nil,
order: :descending,
min_withdrawal: nil
]
opts = Enum.into(Keyword.merge(default, options), %{})
case Backend.list_transactions(
wallet_id,
opts.start,
opts.stop,
opts.order,
opts.min_withdrawal
) do
{:ok, transactions} -> {:ok, Enum.map(transactions, fn t -> Util.keys_to_atom(t) end)}
{:error, message} -> {:error, message}
end
end
@spec get(String.t(), String.t()) :: {:error, String.t()} | {:ok, transaction()}
@doc """
Get transaction by id.
## Options
* `transaction_id` - Transaction ID
"""
def get(wallet_id, transaction_id) do
case Backend.get_transaction(wallet_id, transaction_id) do
{:ok, transaction} -> {:ok, Util.keys_to_atom(transaction)}
{:error, message} -> {:error, message}
end
end
end