⚠️ This EIP is not recommended for general use or implementation as it is likely to change.

EIP-5564: Stealth Address Wallets Source

Stealth addresses for smart contract wallets

AuthorAnton Wahrstätter
TypeStandards Track
Requires 165


This specification defines a standardized way of creating stealth addresses for smart contract wallets. This EIP enables senders of transactions/transfers to generate private stealth addresses for their recipients that only the recipient can eventually unlock.


The standardization of stealth address generation may unlock significant privacy potential within Smart Contract Wallets, allowing the recipient of a transfer to remain private when receiving an asset. A Stealth address is generated by the sender from a shared secret between sender and recipient and can only be unlocked by the recipient because only the recipient can compute the matching private key.

Observers have no possibility of linking the recipient’s stealth address to the recipient’s identity, leaving only the sender with that information.


The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Every smart contract compliant with this EIP wallet MUST implement the EIP-165 (0x01ffc9a7) interface.

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.6;
interface IERC5564 {
    /// @notice Public Key coordinates of the wallet owner
    /// @dev Is used by other wallets to generate stealth addresses
    ///  on behalf of the wallet owner.
    bytes publicKey;

    /// @notice Generates a stealth address that can be accessed only by the recipient.
    /// @dev Function is executed locally by the sender on the recipient's wallet to
    ///  generate a stealthAddress and publishableData S. The Caller/Sender must select a secret
    ///  value s and compute the stealth address of the wallet owner and the matching public key S
    ///  to the selected secret s.
    /// @param secret A secret value selected by the sender
    function generateStealthAddress(uint256 secret) returns (bytes publishableData, address stealthAddress)


interface PubStealthInfoContract {

    /// @noticeImmutable contract that broadcasts an 
    ///  event with the address of the stealthRecipient and 
    ///  publishableData S for every privateTransfer. 
    /// @dev Emits event with private transfer information S and the recipient's address.
    ///  S is generated by the sender and represents the public key to the secret s.
    ///  The sender broadcasts S for every private transfer. Users can use S to check if they were
    ///  the recipients of a respective transfer by comparing it to stealthRecipient.
    /// @param stealthRecipient The address to send the funds to
    /// @param publishableData The public key to the sender's secret
    event PrivateTransfer(address indexed stealthRecipient, bytes publishableData)

Stealth Address Generation (executed locally)

function generateStealthAddress(uint256 secret) public view returns (bytes, address){
        //  s*G = S
        pubkeyFromSecret = ecMul(secret, G);
        //  s*P = q
        sharedSecret = ecMul(secret, Publickey);
        // hash(sharedSecret)
        hashedSharedSecret = keccak256(sharedSecret);
        // hash value to public key
        pubkeyFromHashedSecret = ecMul(hashedSharedSecret, G);
        // derive new public key
        stealthPubkeyRecipient = ecAdd(Publickey, pubkeyFromHashedSecret);
        // generate stealth address
        stealthAddressRecipient = address(stealthPubkeyRecipient);
        // return public key coordinates and stealthAddress
        return (pubkeyFromSecret, stealthAddressRecipient);

For transfering:

Recipient Keypair –> $(p,P) P = p * G$
bytes public pubkeyRecipient;

NOTE: pubkeyRecipient MUST be incorporated in the smart contract wallet contract as a public state variable.


Sender senderKeypair –> $(s,S) | S = s * G $

bytes pubkeyFromSecret = ecMul(s, G);

NOTE: The parameter s represents a sender-generated secret and MUST NOT equal a user’s private key.

$sharedSecret = s * P$

bytes sharedSecret = ecMul(s, pubkeyRecipient);

stealthAddress = $pubtoaddr(P + (G * keccak(sharedSecret)))$

bytes32 hashedSharedSecret = keccak256(sharedSecret);
bytes pubkeyFromHashedSecret = ecMul(hashedSharedSecret, G);
bytes stealthPubkeyRecipient = ecAdd(pubkeyRecipient, pubkeyFromHashedSecret);
address stealthAddressRecipient = address(stealthPubkeyRecipient);

Transfer MUST emit a PrivateTransfer Event containing S and the stealthAddressRecipient.

For receiving (executed locally):

for all PrivateTransfer events do:

if $pubtoaddr(P + (G * keccak(S * p)))$ == $stealthAddressRecipient$:

bytes32 hashedSecret = keccak256(S);
bytes sharedSecret = ecMul(hashedSecret, p);
bytes pubkeyFromSharedSecret = ecMul(sharedSecret, G);
bytes stealthPubkeyRecipient = ecAdd(pubkeyRecipient, pubkeyFromSharedSecret);
address stealthAddressRecipient = address(stealthPubkeyRecipient);
store_key_locally(p + keccak(S * p));


This EIP emerged from the need of having privacy-preserving ways to transfer ownership without revealing the recipient’s identity. Tokens can reveal sensitive private information about the owner. While users might want to prove the ownership of an NFT concert ticket, they might not want to reveal personal account-related information at the same time. The standardization of stealth address generation represents a significant effort for privacy. Privacy-preserving solutions require standards to gain adoption, therefore it is critical to focus on generalizable ways of implementing related solutions.

This extension standardizes the method to generate and look up stealth addresses. Users can send assets without having to interact with the recipient beforehand. Furthermore, users can verify if they have been the recipient of a transfer without requiring interactions with the chain. Stealth addresses allow only the recipients of token transfers to see that they were the recipients.

Backwards Compatibility

No backward compatibility issues were found.

Reference Implementation

You can find an implementation of this standard in gnosisSafeModule.sol.

Security Considerations

The funding of the stealth address wallet represents a known issue that might breach privacy. The wallet that funds the stealth address wallet MUST NOT have any physical connection to the stealth address owner in order to fully leverage the privacy improvements.

Copyright and related rights waived via CC0.


Please cite this document as:

Anton Wahrstätter, "EIP-5564: Stealth Address Wallets [DRAFT]," Ethereum Improvement Proposals, no. 5564, August 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5564.