RaylsApp

RaylsApp is the base contract for all cross-chain applications running in a Rayls Sovereign ledger. It provides the messaging primitives (_raylsSend, _raylsSendToResourceId) and the access control foundation used by every handler contract.

Inheritance: abstract contract. Extend it directly for custom arbitrary-message apps; token handlers inherit it transitively.


Constructor

constructor(
    address _endpoint,
    address _raylsNodeEndpoint,
    address _userGovernance
)
ParameterDescription
_endpointEndpoint in the Rayls Sovereign ledger, routing messages to other institutions through the Private Network Hub
_raylsNodeEndpointEndpoint for the Rayls Public Chain, used when bridging to and from the public chain
_userGovernanceRBAC contract that manages user roles and permissions within the Rayls Sovereign ledger

Cross-chain messaging

_raylsSend, send by address

function _raylsSend(
    uint256 _dstChainId,
    address _destination,
    bytes memory _payload
) internal virtual

Sends a message to a specific contract address on another chain. Use it where you know the destination contract address.

  • _dstChainId, the destination chain ID, whether that is another institution's ledger or the Public Chain
  • _destination, the contract address to call on the destination chain
  • _payload, the ABI-encoded function call, for example abi.encodeWithSignature("myFunction(address,uint256)", to, amount)

_raylsSendToResourceId, send by resource ID

function _raylsSendToResourceId(
    uint256 _dstChainId,
    bytes32 _resourceId,
    bytes memory _payload
) internal virtual

Routes by resourceId rather than by address. Use it where you do not know the exact contract address on the destination chain, since the endpoint resolves the address through the resource registry.

Atomic variant, with lock/revert payloads

function _raylsSendToResourceId(
    uint256 _dstChainId,
    bytes32 _resourceId,
    bytes memory _payload,
    bytes memory _lockData,
    bytes memory _revertDataPayloadSender,
    bytes memory _revertDataPayloadReceiver,
    BridgedTransferMetadata memory transferMetadata
) internal virtual

Used by token handlers for atomic teleports. The extra payloads execute where a transfer has to be reverted, with _revertDataPayloadSender running on the source chain and _revertDataPayloadReceiver on the destination.


Access control

Rayls handlers use role-based access control (RaylsAccessManaged) rather than a single-owner pattern, and roles are enforced by the shared RaylsAccessManagerV1 in the Rayls Sovereign ledger.

The restricted modifier checks whether the caller holds the required role for that function selector:

modifier restricted() {
    _checkCanCall(msg.sender, msg.sig);
    _;
}

Where the caller does not hold the role, the call reverts with RaylsAccessManaged__Unauthorized.

Standard roles

RoleWho holds itTypical functions
OwnerThe institution that deployed the tokenmint, burn, submitTokenUpdate
MESSAGE_EXECUTORThe Rayls relayerCross-chain receive callbacks (receiveTeleport, receiveResourceId, unlock, etc.)
RELAYERThe Rayls relayer (DVP handlers and Enygma)dvpSwapCompleted, crossMint, crossRevertMint, etc.

Roles are registered automatically when a handler is deployed, through _registerAccessControl(address _owner) inside the constructor, so there is no manual role configuration for standard handlers.

onlyRegisteredUsers modifier

modifier onlyRegisteredUsers()

Restricts a function to callers registered with the user governance contract (_userGovernance) in the Rayls Sovereign ledger. It is used on teleportToPublicChain so that only approved users can bridge to the public chain.


Token registration lifecycle

After deploying any token handler, call registerToken on the TokenRegistryReplica system contract in the Rayls Sovereign ledger:

// Call on the TokenRegistryReplica system contract (not on your token)
TokenRegistryReplica.registerToken(
    address tokenAddress,
    SharedObjects.ErcStandard ercStandard,
    bool isCustom
)

This sends a cross-chain message to the TokenRegistry on the Private Network Hub. Once the Private Network Operator approves the registration, the relayer calls receiveResourceId() on your token contract automatically:

// Present in all handlers — called by the relayer after approval
function receiveResourceId(bytes32 _resourceId) public virtual restricted {
    resourceId = _resourceId;
    _registerResourceId(); // maps resourceId → address(this) on the endpoint
}

Registration can be verified by reading the public resourceId variable, where a non-zero value means the token is registered. Teleport functions then work automatically once the registration message has been received.


Minimal custom cross-chain app

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "@rayls/contracts/RaylsApp.sol";

contract HelloWorldContract is RaylsApp {
    string public message;

    constructor(
        address _endpoint,
        address _raylsNodeEndpoint,
        address _userGovernance
    ) RaylsApp(_endpoint, _raylsNodeEndpoint, _userGovernance) {}

    // Send a greeting to a contract on another Rayls Sovereign
    function sendGreeting(uint256 destChainId, address destContract, string memory _msg) external {
        _raylsSend(
            destChainId,
            destContract,
            abi.encodeWithSignature("receiveGreeting(string)", _msg)
        );
    }

    // Receive a greeting — restrict to MESSAGE_EXECUTOR in production
    function receiveGreeting(string memory _msg) public {
        message = _msg;
    }
}

Important: In production, receive functions should be marked restricted and their selectors registered under the MESSAGE_EXECUTOR role, so that only the Rayls relayer can invoke them.


Did this page help you?