Skip to main content

Overview

One of the key aspects of writing confidential smart contracts is receiving encrypted inputs from users:
Notice in the example above the distinction between externalEuint32 and euint32.

Input Types Conversion

The input types externalEuintXX (and externalEbool, externalEaddress) represent user input. Each one is a ciphertext handle that travels with a separate bytes proof, and the pair is what lets the contract authenticate the value. For more on that, read about the ZK-Verifier. Before you can use an encrypted input, convert it to a regular encrypted type, passing the proof alongside the handle:
One signature covers every encrypted input in a call, so the proof is shared. It follows the handle it authenticates rather than sitting last in the parameter list.
Avoid storing externalEuintXX values in contract state. They are unverified until you convert them, so storing one keeps a handle the contract has not authenticated. Always convert with FHE.asE...() first.
Now that amount is of type euint32, you can store or manipulate it:
Read more about the available FHE types and operations in the FHE Encrypted Operations guide.

Full Example

Here’s a complete example showing how to handle encrypted inputs in a transfer function:
For the example above to work correctly, you will also need to manage access to the newly created ciphertexts in the _updateBalance() function. Learn more about access control in the ACL Mechanism guide.

Passing encrypted values between contracts

externalEuintXX is for values arriving from a user. A value arriving from another contract uses sharedEuintXX instead. The distinction matters for security. FHE operations check the permission of the contract performing them, not of whoever called it. A function that accepts a bare euintXX from outside can therefore be handed any handle that contract is allowed on, including one read out of its own storage. It can then be made to return something derived from it. sharedEuintXX closes that. The sharer grants access and records itself in the same step, and the receiver checks who handed the value over:
Pick the receive form by how the value reached you: For receiveEuintXXFromCall, callee must be the address called in that same expression. Naming a merely trusted address checks who created the share rather than who handed it over. A share is single-use and lasts one transaction, so it cannot be stored, replayed, or rebuilt from an event. To keep a received value, call FHE.allowThis on the unwrapped euintXX. Expect NotShared when nothing was shared with you, UnexpectedSharer when the share came from someone other than the party you named, and SenderNotAllowed when the sharer does not hold the handle.
The old spelling, an FHE.allowTransient grant plus a bare euintXX parameter, still compiles and still runs. The compiler will not find these for you, so they have to be found by search. See migrating to 0.7 for the full pass.

Additional Examples

Voting in a Poll

Setting Encrypted User Preferences