# Compliance & access control \[Control who can deposit, hold and transfer your Conduit's shares, and who operates the contracts]

Two things gate your users: an optional **AccountList** and a `transferEnabled` latch on the Conduit. Pass `address(0)` as `accountList` and every check below is skipped.

## Allow-list modes

The AccountList keeps an allow-list and a block-list plus optional sanctions screening, and exposes one predicate per intent: `canDeposit`, `canRedeem`, `canTransfer`, `canReceive`, `canForceRedeem`. The block-list and sanctions apply in every mode.

| Mode | Deposits | Transfers |
|------|----------|-----------|
| `OPEN` | Anyone not blocked or sanctioned | Anyone not blocked or sanctioned |
| `REGULAR` | Allow-listed accounts only | Anyone not blocked or sanctioned |
| `STRICT` | Allow-listed accounts only | Both sender and receiver must be allow-listed |

`REGULAR` is the usual KYC setup; `STRICT` keeps the token inside your verified user base. Switching mode never changes the lists themselves.

## Precedence

Checks resolve in a fixed order: sanctions, then the block-list, then the mode. A sanctioned account fails everything, and a blocked account fails deposits, transfers and receipts even when allow-listed.

Sanctions screening uses a Chainalysis-style oracle through `ISanctionsList` and fails closed: a revert, a non-contract, or malformed data marks the account sanctioned. No predicate reverts, so a broken oracle freezes new activity rather than bricking the Conduit.

:::info
Redeems are the exception. `canRedeem` consults sanctions and nothing else, so blocking an account or de-listing it never traps its funds.
:::

## Deploy and manage the list

Spawning needs `FACTORY_SPAWN` on the AccountList factory; changes afterwards need `ACCOUNT_LIST_MANAGER` scoped to the list. One list can serve several Conduits sharing a compliance perimeter.

```solidity [Solidity]
AccountListFactory.SpawnParams memory params = AccountListFactory.SpawnParams({
    accessControl: externalAccessControl,
    mode: IAccountList.AllowlistMode.REGULAR,
    initialAllowList: initialVerifiedUsers,        // may be empty
    initialBlockList: new address[](0),
    sanctionsEnabled: true,                        // requires a non-zero oracle
    oracle: ISanctionsList(sanctionsOracle),
    deploymentSalt: keccak256("platform-account-list-v1")
});

AccountList accountList = accountListFactory.spawn(params);

accountList.addToAllowList(verifiedUsers);
accountList.addToBlockList(compromisedAccounts);
accountList.setAllowlistMode(IAccountList.AllowlistMode.STRICT);
accountList.toggleSanctions(true);
```

In `REGULAR` or `STRICT` mode, allow-list the ConduitFactory before spawning: it is the `msg.sender` of the seed deposit, so the spawn reverts if it cannot deposit.

## Transfer policy

A transfer needs both gates: `transferEnabled` on the Conduit and, when an AccountList is set, `canTransfer(from, to)`. With the latch off the token is mint-and-burn only: user-to-user transfers, wrapped-query NFT transfers and third-party-receiver creates are denied, while deposits, redeems, fee dispatch and `forceRedeem` keep working. Set the initial state at spawn and flip it later with `enableTransfers()` (role `CONDUIT_SET_TRANSFER_ENABLED`), one way only: a second call reverts `StateUnchanged`.

System moves (mint, burn, fee accrual, the redeem pull-for-burn, `forceRedeem`) use internal ERC-20 primitives and are never screened. The Fee Manager dispatching share fees is exempt as a sender only; its recipient is still screened.

## Eject a blocked or sanctioned holder

`forceRedeem` burns a holder's shares and starts a redeem query owned by and paid to that holder, so it ejects rather than seizes. It needs the holder to be blocked or sanctioned and the caller to hold `CONDUIT_FORCE_REDEEM`; without an AccountList it reverts `ForceRedeemUnavailable`.

## Roles you need

Every Conduit points at an External Access Control (EAC) contract, deployed first. Grant these through it, scoped as shown.

| Role | Grant to | Scope |
|---|---|---|
| `ACCOUNT_LIST_MANAGER` | Compliance officer | AccountList |
| `CONDUIT_FORCE_REDEEM` | Compliance officer | AccountList |
| `CONDUIT_SET_TRANSFER_ENABLED` | Platform admin | Conduit |
| `CONDUIT_SET_INTERCEPTIONS` | Platform admin | Conduit |
| `FEE_MANAGER_SET_FEES` | Platform admin | Fee Manager |
| `FEE_MANAGER_DISPATCH_ERC20` | Operator or keeper | Fee Manager |

See [Access control](/developers/contracts/access-control) for the EAC and the full role catalog.

## Next steps

<Cards>
  <Card title="Access control" description="Deploy the EAC and scope roles." icon="lock" to="/developers/contracts/access-control" />

  <Card title="Quick start" description="Pass your AccountList into spawn." icon="rocket" to="/conduits/quick-start" />
</Cards>
