> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Polling

> Poll async operations with configurable intervals, backoff, and timeouts

<Info>
  Source: [poll.ts](https://github.com/evmts/voltaire/blob/main/src/utils/poll.ts) • Tests: [poll.test.ts](https://github.com/evmts/voltaire/blob/main/src/utils/poll.test.ts)
</Info>

<Warning>
  **This page is a placeholder.** All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.
</Warning>

# Polling

Poll operations until they succeed or timeout, with optional exponential backoff. Essential for waiting on asynchronous Ethereum operations like transaction confirmations, block finalization, or contract state changes.

## Overview

Polling utilities provide:

* **Configurable intervals**: Control polling frequency
* **Exponential backoff**: Progressively longer intervals
* **Timeout handling**: Fail after maximum duration
* **Validation**: Custom success conditions
* **Progress callbacks**: Monitor polling attempts

## Basic Usage

### Simple Polling

Poll until result is truthy:

```typescript theme={null}
import { poll } from '@tevm/voltaire/utils';

const receipt = await poll(
  () => provider.eth_getTransactionReceipt(txHash),
  {
    interval: 1000,
    timeout: 60000
  }
);
```

### With Validation

Poll until validation passes:

```typescript theme={null}
const block = await poll(
  () => provider.eth_getBlockByNumber('latest'),
  {
    interval: 1000,
    validate: (block) => block.number >= targetBlock
  }
);
```

## Poll Options

### PollOptions

```typescript theme={null}
interface PollOptions<T> {
  interval?: number;        // Polling interval in ms (default: 1000)
  timeout?: number;         // Max duration in ms (default: 60000)
  backoff?: boolean;        // Use exponential backoff (default: false)
  backoffFactor?: number;   // Backoff factor (default: 1.5)
  maxInterval?: number;     // Max interval with backoff (default: 10000)
  validate?: (result: T) => boolean;
  onPoll?: (result: T, attempt: number) => void;
}
```

## Polling Functions

### poll

Core polling function with full configuration:

```typescript theme={null}
const data = await poll(
  () => provider.eth_call({ to, data }),
  {
    interval: 500,
    timeout: 30000,
    validate: (result) => result !== '0x',
    onPoll: (result, attempt) => {
      console.log(`Attempt ${attempt}: ${result}`);
    }
  }
);
```

### pollUntil

More expressive when checking specific conditions:

```typescript theme={null}
import { pollUntil } from '@tevm/voltaire/utils';

const blockNumber = await pollUntil(
  () => provider.eth_blockNumber(),
  (num) => num >= 1000000n,
  { interval: 500, timeout: 30000 }
);
```

### pollForReceipt

Convenience function for transaction receipts:

```typescript theme={null}
import { pollForReceipt } from '@tevm/voltaire/utils';

const receipt = await pollForReceipt(
  txHash,
  (hash) => provider.eth_getTransactionReceipt(hash),
  { timeout: 120000 } // 2 minutes
);

if (receipt.status === '0x1') {
  console.log('Transaction successful!');
}
```

### pollWithBackoff

Poll with exponential backoff enabled by default:

```typescript theme={null}
import { pollWithBackoff } from '@tevm/voltaire/utils';

const data = await pollWithBackoff(
  () => provider.eth_call({ to, data }),
  {
    interval: 100,      // Start at 100ms
    backoffFactor: 2,   // Double each time
    maxInterval: 5000,  // Cap at 5s
    timeout: 30000
  }
);
```

## Exponential Backoff

### How It Works

With backoff enabled, intervals increase exponentially:

```
interval = min(interval * backoffFactor, maxInterval)
```

### Example Timeline

Configuration: `{ interval: 1000, backoffFactor: 1.5, maxInterval: 10000 }`

| Attempt | Interval (ms)  |
| ------- | -------------- |
| 1st     | 1000           |
| 2nd     | 1500           |
| 3rd     | 2250           |
| 4th     | 3375           |
| 5th     | 5062           |
| 6th     | 7593           |
| 7th+    | 10000 (capped) |

### When to Use Backoff

Use backoff when:

* Operation may take progressively longer
* Want to reduce server load over time
* Waiting for finality (takes longer as confirmations increase)

Don't use backoff when:

* Need consistent check frequency
* Time-sensitive operations
* Known fixed interval required

## Real-World Examples

### Wait for Transaction

Wait for transaction confirmation with backoff:

```typescript theme={null}
async function waitForTransaction(
  txHash: string,
  confirmations: number = 1
): Promise<TransactionReceipt> {
  const receipt = await pollForReceipt(
    txHash,
    (hash) => provider.eth_getTransactionReceipt(hash),
    {
      interval: 1000,
      timeout: 120000,
      backoff: true,
      backoffFactor: 1.5
    }
  );

  if (confirmations > 1) {
    const targetBlock = receipt.blockNumber + BigInt(confirmations - 1);

    await pollUntil(
      () => provider.eth_blockNumber(),
      (num) => num >= targetBlock,
      { interval: 12000 } // Block time
    );
  }

  return receipt;
}
```

### Wait for Contract Deployment

Poll until contract code is deployed:

```typescript theme={null}
const code = await pollUntil(
  () => provider.eth_getCode(contractAddress),
  (code) => code.length > 2, // More than '0x'
  {
    interval: 1000,
    timeout: 60000,
    backoff: true
  }
);

console.log('Contract deployed!');
```

### Wait for State Change

Poll contract state until condition met:

```typescript theme={null}
import { Abi } from '@tevm/voltaire/Abi';

const abi = [
  {
    type: 'function',
    name: 'balanceOf',
    inputs: [{ type: 'address', name: 'account' }],
    outputs: [{ type: 'uint256', name: 'balance' }]
  }
] as const;

const targetBalance = 1000000n;

await poll(
  async () => {
    const data = Abi.Function.encodeParams(abi[0], [address]);
    const result = await provider.eth_call({
      to: tokenAddress,
      data
    });
    return Abi.Function.decodeResult(abi[0], result)[0];
  },
  {
    interval: 2000,
    validate: (balance: bigint) => balance >= targetBalance,
    onPoll: (balance, attempt) => {
      console.log(
        `Attempt ${attempt}: ${balance}/${targetBalance}`
      );
    }
  }
);
```

### Wait for Block Number

Wait for specific block with progress:

```typescript theme={null}
async function waitForBlock(targetBlock: bigint): Promise<void> {
  await pollUntil(
    () => provider.eth_blockNumber(),
    (current) => current >= targetBlock,
    {
      interval: 12000, // Average block time
      timeout: 300000, // 5 minutes
      onPoll: (current, attempt) => {
        const remaining = targetBlock - current;
        console.log(
          `Block ${current}/${targetBlock} (${remaining} remaining)`
        );
      }
    }
  );
}
```

### Multiple Conditions

Poll until multiple conditions satisfied:

```typescript theme={null}
await poll(
  async () => {
    const [receipt, blockNumber] = await Promise.all([
      provider.eth_getTransactionReceipt(txHash),
      provider.eth_blockNumber()
    ]);
    return { receipt, blockNumber };
  },
  {
    interval: 1000,
    validate: ({ receipt, blockNumber }) => {
      if (!receipt) return false;
      if (receipt.status !== '0x1') return false;

      const confirmations = blockNumber - receipt.blockNumber;
      return confirmations >= 3;
    }
  }
);
```

## Combining with Other Utils

### Poll with Retry

Retry each poll attempt:

```typescript theme={null}
import { poll, retryWithBackoff } from '@tevm/voltaire/utils';

const data = await poll(
  () => retryWithBackoff(
    () => provider.eth_call({ to, data }),
    { maxRetries: 3 }
  ),
  {
    interval: 1000,
    validate: (result) => result !== '0x'
  }
);
```

### Poll with Timeout Per Attempt

Add timeout to each poll attempt:

```typescript theme={null}
import { poll, withTimeout } from '@tevm/voltaire/utils';

const result = await poll(
  () => withTimeout(
    provider.eth_getBlockByNumber('latest'),
    { ms: 5000 }
  ),
  {
    interval: 1000,
    timeout: 60000
  }
);
```

### Poll with Rate Limiting

Rate limit polling attempts:

```typescript theme={null}
import { poll, RateLimiter } from '@tevm/voltaire/utils';

const limiter = new RateLimiter({
  maxRequests: 10,
  interval: 1000
});

const receipt = await poll(
  () => limiter.execute(
    () => provider.eth_getTransactionReceipt(txHash)
  ),
  {
    interval: 500,
    timeout: 60000
  }
);
```

## Error Handling

### Handle Polling Errors

Errors during polling don't stop the poll:

```typescript theme={null}
try {
  const receipt = await poll(
    () => provider.eth_getTransactionReceipt(txHash),
    { timeout: 60000 }
  );
} catch (error) {
  if (error.message.includes('timeout')) {
    console.log('Transaction not confirmed within 60s');
  }
}
```

### Validation Errors

Distinguish between errors and unmet conditions:

```typescript theme={null}
const block = await poll(
  async () => {
    const block = await provider.eth_getBlockByNumber('latest');
    if (!block) {
      throw new Error('Failed to fetch block');
    }
    return block;
  },
  {
    validate: (block) => block.number >= targetBlock,
    interval: 1000
  }
);
```

## Best Practices

### Choose Appropriate Intervals

* **Transaction receipts**: 1000ms (typical block time \~12s)
* **Block numbers**: 12000ms (matches block time)
* **Contract state**: 2000-5000ms (depends on update frequency)
* **Off-chain data**: 5000-10000ms (depends on API)

### Set Reasonable Timeouts

* **Fast operations**: 30000ms (30s)
* **Transaction confirmation**: 120000ms (2 minutes)
* **Multi-block operations**: 300000ms (5 minutes)

### Use Backoff for Long Operations

Enable backoff when:

* Operation may take minutes
* Checking less frequently over time is acceptable
* Want to reduce server load

### Monitor Progress

Always use `onPoll` callback for observability:

```typescript theme={null}
const receipt = await pollForReceipt(
  txHash,
  (hash) => provider.eth_getTransactionReceipt(hash),
  {
    timeout: 120000,
    onPoll: (receipt, attempt) => {
      logger.info('Polling for receipt', {
        txHash,
        attempt,
        found: receipt !== null
      });
    }
  }
);
```

## API Reference

### poll

```typescript theme={null}
async function poll<T>(
  fn: () => Promise<T>,
  options?: PollOptions<T>
): Promise<T>
```

### pollUntil

```typescript theme={null}
async function pollUntil<T>(
  fn: () => Promise<T>,
  predicate: (result: T) => boolean,
  options?: Omit<PollOptions<T>, 'validate'>
): Promise<T>
```

### pollForReceipt

```typescript theme={null}
async function pollForReceipt<TReceipt>(
  txHash: string,
  getReceipt: (hash: string) => Promise<TReceipt | null>,
  options?: Omit<PollOptions<TReceipt | null>, 'validate'>
): Promise<TReceipt>
```

### pollWithBackoff

```typescript theme={null}
async function pollWithBackoff<T>(
  fn: () => Promise<T>,
  options?: PollOptions<T>
): Promise<T>
```

## See Also

* [Retry](/utils/retry) - Retry failed poll attempts
* [Timeout](/utils/timeout) - Add timeouts to polling
* [Rate Limiting](/utils/rate-limit) - Rate limit poll frequency
