# `MPP.Amount`
[🔗](https://github.com/ZenHive/mpp/blob/v0.10.0/lib/mpp/amount.ex#L1)

Amount and decimal helpers for converting human-readable amounts to base units.

All arithmetic is pure integer/string manipulation — no floating point. This
matches the mpp-rs `parse_units` and `parse_dollar_amount` implementations.

## Examples

    iex> MPP.Amount.parse_units("1.5", 6)
    {:ok, "1500000"}

    iex> MPP.Amount.parse_units("0.10", 6)
    {:ok, "100000"}

    iex> MPP.Amount.parse_dollar_amount("$1.50", 2)
    {:ok, {"150", "usd", 2}}

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `parse_dollar_amount` | 2 | Parse a dollar-notation string into {base_units, currency, decimals}. | `input: value`, `decimals: value` |
| `with_base_units` | 2 | Apply parse_units to any intent struct's amount field. | `intent: value`, `decimals: value` |
| `parse_units` | 2 | Convert a human-readable amount to base units by scaling with 10^decimals. | `amount: value`, `decimals: value` |

# `parse_dollar_amount`

```elixir
@spec parse_dollar_amount(String.t(), non_neg_integer()) ::
  {:ok, {String.t(), String.t(), non_neg_integer()}}
  | {:error, atom() | {atom(), String.t()}}
@spec parse_dollar_amount(term(), term()) :: {:error, :invalid_input}
```

Parse a dollar-notation string into \{base_units, currency, decimals\}.

## Parameters

  * `input` - Dollar string like "$1.50", "€0.10", or "1.50 USD" (value)
  * `decimals` - Number of decimal places for the currency (caller-supplied, not inferred from currency code) (value)

## Returns

\{:ok, \{base_units, currency, decimals\}\} where currency is lowercase 3-letter code (`tuple`)

```elixir
# descripex:contract
%{
  params: %{
    decimals: %{
      description: "Number of decimal places for the currency (caller-supplied, not inferred from currency code)",
      kind: :value
    },
    input: %{
      description: "Dollar string like \"$1.50\", \"€0.10\", or \"1.50 USD\"",
      kind: :value
    }
  },
  returns: %{
    type: :tuple,
    description: "{:ok, {base_units, currency, decimals}} where currency is lowercase 3-letter code",
    example: %{decimals: 2, currency: "usd", base_units: "150"}
  }
}
```

# `parse_units`

```elixir
@spec parse_units(String.t(), non_neg_integer()) ::
  {:ok, String.t()} | {:error, atom() | {atom(), String.t()}}
@spec parse_units(term(), term()) :: {:error, :invalid_input}
@spec parse_units(term(), term()) :: {:error, :invalid_input}
```

Convert a human-readable amount to base units by scaling with 10^decimals.

## Parameters

  * `amount` - Human-readable amount string (e.g., "1.5", "100", "0.001") (value)
  * `decimals` - Number of decimal places for the token (e.g., 6 for USDC, 18 for ETH) (value)

## Returns

\{:ok, base_units_string\} or \{:error, reason\} (`tuple`)

## Composes With

  * `with_base_units`

```elixir
# descripex:contract
%{
  params: %{
    decimals: %{
      description: "Number of decimal places for the token (e.g., 6 for USDC, 18 for ETH)",
      kind: :value
    },
    amount: %{
      description: "Human-readable amount string (e.g., \"1.5\", \"100\", \"0.001\")",
      kind: :value
    }
  },
  returns: %{
    type: :tuple,
    description: "{:ok, base_units_string} or {:error, reason}",
    example: "1500000"
  },
  composes_with: [:with_base_units]
}
```

# `with_base_units`

```elixir
@spec with_base_units(map(), non_neg_integer()) ::
  {:ok, map()} | {:error, atom() | {atom(), String.t()}}
```

Apply parse_units to any intent struct's amount field.

## Parameters

  * `intent` - Intent struct (Charge, Session, etc.) with an :amount field (value)
  * `decimals` - Number of decimal places for the token (value)

## Returns

\{:ok, updated_intent\} with amount in base units, or \{:error, reason\} (`tuple`)

## Composes With

  * `parse_units`

```elixir
# descripex:contract
%{
  params: %{
    decimals: %{
      description: "Number of decimal places for the token",
      kind: :value
    },
    intent: %{
      description: "Intent struct (Charge, Session, etc.) with an :amount field",
      kind: :value
    }
  },
  returns: %{
    type: :tuple,
    description: "{:ok, updated_intent} with amount in base units, or {:error, reason}"
  },
  composes_with: [:parse_units]
}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
