Skip to content

Transaction Management

HumanWallet provides functions for monitoring and managing blockchain transactions and user operations. These functions help you track transaction status and wait for confirmations.

Functions Overview

waitForTransaction()

Waits for a regular blockchain transaction to be confirmed and included in a block.

Function Signature

function waitForTransaction(
  config: Config,
  hash: string,
  options?: WaitForTransactionOptions,
): Promise<TransactionReceipt>

Parameters

config

  • Type: Config
  • Description: Configuration object from createConfig()

hash

  • Type: string
  • Description: Transaction hash to wait for

options (optional)

  • Type: WaitForTransactionOptions
  • Properties:
    • timeout: Maximum time to wait in milliseconds (default: 60000)
    • confirmations: Number of confirmations to wait for (default: 1)

Usage Example

import { writeContract, waitForTransaction } from "@humanwallet/core"
 
// Execute a transaction
const hash = await writeContract(authenticatedConfig, {
  address: "0x...",
  abi: tokenAbi,
  functionName: "transfer",
  args: ["0x742d35Cc6634C0532925a3b8D52ECC5c5e9fA008", BigInt("1000000000000000000")],
})
 
console.log("Transaction submitted:", hash)
 
try {
  // Wait for confirmation
  const receipt = await waitForTransaction(config, hash)
 
  console.log("Transaction confirmed!")
  console.log("Block number:", receipt.blockNumber)
  console.log("Gas used:", receipt.gasUsed.toString())
  console.log("Status:", receipt.status) // 'success' or 'reverted'
} catch (error) {
  console.error("Transaction failed or timed out:", error)
}

Advanced Usage

With Custom Timeout and Confirmations

// Wait for 3 confirmations with 2 minute timeout
const receipt = await waitForTransaction(config, hash, {
  timeout: 120000, // 2 minutes
  confirmations: 3,
})
 
console.log("Transaction confirmed with 3 confirmations")

Error Handling

try {
  const receipt = await waitForTransaction(config, hash)
 
  if (receipt.status === "reverted") {
    console.log("Transaction was reverted")
    // Handle revert case
  } else {
    console.log("Transaction successful")
  }
} catch (error) {
  if (error.message.includes("timeout")) {
    console.log("Transaction timed out - may still be pending")
  } else if (error.message.includes("not found")) {
    console.log("Transaction not found")
  } else {
    console.error("Unexpected error:", error)
  }
}

waitForUserOperation()

Waits for an account abstraction user operation to be completed. This is used for gasless transactions executed through HumanWallet.

Function Signature

function waitForUserOperation(
  config: Config,
  hash: string,
  options?: WaitForUserOperationOptions,
): Promise<UserOperationReceipt>

Parameters

config

  • Type: Config
  • Description: Configuration object from createConfig()

hash

  • Type: string
  • Description: User operation hash to wait for

options (optional)

  • Type: WaitForUserOperationOptions
  • Properties:
    • timeout: Maximum time to wait in milliseconds (default: 60000)

Usage Example

import { writeContract, waitForUserOperation } from "@humanwallet/core"
 
// Execute a gasless transaction (returns user operation hash)
const userOpHash = await writeContract(authenticatedConfig, {
  address: "0x...",
  abi: contractAbi,
  functionName: "mint",
  args: [sessionKeyAccount.address, 1],
})
 
console.log("User operation submitted:", userOpHash)
 
try {
  // Wait for user operation completion
  const receipt = await waitForUserOperation(config, userOpHash)
 
  console.log("User operation completed!")
  console.log("Actual transaction hash:", receipt.transactionHash)
  console.log("Block number:", receipt.blockNumber)
  console.log("Success:", receipt.success)
} catch (error) {
  console.error("User operation failed:", error)
}

User Operation Receipt

The UserOperationReceipt contains:

interface UserOperationReceipt {
  transactionHash: string // The actual on-chain transaction hash
  blockNumber: bigint // Block number where it was included
  success: boolean // Whether the operation succeeded
  gasUsed: bigint // Gas consumed by the operation
  logs: Log[] // Event logs emitted
}

Transaction Flow Examples

Complete Transaction Flow

async function executeAndWaitForTransaction(contractParams: WriteContractParameters) {
  try {
    // 1. Submit transaction
    console.log("Submitting transaction...")
    const hash = await writeContract(authenticatedConfig, contractParams)
 
    // 2. Show pending state to user
    console.log("Transaction pending:", hash)
 
    // 3. Wait for confirmation
    console.log("Waiting for confirmation...")
    const receipt = await waitForUserOperation(config, hash)
 
    // 4. Handle result
    if (receipt.success) {
      console.log("✅ Transaction successful!")
      console.log("Transaction hash:", receipt.transactionHash)
      return receipt
    } else {
      console.log("❌ Transaction failed")
      throw new Error("Transaction reverted")
    }
  } catch (error) {
    console.error("Transaction error:", error)
    throw error
  }
}

Batch Transaction Monitoring

async function executeBatchAndMonitor(operations: WriteContractParameters[]) {
  try {
    // Execute batch
    const batchHash = await writeContracts(authenticatedConfig, operations)
    console.log("Batch submitted:", batchHash)
 
    // Monitor progress
    const receipt = await waitForUserOperation(config, batchHash, {
      timeout: 120000, // 2 minutes for batch operations
    })
 
    if (receipt.success) {
      console.log("All operations in batch succeeded")
 
      // Parse logs to see individual operation results
      receipt.logs.forEach((log, index) => {
        console.log(`Operation ${index + 1} log:`, log)
      })
    } else {
      console.log("Batch failed - some operations reverted")
    }
 
    return receipt
  } catch (error) {
    console.error("Batch execution failed:", error)
    throw error
  }
}

React Integration Examples

Transaction Status Hook

import { useState, useEffect } from "react"
import { waitForUserOperation } from "@humanwallet/core"
 
export function useTransactionStatus(config: Config, hash: string | null) {
  const [status, setStatus] = useState<"pending" | "confirmed" | "failed" | null>(null)
  const [receipt, setReceipt] = useState(null)
 
  useEffect(() => {
    if (!hash) return
 
    setStatus("pending")
 
    waitForUserOperation(config, hash)
      .then((receipt) => {
        setReceipt(receipt)
        setStatus(receipt.success ? "confirmed" : "failed")
      })
      .catch((error) => {
        console.error("Transaction monitoring failed:", error)
        setStatus("failed")
      })
  }, [config, hash])
 
  return { status, receipt }
}

Transaction Component

import React from 'react'
import { useTransactionStatus } from './useTransactionStatus'
 
interface TransactionStatusProps {
  config: Config
  hash: string
  onComplete?: (receipt: any) => void
}
 
export function TransactionStatus({ config, hash, onComplete }: TransactionStatusProps) {
  const { status, receipt } = useTransactionStatus(config, hash)
 
  useEffect(() => {
    if (status === 'confirmed' && receipt && onComplete) {
      onComplete(receipt)
    }
  }, [status, receipt, onComplete])
 
  return (
    <div className="transaction-status">
      {status === 'pending' && (
        <div>
          <span>⏳ Transaction pending...</span>
          <div>Hash: {hash}</div>
        </div>
      )}
 
      {status === 'confirmed' && (
        <div>
          <span>✅ Transaction confirmed!</span>
          <div>Block: {receipt?.blockNumber.toString()}</div>
        </div>
      )}
 
      {status === 'failed' && (
        <div>
          <span>❌ Transaction failed</span>
          <div>Please try again</div>
        </div>
      )}
    </div>
  )
}

Error Handling

Timeout Handling

async function waitWithTimeout(hash: string, timeoutMs: number = 60000) {
  try {
    const receipt = await waitForUserOperation(config, hash, {
      timeout: timeoutMs,
    })
    return receipt
  } catch (error) {
    if (error.message.includes("timeout")) {
      // Transaction might still be pending
      console.log("Transaction timed out but may still complete")
 
      // Optionally, continue monitoring with longer timeout
      return waitForUserOperation(config, hash, {
        timeout: timeoutMs * 2,
      })
    }
    throw error
  }
}

Network Error Recovery

async function waitWithRetry(hash: string, maxRetries: number = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await waitForUserOperation(config, hash)
    } catch (error) {
      console.log(`Attempt ${attempt} failed:`, error.message)
 
      if (attempt === maxRetries) {
        throw new Error(`Failed to confirm transaction after ${maxRetries} attempts`)
      }
 
      // Wait before retry
      await new Promise((resolve) => setTimeout(resolve, 5000 * attempt))
    }
  }
}

Best Practices

Progress Feedback

async function executeWithProgress(contractParams: WriteContractParameters) {
  // 1. Show submission state
  console.log("🚀 Submitting transaction...")
 
  const hash = await writeContract(authenticatedConfig, contractParams)
 
  // 2. Show pending state
  console.log("⏳ Transaction submitted, waiting for confirmation...")
  console.log("Hash:", hash)
 
  // 3. Monitor with timeout
  const receipt = await waitForUserOperation(config, hash, {
    timeout: 90000, // 1.5 minutes
  })
 
  // 4. Show final result
  if (receipt.success) {
    console.log("✅ Transaction confirmed!")
  } else {
    console.log("❌ Transaction failed")
  }
 
  return receipt
}

Batch Monitoring

async function monitorBatchExecution(batchHash: string) {
  console.log("Monitoring batch execution...")
 
  const startTime = Date.now()
 
  const receipt = await waitForUserOperation(config, batchHash, {
    timeout: 180000, // 3 minutes for complex batches
  })
 
  const duration = Date.now() - startTime
  console.log(`Batch completed in ${duration}ms`)
 
  if (receipt.success) {
    console.log(`✅ All ${receipt.logs.length} operations succeeded`)
  } else {
    console.log("❌ Batch failed - check individual operations")
  }
 
  return receipt
}

Gas Usage Tracking

async function trackGasUsage(operations: WriteContractParameters[]) {
  const startTime = Date.now()
 
  // Execute operations
  const hashes = await Promise.all(operations.map((op) => writeContract(authenticatedConfig, op)))
 
  // Wait for all confirmations
  const receipts = await Promise.all(hashes.map((hash) => waitForUserOperation(config, hash)))
 
  // Calculate metrics
  const totalGasUsed = receipts.reduce((sum, receipt) => sum + receipt.gasUsed, 0n)
  const successfulOps = receipts.filter((r) => r.success).length
  const duration = Date.now() - startTime
 
  console.log("Gas usage summary:", {
    totalOperations: operations.length,
    successfulOperations: successfulOps,
    totalGasUsed: totalGasUsed.toString(),
    averageGasPerOp: (totalGasUsed / BigInt(receipts.length)).toString(),
    executionTime: `${duration}ms`,
  })
 
  return receipts
}