RaylsErc721Handler
RaylsErc721Handler is the abstract base contract for ERC721 (NFT) tokens in the Rayls Sovereign ledger. Extend it to create a non-fungible token that can be minted, burned and teleported cross-chain.
Inheritance: RaylsApp, ERC721, ERC721Holder, Initializable, ReentrancyGuard, RaylsAccessManaged
Minimal implementation
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import "@rayls/contracts/tokens/RaylsErc721Handler.sol";
contract MyNFT is RaylsErc721Handler {
constructor(
string memory _uri,
string memory _name,
string memory _symbol,
address _endpoint,
address _raylsNodeEndpoint,
address _userGovernance,
address _owner
)
RaylsErc721Handler(
_uri,
_name,
_symbol,
_endpoint,
_raylsNodeEndpoint,
_userGovernance,
_owner,
false
)
{}
}Constructor parameters
| Parameter | Description |
|---|---|
_uri | Base URI for token metadata |
_name | ERC721 collection name |
_symbol | ERC721 collection symbol |
_endpoint | Rayls Sovereign endpoint, which routes messages to other Rayls Nodes through the Private Network Hub |
_raylsNodeEndpoint | Rayls Sovereign endpoint for the Rayls Public Chain |
_userGovernance | RBAC contract that manages user roles and permissions within the Rayls Sovereign ledger |
_owner | Address granted the Owner role (mint, burn, submitTokenUpdate) |
The last parameter (false) marks this as a standard token, so pass true only if you are overriding the default transfer behaviour.
Token registration lifecycle
After deploying your token, call registerToken on the TokenRegistryReplica system contract in the Rayls Sovereign ledger:
TokenRegistryReplica.registerToken(tokenAddress, SharedObjects.ErcStandard.ERC721, false)Once the Private Network Operator approves the registration, the relayer calls receiveResourceId() on your contract automatically:
// Called by the relayer (MESSAGE_EXECUTOR role) after approval — do NOT call manually
function receiveResourceId(bytes32 _resourceId) public virtual restrictedYour token then holds a resourceId and is ready for cross-chain teleports.
Mint and burn
// Owner role only
function mint(address to, uint256 id) public virtual restricted nonReentrant
// Owner role only
function burn(uint256 id) public virtual restricted nonReentrantBoth functions automatically report the change to the TokenRegistry on the Private Network Hub via _submitTokenUpdate.
Balance updates
// Owner role only
function submitTokenUpdate(
SharedObjects.BalanceUpdateType updateType,
uint256 tokenId
) public virtual restrictedBalanceUpdateType is an enum with two values, MINT and BURN. You rarely need to call this function directly, because mint and burn both call it internally.
Teleport functions
Vanilla teleport
A vanilla teleport is a straightforward burn-and-mint operation with no revert mechanism.
// Burn from msg.sender, mint on destination
function teleport(address to, uint256 id, uint256 chainId) public virtual nonReentrant returns (bool)Atomic teleport
The token is locked on the destination until the teleport is confirmed, and if the destination fails, the relayer reverts the operation.
function teleportAtomic(address to, uint256 id, uint256 chainId) public virtual nonReentrant returns (bool)Teleport to Public Chain
Sends an NFT from the Rayls Sovereign ledger to an address on the Rayls Public Chain, and requires the caller to be a registered user.
function teleportToPublicChain(address to, uint256 id, uint256 destinationChainId)
public virtual onlyRegisteredUsers nonReentrant returns (bool)Token locking
ERC721 tokens use per-token locking, so a given token is either locked or it is not, unlike ERC20 which locks amounts:
mapping(address => mapping(uint256 => bool)) lockedTokens
// Read-only helper
function isTokenLocked(address account, uint256 id) public view returns (bool)Receive functions (called by the relayer)
| Function | Trigger |
|---|---|
receiveTeleport(address to, uint256 id) | A vanilla teleport has arrived, so the token is minted |
receiveTeleportAtomic(address to, uint256 id) | An atomic teleport has arrived, so the token is minted and locked |
receiveTeleportFromPublicChain(address to, uint256 id) | A teleport has arrived from the Rayls Public Chain, so the token is unlocked in the Rayls Sovereign ledger |
revertTeleportMint(address to, uint256 id) | An atomic teleport was reverted on the source, so the token is re-minted |
revertTeleportBurn(address to, uint256 id) | An atomic teleport was reverted on the destination, so the token is unlocked and burned |
revertTeleportToPublicChain(address from, uint256 id) | The mint on the Rayls Public Chain failed, so the token is unlocked |
unlock(address to, uint256 id) | An atomic teleport was confirmed, so the locked token is released |
All of these functions are restricted to MESSAGE_EXECUTOR.
Access control summary
| Role | Functions |
|---|---|
| Owner | mint, burn, submitTokenUpdate |
| MESSAGE_EXECUTOR | receiveTeleport, receiveTeleportAtomic, receiveTeleportFromPublicChain, revertTeleportMint, revertTeleportBurn, revertTeleportToPublicChain, unlock, receiveResourceId |
| Any address | teleport, teleportAtomic, teleportToPublicChain* |
teleportToPublicChainadditionally requiresonlyRegisteredUsers.
Updated 5 days ago
