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

# Manage modules

> Add, configure, and remove modules on your Multi-Vehicle

<Info>This page covers the smart contract implementation details. See [Glossary](/developers/glossary).</Info>

Modules are external contracts that extend your Multi-Vehicle's capabilities — for example, distributing Merkl rewards or integrating custom logic. The Modules Manager provides a secure, timelock-protected registry for adding, updating, and executing modules.

As an asset manager, you typically execute modules that have been registered and approved. If you are operating a platform-owned Multi-Vehicle, the platform owner manages the module registry — you can execute authorized modules using the `EXEC` role.

## How modules work

The Modules Manager acts as both a **registry** and an **execution gateway**:

1. **Registry** — Stores authorized module addresses mapped to unique `bytes32` identifiers
2. **Execution gateway** — Authorized callers trigger module execution on target contracts (vehicles)
3. **Target protection** — Each target contract must explicitly allow a module before it can be executed on it
4. **Security** — Adding or updating modules is protected by a mandatory timelock

## Module lifecycle

Every module goes through a three-step process:

1. **Registration** — An authorized `MODULE_MANAGER` calls `addModule`. This creates a pending request and starts the timelock countdown.
2. **Finalization** — After the timelock elapses, anyone can call `approvePendingModule` to activate the module.
3. **Authorization** — The target contract (e.g. a Vehicle) calls `allowModule` to permit execution of that module on itself.

<Note>
  The timelock is a mandatory delay between requesting a module addition and its activation. This gives stakeholders time to review the proposed module before it becomes executable.
</Note>

## Prerequisites

You need the following roles on your External Access Control, scoped to the **Modules Manager** contract:

| Role              | Purpose                                                        |
| ----------------- | -------------------------------------------------------------- |
| `MODULE_MANAGER`  | Add, update, or remove modules from the registry               |
| `EXEC`            | Execute authorized modules on target contracts                 |
| `CANCEL_MODULE`   | Cancel a pending module operation during the timelock          |
| `UPDATE_TIMELOCK` | Change the timelock duration                                   |
| `VEHICLE_ALLOW`   | Allow or disallow modules on a Vehicle (scoped to the Vehicle) |

## Add a module

Adding a module is a two-step process separated by the timelock.

<Steps>
  <Step title="Request module addition">
    Call `addModule` with the module's unique ID and its contract address. The ID must match the value returned by `IModule(module).MODULE_ID()`.

    **Requires:** `MODULE_MANAGER` role.

    <CodeGroup>
      ```solidity Solidity theme={null}
      bytes32 moduleId = keccak256("MERKL_DISTRIBUTOR");
      address moduleAddress = 0x...; // The module contract

      // This starts the timelock countdown
      modulesManager.addModule(moduleId, moduleAddress);
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>
  </Step>

  <Step title="Wait for the timelock">
    Check the current timelock duration and wait for it to elapse.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Query the timelock duration (in seconds)
      uint256 timelockDuration = modulesManager.timelock();

      // The module becomes approvable after: block.timestamp >= requestTimestamp + timelockDuration
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>
  </Step>

  <Step title="Approve the pending module">
    After the timelock has passed, finalize the module registration. This function can be called by any account.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Finalize the module — callable by anyone after the timelock
      modulesManager.approvePendingModule(moduleId);

      // Verify the module is now active
      address activeModule = modulesManager.getModule(moduleId);
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>
  </Step>

  <Step title="Allow the module on target contracts">
    Each target contract must explicitly authorize the module before it can be executed. For Vehicles, use the wrapper function that calls `allowModule` on the Modules Manager.

    **Requires:** `VEHICLE_ALLOW` role scoped to the Vehicle.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Allow the module on a specific vehicle
      vehicle.allowModule(moduleId);

      // Verify authorization
      bool isAllowed = modulesManager.allowed(address(vehicle), moduleId);
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>
  </Step>
</Steps>

## Execute a module

Once a module is active in the registry and allowed on the target, you can execute it.

**Requires:** `EXEC` role.

<CodeGroup>
  ```solidity Solidity theme={null}
  address target = address(vehicle);   // The contract to execute the module on
  bytes32 moduleId = keccak256("MERKL_DISTRIBUTOR");
  bytes memory data = abi.encode(...); // Encoded parameters for the module

  modulesManager.exec(target, moduleId, data);
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

<Info>
  The target contract must implement the `IModuleTarget` interface. All Railnet vehicles implement this interface by default.
</Info>

## Update a module

To update a module's implementation address, the process mirrors addition — request, timelock, approve.

<CodeGroup>
  ```solidity Solidity theme={null}
  bytes32 moduleId = keccak256("MERKL_DISTRIBUTOR");
  address newModuleAddress = 0x...; // The updated module contract

  // Request the update (starts timelock)
  modulesManager.updateModule(moduleId, newModuleAddress);

  // After timelock elapses:
  modulesManager.approvePendingModule(moduleId);
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

<Note>
  Updating a module does not require re-allowing it on target contracts. Existing `allowModule` authorizations carry over to the new implementation.
</Note>

## Remove a module

Removing a module revokes its authorization across all target contracts immediately.

**Requires:** `MODULE_MANAGER` role.

<CodeGroup>
  ```solidity Solidity theme={null}
  bytes32 moduleId = keccak256("MERKL_DISTRIBUTOR");

  // Remove the module (immediate effect)
  modulesManager.removeModule(moduleId);
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

<Warning>
  Removal fails if there is a pending request for the same module ID. Cancel the pending request first with `cancelPendingModule`, then remove.
</Warning>

## Cancel a pending module

If you need to abort a module addition or update during the timelock period, cancel it.

**Requires:** `CANCEL_MODULE` role.

<CodeGroup>
  ```solidity Solidity theme={null}
  bytes32 moduleId = keccak256("MERKL_DISTRIBUTOR");

  // Cancel the pending addition or update
  modulesManager.cancelPendingModule(moduleId);
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

## Disallow a module on a target

Revoke a module's authorization on a specific target without removing it from the registry.

<CodeGroup>
  ```solidity Solidity theme={null}
  // Disallow the module on a specific vehicle
  vehicle.disallowModule(moduleId);

  // The module remains in the registry but can no longer execute on this vehicle
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

## Update the timelock duration

Change the global timelock duration for future module operations.

**Requires:** `UPDATE_TIMELOCK` role.

<CodeGroup>
  ```solidity Solidity theme={null}
  // Set a new timelock of 48 hours
  uint256 newTimelock = 48 hours;
  modulesManager.updateTimelock(newTimelock);
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Configure fees" icon="percent" href="/developers/contracts/fee-manager">
    Set up fee structures for your Multi-Vehicle.
  </Card>

  <Card title="Configure rewards" icon="gift" href="/developers/contracts/interceptors">
    Capture and distribute additional protocol rewards with interceptors.
  </Card>
</CardGroup>
