# Signed Reads

On standard Ethereum, `eth_call` can spoof any `from` address -- there is no signature check. Seismic prevents this: all unsigned `eth_call` requests have `msg.sender` set to the zero address. To read shielded data that depends on `msg.sender`, you need a **signed read**: a signed transaction submitted to `eth_call` that proves the caller's identity.

## Why Signed Reads Matter

Contracts can use `msg.sender` in view functions to gate access to shielded data. A common example: a token contract with a `balanceOf()` that takes no arguments and uses `msg.sender` internally to look up the caller's balance. Without a signed read, the contract sees the zero address and returns that address's balance -- which is almost certainly zero.

```typescript
// Smart read -- balanceOf(saddress) has shielded param → signed read automatically
const myBalance = await contract.read.balanceOf(["0x1234..."]);

// Smart read -- totalSupply() has no shielded params → transparent read automatically
const totalSupply = await contract.read.totalSupply();

// Force signed read -- always encrypted, even for non-shielded functions
const supply = await contract.sread.totalSupply();
```

seismic-viem provides several approaches:

* **`contract.read.functionName()`** -- smart routing via [getShieldedContract](/clients/typescript/viem/contract-instance.md). Auto-detects shielded parameters and uses signed read only when needed.
* **`contract.sread.functionName()`** -- force signed read via [getShieldedContract](/clients/typescript/viem/contract-instance.md). Always uses signed read regardless of parameter types.
* **`signedReadContract()`** -- standalone function, same API shape as viem's `readContract`. Always uses signed read.
* **`walletClient.readContract()`** -- smart routing via the wallet client. Same auto-detection as `contract.read`.
* **`walletClient.sreadContract()`** -- force signed read via the wallet client. Always uses signed read.

The signed read paths all encrypt the calldata, sign it, and decrypt the response automatically.

***

## Standalone: `signedReadContract`

```typescript
import { signedReadContract } from "seismic-viem";
```

### Parameters

`signedReadContract(client, parameters, securityParams?)`

| Parameter      | Type     | Required | Description                    |
| -------------- | -------- | -------- | ------------------------------ |
| `address`      | `Hex`    | Yes      | Contract address               |
| `abi`          | `Abi`    | Yes      | Contract ABI                   |
| `functionName` | `string` | Yes      | Name of the view/pure function |
| `args`         | `array`  | No       | Function arguments             |
| `nonce`        | `number` | No       | Override the nonce             |

The optional third argument `securityParams: SeismicSecurityParams` accepts advanced Seismic metadata overrides (see [Security Parameters](/clients/typescript/viem/shielded-writes.md#security-parameters)). Most callers should omit these; they are mainly useful for deterministic tests/debugging, explicit expiry control, and low-level interop.

### Returns

`Promise<ReadContractReturnType>` -- the decoded return value, typed according to the ABI.

{% hint style="info" %}
If the `client` has no `account` configured, `signedReadContract` falls back to a standard `readContract` (transparent `eth_call`). Pass an account-bearing `ShieldedWalletClient` to get the signed read behavior.
{% endhint %}

### Example

```typescript
import { signedReadContract } from "seismic-viem";

const balance = await signedReadContract(
  client,
  {
    address: "0x1234567890abcdef1234567890abcdef12345678",
    abi: myContractAbi,
    functionName: "balanceOf",
    args: ["0xMyAddress..."],
  },
  {
    blocksWindow: 50n, // optional: expire after 50 blocks instead of the default 100
  },
);
```

***

## Low-level: `signedCall`

For cases where you have raw calldata instead of ABI-encoded parameters -- for example, pre-encoded data or non-ABI interactions:

```typescript
import { signedCall } from "seismic-viem";

const result = await signedCall(client, {
  to: "0x1234567890abcdef1234567890abcdef12345678",
  data: "0x...", // raw calldata
  account: client.account,
  gas: 30_000_000n,
});
```

`signedCall` also accepts `SeismicSecurityParams` as a third argument for overriding the encryption nonce, block hash, or expiry window. See [Shielded Writes](/clients/typescript/viem/shielded-writes.md#security-parameters) for the full parameter table.

***

## How It Works

When you call `signedReadContract` (or `contract.sread.functionName`), the SDK performs the following steps:

1. **ABI-encode** the function call into plaintext calldata
2. **Build Seismic metadata** with `signedRead: true`
3. **Encrypt calldata** with AES-GCM using the shared key derived via ECDH
4. **Sign the transaction:**
   * For **local accounts** (private key): sign as a raw Seismic transaction, send to `eth_call`
   * For **JSON-RPC accounts** (MetaMask): sign EIP-712 typed data via `eth_signTypedData_v4`, send the typed data + signature to `eth_call`
5. **Decrypt the response** returned by the node
6. **Decode the ABI output** into the expected return type

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

***

## Signed Read vs Transparent Read

| Aspect       | `.read` (Smart)                      | `.sread` (Force Signed)                    | `.tread` (Transparent)    |
| ------------ | ------------------------------------ | ------------------------------------------ | ------------------------- |
| Calldata     | Auto-detected by ABI                 | Always encrypted                           | Always plaintext          |
| `msg.sender` | Signer if shielded, zero if not      | Always signer's address                    | Always zero address       |
| Use case     | Default -- handles most cases        | Force encryption for sensitive returns     | Public view functions     |
| Performance  | Optimal -- encrypts only when needed | Slightly slower (sign + encrypt + decrypt) | Standard `eth_call` speed |

```typescript
// Smart read -- auto-detects: balanceOf(saddress) → signed, totalSupply() → transparent
const myBalance = await contract.read.balanceOf(["0x1234..."]);
const totalSupply = await contract.read.totalSupply();

// Force signed read -- always encrypted
const supply = await contract.sread.totalSupply();

// Force transparent read -- always plaintext
const supply2 = await contract.tread.totalSupply();
```

{% hint style="info" %}
The smart `.read` namespace handles most cases correctly by inspecting the ABI. Use `.sread` when you need the response encrypted even though the function has no shielded input parameters. Use `.tread` only for public data where you don't need authentication.
{% endhint %}

{% hint style="warning" %}
`.tread` and `walletClient.treadContract` reject the `account` option and will throw. On Seismic, transparent `eth_call` zeroes out `from` on the node, so any `account` you pass would silently be ignored. For any sender-aware read, use `.sread` / `sreadContract`.
{% endhint %}

## See Also

* [Contract Instance](/clients/typescript/viem/contract-instance.md) -- `getShieldedContract` with `.read` and `.tread` namespaces
* [Shielded Writes](/clients/typescript/viem/shielded-writes.md) -- Encrypted write transactions using the same pipeline
* [Encryption](/clients/typescript/viem/encryption.md) -- ECDH key exchange and AES-GCM details
* [Shielded Wallet Client](/clients/typescript/viem/shielded-wallet-client.md) -- Creating the client used for signed reads


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.seismic.systems/clients/typescript/viem/signed-reads.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
