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

Signed Reads

Encrypted eth_call that proves your identity to the contract

A signed read uses .seismic().call() (for functions without shielded params) or .call() directly (for functions with shielded params) to build a full TxSeismic just like a shielded write, but targets the eth_call endpoint instead of broadcasting a transaction. The node decrypts the calldata inside the TEE, executes the call, encrypts the result, and returns it. The provider then decrypts the response automatically.


Why signed reads exist

Any contract function that depends on msg.sender needs a signed read. A plain eth_call (transparent read) does not attach sender identity, so the contract sees the zero address as the caller.

// Signed read — proves your identity to the contract
// msg.sender = your wallet address
let is_odd = contract.isOdd().seismic().call().await?;

// Transparent read — msg.sender is 0x0
// The contract does not know who is calling
let is_odd = contract.isOdd().call().await?;

A common example: a contract with a balanceOf() function that takes no arguments and uses msg.sender internally to look up the caller's balance. If you call it with a transparent read, the contract sees the zero address and returns its balance — which is almost certainly zero.


What gets encrypted

Both the calldata you send and the result you get back are encrypted. An observer watching the network can see that you made a call to a particular contract address, but not what function you called or what was returned.

Direction
Encrypted
Description

Request (calldata)

Yes

AES-GCM with ECDH shared secret

Response (return data)

Yes

TEE encrypts with the same shared secret


Step-by-step

1. Set up a signed provider

Signed reads require a SeismicSignedProvider because the provider needs a provider-scoped keypair for ECDH key derivation and response decryption.

2. Define the contract interface and call

Use the sol! macro with #[sol(rpc)] to define the interface, then use .seismic().call():

The return value is already decoded — no manual ABI decoding needed.

Use .seismic().call() for signed reads on functions without shielded params, and .seismic().send() or direct .send() for shielded writes. Both encrypt calldata, but .call() also decrypts the response and does not modify on-chain state. Functions with shielded parameters auto-encrypt, so .seismic() is not needed for those.


Signed read vs. transparent read

Aspect

Signed Read (.seismic().call())

Transparent Read (.call())

msg.sender

Your wallet address

Zero address

Calldata

Encrypted

Plaintext

Return data

Encrypted, then decrypted by provider

Plaintext

Provider required

SeismicSignedProvider

Any provider

Use case

Private state, access-controlled data

Public state


Per-call security parameter overrides

You can customize encryption parameters on individual calls:


How the provider handles encryption

Under the hood, a shielded .call() (via .seismic().call() or auto-encryption) performs the following sequence:


Low-level alternative

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


Complete example


When to use transparent reads instead

Not every read needs encryption. Use a transparent read (.call()) when:

  • The function does not depend on msg.sender

  • The data is already public on-chain

  • You want to use an unsigned provider (no private key)

See Also

Last updated