TLDW logo

Deep dive into ERC-7984 with @OpenZeppelin and Zaïffer @ The Zama CoFHE Shop

By Zama

Summary

## Key takeaways - **Encrypted Types Use Off-Chain Handles**: Encrypted types are pointers (handles) to off-chain encrypted values, not the values themselves on-chain. This requires special FHE library operations, which are asynchronous. [02:25], [02:40] - **FHE SafeMath Returns Encrypted Boolean**: Unlike Solidity SafeMath that reverts on overflow, FHE version returns an encrypted boolean success flag since operations are asynchronous and can't revert. Condition on this flag using special FHE branching to handle failures like transferring zero on overflow. [03:37], [04:27] - **Transient vs Permanent FHE Allowances**: Grant contracts permanent access to handles or transient allowances limited to the transaction execution. Always verify sender has rights to manipulate the encrypted type to prevent attacks. [06:22], [07:02] - **ERC7984 Confidential Transfer & Call**: Confidential transfers encrypt amounts with plaintext sender/receiver for compliance; transferAndCall grants transient permissions allowing the recipient contract to operate on the value via onReceivedTransfer hook. Return encrypted boolean on success/failure to enable refunds without revealing info. [11:50], [13:27] - **Use uint64 Not uint256 in FHE**: uint256 is too expensive in FHE; use uint64 by reducing decimals from 18 to 6, which is sufficiently large for most token values. [10:28], [11:02] - **New Pointers Need New Allowances**: Every update like a token transfer creates a new handle/pointer, requiring fresh allowances since prior ones don't apply. Failing this leaves recipients unable to use transferred tokens. [22:00], [22:11]

Topics Covered

  • Handles Pointer Offchain Encrypted Values
  • Async Overflows Need Encrypted SafeMath
  • FHE Allowances Enable Cross-Contract Access
  • Use uint64 Not uint256 for FHE Efficiency
  • ERC7984 Confidential Transfers Require Callbacks

Full Transcript

[music] Hello. Uh, I'm Eric from Open Zeppelin.

Hello. Uh, I'm Eric from Open Zeppelin.

I'm one of the security auditors there, blockchain security researcher, and I've gone over the Zamma contracts pretty extensively.

>> Um, hi, I'm Thomas. Uh, I work at Zypher. Uh I I handle all of the uh

Zypher. Uh I I handle all of the uh uh backend and um and smart contract development and uh hopefully you'll uh

you'll enjoy the talk. Okay. Um,

so here, uh, what we'll go about, uh, real quick is, um, since we're going to be discussing confidential tokens, um, I'll I'll be detailing from a developers

perspective, uh, the sort of primitives that you you'll be manipulating as a smart contract developer on uh, on the ZMA ecosystem. Uh and so that includes

ZMA ecosystem. Uh and so that includes all of the encrypted entities that you might manipulate and also uh how it actually works under the hood. Uh so

keep in mind I'm not a ZMA core developer obviously. So the the whole

developer obviously. So the the whole goal is that you understand these um uh primitives from a uh the smart contract

developers perspective and not from a FHE uh mathematician I guess. Um

do you want to say a word?

>> Oh yeah. Uh, and then after he explains that, uh, I'll be going into some of the Open Zeppelin contracts that are built on the Zama stack. I'll be talking about uh ERC7984

which is the, um, confidential token standard, some of our extensions, and then some secure design tips so that you don't get wrecked.

>> Um, okay. So, what's a what's an encrypted type? um they're uh well

encrypted type? um they're uh well encrypted versions of uh your your favorite solidity types. So you'll have in solidity obviously unsigned in

addresses etc. And so what you can do uh using FHE uh FHVM is have the encrypted

version of these types um sorry what you have to watch out for in these types is that they're uh you'll often see this in the FHVM jargon. um the the word

handles. So you don't actually have the

handles. So you don't actually have the encrypted value of your uh of your var variable onchain. What you have is some

variable onchain. What you have is some sort of pointer that points to uh an off-chain I guess database with all of the encrypted values to your variables.

Um and so this is very important to understand because it has uh a lot of implications when you develop your smart contracts. So yeah, for instance, all of

contracts. So yeah, for instance, all of these new types will have their uh their specific um uh operations. So when you have regular U ends in solidity uh and

you can just use the plus operator here, you have to use to go through some special uh FHE library that allows you to um to to to operate on your encrypted

types. Um and yeah, obviously as I said,

types. Um and yeah, obviously as I said, all the operations are asynchronous since the values are not themselves onchain. Um so there's something that

onchain. Um so there's something that you need to to watch out for when you're manipulating these encrypted types uh is everything that's um you know overflows

and and whatnot. Um again since the operations are asynchronous um overflows are also somewhat asynchronous. That

means that you cannot when you when you use open zeppelin safe map for instance on uh on regular solidity types. um you

know if you have overflows you'll have a a reversion on your on your smart contract and that gives you a bunch of uh uh um of security

uh paradigms that you can implement and ensure that uh you know your contracts will revert whenever you have some sort of problem in uh FHVM types that's not

the case uh it's a bit harder to do uh and so open Zeppelin has implemented an other library that that that works essentially the same way but that's

specifically designed for these new types. Um

types. Um so instead of instead of reverting uh they'll have to return some sort of type and they'll return whether the addition

or the subtraction was uh indeed uh successful or not and this it returns as an encrypted type itself. Um so here for instance I've got a small example where

you know if I use safe math uh try increase and it overflows I'll get a reversion in solidity in FHE it'll actually return me with an um an

encrypted boolean that here I've called e success and then what I can do is actually uh condition on this on this value and um and either um you know here

for instance I'm increasing the total supply if my encrypted total supply overflow close uh I'll have a falsy encrypted uh success um uh result and in the transfer amount I'll actually

transfer zero because here I have to transfer zero since I cannot um uh first revert at this point and second I don't actually know the value uh the the value

of this addition I don't actually know it so I have to go through and I'm using this special branching mechanism from FHE uh where instead of using just a regular

if else construct like I would in regular solidity I have to use this this sort of uh function that builds a symbolic execution tree of my of my

calculation um so with encrypted types you also have uh the concept of ACL who's allowed to manipulate your encrypted types so this is fairly uh important for instance when

you have an encrypted type a smart contract has some sort of encrypted type and wants to send it to some other contract to operate on it. Uh you know cross contract communication is is

something fairly classic in uh in in solidity and in smart contract development. But here you need to

development. But here you need to actually give access to uh the contract you're sending that value to uh so that they can manipulate the uh the encrypted type. So you have multiple ways to do

type. So you have multiple ways to do this. You can either give it a permanent

this. You can either give it a permanent access to the handle um if you remember the handles you know the the the onchain representation of the encrypted type uh

or you can give it a transient allowance so that it only has a right to operate on it during the execution of the transaction.

Um, you also have a way for a contract to know if whoever uh whatever contract is sending it an encrypted type uh has has

the has the right to send you that encrypted type uh because you know people could just like watch the blockchain retrieve some values and send it to your uh to to your contract and somewhat attack it like this. the best

practice is to verify that the the sender sending you the encrypted type has the right to manipulate that that value. Okay. So now uh that that I've

value. Okay. So now uh that that I've talked about you know permissions and stuff like that. So how do you actually uh implement a function that that operates on encrypted arguments. So

there are two ways uh meaning that you'll often have to implement your your functions in in two separate ways because they'll have two separate signatures. So here the first method is

signatures. So here the first method is uh you pass an encrypted um type to your uh to your smart contract. So smart

contract A has some encrypted value wants to send it to smart contract B. Uh

you'll just send it to to to that function that accepts it as a as an argument. So you'll have the uh FHE

argument. So you'll have the uh FHE allowances and and sender verifications that you'll put into place. Um but you also have a second method um which is

when um you know uh some regular accounts non-s smart contract account wants to call a uh a smart contract you'll need to do some you know special uh initialization

uh and also generate some sort of proof to ensure that that you know what you're you're actually sending. Uh and so that's the the second method. But in

practice, what it means is that you have different arguments when it's an um uh when the encrypted value for your argument was first created offchain or or whether it's something that already

exists on chain and you're passing it from a smart contract to another smart contract.

>> So yeah, again it it's something that's that's that's important to remember this this uh asynchronous um uh execution of your encrypted type values. So here in

the concept in the context of a a token you know a regular ERC20 token you'll just do uh you'll have some balances you'll increase and decrease the balances and the total supplies. Uh here

you'll essentially do the same thing except that all of the calculations are done offchain. So onchain what you'll

done offchain. So onchain what you'll have is a symbolic tree that that builds the computation to be executed. uh and

this will be uh read offchain by the Zama systems by the FHVM systems that will read whatever um uh but that will

reproduce offchain that execution in the exact same order it appeared onchain to ensure that they maintain the same state. Um, and so one of the the the the

state. Um, and so one of the the the the the nice things about this is that your smart contract is not not the execution

is it since the execution is is merely building an a computational graph, it's not slow down. One of the drawbacks obviously is that if you want to then

decrypt an FHE value, you'll need to wait for the ZMA systems to have uh executed the the symbolic computation you define during the execution of your smart contracts.

>> Okay. And so um here we have um we have something that's that that's that's definitely very important and then and that seemed very weird for me uh at the

beginning when uh implementing smart contracts in uh in FHVM.

uh in the encrypted types uh you're in encrypted world and turns out everything's more much more expensive.

So when in solidity manipulating a Uint 256 as you do in an ERC20 for instance is is something that we we we've all been doing forever and we wouldn't even

think about the the consequences of doing so in FHE. Um it's it's it generally considered too expensive or sufficiently expensive that you

shouldn't use it everywhere. Um, and as Eric will explain it, in uh in in the encrypted equivalent of the RC20 uh contracts, we're actually using U in 64.

And so you kind of have this natural mapping where you usually values are that that would be 256 in uh um and so actually when you think about it, UN64

is definitely sufficiently large. uh

you'll just have to um you know for instance in the token context instead of of having your tokens be 18 decimals you just make them six decimals um and it's

usually um way sufficient.

Um all right here you go.

>> Yeah. So uh Open Zeppelin has built off of the Zamma stack and developed the ERC7984 standard. Um it's still in ERC stage so

standard. Um it's still in ERC stage so it could change a little bit but uh we don't think it really will. Uh basically

this is a confidential token. Um so it provides a few functions that I've listed here. This isn't all of the API

listed here. This isn't all of the API but uh there's confidential transfers and confidential transfer froms. These are your ERC20

transfer and transfer from functions except that uh the amount itself is encrypted. So this is an encrypted type.

encrypted. So this is an encrypted type.

Um notably the sender and receiver are in plain text and this is for compliance purposes. Um so that you know you can

purposes. Um so that you know you can prove you're not sending to like bad guys or something onchain. Um but also so that event emissions in block

explorers can be logged. Um still emits like that transfer uh event. Um there's

also confidential transfer you know from and call uh and this is really important because you uh going back to the idea of FHE

allowances which is the ability to see and modify uh an encrypted value. Um,

the contract being called will also need to have uh that allowance and be able to do something with that value uh before you're done with the transfer. If you

don't provide that, um, the contract or EOA that's receiving these tokens basically won't be able to do anything with the uh the amount of tokens you've

transferred them. Um so this allows you

transferred them. Um so this allows you to give them permission and then uh you could even give them transient permission uh so that it's just available for that um transaction

execution and then they can do something with it. So you do a call into that

with it. So you do a call into that contract and it can do some operations on this encrypted value before the transaction is through. Um, so you need

to implement this on confidential transfer received function if you want to make a contract that receives ERC7984

tokens and if there is a failure for whatever reason in your contract uh because we can't revert when we're in FHE land uh

you can return a encrypted boolean that the um ERC7984 token can do something with. And that

boolean just says like whether or not the transfer succeeded and uh so that we don't reveal information um because this is all supposed to be confidential. Uh

if there is a failure, the tokens will be refunded to the sender. So they won't be actually sent. Um but we can't know that uh if we're looking at it in the

plain text uh land. So we just include that uh encrypted boolean and then if it's false that basically means the tokens have actually been refunded to the sender and never made it to the

recipient. Um and then there is the uh

recipient. Um and then there is the uh request and disclose encrypted amount functions. So these are two separate

functions. So these are two separate functions. Um, and basically what they

functions. Um, and basically what they mean is, uh, if you are allowed to see that encrypted value, you can request that the FHEVM

creates a proof of what that value is and then you can disclose it or you take that proof and you put it on chain and you prove to the whole world that this

encrypted value equals this plain text value. And that's really important if

value. And that's really important if you're moving tokens into or out of a confidential token wrapper so that you can basically get them out and say, "Yes, you know, I had 100 tokens and now

I'm unwrapping them into uh a regular ERC20." Uh there's also of course your

ERC20." Uh there's also of course your confidential balance of confidential total supply. Uh these will assume that

total supply. Uh these will assume that whoever's calling them has uh allowance permissions. Um and there is

permissions. Um and there is operator-based approval. So, this is

operator-based approval. So, this is like approve in ERC20 except it doesn't include any amounts. It's just saying that somebody has the ability to modify

your account. Um, and that's by design.

your account. Um, and that's by design.

Again, if we included amounts, that would reveal some information about the current balance of the uh account in question. So, we've also got a couple

question. So, we've also got a couple extensions in the Open Zeppelin confidential contracts library. Go check

it out on GitHub. I don't know if we linked it in here. Sorry. Um but one of the extensions is ERC7984

omnibus. So if you recall uh I mentioned

omnibus. So if you recall uh I mentioned in the last slide that the users are not encrypted. So when you send and receive

encrypted. So when you send and receive tokens those uh addresses are actually visible on chain. This is for compliance purposes. Um this omnibus kind of

purposes. Um this omnibus kind of provides a bit of a workaround. So, uh,

it still transfers tokens to and from, uh, accounts that are visible onchain.

However, there's an encrypted like sender and recipient. I'm not sure exactly how to refer to this, so I'll call it like the meta sender and meta recipient. Basically, um this is

recipient. Basically, um this is designed to say like if I'm transferring to to MA um on behalf of somebody else,

uh I can say, "Hey, just so you know, like earmark these funds for that person, that uh recipient." And the idea is that for example, exchanges could uh

use this. Um so they could transfer

use this. Um so they could transfer funds between each other. one exchange

transfers to another exchange and this is logged on chain and everyone can see that that transfer happened but they could say like hey you know these funds are coming from this account and going

to this account and then offchain that exchange knows to credit um you know those accounts uh so yeah it's all about compliance

sadly we still have to pay attention to that um but I've just highlighted here the uh encrypted sender this key and the encrypted recipient. But as you can see

encrypted recipient. But as you can see down here, um it really just is a regular confidential transfer from and

call uh using the uh plain text omnibus from and omnibus to addresses.

There's also the ERC20 wrapper. Um so

this is basically a way to convert your ERC20 token into an ERC 7984 confidential token. Um it has ERC 1363

confidential token. Um it has ERC 1363 support. Um basically this just means

support. Um basically this just means that it has an on transfer received hook. So if you just send tokens

hook. So if you just send tokens directly to the ERC20 wrapper, it will automatically wrap them into uh ERC 7984

assuming that you are calling this on transfer received hook which opens up when contracts do. Um it wraps amounts to the euent 64 which as we discussed

earlier um that's smaller than the typical UN 256. Uh so it will automatically check the decimals of your ERC20 token and basically downscale

whatever amount you're uh sending in there to uh six decimals so that it fits

in the ER or in the EU 64 nicely. Um and

then there is the uh two-stage unwrapping. So the first call unwrap

unwrapping. So the first call unwrap will burn the confidential tokens that you are unwrapping. And then it will

basically ask the FHBM for a decryption proof. And then once you've got that,

proof. And then once you've got that, you can feed it into the finalize unwrap function. And that will verify that

function. And that will verify that proof. and then it will transfer you

proof. and then it will transfer you your uh decrypted ERC20 amount. And uh

it also has a very important thing which is that all of these decryption requests are one-time use. So when you go to prove uh these proofs that the Zamma

FHVM gives you, um it doesn't really care how many times you've proved it before. So inside the contract we have

before. So inside the contract we have just a little check there that says hey like if this withdrawal has already happened don't allow it to happen again.

And then we have ERC 7984 RWA which is for real world assets. Um it just combines a lot of helpful features for use with real world assets. Um so for

example it is pausible by an admin so you can pause all transfers. Um, of

course, there's minting and burning capabilities also from the admin. Uh,

there's a block list or allow list functionality which allows you to, you know, block or allow certain users uh for KYC compliance purposes. Uh, there

are force transfers. So, if you're an admin and you found that somebody is doing something bad and you want to stop them or you just want to start moving tokens around for some reason, um, you

can do that. And then there are freezable amounts per address and these are confidential. So you can freeze a

are confidential. So you can freeze a confidential amount of somebody's balance and that will uh prevent them from moving it. Um and this just allows you to have greater control and again

compliance. It all centers around

compliance. It all centers around compliance. So we've got some security

compliance. So we've got some security concerns when we're dealing with um FHEVM contracts. So, here are a couple

FHEVM contracts. So, here are a couple that we've started noticing are common.

Um, you need to be careful, of course, with your allowances when you're allowing somebody to manipulate uh an FHVM, you know, encrypted value. Uh,

once you've done that, uh, they have it forever and they can give that access to other people unless you're giving them transient allowance, which is just for

that, uh, specific transaction. So yeah,

keep that in mind. Be very careful who you give allowance to because they can basically see that value forever. Now um

and also uh I believe it's in here somewhere else, but um you need to give allowance every time there's a new pointer created. So for example, if I

pointer created. So for example, if I send my tokens to you, uh my balance will be updated and I'll have a new pointer or handle. Um,

uh, yeah. So, you know, you'll need to have allowance for that, too. Uh, make

sure that you only allow decryption and withdrawals once, unless, you know, you don't want that. Um, don't let people withdraw twice. FHE operations can fail

withdraw twice. FHE operations can fail without reverting. So, don't assume

without reverting. So, don't assume success. Uh, take a look at that success

success. Uh, take a look at that success and do something with it. Um,

yep. Don't expect your sender recipient to be hidden. Uh you're probably going to have to decrypt if you want to integrate with DeFi. There's a little star there because that's an open problem. If you're smart and want to

problem. If you're smart and want to work on this, come talk to us. Uh and

keep in mind that decrypt requests become public.

So yeah, secure design principles. If

there's any values that are entangled, any associated pointers, for example, in an AMM, uh if I am trading token A for token B, uh you're going to want to

update four pointers. That's the AMM's token A and token B balance and my token A and token B balance so that it hides what direction the swap was in. Um and

you know how much I might have got.

Uh you need to consider allowances every time you make a new pointer, which is really often. Um there are a few FHEVM

really often. Um there are a few FHEVM functions which will revert if you pass in an uninitialized pointer like not and neg star because that's being worked on

should be gone in the future. Uh use

token transfer call backs wisely you're giving control over to someone whenever you call back into the contract. Uh so

when you transfer to somebody and you have like an on transfer received hook just you know only give that to contracts that you trust.

uh you should understand privacy limitations. The addresses are visible.

limitations. The addresses are visible.

Um this using decryption invalidators is how you solve the problem of you know one withdraw per decrypt. Um basically

just have a flag that says yeah we've handled that uh set of signatures and there is a maximum amount of FHE operations per transaction. If you

exceed that your transaction will revert uh within the EVM. So, just keep that in mind. I It's not exactly a fixed number.

mind. I It's not exactly a fixed number.

It depends which ones you're doing. Um,

but you can find more about that in the Zama docs.

Thank you.

Loading...

Loading video analysis...