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

# Access control and roles

> OpenZeppelin AccessControlEnumerable + Multicall, plus batch grantRoles/revokeRoles (admin-only) and constructor-time role seeding via RoleHolder[] {role, account}.

> Sources: [`src/modules/ACLModule.sol`](https://github.com/zentryHQ/red-potion-contract/blob/main/src/modules/ACLModule.sol) · [`FundRoles.sol`](https://github.com/zentryHQ/red-potion-contract/blob/main/src/modules/FundRoles.sol) · [`FundSpokeACLModule.sol`](https://github.com/zentryHQ/red-potion-contract/blob/main/src/modules/FundSpokeACLModule.sol) · [`FundACLModule.sol`](https://github.com/zentryHQ/red-potion-contract/blob/main/src/modules/FundACLModule.sol) · [`FundManagerACLModule.sol`](https://github.com/zentryHQ/red-potion-contract/blob/main/src/modules/FundManagerACLModule.sol) · [`StrategyACLModule.sol`](https://github.com/zentryHQ/red-potion-contract/blob/main/src/modules/StrategyACLModule.sol)

## The two auth patterns

<Tabs>
  <Tab title="Local ACL (`ACLModule`)">
    OpenZeppelin `AccessControlEnumerable` + `Multicall`, plus batch `grantRoles`/`revokeRoles` (admin-only) and constructor-time role seeding via `RoleHolder[] {role, account}`.

    Used directly by contracts that own their role state:

    | Contract                                                                                                                       | Role constants module                               |
    | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------- |
    | [Fund](/developers/contract-reference/fund)                                                                                    | `FundACLModule` (= `ACLModule` + `FundRoles`)       |
    | [FundManager](/developers/contract-reference/fund-manager)                                                                     | `FundManagerACLModule`                              |
    | [FundManagerDeployer](/developers/contract-reference/fund-manager-deployer)                                                    | roles declared inline                               |
    | [Strategy](/developers/contract-reference/strategy) / [StandaloneStrategy](/developers/contract-reference/standalone-strategy) | `StrategyACLModule` (+ `CallValidatorModule` roles) |
    | StandaloneStrategyDeployer                                                                                                     | roles declared inline                               |
  </Tab>

  <Tab title="Spoke callback ACL (`FundSpokeACLModule`)">
    The fund spokes ([DepositQueue](/developers/contract-reference/deposit-queue), [RedeemQueue](/developers/contract-reference/redeem-queue), [Oracle](/developers/contract-reference/oracle), [FeeManager](/developers/contract-reference/fee-manager), [RiskManager](/developers/contract-reference/risk-manager)) hold **no role state of their own**.

    Their `onlyRole(ROLE)` modifier calls back to the Fund:

    ```solidity theme={"dark"}
    if (!IAccessControl(_fund()).hasRole(role, msg.sender)) revert UnauthorizedRole();
    ```

    So **all roles for a fund and its five spokes are granted/revoked in one place: the Fund** (`fund.grantRoles(...)` by the fund's `DEFAULT_ADMIN_ROLE`).

    Role-name constants live in `FundRoles`, inherited by both Fund and spokes so identifiers always match.
  </Tab>
</Tabs>

## Fund role table (`FundRoles`)

All identifiers are `keccak256("<NAME>")`. “Enforced by” shows where the guarded function lives.

| Role                                                                               | Enforced by  | Grants ability to                                           |
| ---------------------------------------------------------------------------------- | ------------ | ----------------------------------------------------------- |
| `DEFAULT_ADMIN_ROLE` (`0x00`)                                                      | Fund         | Grant/revoke every other role.                              |
| `ACCEPT_REPORT_ROLE`                                                               | Fund         | `acceptReport` — accept prices & settle batches.            |
| `ACCEPT_SUSPICIOUS_REPORT_ROLE`                                                    | Fund         | `acceptSuspiciousReport` — settle despite suspicious flags. |
| `FUND_REDEEM_ROLE`                                                                 | Fund         | `fundRedeem` — deliver assets to settled redeem batches.    |
| `CREATE_STRATEGY_ROLE`                                                             | Fund         | `createStrategy`.                                           |
| `ADD_STRATEGY_ROLE` / `REMOVE_STRATEGY_ROLE`                                       | Fund         | Manage the strategy registry.                               |
| `PUSH_TO_STRATEGY_ROLE` / `PULL_FROM_STRATEGY_ROLE`                                | Fund         | Move assets Fund ↔ strategies.                              |
| `ADD_EXTERNAL_WALLET_ROLE` / `REMOVE_EXTERNAL_WALLET_ROLE` / `PUSH_TO_WALLET_ROLE` | Fund         | Manage the external-wallet whitelist and push assets to it. |
| `SUBMIT_REPORT_ROLE`                                                               | Oracle       | `submitReport` — post batch prices.                         |
| `REJECT_REPORT_ROLE`                                                               | Oracle       | `rejectReport` — veto pending prices.                       |
| `SET_PRICE_SAFETY_ROLE`                                                            | Oracle       | Configure suspicious-price bounds.                          |
| `SET_NEXT_CUTOFF_TIME_ROLE`                                                        | Oracle       | Move the batch cutoff.                                      |
| `SET_MIN_ACCEPT_REPORT_DELAY_ROLE` / `SET_MAX_ACCEPT_REPORT_DELAY_ROLE`            | Oracle       | Tune the accept window.                                     |
| `SET_FEES_ROLE`                                                                    | FeeManager   | `setFeeConfig` (all five fee bps).                          |
| `SET_FEE_RECIPIENT_ROLE`                                                           | FeeManager   | `setFeeRecipient`.                                          |
| `SET_FEE_BASE_ASSET_ROLE`                                                          | FeeManager   | `setFeeBaseAsset`.                                          |
| `SET_DEPOSIT_ALLOWED_ASSETS_ROLE`                                                  | DepositQueue | `setAllowedAssets`.                                         |
| `PAUSE_DEPOSIT_ROLE` / `UNPAUSE_DEPOSIT_ROLE`                                      | DepositQueue | Global & per-asset pause.                                   |
| `CANCEL_DEPOSIT_REQUEST_ROLE`                                                      | DepositQueue | `adminCancelDeposit`.                                       |
| `PULL_DEPOSIT_ASSET_ROLE`                                                          | DepositQueue | `pullAsset` (queue → Fund).                                 |
| `SET_REDEEM_ALLOWED_ASSETS_ROLE`                                                   | RedeemQueue  | `setAllowedAssets`.                                         |
| `PAUSE_REDEEM_ROLE` / `UNPAUSE_REDEEM_ROLE`                                        | RedeemQueue  | Global & per-asset pause.                                   |
| `CANCEL_REDEEM_REQUEST_ROLE`                                                       | RedeemQueue  | `adminCancelRedeem`.                                        |
| `PULL_REDEEM_ASSET_ROLE`                                                           | RedeemQueue  | `pullAsset` (queue → Fund).                                 |
| `SET_TVL_CAP_ROLE`                                                                 | RiskManager  | `setTvlCap`.                                                |
| `SET_BATCH_CAPS_ROLE`                                                              | RiskManager  | `setMaxBatchDepositCap` / `setMaxBatchRedeemCap`.           |
| `SET_MIN_DEPOSIT_AMOUNT_ROLE` / `SET_MIN_REDEEM_AMOUNT_ROLE`                       | RiskManager  | Minimum request sizes.                                      |
| `SET_MAX_DRAWDOWN_ROLE`                                                            | RiskManager  | `setMaxDrawdown`.                                           |
| `SET_WHITELIST_ROLE`                                                               | RiskManager  | `setMerkleRoot` (depositor whitelist).                      |
| `EMERGENCY_PAUSE_ROLE`                                                             | RiskManager  | `emergencyPause` / `emergencyUnpause`.                      |

## Other role tables

| Contract                                                                        | Roles                                                                                                                                                                                                                                       |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **FundManager** (`FundManagerACLModule`)                                        | `CREATE_FUND_ROLE`, plus one `SET_<COMPONENT>_FACTORY_ROLE` per component (fund, share, deposit queue, redeem queue, oracle, fee manager, risk manager, strategy) guarding both `set<Component>Factory` and `set<Component>Implementation`. |
| **FundManagerDeployer**                                                         | `CREATE_FUND_MANAGER_ROLE`, `SET_IMPLEMENTATIONS_ROLE`, `SET_PROTOCOL_FEE_RECIPIENT_ROLE`.                                                                                                                                                  |
| **Strategy / StandaloneStrategy** (`StrategyACLModule` + `CallValidatorModule`) | `CALLER_ROLE` (execute allowlisted calls), `ADD_ALLOWED_CALL_ROLE`, `REMOVE_ALLOWED_CALL_ROLE`.                                                                                                                                             |
| **StandaloneStrategyDeployer**                                                  | `CREATE_STRATEGY_ROLE`, `SET_IMPLEMENTATIONS_ROLE`.                                                                                                                                                                                         |

## Upgrade authority

Every contract is a `TransparentUpgradeableProxy`; upgrade rights belong to whoever owns each proxy's auto-deployed **ProxyAdmin** — the `proxyAdmin` address chosen at tenant/fund creation.

<Info>
  **Roles govern behavior; ProxyAdmin ownership governs code.**

  The two are deliberately independent: an operator can hold rich behavioral roles without any ability to change contract code, and the upgrade authority can change code without holding any operational role.
</Info>

## Separation-of-duties guidance

Because roles can be split across parties and keys, funds should distribute them rather than concentrating everything in one account.

Common splits:

* Keep the **reporter** (`SUBMIT_REPORT_ROLE`) separate from the **acceptor** (`ACCEPT_REPORT_ROLE`).
* Give a third party the **reviewer** veto (`REJECT_REPORT_ROLE`).
* Hold `ACCEPT_SUSPICIOUS_REPORT_ROLE` and `EMERGENCY_PAUSE_ROLE` on more secure keys.
* Keep `DEFAULT_ADMIN_ROLE` and ProxyAdmin ownership on the most protected keys of all.
