# Watching Events (/docs/builders/shreds/watching-events)

# Watching Events

RISE enhances standard Ethereum subscriptions with realtime shred data, delivering events in milliseconds instead of seconds.

## Overview

WebSocket subscriptions on RISE work just like standard Ethereum, but with dramatically faster event delivery. Events are streamed from shreds as transactions are processed, providing instant notifications for your applications.

## Shred Subscription

Subscribe to new shreds in realtime for instant transaction visibility.

### Subscribe

```json
{
  "jsonrpc": "2.0",
  "method": "eth_subscribe",
  "params": ["shreds"],
  "id": 1
}
```

<Callout type="info">
  You can add an extra `true` parameter to populate the `stateChanges` field in shred notifications:

  ```json
  {
    "jsonrpc": "2.0",
    "method": "eth_subscribe",
    "params": ["shreds", true],
    "id": 1
  }
  ```

  Without this parameter, `stateChanges` will be an empty array by default.
</Callout>

### Subscription Response

```json
{
  "jsonrpc": "2.0",
  "result": "0x9ce59a13059e417087c02d3236a0b1cc",
  "id": 1
}
```

### Notification Format

```json
{
  "jsonrpc": "2.0",
  "method": "eth_subscription",
  "params": {
    "subscription": "0x9ce59a13059e417087c02d3236a0b1cc",
    "result": {
      "blockTimestamp": 123456789,
      "blockNumber": 1,
      "shredIdx": 0,
      "startingLogIndex": 0,
      "transactions": [
        {
          "transaction": {
            "hash": "0x...",
            "signer": "0x...",
            "to": "0x...",
            "value": "0x0",
            "type": "0x2"
          },
          "receipt": {
            "status": "0x1",
            "cumulativeGasUsed": "0x5208",
            "logs": [],
            "type": "0x2"
          }
        }
      ],
      "stateChanges": {
        "0x...": {
          "nonce": 1,
          "balance": "0x...",
          "storage": {},
          "newCode": null
        }
      }
    }
  }
}
```

### Shred Object Structure

```typescript
interface Shred {
  blockTimestamp: bigint;
  blockNumber: bigint;
  shredIndex: number;
  startingLogIndex: number;
  transactions: ShredTransaction[];
  stateChanges: ShredStateChange[];
}

type ShredTransactionBase = {
  hash: string;
  signer: string;  // Address that signed the transaction
  to: string;
  value: bigint;
  gas: bigint;
  status: "success" | "reverted";
  cumulativeGasUsed: bigint;
  logs: Array<{
    address: string;
    topics: string[];
    data: string;
  }>;
  chainId?: number;
  nonce?: bigint;
}

// Transaction type variants
type ShredTransactionLegacy = ShredTransactionBase & {
  type: "legacy";
  gasPrice: bigint;
}

type ShredTransactionEip1559 = ShredTransactionBase & {
  type: "eip1559";
  maxFeePerGas: bigint;
  maxPriorityFeePerGas: bigint;
  accessList: AccessList;
}

type ShredTransactionEip2930 = ShredTransactionBase & {
  type: "eip2930";
  gasPrice: bigint;
  accessList: AccessList;
}

type ShredTransactionEip7702 = ShredTransactionBase & {
  type: "eip7702";
  maxFeePerGas: bigint;
  maxPriorityFeePerGas: bigint;
  accessList: AccessList;
  authorizationList: SignedAuthorizationList;
}

type ShredDepositTransaction = ShredTransactionBase & {
  type: "deposit";
  sourceHash: string;
  mint: bigint;
  isSystemTransaction: boolean;
}

type ShredTransaction =
  | ShredTransactionLegacy
  | ShredTransactionEip1559
  | ShredTransactionEip2930
  | ShredTransactionEip7702
  | ShredDepositTransaction;

interface ShredStateChange {
  address: string;
  nonce: number;
  balance: bigint;  // Note: bigint, not string
  storageChanges: Array<{
    slot: string;
    value: string;
  }>;
  newCode: string | null;
}
```

### Using with viem

```typescript
import { createPublicClient, webSocket } from 'viem'
import { shredActions } from 'shreds/viem'
import { riseTestnet } from 'viem/chains'

const client = createPublicClient({
  chain: riseTestnet,
  transport: webSocket('wss://testnet.riselabs.xyz/ws')
}).extend(shredActions)

// Watch for new shreds
const unwatch = client.watchShreds({
  includeStateChanges: true, // Optional: include state changes in shreds
  onShred: (shred) => {
    console.log(`Shred ${shred.shredIndex} confirmed`)
    console.log(`Transactions: ${shred.transactions.length}`)

    shred.transactions.forEach(tx => {
      console.log(`- ${tx.hash}: ${tx.status}`)
    })
  },
  onError: (error) => {
    console.error('Subscription error:', error)
  }
})

// Stop watching
// unwatch()
```

## Logs Subscription

Subscribe to contract event logs with shred-speed delivery. On RISE, logs are delivered from shreds instead of blocks for faster event processing.

### Subscribe with Filter

```json
{
  "jsonrpc": "2.0",
  "method": "eth_subscribe",
  "params": [
    "logs",
    {
      "address": "0x...",
      "topics": ["0x..."]
    }
  ],
  "id": 1
}
```

### Enhanced Behavior

* Events delivered immediately when transactions are processed in shreds
* No waiting for block confirmation
* Standard Ethereum log format maintained for compatibility
* Filters work exactly like standard Ethereum

### Using with viem

```typescript
// Watch for all Transfer events on a token contract
const unwatch = client.watchContractEvent({
  address: '0x...',
  abi: erc20Abi,
  eventName: 'Transfer',
  onLogs: logs => {
    // Events arrive in milliseconds!
    logs.forEach(log => {
      console.log(`Transfer: ${log.args.from} -> ${log.args.to}`)
      console.log(`Amount: ${formatEther(log.args.value)}`)
    })
  }
})
```

### Filter Specific Events

```typescript
// Watch for transfers to a specific address
client.watchContractEvent({
  address: tokenAddress,
  abi: erc20Abi,
  eventName: 'Transfer',
  args: {
    to: myAddress // Filter by recipient
  },
  onLogs: logs => {
    console.log(`Received ${logs.length} transfers`)
  }
})
```

## Example

### Balance Monitor

Monitor an address for balance changes in realtime:

```typescript
let lastBalance = await client.getBalance({ address: monitoredAddress })

client.watchShreds({
  onShred: async (shred) => {
    // Check if this shred affects the monitored address
    const affected = shred.stateChanges.find(
      change => change.address.toLowerCase() === monitoredAddress.toLowerCase()
    )

    if (affected) {
      const newBalance = BigInt(affected.balance)
      const change = newBalance - lastBalance

      console.log(`Balance changed by ${formatEther(change)} ETH`)
      console.log(`New balance: ${formatEther(newBalance)} ETH`)

      lastBalance = newBalance
    }
  }
})
```

## Multiple Subscriptions

You can maintain multiple subscriptions simultaneously:

```typescript
// Watch shreds
const unwatchShreds = client.watchShreds({
  onShred: handleShred
})

// Watch token transfers
const unwatchTransfers = client.watchContractEvent({
  address: tokenAddress,
  abi: erc20Abi,
  eventName: 'Transfer',
  onLogs: handleTransfers
})

// Watch DEX swaps
const unwatchSwaps = client.watchContractEvent({
  address: dexAddress,
  abi: dexAbi,
  eventName: 'Swap',
  onLogs: handleSwaps
})

// Clean up when done
// unwatchShreds()
// unwatchTransfers()
// unwatchSwaps()
```

## Error Handling

Handle connection errors and reconnection:

```typescript
client.watchShreds({
  onShred: (shred) => {
    console.log('New shred:', shred.shredIndex)
  },
  onError: (error) => {
    console.error('Subscription error:', error)
    // Implement reconnection logic
  }
})
```

## Performance Characteristics

### Event Delivery Times

* **Shred Events**: 3-5ms from transaction execution
* **Log Events**: 3-5ms from transaction execution
* **State Changes**: Immediate in shred notification

### Throughput

* **Events per Second**: 10,000+
* **Concurrent Subscriptions**: Unlimited
* **Message Size**: Optimized for low latency

## Best Practices

### 1. Use Event Filters

Filter events at the RPC level to reduce bandwidth:

```typescript
// ✅ Good - filtered at source
client.watchContractEvent({
  address: tokenAddress,
  eventName: 'Transfer',
  args: { to: myAddress }
})

// ❌ Avoid - filtering client-side
client.watchContractEvent({
  address: tokenAddress,
  eventName: 'Transfer',
  onLogs: logs => {
    const filtered = logs.filter(log => log.args.to === myAddress)
  }
})
```

### 2. Handle High-Frequency Events

Debounce or batch updates for high-frequency events:

```typescript
let pendingUpdates = []

client.watchContractEvent({
  eventName: 'PriceUpdate',
  onLogs: logs => {
    pendingUpdates.push(...logs)
  }
})

// Batch process every 100ms
setInterval(() => {
  if (pendingUpdates.length > 0) {
    processBatch(pendingUpdates)
    pendingUpdates = []
  }
}, 100)
```

### 3. Clean Up Subscriptions

Always unsubscribe when done:

```typescript
const unwatch = client.watchShreds({ onShred })

// In cleanup (e.g., React useEffect)
return () => {
  unwatch()
}
```

## Next Steps

* [API Methods](/docs/builders/shreds/api-methods) - Core Shred API methods
* [Quickstart](/docs/builders/shreds/quickstart) - Build your first app
