> For the complete documentation index, see [llms.txt](https://docs.t-rex.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.t-rex.network/guides/issuer/25.-commission-a-suite-and-grant-roles.md).

# 25. Commission a suite and grant roles

Commissioning wires a deployed suite to its access manager: it maps every privileged function to a role of the Issuer's domain and sets which role administers which. Granting then gives those roles to the Issuer's operators. The Issuer triggers both, right after deployment. Until they are done, the suite is not operable: every privileged function answers to the holder of `ADMIN_ROLE` only, the token holds no `AGENT` role and the registry no `IRS_WRITER`.

When the `TREXFactory` deployed the access manager, the suite is already commissioned: step 2 is skipped.

An Issuer that supplied its own manager at deployment commissions the suite itself, in step 2. Such a manager comes from one of two sources.

| Source of the manager                                                                                                        | Who answers for its code                                                                 |
| ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| The access manager factory of the T-REX Network \[\[TBC: name of the access manager factory and of its deployment function]] | The T-REX Network. The factory deploys the reference `TREXAccessManager` of the network. |
| Any other deployment                                                                                                         | The Issuer. The T-REX Network does not review that contract.                             |

In both cases the Issuer decides who holds which role. The protocol sets the roles and what each one gates. It does not prescribe how an Issuer splits them between its teams and its providers.

## At a glance

|                   |                                                                                                                                                                                                                          |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Triggered by**  | Issuer                                                                                                                                                                                                                   |
| **Actors**        | Issuer, Token Agent, Registry Agent, Compliance Owner, Platform                                                                                                                                                          |
| **Contracts**     | `TREXAccessManager`, `Token`, `TREXRegistry`, `IdentityRegistryStorage`, `ModularCompliance`                                                                                                                             |
| **Preconditions** | The token suite is deployed (see [01](/guides/issuer/01.-deploy-a-token-suite.md)); the Issuer's account holds `ADMIN_ROLE` on the suite's `TREXAccessManager`; the Issuer has decided which account receives which role |
| **Result**        | Every privileged function of the suite is mapped to a role of the Issuer's domain, and the accounts the Issuer chose hold those roles                                                                                    |

## Actors and roles

| Actor            | Role in this flow                                                                             | What they hold                                       |
| ---------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| Issuer           | Commissions the suite and grants the administrative roles.                                    | `ADMIN_ROLE` on the `TREXAccessManager`              |
| Token Agent      | Receives agent roles on the token. Does not transact in this flow.                            | An account or an ONCHAINID that receives the roles   |
| Registry Agent   | Receives the `AGENT` role. Does not transact. Typically the same operator as the Token Agent. | An account that receives the role                    |
| Compliance Owner | Receives `OWNER`. Usually the Issuer itself.                                                  | An account that receives the role                    |
| Platform         | Computes role identifiers, runs the reads and submits with the Issuer's key.                  | The Issuer's signing key, or a key authorized for it |

## Sequence

```mermaid
sequenceDiagram
    participant Platform
    participant Issuer
    participant AM as TREXAccessManager
    participant IRS as IdentityRegistryStorage

    Platform->>AM: domainOf(token) (read)
    Platform->>AM: getTargetFunctionRole(token, selector) (read)
    alt the suite is not commissioned
        Issuer->>AM: createDomain(name), assign(domainId, token)
        Issuer->>AM: grantRole(AGENT, token), grantRole(IRS_WRITER, registry)
        Issuer->>AM: setTargetFunctionRole for each contract of the suite
        Issuer->>AM: setRoleAdmin for each role
        Issuer->>IRS: bindIdentityRegistry(registry)
    end
    Issuer->>AM: grantRole(OWNER, account, delay)
    Issuer->>AM: grantRole(SUITE_ADMIN, account, delay)
    Issuer->>AM: grantRole(AGENT_ADMIN, account, delay)
    Issuer->>AM: grantRole for each operational role
    Platform->>AM: hasRole(roleId, account) (read)
```

## Step by step

### Step 1. Read the starting state

**Who:** Platform, on behalf of the Issuer

**Call:** Off-chain, using read calls

**Before the call:** The Platform knows the token's address.

**What happens:** The Platform reads `Token.authority()` to find the manager, `TREXAccessManager.domainOf(token)` to find the domain, and `TREXAccessManager.getTargetFunctionRole(token, selector)` for one privileged function, for example `mint`. A domain of zero, or a function still mapped to `ADMIN_ROLE`, means the suite is not commissioned.

**What changes:** Nothing on-chain.

### Step 2. Commission the suite

Skip this step when the factory deployed the manager.

**Who:** Issuer (`ADMIN_ROLE`)

**Call:** The commissioning of `AccessManagerSetupLib.commissionSuite(accessManager, token)`, executed from a script or a contract that holds `ADMIN_ROLE`. Before it: `TREXAccessManager.createDomain(name)` for a new domain, then `TREXAccessManager.assign(domainId, token)` in every case. The commissioning then makes these calls on the manager and on the storage:

* `assign(domainId, storage)`, when the storage has no domain yet;
* `grantRole` of `AGENT` to the token, and of `IRS_WRITER` to the registry;
* `setTargetFunctionRole(target, selectors, roleId)` for the token, the registry, the compliance and the storage;
* `setRoleAdmin(roleId, adminRoleId)` for each role;
* `IdentityRegistryStorage.bindIdentityRegistry(registry)`, unless the registry is already bound.

**Before the call:** The Issuer chooses the domain: a new one for this asset, or an existing one, in which case the asset shares the agents of that domain.

**What the contract checks:**

1. The caller holds `ADMIN_ROLE` on the manager, for every mapping and for the two grants. Otherwise the call reverts with `AccessManagerUnauthorizedAccount`.
2. The domain exists and the token is assigned to one. Otherwise `DomainNotFound` or `NotAssigned`.
3. When the storage is already in use by another suite: the caller holds `IRS_BINDER` in the storage's domain. Otherwise the binding reverts with `AccessManagedUnauthorized`.

**What changes:** Every privileged function of the four contracts is mapped to its role in the domain. `AGENT_ADMIN` administers the agent roles, `IRS_BINDER` and `VALIDATION_KEEPER`. `SUITE_ADMIN` administers `TOKEN_MANAGER`, `IDENTITY_MANAGER` and `COMPLIANCE_MANAGER`. The token holds `AGENT` and the registry holds `IRS_WRITER`. The registry is bound to its storage. The manager emits `DomainCreated`, `DomainAssigned`, `TargetFunctionRoleUpdated`, `RoleAdminChanged` and `RoleGranted`.

### Step 3. Grant the administrative roles

**Who:** Issuer (`ADMIN_ROLE`)

**Call:** `TREXAccessManager.grantRole(roleId, account, executionDelay)`, once per role and account

**Before the call:** The Platform computes each role identifier from the domain and the role number:

`roleId = domainId * 2^32 + roleNumber`

| Role                     | Number | Role                 | Number |
| ------------------------ | ------ | -------------------- | ------ |
| `OWNER`                  | 1      | `TOKEN_MANAGER`      | 10     |
| `AGENT`                  | 2      | `IDENTITY_MANAGER`   | 11     |
| `AGENT_MINTER`           | 3      | `AGENT_ADMIN`        | 12     |
| `AGENT_BURNER`           | 4      | `SUITE_ADMIN`        | 13     |
| `AGENT_PARTIAL_FREEZER`  | 5      | `IRS_BINDER`         | 14     |
| `AGENT_ADDRESS_FREEZER`  | 6      | `IRS_WRITER`         | 15     |
| `AGENT_RECOVERY_ADDRESS` | 7      | `COMPLIANCE_MANAGER` | 16     |
| `AGENT_FORCED_TRANSFER`  | 8      | `VALIDATION_KEEPER`  | 17     |
| `AGENT_PAUSER`           | 9      |                      |        |

The Issuer grants `OWNER`, `SUITE_ADMIN` and `AGENT_ADMIN`. `executionDelay` is zero for a role whose holder acts at once, or a number of seconds for a role whose holder must schedule its calls.

**What the contract checks:**

1. The caller holds the role that administers `roleId`. For these three roles that is `ADMIN_ROLE`. Otherwise `AccessManagerUnauthorizedAccount`.

**What changes:** The account holds the role in the domain. The manager emits `RoleGranted(roleId, account, delay, since, newMember)`.

### Step 4. Grant the operational roles

**Who:** The holders of `SUITE_ADMIN` and of `AGENT_ADMIN`, or the Issuer after granting itself those roles

**Call:** `TREXAccessManager.grantRole(roleId, account, executionDelay)`

**Before the call:** Step 3 is done. The Issuer has decided how to split duties.

**What the contract checks:**

1. The caller holds the role that administers `roleId`: `SUITE_ADMIN` for `TOKEN_MANAGER`, `IDENTITY_MANAGER` and `COMPLIANCE_MANAGER`; `AGENT_ADMIN` for `AGENT`, the seven agent roles, `IRS_BINDER` and `VALIDATION_KEEPER`. Otherwise `AccessManagerUnauthorizedAccount`.

**What changes:** Each account holds its role. One `RoleGranted` event per grant.

### Step 5. Verify

**Who:** Platform

**Call:** Off-chain, using read calls

**Before the call:** Steps 3 and 4 are confirmed.

**What happens:** For each grant, the Platform reads `TREXAccessManager.hasRole(roleId, account)`, which returns whether the account is a member and with what delay. For one function per role, it reads `TREXAccessManager.canCall(account, target, selector)`.

**What changes:** Nothing on-chain.

## State after the flow

* `TREXAccessManager.domainOf(token)` returns the Issuer's domain.
* `getTargetFunctionRole` returns a role of that domain for every privileged function of the token, the registry, the compliance and the storage.
* `hasRole` is true for each account and role the Issuer granted.
* The token holds `AGENT` and the registry holds `IRS_WRITER`.
* The token is still paused and has no supply.

## Failure modes

| Symptom                                                                    | Cause                                                                                                                                                                                       | Fix                                                                                           |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| A privileged call on the suite reverts with `AccessManagedUnauthorized`    | The caller does not hold the role, or holds it in another domain                                                                                                                            | Read `hasRole` with the identifier of the token's domain. Grant the role                      |
| A privileged call reverts with `AccessManagerNotScheduled`                 | The caller holds the role with an execution delay and did not schedule the call                                                                                                             | Schedule the call on the manager, wait for the delay, then call                               |
| Every privileged call by an operator reverts, although its role is granted | The suite is not commissioned: its functions are not mapped, and answer to `ADMIN_ROLE` only (step 2)                                                                                       | Commission the suite                                                                          |
| Commissioning reverts with `NotAssigned`                                   | The token is not assigned to a domain (step 2, check 2)                                                                                                                                     | `assign(domainId, token)` first                                                               |
| `grantRole` reverts with `AccessManagerUnauthorizedAccount`                | The caller does not hold the administering role (steps 3 and 4)                                                                                                                             | Use the account that holds `ADMIN_ROLE`, `SUITE_ADMIN` or `AGENT_ADMIN`, as the role requires |
| A recovery reverts when it registers the new wallet                        | The token does not hold `AGENT` in its domain                                                                                                                                               | Commission the suite again. It grants the role                                                |
| A registration reverts in the storage                                      | The registry does not hold `IRS_WRITER`, or is not bound to the storage                                                                                                                     | Commission the suite again                                                                    |
| An operator can act on another asset of the Issuer                         | Both assets are in the same domain                                                                                                                                                          | Intended for a shared team. Otherwise place the assets in separate domains                    |
| A batch call reverts for a holder that has a delay                         | A batch function is authorized by the role of its single operation and requires immediate permission. `batchRegisterIdentity` is the exception: it has its own mapping and can be scheduled | Use the single operation, scheduled, or grant the role without delay                          |

## Common misunderstandings

> **Wrong:** "The admin of the manager can mint."
>
> **Right:** `ADMIN_ROLE` maps functions to roles and grants roles. Once the suite is commissioned, `mint` is gated by `AGENT_MINTER`, and the admin does not hold it unless it grants it to itself. Administering the suite and operating the token are separate powers.

> **Wrong:** "Granting `AGENT` makes someone the token's agent."
>
> **Right:** `AGENT` gates registrations in the registry and requests of cross-chain validations on behalf of holders. Minting, burning, freezing, forced transfers, recovery and the pause are seven separate roles, each granted on its own.

> **Wrong:** "A role is granted on a token."
>
> **Right:** A role is granted in a domain. It applies to every token assigned to that domain. Two tokens in one domain share their agents. To separate the teams of two assets, place the assets in two domains.

> **Wrong:** "Assigning a token to a domain gives that domain's roles power over it."
>
> **Right:** The domain registry is a directory. Authority comes from the mapping of each function to a role, which commissioning writes. `assign` alone maps nothing and revokes nothing.

## Integration notes

* Compute role identifiers from the domain read on-chain, never from a constant. The same role has a different identifier in each domain.
* Decode `RoleGranted` and `RoleRevoked` events to maintain the list of role holders: the domain is the upper half of the identifier and the role number the lower half.
* Group the grants of steps 3 and 4 in one transaction with the manager's `multicall`.
* Give the sensitive roles a delay and the emergency roles none. A pauser acts at once. A change of compliance rules can wait.
* A role can be held by an ONCHAINID. The role check applies to the identity that makes the call, not to the key that instructed it.
* Commissioning can be run again. It applies the same mappings.
* Hold `ADMIN_ROLE` in a multisig or in the Issuer's governance. It has no on-chain fallback.
* For an asset open on satellites, grant `VALIDATION_KEEPER` to the keeper that the T-REX Network runs: [35](/guides/cross-chain/35.-discard-or-resolve-a-validation.md). `[[TBC: the address of the keeper of the T-REX Network]]`
* `createDomain`, `assign` and `domainOf` are functions of `TREXAccessManager`. A manager from another source may not have them. The Issuer then chooses the domain number of each asset and keeps it in its own records. A manager from the access manager factory of the T-REX Network avoids this.

## Related flows

* [01 Deploy a token suite](/guides/issuer/01.-deploy-a-token-suite.md) : the precondition
* [03 Configure required claim topics](/guides/issuer/03.-configure-required-claim-topics.md) : first use of `OWNER`
* [04 Add a trusted Claim Issuer](/guides/issuer/04.-add-a-trusted-claim-issuer.md) : first use of `OWNER`
* [11 Add a compliance rule](/guides/issuer/11.-add-a-compliance-rule.md) : uses `OWNER` on the compliance
* [08 Mint a position to an investor](/guides/positions/08.-mint-a-position-to-an-investor.md) : first use of `AGENT_MINTER`
* [16 Pause and unpause a token](/guides/agents/16.-pause-and-unpause-a-token.md) : lifting the initial pause needs `AGENT_PAUSER`


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.t-rex.network/guides/issuer/25.-commission-a-suite-and-grant-roles.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
