EIP-5564 Implementation
This document details SIP Protocol’s implementation of EIP-5564 (Stealth Addresses) and ERC-6538 (Stealth Meta-Address Registry), including technical decisions, security considerations, and integration guides.
Overview
Section titled “Overview”EIP-5564 defines a standardized approach to stealth addresses on Ethereum. SIP Protocol implements the secp256k1 scheme (scheme id 1) for full compatibility with the Ethereum ecosystem.
Key Properties
Section titled “Key Properties”| Property | SIP Implementation |
|---|---|
| Scheme | 1 (secp256k1) |
| Curve | secp256k1 |
| Key format | Compressed (33 bytes) |
| Hash function | SHA-256 |
| View tag | First byte of shared secret hash |
| Address derivation | Keccak-256 of uncompressed public key |
Stealth Meta-Address Format
Section titled “Stealth Meta-Address Format”A stealth meta-address encodes two public keys needed to generate stealth addresses:
sip:<chain>:<spendingKey>:<viewingKey>Components
Section titled “Components”| Component | Size | Description |
|---|---|---|
chain | variable | Chain identifier (e.g., ethereum, polygon) |
spendingKey | 33 bytes | Compressed secp256k1 public key for spending |
viewingKey | 33 bytes | Compressed secp256k1 public key for scanning |
Key Format
Section titled “Key Format”Compressed secp256k1 public keys are 33 bytes:
- First byte:
0x02(y is even) or0x03(y is odd) - Remaining 32 bytes: x-coordinate
// Valid compressed keys'0x02' + 64 hex chars // 33 bytes total, y even'0x03' + 64 hex chars // 33 bytes total, y odd
// Invalid formats'0x04' + 128 hex chars // Uncompressed (65 bytes)'0x' + 64 hex chars // Missing prefix (32 bytes)Generation
Section titled “Generation”import { generateStealthMetaAddress } from '@sip-protocol/sdk'
const { metaAddress, spendingPrivateKey, viewingPrivateKey } = generateStealthMetaAddress('ethereum')
// metaAddress.spendingKey: 0x02abc...// metaAddress.viewingKey: 0x03def...Encoding/Decoding
Section titled “Encoding/Decoding”import { encodeStealthMetaAddress, decodeStealthMetaAddress,} from '@sip-protocol/sdk'
// Encode for sharingconst encoded = encodeStealthMetaAddress(metaAddress)// "sip:ethereum:0x02abc...:0x03def..."
// Decode from stringconst decoded = decodeStealthMetaAddress(encoded)Stealth Address Generation
Section titled “Stealth Address Generation”The protocol follows EIP-5564’s cryptographic scheme:
Mathematical Description
Section titled “Mathematical Description”Given:
- Recipient spending public key:
K - Recipient viewing public key:
V - Ephemeral private key:
r(randomly generated) - Ephemeral public key:
R = r * G
The stealth address is derived as:
1. Compute shared secret point: S = r * K2. Hash the shared secret: h = SHA256(S)3. Derive stealth public key: P = V + h * G4. Derive Ethereum address: addr = keccak256(decompress(P))[12:32]5. View tag: vt = h[0]Implementation
Section titled “Implementation”import { generateStealthAddress } from '@sip-protocol/sdk'
const { stealthAddress, sharedSecret } = generateStealthAddress(recipientMetaAddress)
// stealthAddress.address: 0x02xyz... (compressed public key)// stealthAddress.ephemeralPublicKey: 0x03abc... (must be published)// stealthAddress.viewTag: 42 (0-255, first byte of hash)Address Derivation
Section titled “Address Derivation”Converting the stealth public key to an Ethereum address:
import { publicKeyToEthAddress } from '@sip-protocol/sdk'
// Compressed public key → Ethereum addressconst ethAddress = publicKeyToEthAddress(stealthAddress.address)// "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"Steps:
- Decompress the public key (33 bytes → 65 bytes)
- Remove the
04prefix (65 → 64 bytes) - Keccak-256 hash (64 → 32 bytes)
- Take last 20 bytes
- Apply EIP-55 checksum
Ephemeral Key and Shared Secret
Section titled “Ephemeral Key and Shared Secret”Ephemeral Key
Section titled “Ephemeral Key”The ephemeral key is generated per-payment and must be published for the recipient to claim funds:
// Sender generates ephemeral keyconst { stealthAddress } = generateStealthAddress(recipientMetaAddress)
// Ephemeral public key must be published (announcer contract or off-chain)console.log('Publish:', stealthAddress.ephemeralPublicKey)Shared Secret Derivation
Section titled “Shared Secret Derivation”Sender computes:
S = r * K (ephemeral private × spending public)Recipient computes:
S = k * R (spending private × ephemeral public)Both arrive at the same point due to ECDH properties.
View Tag Optimization
Section titled “View Tag Optimization”The view tag enables efficient scanning by allowing quick rejection of non-matching payments.
How It Works
Section titled “How It Works”- View tag = first byte of
SHA256(shared_secret_point) - Range: 0-255
- Expected false positive rate: ~1/256 (0.39%)
Scanning Process
Section titled “Scanning Process”import { checkViewTag } from '@sip-protocol/sdk'
// For each potential paymentfor (const payment of potentialPayments) { // Quick check using view tag const viewTagMatch = checkViewTag( payment.ephemeralPublicKey, mySpendingPrivateKey, payment.viewTag )
if (!viewTagMatch) { continue // Skip - not our payment (~99.6% of payments) }
// View tag matches - do full computation const stealthPrivateKey = deriveStealthPrivateKey(...) const computedAddress = privateKeyToAddress(stealthPrivateKey)
if (computedAddress === payment.toAddress) { // This is our payment! console.log('Found payment:', payment) }}Performance Impact
Section titled “Performance Impact”Without view tag: O(n × full_computation) With view tag: O(n × (cheap_check + 0.004 × full_computation))
For 1 million transactions, this reduces full computations from 1M to ~4K.
Announcer Contract Integration
Section titled “Announcer Contract Integration”EIP-5564 defines an announcer contract for publishing ephemeral keys on-chain.
Standard Interface
Section titled “Standard Interface”interface IERC5564Announcer { event Announcement( uint256 indexed schemeId, address indexed stealthAddress, address indexed caller, bytes ephemeralPubKey, bytes metadata );
function announce( uint256 schemeId, address stealthAddress, bytes memory ephemeralPubKey, bytes memory metadata ) external;}SIP Integration
Section titled “SIP Integration”import { createAnnouncerClient } from '@sip-protocol/sdk'
const announcer = createAnnouncerClient({ address: '0x...', // EIP-5564 announcer contract provider: ethersProvider,})
// After sending to stealth addressawait announcer.announce({ schemeId: 1, // secp256k1 stealthAddress: ethAddress, ephemeralPubKey: stealthAddress.ephemeralPublicKey, metadata: '0x', // Optional: encrypted note})Scanning Announcements
Section titled “Scanning Announcements”// Listen for announcementsconst announcements = await announcer.getAnnouncements({ fromBlock: 19000000, schemeId: 1,})
// Filter using view tagconst myPayments = announcements.filter(a => checkViewTag(a.ephemeralPubKey, mySpendingPrivateKey, a.viewTag))Registry Contract (ERC-6538)
Section titled “Registry Contract (ERC-6538)”ERC-6538 defines a registry for storing stealth meta-addresses on-chain.
Standard Interface
Section titled “Standard Interface”interface IERC6538Registry { event StealthMetaAddressSet( address indexed registrant, uint256 indexed schemeId, bytes stealthMetaAddress );
function registerKeys( uint256 schemeId, bytes memory stealthMetaAddress ) external;
function registerKeysOnBehalf( address registrant, uint256 schemeId, bytes memory signature, bytes memory stealthMetaAddress ) external;
function stealthMetaAddressOf( address registrant, uint256 schemeId ) external view returns (bytes memory);}Registration
Section titled “Registration”import { createRegistryClient } from '@sip-protocol/sdk'
const registry = createRegistryClient({ address: '0x...', // ERC-6538 registry signer: ethers.signer,})
// Register your meta-addressawait registry.registerKeys({ schemeId: 1, // secp256k1 stealthMetaAddress: encodedMetaAddress,})Lookup
Section titled “Lookup”// Look up recipient's meta-addressconst recipientMeta = await registry.stealthMetaAddressOf( recipientEthAddress, 1 // schemeId)
if (!recipientMeta) { throw new Error('Recipient has no registered stealth meta-address')}Security Considerations
Section titled “Security Considerations”Private Key Protection
Section titled “Private Key Protection”| Key | Sensitivity | Storage Recommendation |
|---|---|---|
| Spending private | Critical | Hardware wallet, encrypted vault |
| Viewing private | High | Secure storage, may share with auditors |
| Ephemeral private | Temporary | Securely wipe after use |
Threat Model
Section titled “Threat Model”| Threat | Mitigation |
|---|---|
| Spending key compromise | Full loss of funds - treat as seed phrase |
| Viewing key compromise | Privacy loss only - can regenerate |
| Ephemeral key reuse | Never reuse - always generate fresh |
| View tag collision | ~0.4% false positives, full derivation required |
Best Practices
Section titled “Best Practices”- Key generation: Use cryptographically secure random number generator
- Key storage: Encrypt at rest, never log or transmit unencrypted
- Ephemeral keys: Generate fresh for each payment, securely wipe after
- View tags: Always check before full computation
- Registry: Verify on-chain meta-address matches expected format
Secure Memory
Section titled “Secure Memory”SIP SDK uses secure memory practices:
import { secureWipe } from '@sip-protocol/sdk'
const privateKey = generatePrivateKey()try { // Use key} finally { secureWipe(privateKey) // Overwrite memory}Comparison with Other Implementations
Section titled “Comparison with Other Implementations”| Feature | SIP Protocol | Umbra | Fluidkey |
|---|---|---|---|
| Scheme | secp256k1 | secp256k1 | secp256k1 |
| View tag | Yes (1 byte) | Yes (1 byte) | Yes (1 byte) |
| Multi-chain | Yes | Ethereum only | Ethereum L2s |
| ERC-6538 | Planned | No | Partial |
| Compliance | Viewing keys | No | No |
| Cross-chain | NEAR Intents | No | No |
SIP Advantages
Section titled “SIP Advantages”- Cross-chain: Works across 15+ chains via NEAR Intents
- Compliance: Viewing keys for selective disclosure
- Multi-curve: secp256k1 (EVM) and ed25519 (Solana/NEAR)
- SDK: Comprehensive TypeScript SDK with React hooks
Deviations from Standard
Section titled “Deviations from Standard”SIP Protocol is fully compatible with EIP-5564 scheme 1, with these extensions:
| Extension | Description |
|---|---|
| Ed25519 support | Additional scheme for Solana/NEAR (not on-chain) |
| Cross-chain | Settlement via NEAR Intents |
| Viewing keys | Additional layer for compliance |
| SIP encoding | sip:chain:spending:viewing format |
Audit Checklist
Section titled “Audit Checklist”Before deploying to production:
- Private keys never logged or transmitted
- Ephemeral keys generated fresh per payment
- Secure random number generator used
- Keys wiped from memory after use
- View tag checked before full derivation
- Registry contract address verified
- Announcer contract address verified
- Chain IDs validated
- Input validation on all parameters