Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Welcome to the documentation for fhEVM Solidity Library! This comprehensive guide provides developers with detailed information on the library's functions, parameters, and usage examples. Explore how to leverage TFHE's powerful capabilities for computing over encrypted data within Solidity smart contracts, enabling secure computations and encrypted data manipulation. Unlock a new level of privacy and confidentiality in your blockchain applications with fhEVM Solidity Library.
Our library TFHE requires Solidity version 0.8.19 specifically, as we rely on features exclusive to this version and do not currently provide support for versions beyond it.
Our library compiles seamlessly with the traditional Solidity compiler and is generally compatible with traditional Solidity tools. However, it's important to note that the execution is designed to function exclusively on a fhEVM. As a result, this library is not intended for deployment on a classic EVM, such as Goerli or Ganache.
To get started with fhEVM Solidity Library, you need to install it as a dependency in your JavaScript project. You can do this using npm (Node Package Manager) or Yarn. Open your terminal and navigate to your project's directory, then run one of the following commands:
This will download and install the fhEVM Solidity Library and its dependencies into your project.
The best way to start writing smart contracts with fhEVM is to use our Hardhat template.
It allows you to start a fhEVM docker image and run your smart contract on it. Read the README for more information.
If you need to get coins for a specific wallet, you can use the faucet as follow:
We provide a docker image to spin up a fhEVM node for local development.
You can get 10 Zama token on https://faucet.zama.ai/
📙 White paper | 📁 Github | 💛 Community support | 🟨 Zama Bounty Program
There used to be a dilemma in blockchain: keep your application and user data on-chain, allowing everyone to see it, or keep it privately off-chain and lose contract composability. Thanks to a breakthrough in homomorphic encryption, Zama’s fhEVM makes it possible to run confidential smart contracts on encrypted data, guaranteeing both confidentiality and composability.
fhevmjs
is a javascript library that enables developers to interact with blockchains using Zama's cutting-edge technology based on TFHE (Fully Homomorphic Encryption over the Torus). This library provides a seamless integration of TFHE encryption capabilities into web3 applications, allowing for secure and private interactions with smart contracts.
The TFHE
Solidity library we introduce is a powerful tool that empowers developers to manipulate encrypted data using TFHE within smart contracts. With this library, developers can perform computations over encrypted data, such as addition, multiplication, comparison and more, while maintaining the confidentiality of the underlying information.
Workshop during ETHcc [by Morten Dahl — Zama]
Confidential ERC-20 Tokens Using Homomorphic Encryption [by [Clément Danjou — Zama]
On-chain Blind Auctions Using Homomorphic Encryption [by Clément Danjou — Zama]
Battleship [by Owen Murovec]
Darkpool [by Owen Murovec]
Here are the main steps from the official guide provided by Metamask:
Add these informations to access to blockchain
You can use Remix to interact with a blockchain using fhEVM. If you want to send an encrypted input, you need to encrypt it with fhevmjs CLI tool for example. It becomes more complex if you want to reencrypt a value directly in Remix.
To avoid this problem, we developed a version of Remix IDE with these two missing features:
Encryption of input
Generation of public key and signature for reencryption and decryption.
You can use it on https://remix.zama.ai.
First, read the usage section regarding Solidity version and EVM.
To import TFHE library, simply import it at the top of your contract.
import "fhevm/lib/TFHE.sol";
UPDATE: Remix doesn't take into consideration the package.json of fhevm to fetch dependencies. If you're using fhevm/abstracts/EIP712WithModifier.sol
, it will fetch the latest version of the @openzeppelin/contracts
package, which runs only on the Shanghai EVM (Solidity version ^0.8.20). Since fhEVM is not compatible with versions above 0.8.19, it will fail. To fix that, go to .deps/fhevm/abstracts/EIP712WithModifier.sol
and change the imports as follows:
Be sure to be on the correct network before deploying your contract
````
The TFHE
library provides encrypted integer types and a type system that is checked both at compile time and at run time.
Encrypted integers behave as much as possible as Solidity's integer types. Currently, however, behaviour such as "revert on overflow" is not supported as this would leak some information about the encrypted value. Therefore, arithmetic on e(u)int
types is unchecked, i.e. there is wrap-around on overflow.
Encrypted integers with overflow checking are coming soon to the TFHE
library. They will allow reversal in case of an overflow, but will leak some information about the operands.
In terms of implementation in the fhEVM
, encrypted integers take the form of FHE ciphertexts. The TFHE
library abstracts away that and, instead, exposes ciphertext handles to smart contract developers. The e(u)int
types are wrappers over these handles.
The following encrypted data types are defined:
type | supported |
---|---|
Higher-precision integers are supported in the TFHE-rs
library and can be added as needed to fhEVM
.
NOTE 1: The
ebool
type is currently implemented as aneuint8
. A more optimized native boolean type will replaceeuint8
.
When users send serialized ciphertexts as bytes
to the blockchain, they first need to be converted to the respective encrypted integer type. Conversion verifies if the ciphertext is well-formed and includes proof verification. These steps prevent usage of arbitrary inputs. For example, following functions are provided for ebool
, euint8
, euint16
and euint32
:
TFHE.asEbool(bytes ciphertext)
verifies the provided ciphertext and returns an ebool
TFHE.asEuint8(bytes ciphertext)
verifies the provided ciphertext and returns an euint8
TFHE.asEuint16(bytes ciphertext)
verifies the provided ciphertext and returns an euint16
TFHE.asEuint32(bytes ciphertext)
verifies the provided ciphertext and returns an euint32
... more functions for the respective encrypted integer types
Fields | Value |
---|---|
Fields | Value |
---|---|
Network Name
Zama Network
New RPC URL
https://devnet.zama.ai
Chain ID
8009
Currency symbol
ZAMA
Block explorer URL (Optional)
https://main.explorer.zama.ai
Network Name
Zama Local
New RPC URL
http://localhost:8545/
Chain ID
9000
Currency symbol
ZAMA
Block explorer URL (Optional)
ebool
yes (1)
euint8
yes
euint16
yes
euint32
yes
euint64
no, coming soon
eint8
no, coming soon
eint16
no, coming soon
eint32
no, coming soon
eint64
no, coming soon
We allow explicit decryption requests for any encrypted type. The values are decrypted with the network private key (the threshold decryption protocol is in the works).
The reencrypt functions takes as inputs a ciphertext and a public encryption key (namely, a NaCl box).
During reencryption, the ciphertext is decrypted using the network private key (the threshold decryption protocol is in the works). Then, the decrypted result is encrypted under the user-provided public encryption key. The result of this encryption is sent back to the caller as bytes memory
.
It is also possible to provide a default value to the reencrypt
function. In this case, if the provided ciphertext is not initialized (i.e., if the ciphertext handle is 0
), the function will return an encryption of the provided default value.
NOTE: If one of the following operations is called with an uninitialized ciphertext handle as an operand, this handle will be made to point to a trivial encryption of
0
before the operation is executed.
In the example above (balanceOf
), this view function need to validate the user to prevent anyone to reencrypt any user's balance. To prevent this, the user provides a signature of the given public key. The best way to do it is to use EIP-712 standard. Since this is something very useful, fhEVM library provide an abstract to use in your contract:
When a contract uses EIP712WithModifier
abstract, a modifier is available to check user signature.
This signature can be generated on client side using fhevmjs library.
The result of comparison operations is of type ebool
. Typical boolean operations are not currently supported for this type, because it is encrypted.
That said, there are possibilities to condition on ebool
with or without information leakage.
The first way to have a condition based on an ebool
is to decrypt it and then either use an if statement or pass it to a require
. This solution is the simplest way to do a condition but it will leak information. To illustrate this, let's take an example where a user bids for an item in a blind auction.
In this code, we first evaluate a homomorphic comparison checking that the user has bid more than the highest bid. This homomorphic comparison will return an encryption of 0 if false, or an encryption of 1 if true. Since we are decrypting this value with TFHE.decrypt
, we are leaking information: if the user didn't bid enough tokens, the transaction is reverted. For example, a user can know the value of the highest bid by trying every possible values and finally bid just one token above.
To avoid information leakage, fhEVM provides a method which acts as a ternary operator on encrypted integers. This method is called cmux.
It is important to keep in mind that each time we assign a value using TFHE.cmux
, the value changes, even if the plaintext value remains the same.
The decryption statements described above may lead to important delays during the transaction execution as several of them may need to be processed in a single transaction. Given that those decryptions might be used for control flow by using the Solidity require
function, we introduce optimistic require statements (optReq
). These require statements take as input a value to type ebool
and are accumulated throughout the execution of the transaction. The accumulated boolean value is decrypted via the threshold decryption protocol either when an explicit decryption is executed, or at the very end of a transaction execution. If the decryption returns false
, the transaction is reverted. Otherwise, state changes are persisted as usual. Optimistic requires may be more efficient, but this efficiency comes at the price of paying the full transaction gas cost if one of the boolean predicates is false.
The current devnet has a gas limit of 10,000,000. If you send a transaction exceeding this limit, it won't be executed. Consequently, your wallet won't be able to emit a new transaction. To address this, emit a new transaction with the same nonce but the correct gas limit. In Metamask, you can enforce the use of a specific nonce by enabling the feature in 'Advanced Settings'.
When you include a require statement in a transaction like require(TFHE.decrypt(ebool), "It didn't work");
, the revert message will not be returned if ebool
is false.
When you invoke the gas estimation method, fhEVM may not provide an accurate estimation if your code includes TFHE.decrypt
because it is unable to determine the actual value. In such cases, the gas estimation assumes that TFHE.decrypt()
returns 1
. Depending on your code, this assumption may lead to a deviation from the actual gas usage. To mitigate this, consider adding a 20% buffer to the gas estimation or more. However, be cautious not to exceed 10,000,000 as the upper limit. We've written a method, available in the hardhat template, to tackle this issue. Feel free to use it.
First, to understand the issue, you need to know that a euint32
or a ebool
are uint256
under the hood: they are a 256bits hash of the actual ciphertext. So if you set your properties directly in the contract as follows:
The TFHE.asEuint32(42)
will be executed during compilation to evaluate your property MY_CONSTANT
, because the compiler expect to have an actual value. Since you're not calling the precompiles which would return a trivial encryption of 42
, you get a 0
value.
In the contract, obtaining the bytes of the ciphertext is not possible. As outlined in our whitepaper, you manipulate the hash of the ciphertext in the EVM; the actual ciphertext is only utilized when interacting with the precompiles (TFHE.decrypt
, TFHE.add
, ...).
This error occurs when you haven't yet published any contracts on the blockchain. To resolve this, simply deploy a contract on the blockchain.
You can listen for events using WebSocket on port 8546
of your local Docker image. Alternatively, if you prefer to do it on Zama's devnet, you can use the WebSocket server available at wss://devnet.ws.zama.ai/
.
The functions exposed by the TFHE
Solidity library come in various shapes and sizes in order to facilitate developer experience. For example, most binary operators (e.g., add
) can take as input any combination of the supported data types.
In the fhEVM
, FHE operations are only defined on same-type operands. Implicit upcasting will be done automatically, if necessary.
Most binary operators are also defined with a mix of ciphertext and plaintext operands, under the condition that the size of the plaintext operand is at most the size of the encrypted operand. For example, add(uint8 a, euint8 b)
is defined but add(uint32 a, euint16 b)
is not. Note that these ciphertext-plaintext operations may take less time to compute than ciphertext-ciphertext operations.
asEuint
The asEuint
functions serve three purposes:
verify ciphertext bytes and return a valid handle to the calling smart contract;
cast a euintX
typed ciphertext to a euintY
typed ciphertext, where X != Y
;
trivially encrypt a plaintext value.
The first case is used to process encrypted inputs, e.g. user-provided ciphertexts. Those are generally included in a transaction payload.
The second case is self-explanatory. When X > Y
, the most significant bits are dropped. When X < Y
, the ciphertext is padded to the left with trivial encryptions of 0
.
The third case is used to "encrypt" a public value so that it can be used as a ciphertext. Note that what we call a trivial encryption is not secure in any sense. When trivially encrypting a plaintext value, this value is still visible in the ciphertext bytes. More information about trivial encryption can be found here.
asEbool
The asEbool
functions behave similarly to the asEuint
functions, but for encrypted boolean values.
The reencrypt functions takes as inputs a ciphertext and a public encryption key (namely, a NaCl box).
During reencryption, the ciphertext is decrypted using the network private key (the threshold decryption protocol is in the works). Then, the decrypted result is encrypted under the user-provided public encryption key. The result of this encryption is sent back to the caller as bytes memory
.
It is also possible to provide a default value to the reencrypt
function. In this case, if the provided ciphertext is not initialized (i.e., if the ciphertext handle is 0
), the function will return an encryption of the provided default value.
NOTE: If one of the following operations is called with an uninitialized ciphertext handle as an operand, this handle will be made to point to a trivial encryption of
0
before the operation is executed.
add
, sub
, mul
, div
, rem
)Performs the operation homomorphically.
Note that division/remainder only support plaintext divisors.
AND
, OR
, XOR
)Unlike other binary operations, bitwise operations do not natively accept a mix of ciphertext and plaintext inputs. To ease developer experience, the TFHE
library adds function overloads for these operations. Such overloads implicitely do a trivial encryption before actually calling the operation function, as shown in the examples below.
<<
, >>
)Shifts the bits of the base two representation of a
by b
positions.
eq
, ne
, ge
, gt
, le
, lt
)Note that in the case of ciphertext-plaintext operations, since our backend only accepts plaintext right operands, calling the operation with a plaintext left operand will actually invert the operand order and call the opposite comparison.
The result of comparison operations is an encrypted boolean (ebool
). In the backend, the boolean is represented by an encrypted unsinged integer of bit width 8, but this is abstracted away by the Solidity library.
cmux
)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.
min
, max
Returns the minimum (resp. maximum) of the two given values.
neg
, not
)There are two unary operators: neg
(-
) and not
(!
). Note that since we work with unsigned integers, the result of negation is interpreted as the modular opposite. The not
operator returns the value obtained after flipping all the bits of the operand.
NOTE: More information about the behavior of these operators can be found at the TFHE-rs docs.
Random encrypted integers can be generated fully on-chain.
That can only be done during transactions and not on an eth_call
RPC method, because PRNG state needs to be mutated on-chain during generation.
WARNING: Not for use in production! Currently, integers are generated in the plain via a PRNG whose seed and state are public, with the state being on-chain. An FHE-based PRNG is coming soon, where the seed and state will be encrypted.
name | function name | type | ETA |
---|---|---|---|
NOTE 1: Methods prefixed with
safe
will do an overflow check by decrypting an overflow bit and revert if that bit is true.
NOTE 2: Random encrypted integers that are generated fully on-chain. Currently, implemented as a mockup by using a PRNG in the plain. Not for use in production!
The TFHE
library defines the following operations with FHE ciphertexts:
name | function name | symbol | type |
---|---|---|---|
NOTE 1: Random encrypted integers that are generated fully on-chain. Currently, implemented as a mockup by using a PRNG in the plain. Not for use in production!
Overloaded operators +
, -
, *
, &
, ... on encrypted integers are supported (using for). As of now, overloaded operators will call the versions without an overflow check.
More information about the supported operations can be found in the function specifications page or in the TFHE-rs docs.
If you find yourself in search of a missing feature, we encourage you to consult our roadmap for upcoming developments. Alternatively, don't hesitate to reach out to us on Discord or visit our community forum.
Welcome to the documentation for fhevmjs, a JavaScript library that enables interaction with blockchain using Zama's technology! This comprehensive guide provides developers with detailed information on encryption of data using TFHE (Fully Homomorphic Encryption over the Torus) and generation of EIP-719 tokens for reencrypt data.
To get started with fhevmjs, you need to install it as a dependency in your JavaScript project. You can do this using npm, Yarn or pnpm. Open your terminal and navigate to your project's directory, then run one of the following commands:
This will download and install the fhevmjs library and its dependencies into your project.
by
by
by
To use the library in your project, you need to load WASM of first with initFhevm
.
In the codebase, there is a new URL('tfhe_bg.wasm')
which triggers a resolve by Webpack. If yo u encounter an issue, you can add a fallback for this file by adding a resolve configuration in y our webpack.config.js
:
If you encounter any other issue, you can force import of the browser package.
If you have an issue with bundling the library (for example with some SSR framework), you can use the prebundled version available in fhevmjs/bundle
. Just embed the library with a <script>
tag and you're good to go.
fhevmjs include a Command-Line Interface (CLI) tool. With this handy utility, you can encrypt 8/16/32bits integer with the blockchain's FHE public key. To get started with fhevmjs CLI, first, ensure you have Node.js installed on your system. Next, install the fhevmjs package globally using the '-g' flag, which allows you to access the CLI tool from any directory:
Once installed, fhevm
command should be available. You can get all commands and options available with fhevm help
or fhevm encrypt help
.
Encrypt 71721075 as 32bits integer:
The library provides a set of functions to encrypt integers of various sizes (8, 16, and 32 bits) using the blockchain's public key. These encrypted integers can then be securely used as parameters for smart contract methods within the blockchain ecosystem.
value: number
(required): A number between 0 and 255.
Uint8Array
value: number
(required): A number between 0 and 65535.
Uint8Array
value: number
(required): A number between 0 and 4294967295.
Uint8Array
First, you need to create an instance. An instance allows you to:
Encrypt inputs with blockchain public key
Manage user keys to reencrypt contract's encrypted data
params
(required):
chainId
(required): Id of the chain
publicKey
(required): Public key of the blockchain
keypairs
(optional): A list of keypairs associated with contract
Promise<FhevmInstance>
With a bundler such as Webpack or Rollup, imports will be replaced with the version mentioned in the "browser"
field of the package.json
. If you encounter issue with typing, you can use this using TypeScript 5.
Add w/ overflow check
TFHE.safeAdd
Binary, Decryption
Coming soon (1)
Sub w/ overflow check
TFHE.safeSub
Binary, Decryption
Coming soon (1)
Mul w/ overflow check
TFHE.safeMul
Binary, Decryption
Coming soon (1)
Div
TFHE.div
Binary
-
Rem
TFHE.rem
Binary
-
Random unsigned int
TFHE.randEuintX()
Random
-
Random signed int
TFHE.randEintX()
Random
-
Add
TFHE.add
+
Binary
Sub
TFHE.sub
-
Binary
Mul
TFHE.mul
*
Binary
Div (plaintext divisor)
TFHE.div
Binary
Rem (plaintext divisor)
TFHE.rem
Binary
BitAnd
TFHE.and
&
Binary
BitOr
TFHE.or
|
Binary
BitXor
TFHE.xor
^
Binary
Shift Right
TFHE.shr
Binary
Shift Left
TFHE.shl
Binary
Equal
TFHE.eq
Binary
Not equal
TFHE.ne
Binary
Greater than or equal
TFHE.ge
Binary
Greater than
TFHE.gt
Binary
Less than or equal
TFHE.le
Binary
Less than
TFHE.lt
Binary
Min
TFHE.min
Binary
Max
TFHE.max
Binary
Neg
TFHE.neg
-
Unary
Not
TFHE.not
~
Unary
Cmux
TFHE.cmux
Ternary
Decrypt
TFHE.decrypt()
Decryption
Reencrypt
TFHE.reencrypt()
Reencryption
Optimistic Require
TFHE.optReq()
Decryption
Random unsigned int (mockup)
TFHE.randEuintX()
Random
Workshop during ETHcc by Morten Dahl (Zama)
Confidential ERC-20 Tokens Using Homomorphic Encryption by Clément Danjou (Zama)
On-chain Blind Auctions Using Homomorphic Encryption by Clément Danjou (Zama)
The library provides a convenient function that generates a JSON object based on the EIP-712 standard. This JSON object includes a public key and is specifically designed to facilitate data reencryption in a smart contract environment.
By utilizing this JSON object and having it signed by the user, a secure process of reencrypting data becomes possible within a smart contract. The signed JSON includes the necessary information, including the public key, which allows for seamless reencryption of the data.
options
(required):
verifyingContract: string
(required): The address of the contract
name: string
(optional): The name used in the EIP712
version: string
(optional): The version used in the EIP712
FhevmToken
contractAddress: string
(required): address of the contract
ciphertext: Uint8Array | string
(required): a ciphertext, as a binary or hex string, to decrypt
number
This method allows you to store the signature of a public key for a specific contract, in order to retrieve it later. The signature is also serialized in serializeKeypairs
.
contractAddress: string
(required): address of the contract
signature: string
(required): the signature of the EIP-712 token
This method returns true if contract has a keypair and a signature.
contractAddress: string
(required): address of the contract
boolean
This method returns the saved public key and signature for a specific contract. If the contract has no keypair or no signature, this method returns null
.
contractAddress: string
(required): address of the contract
TokenSignature
or null
This method is useful if you want to store contract keypairs in the user LocalStorage.
ExportedContractKeypairs
: