Decrypt single value in Solidity

This example demonstrates the FHE decryption mechanism in Solidity.

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, euint32 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";

contract DecryptSingleValueInSolidity is SepoliaConfig {
  euint32 private _encryptedUint32; // = 0 (uninitizalized)
  uint32 private _clearUint32; // = 0 (uninitizalized)

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

  function initializeUint32(uint32 value) external {
    // Compute a trivial FHE formula _trivialEuint32 = value + 1
    _encryptedUint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));

    // Grant FHE permissions to:
    // ✅ The contract itself (`address(this)`): allows it to request async decryption to the FHEVM backend
    //
    // Note: If you forget to call `FHE.allowThis(_trivialEuint32)`,
    //       any async decryption request of `_trivialEuint32`
    //       by the contract itself (`address(this)`) will fail!
    FHE.allowThis(_encryptedUint32);
  }

  function initializeUint32Wrong(uint32 value) external {
    // Compute a trivial FHE formula _trivialEuint32 = value + 1
    _encryptedUint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
  }

  function requestDecryptSingleUint32() external {
    bytes32[] memory cypherTexts = new bytes32[](1);
    cypherTexts[0] = FHE.toBytes32(_encryptedUint32);

    // Two possible outcomes:
    // ✅ If `initializeUint32` was called, the decryption request will succeed.
    // ❌ If `initializeUint32Wrong` was called, the decryption request will fail 💥
    //
    // Explanation:
    // The request succeeds only if the contract itself (`address(this)`) was granted
    // the necessary FHE permissions. Missing `FHE.allowThis(...)` will cause failure.
    FHE.requestDecryption(
      // the list of encrypte values we want to decrypt
      cypherTexts,
      // the function selector the FHEVM backend will callback with the clear values as arguments
      this.callbackDecryptSingleUint32.selector
    );
  }

  function callbackDecryptSingleUint32(uint256 requestID, uint32 decryptedInput, bytes[] memory signatures) external {
    //
    // ===============================
    //    ☠️🔒 SECURITY WARNING! 🔒☠️
    // ===============================
    //
    // Must call `FHE.checkSignatures(...)` here!
    //            ------------------------
    //
    // This callback must only be called by the authorized FHEVM backend.
    // To enforce this, the contract author MUST verify the authenticity of the caller
    // by using the `FHE.checkSignatures` helper. This ensures that the provided signatures
    // match the expected FHEVM backend and prevents unauthorized or malicious calls.
    //
    // Failing to perform this verification allows anyone to invoke this function with
    // forged values, potentially compromising contract integrity.
    //
    // The responsibility for signature validation lies entirely with the contract author.
    //
    FHE.checkSignatures(requestID, signatures);
    _clearUint32 = decryptedInput;
  }

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

Last updated