RaylsErc20Handler

RaylsErc20Handler is the abstract base contract for ERC20 tokens on a Rayls Privacy Node. Extend it to create a fungible token that can be minted, burned, and teleported cross-chain.

Inheritance: RaylsApp, ERC20, Initializable, RaylsAccessManaged


Minimal implementation

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

import "@rayls/contracts/tokens/RaylsErc20Handler.sol";

contract MyToken is RaylsErc20Handler {
    constructor(
        string memory _name,
        string memory _symbol,
        address _endpoint,
        address _raylsNodeEndpoint,
        address _userGovernance,
        address _owner
    )
        RaylsErc20Handler(
            _name,
            _symbol,
            _endpoint,
            _raylsNodeEndpoint,
            _userGovernance,
            _owner,
            false
        )
    {}
}

Constructor parameters

ParameterDescription
_nameERC20 token name
_symbolERC20 token symbol
_endpointPrivacy Node endpoint — routes messages to other Privacy Nodes via the Private Network Hub
_raylsNodeEndpointPrivacy Node endpoint for the Rayls Public Chain
_userGovernanceRBAC contract that manages user roles and permissions within the Privacy Node
_ownerAddress granted the Owner role (mint, burn, submitTokenUpdate)

The last parameter (false) marks this as a standard token. Pass true only if you are overriding the default transfer behavior.


Token registration lifecycle

After deploying your token, call registerToken on the Privacy Node's TokenRegistryReplica system contract to submit it for approval:

// Call on the TokenRegistryReplica system contract (not on your token)
TokenRegistryReplica.registerToken(
    address tokenAddress,          // your deployed token contract
    SharedObjects.ErcStandard ercStandard,  // e.g. ErcStandard.ERC20
    bool isCustom                  // false for standard handlers
)

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:

// Called by the relayer (MESSAGE_EXECUTOR role) after approval — do NOT call manually
function receiveResourceId(bytes32 _resourceId) public virtual restricted

After receiveResourceId is called, your token has a resourceId and is ready for cross-chain teleports.


Mint and burn

// Owner role only
function mint(address to, uint256 value) public virtual restricted

// Owner role only
function burn(address from, uint256 value) public virtual restricted

Both functions automatically report the balance change to the Private Hub's TokenRegistry via _submitTokenUpdate.


Balance updates

// Owner role only — manually trigger a balance sync to the Private Hub
function submitTokenUpdate(
    SharedObjects.BalanceUpdateType updateType,
    uint256 amount
) public virtual restricted

BalanceUpdateType is an enum: MINT or BURN. You rarely need to call this directly — mint and burn call it internally.


Teleport functions

All teleport functions burn tokens on the source chain and trigger a mint on the destination chain.

Vanilla teleport (VT20)

Simple burn-and-mint with no revert mechanism.

// Burn from msg.sender, mint on destination
function teleport(address to, uint256 value, uint256 chainId) public virtual returns (bool)

// Burn from `from` (requires prior approval from `from`)
function teleportFrom(address from, address to, uint256 value, uint256 chainId) public virtual returns (bool)

Atomic teleport (AT20)

Tokens are locked on the destination until the transfer is confirmed. If the destination fails, the relayer reverts the operation and restores the sender's tokens.

// Atomic burn from msg.sender
function teleportAtomic(address to, uint256 value, uint256 destinationChainId) public virtual returns (bool)

// Atomic burn from `from` (requires prior approval)
function teleportAtomicFrom(address from, address to, uint256 value, uint256 destinationChainId) public virtual returns (bool)

Teleport to Public Chain

Send tokens from a Privacy Node to an address on the Rayls Public Chain. Requires the caller to be a registered user (onlyRegisteredUsers).

function teleportToPublicChain(address to, uint256 value, uint256 destinationChainId)
    public virtual onlyRegisteredUsers returns (bool)

Tokens are locked on the Privacy Node until the Public Chain mint is confirmed. If it fails, revertTeleportToPublicChain restores the balance.


Receive functions (called by the relayer)

These are marked restricted and can only be called by the MESSAGE_EXECUTOR role. You do not call them directly.

FunctionTrigger
receiveTeleport(address to, uint256 value)Vanilla teleport arrived — mints tokens
receiveTeleportAtomic(address to, uint256 value)Atomic teleport arrived — mints and locks tokens pending confirmation
receiveTeleportFromPublicChain(address to, uint256 value)Public Chain → Privacy Node transfer arrived — unlocks tokens
revertTeleportMint(address to, uint256 value)Atomic teleport reverted on source — re-mints tokens
revertTeleportBurn(address to, uint256 value)Atomic teleport reverted on destination — unlocks and burns
revertTeleportToPublicChain(address from, uint256 amount)Public Chain mint failed — unlocks tokens
unlock(address to, uint256 amount)Confirms atomic teleport — releases locked tokens

Access control summary

RoleFunctions
Ownermint, burn, submitTokenUpdate
MESSAGE_EXECUTORreceiveTeleport, receiveTeleportAtomic, receiveTeleportFromPublicChain, revertTeleportMint, revertTeleportBurn, revertTeleportToPublicChain, unlock, receiveResourceId
Any addressteleport, teleportFrom, teleportAtomic, teleportAtomicFrom, teleportToPublicChain*
  • teleportToPublicChain additionally requires onlyRegisteredUsers.

Roles are managed by the RBAC contract (_userGovernance) configured at deployment. See RaylsApp for details.