# `Onchain.Fees`
[🔗](https://github.com/ZenHive/onchain/blob/v0.5.4/lib/onchain/fees.ex#L1)

EIP-1559 fee suggestion math over `Cartouche.FeeHistory.t()`.

Pure functions — no RPC, no I/O. Pair with `Onchain.RPC.fee_history/2` to
fetch the input struct.

## Algorithm

Given a fee history with `base_fee_per_gas` (block_count + 1 entries — the
array is oldest-to-newest with the projected next-block fee at the LAST index
per the `eth_feeHistory` spec) and `reward` (block_count rows × N percentile
columns), `suggest_fees/2`:

1. Reads `base_fee = List.last(history.base_fee_per_gas)` — the next-block fee.
2. For each block, picks the priority fee at `:percentile_index` (default 0).
3. Takes the median of those per-block priority fees as `max_priority`.
4. Computes `max_fee = ceil(base_fee × buffer) + max_priority`.

## Buffer default

The default `:buffer` is `1.2` for parity with cartouche's `v2_gas_parameters`.
EIP-1559's reference recommendation is `2.0` for ~12-block headroom; pass
`buffer: 2.0` for the conservative default. Lower values risk transactions
getting stuck in a rising-fee market.

## Functions

| Function | Purpose |
|----------|---------|
| `suggest_fees/2` | Compute `{base_fee, max_priority, max_fee}` recommendation |
| `suggest_fees!/2` | Same, raises on error |

## Error Format

- `{:error, :no_reward_data}` — `history.reward` is `nil` or empty (caller
  passed empty `reward_percentiles` to `eth_feeHistory`).
- `{:error, {:percentile_index_out_of_range, idx, width}}` — requested column
  doesn't exist in the reward rows.
- `{:error, {:invalid_buffer, value}}` — buffer is not a positive number.

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `suggest_fees!` | 2 | Compute EIP-1559 fee recommendation. Raises on error. | `history: value`, `opts: value` |
| `suggest_fees` | 2 | Compute EIP-1559 fee recommendation from a Cartouche.FeeHistory struct. | `history: value`, `opts: value` |

# `suggest_fees`

```elixir
@spec suggest_fees(
  Cartouche.FeeHistory.t(),
  keyword()
) ::
  {:ok, {non_neg_integer(), non_neg_integer(), non_neg_integer()}}
  | {:error, term()}
```

Compute EIP-1559 fee recommendation from a Cartouche.FeeHistory struct.

## Parameters

  * `history` - Cartouche.FeeHistory.t() — typically from Onchain.RPC.fee_history/2 (value)
  * `opts` - Options: :percentile_index (default 0 — column in history.reward), :buffer (default 1.2 — base-fee multiplier for max_fee headroom) (default: `[]`, value)

## Returns

Tuple of \{base_fee, max_priority, max_fee\} in wei (`{:ok, {non_neg_integer, non_neg_integer, non_neg_integer}} | {:error, term}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :percentile_index (default 0 — column in history.reward), :buffer (default 1.2 — base-fee multiplier for max_fee headroom)",
      kind: :value
    },
    history: %{
      description: "Cartouche.FeeHistory.t() — typically from Onchain.RPC.fee_history/2",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, {non_neg_integer, non_neg_integer, non_neg_integer}} | {:error, term}",
    description: "Tuple of {base_fee, max_priority, max_fee} in wei"
  }
}
```

# `suggest_fees!`

```elixir
@spec suggest_fees!(
  Cartouche.FeeHistory.t(),
  keyword()
) :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}
```

Compute EIP-1559 fee recommendation. Raises on error.

## Parameters

  * `history` - Cartouche.FeeHistory.t() (value)
  * `opts` - Options: :percentile_index, :buffer (default: `[]`, value)

## Returns

Tuple of \{base_fee, max_priority, max_fee\} in wei (`{non_neg_integer, non_neg_integer, non_neg_integer}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :percentile_index, :buffer",
      kind: :value
    },
    history: %{description: "Cartouche.FeeHistory.t()", kind: :value}
  },
  returns: %{
    type: "{non_neg_integer, non_neg_integer, non_neg_integer}",
    description: "Tuple of {base_fee, max_priority, max_fee} in wei"
  }
}
```

---

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