Decrypt multiple values in Solidity

This example demonstrates the FHE decryption mechanism in Solidity with multiple values.

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 DecryptMultipleValuesInSolidity is SepoliaConfig {
  ebool private _encryptedBool; // = 0 (uninitizalized)
  euint32 private _encryptedUint32; // = 0 (uninitizalized)
  euint64 private _encryptedUint64; // = 0 (uninitizalized)

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

  // 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 decryption requests.
    FHE.allowThis(_encryptedBool);
    FHE.allowThis(_encryptedUint32);
    FHE.allowThis(_encryptedUint64);
  }

  function requestDecryptMultipleValues() external {
    // To decrypt multiple values, we must construct an array of the encrypted values
    // we want to decrypt.
    //
    // ⚠️ Warning: The order of values in the array is critical!
    // The FHEVM backend will pass the 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 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: Argument 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,
    bool decryptedBool,
    uint32 decryptedUint32,
    uint64 decryptedUint64,
    bytes[] memory signatures
  ) external {
    // ⚠️ Don't forget the signature checks! (see `DecryptSingleValueInSolidity.sol` for detailed explanations)
    FHE.checkSignatures(requestID, signatures);
    _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