RaylsApp
RaylsApp is the base contract for all cross-chain applications on Rayls Privacy Nodes. It provides the messaging primitives (_raylsSend, _raylsSendToResourceId) and the access control foundation used by every handler contract.
Inheritance: abstract contract \u2014 extend it directly for custom arbitrary-message apps; token handlers inherit it transitively.
Constructor
constructor(
address _endpoint,
address _raylsNodeEndpoint,
address _userGovernance
)| Parameter | Description |
|---|---|
_endpoint | Privacy Node endpoint \u2014 routes messages to other Privacy Nodes via the Private Network Hub |
_raylsNodeEndpoint | Privacy Node endpoint for the Rayls Public Chain \u2014 used when bridging to/from the public chain |
_userGovernance | RBAC contract that manages user roles and permissions within the Privacy Node |
Cross-chain messaging
_raylsSend \u2014 send by address
_raylsSend \u2014 send by addressfunction _raylsSend(
uint256 _dstChainId,
address _destination,
bytes memory _payload
) internal virtualSends a message to a specific contract address on another chain. Use when you know the destination contract address.
_dstChainId\u2014 destination Privacy Node chain ID (or Public Chain ID)_destination\u2014 the contract address to call on the destination chain_payload\u2014 ABI-encoded function call, e.g.abi.encodeWithSignature("myFunction(address,uint256)", to, amount)
_raylsSendToResourceId \u2014 send by resource ID
_raylsSendToResourceId \u2014 send by resource IDfunction _raylsSendToResourceId(
uint256 _dstChainId,
bytes32 _resourceId,
bytes memory _payload
) internal virtualRoutes by resourceId instead of address. Use when you don't know the exact contract address on the destination chain (the endpoint resolves it via the resource registry).
Atomic variant \u2014 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 virtualUsed by token handlers for atomic teleports. The extra payloads are executed if the transfer needs to be reverted: _revertDataPayloadSender runs on the source chain, _revertDataPayloadReceiver on the destination.
Access control
Rayls handlers use a role-based access control system (RaylsAccessManaged) instead of a single-owner pattern. Roles are enforced by the shared RaylsAccessManagerV1 on the Privacy Node.
The restricted modifier checks whether the caller has the required role for that function selector:
modifier restricted() {
_checkCanCall(msg.sender, msg.sig);
_;
}If the caller does not have the role, the call reverts with RaylsAccessManaged__Unauthorized.
Standard roles
| Role | Who holds it | Typical functions |
|---|---|---|
| Owner | The institution that deployed the token | mint, burn, submitTokenUpdate |
| MESSAGE_EXECUTOR | The Rayls relayer | Cross-chain receive callbacks (receiveTeleport, receiveResourceId, unlock, etc.) |
| RELAYER | The Rayls relayer (DVP handlers and Enygma) | dvpSwapCompleted, crossMint, crossRevertMint, etc. |
Roles are registered automatically when a handler is deployed, via _registerAccessControl(address _owner) inside the constructor. You do not need to configure roles manually for standard handlers.
onlyRegisteredUsers modifier
onlyRegisteredUsers modifiermodifier onlyRegisteredUsers()Restricts a function to callers who are registered with the Privacy Node's user governance contract (_userGovernance). Used on teleportToPublicChain to ensure only approved users can bridge to the public chain.
Token registration lifecycle
After deploying any token handler, call registerToken on the Privacy Node's TokenRegistryReplica system contract:
// 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 Private Hub's TokenRegistry. Once the Hub operator approves the registration, the relayer calls receiveResourceId() on your token contract automatically:
// Present in all handlers \u2014 called by the relayer after approval
function receiveResourceId(bytes32 _resourceId) public virtual restricted {
resourceId = _resourceId;
_registerResourceId(); // maps resourceId \u2192 address(this) on the endpoint
}You can verify registration by reading the public resourceId variable \u2014 a non-zero value means the token is registered. Teleport functions will 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 Privacy Node
function sendGreeting(uint256 destChainId, address destContract, string memory _msg) external {
_raylsSend(
destChainId,
destContract,
abi.encodeWithSignature("receiveGreeting(string)", _msg)
);
}
// Receive a greeting \u2014 restrict to MESSAGE_EXECUTOR in production
function receiveGreeting(string memory _msg) public {
message = _msg;
}
}Important: In production, receive functions should be marked
restrictedand their selectors registered under theMESSAGE_EXECUTORrole so only the Rayls relayer can invoke them.
Updated 4 days ago
