Skip to main content

Overview

Let’s take a look at a simple contract that uses FHE to encrypt a counter, and break it down into its components.

Complete Contract Example

Breaking Down the Contract

Importing the FHE Library

To start using FHE, we need to import the FHE library. In this example, we’re importing the types euint64 and InEuint64 from the FHE library.
We want to keep the counter encrypted at all times, so we’ll use the euint64 type.

State Variables

Next, we define some state variables for the contract:

Constructor Initialization

In the constructor, we initialize the counter and delta variables. We encrypt the delta here to avoid calculating the same encrypted value every time we increment or decrement the counter.
We wanted the example contract to be as simple as possible, so readers can plug-and-play it into their preferred environment.There are some privacy improvements that could be made to this contract.
When we initialize the delta and counter variables, we use trivial encryption.Trivial encryption produces a ciphertext from a public value, but this variable, even though represented as a ciphertext handle, is not really confidential because everyone can see what is the plaintext value that went into it.To make it completely private, we need to initialize these variables with an InEuint from the calldata.More about trivial encryption here.

Access Control

For every encrypted variable, we need to call FHE.allowThis() to allow the contract to access it. Allowing access to encrypted variables is an important concept in FHE-enabled contracts. Without it, the contract could not continue to use this encrypted variable in future transactions. You can read more about this in the ACL Mechanism page.

Increment and Decrement Functions

In the increment_counter and decrement_counter functions, we use the FHE.add and FHE.sub functions to increment and decrement the counter, respectively. And we also call FHE.allowThis() to allow the contract to access the new counter value.

Reset Function

In the reset_counter function, we receive an InEuint64 value, which is a type that represents an encrypted value that can be used to reset the counter. This value is an encrypted value that we created client-side using the SDK (read more about it here).

Decryption: Allow Public and Reveal

Decryption follows a two-step on-chain pattern, with an off-chain step in between. Step 1: Allow public decryption (on-chain) The owner calls allow_counter_publicly to mark the counter as eligible for public decryption:
Step 2: Decrypt off-chain Anyone can now request decryption off-chain using decryptForTx, which returns the plaintext value and a Threshold Network signature:
Step 3: Publish on-chain with proof The decrypted value and signature are submitted on-chain. FHE.publishDecryptResult verifies the signature and stores the plaintext — if the signature is invalid, the transaction reverts:

Reading the Decrypted Value

Once the result has been published, anyone can read the counter’s value using get_counter_value. This function uses FHE.getDecryptResultSafe to check if a published result is available:
If the result has not been published yet, the function reverts. Otherwise, it returns the plaintext value.

Privacy Considerations

In this contract, only the owner can allow public decryption. Once reveal_counter is called, the plaintext value is published on-chain and visible to everyone. What if we want to allow the owner to privately read the value without revealing it publicly? For that, we need to add a call for FHE.allow(counter, owner) or FHE.allowSender(counter) every time that we change the counter’s value. This will allow the owner to read the encrypted counter’s value using the get_encrypted_counter_value function and decrypt it privately off-chain using decryptForView:

Next Steps