For the complete documentation index, see llms.txt. This page is also available as Markdown.

Shielded Calls

Encrypted writes and signed reads using seismic-alloy

Privacy-preserving contract interactions where calldata (and optionally return data) is encrypted using the TEE's public key.

Overview

Shielded calls use the Seismic transaction type (0x4A) to encrypt calldata before it reaches the network. Functions with shielded parameters (e.g., suint256, saddress) auto-encrypt via ShieldedCallBuilder — you can call .send() or .call() directly. For non-shielded functions that still need encryption (e.g., isOdd(), increment()), use .seismic() to opt in.

There are two shielded operations:

  • Shielded Write — Encrypted send_transaction that modifies on-chain state

  • Signed Read — Encrypted eth_call that reads state without modification

Both require a SeismicSignedProvider because they need a private key for ECDH key derivation and transaction signing.

Defining a Contract Interface

Use Alloy's sol! macro with #[sol(rpc)] to define your contract interface:

use seismic_prelude::client::*;
// The prelude includes both SeismicCallExt (adds .seismic() to SolCallBuilder)
// and ShieldedCallExt (adds .call(), .send(), .eip712() to ShieldedCallBuilder).
// You don't need to import them individually.

sol! {
    #[sol(rpc)]
    contract SeismicCounter {
        event setNumberEmit();
        event incrementEmit();

        function setNumber(suint256 newNumber) public;
        function increment() public;
        function isOdd() public view returns (bool);
    }
}

This generates type-safe call builders for each function. The #[sol(rpc)] attribute adds .call() and .send() methods. Functions with shielded parameters (like setNumber(suint256)) automatically return a ShieldedCallBuilder that encrypts on .call() or .send(). For functions without shielded parameters (like isOdd() and increment()), use SeismicCallExt to call .seismic() and opt into encryption.

Shielded Write

A shielded write sends an encrypted transaction that modifies on-chain state. The calldata is encrypted with AES-GCM using a shared secret derived from the TEE's public key.

Complete Example

Signed Read

A signed read executes an encrypted eth_call that proves the caller's identity to the TEE. This is required for reading private state that is access-controlled by msg.sender.

Complete Example

SecurityParams (Per-Call Overrides)

Customize encryption parameters on individual calls:

Method
Description
Default

.expires_at(block_number)

Set transaction expiration block

Current block + 100

.recent_block_hash(hash)

Pin to a specific chain state

Latest block hash

.encryption_nonce(nonce)

Override the AEAD nonce (testing only)

Random

You can also use SecurityParams directly with the provider-level _with methods:

EIP-712 (Browser Wallet Compatibility)

For wallets that cannot sign custom RLP-encoded transaction types (e.g., MetaMask), use EIP-712 typed data signing:

How Encryption Works

The filler pipeline handles encryption transparently:

You never need to call encryption functions manually. The provider's filler pipeline handles all cryptographic operations when you use .seismic() or when the function has shielded parameters.

Shielded Write vs. Signed Read

Aspect
Shielded Write
Signed Read

Method (no shielded params)

.seismic().send()

.seismic().call()

Method (shielded params)

.send() (auto-encrypts)

.call() (auto-encrypts)

State changes

Yes

No

Calldata encrypted

Yes

Yes

Response encrypted

N/A (returns receipt)

Yes (auto-decrypted)

Gas cost

Yes

No (simulated)

signed_read flag

false

true

For functions with shielded parameters (e.g., suint256, saddress), the sol! macro automatically returns a ShieldedCallBuilder — you can call .send() or .call() directly without .seismic(). Use .seismic() only for functions without shielded parameters that still need encryption.

Low-Level Alternative

If you need direct control without the #[sol(rpc)] call builder pattern, use SignedProviderExt methods:

seismic_call_raw is the low-level variant that takes a SendableTx and returns raw Bytes without ABI decoding.

Important Notes

  • Create transactions cannot be seismic. Contract deployment must use standard (non-seismic) transactions. Deploy first, then interact with shielded calls. See Transparent Calls for deployment patterns.

  • Signed provider required. Both shielded writes and signed reads need a SeismicSignedProvider. An unsigned provider cannot perform shielded operations.

  • Automatic response decryption. When using .call() on a ShieldedCallBuilder, the provider automatically decrypts the TEE's encrypted response.

  • Shielded types in ABI. The sol! macro handles shielded Solidity types (suint256, sbool, saddress) — they map to their standard ABI counterparts for encoding. Functions with shielded types in their arguments automatically return a ShieldedCallBuilder, so no .seismic() is needed.

See Also

Last updated