Viewers ​
Viewers are addresses that have permission to decrypt the data associated with a handle. Managing viewers is essential for controlling who can view the decrypted data.
INFO
An account which is a viewer for a handle can get the decrypted value of a handle with the decrypt method of the SDK.
Checking Viewers ​
The Nox protocol smart contract provides a function to check if a specific address is a viewer for a given handle:
function isViewer(bytes32 handle, address account) external view returns (bool);isViewer ABI:
[
{
"inputs": [
{
"internalType": "bytes32",
"name": "handle",
"type": "bytes32"
},
{
"internalType": "address",
"name": "viewer",
"type": "address"
}
],
"name": "isViewer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
}
]const noxContract = new Contract(
NOX_CONTRACT_ADDRESS,
NOX_CONTRACT_ABI,
provider
);
const isViewer: boolean = await noxContract.isViewer(handle, account);const isViewer = await publicClient.readContract({
address: NOX_CONTRACT_ADDRESS,
abi: NOX_CONTRACT_ABI,
functionName: 'isViewer',
args: [handle, viewerAddress],
});Adding Viewers ​
The Nox protocol smart contract provides a function for admins to add a specific address as a viewer for a given handle:
INFO
Only allowed admins can add viewers. (see Manage Admins guide for more details on admins management).
WARNING
Once added, a viewer cannot be removed.
This is by design: once a viewer has been granted access, they can decrypt the handle at any time. Revoking on-chain permission would give a false sense of security — the viewer may have already decrypted and stored the data locally via the Handle Gateway.
function addViewer(bytes32 handle, address account) external;addViewer ABI:
[
{
"inputs": [
{
"internalType": "bytes32",
"name": "handle",
"type": "bytes32"
},
{
"internalType": "address",
"name": "viewer",
"type": "address"
}
],
"name": "addViewer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]const noxContract = new Contract(
NOX_CONTRACT_ADDRESS,
NOX_CONTRACT_ABI,
signer
);
const tx = await noxContract.addViewer(handle, viewerAddress);
await tx.wait();const [userAddress] = await walletClient.getAddresses();
await walletClient.writeContract({
account: userAddress,
chain: CHAIN,
address: NOX_CONTRACT_ADDRESS,
abi: NOX_CONTRACT_ABI,
functionName: 'addViewer',
args: [handle, viewerAddress],
});Isolating Access via a New Handle ​
There is no on-chain revoke for viewer access. For use cases that require access isolation (e.g. end of a regulatory audit), the recommended pattern is to migrate to a fresh handle:
- Create a new handle with the same value —
Nox.add(existingHandle, Nox.toEuint256(0))produces a new handle with a fresh ACL. Use the matching converter for other types (Nox.toEuint16,Nox.toEbool, etc.). - Update your contract's storage to point to the new handle.
- Grant access only to the addresses that should retain access on the new handle.
The old handle remains accessible to previous viewers, but is no longer used by your application.
INFO
This pattern costs extra gas and does not destroy the ciphertext on the Handle Gateway. It is an application-level isolation, not a cryptographic revoke.
