Approving new tokens
A token can only move between the Rayls Sovereign chains of a Rayls Private Network once the Private Network operator has approved it on the Private Network Hub. Approval gives the token a resource ID that every chain recognises.
The issuing institution registers and submits the token from its own Rayls Sovereign chain (Privacy Node in the code). The operator then approves it on the Hub, and the Hub tells the issuer's chain that the token is active.
The flow
| Step | Who | Where | Call | Result |
|---|---|---|---|---|
| 1. Deploy | Issuer | Issuer's chain | Deploy the token. Enygma tokens must be deployed through the chain's contract factory. | |
| 2. Register | Issuer | Issuer's chain | registerToken(tokenAddress) on the chain's token registry (PNTokenRegistryV1) | Chain status WAITING_APPROVAL |
| 3. Authorise locally | Issuer's chain administrator | Issuer's chain | updatePrivacyNodeStatus(tokenAddress, 2) | Chain status AUTHORIZED |
| 4. Submit | Issuer's chain administrator | Issuer's chain | submitToHub(tokenAddress) | Hub status on the chain: WAITING_APPROVAL. A registration message goes to the Hub. |
| 5. Record | Hub | Hub | The message calls addToken on the Hub's TokenRegistry | The token gets a resource ID. Hub status NEW. |
| 6. Approve | Private Network operator | Hub | updateStatus(resourceId, 1) on the Hub's TokenRegistry | Hub status ACTIVE. An activation message goes to the issuer's chain. |
| 7. Activate | Hub | Issuer's chain | The message calls activateToken | The token takes its resource ID. Hub status on the chain: AUTHORIZED. The token can now move. |
Other participants don't register the token. The first time a chain receives it, a copy is deployed there automatically.
What the Hub checks at step 5
When the registration message reaches the Hub, addToken:
- accepts it only if the issuing chain is an active participant with the
ISSUERrole (see Adding new participants); otherwise the registration fails on the Hub, and the token staysWAITING_APPROVALon the issuer's chain; - rejects a token whose name is already registered on the Hub. Names are unique across the network;
- assigns the resource ID and records the token with status
NEW; - for an Enygma token, creates the token's Enygma contract on the Hub; for a delivery-versus-payment (DvP) ERC-721 or ERC-1155 token, creates its DvP contract on the Hub.
Supported standards are ERC-20, ERC-721, ERC-1155, Enygma, and the DvP versions of ERC-721 and ERC-1155. See Supported token standards.
The statuses
The issuer's chain tracks two statuses for each token; the Hub tracks one.
| Registry | Field | Values |
|---|---|---|
Issuer's chain (PNTokenRegistryV1) | privacyNodeStatus | UNDEFINED (0), WAITING_APPROVAL (1), AUTHORIZED (2), UNAUTHORIZED (3), FROZEN (4) |
| Issuer's chain | hubStatus | Same values as above |
Hub (TokenRegistryV1) | status | NEW (0), ACTIVE (1), INACTIVE (2) |
On the issuer's chain, a token can move to other chains once both its privacyNodeStatus and its hubStatus are AUTHORIZED.
Approve a token
1. Find tokens waiting for approval
With the governance API:
curl "http://<governance-api>/audit/tokens?status=new"{
"data": [
{
"resourceId": "9f4c…",
"name": "Bank B Deposit Token",
"symbol": "BBDT",
"metadataURL": "",
"decimals": 2,
"issuerId": "200002",
"status": "new",
"ercStandard": "erc20",
"totalSupply": "0",
"circulatingSupply": [],
"frozenChainIds": [],
"createdAt": "2026-10-06T10:02:13Z",
"updatedAt": "2026-10-06T10:02:13Z"
}
],
"total": 1,
"limit": 10,
"page": 1
}GET /audit/tokens also filters by name, symbol, issuerId, ercStandard, decimals, createdAfter and createdBefore, and pages with page and limit (up to 100). GET /audit/tokens/{resourceId} returns one token.
To read the Hub directly, run npx hardhat getAllTokens. It prints each token's resource ID, name, symbol, and whether it is approved.
2. Check the token
There is no automatic check. Before approving, confirm with the issuer that the name, symbol, standard, decimals and issuing chain (issuerId) are the ones it intended.
3. Approve it
Set the token's Hub status to ACTIVE (1) with the operator's ADMIN account, or an account with the optional PRIVATE_NETWORK_OPERATOR role. In a Hardhat script, with operator and registry set up as in Adding new participants:
const tokenRegistry = await ethers.getContractAt('TokenRegistryV1', await registry.getContract('TokenRegistry'), operator);
await (await tokenRegistry.updateStatus(resourceId, 1)).wait();The Hardhat tasks in rayls-sovereign-contracts do the same. They use PNH_RPC_URL, PNH_DEPLOYMENT_PROXY_REGISTRY and PRIVATE_KEY_SYSTEM.
| Task | What it approves |
|---|---|
tokens:approve-hub --symbol <SYMBOL> | The token whose resource ID is in the TOKEN_<SYMBOL>_RESOURCE_ID environment variable |
tokens:approve-last-hub | The most recently registered token on the Hub |
tokens:approve-last-batch-hub --n <N> | The N most recently registered tokens |
tokens:approve-all-hub | Every registered token that isn't approved yet |
Approve deliberatelyThe
lasttasks approve whatever the Hub registered most recently, and thealltask approves everything pending, from any issuer, without showing you the tokens first. On a shared network, list the pending tokens and approve them one at a time.
4. Confirm activation
The Hub records the approval at once: GET /audit/tokens/{resourceId} shows "status": "active" after the listener has indexed it. The token can move only after the activation message has reached the issuer's chain. On that chain, getHubStatus(tokenAddress) on PNTokenRegistryV1 returns AUTHORIZED (2), and tokens:statuses --pn <ID> --token-address <address> prints the token's statuses.
Not approving a token
The Hub has no rejection. A token you don't approve stays NEW on the Hub and WAITING_APPROVAL on the issuer's chain, and can't move. The issuer's chain administrator can mark it locally with rejectToken(tokenAddress), which sets its Hub status on that chain to UNAUTHORIZED.
Stopping an approved token
To stop an approved token, freeze it for the participants concerned. See Freezing tokens.
For the issuer
The issuer's steps use roles on its own chain: TOKEN_CREATOR for registerToken, and PN_TOKEN_REGISTRY_ADMIN for updatePrivacyNodeStatus, submitToHub and rejectToken. If the chain's business roles are active, PRIVACY_NODE_OPERATOR can also call updatePrivacyNodeStatus and submitToHub (see Private Network roles). The Hardhat tasks for them are tokens:register, tokens:approve-pn and submitTokenToHub.
The Rayls Sovereign Ops API, an optional service for a Rayls Sovereign chain, exposes the same steps:
| Step | Ops API call |
|---|---|
| Register | POST /api/tokens/{address}/register |
| Authorise locally | PATCH /api/v1/admin/tokens/{address}/status with {"status": "authorized"} |
| Submit to the Hub | POST /api/v1/admin/tokens/{address}/submit with {"target": "hub"} |
| List tokens waiting for local approval | GET /api/tokens/registry/pending |
For an Enygma token from end to end, see Running Rayls Enygma.
Updated 1 day ago
