Multisig Confidential: A Multisig Smart Account Powered by Fully Homomorphic Encryption

EthCC[9] · 31 Mar 2026 · 18:05

Ernesto García presents confidential multisignature modules for ERC-7579 smart accounts. The presentation combines programmable accounts with fully homomorphic encryption to enable private computation on encrypted wallet data.

Speakers

Topics

From EthCC[9], hosted by Ethereum France.

Transcript

All right. Hey everyone, how's it going? Nice to see you all. All good, thank you. All right, so let's get started. Today we're going to talk about project that we have been developing at OpenZeppelin. It is called a multi-sig confidential, but we had this we have this agreement and partnership with Sama. Probably you're already familiar. They They basically provide tooling for doing FHE on Solidity, which is pretty great for confidentiality. So, let's get into it. For today's agenda, we're going to divide this into four main like sections or topics. The first one being an introduction to 7579 as a mechanism to implement functionality in an account.

This is important before the second topic, which is going private, right? What does it mean to be to make a a wallet or an account that is private and how does it interact with applications? For the third topic, we're going to explore the combination of fully homomorphic encryption FHE plus ERC-4337, and finally we're going to discuss, if we have enough time, about what I call input coordination, which is a fundamental problem about communication between signers in a multi-sig when they are interacting with private applications. So, let's jump into it. So, for the introduction, we're going to answer the question how does an ERC-7579 account work and in the context of a

multi-sig right? As I mentioned, 7579 is essentially the way in which you bring functionality to an to an account that is already deployed, right? We think this has the best probably the best chances to become like the standard, especially because there are too many accounts that are already deployed and modularity in runtime is probably the best way to achieve new functionality. So, quick reminder, essentially the way this works is that you have a couple of entities interacting in 4337. There is a bundler that calls the entry point to process user operations, and the user operation will validate the user operation against the uh account itself, right? Once it's

validated, it will return the validation, and it will execute on an application, right? This is a standard 4337 functionality. So, then uh jumping into how a 7579 multi-sig should work, uh considering that the validation phase of 4337 is where we should plug in the validation mechanism. This is essentially uh this, you know, is just a sequence diagram of how it works, right? Couple things to note. Number one is that uh one of the challenges that we run into is that for a multi-sig, you usually need different types of keys, right? We didn't want to create an enum for this. Uh we didn't want to um restrict it to use a single uh signature mechanism. So, this is why we developed

ERC-7913. This allows to register keys in a way that combines a verifier contract with a public key, so you can basically plug in any validation mechanism that you would need. Second important thing is that this should be able to be installed in any 7579 account. So, that's, you know, that's sort of the purpose and and the idea behind the standardization. Okay. So, the problem with this uh basic design of a multi-sig is that number one, the who is controlling the account is kind of important because, you know, someone can just look for you in the street and basically threat you to sign something, and that's a problem, right? So, we want

to keep the people who is signing into the multi-sig private. We also want to keep private how many people do you need? Let's say you have a multi-sig that is a two out of three. You don't want to reveal that there are three signers, and also you don't want to reveal that you need two of them, right? And finally, for what? Because in more uh complex scenarios for governance, I'm not talking about governance in in those, I'm talking about governance in institutions or probably, you know, banks or boards. Probably in those cases, you need to also protect signers from each other, right? You want to keep decisions private and this is the way to do it.

So, the solution to this general problem is just to encrypt both the signers and the threshold and this is the uh let's call it direction that we started with and this is what we this is what we developed essentially. So, let's jump into the next topic, which is going private, which is making the multi-sig actually private, right? So, what does it mean to make it private? It means to encrypt the components that I already mentioned, but for this you need a primer into FHE using SAMA. For this, I would highly recommend this talk. Um you can see the link there and it has a really good introduction. This is by one of the members of the OpenZeppelin team and it basically walks you through how FHE works in Solidity. There are a couple of

considerations to consider. Like for example, the handles are pointing to encrypted data are not necessarily the value itself. So, you can operate with handles, but these are bytes 32 and all of the representations of encrypted data in a contract are essentially handles. Another thing is that handles can be operated without leaking information. What does this mean? Let's say you have two encrypted three encrypted, you can sum them up and you will get five encrypted without revealing that you sum two and three, right? Um third thing is that there is no branching and no reverting. In a traditional multi-sig, for example, you need to revert if someone sign and is is not an authorized signer, right? In this

case, there is no such a thing because reverting actually reveals information and it makes an an oracle so you can query until you get like a correct result, right? And finally is that it requires asynchronous decryption, basically two steps, one in which you request decryption and the other in which you actually decrypt. So, this doesn't really plug well into 4337, but it's one of the challenges that we uh faced and I think we kind of solved. So, jumping into the core design of this, there are basically two data structures here. Number one is a bytes32 set from OpenZeppelin contracts. It's a set of handles. What this is uh encoding or storing is the pointers to the actual

addresses that or signers that can sign into the multi-sig, right? But these are encrypted. And also the threshold, you can see that there is an e uint32. This is for encrypted uint and is also handle, right? So, basically you have these two components and this is enough to construct a multi-sig that is confidential right? Um so, well the thing is FHE does enable operating over encrypted values, but the thing is it doesn't really allow verifying a signature in a private way, right? Because verifying a signature is a set of uh mathematical steps that you would require to translate into FHE. So, the problem is that that is not really

feasible right now, right? Or it would be very expensive, so this is not the route that we took. So, what are we doing instead? The thing is um to support arbitrary signature mechanisms, we decided that the validation uh uses purely FHE but for only accounting the threshold and verifying that a signer is part of the multi-sig, but not for the validation itself, right? So, this is an internal function that we're exposing in the contract. It's for validating uh non-operation and it returns an encrypted boolean, right?

So, the thing is you will need to pass the signing signers in in in plain text along with their corresponding IDs, which essentially reveals who is the signer behind each encrypted ID. But, the thing is that you can also compose it, let's say, in an in a user operation with a reset of the signers. There is an important thing about FHE, which is that the handles generated by Sama are non-deterministic. So, you can re-encrypt the same signer and you will get a different handle. If you reset all the signers and the threshold, you're basically removing all the leaked information from the operation, right?

Um another thing to note is that the signatures are also in plain text. So, you can actually also use them to compare whether the signature is valid for each one of the signers, right? Um this is how validation works. As you can see, there is no branching because we cannot revert. So, the way we do it is that we are accumulating the results of the signers and the validation of each signature into an encrypted Boolean. The Boolean remains encrypted until the account itself decides to make it public and process it. So, for example, you can sign a user operation, nobody knows the content of the user operation, and nobody knows whether the user operation is valid, right? Until you actually execute it.

So, at this point, there are a couple of things to um to note. For example, the threshold is still not not known. You can essentially pass obfuscated um signers into the validation, and it will not be counted towards the threshold, right? Um another thing is that the signature um oh, you don't know if the signature counted, exactly. You also don't know if the signer plus ID pair is valid, so you can also obfuscate the input, and you also don't know if the whole validation succeed, right? The majority of the times you will pass a validation that actually succeeded, so this is not necessarily a secret, but you know, you can use it if you want to obfuscate the output.

Okay. So, now that we have this general idea on F A sheep loss for plus and a smart account. Let's jump into how it works with 4337, right? Because 4337 has its own validation and execution flow and we want to hook into that. So remember this this validation function, right? It returned an evil. So the problem is if I return an evil, how do I ever use it, right? How do I plug it into validation for 4337? And the answer is well, we need to go through this asynchronous process of decryption, right? Number one, we need to request a disclosure for the operation. And then we need to actually disclose the operation. This is basically for any

hash that the account would sign, but in the case of 4337, we are hooking the second step, that actual decryption into the validate user up, right? So the process goes as follows. You first sign the operation with your signers, you you provided as an input, generate the evil then then you request disclosure that is required an external relay here and finally you use process the user operation using the entry point as you would normally do, but instead of providing a signature know that you will provide a signature but for decryption. This is a signature from the summer's KMS that basically lets

the application know that they are or you are allowed to decrypt the the value, right? So the entry point will process this, it will execute and that's essentially it, right? So now remember I mentioned this before, but the the thing with F A sheep is that it generates handles that are not deterministic. So you can encrypt the same signer again and you will get a different handle. So the trick here is you can in a user operation batch a set of operations sub operation, let's call them. And the last operation can reset all the signers, and you can even put the same signers in the same threshold, and no one no one will know right?

Um this is the example that I'm using, and let's jump into the last topic. This is about input coordination, right? Because we already talked about what does it mean to make a multi-sig confidential, but now the multi-sig needs to interact with applications, and the application is not necessarily confidential, right? If the multi-sig transfers to any USDT, everyone will see it, and and that's probably not the best case scenario. So, what about uh interacting with confidential apps? So, we already have our account plus a 75.79 multi-sig that is confidential. This is controlled, let's say, by three people, but they will face an issue. One of them will start an operation and will say, "Okay, I want to transfer 10

confidential USDC USDT or any other token right?" For this, they will prepare the input, and they will generate the handle and an input proof. What is the problem? The other two signers don't know whether this is actually a 10 or 200, right? So, how do they trust each other? And the answer to this question is that they require a point of coordination. So, part of the project that we developed at Open Zeppelin includes what we called a multi-sig coordinator. It is essentially a contract that allows the signers of the multi-sig to submit a value, a handle, and that allows to give uh permission for decryption to all of them. So, in this case, the first signer

will submit a 10 confidential value, and the others will be able to request request decryption access, verify that the value is actually what they were told it is, and then sign the operation, right? This is essentially the uh interface for the multi-sig coordinator. As you can see, there is a from external function that allows receiving the input and then an allow handle function in which one each one of the signers will call to ask for decryption access so that they can verify the actual input, right? So, in conclusion, this whole stack that we worked in enables confidential values for for a multi-sig through a 7579 module. The purpose is

interoperability and also, you know, to get us as more as as most adoption as we can. Also, the same multi-sig can be extended to various encrypted signers and even you can add encrypted weights per signer. Imagine we actually have a variant of this, but it is a variant in which each one of the signers have a different weight towards the threshold. So, you can have a two out of three multi-sig, but one of the three signers has a weight of three. So, them alone can approve operations, right? And finally, the FAC access control list, which is the one that allows decryption access on handles, is also a concern that is well acknowledged into the whole stack, right? The last thing

to plug into this is an a confidential application and for that we recommend using confidential contracts from OpenZeppelin, which uses Summer. So, you all are well invited to take a look into our tokens and some other contracts that we have developed. That said, thank you so much and I'm open to any question. Any questions? I think Well, there's one. There's one over there.

Oh my god, that's my minor year. Uh yeah, I had a question about the 437 flow. You mentioned that you first need to send the signature on chain to like produce the e-bullion and then you this will be decrypted and then it will be used in the validate user up later on. So like one of the big advantage of 437 is that all that validate user up part that is done later you can use a paymaster or whatever for gas refunding.

So the first submission of the of the of the like signatures that are that that compute the e-bullion is there a pattern to do that in a way where gas is paid by a third party? Um yeah, I think so. You can call the request disclose operation through another account using 4337, right? So essentially we tie this to the message sender. I think it's not shown there. Um well, there is a relayer argument. I basically inline an internal function here so it doesn't really make more much sense, but that's the idea. Right?

It it is tied to the message sender can be another account and it can plug into another 4337 flow essentially. Behind you. Thank you. Great talk. Uh Thanks. How practical do you think this approach would be since FHE seems to be very computationally heavy?

Right, that's an an amazing question. Um I would say it is feasible, but as an engineer that you know, I'm pretty biased. I would say for a normal user there it requires a couple of extra steps even even without considering the computational cost. So yeah, number one it is very costly computationally, but I think for confidential operations in a multi-sig especially in a board of a company, I think that makes sense. But still, they will require to do a couple of steps to decrypt values, make sure that the input is correct, and all of this is quite a mess, to be honest, because it requires a couple of extra signatures. So, it's not really user-friendly from my point of view, but

I think it it does the job, and probably we we want to get there first, and then start optimizing. But yeah, great question. I think that's Yeah. All right, that's it. Thank you so much.