{"id":25633082,"url":"https://github.com/zkemail/email-tx-builder","last_synced_at":"2025-08-11T05:13:00.097Z","repository":{"id":227836886,"uuid":"769395822","full_name":"zkemail/email-tx-builder","owner":"zkemail","description":"A Solidity library that authenticates that the user is a correct holder of the specific email address, and specify entirely in Solidity how you want to string parse the emails.","archived":false,"fork":false,"pushed_at":"2025-08-05T18:52:58.000Z","size":3482,"stargazers_count":78,"open_issues_count":7,"forks_count":19,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-08-05T20:40:07.742Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Solidity","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zkemail.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-03-09T00:53:59.000Z","updated_at":"2025-04-28T08:50:16.000Z","dependencies_parsed_at":"2025-04-14T18:20:29.636Z","dependency_job_id":"53c3af8e-7e91-48f2-9380-7359a6637e0d","html_url":"https://github.com/zkemail/email-tx-builder","commit_stats":null,"previous_names":["zkemail/ether-email-auth","zkemail/email-tx-builder"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zkemail/email-tx-builder","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkemail%2Femail-tx-builder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkemail%2Femail-tx-builder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkemail%2Femail-tx-builder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkemail%2Femail-tx-builder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zkemail","download_url":"https://codeload.github.com/zkemail/email-tx-builder/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkemail%2Femail-tx-builder/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269832966,"owners_count":24482347,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-22T21:27:55.260Z","updated_at":"2025-08-11T05:13:00.065Z","avatar_url":"https://github.com/zkemail.png","language":"Solidity","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ether Email-Auth SDK\n## Overview\nOne issue with existing applications on Ethereum is that all users who execute transactions on-chain must install Ethereum-specific tools such as wallets and manage their own private keys.\nOur ether email-auth SDK solves this issue: it allows users to execute any transaction on-chain simply by sending an email.\n\nUsing the SDK, a developer can build a smart contract with the following features without new ZKP circuits.\n1. (Authorization) The contract can authorize any message in the email body that the user sends with a DKIM signature generated by an email provider, e.g., Gmail. \n2. (Authentication) The contract can authenticate that the given Ethereum address corresponds to the email address in the From field of the email.\n3. (Privacy) No on-chain information reveals the user's email address itself. In other words, any adversary who learns only public data cannot estimate the corresponding email address from the Ethereum address.\n\nOne of its killer applications is email-based account recovery, social recovery for smart accounts such as Safe, Clave, and so on.\nIn social recovery, the account owner must appoint trusted persons as guardians who are authorized to update the private key for controlling the account.\nHowever, not all such persons are necessarily Ethereum users.\nOur solution mitigates this constraint by allowing guardians to complete the recovery process simply by sending an email.\nIn other words, any trusted persons can work as guardians as long as they can send emails.\nUsing the ether email-auth SDK, we construct a library and tools for any smart account providers to integrate our email-based account recovery just by implementing a few Solidity functions and a frontend!\n\n## Architecture\nIn addition to a user and a smart contract employing our SDK, there is a permissionless server called Relayer.\nThe Relayer connects the off-chain world, where the users are, with the on-chain world, where the contracts reside, without compromising security.\nSpecifically, the user, the Relayer, and the contract collaborate as follows:\n1. (Off-chain) The user sends the Relayer an email containing a message called command.\n2. (Off-chain) The Relayer generates **an email-auth message for the given email, consisting of data about the command, an Ethereum address corresponding to the user's email address, a ZK proof of the email, and so on**.\n3. (Off-chain -\u003e On-chain) The Relayer broadcasts an Ethereum transaction to call the contract with the email-auth message.\n4. (On-chain) After verifying the given email-auth message, the contract executes application-specific logic according to the command in that message for an Ethereum account derived from the user's email address.\n5. (On-chain -\u003e Off-chain) The Relayer sends the user an email to report the execution result of the contract.\n\n![Architecture Flow](./docs/images/architecture-flow.png)\n\n## Novel Concepts\n### Account Code and Salt\nAn account code is a random integer in a finite scalar field of BN254 curve. \nIt is a private randomness to derive a CREATE2 salt of the user’s Ethereum address from the email address, i.e., `userEtherAddr := CREATE2(hash(userEmailAddr, accountCode))`. \nThat CREATE2 salt is called account salt, which is published on-chain.\n**As long as the account code is hidden, no adversary can learn the user's email address from on-chain data.** \n\n### Invitation Code\nAn invitation code is a hex string of the account code along with a prefix, contained in the email body and inherited by the user's reply.\nBy confirming that a user sends an email with the invitation code, the contract can ensure that the account code is available to that user. \nIt ensures the user’s liveness even when a malicious relayer or another user generates the user's account code because it prevents them from withholding the account code. \n**It suggests that the contract must check if the given email sent by the user contains the invitation code before confirming that user’s account for the first time.**\n\nNotably, the email-auth message, which represents data in the user's email along with its ZK proof, has a boolean field `isCodeExist` such that the value is true if the invitation code is in the email.\nHowever, a command in the email-auth message masks characters for the invitation code.\n**Consequently, no information beyond the existence of the invitation code is disclosed.**\n\n### Command Template\nEach application defines expected formats for a command in the email body as command templates.\n**To define the command templates, a developer does not need to write any ZKP circuits.**\n\nSpecifically, the command template is an array of strings, each of which has some fixed strings without space and the following variable parts:\n- `\"{string}\"`: a string. Its Solidity type is `string`.\n- `\"{uint}\"`: a decimal string of the unsigned integer. Its Solidity type is `uint256`.\n- `\"{int}\"`: a decimal string of the signed integer. Its Solidity type is `int256`.\n- `\"{decimals}\"`: a decimal string of the decimals. Its Solidity type is `uint256`. Its decimal size is fixed to 18. E.g., “2.7” ⇒ `abi.encode(2.7 * (10**18))`.\n- `\"{ethAddr}\"`: a hex string of the Ethereum address. Its Solidity type is `address`. Its value MUST be either 0x+lowercase, 0x+uppercase, or 0x+checksumed addresses.\n\n## Package Components\nThere are four significant packages in this repo:\n### `circuits` Package\nIt has a main circom circuit for verifying the email along with its DKIM signature, revealing a command message that masks an email address and an invitation code, and deriving an account salt from the email address in the From field and the given account code, which should match with the invitation code if it exists in the email.\nThe circuit is agnostic to application specifications such as command templates.\n**Therefore, a developer does not need to make new circuits.**\n\nIn a nutshell, our circuit 1) verifies the given RSA signature for the given email header and the RSA public key, 2) exposes a substring between predefined prefix and suffix as a command from the email body while masking the invitation code and email address, and 3) computes the account salt derived from the email address in the From field and the given account code, which must match the invitation code, if present.\nThis allows our on-chain verifier to authenticate the email sender and authorize the command in the email body while protecting privacy.\n\nFor detailed setup instructions, see [here](./packages/circuits/README.md).\n\n### Infrastructure Package\nFor detailed instructions on how to manage infrastructure metrics and alerts in Google Cloud Platform (GCP), please refer to the [Infrastructure Management Scripts](./infrastructure/README.md) documentation.\n\n### `contracts` Package\nIt has Solidity contracts that help smart contracts based on our SDK verify the email-auth message. Among them, there are three significant contracts: verifier, DKIM registry, and email-auth contracts.\n\nThe verifier contract in `Verifier.sol` has a responsibility to verify the ZK proof in the given email-auth message. \nIt is a global contract, that is, multiple users in your application will use the same verifier contract.\nYou can deploy a new verifier contract once or use the already-deployed contract.\n\nThe DKIM registry contract has a responsibility to manage a mapping between an email domain name and its latest public keys used to verify the DKIM signatures. \nIt should have mechanisms such that each user or some trusted custodians can rotate and void the registered public keys.\nFor example, the contract in `ECDSAOwnedDKIMRegistry.sol` requires ECDSA signatures from an owner of the predefined Ethereum address.\nIf you use the common trusted custodians for all users, you can deploy a new DKIM registry contract once or use the already-deployed contract.\nIf each user should be able to modify the registered public keys, a new DKIM registry contract needs to be deployed for each user.\n\nThe email-auth contract in `EmailAuth.sol` is a contract for each email user.\nIts contract Ethereum address is derived from 1) its initial owner address, 2) an address of a controller contract that can define the supported command templates and 3) the account salt, i.e., the hash of the user's email address and one account code held by the user, through CREATE2.\nIt provides a function `authEmail` to verify the email-auth message by calling the verifier and the DKIM registry contracts.\n\nYour application contract can employ those contracts in the following manner:\n1. For a new email user, the application contract deploys (a proxy of) the email-auth contract. Subsequently, the application contract sets the addresses of the verifier and the DKIM registry contracts and some command templates for your application to the email-auth contract. Here, the email-auth contract registers the application contract as a controller contract that has permissions to modify the command templates.\n2. Given a new email-auth message from the email user, the application contract calls the `authEmail` function in the email-auth contract for that user. If it returns no error, the application contract can execute any processes based on the message in the email-auth message.\n\nFor detailed setup instructions, see [here](./packages/contracts/README.md).\n\n### `relayer` Package\nIt has a Rust implementation of the Relayer server.\nUnfortunately, the current code only supports an application of the email-based account recovery described later.\nWe will provide a more generic implementation in the future.\n\nFor detailed setup instructions, see [here](./packages/relayer/README.md).\n\n### `prover` Package\nIt has some scripts for a prover server that generates a proof of the main circuit in the circuits package.\nThe Relayer calls the prover server for each given email to delegate the proof generation.\nYou can deploy the prover server either on your local machine or [Modal instances](https://modal.com/).\n\n### Security Notes\nOur SDK only performs the verification of the email-auth message.\n**You have a responsibility to ensure security and privacy in your application.**\n\nHere, we present a list of security notes that you should check.\n- As described in the Subsection of \"Invitation Code\", for each email user, your application contract must ensure that the value of `isCodeExist` in the first email-auth message is true.\n- The application contract can configure multiple command templates for the same email-auth contract. However, the Relayer can choose any of the configured templates, as long as the message in the command matches with the chosen template. For example, if there are two templates \"Send {decimals} {string}\" and \"Send {string}\", the message \"Send 1.23 ETH\" matches with both templates. We recommend defining the command templates without such ambiguities.\n- To protect the privacy of the users' email addresses, you should carefully design not only the contracts but also the Relayer server, which stores the users' account codes. For example, an adversary can breach that privacy by exploiting an API provided by the Relayer that returns the Ethereum address for the given email address and its stored account code. Additionally, if any Relayer's API returns an error when no account code is stored for the given email address, the adversary can learn which email addresses are registered.\n\n## Application: Email-based Account Recovery\nEmail-based account recovery can be used to recover any account type, the following representative example will illustrate how to use our SDK to recover an ECDSA-based smart account. We provide contracts and a production Relayer server for email-based account recovery. They assume a life cycle of the account recovery in four phases:\n1. (Requesting a guardian) An account owner requests a holder of a specified email address to become a guardian.\n2. (Accepting a guardian) If the requested guardian sends an email to accept the request, the Ethereum address of the email-auth contract corresponding to the guardian's email address is registered on-chain.\n3. (Processing a recovery for each guardian) When a guardian sends an email to recover the account, the state data for the recovery is updated.\n4. (Completing a recovery) If the required condition for the recovery holds, the account recovery is done.\n\nSpecifically, we expect the following UX. **Notably, the guardian only needs to reply to emails sent from the Relayer in every process.**\n1. (Requesting a guardian 1/4) A web page to configure guardians for email-based account recovery requests the account owner to input the guardian's email address and some related data such as the length of timelock until that guardian's recovery request is enabled. \n2. (Requesting a guardian 2/4) The frontend script on the web page randomly generates a new account code and derives the guardian's Ethereum address from the input guardian's email address and that code. \nIt then requests the account owner to send a transaction to register the guardian request on-chain, passing the derived Ethereum address and the related data (not the private data such as the email address and the account code).\n3. (Requesting a guardian 3/4) The frontend script also sends the account address, the guardian's email address, and the account code to the Relayer. \n4. (Requesting a guardian 4/4) The Relayer then sends the guardian an email to say \"The owner of this account address requests you to become a guardian\".\n5. (Accepting a guardian 1/3) If confirming the request, the guardian replies to the Relayer's email.\n6. (Accepting a guardian 2/3) The Relayer sends an email-auth message for the guardian's email on-chain.\n7. (Accepting a guardian 3/3) If the given email-auth message is valid, an email-auth contract for the guardian is deployed, and its address is registered as the guardian.\n8. (Processing a recovery for each guardian 1/6) When losing a private key for controlling the account, on the web page to recover the account, the account owner inputs the account address to be recovered, the guardian's email address and a new EOA address called owner address derived from a fresh private key.\n9.  (Processing a recovery for each guardian 2/6) The frontend script sends the input data to the Relayer.\n10. (Processing a recovery for each guardian 3/6) The Relayer then sends the guardian an email to say \"Please rotate the owner address for this account address to this EOA address\".\n11. (Processing a recovery for each guardian 4/6) If confirming the requested recovery, the guardian replies to the Relayer's email.\n12. (Processing a recovery for each guardian 5/6) The Relayer sends an email-auth message for the guardian's email on-chain.\n13. (Processing a recovery for each guardian 6/6) If the given email-auth message is valid, the states for the recovery are updated.\n14. (Completing a recovery 1/2) When the frontend script finds that the required condition to complete the recovery holds on-chain, e.g., enough number of the guardian's confirmations are registered into the smart account, it requests the Relayer to complete the recovery.\n15. (Completing a recovery 2/2) The Relayer sends a transaction to call a function for completing the recovery. If it returns no error, the owner address should be rotated.\n\n![Account Recovery Flow](./docs/images/account-recovery-flow.png)\n\n### Integration to your Wallet\nUsing our SDK, you can integrate email-based account recovery into your smart accounts. \nSpecifically, it provides an abstract contract in `EmailAccountRecovery.sol` and a Relayer server compatible with that contract, agnostic to the specification of each smart account brand.\nIn short, by 1) making a contract called **controller** that implements seven missing functions in the abstract contract, and 2) building your frontend to call the controller contract and the Relayer, you can support the email-based account recovery described above. For example, if your wallet supports a module system to extend the wallet functionality, you can make a new module contract for this email-based recovery as the controller. Notably, our SDK **does not** specify concrete account specifications and the logic in the controller **after** verifying the email-auth messages for accepting a guardian and processing a recovery.\nOur SDK cannot ensure security and privacy in the entire process without your careful implementation.\n\nSpecifically, you can integrate the email-based account recovery into your smart accounts in the following steps.\n1. (Contracts 1/6) First, you build a new controller contract with imports of the `EmailAccountRecovery` abstract contract in `EmailAccountRecovery.sol`. Your Solidity compiler will require you to implement the following seven functions: \n`isActivated`,\n`acceptanceCommandTemplates`, `recoveryCommandTemplates`, `extractRecoveredAccountFromAcceptanceCommand`, `extractRecoveredAccountFromRecoveryCommand`, `acceptGuardian`, `processRecovery`, and `completeRecovery`.\n2. (Contracts 2/6) You define expected command templates for two types of emails sent from guardians, one for accepting the role of the guardian, and the other for confirming the account recovery. You can implement the former and latter command templates in the `acceptanceCommandTemplates` and `recoveryCommandTemplates` functions, respectively. This is an example of the command templates:\n    - Template in `acceptanceCommandTemplates`: `\"Accept guardian request for {ethAddr}\"`, where the value of `\"{ethAddr}\"` represents the account address.\n    - Template in `recoveryCommandTemplates`: `\"Set the new signer of {ethAddr} to {ethAddr}\"`, where the values of the first and second `\"{ethAddr}\"`, respectively, represent the account address and the new owner address.\n3. (Contracts 3/6) You also define how to extract an account address to be recovered from the command parameters for the templates in `acceptanceCommandTemplates` and `recoveryCommandTemplates`, respectively.\n3. (Contracts 4/6) Before implementing the remaining functions in `EmailAccountRecovery`, you implement a requesting function into the controller that allows the account owner to request a guardian, which is expected to be called by the account owner directly. Our SDK **does not** specify any interface or implementation of this function. For example, the function can simply take as input a new guardian's email-auth contract address computed by CREATE2, and store it as a guardian candidate. If you want to set a timelock for each guardian, the requesting function can additionally take the timelock length as input.\n4. (Contracts 5/6) You implement the `acceptGuardian` and `processRecovery` functions into the controller. These two functions are, respectively, called by the controller itself after verifying the email-auth messages for accepting a guardian and processing a recovery. Each of them takes as input the guardian's email-auth contract address, an index of the chosen command template, the values for the variable parts of the message in the command, and the email nullifier. You can assume these arguments are already verified. For example, the `acceptGuardian` function stores the given guardian's address as the confirmed guardian, and the `processRecovery` function stores the given new owner's address or sets a timelock.\n5. (Contracts 6/6) You finally implement the `completeRecovery` function into the controller. It should rotate the owner's address in the smart account if some required conditions hold. This function can be called by anyone, but is assumed to be called by the Relayer and can take as input arbitrary bytes.\n6. (Frontend 1/3) Next, you build a frontend for the account recovery. You prepare a page where the account owner configures guardians. It requests the account owner to input the account address (`account_eth_addr`) and the guardian's email address  (`guardian_email_addr`), generates a random account code (`account_code`), constructs an expected command (`command`) for the command template whose index is `template_idx` in the output of the `acceptanceCommandTemplates()` function. It then requests the account owner to call the requesting function in the controller contract. After that, it calls the Relayer's `acceptanceRequest` API with `guardian_email_addr`, `account_code`, `template_idx`, and the address of the controller contract `controller_eth_addr`.\n7. (Frontend 2/3) You also prepare a page where the account owner requests guardians to recover the account. It requests the account owner to input the account address (`account_eth_addr`) and the guardian's email address (`guardian_email_addr`), and constructs an expected command (`command`) for the command template whose index is `template_idx` in the output of the `recoveryCommandTemplates()` function. It calls the Relayer's `recoveryRequest` API with those data and `controller_eth_addr`.\n8. (Frontend 3/3) It simulates off-chain if the `completeRecovery` function in the smart account will return no error at regular time intervals. When it stands, the frontend calls the Relayer's `completeRequest` API with sending `account_eth_addr`, `controller_eth_addr`, and a calldata for the `completeRecovery` function `complete_calldata`.  \n\nWe show some important points to implement the email-based account recovery for your smart accounts securely.\n- **Timelock is strongly recommended.** It allows an account owner who **does not** lose the private key to cancel the processing recovery when malicious guardians start it.\n- **A single guardian might be not secure enough** because an adversary can start the recovery only by exploiting that guardian's email account, e.g., stealing the password for that email account. Although the account owner can cancel the recovery, it requires the account owner to become online before the timelock expires.\n- **If the main relayer goes down, users cannot continue the recovery until another relayer is available.** The frontend should allow the account owner to choose an arbitrary relayer's email address to improve service availability.\n\nFor detailed implementation, see [docs/getting-started.md](./docs/getting-started.md).\n\nRegarding the contract addresses already deployed on Base Sepolia, see [here](./docs/deployed-contracts.md).\n\n## Support and Contact\n\nWe prioritize the security and user experience of ZK Email. If you encounter any issues or have questions, please contact us at:\n\n- **Support Email**: [support@zk.email](mailto:support@zk.email)\n- **Telegram groups**: [t.me/zkemail](https://t.me/zkemail)\n\nOur team will respond quickly to help resolve any problems.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzkemail%2Femail-tx-builder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzkemail%2Femail-tx-builder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzkemail%2Femail-tx-builder/lists"}