> For the complete documentation index, see [llms.txt](https://docs.zama.org/protocol/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zama.org/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm.md).

# 3. Turn it into FHEVM

In this tutorial, you'll learn how to take a basic Solidity smart contract and progressively upgrade it to support Fully Homomorphic Encryption using the FHEVM library by Zama.

Starting with the plain `Counter.sol` contract that you built from the ["Write a simple contract" tutorial](/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract.md), and step-by-step, you’ll learn how to:

* Replace standard types with encrypted equivalents
* Integrate zero-knowledge proof validation
* Enable encrypted on-chain computation
* Grant permissions for secure off-chain decryption

By the end, you'll have a fully functional smart contract that supports FHE computation.

## Initiate the contract

{% stepper %}
{% step %}

## Create the `FHECounter.sol` file

Navigate to your project’s `contracts` directory:

```sh
cd <your-project-root-directory>/contracts
```

From there, create a new file named `FHECounter.sol`, and copy the following Solidity code into it:

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

/// @title A simple counter contract
contract Counter {
  uint32 private _count;

  /// @notice Returns the current count
  function getCount() external view returns (uint32) {
    return _count;
  }

  /// @notice Increments the counter by a specific value
  function increment(uint32 value) external {
    _count += value;
  }

  /// @notice Decrements the counter by a specific value
  function decrement(uint32 value) external {
    require(_count >= value, "Counter: cannot decrement below zero");
    _count -= value;
  }
}
```

This is a plain `Counter` contract that we’ll use as the starting point for adding FHEVM functionality. We will modify this contract step-by-step to progressively integrate FHEVM capabilities.
{% endstep %}

{% step %}

## Turn `Counter` into `FHECounter`

To begin integrating FHEVM features into your contract, we first need to import the required FHEVM libraries.

#### Replace the current header

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

#### With this updated header:

```solidity
// 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";
```

These imports:

* **FHE** — the core library to work with FHEVM encrypted types
* **euint32** and **externalEuint32** — encrypted uint32 types used in FHEVM
* **ZamaEthereumConfig** — provides the FHEVM configuration for the Ethereum mainnet or Ethereum Sepolia testnet networks.\
  Inheriting from it enables your contract to use the FHE library

#### Replace the current contract declaration:

```solidity
/// @title A simple counter contract
contract Counter {
```

#### With the updated declaration :

```solidity
/// @title A simple FHE counter contract
contract FHECounter is ZamaEthereumConfig {
```

This change:

* Renames the contract to `FHECounter`
* Inherits from `ZamaEthereumConfig` to enable FHEVM support

{% hint style="warning" %}
This contract must inherit from the `ZamaEthereumConfig` abstract contract; otherwise, it will not be able to execute any FHEVM-related functionality on Sepolia or Hardhat.
{% endhint %}

From your project's root directory, run:

```sh
npx hardhat compile
```

Great! Your smart contract is now compiled and ready to use **FHEVM features.**
{% endstep %}
{% endstepper %}

## Apply FHE functions and types

{% stepper %}
{% step %}

## Comment out the `increment()` and `decrement()` Functions

Before we move forward, let’s comment out the `increment()` and `decrement()` functions in `FHECounter`. We'll replace them later with updated versions that support FHE-encrypted operations.

```solidity
 /// @notice Increments the counter by a specific value
// function increment(uint32 value) external {
//     _count += value;
// }

/// @notice Decrements the counter by a specific value
// function decrement(uint32 value) external {
//     require(_count >= value, "Counter: cannot decrement below zero");
//     _count -= value;
// }
```

{% endstep %}

{% step %}

## Replace `uint32` with the FHEVM `euint32` Type

We’ll now switch from the standard Solidity `uint32` type to the encrypted FHEVM type `euint32`.

This enables private, homomorphic computation on encrypted integers.

#### Replace

```solidity
uint32 _count;
```

and

```solidity
function getCount() external view returns (uint32) {
```

#### With :

```solidity
euint32 _count;
```

and

```solidity
function getCount() external view returns (euint32) {
```

{% endstep %}

{% step %}

## Replace `increment(uint32 value)` with the FHEVM version `increment(externalEuint32 value)`

To support encrypted input, we will update the increment function to accept a value encrypted off-chain.

Instead of using a `uint32`, the new version will accept an `externalEuint32`, which is an encrypted integer produced off-chain and sent to the smart contract.

To ensure the validity of this encrypted value, we also include a second argument:`inputProof`, a bytes array containing a Zero-Knowledge Proof of Knowledge (ZKPoK) that proves two things:

1. The `externalEuint32` was encrypted off-chain by the function caller (`msg.sender`)
2. The `externalEuint32` is bound to the contract (`address(this)`) and can only be processed by it.

#### Replace

```solidity
 /// @notice Increments the counter by a specific value
// function increment(uint32 value) external {
//     _count += value;
// }
```

#### With :

```solidity
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
  //     _count += value;
}
```

{% endstep %}

{% step %}

## Convert `externalEuint32` to `euint32`

You cannot directly use `externalEuint32` in FHE operations. To manipulate it with the FHEVM library, you first need to convert it into the native FHE type `euint32`.

This conversion is done using:

```solidity
FHE.fromExternal(inputEuint32, inputProof);
```

This method verifies the zero-knowledge proof and returns a usable encrypted value within the contract.

#### Replace

```solidity
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
  //     _count += value;
}
```

#### With :

```solidity
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
  euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
  //     _count += value;
}
```

{% endstep %}

{% step %}

## Convert `_count += value` into its FHEVM equivalent

To perform the update `_count += value` in a Fully Homomorphic way, we use the `FHE.add()` operator. This function allows us to compute the FHE sum of 2 encrypted integers.

#### Replace

```solidity
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
  euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
  //     _count += value;
}
```

#### With :

```solidity
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
  euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
  _count = FHE.add(_count, evalue);
}
```

{% hint style="info" %}
This FHE operation allows the smart contract to process encrypted values without ever decrypting them — a core feature of FHEVM that enables on-chain privacy.
{% endhint %}
{% endstep %}
{% endstepper %}

## Grant FHE Permissions

{% hint style="warning" %}
This step is critical! You must grant FHE permissions to both the contract and the caller to ensure the encrypted `_count` value can be decrypted off-chain by the caller. Without these 2 permissions, the caller will not be able to compute the clear result.
{% endhint %}

To grant FHE permission we will call the `FHE.allow()` function.

#### Replace

```solidity
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
  euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
  _count = FHE.add(_count, evalue);
}
```

#### With :

```solidity
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
  euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
  _count = FHE.add(_count, evalue);

  FHE.allowThis(_count);
  FHE.allow(_count, msg.sender);
}
```

{% hint style="info" %}
We grant **two** FHE permissions here — not just one. In the next part of the tutorial, you'll learn why **both** are necessary.
{% endhint %}

## Convert `decrement()` to its FHEVM equivalent

Just like with the `increment()` migration, we’ll now convert the `decrement()` function to its FHEVM-compatible version.

Replace :

```solidity
/// @notice Decrements the counter by a specific value
function decrement(uint32 value) external {
  require(_count >= value, "Counter: cannot decrement below zero");
  _count -= value;
}
```

with the following :

```solidity
/// @notice Decrements the counter by a specific value
/// @dev This example omits overflow/underflow checks for simplicity and readability.
/// In a production contract, proper range checks should be implemented.
function decrement(externalEuint32 inputEuint32, bytes calldata inputProof) external {
  euint32 encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);

  _count = FHE.sub(_count, encryptedEuint32);

  FHE.allowThis(_count);
  FHE.allow(_count, msg.sender);
}
```

{% hint style="warning" %}
The `increment()` and `decrement()` functions do not perform any overflow or underflow checks.
{% endhint %}

## Compile `FHECounter.sol`

From your project's root directory, run:

```sh
npx hardhat compile
```

Congratulations! Your smart contract is now fully **FHEVM-compatible**.

Now you should have the following files in your project:

* [`contracts/FHECounter.sol`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#fhecounter.sol) — your Solidity smart FHEVM contract
* [`test/FHECounter.ts`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#fhecounter.ts) — your FHEVM Hardhat test suite written in TypeScript

In the [next tutorial](https://github.com/zama-ai/fhevm/blob/release/0.13.x/docs/solidity-guides/getting-started/quick-start-tutorial/test_fhevm_contract.md), we’ll move on to the **TypeScript integration**, where you’ll learn how to interact with your newly upgraded FHEVM contract in a test suite.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.zama.org/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
