Arithmetic Operations

FHE arithmetic (add, sub, mul, div, rem, min, max) on encrypted values. Includes gas cost comparisons and key limitations, such as plaintext divisors for division and remainder operations.

circle-info

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.

chevron-right🔐 FHE API Reference (12 items)hashtag

Types: euint32 · externalEuint32

Functions:

  • FHE.add() - Homomorphic addition: result = a + b (overflow wraps)

  • FHE.allow() - Grants PERMANENT permission for address to decrypt/use value

  • FHE.allowThis() - Grants contract permission to operate on ciphertext

  • FHE.div() - Homomorphic division: result = a / b (plaintext divisor only)

  • FHE.fromExternal() - Validates and converts external encrypted input using inputProof

  • FHE.max() - Returns larger of two encrypted values

  • FHE.min() - Returns smaller of two encrypted values

  • FHE.mul() - Homomorphic multiplication: result = a * b

  • FHE.rem() - Homomorphic remainder: result = a % b (plaintext divisor only)

  • FHE.sub() - Homomorphic subtraction: result = a - b (underflow wraps)

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

import {FHE, euint32, externalEuint32} from "@fhevm/solidity/lib/FHE.sol";
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";

/**
 * @notice FHE arithmetic (add, sub, mul, div, rem, min, max) on encrypted values.
 *         Includes gas cost comparisons and key limitations, such as plaintext
 *         divisors for division and remainder operations.
 *
 * @dev div/rem only work with plaintext divisors (not encrypted).
 */
contract FHEArithmetic is ZamaEthereumConfig {
    euint32 private _a;
    euint32 private _b;
    euint32 private _result;

    function setA(externalEuint32 inputA, bytes calldata inputProof) external {
        _a = FHE.fromExternal(inputA, inputProof);
        FHE.allowThis(_a);
    }

    function setB(externalEuint32 inputB, bytes calldata inputProof) external {
        _b = FHE.fromExternal(inputB, inputProof);
        FHE.allowThis(_b);
    }

    /// @notice Encrypted addition: result = a + b
    function computeAdd() external {
        _result = FHE.add(_a, _b);
        _grantPermissions();
    }

    /// @notice Encrypted subtraction: result = a - b
    /// @dev ⚠️ No underflow protection! Wraps around at 0.
    function computeSub() external {
        _result = FHE.sub(_a, _b);
        _grantPermissions();
    }

    /// @notice Encrypted multiplication: result = a * b
    /// @dev ⚠️ No overflow protection! May wrap at type max.
    function computeMul() external {
        _result = FHE.mul(_a, _b);
        _grantPermissions();
    }

    /// @notice Encrypted division: result = a / divisor
    /// @dev ❌ WRONG: FHE.div(encryptedA, encryptedB) - not supported!
    ///      ✅ CORRECT: FHE.div(encryptedA, plaintextB)
    function computeDiv(uint32 divisor) external {
        _result = FHE.div(_a, divisor);
        _grantPermissions();
    }

    /// @notice Encrypted remainder: result = a % modulus
    /// @dev Modulus must be plaintext (same limitation as div)
    function computeRem(uint32 modulus) external {
        _result = FHE.rem(_a, modulus);
        _grantPermissions();
    }

    /// @notice Encrypted minimum: result = min(a, b)
    function computeMin() external {
        _result = FHE.min(_a, _b);
        _grantPermissions();
    }

    /// @notice Encrypted maximum: result = max(a, b)
    function computeMax() external {
        _result = FHE.max(_a, _b);
        _grantPermissions();
    }

    function getResult() public view returns (euint32) {
        return _result;
    }

    function _grantPermissions() internal {
        FHE.allowThis(_result);
        FHE.allow(_result, msg.sender);
    }
}

Last updated