Skip to main content
Railnet uses External Access Control (EAC) — a flexible role system that supports global, scoped, and public roles. As a Conduit owner, you configure EAC to control who can operate strategies, execute STEAM queries, manage fees, and more.

Role types

Standard roles that apply across the entire protocol. If you grant an account the VEHICLE_STEAM role globally, they can interact with all yield sources.
accessControl.grantRole(Roles.VEHICLE_STEAM, operatorAddress);
Roles restricted to a specific contract. Grant the VEHICLE_STEAM role scoped to a single yield source, and the account can only interact with that source.
accessControl.grantScopedRole(
    Roles.VEHICLE_STEAM,
    address(myVehicle),  // scope
    operatorAddress
);
Scoped roles are encoded as keccak256(abi.encodePacked(role, scope)).
Roles effectively granted to everyone. When a role is public, hasRole checks always return true regardless of the account.
// Make VEHICLE_STEAM public for a specific Vehicle
accessControl.setScopedRolePublic(
    Roles.VEHICLE_STEAM,
    address(myVehicle),
    true
);
The DEFAULT_ADMIN_ROLE can never be made public.

Deploy and configure access control

Every Conduit needs an EAC contract. This section walks you through deploying one and granting the roles your product needs.
1

Deploy ExternalAccessControl

Deploy the EAC contract with your initial admin. Set initialDelay to a non-zero value (e.g. 48 hours) for production deployments — this protects admin transfers with a time delay.
ExternalAccessControlFactory.SpawnParams memory params = ExternalAccessControlFactory.SpawnParams({
    initialDelay: 48 hours,              // Time-delayed admin transfer for security
    initialDefaultAdmin: platformAdmin,   // Your platform's admin address (use multisig)
    initialRoles: new ExternalAccessControlFactory.RoleAttribution[](0),
    deploymentSalt: keccak256("platform-eac-v1")
});

ExternalAccessControl eac = eacFactory.spawn(params);
Use a multisig wallet (e.g. Safe) as the initialDefaultAdmin. This is the most privileged role in the system — it can grant and revoke any role.
2

Grant operator roles

Authorize your operators and asset managers for the specific strategies and yield sources they manage.
// Grant STEAM access scoped to a specific Multi-Vehicle
eac.grantScopedRole(Roles.VEHICLE_STEAM, address(multiVehicle), operator);

// Grant Multi-Vehicle management roles
eac.grantScopedRole(Roles.MULTI_VEHICLE_DEPOSIT, address(multiVehicle), operator);
eac.grantScopedRole(Roles.MULTI_VEHICLE_DISPATCH, address(multiVehicle), operator);
eac.grantScopedRole(Roles.MULTI_VEHICLE_REBALANCE, address(multiVehicle), operator);
3

Authorize yield sources

Allow the strategy to interact with specific yield sources.
// Authorize a Vehicle in the Multi-Vehicle registry
eac.grantScopedRole(
    Roles.MULTI_VEHICLE_SET_VEHICLE_AUTHORIZATION,
    address(multiVehicle),
    platformAdmin
);
4

Configure fee management roles

// Allow updating fees
eac.grantScopedRole(Roles.FEE_MANAGER_SET_FEES, address(feeManager), feeAdmin);

// Allow distributing collected fees
eac.grantScopedRole(Roles.FEE_MANAGER_DISPATCH_ERC20, address(feeManager), operator);

Key roles reference

Yield source operations

RoleAllows
VEHICLE_STEAMcreate(), resume(), unlock(), recover() on Vehicles
VEHICLE_SET_INTERCEPTIONSConfigure reward interception rules
VEHICLE_ALLOWManage module allowlist

Strategy management

RoleAllows
MULTI_VEHICLE_DEPOSITDirect deposits into accounting
MULTI_VEHICLE_DISPATCHSend assets to yield sources
MULTI_VEHICLE_REBALANCERebalance across positions
MULTI_VEHICLE_MOVE_ASSETSMove assets between sectors
MULTI_VEHICLE_MOVE_SHARESMove shares between sectors
MULTI_VEHICLE_SET_VEHICLE_AUTHORIZATIONAuthorize/deauthorize yield sources
MULTI_VEHICLE_SET_QUEUESConfigure queue parameters
MULTI_VEHICLE_SET_THRESHOLDSSet operational thresholds
MULTI_VEHICLE_PROGRESS_QUERYAdvance sub-query state

Fee management

RoleAllows
FEE_MANAGER_SET_FEESUpdate fee percentages
FEE_MANAGER_SET_FEE_RECIPIENTSUpdate fee recipients
FEE_MANAGER_DISPATCH_ERC20Distribute collected fees
FEE_MANAGER_REDEEM_VEHICLE_SHARESRedeem fee shares

Infrastructure

RoleAllows
FACTORY_SPAWNDeploy new contracts via factories
BEACON_UPGRADEUpgrade beacon implementations
BEACON_FREEZEPermanently freeze a beacon
BEACON_PAUSE / BEACON_UNPAUSEPause/unpause beacons

Checking permissions

// Check global role
bool hasAccess = eac.hasRole(Roles.VEHICLE_STEAM, account);

// Check scoped role
bool hasScopedAccess = eac.hasScopedRole(
    Roles.VEHICLE_STEAM,
    address(myVehicle),
    account
);

// Check either global or scoped
bool hasAnyAccess = eac.hasRoleOrScopedRole(
    Roles.VEHICLE_STEAM,
    address(myVehicle),
    account
);

Admin transfer

The DEFAULT_ADMIN_ROLE uses a time-delayed transfer for security:
1

Initiate transfer

eac.beginDefaultAdminTransfer(newAdmin);
2

Wait for delay

The configured delay (e.g., 48 hours) must pass.
3

Accept transfer

The new admin calls:
eac.acceptDefaultAdminTransfer();
Use scoped roles whenever possible. They provide granular control and limit the blast radius if an account is compromised.