Cross-Chain Stealth Addresses
Cross-Chain Stealth Addresses
Section titled “Cross-Chain Stealth Addresses”When performing cross-chain swaps with stealth addresses, understanding curve compatibility is essential. Different blockchains use different elliptic curves, which affects how refund addresses are generated.
Curve Compatibility by Chain
Section titled “Curve Compatibility by Chain”SIP supports two elliptic curves to enable stealth addresses across blockchain ecosystems:
| Curve | Chains | Address Format |
|---|---|---|
| secp256k1 | Ethereum, Bitcoin, Zcash, Polygon, Arbitrum, Base | Keccak256/Hash160 derived |
| ed25519 | Solana, NEAR | Raw 32-byte public key |
Quick Reference Table
Section titled “Quick Reference Table”| From → To | Curves | Automatic Refund? |
|---|---|---|
| Ethereum → Polygon | secp256k1 → secp256k1 | Yes |
| Solana → NEAR | ed25519 → ed25519 | Yes |
| Ethereum → Solana | secp256k1 → ed25519 | No - requires senderAddress |
| Solana → Ethereum | ed25519 → secp256k1 | No - requires senderAddress |
Same-Curve Swaps (Automatic Refunds)
Section titled “Same-Curve Swaps (Automatic Refunds)”When both chains use the same curve, SIP can automatically generate a stealth refund address from your meta-address.
EVM → EVM Example
Section titled “EVM → EVM Example”import { createShieldedIntent, PrivacyLevel } from '@sip-protocol/sdk'
// Ethereum to Polygon - both use secp256k1const intent = await createShieldedIntent({ input: { asset: { chain: 'ethereum', symbol: 'ETH', address: null, decimals: 18 }, amount: 1_000_000_000_000_000_000n, // 1 ETH }, output: { asset: { chain: 'polygon', symbol: 'MATIC', address: null, decimals: 18 }, minAmount: 0n, maxSlippage: 0.01, // 1% }, privacy: PrivacyLevel.SHIELDED, recipientMetaAddress, // secp256k1 meta-address // senderAddress NOT required - automatically derived (same-curve)})Solana → NEAR Example
Section titled “Solana → NEAR Example”// Solana to NEAR - both use ed25519const intent = await createShieldedIntent({ input: { asset: { chain: 'solana', symbol: 'SOL', address: null, decimals: 9 }, amount: 10_000_000_000n, // 10 SOL }, output: { asset: { chain: 'near', symbol: 'NEAR', address: null, decimals: 24 }, minAmount: 0n, maxSlippage: 0.01, // 1% }, privacy: PrivacyLevel.SHIELDED, recipientMetaAddress, // ed25519 meta-address // senderAddress NOT required - automatically derived (same-curve)})Cross-Curve Swaps (Manual Refund Address)
Section titled “Cross-Curve Swaps (Manual Refund Address)”When chains use different curves, you must provide a senderAddress for refunds because:
- secp256k1 keys cannot derive ed25519 addresses (and vice versa)
- Mathematical incompatibility - the curves have different properties
- No key derivation path exists between the two curve types
EVM → Solana Example
Section titled “EVM → Solana Example”// Cross-curve: secp256k1 → ed25519// MUST provide senderAddress (in the options arg) for refundsconst intent = await createShieldedIntent( { input: { asset: { chain: 'ethereum', symbol: 'ETH', address: null, decimals: 18 }, amount: 1_000_000_000_000_000_000n, // 1 ETH }, output: { asset: { chain: 'solana', symbol: 'SOL', address: null, decimals: 9 }, minAmount: 0n, maxSlippage: 0.01, // 1% }, privacy: PrivacyLevel.SHIELDED, recipientMetaAddress, // ed25519 meta-address for Solana }, { senderAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f2bD11', // Your Ethereum address for refunds },)Solana → EVM Example
Section titled “Solana → EVM Example”// Cross-curve: ed25519 → secp256k1// MUST provide senderAddress (in the options arg) for refundsconst intent = await createShieldedIntent( { input: { asset: { chain: 'solana', symbol: 'SOL', address: null, decimals: 9 }, amount: 10_000_000_000n, // 10 SOL }, output: { asset: { chain: 'ethereum', symbol: 'ETH', address: null, decimals: 18 }, minAmount: 0n, maxSlippage: 0.01, // 1% }, privacy: PrivacyLevel.SHIELDED, recipientMetaAddress, // secp256k1 meta-address for Ethereum }, { senderAddress: 'DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK', // Your Solana address for refunds },)Error Messages
Section titled “Error Messages”If you attempt a cross-curve swap without providing senderAddress, you’ll receive one of these errors:
secp256k1 Meta-Address + ed25519 Input Chain
Section titled “secp256k1 Meta-Address + ed25519 Input Chain”ValidationError: Cross-curve refunds not supported: input chain solanarequires ed25519 but meta-address uses secp256k1. Please provide asenderAddress for refunds, or use matching curves for input/output chains.ed25519 Meta-Address + secp256k1 Input Chain
Section titled “ed25519 Meta-Address + secp256k1 Input Chain”ValidationError: Cross-curve refunds not supported: input chain ethereumrequires secp256k1 but meta-address uses ed25519. Please provide asenderAddress for refunds, or use matching curves for input/output chains.Why This Limitation Exists
Section titled “Why This Limitation Exists”Cryptographic Incompatibility
Section titled “Cryptographic Incompatibility”The two curve types are mathematically incompatible:
| Property | secp256k1 | ed25519 |
|---|---|---|
| Curve type | Weierstrass | Twisted Edwards |
| Field prime | 2^256 - 2^32 - 977 | 2^255 - 19 |
| Group order | ~2^256 | ~2^252 |
| Signature scheme | ECDSA | EdDSA |
There is no mathematical transformation to convert a secp256k1 public key into a valid ed25519 public key (or vice versa). They operate in completely different mathematical spaces.
Stealth Address Derivation
Section titled “Stealth Address Derivation”SIP stealth addresses are derived from the meta-address public keys:
Stealth Address = SpendingKey + hash(SharedSecret) × GWhere G is the generator point of the specific curve. Using a secp256k1 generator with ed25519 arithmetic (or vice versa) produces invalid addresses.
Best Practices
Section titled “Best Practices”1. Match Curves When Possible
Section titled “1. Match Curves When Possible”For the best UX, prefer swaps between chains using the same curve:
- EVM ecosystem: Ethereum, Polygon, Arbitrum, Base, Optimism
- Non-EVM ecosystem: Solana, NEAR
2. Always Provide senderAddress for Cross-Curve
Section titled “2. Always Provide senderAddress for Cross-Curve”When building UIs, detect cross-curve scenarios and prompt users for their refund address:
function needsSenderAddress(inputChain: string, outputChain: string): boolean { const secp256k1Chains = ['ethereum', 'polygon', 'arbitrum', 'base', 'bitcoin', 'zcash'] const ed25519Chains = ['solana', 'near']
const inputIsSecp = secp256k1Chains.includes(inputChain) const outputIsSecp = secp256k1Chains.includes(outputChain)
return inputIsSecp !== outputIsSecp // Different curves}3. Validate Address Format
Section titled “3. Validate Address Format”Ensure the senderAddress matches the input chain’s expected format:
The SDK exposes chain-specific validators. Dispatch on the input chain to validate the refund address:
import { isValidSolanaAddress, isValidNearImplicitAddress, isValidNearAccountId,} from '@sip-protocol/sdk'
function isValidAddress(address: string, chain: string): boolean { switch (chain) { case 'solana': return isValidSolanaAddress(address) case 'near': // NEAR accepts implicit (64-hex) accounts and named accounts (alice.near) return isValidNearImplicitAddress(address) || isValidNearAccountId(address) default: // EVM chains (ethereum, polygon, arbitrum, base, ...): 0x + 40 hex chars return /^0x[0-9a-fA-F]{40}$/.test(address) }}
if (!isValidAddress(senderAddress, inputChain)) { throw new Error(`Invalid ${inputChain} address format`)}Summary
Section titled “Summary”| Scenario | senderAddress Required? | Refund Address |
|---|---|---|
| EVM → EVM | No | Auto-generated stealth address |
| Solana → NEAR | No | Auto-generated stealth address |
| EVM → Solana | Yes | User-provided address |
| Solana → EVM | Yes | User-provided address |
Cross-curve swaps trade off some privacy (using a known refund address) for cross-ecosystem compatibility. Same-curve swaps maintain full privacy with automatically generated stealth refund addresses.