Shielded Write
Encrypted transactions — lifecycle, security parameters, and the filler pipeline
How it works
When you send a shielded write (either via auto-encryption for functions with shielded params, or via .seismic().send() for other functions), the SDK:
Fetches your nonce and the latest block hash from the node
Populates
TxSeismicElements(encryption nonce, TEE public key reference, block hash, expiry block)Encrypts the calldata using AES-GCM with a shared key derived via ECDH between the provider's keypair and the node's TEE public key
Signs and broadcasts the transaction as type
0x4A
The encrypted calldata is bound to the transaction context (chain ID, nonce, block hash, expiry) via AES-GCM additional authenticated data, so it cannot be replayed or tampered with.
Step-by-step
1. Set up a signed provider
Shielded writes require a SeismicSignedProvider because you need a private key for both transaction signing and ECDH key derivation.
use seismic_prelude::client::*;
use seismic_alloy_network::reth::SeismicReth;
let signer: PrivateKeySigner = "0xYOUR_PRIVATE_KEY".parse()?;
let wallet = SeismicWallet::<SeismicReth>::from(signer);
let url: reqwest::Url = "https://testnet-1.seismictest.net/rpc".parse()?;
let provider = SeismicProviderBuilder::new()
.wallet(wallet)
.connect_http(url)
.await?;2. Define the contract interface
Use Alloy's sol! macro with #[sol(rpc)] to define your contract interface. Shielded Solidity types (suint256, sbool, etc.) map to their standard ABI counterparts for encoding:
This generates type-safe call builders with .call() and .send() methods.
3. Build and send
Functions with shielded parameters (like setNumber(suint256)) auto-encrypt — just call .send() directly. For functions without shielded parameters (like increment()), use .seismic().send() to opt into encryption:
4. Verify success
Security parameters
Every shielded transaction includes a block-hash freshness check and an expiry window. The SeismicElementsFiller automatically populates these with sane defaults:
encryption_nonce
Random 12 bytes
Unique per-transaction AES-GCM nonce
encryption_pubkey
Provider's public key
Client's ECDH public key for key derivation
blocks_window
100 blocks
Freshness window for the block hash
expires_at_block
current_block + blocks_window
Block number after which the transaction is invalid
recent_block_hash
Latest block hash
Anchors the transaction to a specific chain state
These values are set automatically by the filler pipeline. You can override them per-call:
You can also pass SecurityParams directly when using the provider-level _with methods:
What happens under the hood
The filler pipeline processes your transaction in this order:
You never call encryption functions manually. For functions with shielded parameters, the ShieldedCallBuilder handles everything automatically. For other functions, the .seismic() marker tells the filler pipeline to handle everything.
Create transactions cannot be seismic
Contract deployment (TxKind::Create) always uses transparent transactions. The Seismic protocol does not support encrypting deployment bytecode. Deploy your contract first, then interact with it using shielded calls:
Error handling
Common failure modes and how to handle them:
Complete example
See Also
Signed Reads - Encrypted
eth_calloperationsShielded Calls - API reference for shielded operations
Transparent Calls - Non-encrypted operations
SeismicSignedProvider - Provider required for shielded writes
Encryption - Detailed encryption pipeline
Last updated

