Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/solidity-guides/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@
- [Encrypted inputs](inputs.md)
- [Access Control List](acl/README.md)
- [ACL examples](acl/acl_examples.md)
- [User decryption delegation](acl/delegation.md)
- [Reorgs handling](acl/reorgs_handling.md)
- [Logics](logics/README.md)
- [Branching](logics/conditions.md)
- [Dealing with branches and conditions](logics/loop.md)
- [Error handling](logics/error_handling.md)
- [Decryption](decryption/oracle.md)
- [FHEVM API reference](functions.md)

## Development Guide

Expand Down
12 changes: 12 additions & 0 deletions docs/solidity-guides/acl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ To check if an entity has permission to access a ciphertext, use functions like
- **`isSenderAllowed`**: Simplifies checks for the current transaction sender.
- **`isPubliclyDecryptable`**: Verifies whether any entity is permitted to retrieve the ciphertext's cleartext value off-chain.
- **`checkSignatures`**: Verifies the authenticity of a cleartext value by checking cryptographic signatures. This ensures that the value submitted back to the chain originated from a legitimate public decryption operation on the associated ciphertext handle.
- **`isAccountDenied`**: Checks whether an account is on the deny list, preventing it from interacting with encrypted values.

### User decryption delegation

The ACL supports delegating user decryption rights to another address (for example, a backend service or relayer). This enables workflows where a user authorizes a third party to decrypt values on their behalf for specific contracts.

- **`delegateUserDecryption`**: Grants a delegate the right to decrypt values on behalf of the caller for a specific contract, with an expiration date.
- **`delegateUserDecryptionWithoutExpiration`**: Same as above, but without an expiration date.
- **`revokeUserDecryptionDelegation`**: Revokes a previously granted delegation.
- **`isUserDecryptable`**: Checks if a handle can be decrypted by a user in the context of a specific contract.

For complete function signatures and examples, see the [FHEVM API reference](../functions.md#user-decryption-delegation).

## Practical uses of the ACL

Expand Down
117 changes: 117 additions & 0 deletions docs/solidity-guides/acl/delegation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# User decryption delegation

This document explains how to delegate user decryption rights in FHEVM. Delegation allows a user to authorize another address (such as a backend service, relayer, or smart account) to decrypt encrypted values on their behalf for specific contracts.

## Why use delegation?

In many dApp architectures, the end user does not directly interact with the decryption process. Instead, a backend service or relayer handles it. Delegation enables this pattern securely:

- A user grants a delegate the right to decrypt their values for a specific contract.
- The delegate can then perform user decryptions without the user being online.
- Delegations can be time-limited or indefinite.
- Users can revoke delegations at any time.

## Granting delegation

### Single contract delegation

```solidity
// Delegate with an expiration date (Unix timestamp)
FHE.delegateUserDecryption(delegate, contractAddress, expirationDate);

// Delegate without expiration
FHE.delegateUserDecryptionWithoutExpiration(delegate, contractAddress);
```

**Parameters**

| Parameter | Type | Description |
| ----------------- | --------- | --------------------------------------------------------------------------- |
| `delegate` | `address` | The address receiving decryption rights. Cannot be the contract address. |
| `contractAddress` | `address` | The contract whose ciphertexts the delegate can decrypt on the user's behalf. |
| `expirationDate` | `uint64` | Unix timestamp after which the delegation expires. |

### Batch delegation

To delegate decryption rights across multiple contracts in a single call:

```solidity
address[] memory contracts = new address[](2);
contracts[0] = address(contractA);
contracts[1] = address(contractB);

// With expiration
FHE.delegateUserDecryptions(delegate, contracts, expirationDate);

// Without expiration
FHE.delegateUserDecryptionsWithoutExpiration(delegate, contracts);
```

## Revoking delegation

```solidity
// Revoke for a single contract
FHE.revokeUserDecryptionDelegation(delegate, contractAddress);

// Revoke for multiple contracts
FHE.revokeUserDecryptionDelegations(delegate, contractAddresses);
```

## Checking delegation status

### Check if a handle is user-decryptable

```solidity
bool canDecrypt = FHE.isUserDecryptable(handle, user, contractAddress);
```

Returns `true` if both the `user` and the `contractAddress` have persistent ACL permission on the handle. Returns `false` if `user == contractAddress`.

### Check if delegation is active

```solidity
bool isActive = FHE.isDelegatedForUserDecryption(delegator, delegate, contractAddress, handle);
```

Returns `true` if `delegate` has an active (non-expired) delegation from `delegator` for the given handle and contract.

### Get delegation expiration date

```solidity
uint64 expiration = FHE.getDelegatedUserDecryptionExpirationDate(delegator, delegate, contractAddress);
```

Returns the expiration timestamp. Returns `0` if no delegation exists.

## Example: delegating to a backend relayer

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

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

contract ConfidentialVault is ZamaEthereumConfig {
mapping(address => euint64) private balances;

function deposit(externalEuint64 encryptedAmount, bytes calldata inputProof) external {
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
balances[msg.sender] = FHE.add(balances[msg.sender], amount);
FHE.allowThis(balances[msg.sender]);
FHE.allow(balances[msg.sender], msg.sender);
}

/// @notice Allow a relayer to decrypt your balance on your behalf
function authorizeRelayer(address relayer, uint64 expiration) external {
FHE.delegateUserDecryption(relayer, address(this), expiration);
}

/// @notice Revoke a relayer's decryption rights
function revokeRelayer(address relayer) external {
FHE.revokeUserDecryptionDelegation(relayer, address(this));
}
}
```

In this example, a user can authorize a relayer service to decrypt their vault balance. The relayer can then fetch the decrypted balance on behalf of the user without the user needing to be online. The delegation expires at the specified timestamp, and the user can revoke access at any time.
4 changes: 2 additions & 2 deletions docs/solidity-guides/configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ By inheriting these configuration contracts, you ensure seamless initialization

## ZamaConfig.sol

The `ZamaConfig` library exposes functions to retrieve FHEVM configuration structs and contract addresses for supported networks (currently only the Sepolia testnet).
The `ZamaConfig` library exposes functions to retrieve FHEVM configuration structs and contract addresses for supported networks: Ethereum mainnet, Sepolia testnet, and local Hardhat environments.

Under the hood, this library encapsulates the network-specific addresses of Zama's FHEVM infrastructure into a single struct (`FHEVMConfigStruct`).
Under the hood, this library encapsulates the network-specific addresses of Zama's FHEVM infrastructure into a single struct (`CoprocessorConfig`).

## ZamaEthereumConfig

Expand Down
26 changes: 22 additions & 4 deletions docs/solidity-guides/contract_addresses.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
## Table of all addresses
## Ethereum mainnet

These are Sepolia addresses.
| Contract/Service | Address |
| -------------------------- | ------------------------------------------ |
| ACL_CONTRACT | 0xcA2E8f1F656CD25C01F05d0b243Ab1ecd4a8ffb6 |
| FHEVM_EXECUTOR_CONTRACT | 0xD82385dADa1ae3E969447f20A3164F6213100e75 |
| KMS_VERIFIER_CONTRACT | 0x77627828a55156b04Ac0DC0eb30467f1a552BB03 |

## Sepolia testnet

| Contract/Service | Address/Value |
| -------------------------- | ------------------------------------------ |
| FHEVM_EXECUTOR_CONTRACT | 0x92C920834Ec8941d2C77D188936E1f7A6f49c127 |
| ACL_CONTRACT | 0xf0Ffdc93b7E186bC2f8CB3dAA75D86d1930A433D |
| HCU_LIMIT_CONTRACT | 0xa10998783c8CF88D886Bc30307e631D6686F0A22 |
| FHEVM_EXECUTOR_CONTRACT | 0x92C920834Ec8941d2C77D188936E1f7A6f49c127 |
| KMS_VERIFIER_CONTRACT | 0xbE0E383937d564D7FF0BC3b46c51f0bF8d5C311A |
| HCU_LIMIT_CONTRACT | 0xa10998783c8CF88D886Bc30307e631D6686F0A22 |
| INPUT_VERIFIER_CONTRACT | 0xBBC1fFCdc7C316aAAd72E807D9b0272BE8F84DA0 |
| DECRYPTION_ADDRESS | 0x5D8BD78e2ea6bbE41f26dFe9fdaEAa349e077478 |
| INPUT_VERIFICATION_ADDRESS | 0x483b9dE06E4E4C7D35CCf5837A1668487406D955 |
| RELAYER_URL | `https://relayer.testnet.zama.org` |
| GATEWAY_CHAIN_ID | 10901 |

## Local / Hardhat (chain ID 31337)

| Contract/Service | Address |
| -------------------------- | ------------------------------------------ |
| ACL_CONTRACT | 0x50157CFfD6bBFA2DECe204a89ec419c23ef5755D |
| FHEVM_EXECUTOR_CONTRACT | 0xe3a9105a3a932253A70F126eb1E3b589C643dD24 |
| KMS_VERIFIER_CONTRACT | 0x901F8942346f7AB3a01F6D7613119Bca447Bb030 |

{% hint style="info" %}
You do not need to configure these addresses manually. Inheriting from `ZamaEthereumConfig` automatically resolves the correct addresses based on the current `block.chainid`.
{% endhint %}
157 changes: 152 additions & 5 deletions docs/solidity-guides/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,18 @@ The `asEbool` functions behave similarly to the `asEuint` functions, but for enc
### Configuration

```solidity
function setFHEVM(FHEVMConfig.FHEVMConfigStruct memory fhevmConfig) internal
function setCoprocessor(CoprocessorConfig memory coprocessorConfig) internal
```

Sets the FHEVM configuration for encrypted operations.
Sets the FHEVM coprocessor configuration for encrypted operations. The `CoprocessorConfig` struct contains the addresses of the ACL, Coprocessor (FHEVMExecutor), and KMSVerifier contracts. In most cases, you do not need to call this directly — inherit from `ZamaEthereumConfig` instead, which calls this automatically based on the current chain ID.

### Initialization Checks

```solidity
function isInitialized(T v) internal pure returns (bool)
```

Returns true if the encrypted value is initialized, false otherwise. Supported for all encrypted types (T can be ebool, euintX, eaddress, ebytesX).
Returns true if the encrypted value is initialized, false otherwise. Supported for all encrypted types (T can be ebool, euintX, eaddress).

### Arithmetic operations

Expand Down Expand Up @@ -264,7 +264,7 @@ function gt(euint16 a, uint32 b) internal view returns (ebool)
function select(ebool control, T a, T b) internal returns (T)
```

If control is true, returns a, otherwise returns b. Available for ebool, eaddress, and ebytes\* types.
If control is true, returns a, otherwise returns b. Available for all encrypted types (ebool, euintX, eaddress).

This operator takes three inputs. The first input `b` is of type `ebool` and the two others of type `euintX`. If `b` is an encryption of `true`, the first integer parameter is returned. Otherwise, the second integer parameter is returned.

Expand Down Expand Up @@ -389,7 +389,154 @@ function finalize() public {
}
```

## Additional Notes
## Public decryption functions

These functions support the three-step public decryption workflow. For a complete tutorial, see [Public decryption](decryption/oracle.md).

### Make publicly decryptable

```solidity
function makePubliclyDecryptable(T value) internal returns (T)
```

Marks an encrypted value as publicly decryptable. Once called, any entity can request the off-chain decryption of this value via the relayer SDK. Supported for all encrypted types (T can be ebool, euintX, eaddress). The calling contract must have ACL permission to access the handle.

### Check if publicly decryptable

```solidity
function isPubliclyDecryptable(T value) internal view returns (bool)
```

Returns true if the encrypted value has been marked as publicly decryptable. Supported for all encrypted types.

### Verify decryption signatures

```solidity
function checkSignatures(
bytes32[] memory handlesList,
bytes memory abiEncodedCleartexts,
bytes memory decryptionProof
) internal
```

Verifies that the cleartext values submitted on-chain match the authentic decryption results from the KMS. Reverts if:

- The `decryptionProof` is empty or has invalid length
- The number of valid signatures is below the KMS signers threshold
- Any signature is from a non-registered KMS signer

Emits a `PublicDecryptionVerified(handlesList, abiEncodedCleartexts)` event on success.

{% hint style="warning" %}
The order of handles in `handlesList` must match the order used when calling `publicDecrypt` off-chain. A proof computed for `[handleA, handleB]` is different from a proof computed for `[handleB, handleA]`.
{% endhint %}

### Validate decryption result (view)

```solidity
function isPublicDecryptionResultValid(
bytes32[] memory handlesList,
bytes memory abiEncodedCleartexts,
bytes memory decryptionProof
) internal view returns (bool)
```

A `view` variant of `checkSignatures`. Returns `true` if the KMS signatures are valid, `false` otherwise (or reverts on malformed input). Unlike `checkSignatures`, this function does not emit events or cache results.

{% hint style="info" %}
Prefer `checkSignatures` over this function in most cases. `checkSignatures` provides better safety (replay protection via events), is optimized for gas via caching, and is the standard approach for on-chain verification. Use `isPublicDecryptionResultValid` only when you need a read-only validation check (for example, in off-chain simulations).
{% endhint %}

### Convert to bytes32

```solidity
function toBytes32(T value) internal pure returns (bytes32)
```

Converts an encrypted type handle to its underlying `bytes32` representation. Supported for all encrypted types (ebool, euintX, eaddress). This is required when building the `handlesList` array for `checkSignatures`.

**Example**

```solidity
bytes32[] memory handles = new bytes32[](2);
handles[0] = FHE.toBytes32(encryptedFoo);
handles[1] = FHE.toBytes32(encryptedBar);
```

## User decryption delegation

These functions allow users to delegate their decryption rights to another address (for example, a backend service or a relayer) for specific contracts.

### Delegate user decryption

```solidity
function delegateUserDecryption(address delegate, address contractAddress, uint64 expirationDate) internal
function delegateUserDecryptionWithoutExpiration(address delegate, address contractAddress) internal
```

Delegates the caller's user decryption rights to `delegate` for ciphertexts associated with `contractAddress`. The delegation can have an expiration date or be indefinite.

- The `delegate` cannot be the contract address itself.
- The `delegate` cannot be `address(this)`.

### Batch delegate user decryption

```solidity
function delegateUserDecryptions(
address delegate,
address[] memory contractAddresses,
uint64 expirationDate
) internal

function delegateUserDecryptionsWithoutExpiration(
address delegate,
address[] memory contractAddresses
) internal
```

Delegates user decryption rights across multiple contracts in a single call.

### Revoke user decryption delegation

```solidity
function revokeUserDecryptionDelegation(address delegate, address contractAddress) internal
function revokeUserDecryptionDelegations(address delegate, address[] memory contractAddresses) internal
```

Revokes previously granted decryption delegation for one or more contracts.

### Query delegation status

```solidity
function isDelegatedForUserDecryption(
address delegator,
address delegate,
address contractAddress,
bytes32 handle
) internal view returns (bool)

function getDelegatedUserDecryptionExpirationDate(
address delegator,
address delegate,
address contractAddress
) internal view returns (uint64)

function isUserDecryptable(bytes32 handle, address user, address contractAddress) internal view returns (bool)
```

- **`isDelegatedForUserDecryption`**: Checks if `delegate` has active decryption delegation from `delegator` for a specific handle and contract.
- **`getDelegatedUserDecryptionExpirationDate`**: Returns the expiration timestamp of a delegation. Returns `0` if no delegation exists.
- **`isUserDecryptable`**: Checks if a handle can be decrypted by `user` in the context of `contractAddress`. Returns `true` only if both the user and the contract have persistent ACL permission on the handle.

## Account deny list

```solidity
function isAccountDenied(address account) internal view returns (bool)
```

Returns whether the given account is on the deny list. Denied accounts cannot interact with encrypted values.

## Additional notes

- **Underlying implementation**:\
All encrypted operations and access control functionalities are performed through the underlying `Impl` library.
Expand Down
2 changes: 2 additions & 0 deletions docs/solidity-guides/getting-started/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,7 @@ fhevm enforces access control with a blockchain-based Access Control List (ACL):
- **Transient access**: `FHE.allowTransient` provides temporary access for specific transactions.
- **Validation**: `FHE.isSenderAllowed` ensures that only authorized entities can interact with ciphertexts.
- **Persistent public decryption**: `FHE.makePubliclyDecryptable`, `FHE.isPubliclyDecryptable` makes a given ciphertext permanently publicly decryptable.
- **User decryption delegation**: `FHE.delegateUserDecryption` allows users to delegate decryption rights to another address.
- **Account deny list**: `FHE.isAccountDenied` checks whether an account is blocked from interacting with encrypted values.

For more information see [ACL](../acl/README.md).
Loading
Loading