# `DpExchange.Core.Types.ApprovedAddress`
[🔗](https://github.com/DistortionPoint/dp-exchange-core/blob/v0.3.120/lib/dp_exchange/core/types/approved_address.ex#L1)

An address on a venue's withdrawal allow-list.

## Why this is in the contract at all

Two venues in this family gate withdrawals behind an allow-list: a withdrawal to an
address that is not on it is refused, whatever the balance. A facade that could not
express the list would leave a caller unable to tell "this withdrawal failed" from "this
withdrawal was never going to work", and unable to do anything about the second.

## `:status` and `:active_from` are the whole point

Adding an address is not the same as being able to use it. Venues impose a waiting period
between approval and first use — the point of an allow-list being that an attacker who
takes an account cannot immediately add their own address and drain it.

So an address can be on the list and still unusable:

  * `:pending` — requested, not yet usable
  * `:active` — usable now
  * `:rejected` — refused

`:active_from` is when a `:pending` address becomes usable, where the venue states it.
**`nil` does not mean "usable now"** — it means the venue did not say, and a caller
planning a withdrawal against an unstated activation is planning against a guess.

# `status`

```elixir
@type status() :: :pending | :active | :rejected
```

# `t`

```elixir
@type t() :: %DpExchange.Core.Types.ApprovedAddress{
  active_from: DateTime.t() | nil,
  address: String.t(),
  asset: String.t() | nil,
  label: String.t() | nil,
  network: String.t(),
  provider: atom(),
  requested_at: DateTime.t() | nil,
  status: status()
}
```

# `new`

```elixir
@spec new(keyword() | map()) :: t()
```

Builds a `t:t/0`, failing closed if a required field is absent or `nil`.

`@enforce_keys` guards presence, not `nil` — see `DpExchange.Core.Types.Validate`.

Also raises on a `:status` that is not `:pending`, `:active` or `:rejected`. This is the
gate on a withdrawal destination: `usable?/2` has a clause for each of the three and none
for a fourth, so a venue's own word (`"pending-time"`, `:expired`, a string) built here
and then crashed the caller's process the first time it asked whether it could withdraw.
A status this contract has no word for is the venue's news to map, not this type's to guess.

# `usable?`

```elixir
@spec usable?(t(), DateTime.t()) :: boolean() | nil
```

Whether this address can be withdrawn to as at `now`.

Returns `nil` when the address is `:pending` and the venue stated no activation time —
**unknown, not usable**. The only safe reading of an unstated activation is to ask the
venue again rather than to attempt the withdrawal.

---

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