> ## Documentation Index
> Fetch the complete documentation index at: https://cofhe-docs.fhenix.zone/llms.txt
> Use this file to discover all available pages before exploring further.

# Adding FHE to an Existing Contract

> Step-by-step guide for integrating FHE capabilities into contracts you've already built

## Overview

Let's start with an existing contract, and walk through the steps to migrate the contract to CoFHE.

The contract is a very simple voting contract.

## Original Contract

```solidity theme={null}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.25;

contract VotingExample {
    struct Option {
        string name;
        uint64 votes;
    }

    struct Proposal {
        string name;
        uint256 deadline;
        Option[] options;
        mapping(address => bool) hasVoted;
        bool exists;
        uint8 winner;
    }

    address public owner;
    uint256 public proposalCount;
    mapping(uint256 => Proposal) public proposals;

    event ProposalCreated(
        uint256 indexed proposalId,
        string name,
        uint256 deadline
    );
    event VoteCast(
        uint256 indexed proposalId,
        address indexed voter,
        uint256 optionIndex
    );

    error NotOwner();
    error InvalidOptionCount();
    error ProposalNotFound();
    error DeadlineExpired();
    error AlreadyVoted();
    error InvalidOptionIndex();

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        if (msg.sender != owner) revert NotOwner();
        _;
    }

    function createProposal(
        string memory _name,
        string[] memory _options,
        uint256 _deadline
    ) external onlyOwner returns (uint256) {
        if (_options.length < 2 || _options.length > 4)
            revert InvalidOptionCount();

        uint256 proposalId = proposalCount++;
        Proposal storage proposal = proposals[proposalId];

        proposal.name = _name;
        proposal.deadline = _deadline;
        proposal.exists = true;

        for (uint i = 0; i < _options.length; i++) {
            proposal.options.push(Option({name: _options[i], votes: 0}));
        }

        emit ProposalCreated(proposalId, _name, _deadline);
        return proposalId;
    }

    function vote(uint256 _proposalId, uint256 _optionIndex) external {
        Proposal storage proposal = proposals[_proposalId];

        if (!proposal.exists) revert ProposalNotFound();
        if (block.timestamp >= proposal.deadline) revert DeadlineExpired();
        if (proposal.hasVoted[msg.sender]) revert AlreadyVoted();
        if (_optionIndex >= proposal.options.length)
            revert InvalidOptionIndex();

        proposal.options[_optionIndex].votes++;
        proposal.hasVoted[msg.sender] = true;

        emit VoteCast(_proposalId, msg.sender, _optionIndex);
    }

    function getProposal(
        uint256 _proposalId
    )
        external
        view
        returns (
            string memory name,
            uint256 deadline,
            Option[] memory options,
            bool exists
        )
    {
        Proposal storage proposal = proposals[_proposalId];
        return (
            proposal.name,
            proposal.deadline,
            proposal.options,
            proposal.exists
        );
    }

    function hasVoted(
        uint256 _proposalId,
        address _voter
    ) external view returns (bool) {
        return proposals[_proposalId].hasVoted[_voter];
    }
}
```

In this contract, the owner can create a proposal with anywhere from 2-4 possible voting options. Users can then vote on this proposal by the deadline, at which point the proposal can be closed, and the result revealed.

Unfortunately, the contract is completely public, and the vote tallies can be observed during the voting period. We will update this contract to interact with CoFHE, and use FHE encrypted variables to store the votes until the result is revealed.

We will pay special attention to the following updates that need to be made:

1. **Constant time computation**
2. **Handling the `if/else` case**
3. **Handling the `require` case**
4. **Revealing the result with the decrypt-with-proof pattern**

## Migrating the Contract

### Step 1: Import `FHE.sol`

The first thing that we need to do is import `FHE.sol` from the `cofhe-contracts` repo. This package allows smart contracts to start using FHE operations and encrypted variables.

```solidity theme={null}
pragma solidity ^0.8.25;

import {FHE, euint64, InEuint64} from "@fhenixprotocol/cofhe-contracts/FHE.sol";

contract VotingExample {
```

### Step 2: Encrypt the vote counters with `euint64`

The amount of votes cast for each option needs to be encrypted, so we can switch the variable type from a `uint64` to an `euint64` (`euint64` is included in the `FHE.sol` import). The vote counts will be represented by a `ctHash` which acts as a handle and pointer to the encrypted number of votes.

```solidity theme={null}
struct Option {
    string name;
    euint64 votes;  // Changed from uint64
}
```

### Step 3: Initialize the `euint64` vote counts

Now that the votes type has changed, it must be initialized by performing a `trivialEncrypt` of the starting value:

```solidity theme={null}
function createProposal(
    string memory _name,
    string[] memory _options,
    uint256 _deadline
) external onlyOwner returns (uint256) {
    if (_options.length < 2 || _options.length > 4)
        revert InvalidOptionCount();

    uint256 proposalId = proposalCount++;
    Proposal storage proposal = proposals[proposalId];

    proposal.name = _name;
    proposal.deadline = _deadline;
    proposal.exists = true;

    for (uint i = 0; i < _options.length; i++) {
        proposal.options.push(
            Option({name: _options[i], votes: FHE.asEuint64(0)})
        );
    }

    emit ProposalCreated(proposalId, _name, _deadline);
    return proposalId;
}
```

This will work, but it may make sense to prepare the trivially encrypted values in the constructor rather than in each transaction to save on gas and the number of FHE operations being executed. Let's see how that would look:

```solidity theme={null}
contract VotingExample {
    ...

    euint64 private EUINT64_ZERO;
    euint64 private EUINT64_ONE;

    constructor() {
        owner = msg.sender;
        EUINT64_ZERO = FHE.asEuint64(0);
        EUINT64_ONE = FHE.asEuint64(1);
    }

    function createProposal(
        string memory _name,
        string[] memory _options,
        uint256 _deadline
    ) external onlyOwner returns (uint256) {
        ...

        for (uint i = 0; i < _options.length; i++) {
            proposal.options.push(
                Option({name: _options[i], votes: EUINT64_ZERO})
            );
        }

       ...
    }
}
```

### Step 4: Handle user votes with `InEuint8`

We now need to handle the user's vote casting. The first thing that we need to do is hide which option the user is voting for. We can do this by replacing the `vote` function parameter `uint256 _optionIndex` with `InEuint8 memory _optionIndex`. `InEuint8` is an encrypted input type. We then need to convert the `InEuint8` to an `euint8` for use in computation.

<Note>
  Encrypting inputs requires the use of the [**Client SDK**](/client-sdk/introduction/overview) (`@cofhe/sdk`).

  Read more about [**encrypted inputs**](/client-sdk/guides/encrypting-inputs).
</Note>

```solidity theme={null}
function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external {
    euint8 optionIndex = FHE.asEuint8(_optionIndex);
```

### Step 5: Constant time computation

In order to preserve the confidentiality of the user's vote, we must make sure that we aren't leaking any information about the user's choice. If we only updated the voting option that the user has selected, then a user's vote could be deduced by simply watching which vote counter changes. Therefore, we must update *all* the vote counters to hide the user's true vote:

```solidity theme={null}
function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external {
    euint8 optionIndex = FHE.asEuint8(_optionIndex);
    Proposal storage proposal = proposals[_proposalId];

    if (!proposal.exists) revert ProposalNotFound();
    if (block.timestamp >= proposal.deadline) revert DeadlineExpired();
    if (proposal.hasVoted[msg.sender]) revert AlreadyVoted();

    for (uint8 i = 0; i < proposal.options.length; i++) {
        proposal.options[i].votes = FHE.add(
            proposal.options[i].votes,
            FHE.select(
                optionIndex.eq(FHE.asEuint8(i)),
                EUINT64_ONE,
                EUINT64_ZERO
            )
        );
    }
    proposal.hasVoted[msg.sender] = true;

    emit VoteCast(_proposalId, msg.sender, optionIndex);
}
```

Let's break down how this works:

* We iterate through each of the proposal options
* We *always* perform an `FHE.add` on every options' vote count, this means that every options' vote count will change any time a user votes
* We only want to increment the user's selected choice, so we use `FHE.select`
* The API for select is `FHE.select(conditional, ifTrue, ifFalse)`
* Without the FHE syntax, the logic is as follows:

```solidity theme={null}
proposal.options[i].votes =
    proposal.options[i].votes + (_optionIndex == i ? 1 : 0);
```

### Step 6: Handling `if/else`

Branching based on encrypted variables is not allowed, so there is one more change to make to the `vote` function, which is to remove the **`if/else`** branch that relies on `_optionIndex`.

<Warning>
  **`require`** statements are also not allowed when working with encrypted variables for the same reason as above.
</Warning>

```solidity theme={null}
// Remove this check - it cannot work with encrypted variables
// if (_optionIndex >= proposal.options.length)
//     revert InvalidOptionIndex();
```

<Note>
  It is important to *never* use an encrypted variable as part of an if/else branch, since the encrypted variable is always truthy. Instead, use `FHE.select` to replace the value with 0.
</Note>

### Step 7: Update `VoteCast` event

Currently the `VoteCast` event emits a `uint256 optionIndex`. Now that the user's vote is an encrypted variable (`euint8`), we need to update the event to emit the user's encrypted input:

```solidity theme={null}
event VoteCast(
    uint256 indexed proposalId,
    address indexed voter,
    euint8 optionIndex  // Changed from uint256
);
```

And its invocation:

```solidity theme={null}
emit VoteCast(_proposalId, msg.sender, optionIndex);
```

This event will be emitted with the encrypted `euint8 optionIndex`. This is important as in the future the FHE block explorer will be able to decrypt these variables and show the true event log, but only if we make one more change.

### Step 8: Granting access with `FHE.allow`

By default, access and computation on an encrypted variable is blocked, and trying to use a variable before access has been granted will cause AccessControlList.sol (ACL) to revert with an `ACLNotAllowed` error. Access is granted using `FHE.allow` (and its variants `FHE.allowSender`, `FHE.allowThis`, and `FHE.allowPublic`). After access is granted, the encrypted variable may be used in a computation, or decrypted by any of the authorized users or contracts.

**`FHE.allow` variants:**

* `FHE.allow(ctHash, address)` - grants access to `address`. `ctHash` is any encrypted variable like `euint64` or `ebool`
* `FHE.allowSender(ctHash)` - grants access to `msg.sender`
* `FHE.allowThis(ctHash)` - grants access to the executing contract (`address(this)`)
* `FHE.allowPublic(ctHash)` - grants access to everyone, useful for things like an encrypted totalSupply variable, which everyone should have access to

```solidity theme={null}
function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external {
    euint8 optionIndex = FHE.asEuint8(_optionIndex);
    Proposal storage proposal = proposals[_proposalId];

    if (!proposal.exists) revert ProposalNotFound();
    if (block.timestamp >= proposal.deadline) revert DeadlineExpired();
    if (proposal.hasVoted[msg.sender]) revert AlreadyVoted();

    for (uint8 i = 0; i < proposal.options.length; i++) {
        proposal.options[i].votes = FHE.add(
            proposal.options[i].votes,
            FHE.select(
                optionIndex.eq(FHE.asEuint8(i)),
                EUINT64_ONE,
                EUINT64_ZERO
            )
        );
        // Grant this contract access to each vote count
        // Without this, FHE.add(options[i].votes, ...) would revert
        FHE.allowThis(proposal.options[i].votes);
    }
    proposal.hasVoted[msg.sender] = true;

    // Grant msg.sender access to their votingIndex
    // Without this, the msg.sender would not be able to see their vote in the explorer (coming soon)
    FHE.allowSender(optionIndex);
    emit VoteCast(_proposalId, msg.sender, optionIndex);
}
```

<Warning>
  It is critical to ensure that `FHE.allowThis` is used on encrypted variables that need to be used later in the contract's lifecycle. Contracts must have access to variables in order to perform FHE operations on those variables.
</Warning>

### Step 9: Finalize the voting with `FHE.allowPublic`

Because the votes are encrypted for the lifetime of the proposal, after the proposal has ended, we need to decrypt the results and reveal the winner. Let's start by adding a `finalizeVote` function that marks the vote counts as publicly decryptable:

```solidity theme={null}
function finalizeVote(uint256 _proposalId) external {
    if (msg.sender != owner) revert NotOwner();

    Proposal storage proposal = proposals[_proposalId];
    if (!proposal.exists) revert ProposalNotFound();
    if (block.timestamp < proposal.deadline) revert DeadlineNotReached();

    for (uint8 i = 0; i < proposal.options.length; i++) {
        FHE.allowPublic(proposal.options[i].votes);
    }
}
```

`FHE.allowPublic` marks each vote count as eligible for public decryption. Anyone can now request the plaintext values off-chain.

### Step 10: Reveal the results with `FHE.publishDecryptResult`

After `finalizeVote` is called, the vote counts need to be decrypted off-chain and published on-chain with proof. We add a `revealResults` function that accepts the decrypted values and their Threshold Network signatures:

```solidity theme={null}
function revealResults(
    uint256 _proposalId,
    uint64[] calldata _decryptedVotes,
    bytes[] calldata _signatures
) external {
    Proposal storage proposal = proposals[_proposalId];
    if (!proposal.exists) revert ProposalNotFound();
    require(
        _decryptedVotes.length == proposal.options.length,
        "Mismatched lengths"
    );

    for (uint8 i = 0; i < proposal.options.length; i++) {
        FHE.publishDecryptResult(
            proposal.options[i].votes,
            _decryptedVotes[i],
            _signatures[i]
        );
    }
}
```

The client-side flow looks like this:

```typescript theme={null}
// 1. Finalize the vote (owner only)
await voting.finalizeVote(proposalId);

// 2. Decrypt each option's vote count off-chain
const proposal = await voting.getProposal(proposalId);
const decryptedVotes = [];
const signatures = [];

for (const option of proposal.options) {
    const result = await client
        .decryptForTx(option.votes)
        .withoutPermit()
        .execute();
    decryptedVotes.push(result.decryptedValue);
    signatures.push(result.signature);
}

// 3. Publish all results on-chain
await voting.revealResults(proposalId, decryptedVotes, signatures);
```

### Step 11: Checking the results with `FHE.getDecryptResultSafe`

Finally, we can update our `getProposal` function to check the final state of the proposal. Once the results have been published, `FHE.getDecryptResultSafe` will return the plaintext values:

```solidity theme={null}
function getProposal(
    uint256 _proposalId
)
    external
    view
    returns (
        string memory name,
        uint256 deadline,
        bool exists,
        string[] memory options,
        uint256[] memory votes,
        bool finalized,
        uint8 winner
    )
{
    Proposal storage proposal = proposals[_proposalId];

    // Plaintext values can be assigned directly
    name = proposal.name;
    deadline = proposal.deadline;
    exists = proposal.exists;

    options = new string[](proposal.options.length);
    for (uint8 i = 0; i < proposal.options.length; i++) {
        options[i] = proposal.options[i].name;
    }

    // Fetch the decrypted results with `FHE.getDecryptResultSafe`.
    // If any of the results have not yet been decrypted, set `finalized` to false.
    // Store the decrypted result counts in the `votes` list to be returned.
    votes = new uint256[](proposal.options.length);
    finalized = true;
    for (uint8 i = 0; i < proposal.options.length; i++) {
        (uint256 result, bool decrypted) = FHE.getDecryptResultSafe(
            proposal.options[i].votes
        );
        votes[i] = decrypted ? result : 0;
        if (!decrypted) finalized = false;
    }

    // If all votes have been decrypted, determine the winning option.
    if (finalized) {
        uint256 maxVotes = 0;
        winner = 0;
        for (uint8 i = 0; i < proposal.options.length; i++) {
            if (votes[i] > maxVotes) {
                maxVotes = votes[i];
                winner = i;
            }
        }
    }
}
```

In this block you can see a few changes. The first is that we have split `options` and `votes` from the `getProposal` return type, which allows us to better handle the decryption results.

We use `FHE.getDecryptResultSafe` to fetch the decryption result of each of the vote counts, which returns the result as well as a flag indicating whether the decryption has posted.

Once all the decryptions have posted, the `finalized` flag will update to be `true`, and the `winner` determined based on the vote counts.

## Conclusions

In this tutorial, we walked through migrating a simple voting contract to use CoFHE. The key changes were:

1. Changing the vote counts from plain `uint64` to encrypted values using `euint64`
2. Modifying the voting function to use encrypted addition instead of plain addition
3. Using `FHE.allowPublic` to mark values as decryptable after the voting deadline
4. Adding a `revealResults` function that publishes decrypted values on-chain with `FHE.publishDecryptResult`
5. Updating the getter function to handle decryption of results safely

The resulting contract provides the same functionality as the original, but with the added privacy benefit that individual votes are not visible on-chain until the final tally is decrypted. This demonstrates how CoFHE can be used to add privacy to existing contracts with minimal changes to the core logic.

## Final `FHEVotingExample.sol`

```solidity theme={null}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.25;

import {FHE, euint64, InEuint8} from "@fhenixprotocol/cofhe-contracts/FHE.sol";

contract FHEVotingExample {
    struct Option {
        string name;
        euint64 votes;
    }

    struct Proposal {
        string name;
        uint256 deadline;
        Option[] options;
        mapping(address => bool) hasVoted;
        bool exists;
        uint8 winner;
    }

    address public owner;
    uint256 public proposalCount;
    mapping(uint256 => Proposal) public proposals;

    euint64 private EUINT64_ZERO;
    euint64 private EUINT64_ONE;

    event ProposalCreated(
        uint256 indexed proposalId,
        string name,
        uint256 deadline
    );
    event VoteCast(
        uint256 indexed proposalId,
        address indexed voter,
        euint8 optionIndex
    );

    error NotOwner();
    error InvalidOptionCount();
    error ProposalNotFound();
    error DeadlineExpired();
    error AlreadyVoted();
    error InvalidOptionIndex();
    error DeadlineNotReached();

    constructor() {
        owner = msg.sender;
        EUINT64_ZERO = FHE.asEuint64(0);
        EUINT64_ONE = FHE.asEuint64(1);
    }

    modifier onlyOwner() {
        if (msg.sender != owner) revert NotOwner();
        _;
    }

    function createProposal(
        string memory _name,
        string[] memory _options,
        uint256 _deadline
    ) external onlyOwner returns (uint256) {
        if (_options.length < 2 || _options.length > 4)
            revert InvalidOptionCount();

        uint256 proposalId = proposalCount++;
        Proposal storage proposal = proposals[proposalId];

        proposal.name = _name;
        proposal.deadline = _deadline;
        proposal.exists = true;

        for (uint i = 0; i < _options.length; i++) {
            proposal.options.push(
                Option({name: _options[i], votes: EUINT64_ZERO})
            );
        }

        emit ProposalCreated(proposalId, _name, _deadline);
        return proposalId;
    }

    function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external {
        euint8 optionIndex = FHE.asEuint8(_optionIndex);
        Proposal storage proposal = proposals[_proposalId];

        if (!proposal.exists) revert ProposalNotFound();
        if (block.timestamp >= proposal.deadline) revert DeadlineExpired();
        if (proposal.hasVoted[msg.sender]) revert AlreadyVoted();

        for (uint8 i = 0; i < proposal.options.length; i++) {
            proposal.options[i].votes = FHE.add(
                proposal.options[i].votes,
                FHE.select(
                    optionIndex.eq(FHE.asEuint8(i)),
                    EUINT64_ONE,
                    EUINT64_ZERO
                )
            );
            FHE.allowThis(proposal.options[i].votes);
        }
        proposal.hasVoted[msg.sender] = true;

        FHE.allowSender(optionIndex);
        emit VoteCast(_proposalId, msg.sender, optionIndex);
    }

    function finalizeVote(uint256 _proposalId) external {
        if (msg.sender != owner) revert NotOwner();

        Proposal storage proposal = proposals[_proposalId];
        if (!proposal.exists) revert ProposalNotFound();
        if (block.timestamp < proposal.deadline) revert DeadlineNotReached();

        for (uint8 i = 0; i < proposal.options.length; i++) {
            FHE.allowPublic(proposal.options[i].votes);
        }
    }

    function revealResults(
        uint256 _proposalId,
        uint64[] calldata _decryptedVotes,
        bytes[] calldata _signatures
    ) external {
        Proposal storage proposal = proposals[_proposalId];
        if (!proposal.exists) revert ProposalNotFound();
        require(
            _decryptedVotes.length == proposal.options.length,
            "Mismatched lengths"
        );

        for (uint8 i = 0; i < proposal.options.length; i++) {
            FHE.publishDecryptResult(
                proposal.options[i].votes,
                _decryptedVotes[i],
                _signatures[i]
            );
        }
    }

    function getProposal(
        uint256 _proposalId
    )
        external
        view
        returns (
            string memory name,
            uint256 deadline,
            bool exists,
            string[] memory options,
            uint256[] memory votes,
            bool finalized,
            uint8 winner
        )
    {
        Proposal storage proposal = proposals[_proposalId];

        name = proposal.name;
        deadline = proposal.deadline;
        exists = proposal.exists;

        options = new string[](proposal.options.length);
        for (uint8 i = 0; i < proposal.options.length; i++) {
            options[i] = proposal.options[i].name;
        }

        votes = new uint256[](proposal.options.length);
        finalized = true;
        for (uint8 i = 0; i < proposal.options.length; i++) {
            (uint256 result, bool decrypted) = FHE.getDecryptResultSafe(
                proposal.options[i].votes
            );
            votes[i] = decrypted ? result : 0;
            if (!decrypted) finalized = false;
        }

        if (finalized) {
            uint256 maxVotes = 0;
            winner = 0;
            for (uint8 i = 0; i < proposal.options.length; i++) {
                if (votes[i] > maxVotes) {
                    maxVotes = votes[i];
                    winner = i;
                }
            }
        }
    }

    function hasVoted(
        uint256 _proposalId,
        address _voter
    ) external view returns (bool) {
        return proposals[_proposalId].hasVoted[_voter];
    }
}
```

## Next Steps

* Learn about [Migrating from FHE.decrypt](/tutorials/migrating-from-fhe-decrypt) to the new decryption flow
* Explore [ACL Usage Examples](/tutorials/acl-usage-examples) for complex access control patterns
* Review [Best Practices](/fhe-library/introduction/best-practices) for security considerations
