Abstract
This ERC defines a standard for encrypted data in Ethereum smart contracts. It introduces typed handles for encrypted values represented as bytes32, external ciphertext types with validation proofs, and a standardized storage layout for protocol contracts. The specification includes interfaces for access control, coprocessor-assisted operations, and key management verification, enabling confidential computation and interoperability across contracts and toolchains.
Motivation
Many decentralized applications require operations on confidential data, but Ethereum currently lacks a standardized way to represent and process encrypted values. As a result, developers often resort to ad hoc solutions that reduce composability and increase implementation risk.
This standard introduces a unified abstraction for encrypted data that can be used consistently across contracts and toolchains. By defining common types and storage layouts, it enables applications to interoperate while preserving confidentiality.
Typical use-cases include:
- Private token transfers, where amounts remain hidden from public observers.
- Sealed-bid auctions, in which bids are committed privately and revealed only when finalized.
- Anonymous voting, allowing governance participants to cast verifiable but confidential votes.
- Cross-chain confidential state, where encrypted handles can be shared between chains without disclosing values.
- Games and quizzes, where secret inputs or answers must be committed without being exposed prematurely.
Standardizing these mechanisms allows developers to build privacy-preserving applications while maintaining Ethereum’s core guarantees of composability, verifiability, and auditability.
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
Definitions
- Cleartext: Unencrypted data in a native Solidity type.
- Ciphertext: An encryption of the cleartext.
- Protocol contracts: A set of contracts that enable interaction with an offchain service, define access to handles, or initiate decryption requests.
- Handle: A
bytes32on-chain or off-chain reference to a ciphertext, used in contracts and UIs to avoid storing large ciphertexts directly. The actual ciphertext MAY not be stored on-chain, for e.g. in the context of cross-chain messaging. - Encrypted Data Type: A handle type alias used for an encrypted value.
- Solidity Library: On-chain utility library that exposes encryption, decryption, operations and access control functions.
Core Encrypted Data Types
The library SHOULD define aliases for encrypted data types:
type eaddress is bytes32;
type ebool is bytes32;
type eint8 is bytes32;
type eint16 is bytes32;
type eint24 is bytes32;
type eint32 is bytes32;
type eint40 is bytes32;
type eint48 is bytes32;
type eint56 is bytes32;
type eint64 is bytes32;
type eint72 is bytes32;
type eint80 is bytes32;
type eint88 is bytes32;
type eint96 is bytes32;
type eint104 is bytes32;
type eint112 is bytes32;
type eint120 is bytes32;
type eint128 is bytes32;
type eint136 is bytes32;
type eint144 is bytes32;
type eint152 is bytes32;
type eint160 is bytes32;
type eint168 is bytes32;
type eint176 is bytes32;
type eint184 is bytes32;
type eint192 is bytes32;
type eint200 is bytes32;
type eint208 is bytes32;
type eint216 is bytes32;
type eint224 is bytes32;
type eint232 is bytes32;
type eint240 is bytes32;
type eint248 is bytes32;
type eint256 is bytes32;
type euint8 is bytes32;
type euint16 is bytes32;
type euint24 is bytes32;
type euint32 is bytes32;
type euint40 is bytes32;
type euint48 is bytes32;
type euint56 is bytes32;
type euint64 is bytes32;
type euint72 is bytes32;
type euint80 is bytes32;
type euint88 is bytes32;
type euint96 is bytes32;
type euint104 is bytes32;
type euint112 is bytes32;
type euint120 is bytes32;
type euint128 is bytes32;
type euint136 is bytes32;
type euint144 is bytes32;
type euint152 is bytes32;
type euint160 is bytes32;
type euint168 is bytes32;
type euint176 is bytes32;
type euint184 is bytes32;
type euint192 is bytes32;
type euint200 is bytes32;
type euint208 is bytes32;
type euint216 is bytes32;
type euint224 is bytes32;
type euint232 is bytes32;
type euint240 is bytes32;
type euint248 is bytes32;
type euint256 is bytes32;External Encrypted Input Types and Validation
In addition to the core encrypted data types, this standard defines external encrypted data types to represent ciphertexts generated off-chain. These types are:
type externalEbool is bytes32;
type externalEaddress is bytes32;
// External Encrypted Unsigned Integers
type externalEuint8 is bytes32;
type externalEuint16 is bytes32;
type externalEuint24 is bytes32;
type externalEuint32 is bytes32;
type externalEuint40 is bytes32;
type externalEuint48 is bytes32;
type externalEuint56 is bytes32;
type externalEuint64 is bytes32;
type externalEuint72 is bytes32;
type externalEuint80 is bytes32;
type externalEuint88 is bytes32;
type externalEuint96 is bytes32;
type externalEuint104 is bytes32;
type externalEuint112 is bytes32;
type externalEuint120 is bytes32;
type externalEuint128 is bytes32;
type externalEuint136 is bytes32;
type externalEuint144 is bytes32;
type externalEuint152 is bytes32;
type externalEuint160 is bytes32;
type externalEuint168 is bytes32;
type externalEuint176 is bytes32;
type externalEuint184 is bytes32;
type externalEuint192 is bytes32;
type externalEuint200 is bytes32;
type externalEuint208 is bytes32;
type externalEuint216 is bytes32;
type externalEuint224 is bytes32;
type externalEuint232 is bytes32;
type externalEuint240 is bytes32;
type externalEuint248 is bytes32;
type externalEuint256 is bytes32;
// External Encrypted Signed Integers
type externalEint8 is bytes32;
type externalEint16 is bytes32;
type externalEint24 is bytes32;
type externalEint32 is bytes32;
type externalEint40 is bytes32;
type externalEint48 is bytes32;
type externalEint56 is bytes32;
type externalEint64 is bytes32;
type externalEint72 is bytes32;
type externalEint80 is bytes32;
type externalEint88 is bytes32;
type externalEint96 is bytes32;
type externalEint104 is bytes32;
type externalEint112 is bytes32;
type externalEint120 is bytes32;
type externalEint128 is bytes32;
type externalEint136 is bytes32;
type externalEint144 is bytes32;
type externalEint152 is bytes32;
type externalEint160 is bytes32;
type externalEint168 is bytes32;
type externalEint176 is bytes32;
type externalEint184 is bytes32;
type externalEint192 is bytes32;
type externalEint200 is bytes32;
type externalEint208 is bytes32;
type externalEint216 is bytes32;
type externalEint224 is bytes32;
type externalEint232 is bytes32;
type externalEint240 is bytes32;
type externalEint248 is bytes32;
type externalEint256 is bytes32;Each of these is an alias for bytes32, used to encode a reference to an externally generated ciphertext.
Purpose of External Encrypted Types
These externalE* types are introduced to:
- Distinguish unverified off-chain generated ciphertexts from verified or computed on-chain ciphertexts
- Enable validation and type-safe usage in smart contracts
- Provide extensibility and composability in multi-chain or hybrid compute models
Contracts
Protocol contracts MUST include at least these 4 contracts.
ACL (Access Control List)
The ACL Contract is a trusted host contract on the host chain responsible for managing access rights to encrypted data handles. It defines which addresses (users or contracts) can use, decrypt, or grant access to encrypted values.
Coprocessor
The Coprocessor contract is responsible for validating encrypted inputs and executing computations.
KMS Verifier
This contract is responsible for managing and verifying the decryption received. This ensures trustless decryption handling without the need to trust an oracle or user.
Input Representation and Proof Verification
Values of type externalE* reference ciphertexts generated off-chain. To ensure their integrity and origin, a proof MUST be provided along with the value when submitted to a smart contract.
This proof could be a signature, a zero-knowledge proof, or any other verification metadata appropriate for the application.
The coprocessor contract MUST provide a function for validating these values:
function verifyInput(
bytes32 inputHandle,
address userAddress,
bytes memory inputProof
) public virtual returns (bytes32 result);- input: A
bytes32value cast to an appropriateexternalE*type (e.g.,externalEbool, etc.). - proof: Bundled verification metadata.
- returns: A validated encrypted value cast to its encrypted data type (e.g., euint32, ebool, etc.).
This function MUST verify the authenticity and correctness of the external value using the supplied proof. The verification process MUST ensure that the encrypted value cannot be reused in a different context. If the validation succeeds, the function MUST return a value of a core encrypted data type, enabling safe usage within the contract. Otherwise, the transaction MUST revert.
Note: The type of the input parameter and the return value MUST be aligned with the expected data type (e.g., externalEuint64 to euint64, externalEbool to ebool, etc.).
This design allows smart contracts to securely accept and process encrypted inputs created outside the chain while preserving type safety and auditability.
Standardized Storage Layout
Implementations MUST adhere to a standardized storage layout for all encrypted-related components. This ensures consistent slot usage across different libraries and guarantees compatibility between contracts compiled with different backends.
Specifically:
-
Storage slots MUST be deterministic and stable across implementations.
-
Core contracts such as
ACLorCoprocessoraddresses MUST be stored at predefined slots or under a standardized namespacing convention:keccak256(abi.encode(uint256(keccak256("confidential.storage.config")) - 1)) & ~bytes32(uint256(0xff))- The on-chain contracts are stored with this struct:
struct CoprocessorConfig { address ACLAddress; address CoprocessorAddress; address KMSVerifierAddress; } -
Any contract extending shared standards (e.g., ConfidentialToken) MUST rely on this layout to retrieve and store encrypted metadata and associated access control configurations.
-
dApp contracts SHOULD expose a function to determine which protocol is used.
function confidentialProtocolId() external returns(uint256 confidentialProtocolId);This layout requirement ensures that protocols remain interoperable, even if the underlying implementations differ.
Operations on Encrypted Data
Smart contracts that integrate this standard MAY support homomorphic operations over encrypted values without requiring prior decryption. These operations enable computation on encrypted data directly, preserving confidentiality while allowing contracts to enforce logic.
The Coprocessor contract SHOULD expose functions for basic arithmetic and logical operations on encrypted data types. These functions MUST operate on the encrypted data types defined in this standard and return new encrypted values.
Supported Operations
The following operations MAY be provided for compatible encrypted types:
Addition
function add(bytes32 a, bytes32 b) external returns (euint
result);Subtraction
function sub(bytes32 a, bytes32 b) external returns (euint
result);Multiplication
function mul(bytes32 a, bytes32 b) external returns (euint
result);Equality Comparison
function eq(bytes32 a, bytes32 b) external returns (ebool
result);Conditional Selection
function select(bytes32 condition, bytes32 thenValue, bytes
elseValue) external returns (euint32 result);Implementations MAY choose to extend this set with additional operations (e.g., modular arithmetic, bitwise logic) supported by the underlying encryption scheme.
Type Safety and Interoperability
All operations MUST preserve type safety by accepting and returning specific encrypted data types. Mixing types (e.g., euint32 with euint64) MUST be explicitly cast via well-defined conversion functions if such behavior is desired. This avoids ambiguity and enforces consistency across contracts and toolchains.
Constraints and Considerations
- Operations MUST NOT leak information about the encrypted operands or results.
- Operations SHOULD be implemented in a way that is compatible with circuit-based or homomorphic evaluation strategies.
- Implementers MAY introduce operation limits (e.g., maximum supported depth or operand size) depending on performance or cryptographic constraints.
This module allows developers to perform meaningful computation on private data while maintaining the composability and auditability guarantees of Ethereum smart contracts.
On-Chain ACL for Handle-Based Permissions
The ACL contract MAY provide functions to allow a bytes32 value for a given address, and to check
whether such a value is allowed.
function allow(bytes32 handle, address entity) external;
function isAllowed(bytes32 handle, address entity) external view
returns (bool);- allow: Grants access for a given address to a specific handle.
- isAllowed: Returns true if the address has access to the given handle.
The ACL MAY include account delegation:
function delegateAccount(address delegatee, address[] memory contractAddresses, uint expiration) external;
function revokeDelegation(address delegatee, address[] memory contractAddresses) external;
function allowedOnBehalf(address delegatee,bytes32 handle,address contractAddress,address account) external view returns (bool);- delegateAccount: Grants delegation rights to delegatee, allowing them to act on behalf of the sender for the specified contract addresses with an expiration date (timestamp). This delegation MAY apply to all handles associated with the sender within those contracts.
- revokeDelegation: Revokes previously granted delegation rights from delegatee for the specified contract addresses.
- allowedOnBehalf: Returns true if delegatee is authorized to act on behalf of account for the given handle and contract address; otherwise returns false.
Smart contracts can integrate this module to perform permission checks before granting access to sensitive operations.
JavaScript API
To support encrypted data workflows in frontend applications, a reference JavaScript library provides a standardized interface for constructing encrypted inputs, validating decryption access, and sending decryption requests—either directly or via delegation, based on cryptographic proofs using EIP-712.
Encrypted Input Creation
const input = instance.createInputBuilder({
contractAddress: "0xYourContractAddress",
userAddress: "0xUserAddress",
});
input.pushEuint8(33).pushEbool(true);
const { handles, inputProof } = input.encrypt();createInputBuilder(metadata: Record<string, any>): { handles: Uint8Array[],
inputProof: Uint8Array }- Initializes a new encrypted input context.
- Accepts arbitrary metadata (e.g., contract address, user address, session data, etc.).
- Returns a builder that allows the developer to append encrypted fields and call
.encrypt()to finalize..pushEuint8(value: number): Input,.pushEbool(value: boolean): Input, ...: Appends values to the encrypted input. Returns the input.encrypt(): Promise<{ handles: Uint8Array[], inputProof: Uint8Array }>: Finalizes encryption of the appended inputs. Returns encryptedexternalE*handles ready for submission to smart contracts or validation routines.
User Decryption
To decrypt encrypted data client-side, the user must sign a decryption authorization using EIP-712. The userDecrypt method accepts encrypted handles, a key pair, and the signed proof to perform decryption locally.
const decryptedValues = await instance.userDecrypt(
handleContractPairs, // Array of HandleContractPair
privateKey, // User’s decryption key (e.g., libsodium)
publicKey, // User’s encryption key (must match EIP-712)
signature, // Signature over EIP-712 message
contractAddresses, // Contracts the user is authorizing
userAddress, // Ethereum address of the user
startTimestamp, // Start time for access
durationDays, // Duration (in days) the access is valid
);userDecrypt(
handleContractPairs: HandleContractPair[],
privateKey: string,
publicKey: string,
signature: string,
contractAddresses: string[],
userAddress: string,
startTimestamp: string | number,
durationDays: string | number,
delegatorAddress?: string
): Promise<any[]>- handleContractPairs: Array of
{ handle: string; contractAddress: string }referencing encrypted inputs. - privateKey: The secret key used to decrypt (e.g., libsodium format).
- publicKey: Corresponding public key, included in the signed message.
- signature: EIP-712 signature proving that the public key is authorized by the user.
- contractAddresses: Contracts for which the user grants decryption rights.
- userAddress: Ethereum address of the authorizing user.
- startTimestamp: When the decryption access begins.
- durationDays: Number of days access is valid from startTimestamp.
- delegatorAddress: Address of the user on whose behalf the decryption is being performed (optional).
EIP-712 Message Format (Standard Decryption)
{
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" }
],
"UserDecryptRequestVerification": [
{ "name": "publicKey", "type": "bytes" },
{ "name": "contractAddresses", "type": "address[]" },
{ "name": "contractsChainId", "type": "uint256" },
{ "name": "startTimestamp", "type": "uint256" },
{ "name": "durationDays", "type": "uint256" }
]
},
"primaryType": "UserDecryptRequestVerification",
"domain": {
"name": "Decryption",
"version": "1",
"chainId": 1,
"verifyingContract": "0x1C5630F86A1Aa6fE1F7C625f52A2503DfAaC7922"
},
"message": {
"publicKey": "0xd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
"contractAddresses": [
"0xCb28e13094714cE75Af8D8A3fD81bDdFb8B03fA1",
"0x4De6A93aBC8C90D35eB18477f9C2cF029Aa4Eaa2"
],
"contractsChainId": 1,
"startTimestamp": 1753775700000,
"durationDays": 365
}
}EIP-712 Message Format (Delegated Decryption)
{
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" }
],
"DelegatedUserDecryptRequestVerification": [
{ "name": "publicKey", "type": "bytes" },
{ "name": "contractAddresses", "type": "address[]" },
{ "name": "delegatorAddress", "type": "address" },
{ "name": "contractsChainId", "type": "uint256" },
{ "name": "startTimestamp", "type": "uint256" },
{ "name": "durationDays", "type": "uint256" }
]
},
"primaryType": "DelegatedUserDecryptRequestVerification",
"domain": {
"name": "Decryption",
"version": "1",
"chainId": 1,
"verifyingContract": "0x1C5630F86A1Aa6fE1F7C625f52A2503DfAaC7922"
},
"message": {
"publicKey": "0xd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
"contractAddresses": [
"0xA4c3D920f7e1B93C46E6e9E1D3C15F9B2f1E0c9a",
"0x9F1D7a56AeC0eD3F3c92b8E9e42D8bC45Ea8F2B4",
"0xB89aE101cF0073e03f2B1D1248F7b63F6C1A003e",
"0x6eD9350b7BcC3FeF46B9AD1f45E212A1a2Ae15F0"
],
"delegatorAddress": "0x71721075e4Cc9389aA46b10d705A98fEFdCd3A4C",
"contractsChainId": 1,
"startTimestamp": 1753775700000,
"durationDays": 365
}
}This format allows a user to delegate decryption rights to another agent (e.g., relayer, backend service) while keeping full auditability and expiration control.
Public Decryption
When confidentiality is not required, encrypted values can be decrypted without signature or identity verification. This is useful in test environments, open-data scenarios, or where user-specific access control is not enforced.
const result = instance.publicDecrypt([encryptedData1, encryptedData2]);publicDecrypt(handles: (Uint8Array | string)[]): any[]- handles: An array of encrypted data, each as a Uint8Array or base64/hex-encoded string.
- Returns: An array of decrypted cleartext values, in the same order as provided.
Encryption Scheme
- MUST use a standardized encryption scheme.
- MAY support arithmetic operations including addition and conditional logic (e.g., FHE/TFHE).
- SHOULD support efficient re-encryption and key switching.
Key Management
- MUST provide secure key generation and distribution.
- SHOULD support key rotation and recovery.
- MUST protect against key compromise.
- MAY distribute decryption key shares across a decentralized protocol.
Rationale
Architecture
This standard specifies a smart contract layer designed to operate on encrypted data using handles.
Design Decisions
- Use of Handles: Avoids storing large ciphertexts on-chain, separates contract logic from access control, and supports cross-chain references.
- Deterministic Input Verification: Ensures that encryption proofs are verifiable.
Security Considerations
- Input Verification: Prevent unauthorized reuse of ciphertexts without knowledge of cleartexts.
- Ciphertext Immutability: Prevent ciphertext tampering or replay.
- Proof Binding: Prevent attackers from reusing encrypted inputs in a different context.
Reference Implementation
fhevm Protocol using encrypted types, based on FHE encrypted-types Solidity encrypted types @zama-fhe/relayer-sdk JavaScript library
Copyright
Copyright and related rights waived via CC0.
