EncryptedCounter.sol
This contract is included as a reference that is used throughout the SDK documentation to explain core functionality such as encrypting inputs, decrypting for view, and decrypting for transactions.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;
import '@fhenixprotocol/cofhe-contracts/FHE.sol';
contract EncryptedCounter {
address public owner;
euint32 public count;
bool public decrypted;
uint32 public decryptedCount;
constructor(uint32 initialValue) {
owner = msg.sender;
count = FHE.asEuint32(initialValue);
FHE.allowThis(count);
FHE.allowSender(count);
}
error OnlyOwnerAllowed(address caller);
modifier onlyOwner() {
if (msg.sender != owner) revert OnlyOwnerAllowed(msg.sender);
_;
}
function getCount() external view returns (euint32) {
return count;
}
function setCount(InEuint32 memory _inCount) external onlyOwner {
count = FHE.asEuint32(_inCount);
FHE.allowThis(count);
FHE.allowSender(count);
decrypted = false;
decryptedCount = 0;
}
function incrementCount() external onlyOwner {
count = FHE.add(count, FHE.asEuint32(1));
FHE.allowThis(count);
FHE.allowSender(count);
decrypted = false;
decryptedCount = 0;
}
function allowCountPublicly() external onlyOwner {
FHE.allowPublic(count);
}
function revealCount(uint32 _decrypted, bytes memory _signature) external {
FHE.verifyDecryptResult(count, _decrypted, _signature);
decrypted = true;
decryptedCount = _decrypted;
}
}Contract walkthrough
Submit an encrypted value
setCount accepts an encrypted input (InEuint32) and stores it as the new counter value. The caller submits an already-encrypted value — the contract converts it into an FHE ciphertext, updates allowances, and resets any previously revealed plaintext.
function setCount(InEuint32 memory _inCount) external onlyOwner {
count = FHE.asEuint32(_inCount);
FHE.allowThis(count);
FHE.allowSender(count);
decrypted = false;
decryptedCount = 0;
}Retrieve an encrypted value
The counter is stored as an euint32 — an encrypted handle. getValue returns this handle (a bytes32 on-chain), which can then be decrypted off-chain. The count state variable is also public, so it can be read directly.
euint32 public count;
function getCount() external view returns (euint32) {
return count;
}Manipulate an encrypted value
incrementCount performs an FHE addition on the encrypted counter, adding 1 without ever decrypting the value. The result is a new ciphertext that replaces the old one.
function incrementCount() external onlyOwner {
count = FHE.add(count, FHE.asEuint32(1));
FHE.allowThis(count);
FHE.allowSender(count);
decrypted = false;
decryptedCount = 0;
}Reveal an encrypted value
revealCount accepts a plaintext value and a cryptographic signature proving it matches the current encrypted counter. The contract verifies the proof on-chain via FHE.verifyDecryptResult and, if valid, stores the plaintext. allowCountPublicly can be called beforehand to allow anyone to generate the decryption proof (no permit required).
function allowCountPublicly() external onlyOwner {
FHE.allowPublic(count);
} function revealCount(uint32 _decrypted, bytes memory _signature) external {
FHE.verifyDecryptResult(count, _decrypted, _signature);
decrypted = true;
decryptedCount = _decrypted;
}