magnifying-glassSigned Reads

Encrypted eth_call that proves your identity to the contract

A signed read uses seismic_call() 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 result = provider.seismic_call(SendableTx::Builder(tx.into())).await?;

// Transparent read -- msg.sender is 0x0
// The contract does not know who is calling
let result = provider.call(tx).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 an ephemeral keypair for ECDH key derivation and response decryption.

2. Encode the call input

Use the sol! macro to define the interface and encode the view function:

3. Build a seismic transaction

Mark the transaction as seismic with .seismic() so the provider knows to encrypt:

4. Use seismic_call() instead of call()

seismic_call() encrypts the request, sends a signed eth_call, and decrypts the response:

circle-info

Use seismic_call() for signed reads and send_transaction() for shielded writes. Both encrypt calldata, but seismic_call() also decrypts the response and does not modify on-chain state.

5. Decode the decrypted response

The result is a Bytes value containing the ABI-encoded return data. Decode it using the generated types:


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


How the provider handles encryption

Under the hood, seismic_call() performs the following sequence:


Complete example


When to use transparent reads instead

Not every read needs encryption. Use a transparent read (provider.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