# How to Wait for Transaction Receipts Using `waitForTransactionReceipt` in GenLayer

> Learn how to wait for transaction receipts with GenLayer's `waitForTransactionReceipt`. This function polls the blockchain to get receipt status, gas usage, and return data.

- Repository: [GenLayer Labs/genlayer-project-boilerplate](https://github.com/genlayerlabs/genlayer-project-boilerplate)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Use `client.waitForTransactionReceipt({ txHash, timeout?, pollInterval? })` to poll the GenLayer blockchain until a transaction is mined and returns a `TransactionReceipt` with status, gas usage, and return data.**

The `genlayer-js` SDK provides this method on any client instance created with `createClient()`. It is the standard pattern for confirming that write operations—such as creating bets, resolving outcomes, or deploying contracts—have been finalized on-chain before your application proceeds. This article walks through concrete implementations found in the `genlayerlabs/genlayer-project-boilerplate` repository.

## When to Use `waitForTransactionReceipt`

Any time you invoke a state-changing operation on a GenLayer contract, you receive a `txHash` immediately but must wait for miners to process the transaction. Waiting ensures:

- The transaction succeeded (`status === 1`) or failed (`status === 0`)
- You have access to return values, gas usage, and block number
- Subsequent operations or UI updates depend on confirmed state

The boilerplate demonstrates this pattern in three production contexts.

## Creating a Bet and Waiting for Confirmation

In [`frontend/lib/contracts/FootballBets.ts`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/frontend/lib/contracts/FootballBets.ts), the `createBet` method submits a bet to the `FootballBets` contract and blocks until receipt. This prevents UI race conditions where a user might refresh before the bet is recorded.

```typescript
// FootballBets.ts line 210 (excerpt)
const txHash = await this.client.writeContract({
  address: this.contractAddress,
  functionName: "create_bet",
  args: [gameDate, team1, team2, predictedWinner],
});

const receipt = await this.client.waitForTransactionReceipt({
  txHash,
  timeout: 60_000,      // 60 seconds maximum wait
  pollInterval: 2_000,  // check every 2 seconds
});

if (receipt.status === 0) {
  throw new Error("Bet creation failed");
}
// Receipt contains: status, gasUsed, blockNumber, returnData, etc.

```

The `timeout` and `pollInterval` parameters are optional. Omitting them uses SDK defaults (typically 2-second polling with a reasonable timeout).

## Resolving a Bet and Confirming Finality

The same pattern appears in `resolveBet` at line 241, where the application must confirm that a bet resolution is immutable before updating user balances.

```typescript
// FootballBets.ts line 241 (excerpt)
const txHash = await this.client.writeContract({
  address: this.contractAddress,
  functionName: "resolve_bet",
  args: [betId],
});

const receipt = await this.client.waitForTransactionReceipt({
  txHash,
});

if (receipt.status === 0) {
  throw new Error("Bet resolution failed");
}
// Safe to proceed with UI refresh or dependent calls

```

Notice this call uses default timing parameters. Choose explicit values when your UX demands faster feedback or longer tolerance for network congestion.

## Deploying Contracts and Capturing the Address

The deployment script at [`deploy/deployScript.ts`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/deploy/deployScript.ts) uses `waitForTransactionReceipt` to obtain the deployed contract address—critical for subsequent contract interactions.

```typescript
// deployScript.ts line 25 (excerpt)
const deployment = await client.deployContract({
  abi,
  bytecode,
  constructorArgs: [],
});

const receipt = await client.waitForTransactionReceipt({
  txHash: deployment.txHash,
});

console.log("Contract deployed at:", receipt.contractAddress);

```

Without awaiting the receipt, the script would exit before the deployment transaction is mined, leaving no reliable way to reference the new contract.

## Understanding the `waitForTransactionReceipt` Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `txHash` | `string` | Yes | The transaction hash returned from `writeContract`, `deployContract`, or similar |
| `timeout` | `number` (ms) | No | Maximum time to wait before throwing an error |
| `pollInterval` | `number` (ms) | No | Polling frequency; default ~2000ms |

The method returns a `Promise<TransactionReceipt>` with these key fields:

- **`status`**: `1` for success, `0` for failure
- **`gasUsed`**: Gas consumed by the transaction
- **`blockNumber`**: Block where the transaction was included
- **`contractAddress`**: Deployed address (for deployments only)
- **`returnData`**: ABI-encoded return values from the contract method

## Client Setup Prerequisites

`waitForTransactionReceipt` is available on any client created through `genlayer-js`. The boilerplate configures this in [`frontend/lib/genlayer/client.ts`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/frontend/lib/genlayer/client.ts):

```typescript
import { createClient, simulator } from "genlayer-js";

export const client = createClient({
  chain: simulator,
  endpoint: process.env.NEXT_PUBLIC_GENLAYER_RPC,
  account: {
    address: "0x...", // or derived from private key
    privateKey: "0x...",
  },
});

```

The client exposes `waitForTransactionReceipt` as a bound method, so it inherits your configured endpoint and account context automatically.

## Error Handling Patterns

Always inspect `receipt.status` before proceeding. A failed transaction consumes gas but produces no state changes. Common handling patterns include:

```typescript
const receipt = await client.waitForTransactionReceipt({ txHash });

if (receipt.status === 0) {
  // Log for debugging, alert user, or trigger rollback
  console.error("Transaction failed:", txHash);
  throw new Error(`Transaction failed: ${receipt.errorMessage || "unknown"}`);
}

// Transaction confirmed—safe to use receipt.returnData
const [betId] = decodeReturnData(receipt.returnData, "uint256");

```

For deployment failures, check that `receipt.contractAddress` is defined and valid.

## Summary

- **`waitForTransactionReceipt` is the polling abstraction** in `genlayer-js` that converts a `txHash` into a confirmed `TransactionReceipt`
- **Three canonical use cases** appear in the boilerplate: bet creation (`FootballBets.ts:210`), bet resolution (`FootballBets.ts:241`), and contract deployment (`deployScript.ts:25`)
- **Always verify `receipt.status === 1`** before treating operations as successful
- **Tune `timeout` and `pollInterval`** based on expected block times and your application's responsiveness requirements
- **The method works identically** in browser and Node.js environments through the same `createClient` configuration

## Frequently Asked Questions

### What happens when `waitForTransactionReceipt` times out?

The promise rejects with an error indicating the timeout was exceeded. Wrap the call in `try/catch` to handle network congestion or stalled transactions gracefully. You may then choose to retry, alert the user, or mark the operation as pending for later reconciliation.

### Can I use `waitForTransactionReceipt` for read-only calls?

No. Read-only methods like `readContract` return data immediately without generating a transaction hash. `waitForTransactionReceipt` requires a `txHash` from a state-changing operation such as `writeContract` or `deployContract`.

### Does polling with `waitForTransactionReceipt` consume rate limits or incur costs?

Polling is a client-side operation that queries the RPC endpoint. It does not consume gas or submit transactions. However, frequent polling against a hosted endpoint may count toward rate limits—tune `pollInterval` accordingly, especially in production deployments.

### How do I extract custom return values from the receipt?

Decode `receipt.returnData` using the same ABI as your contract. The `genlayer-js` package provides utility functions compatible with viem-style encoding, or you can use standard Ethereum ABI decoding libraries to parse the byte array into typed values.