# Guardrails and mandates \[What the owner keeps, what the asset manager gets, and how to grant and revoke it]

:::info
In Railnet smart contracts, an Allocation Strategy is implemented as a **MultiVehicle**.
:::

A mandate is your request for an asset manager to run an Allocation Strategy you own, inside limits the contracts enforce. You keep admin control; they get scoped operational roles. For Advanced Strategy guardrails, see the [policy engine](/strategies/advanced/policy-engine) instead.

<RbacDelegation />

## What the owner keeps

Agree these terms before deploying, then set them on-chain. Each stays with the owner.

| Guardrail | Role, retained by the owner | Enforced by |
|---|---|---|
| Which yield sources the strategy may use | `MULTI_VEHICLE_SET_VEHICLE_AUTHORIZATION`, scoped to the Vehicle Manager | Vehicle Manager rejects any unauthorized source |
| Maximum exposure per source | same role, via the per-source `cap` | Accounting engine checks the cap before every allocation |
| Fee rates | `FEE_MANAGER_SET_FEES` | Immutable max caps set at deployment |
| Fee recipients | `FEE_MANAGER_SET_FEE_RECIPIENTS` | Fee Manager |
| Who holds which role | `DEFAULT_ADMIN_ROLE` | External Access Control |
| Contract upgrades | `BEACON_UPGRADE` | Beacon |

Never grant `DEFAULT_ADMIN_ROLE` to the asset manager: it can grant and revoke every other role, including its own. See [Create an Allocation Strategy](/strategies/allocation/create) for authorizing sources and setting caps.

## What the asset manager gets

With the roles below the manager can allocate, rebalance and manage queues, but cannot authorize a new source, change fees, or grant roles.

| Operation | Role | Scope |
|---|---|---|
| Move assets and shares between sectors | `MULTI_VEHICLE_MOVE` | Sector Accounting Engine |
| Dispatch to a yield source | `MULTI_VEHICLE_DISPATCH` | Sector Accounting Engine |
| Configure allocation queues | `MULTI_VEHICLE_SET_QUEUES` | Queue Strategy Engine |
| Progress sub-queries | `MULTI_VEHICLE_PROGRESS_QUERY` | Sub Query Engine |
| Feed the redemption queue | `MULTI_VEHICLE_FEED_QUERY_REDEEM_QUEUE` | Vehicle Manager |
| Retrieve redemption queue assets | `MULTI_VEHICLE_RETRIEVE_QUERY_REDEEM_QUEUE_ASSETS` | Vehicle Manager |
| Trigger fee distribution (optional) | `FEE_MANAGER_DISPATCH_ERC20` | Fee Manager |

Rebalancing is `move` plus `dispatch`, so it needs no role of its own.

## Grant the roles

A scoped role only authorizes calls on the contract named in the scope. Granting the right role with the wrong scope authorizes nothing.

```solidity [Solidity]
address am = 0x...; // Asset manager
IVehicleManager manager = MultiVehicle(multiVehicle).manager();
ISectorAccountingEngine accounting = manager.accountingEngine();
IQueueStrategyEngine strategy = manager.queueStrategyEngine();
ISubQueryEngine subQueryEngine = manager.subQueryEngine();

eac.grantScopedRole(keccak256("MULTI_VEHICLE_MOVE"), address(accounting), am);
eac.grantScopedRole(keccak256("MULTI_VEHICLE_DISPATCH"), address(accounting), am);
eac.grantScopedRole(keccak256("MULTI_VEHICLE_SET_QUEUES"), address(strategy), am);
eac.grantScopedRole(keccak256("MULTI_VEHICLE_PROGRESS_QUERY"), address(subQueryEngine), am);
eac.grantScopedRole(keccak256("MULTI_VEHICLE_FEED_QUERY_REDEEM_QUEUE"), address(manager), am);
eac.grantScopedRole(keccak256("MULTI_VEHICLE_RETRIEVE_QUERY_REDEEM_QUEUE_ASSETS"), address(manager), am);
eac.grantScopedRole(keccak256("FEE_MANAGER_DISPATCH_ERC20"), address(feeManager), am);
```

Then confirm the manager has the operational roles and none of the admin ones:

```solidity [Solidity]
bool canDispatch = eac.hasRoleOrScopedRole(
    keccak256("MULTI_VEHICLE_DISPATCH"), address(accounting), am
);
bool hasAdmin = eac.hasRoleOrScopedRole(
    keccak256("DEFAULT_ADMIN_ROLE"), address(0), am
);
assert(canDispatch && !hasAdmin);
```

## Adjust and revoke

Mandate terms change without redeploying. Authorize a source and the manager can allocate to it immediately; unauthorize one only after capital has been withdrawn from it. Tighter queue targets restrict future allocations and leave existing positions alone. Fee changes must stay inside the immutable caps, and pending fees should be dispatched before you change recipients.

To offboard a manager, check that no query is still in a non-terminal state (async sources like Ethena hold a cooldown), then revoke each scoped role with `eac.revokeScopedRole(role, scope, am)` using the same role and scope pairs from the grant above. In-flight operations complete; new ones cannot start. Granting the same set to a replacement is all that is needed to re-delegate — the strategy, guardrails and fees are untouched.

Every allocation emits an event, so you can verify from the [API](/developers/api) that capital only went to authorized sources.

## Next steps

<Cards>
  <Card title="Operate an Allocation Strategy" description="Configure queues, move and dispatch capital, rebalance." icon="sliders" to="/strategies/allocation/operate" />

  <Card title="Access control" description="Scoped roles, the EAC surface, and the full role catalog." icon="shield-half" to="/developers/contracts/access-control" />

  <Card title="Fee Manager" description="Fee formulas, caps, and recipient splits." icon="percent" to="/developers/contracts/fee-manager" />
</Cards>
