fhEVM is compatible with Remix, but we strongly recommend using our fork of Remix. Here are the steps to deploy your smart contract on Remix:

1) Build your application logic like you normally would in Solidity. Please make sure that you import the TFHE.sol library if you want to access the TFHE functionalities.

pragma solidity ^0.8.20;

import "fhevm/lib/TFHE.sol";

We recommend to use the same solidity version (for example v0.8.24) for the entire project. You can switch to this exact compiler version in the Solidity Compiler tab on the left:

2) Select the Inco Gentry Testnet on your Metamask. If you haven't added this network yet, you can find the details and steps here: Connect Metamask

3) Switch your environment to "Injected Provider - Metamask" from the first dropdown menu, and click on "Deploy". We have a few sample contracts here: Example dApps. Make sure that you have INCO testnet tokens on your wallet address. If not, you can get INCO testnet tokens here: Faucet

Providing encrypted data to smart contract

It's important to note that in order to provide ebool, euint8, euint16, euint32 as function parameters to your smart contract, you have to pass them as bytes to your contract function. You can generate ciphertext bytes using our utils page and use one of the following functions to cast it to an encrypted solidity type:

  • ebool secret = TFHE.asEbool(inSecretBytes)

  • euint8 secret = TFHE.asEuint8(inSecretBytes)

  • euint16 secret = TFHE.asEuint16(inSecretBytes)

  • euint32 secret = TFHE.asEuint32(inSecretBytes)

Our Remix fork allows you to encrypt plaintext automatically, just have a function that takes in "bytes" and use the drop down:

Select a desired type and enter plaintext value, that will automatically get converted into bytes for you.

If you don't use Remix, then the quickest way to generate ciphertexts for testing is to use our tool: Please make sure that your wallet is connected to Inco Network.

Securing your secrets

Before re-encrypting data to an arbitrary, potentially invalid public key, you can guarantee that the caller owns the public key we want to re-encrypt for by letting the caller sign a message with their private key and verify the signature on chain. This can all be done with ease with the use of onlySignedPublicKey() modifier. First, import the following library:

import "fhevm/abstracts/EIP712WithModifier.sol";

Next, make your smart contract implement EIP712WithModifier like this:

contract MyContract is EIP712WithModifier {
   constructor() EIP712WithModifier("Authorization token", "1") {}


Now you can use onlySignedPublicKey() modifier to guard a function that re-encrypts a random value:

function revealSecret(bytes32 publicKey, bytes calldata signature) public view onlySignedPublicKey(publicKey, signature) returns (bytes memory) {
   return TFHE.reencrypt(TFHE.randEuint8(), publicKey);

For as long as function parameters are either called "publicKey", "pubKey" or "pk" for the public key and "signature", "sign" or "s" for the signature, in remix you should now see the following:

Then press "Generate" to generate a signature, it will automatically fill in your public key.

This will prompt a signature request on your selected web3 provider and you should see a confirmation dialog appear either in your Metamask or a hardware wallet.

Last updated