Public Decrypt multiple values

This example demonstrates the FHE public decryption mechanism with multiple value.

Public decryption is a mechanism that makes encrypted values visible to everyone once decrypted. Unlike user decryption where values remain private to authorized users, public decryption makes the data permanently visible to all participants. The public decryption call occurs onchain through smart contracts, making the decrypted value part of the blockchain's public state.

To run this example correctly, make sure the files are placed in the following directories:

  • .sol file → <your-project-root-dir>/contracts/

  • .ts file → <your-project-root-dir>/test/

This ensures Hardhat can compile and test your contracts as expected.

// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;

import { FHE, ebool, euint32, euint64 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";

contract PublicDecryptMultipleValues is SepoliaConfig {
  ebool private _encryptedBool; // = 0 (uninitialized)
  euint32 private _encryptedUint32; // = 0 (uninitialized)
  euint64 private _encryptedUint64; // = 0 (uninitialized)

  bool private _clearBool; // = 0 (uninitialized)
  uint32 private _clearUint32; // = 0 (uninitialized)
  uint64 private _clearUint64; // = 0 (uninitialized)

  // solhint-disable-next-line no-empty-blocks
  constructor() {}

  function initialize(bool a, uint32 b, uint64 c) external {
    // Compute 3 trivial FHE formulas

    // _encryptedBool = a ^ false
    _encryptedBool = FHE.xor(FHE.asEbool(a), FHE.asEbool(false));

    // _encryptedUint32 = b + 1
    _encryptedUint32 = FHE.add(FHE.asEuint32(b), FHE.asEuint32(1));

    // _encryptedUint64 = c + 1
    _encryptedUint64 = FHE.add(FHE.asEuint64(c), FHE.asEuint64(1));

    // see `DecryptSingleValueInSolidity.sol` for more detailed explanations
    // about FHE permissions and asynchronous public decryption requests.
    FHE.allowThis(_encryptedBool);
    FHE.allowThis(_encryptedUint32);
    FHE.allowThis(_encryptedUint64);
  }

  function requestDecryptMultipleValues() external {
    // To public decrypt multiple values, we must construct an array of the encrypted values
    // we want to public decrypt.
    //
    // ⚠️ Warning: The order of values in the array is critical!
    // The FHEVM backend will pass the public decrypted values to the callback function
    // in the exact same order they appear in this array.
    // Therefore, the order must match the parameter declaration in the callback.
    bytes32[] memory cypherTexts = new bytes32[](3);
    cypherTexts[0] = FHE.toBytes32(_encryptedBool);
    cypherTexts[1] = FHE.toBytes32(_encryptedUint32);
    cypherTexts[2] = FHE.toBytes32(_encryptedUint64);

    FHE.requestDecryption(
      // the list of encrypte values we want to public decrypt
      cypherTexts,
      // Selector of the Solidity callback function that the FHEVM backend will call with
      // the decrypted (clear) values as arguments
      this.callbackDecryptMultipleValues.selector
    );
  }

  // ⚠️ WARNING: The `cleartexts` argument is an ABI encoding of the decrypted values associated 
  // to the handles (using `abi.encode`). 
  // 
  // These values' types must match exactly! Mismatched types—such as using `uint32 decryptedUint64` 
  // instead of the correct `uint64 decryptedUint64` can cause subtle and hard-to-detect bugs, 
  // especially for developers new to the FHEVM stack.
  // Always ensure that the parameter types align with the expected decrypted value types.
  // 
  // !DOUBLE-CHECK!
  function callbackDecryptMultipleValues(
    uint256 requestID,
    bytes memory cleartexts,
    bytes memory decryptionProof
  ) external {
    // ⚠️ Don't forget the signature checks! (see `DecryptSingleValueInSolidity.sol` for detailed explanations)
    // The signatures are included in the `decryptionProof` parameter.
    FHE.checkSignatures(requestID, cleartexts, decryptionProof);

    (bool decryptedBool, uint32 decryptedUint32, uint64 decryptedUint64) = abi.decode(cleartexts, (bool, uint32, uint64));
    _clearBool = decryptedBool;
    _clearUint32 = decryptedUint32;
    _clearUint64 = decryptedUint64;
  }

  function clearBool() public view returns (bool) {
    return _clearBool;
  }

  function clearUint32() public view returns (uint32) {
    return _clearUint32;
  }

  function clearUint64() public view returns (uint64) {
    return _clearUint64;
  }
}

Last updated