> ## Documentation Index
> Fetch the complete documentation index at: https://docs.passage.coinlist.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Allowlist a wallet

> Prove a user owns their wallet and register it against an offer with authorizeWallet.

Some offers only settle to wallets that the issuer has allowlisted. Superstate assets work this way, and the same allowlists are what make permissioned Uniswap pools and RWA lending markets available to your users.

`authorizeWallet` does both halves of that in one call: it proves the user controls the wallet by having them sign a challenge, then registers the address against the offer, broadcasting an allowlist transaction if the contract requires one.

<Note>
  The flow involves two identities. The **CoinList session** (from [OAuth](/sdk/oauth-authentication)) identifies *who* is investing, and the **wallet** is *where* assets are delivered. Allowlisting is what links them, which is why the user signs a message before they can transact.
</Note>

## Prerequisites

* A completed [OAuth](/sdk/oauth-authentication) session with a working `CoinListProvider`
* A connected wallet. Wallet selection and connection are your app's responsibility - the SDK operates on an already-connected `EvmWallet`.

## Authorize a wallet

```tsx theme={null}
const result = await coinlist.swap.authorizeWallet({
  wallet,
  offerId: OFFER_ID,
  contractAddress: SWAP_CONTRACT_ADDRESS,
  chain: SWAP_CHAIN,
  onProgress: (phase) => setPhase(phase),
});

if (result.type === "success") {
  setAuthorized(true);
} else {
  setError(result.error.step);
}
```

`authorizeWallet` is **idempotent**. If the wallet is already authorized it returns `success` immediately without prompting the user, so it is safe to call at the start of every session.

Flow-level failures come back as `{ type: "error" }` rather than throwing.

### Parameters

| Parameter         | Type                                        | Description                                                               |
| ----------------- | ------------------------------------------- | ------------------------------------------------------------------------- |
| `wallet`          | `EvmWallet`                                 | An already-connected wallet that can sign messages and send transactions. |
| `offerId`         | `OfferId`                                   | The offer to allowlist the wallet against.                                |
| `contractAddress` | `EvmContractAddress`                        | The swap contract for the offer.                                          |
| `chain`           | `EthereumChain`                             | The EVM chain to authorize on, for example `ethereum_mainnet`.            |
| `onProgress`      | `(phase: WalletAuthorizationPhase) => void` | Optional. Called as the flow advances, for rendering loading states.      |

## Progress phases

The phases are emitted in order. Two of them open a wallet popup:

`checking-authorization` → `requesting-challenge` → **`signing-message`** *(wallet popup)* → `submitting-signature` → **`broadcasting-transaction`** *(wallet popup, only when the contract needs an allowlist transaction)* → `awaiting-confirmation` → `verifying-authorization`

## Error steps

`WalletAuthorizationError` is tagged by the step that failed, so you can tell a user rejection apart from an infrastructure problem.

| `step`                | What happened                                                                           |
| --------------------- | --------------------------------------------------------------------------------------- |
| `authorization-check` | The initial check for existing authorization failed.                                    |
| `challenge-request`   | Requesting the ownership challenge failed.                                              |
| `signing`             | The user did not sign the challenge. Carries a `cause` of type `WalletError`.           |
| `allow-wallet`        | Submitting the signature to allowlist the wallet failed.                                |
| `broadcast`           | Broadcasting the allowlist transaction failed. Carries a `cause` of type `WalletError`. |
| `not-authorized`      | The flow completed but the on-chain re-check still reports the wallet as unauthorized.  |

The `signing` and `broadcast` steps carry a `WalletError` cause, which distinguishes `user_rejected` from `insufficient_funds`, `contract_reverted`, and `timeout`. See [Errors and edge cases](/sdk/errors) for the full breakdown and for guidance on which are worth retrying.

## Underlying endpoints

`authorizeWallet` wraps three Frontline API endpoints. Use them directly only if you are not using the React SDK.

| Endpoint                                  | Purpose                                                                                  |
| ----------------------------------------- | ---------------------------------------------------------------------------------------- |
| `GET /v1/wallet/authorized`               | Reads `authorized(address)` on the swap contract to check whether a wallet may transact. |
| `POST /v1/wallet-ownership`               | Requests a challenge for the user to sign.                                               |
| `POST /v1/offers/{offer_id}/allow-wallet` | Submits the signature and allowlists the address for the offer.                          |

## Next steps

<CardGroup cols={2}>
  <Card title="Swap flow" icon="arrows-rotate" href="/sdk/swap-flow">
    The full on-chain purchase flow, with authorization as step 3.
  </Card>

  <Card title="Errors and edge cases" icon="triangle-exclamation" href="/sdk/errors">
    Every error shape the SDK flows return, and how to handle them.
  </Card>
</CardGroup>
