The decryption of the confidential state can directly happen on-chain using TFHE.decrypt() (see Decryption), in which case the plain text is viewable by the public. In some cases, this might not be desirable, and we might want only a small number of users to be able to see the decrypted value. For example, in an EncryptedERC20 application, users should only be able to decrypt their own balance.

This can be done off-chain using reencryption. The authorized end user will create a local keypair, use TFHE.reencrypt() on the smart contract side and instance.decrypt() on the client side.

The publicKey and privateKey pair is generated using generatePublicKey() from fhevmjs and the re-encrypted response can be decrypted using the instance.decrypt() function on the client side.

Here's the fhevmjs code for a user to view their own balance for a private ERC-20 contract :

const instance = await createInstance({ chainId, publicKey });
const CONTRACT_ADDRESS = '0x1c786b8ca49D932AFaDCEc00827352B503edf16c';

// Get or generate the end user's local keypair.
const getReencryptPublicKey = async (contractAddress) => {
  if (!instance.hasKeypair(contractAddress)) {
    const eip712Domain = {
      // Give a user-friendly name to the specific contract you're signing for.
      // This must match the EIP712WithModifier string in the contract constructor.
      name: 'Authorization token',
      // This identifies the latest version.
      // This must match the EIP712WithModifier version in the contract constructor.
      version: '1',
      // This defines the network, in this case, Gentry Testnet.
      chainId: 9090,
      // Add a verifying contract to make sure you're establishing contracts with the proper entity.
      verifyingContract: contractAddress,
    const reencryption = instance.generatePublicKey(eip712Domain);
    const params = [userAddress, JSON.stringify(reencryption.eip712)];
    const sig = window.ethereum.request({
      method: "eth_signTypedData_v4",
    instance.setSignature(contractAddress, sig);

  return instance.getPublicKey(contractAddress);

const reencrypt = await getReencryptPublicKey(CONTRACT_ADDRESS);
const encryptedBalance = await contract.balanceOf(reencrypt.publicKey, reencrypt.signature);
const balance = await instance.decrypt(CONTRACT_ADDRESS, encryptedBalance)

Please note that the balanceOf function on the private ERC-20 contract (see Confidential ERC-20) is gated with an EIP 712 signature requirement. This is to prove that the users calling the balanceOf function to view their balance is indeed the users themselves.

Last updated