# Building a Smart Passkey Wallet with AA by Farhad Asgarov | Devcon SEA

- Speakers: Farhad Asgarov
- Channel: [Devcon](https://streameth.org/devcon)
- Date: 2025-10-09
- Duration: 1:27:58
- Topics: Science & Technology
- Watch: https://streameth.org/watch/yt-IQ8J0wTHk98
- YouTube: https://www.youtube.com/watch?v=IQ8J0wTHk98

## Description

Passkeys are now one of the most popular tools in building good UX in Ethereum ecosystem. In this workshop, I will build a secure simple smart wallet utilizing passkeys and start sending basic transactions on L2s. During 2h worksop, we will try to cover lots of topics related to wallet usability and achieve a great example. The power of Account Abstraction with RIP-7212 will be clearly visible.

Speaker(s): Farhad Asgarov
Skill level: Intermediate
Track: Usability
Keywords: Live Coding, Security, User Experience, Account Abstraction, hardware

Follow us: https://twitter.com/efdevcon, https://twitter.com/ethereum, https://warpcast.com/devcon
Learn more about devcon: https://www.devcon.org/
Learn more about ethereum: https://ethereum.org/ 

Visit the https://archive.devcon.org/ to gain access to the entire library of Devcon talks with the ease of filtering, playlists, personalized suggestions, decentralized access on Swarm, IPFS and more.

Devcon is the Ethereum conference for developers, researchers, thinkers, and makers. 
Devcon SEA was held in Bangkok, Thailand on Nov 12 - Nov 15, 2024.
Devcon is organized and presented by the Ethereum Foundation. To find out more, please visit https://ethereum.foundation/

## Transcript

Hello everyone. Um, welcome to our workshop today. I will be hosting a workshop about building a smart passkey wallet with account abstraction. So, uh, my name is Farhad and I am a software engineer at Klever. So, we're building a account abstraction wallet on zkSync with passkeys. We're trying to simplify the user experience of the crypto usage uh, in the wallet level. So, I will be firstly giving the technical background about passkeys, account abstraction, uh, paymasters, some other technical details about the uh, account abstraction and the uh, passkeys. So, then we will be going to uh, live coding. So, if you want to join to the coding part, uh, you're invited. So, firstly I will be talking about what is passkeys, account abstraction, smart wallet, and the explain some technical um, terms about it. So, uh, probably you have used passkeys before. If you If you use passkeys before, you can raise your hands. Yeah, there are lots of people. Uh, passkeys are basically a tools for doing a two-factor authentication in the web in the current uh, state. So, big companies started utilizing passkeys with uh, their authentication flows like Google, Amazon, and other web2 companies. So, uh, why don't we use this Why Why don't we use it on the uh, blockchain industry? Uh, so I will be explaining those and then we'll be talking about the account abstraction. So, account abstraction is simply a way to have your wallets in a programmable way with uh different verification methods like uh like any custom logic that you can apply on the smart contract level to verify your users. Uh Account abstraction also introduces the batch transactions where you sign uh where you sign only once for multiple transactions. So, instead of doing uh approval and transfers or uh some other multiple operations with different signings, you just sign once and send those transac- transactions at the same time. Uh account abstraction also introduces paymasters where you can uh sponsor for your gas uh sponsor the gas of the users uh or users can pay with any ERC-20 token for their gas. Uh so, we will be also utilizing passkeys today in our uh live coding session. We will be building a gasless paymaster to sponsor the gas uh of our users. And also, we will be building with the native account abstraction. Uh it's pretty similar to the normal account abstraction, but introduces uh it in the protocol level of the uh rollups in the current rollups, and we will be building on a rollup called zkSync uh because we have a short amount of time. Uh we have only 2 hours, and we will be building a complete wallet from scratch from zero to the production level kind of a production level wallet. Uh also, we discussed about passkeys and uh why it's not used in the current blockchain world. It was the the reason behind that is that uh the Ethereum also Bitcoin uses uh a elliptic curve called secp256k1. So, uh it's kind kind of elliptic curve which produces signatures and you can validate those signatures on the smart contract level by simply calling some libraries, ECDSA libraries. But, uh signatures given by the passkeys are different. So, in the current modern devices like your iPhones, your Android phones, and also Mac devices, uh produce a signature that is odd to Ethereum world. So, this signatures are given in the different elliptic curve called P-256 or also sec uh P-256R1. Maybe you've heard of it. So, there's no uh there's a no optimized way in Ethereum to verify those signatures. And uh precompiled contracts are introduced using EIP-7212 uh to verify those signatures in an optimized way as we do it for normal Ethereum and Bitcoin signatures. So, today's goal is a building an authentication with passkeys uh in the wallet level. So, we will implement a simple transfer functionality uh with uh with your biometrics. Uh we will introduce gasless transactions with uh paymasters. And we will be using multi-call to get multiple uh token balances for your uh account. So, normally in the normal wallets, you just have to send multiple multiple RPC calls to the network to get the balance of your account. But, with uh with multi-calls, you can just simply uh collect the users' balances with just one RPC call. And we will be also building this. And uh we will be also seeing the batch transactions as I previously uh discussed about, which is uh like combination of transactions with the same signature. At the same time, you're just sending multiple transactions instead of sending instead of signing the those transactions uh multiple times. So, this is important for wallet user experience. Uh if you have used AOAs before, you probably know that in the for example, in the swapping, if you want to swap from USDC to ETH, for example, you have to first approve your USDC and then swap uh and even if the app is not implementing it correctly, you will have approvals left behind uh maybe an infinite approval, which might be risky for the future. So, with the batch transactions, you can handle everything in a way that it's secure, it's easier to use, and just sign once, send once, and done. So, we will be firstly uh setting our repository in the uh workshop. So, I will be going back to the going to back going back to my computer. So, I prepared some starter uh repository for today. Uh it's simply a repository that helps you to start your uh coding. It's special for today, so you can uh if you want to join the workshop and do the live coding, you can go to my GitHub account. And uh in this QR, there's a repository for uh getting started. And uh let's see let's see a bit what is inside this repository. So, it's uh it shows you the ways how you can set up your development environment with with the packages. It gives you the SDK for working with the smart accounts, also with passkeys. And you will be initializing it. It talks about the components of the SDK. And also creating a passkey for your account using existing passkeys, sending transactions and sending batch transactions are also described in the readme. So, we will start by simply um cloning this repository and then I will be going to the live coding and also explaining some passkeys after like showing how you can clone this repository and set up your development environment. So, we simply I will simply go to my terminal and go to my working directory and create a test repository test folder so that I can just simply write git clone and uh the URL of the repository. So, it will be cloning some files and it's in the test um So, oh sorry, I cloned in the wrong place. So, I will be cloning again. Also, in the readme file, it shows you the way how you can use the submodules because we will be using the open-source contracts developed by us so that there's no need to worry about the smart contract side so that you you easily start to your development on the client side. So, I'll be also showing the those contracts and we'll be also building a a passkey validator contracts from the existing ones and I will be explaining the things that you should do on the passkey validation on the smart contract level. So, to update the submodules, which is located in the repository, you just simply need to copy and paste uh this inside the repository. So, it's cloning the files from the submodule itself. So, uh yeah, it's there. And you need to also use some private keys to deploy your um deploy your smart contracts and also deploy your smart accounts. So, in our contract stack, it's uh defined that you need to use the same private key for uh deploying contracts and also deploying the smart accounts produced with the uh produced from the factory contract that we are going to deploy. So, um I'll to not expose my uh private key, I'll be just uh using the pre-cloned version of the uh repository. So, everything is here. And I will be starting to the uh contract deployment, but also let's go back to our slides and discuss more about uh the smart contract deployment. So, uh today we'll be deploying these contracts with just one uh command. So, I will explain their purposes. So, we have some uh six uh or seven contracts. Yeah. The batch color contract will be used for sending batch transactions. The implementation contract will be used for the main implementation of our wallet. The registry contract will be used for storing your addresses in the smart contract to identify that this account belongs to your contract stack. The gas is paymaster will be used for implementing gas is transactions. The proxy is not important for today. It's for future. You can use upgradeable contracts to like you can upgrade your contracts in the future to change them if you need. So the passkey validator will be used to validate the passkey signatures. That we will be also showing how you can build a passkey validator contract. And also the account factory is the is basically the contract that deploys that produces your accounts one by one. And also since we're building the passkey validator contract, we need to understand the components of the passkeys authentication. So the passkey uh if you try to sign the with the passkey try to sign a message with the passkey, it simply produces an authenticator data that contains the information of your um RPID hash and other related stuff to your authentication. Whether you gave the biometrics or not, whether user set up some flags in the authenticator. The client data mostly contains the uh challenge that you want to sign. Challenge is basically the message that you want to sign. It's in the it's called challenge in the web authen standard. Also the signature that will be provided which is P256 signature as I previously mentioned. So we will be showing the uh how you can verify and create create a um passkey message and verify it on the smart contract level. So, it's simply the hash of the authenticator data uh plus like the concatenation of the authenticator data and the client data hash. Um So, let's go back to the main part where we'll be building the wallet from scratch. So, um Oops. Yeah. Since we cloned our uh repository, we can continue uh by setting up our deployments. So, uh I will open a terminal in my repository in my um local repository, so that I can go to the uh contracts repo. So, in this contracts repo, you can just simply um after putting your environment variable, you can simply go to um like we will check whether we have enough balance in our account in the in this uh private key that that private key belongs to. So, I have uh I will be using a uh zkSync Sepolia testnet, which you can which you can find the details from the chainlist or uh any other source. And uh since I have enough gas to pay enough um ether to pay for the deployment, I will be um I will be just running a command to deploy them. But, firstly, we will need to go to the smart contracts repository and build our build our validator contract, which is a passkey validator. So, in this contract stack, you see the an existing passkey validator, but we will not be using this today. Today, we'll be building our own because this contract contains some constants to optimize your uh optimize your gas. But, uh for today, we don't need them because uh you need to debug your authenticator data and client data that I described before to make it uh like optimized. So, I will be just uh copying this file and also uh creating a new validator over here, calling this passkey validator Devcon and pasting this to here. So, in the validate signature, you can see that uh there are two types of signatures that we can have. So, the the first signature that uh included that's included in the first if block is simply the optimized way to uh verify this validate this signature, but we'll be using, as I said, the uh the non-optimized way to validate this signature. So, I will be just removing this part and just keeping this validate fat signature uh function over here. So, let's uh check the components of the fat signature that we called. Uh so, it contains the authenticated authenticator data that comes from the passkey authentication, the client data suffix, which will be concatenated with the the given challenge, and the client data prefix, which is always the same, so you can simply uh hardcode it. And also, uh the RS formatted signature will be provided from the client's side so that it gets validated. Uh we have some malleability checks over here and also the flags are uh controlled so that it contains the user presence and the user verification flags of the authenticator data. Uh if you go back to slides, over here you can also go to the Mozilla docs about the authenticator data and uh about the client data over here so that you can see that the uh client data JSON or client data includes your challenge itself. The challenge will be the transaction data uh simply. So, the concept is instead of uh signing random messages in the uh web 2 world, we will be signing the actual transaction data uh as a challenge so that we can understand what kind of uh transaction the user sends and what kind of transactions to uh validate. So, uh everything in the passkey world is base 64 URL encoded. So, you need to make sure that your challenge uh and other parameters are also in the base 64 URL format. So, uh we concatenate the challenge data prefix, our challenge uh base 64, and the client data suffix. And then create a message by simply uh like hashing them and combining them together and then call the uh P256 verifier contract, which is defined with the uh which is defined in the ZK sync uh core. So, when we go back to uh the explorer of the ZK sync, When we paste this address, it's simply 0x100. You can see that the contract exists here. So, it's a P256 verifier contract that is precompiled contract so that it's up in a it's verifies your passkey uh signature in a optimized way. Uh let's go back to code and to remove some uh stuff over here because these are simply for optimization as I told. The authenticator data hardcoded version is also not needed. And we can simply remove this function, too, because this is the optimized signature verification. We're not using this, also. And I think everything is right except the name. Uh passkey validator Devcon, we will be calling this like this. And it looks fine. And we can simply continue by running the compile command to generate our um contracts to compile our contracts and get their artifacts. And we'll be using the deploy MVP script, which simply gets all the artifacts and deploy the contracts one by one, as you can see. And it gives the required um constructor parameters to the each contract so that they work well together. And then it simply logs the uh those contract addresses so that we can use them on client level. And we will be using the um passkey validator Devcon uh as as I uh named it like this. Let's check the name again to not fail. Uh yep. It looks fine. So, I will be calling the deploy MVP um command. And the contracts are getting deployed there. So, it will take like 2 to 3 minutes. Uh since here, if you have any questions, you can uh feel free to ask. Or I will continue to the client level and explain some components of the client. Uh it's also in the same repository. Yep. Uh Do you have a microphone, by the way? Or Yeah, you can ask. Yes. Uh they were at the validators uh folder. Mhm. And I just copied and pasted the validator pasque validator.sol and created a uh clone of it. Mhm. And simply just copied, pasted this file, and removed some of them. Ok- okay, so what exactly do we have to remove? Uh so, you need to just uh from from this contract, you just need to uh remove this part. And uh just keep the validate fat signature and also some uh constant values over here like this line, this line, this line, and yeah, that's it. Uh but, you can also use the same contract for this uh workshop because they both going to work because the client side just uses the second second part. Mhm. So, it's uh just enough to use the existing contracts. Okay, thank you. And you said your Yeah, sure. Um Here it is. So, yeah, I will be moving to the client level if the contracts are deployed. It's still getting deployed, but let's go to the GitHub and see uh the real see the more documentation about the existing um client. So, um we will be defining our smart wallet SDK that which is I called in my NPM package. So, the I created an NPM package for this workshop so that uh it gets uh easy for you to get started. You will be just copying and pasting the smart contract that uh produced from the previous step after our deployment. And uh we just need to initialize the smart wallet SDK instance and give the chain ID, which is 300, uh which is the chain ID of the Sepolia zkSync, and the contracts, also optionally the API URL, will be used for deploying your contracts. So, as I previ- pre- previously mentioned, you need to uh de- deploy your smart accounts with an AOA, and you need to put the uh the private key of your uh of your deployer, of your smart contract deployer to the client level somehow in a secure way so that your you're also able to you're also able to um deploy the smart accounts in a correct way. So, the SDK includes the core, uh which is uh helpful for preparing the transaction call data. The deployer will be used for deploying your smart uh accounts. Uh the multi call will be used for having multi read operations. And the web auth 10 instance will be used for uh utils for the passkeys. It's simply an extension uh over uh web auth 10 to simplify the passkey operations. So, I will be showing those functions. Like we will be uh running the uh client application on our local and code each part one by one. So, here are the smart contract addresses, as you can see. I will just copy and paste it to the to my client side. So, I can go to the uh to my client. And I will be initializing my SDK on the source. And I will call it SDK.ts. So, we're working with mostly TypeScript because there are there are lots of libraries to work with. And it's quite going well with the uh web development. So, uh what we need to do is let's put them here. Uh but we need to also get the codes for initialization. So, yeah. This These are the codes for the in for initializing your SDK. We just import the smart wallet SDK and then uh set the contract addresses. And we don't need API URL for this workshop because we'll be using the localhost one. So, you don't have to override it. But if you want to deploy it somewhere in the in the web to uh share with your friends, you will need to update this API URL value with your domain. So, the SDK instance is ready. We can close the terminal for now. And uh we can start our development environment as also described in this readme. So, uh So, you don't have to do anything after this. You just need to run the npm run dev to run the development run the development environment. So, I'll go to the client and run the dev environment. Uh So, I need to just uh uh go back to my uh previously cloned version because like my private key exists there. So, it's called it's in my smart wallet's uh directory. I can go to clients and just npm run dev. So, as you can see it started the localhost 3000 for the development. When we go to localhost 3000, it's uh welcomes us with a simple UI that says that uh you either need to create a smart wallet or log in to an existing one. So, uh we will be implementing all these steps like creating and the logging part so that you are able to uh like create a new account and also the log in to the next to an existing account. So, to do this, let's also check the architecture of our client application. It simply it simply contains some components like UI components, some constants which are uh which defines the token addresses, their pictures and stuff. Some hooks to get balances. And it uses the some global state management tools for storing the credentials in your local. And also some utils over here. And the main thing happens on the views. So in the when we go to the layout you can see that um there's a you can see that on the um um On the there's there are two views like home view and the old view. In this part we will be seeing the So when we run the our development environment we only see the old view where the authentication happens like there are two steps the deployment and the login. So we'll be firstly implementing the deployment. So let's go back to our read me file and just copy and paste this registering part. And put it to the deployment. We need to import the SDK that we created the SDK instance. And finally it gives us the status and if it's one it means that your deployment is successful and you can continue and do some other operations like setting a global state and more. So I will just throw an error if it is failed and then set up some global state over here. So there's a hook in the repository called set credential. So this set credential is simply holding the credential ID of your passkey. It's the unique identifier for each passkey and the public address that is produced when uh you deploy the account. So, we need to create our credential first of all. So, the credential is simply the passkey ID and the public address, and we will set the global state, as you can see, and then we will be initializing our uh core instance with the given credential, and we will also set the uh set this credential to the local storage, because when we refresh the page, we need to somehow get the latest account that we logged in or registered. So, that uh I also created a storage instance storage uh util, so that it's helpful for like setting JSON uh formatted uh values to your uh local storage. So, it's also defined with the storage keys.credential. It's simply a string value that defines for which key this value should be set, and also uh you you will need to pass the credential over here, and also use the set JSON item because this credential is an object value. So, it works uh So, that it works. So, it only accepts uh string values. On the other uh case, so we will just use the set JSON item function. So, let's go back and see whether uh it's going to work. So, when I try to uh press the create smart wallet button, it will probably fail because I missed some part, but I will explain soon. So, it's loading because it cannot deploy, because we need to deploy our uh smart account in an API level, which is defined in the route TS inside the app. So, Next.js applications allow you defining APIs on the client level so that you can securely put your environment variables in your inside your app and use them on the server side. It's it's creates a node environment for your client application. So, here we need to put our factory contract address that we produced before which is also we put also uh to the uh SDK instance creation part. And I will just paste this account factory to here. And as you can see it's thrown an error because it couldn't find the address of our account factory. So, I'll go back and refresh and try to deployment again. I will give my uh biometrics. And it should work. Yeah. So, we deployed our first account. We can go to the block explorer and see what's here. So, uh when you see as contract over here, it means that uh you have deployed a like there's a contract uh associated with this address. So, this is not a dummy address. So, there's a contract. Uh we failed to verify this contract, but anyways, uh it's simply the main implementation. Um So, what we can do now is simply go back to the uh our application and see that there's a disconnect button over here. Uh it means that log out from your smart account. And we will implement the login part to use the same passkey. So, when you go to the uh passwords of your uh uh, device and go to the local host. You can see the passkeys over here. Like this is the one that I created in the latest time. Uh, like 3 minutes uh, like 1 minute ago. So, the passkeys are uh, bounded to your domain. So, if you create a passkey on your local host, you can only use it on your local host. So, you have to be careful about this if you build an application. So, passkeys are, as I said, bounded to your domain and only used in in the domain that they are created. So, we will implement the login part to use the existing passkey that we created like 2 minutes ago. Uh, it's also defined in the readme. So, you can just simply copy and uh, paste this inside the auth view. Auth view, uh, if you forgot, it's the it's simply this page. It's simply this page and uh, in the login account function, we will be implementing the uh, login to the existing passkey. I'll just remove the comments. I will again build the credential object, which is the passkey ID and the public address, which is located inside the passkey response that user handle. So, user handle is the passkey ID that we provided in the in the registration part. And when you try to uh, Let's Let's read a bit this code. So, uh, this library that I built is actually using a passwordless ID {slash} WebAuthn uh, open source package, which is also doing some passkey operations. And in the authentication part, it simply passes the credential ID and a random challenge uh, during to sign in the login and some odds uh, uh options like some uh other configuration. And during the creation during the registration, we simply um get a random username, which is uh the the current date, and also the random challenge uh to create our passkey. So, in the login account, we we should have to do the same thing like the set the global state, set the local storage, and also connect our SDK to the to this uh credential. So, when I go back and refresh, I press the login to existing wallet. As you can see, the uh the passkeys are shown here. This is the latest one that I created. And uh I press this to choose it and give my credential, give my uh biometrics, and I logged into my uh smart wallet. So, it says loading because it cannot fetch the balances. So, we will implement the setting balances. We will also implement the uh like restoring our state because when I refresh the page, as you can see, it's gone. We have to press login again and sign in. But, we uh have an easier way. So, when we set the local storage, as you can see in the local host, there's a credential value, which stores our credential ID, which is a passkey ID, and the public address that I created in the in the last registration. And uh to use this, we have to go back to layout. Uh there's a component called state setter, and it simply gets the credential from the local storage by simply calling uh get JSON item from the storage instance. And you need to import these values from the like store and the utils to get the types since we are using the TypeScript. And also the set credential global state setter again comes here. And again we need to connect the SDK.core connect with this credential. So you simply build an object which has a passkey ID and public address. Set it to your store, set it your global storage, set it to your local storage to restore it and use these values for sending transactions. So when I refresh the page, as you can see it's already restored and it's the same address that I previously deployed. So there are two views, two main views over here which is a receive and send. We will implement a transfer functionality and also the receive is already there. So it this QR code contains your address so that we can fund. But before funding let's solve the balance fetch issue which is located in the use balances query hook. There are some commented places so we will implement it from scratch. Let's just uh commented let's just remove the comments and as we previously saw there's a multi call instance inside the SDK and it already contains the get token balances function which is simply sending uh some it feels it simply generates some um RPC calls and combining them together. Like it's it calls balance of function for uh the ERC-20 interface and also the for ETH it's not the same as ERC-20, so you need to call some other uh values. Some you need to make some different RPC calls. So, it contains there you can read the codes from here, but I will not go deeper. Just call the get get token balances and also the pass the addresses of the tokens that you want to get the address of. So, import the balances, which is a simply a uh the mapping of string mapping the mapping of the addresses to the balance of the uh of that token. And uh we will set the global state of the balances. Uh and also there is a another global state that is called is balances set, which is used for controlling whether we are uh we could successfully fetch the balances or not. So, uh it should be ready. So, there's also when we go back to the balances query, as you can see, it's refreshes this query every 10 seconds, so it will get your balance in every every 10 seconds and you will see the updated uh balance. So, uh when we go to home, as you can see, we have uh fetched the balance of ETH, USDC, and the Tether and the Tether. Um So, we'll be using with ETH today because I don't have enough uh USDC and on testnet. I will be showing the like operations with the ETH. But, the uh ERC-20 tokens are quite the same with the ETH. Uh if you want to generate transaction, uh but I will be showing how you can create transaction call data with the util that I built. So, uh to do this, we have to fund firstly our account. We can just go to receive, copy our address, go to my MetaMask, and I will send some money to my wallet, and let's see whether I can see it uh in my client implementation. So, I will send some ETH to it, like let's make it 0.005 ETH. And uh I'll go back to home and see whether this refreshes. Yeah, as as you can see, I have now 0.005 ETH in my balance. So, uh I can use this ETH. I can transfer to someone else. I can use batch transactions to send it to multiple users at the same time. And when I go to send, as you can see, this place is also not implemented. Uh when I go to send view, this is the This is the place of this page. This is the code of this page. And uh we will implement the transfer functionality, but first of all, let's uh comment this out because it also uses some utils from the library that I built, and also gets the balances from the global state, which you can access by simply calling uh use balances hook. So, uh it also formats this big number, which is defined in ethers. Uh it's simply formatted to an human-readable way, so you don't have a so that you don't have a like 18 uh decimal values on the UI. It's some somehow prettifies it to a six-digit. Let's go back and see that we can see our available tokens uh in our wallet. And also in send view, let's see what states we have. We have a value that uh simply holds this this place. You can just enter some uh like digits, the the amount that you want to send, the ETH amount, and the address of the recipient. So, of course, you can improve this implementation to send to usernames, for example, uh that we also do uh our own Clay wallet, so that you can send uh to your friend's username instead of addresses. Or you can also improve this to accept the ENS uh of your friends, so that you don't have trouble by just copying and pasting the 20-byte address every time. So, I will send the money to myself right now. I will not implement the that resolver, like ENS resolvers and stuff. I will just copy and paste the receiver address and send some uh ETH to it. Let's send 0.001 ETH. And uh to send an ether, we need to implement the transfer logic, of course. When I go to my readme file, as uh as you can see here, we have uh an implementation for sending the exact amount of ether. So, I will just copy and paste this. By importing the SDK, I can access the core. And for the two value, I will set the receiver, and the value I will set as the amount that I entered, which is stored in the value state. So, you need to also parse the ether value because uh it the ether has a decimals of 18, and the parsed ether is basically adds like multiplies this uh value to the 10 10 to the 18 so that uh the blockchain understands how much amount you want to send. And also, you will be calling the sign and send function from the SDK. So, we went to the node modules and see what's inside. So, this uh function also appends a paymaster to your uh transaction so that you don't have to worry about paying the gas. So, you will simply pay for your users' gas so that people just simply can uh send transactions without worrying about how much ether they have for the gas. So, it gets the transaction by calling the sign function. It simply does some some interesting stuff from the uh things that I implemented. You can also read those codes from the uh repository. It's uh open source. But, uh since we don't have to worry about the difficult parts over here, we just have to create the transaction uh which simply like sets the transaction object for your blockchain. Sets some uh required parameters like gas price, gas limits, chain ID, and other parameters. And uh we'll be signing with the pass key and then sending this transaction to the network to get uh validated. I'll go back to the client application and press send. This uh transaction will probably fail because we again didn't do something that we should have done. So, as you can see, it errors with the failed to validate the transaction. The reason is paymaster validation error. So, when we go back to our addresses that we deployed on the client. We have a gasless paymaster over here. So, let's paste this to the explorer and see what's inside. Um so, I'm in the main nets. I should go to the Sepolia. Okay, as you can see there's a contract called gasless paymaster and it has no balance inside. But, since we are paying for the fees, someone has to pay for the fees, and this paymaster will hold some ether inside to sponsor your transactions. So, I will send some money to this paymaster. So, you need to eventually check your paymaster address if you want to go in production so so that you don't get out of funds so that users don't have to pay for the gas. I'll just paste this address to the to my MetaMask and send uh let's send $100. It's too much for now, but anyways, it's the test net ETH. So, when I refresh, as you can see my money is here so that I can start sponsoring the transactions of the users. Uh let's go back to the client and refresh again. So, I have 0.005 ETH and I will send again this the same transaction, the exact same transaction that I previously did. Copy my copy paste my address to the receiver and send 0.001 ETH to this address. I'm again giving my uh biometric authentication. So, it appends the signature and sends the transaction. So, it will the balance will be uh updated in 10 seconds. But, as you can see, it went down to 0.004. Uh we go we can go back to the address on the explorer and see the transaction history. So, what happened here? So, in the transfers part, as you can see, I just uh now sent 0.001 ETH to this address. Let's go to the transaction and see inside. So, here uh the zkSync explorer shows that this fee is paid by paymaster, uh which uh has an address of this. And uh it's paid for the 3 cents transaction fee. Let's close this page. Um So, we can simply we can improve this part by simply uh calling the another um function over here to update our state much faster. Like, I will be I'm using a React Query for this uh repository. So, it's a helpful tool for um sending an API request to somewhere or sending like RPC calls, etc. Um I will be just calling a refresh queries function from the React Query and passing the uh the key of the um balance fetcher query, which is defined here. As you can see, it contains uh the queries it contains the balances value, which is a string value, and the public address of the of the user. So, that we set the balance for each user, each registered user. So, to so so to refresh the query, uh I need to call this refresh query function and also pass the query key. So, let's try it and see whether it works. I will again paste this address and start and send. Let's send a bit slow amount so we can see full balance. Okay, it went down as you can see my balance went down. So, for the next part we will be um sending a batch transactions. So, as I previously discussed, batch transactions are used for combining multiple transactions into together so that you just sign the transaction once and then uh send to the network. So, we will be using some uh utils again from the SDK that I built but we will be sending an ETH amount to different addresses. So, let's go back to send view and expand this part as uh like uh add another state as receiver two. And also the value two to send also different amount uh to to different addresses. The selected token will be the same and I will be putting another input over here so that we can also uh set the state for the second receiver. So, this is the receiver one and this is the receiver two. Uh so, this sets the set this states this this sets the state for the receiver two and also contains the value of the receiver two. And this is for the first receiver. So, let's go back to the send. And as you can see, we can see now two inputs. We have uh to set We can set We can just remove this value two to send the same amount to not lose time. And uh I think we are ready, but we need to implement the uh batch transaction function. So, I will comment out this part and simply uh start again by using the batch transaction util from the core instance. So, as you can see, there's a function called get batch get batch transactions. Get batch transaction, and it simply accepts array of uh transactions which has a two address, a value, and a call data. So, since we are transferring ETH, we will be just setting the value and the two amount two uh fields. So, when I go back to my um get batch transaction function implementation, I will be setting the two as uh receiver and my second two value as the receiver two. I will also putting the value as the as as I did before, like I have to multiply it by 10 to the power of 18 since ETH has this decimal. I will be setting the same amount for this uh for the second uh receiver. Again, I will need to call the uh sign and send method from the from the transaction instance that I created and also refresh the queries. So, let's go back and see how much balance we have. So, we have 0.0039 ETH inside. And I will be setting two different addresses to transfer money to different addresses with the same transaction. So, I'm setting my address as the first as the second receiver and also my um my main wallet as a first receiver. And let's set the amount of 0.00 uh 1 and start sending transaction by Again, we need to uh give the biometric authentication so that goes to the smart account smart account passkey validator and gets verified. As you can see, my balance is down to 0.0019. I sent one ETH to each user. Let's go back to the block explorer and see the transactions. So, uh in the transfers, it seems like uh like multiple transfers, but I actually sent it with this method uh to the blockchain so that it sent it transferred twice, but actually I sent one transaction. So, let's go to the transaction and see what happened again. So, as you can see, uh our wallet transferred 0.01 ETH to this address and this address with the same transaction. And also we sponsored for the gas. So, um let's go back and um see what else we can do. I think uh I think the implementation is done for our wallet. So, what we have in the end? So, we have a smart wallet that we can log in and like in the beginning we all of course like deployed our smart contract and now we can just simply log in to it with our biometrics. Which has like we have a multi poll RPC calls in the home page so that we fetch the balances for different tokens. We have a send functionality which we can send multiple which we can send ether to multiple users at the same time with the same transaction so that like you don't worry about the gas fees and other stuff. And we sponsor the transactions with a gasless paymaster. And we also have a place to receive your funds from different wallets so that you deposit you top up your balance. And you can of course like uh uh disconnect and create a new wallet, continue to your path with the new wallets and do additional things. So, uh let's talk about a bit uh I can accept some questions if you have any. Yep. Thanks. Yep. Uh if you're Uh if you're building a mobile wallet, there's no restriction for the web use on iOS side. But Android doesn't allow you to uh use your passkeys on the web view. Um currently um I think there are some passkey wallets that are built in the browser level and they're using uh another like they're using a pop-up to navigate to users to their domains in another window. And there they uh sign for the transaction in that domain so that they can use that same passkey for different Uh yes, it works for the iframe on the browser level. Uh yeah. Yeah? All right. Can you associate multiple uh passkeys Can you associate multiple passkeys to a single account abstraction wallet? So yeah, um the contracts that I showed uh is a a modular account that can have multiple public keys inside. So you can assign like as many as possible uh as many public keys as possible to the smart account. So how it works in this contract that I showed, it simply tries to validate over all public keys. So at the end, if it finds a correct public key, uh it's validates the transaction, otherwise it fails. So uh this is possible because of the like RIP-7212 because uh normally like validating this transaction is taking too much gas, but uh with the precompiled contract, you can just uh like validate through through the all too much gas and in the current roll up state it's just like cents some cents. In uh that that's just on ZK sync? Uh currently the precompiled contract is deployed to ZK sync and Kakarot as I guess. Okay. And some other roll ups are also integrating it and they will be live probably in the uh short amount of time. But since it's live on ZK sync I showed the demo version of the wallet on ZK sync. Got it. Thank you. Yeah, please. So yeah, during the deployment you provide a salt to your account. Uh so like you you generate a random value for each uh wallet so that you don't have uh like you don't run into the trouble that you try to deploy the same address. So in ZK sync there's a function called create two in the um system contract. So it computes the address for your salt value. So uh I can show from the code actually. So during the deployment part on the client side we simply call the factory call the function from the um deploy account uh from the factory contract which is called deploy account. We pass the salt value to the uh deploy account function where it calculates the uh public address. And uh it's a deterministic way actually. With the same salt you produce the same address. But the problem is uh that it's different for the other chains. For the ZK sync, uh the create two produces a different value for uh different public address than for for example from base. Uh so that's uh this is the current technical problem ongoing cross-chain with ZK sync and other chains. So you you cannot produce the same address for ZK sync and other chains. Yeah? The elliptic curve function are different on the mobile phone and the smart contract. So I guess it's very that verification on the smart contract is insanely expensive. Yes, exactly. So uh we can go to the beginning. So uh normally the transaction the um We will go back to here. So yeah. Normally uh the Ethereum and the Bitcoin also uses a elliptic curve called secp256k1. But the passkeys uh and secure enclave also uses a uh elliptic curve called p256 or secp256r1, which is different than Ethereum as you mentioned. So it's really costly to uh verify those transactions, but there's a a roll-up improvement proposal called 7212, which was an EIP before, which is developed uh from our team actually. Uh some folks from our team. Let's go to the uh definition of it and see what it recommends. So this proposal creates a precompile contract that performs a signature verification in the secp256r1 curve. So that it's the verification cost will be the exactly equal to the verification of the secp256k1 curve. So, in our case, we don't run into the trouble. There are some other like optimized smart contract implementations that we used previously, but they are like almost taking 50 cents for each transaction, which is quite high. Uh but like after the blobs, it's like a bit less, but again using a precompiled contract is always much more optimized. Yeah, please. I'm next, sorry. Sorry. Here. Here. Here, on the right. Okay. Sorry. Um so, my question is um we didn't get into that part probably, but uh you said that paymaster is paying fee for transactions, right? So, could we get into that part like um you're trying to send a transaction for address A to address B, but some other address is paying for for fees, right? How is that uh done in in your example? So, you mean a wallet B pays for the Yeah. address wallet A? No, no. You have um I'm sending a transaction from address A to address B, but some other address C, for example, is paying for fee, which is the your case, right? Uh where paymaster is paying fees, right? Am I right? Did I get it? Uh like currently, the paymaster is a separate contract that contains a balance inside, which has a custom code inside. So, it checks uh it calculates the how much gas it the wallet A should pay, and it checks whether I have a balance the paymaster has a balance uh of that value, and pays for the gas. But, the wallet C um I cannot imagine a way In the wallet the address see You're sending transaction from address A to address B, right? Yeah. Uh, but the thing that I didn't get probably or was not presented is that you were saying that address A won't be charged for a fee. Yes, exactly. Yeah. Yeah. So, some somebody else is paying that fee. Uh, and that's what I'm asking. How how is that done? Yeah, uh, like the the payer the payer is actually the contract itself. So, with the account abstraction, uh, you can create a contract and put some money in the contract. So, when you send money from your account to another account, that contract you add that contract address and, uh, like prepare a paymaster data. It's called paymaster contract. Put that paymaster data inside the transaction object. So, it uh, also like uh, checks for the balance of the paymaster contract and the paymaster pays the fees. Yeah, and the the that's that part is clear, but if contract does that if contract is paying for fees and, uh, address A that is sender needs to tell to that contract Yeah. what to do. And, uh, that's possible only through a transaction and fee for that transaction needs to be paid. Yeah. Who is paying it? The paymaster contract. And that? Yes. Yeah. That that part is not Like I will show like the paymaster, uh, addition to the smart con- like the transaction in, uh, generation. Mhm. So, when you, um, go to the So, this is the the code that I built for SDK. So, it's simply when you um try to send a transaction, it's appends a paymaster to the transaction data. So, this append paymaster is simply adding the the gasless paymaster address and sell telling that this is a general-purpose paymaster, which is uh gasless paymaster in in our in our case. May I interrupt you here? So, now we have a transaction like setting from, setting to, setting data, setting custom data, right? But, that transaction needs to go to a contract. That contract will know what to do next, right? Yes. And that costs something. Yep. So, does it mean when I see that transaction on the Etherscan, that I'm not the sender of it? Uh like it's actually like this in the other chains, but zkSync supports a native account abstraction, so that there's no bundler. You just sent this the custom type of transaction to the zkSync network using a provider uh provider instance of the ethers, for example, and contract like the the network itself understands who should pay the gas and uh who actually sent the transaction. So, you actually correctly see the sender and the paymaster on the explorer level. But, in the other chains, it seems that uh someone else sent the transaction. It's actually shown in the internal transactions. Like there's a new tab in the Etherscan. Maybe you noticed that. Yeah, yeah, yeah. It's shows in there, but in in the ZK sync, uh since there there's a native account abstraction, uh it's it's shown in a correct way. Okay, thank you. Welcome. Yeah. Uh it's kind of an aside, but do you know the latest on the uh proposal that you just mentioned in terms of integrating that to an L1, like adding the precompile to mainnet, let's say? Is that going to happen or has that just kind of blocked? Uh yeah, there there are some uh like updates about this, uh like in the internal talks with the Ethereum team. Uh there are some talks about like implementing this to the uh mainnet, but the actually the the the proposal is uh suggested by the Ulaş from like he's sitting uh on the left side of you. Maybe he can answer about this question, like whether it's going to be happening on the mainnet or no. Hey. Kints next hardfork Pectra is currently very overbooked with lots of proposals, so I don't see that's very realistic for the Pectra, but it's still on the CFI list, and if some of the core devs or client teams focus on implementing the this this proposal and allocating that much, we we can have it on Ethereum maybe in Pectra, maybe in Pectra B, which is separated into two forks, but at least now we have on each major rollups, including optimism and optimism ecosystem rollups, ZK sync, all ZK sync elastic chains, and in Arbitrum soon, I think we are having adoption, and it's it's still good. Do you need on L1, for example? Why do Why do you need I'm just curious. Okay. Yeah, any more questions? Yes, please. Hello. I'm curious to know like how this can be used in a real world. So, what I understand we are use private key to deploy contracts. And then we have used client UI part, and we are sending the funds. Uh so, if we have private key, we can use that private key directly and send the funds. So, how this can be used in a real world? In real world, you mean? So, uh the private key on the like in the client level is actually for deploying the trans deploying the account for the first time. So, it's not used in other cases. So, in the first part, when you deploy an account, someone has to send a transaction, but there's no signer in the beginning. Uh so, we use that private key to initialize our contract. After that, we can just uh throw away that private key and do not use it. Uh we will be using just the the pass keys uh private key, which is actually like uh it's embedded inside your device, so you cannot uh like reach it. Uh you just You can just sign transaction with that without knowing what the private key is. So, in the real world, you can have an account abstraction wallet. So, you deploy this wallet with the some with some private key. You just throw away that private key. You will not use it and have a somehow um like hardware level security in your phone. Some kind of a like the most probably the secure way to uh send the transaction with that kind of abstraction right now. So, we are currently building that kind of wallet. Uh so, there are some real-world uh usages. Yes, got it. Clear. And I have second question. Uh that is we have run it on localhost, right? So, I can run it on separate machine and the client, not the contract, and use the same addresses. Uh and I also create a uh passkey there. So, I can have two passkeys for single account if I have the addresses. The paymaster master address and all the addresses we get after deploying the contract. So, I can deploy multiple instances of that and access the same account. Yes, exactly. I'm understanding correctly. Yeah, yeah. I got the question. Uh like in the first part, you can of course, you can like move passkeys from one device to another if you want to say if if you want to use the same passkey in different accounts, and it's automatically done on iOS, and it's automatically done with the Chrome uh uh as far as I know. Like, if you create a passkey on your first Android device and uh like combine like use the same Google account for those two uh devices, you will have the same passkey on the same devices. But, in the same part on the second part, uh you asked about using two passkeys for the same account. Yes, this is also possible. Uh you can have two public keys inside your smart contract to verify through uh and you will just add one owner to your smart contract and then add another owner to your contract. Uh you can add x amount of uh owners to your contract so that you will have multiple passkeys for the same address. Okay, got it. Thank you. Yeah, but uh you will be like each passkey belongs to the one domain. So, you cannot change the domain after that. So, you need to be careful about the passkey creation, so where it's created, whether that domain is accessible, uh like some other concepts if you want to go to production, uh yeah. So, yeah. Um Oh, there are some Yo, there are some questions from this I didn't know that there are some questions from the live. Is there a live example of passkey wallet? Um yeah, there are some passkey wallets exist in the ecosystem. So, the one of them is Coinbase smart wallet, which is accessible on the uh browser level, so you can just simply use your uh browser to access to the uh smart accounts. Also, there are some mobile wallets, which one of them is we are building called clave. And uh like these are two different the these these are two different implementations. One is the mobile, one is the uh web implementation, but they are trying to solve the user experience problems on the ecosystem. Can you show clave and how it's used in real life? Uh well, it's it's probably not allowed to do some uh like uh like commercial things here, but like you can see on YouTube, for example, you can uh go to the uh our website and get clave.io and see, uh, the real world usages and how we like simplify the crypto experience for the newcomers and also the existing users. If for example, iCloud keystore safe to use for passkeys, uh, syncing passkeys doesn't have same security, uh, doesn't have some security issue. Probably that's a question. Um, Um, so the iCloud keystore actually This is not the, like, normally you have, uh, credit card information in your iOS devices, for example. The passkey private key is stored also at the same place. So, um, when you create a passkey, its private key is stored in the, um, separate place from your devices, uh, from your devices operating system, so that it's quite safe to put your passkeys. Uh, it's called the secure enclave. You can search about it on the internet. So, um, it's I would say pretty it's pretty secure as like putting your Apple, uh, like, your card information to Apple. Um, Yep. Ledger? Uh, like, during the syncing, the, uh, thing is that, for example, for iOS devices, you, um, add your devices to your trusted circle. Uh, of course, there is there are some there are might be some attack vectors, but, uh, also some, uh, security measures are taken for securely, like, syncing those, um, passkeys through your devices. Like, if you want to access a passkey from your other device, for example, or add that device to your trusted circle, you just you just have to uh pass through a two-factor authentication uh on the Apple side and do some additional stuff. But, if you are uh if you want to not sync it, you're you can also continue with that option. Uh this the example that I showed also like syncs it to your Chrome uh account so that you can use it in different uh like um places. Also, Android, I guess, introduced some new stuff with the passkeys. So, if you create a um passkey on your uh Chrome, you will be able to access it from your phones also. So, they're also bringing some additional things, additional updates. Uh currently, it supports the zkSync Era network. Will it support other networks in the future, such as EVM, um Optimism, Sui, Solana? Well, uh as I previously told, uh there are some technical difficulties on calculating the same address on zkSync and other networks, like oh like Optimism and other like EVM chains. Um for Solana and other uh other blockchains, it's kind of different implementation. So, probably, we our next goal might be going to cross-chain in the future when uh we have enough technical um capability to do it. But, right now, we are only uh going zkSync because uh we're trying to make the UX much more simpler. Uh and when it's cross-chain and the addresses are different and there are no ways to do um like multiple like for example, we are currently supporting a social recovery method where you can add your friend as your guardian. So, uh if you if something happens to your phone, for example, or you le- lose your passkey somehow, you delete it accidentally, you can uh request your friend to recover your account in 48-hour time lock. So, this is also possible with the account abstraction and with the smart accounts. So, um when you go cross-chain, you have to like add those guardians in different networks one by one, send transactions for each network, and this is probably going to be solved with the um keystore rollups in the future if there are going to be a good implementations of them. Um but yes, since there are some technical difficulties, we are trying to keep the uh user experience as simple as possible. And yeah. What would be the best method to implement a social recovery in a passkey system? So, the contracts that I showed also contains a like the contracts are designed in a way that you can add modules to your contract, and the one of the modules that we built before is the social recovery. The social recovery contract is simply um helps you to recover your accounts uh using your uh friends, uh their uh other like other wallets, you can assign them as the guardian to a smart contract and uh give some time lock, some uh threshold, like maybe you will require two of your three guardians to accept the uh recovery um recovery request, and your account will be recovered after um after like some time lock time. So, uh in our implementation we just. For simplicity we make the. We allow only three guardians. And only one of them can complete the. Recovery process because it's kind of difficult to. Get approval from. Multiple guardians and one guardian is probably enough for recovering your account. We also support ZK email. We will be supporting soon it. The ZK email helps you. Um. Adding emails as your guardians to your smart contract. So in this case. The email that you send have a DKIM signature and you can just verify that signature on chain using some. Cool stuff that ZK email team built. And you just assign your. Email as a guardian and then. You just. Recover your account by sending an email. So that's a pretty good feature for the user experience. In the. In the real world. What is the typical transaction cost for pass key transaction on ZK sync? So typical transaction as you prove to show is probably one to three cents. Depends on the network availability but it's the same as sending transaction from your AOA. Because there are. Precompiled contracts. The cost is pretty low and you don't have to worry about the gas fees on ZK sync. So yeah. Any other questions? I think no questions from the. Audience and then also no questions from the chat. We can stop a bit earlier. Um, so today we built a simple version of the smart accounts on our browser. Oh, there's a question. Uh, if the passkey standard is upgraded in the future, how can we guarantee the I think the question is trimmed somehow. But if the passkey standard is upgraded in the future, it will definitely be the backward compatible with the existing implementations. But if you uh, like want to update your passkey in the future with the new uh, with the new uh, standard in the future, like let's say if the passkeys are if the passkeys are going to be quantum resistant in the future, you can just simply create a new passkey and update your owner in the smart contract so that it will continue working with that new standard. Uh, yeah. We built a smart wallet with that works with passkeys today. We sponsored the users transa- transactions using Gnosis Paymasters. We sent multiple transactions at the same time. Uh, the all the codes are open source in the repository that I uh, showed in the beginning. It's in my GitHub account. And you can of course like use it for your own cases um, if you have any plans in the future to build uh, cool wallets to simplify the user experience. Uh, thanks for joining.
