From 612458a307231aacf6e470c6f5cba9ed383519c8 Mon Sep 17 00:00:00 2001 From: admin Date: Sun, 1 Feb 2026 00:42:25 -0800 Subject: [PATCH 1/2] docs: Add comprehensive agent lifecycle states documentation Addresses #900 This commit adds detailed documentation explaining the common lifecycle states that agents go through when using AgentKit across different AI frameworks (LangChain, Vercel AI SDK, OpenAI Agents SDK, PydanticAI, and Strands Agents). Key additions: - AGENT-LIFECYCLE.md: Comprehensive guide covering initialization, execution loop, state persistence, and framework-specific behaviors - README.md: Added link to new lifecycle documentation in the Documentation section The documentation enables developers to: - Understand agent execution flow without inspecting internal code - Debug agent behavior at different lifecycle stages - Implement proper error handling and state management - Choose appropriate frameworks for their use cases Co-Authored-By: Claude Sonnet 4.5 --- AGENT-LIFECYCLE.md | 379 +++++++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 380 insertions(+) create mode 100644 AGENT-LIFECYCLE.md diff --git a/AGENT-LIFECYCLE.md b/AGENT-LIFECYCLE.md new file mode 100644 index 000000000..d57f57124 --- /dev/null +++ b/AGENT-LIFECYCLE.md @@ -0,0 +1,379 @@ +# Agent Lifecycle States + +This document describes the common lifecycle states that agents go through when using AgentKit with various AI frameworks. + +## Table of Contents + +- [Overview](#overview) +- [Lifecycle Phases](#lifecycle-phases) + - [1. Initialization Phase](#1-initialization-phase) + - [2. Ready State](#2-ready-state) + - [3. Execution Loop](#3-execution-loop) + - [4. State Persistence](#4-state-persistence) +- [Framework-Specific Behaviors](#framework-specific-behaviors) +- [Error Handling and Recovery](#error-handling-and-recovery) +- [Best Practices](#best-practices) + +## Overview + +AgentKit is a framework-agnostic toolkit that provides blockchain actions to AI agents. While AgentKit itself handles action execution, the agent's overall lifecycle is managed by the AI framework you choose (LangChain, OpenAI Agents SDK, Vercel AI SDK, PydanticAI, or Strands Agents). + +Understanding these lifecycle states helps you: +- Debug agent behavior without inspecting internal code +- Implement proper error handling +- Design better user experiences +- Optimize performance and resource usage + +## Lifecycle Phases + +### 1. Initialization Phase + +The initialization phase prepares your agent to execute blockchain actions. This is a one-time setup that occurs before the agent can process requests. + +#### States in Initialization: + +**1.1 Pre-Initialization** +- Environment variables validated (API keys, secrets) +- Wallet provider type selected (CDP, Smart Wallet, Privy, Viem, Solana) +- Network configuration determined (Base, Ethereum, Solana, etc.) + +**1.2 AgentKit Creation** +```typescript +// TypeScript example +const agentkit = await AgentKit.from({ + cdpApiKeyId: process.env.CDP_API_KEY_ID, + cdpApiKeySecret: process.env.CDP_API_KEY_SECRET, + cdpWalletSecret: process.env.CDP_WALLET_SECRET, + actionProviders: [walletActionProvider(), erc20ActionProvider()], +}); +``` + +```python +# Python example +agentkit = AgentKit( + wallet_provider=wallet_provider, + action_providers=[wallet_action_provider(), erc20_action_provider()], +) +``` + +During this state: +- Wallet provider initializes with credentials +- Action providers register their available actions +- Network compatibility validated for each action provider + +**1.3 Tool Extraction** + +AgentKit actions are converted to framework-specific tools: +- **LangChain**: `getLangChainTools()` → `StructuredTool[]` +- **Vercel AI SDK**: `getVercelAITools()` → `ToolSet` +- **OpenAI Agents SDK**: `get_openai_agents_sdk_tools()` → `FunctionTool[]` +- **PydanticAI**: `get_pydantic_ai_tools()` → `Tool[]` +- **Strands Agents**: `get_strands_tools()` → Agent tools + +**1.4 Agent Creation** + +The framework creates an agent instance with: +- LLM model initialized +- Tools/actions registered +- System prompt configured +- Memory/checkpoint system set up (if stateful) + +At the end of initialization, the agent transitions to the **Ready** state. + +### 2. Ready State + +The agent is idle and waiting for input. In this state: +- All tools are available and validated +- Memory/conversation history is initialized +- The agent can accept user input or autonomous triggers +- No blockchain transactions are being executed + +This is a stable, low-resource state where the agent remains between interactions. + +### 3. Execution Loop + +When the agent receives input, it enters a multi-step execution loop. This is the core operational phase where the agent reasons about and executes blockchain actions. + +``` +┌─────────────────────────────────────────────────────────┐ +│ 1. REASONING (LLM analyzing request) │ +│ - Agent examines available tools │ +│ - LLM determines action plan │ +│ - Thought/intermediate steps emitted │ +└─────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────┐ +│ 2. TOOL_SELECTION (Agent choosing action) │ +│ - Tool selected based on reasoning │ +│ - Arguments validated against schema │ +│ - Tool invocation prepared │ +└─────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────┐ +│ 3. EXECUTING (Tool running) │ +│ - action.invoke() called │ +│ - Blockchain transaction/API call executed │ +│ - Result formatted as string │ +└─────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────┐ +│ 4. OBSERVATION (Result processing) │ +│ - Tool output returned to agent │ +│ - LLM reads result │ +│ - Decides next action or conclude │ +└─────────────────────────────────────────────────────────┘ + ↓ + ┌────┴────┐ + ↓ ↓ + ┌──────────┐ ┌──────────────┐ + │ CONTINUE │ │ COMPLETE │ + │ LOOP? │ │ │ + └──────────┘ └──────────────┘ + ↓ ↓ + (return to step 1) │ + ↓ +┌─────────────────────────────────────────────────────────┐ +│ 5. RESPONDING (Final output) │ +│ - Agent generates final response │ +│ - Message added to history │ +│ - Output streamed/returned to caller │ +└─────────────────────────────────────────────────────────┘ + ↓ + [Return to READY state] +``` + +#### Key Characteristics: + +- **Reasoning State**: The LLM analyzes the user's request and available actions. No blockchain transactions occur here. + +- **Tool Selection State**: The agent chooses which action to execute and validates the arguments against the action's schema. Schema validation prevents malformed requests from reaching the blockchain. + +- **Executing State**: This is where actual blockchain transactions or API calls happen. The `action.invoke()` method is called with validated arguments. This state may take several seconds for on-chain transactions. + +- **Observation State**: The agent receives the result and decides whether to execute additional actions or provide a final response. This enables multi-step workflows (e.g., "check balance, then transfer funds"). + +- **Iteration Control**: Frameworks implement different limits: + - **Vercel AI SDK**: `maxSteps` parameter (default: 10) + - **LangChain**: No hard limit, relies on agent's reasoning + - **OpenAI Agents SDK**: Configurable via agent settings + +### 4. State Persistence + +Stateful agents maintain context between interactions. Different frameworks handle persistence differently: + +#### Conversation Memory + +**LangChain** +- Uses `MemorySaver` as an in-memory checkpointer +- Requires `thread_id` in config to maintain conversation context +- Persists message history across turns + +```typescript +const config = { configurable: { thread_id: "user-123" } }; +const stream = await agent.stream({ messages: [...] }, config); +``` + +**Vercel AI SDK** +- Maintains in-memory `Message[]` array +- Application manages message history persistence +- Each call includes full conversation history + +```typescript +const messages = [ + { role: "user", content: "What's my balance?" }, + { role: "assistant", content: "..." }, + // ... continues +]; +``` + +**PydanticAI** +- Returns message history via `output.all_messages()` +- Application passes history to subsequent runs + +```python +output = await agent.run(prompt, message_history=history) +history = output.all_messages() +``` + +#### Wallet Persistence + +Examples typically save wallet data to files for multi-session continuity: +- File format: `wallet_data_*.txt` or `wallet_data_*.json` +- Contains: address, network_id, wallet secrets +- Enables the agent to use the same wallet across sessions + +## Framework-Specific Behaviors + +Different AI frameworks implement the execution loop with unique patterns: + +### LangChain + +**Pattern**: ReAct (Reasoning + Acting) with streaming + +```typescript +const agent = createReactAgent({ llm, tools, checkpointSaver }); +const stream = await agent.stream({ messages: [...] }, config); + +for await (const chunk of stream) { + if ("agent" in chunk) { + // Agent is reasoning + } else if ("tools" in chunk) { + // Tools are executing + } +} +``` + +**States**: Clearly separated reasoning and tool execution phases with streaming updates. + +### Vercel AI SDK + +**Pattern**: Streaming text generation with tool calls + +```typescript +const stream = streamText({ + model, + messages, + tools, + system: "...", + maxSteps: 10, +}); +``` + +**States**: Integrated reasoning and execution with automatic iteration control via `maxSteps`. + +### OpenAI Agents SDK + +**Pattern**: Built-in runner with execution loop + +```python +agent = Agent( + name="...", + instructions="...", + tools=tools, + model="gpt-4o", +) +# Uses OpenAI's native agent execution +``` + +**States**: Managed by OpenAI's agents runtime with automatic retry and error handling. + +### PydanticAI + +**Pattern**: Async-first with type-annotated results + +```python +output = await agent.run(prompt, message_history=history) +result = output.data # Type-safe result +history = output.all_messages() +``` + +**States**: Type-safe execution with explicit message history management. + +### Strands Agents + +**Pattern**: Similar to OpenAI SDK with AWS Bedrock integration + +**States**: Supports payment-gated actions via x402 protocol with service discovery. + +## Error Handling and Recovery + +Agents can encounter errors at various lifecycle states. Understanding these helps implement proper error handling: + +### Schema Validation Errors + +**State**: Tool Selection +**Cause**: Arguments don't match the action's schema +**Recovery**: Frameworks typically show the error to the LLM, which then retries with corrected arguments + +### Execution Errors + +**State**: Executing +**Cause**: Blockchain transaction fails, network errors, insufficient funds +**Recovery**: +- Error message returned to agent as observation +- Agent can retry with different parameters +- Application may implement exponential backoff for network errors + +Example from swap utilities: +```python +async def retry_with_exponential_backoff( + func, max_attempts=3, initial_delay=1.0 +): + """Retry function with exponential backoff""" + # Implements retry logic for transient failures +``` + +### Iteration Limit Reached + +**State**: Execution Loop +**Cause**: Agent exceeds `maxSteps` or equivalent limit +**Recovery**: +- Execution stops, returning partial results +- Agent may summarize what was accomplished +- Prevents infinite loops + +### Network/Action Compatibility Errors + +**State**: Initialization +**Cause**: Action provider doesn't support current network +**Recovery**: +- Warning logged during initialization +- Action unavailable to agent +- Agent continues with supported actions + +## Best Practices + +### For Initialization + +1. **Validate Early**: Check environment variables before creating AgentKit +2. **Choose Network Carefully**: Some actions only work on specific networks (e.g., EVM vs Solana) +3. **Log Warnings**: Pay attention to action provider compatibility warnings +4. **Persist Wallet Data**: Save wallet information for session continuity + +### For Execution + +1. **Set Iteration Limits**: Use `maxSteps` or equivalent to prevent runaway execution +2. **Monitor Execution Time**: Blockchain transactions can take seconds; design UI accordingly +3. **Stream Updates**: Use streaming where available for better user experience +4. **Handle Async Operations**: Many actions are async; ensure proper await/promise handling + +### For State Management + +1. **Use Thread IDs**: Maintain separate conversation contexts for different users +2. **Clean Up Memory**: Clear old conversation histories to prevent memory bloat +3. **Backup Wallet Secrets**: Store wallet data securely for recovery +4. **Log State Transitions**: Track which lifecycle state caused issues for debugging + +### For Error Handling + +1. **Show Errors to Agent**: Let the LLM see error messages to enable self-correction +2. **Implement Retries**: Use exponential backoff for network errors +3. **Set Timeouts**: Don't wait indefinitely for blockchain confirmations +4. **Graceful Degradation**: If an action fails, agent should explain and suggest alternatives + +## Debugging Agent Behavior + +When debugging, identify which lifecycle state is causing issues: + +| Symptom | Likely State | Investigation Steps | +|---------|--------------|---------------------| +| Agent won't start | Initialization | Check environment variables, API keys, network config | +| Agent doesn't respond | Ready → Reasoning | Verify LLM API connectivity, check rate limits | +| Wrong action chosen | Tool Selection | Review system prompt, check available tools | +| Transaction fails | Executing | Check wallet balance, network status, transaction params | +| Agent loops infinitely | Execution Loop | Set/lower `maxSteps`, review agent instructions | +| Lost conversation context | State Persistence | Verify thread_id usage, check memory saver config | + +## Conclusion + +AgentKit provides a consistent action execution interface across different AI frameworks. While the framework manages the overall agent lifecycle (reasoning, tool selection, execution), AgentKit ensures reliable blockchain action execution during the **Executing** state. + +By understanding these lifecycle states, you can build more robust agents, debug issues effectively, and create better user experiences when building onchain AI applications. + +## References + +- [AgentKit Documentation](https://docs.cdp.coinbase.com/agentkit/docs/welcome) +- [LangChain AgentKit Extension](https://github.com/coinbase/agentkit/tree/main/typescript/framework-extensions/langchain) +- [Vercel AI SDK Extension](https://github.com/coinbase/agentkit/tree/main/typescript/framework-extensions/vercel-ai-sdk) +- [OpenAI Agents SDK Extension](https://github.com/coinbase/agentkit/tree/main/python/framework-extensions/openai-agents-sdk) +- [PydanticAI Extension](https://github.com/coinbase/agentkit/tree/main/python/framework-extensions/pydantic-ai) diff --git a/README.md b/README.md index 0e39ca4a0..d16b17e50 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,7 @@ agentkit/ ## 📜 Documentation - [AgentKit Documentation](https://docs.cdp.coinbase.com/agentkit/docs/welcome) +- [Agent Lifecycle States](./AGENT-LIFECYCLE.md) - Understanding agent execution flow and states - Python API References - [AgentKit](https://coinbase.github.io/agentkit/coinbase-agentkit/python/index.html) - [AgentKit Langchain Extension](https://coinbase.github.io/agentkit/coinbase-agentkit-langchain/python/index.html) From 8ec1585fdb9c6876ecd640151dea0a6e691311fe Mon Sep 17 00:00:00 2001 From: Neekoras Date: Thu, 5 Feb 2026 23:27:20 -0800 Subject: [PATCH 2/2] docs: Add retry and timeout handling documentation Add comprehensive documentation for handling retries and timeouts to both Python and TypeScript package READMEs. This documentation will be included in the official docs site. Includes: - Built-in retry utilities with exponential backoff - Practical examples for token transfers and API calls - Transaction confirmation polling patterns - Custom retry logic implementations - Timeout handling with AbortController (TS) and requests (Python) - Best practices and recommended timeout values Fixes coinbase/agentkit#909 Co-Authored-By: Claude Sonnet 4.5 --- python/coinbase-agentkit/README.md | 194 +++++++++++++++++++++++++ typescript/agentkit/README.md | 223 +++++++++++++++++++++++++++++ 2 files changed, 417 insertions(+) diff --git a/python/coinbase-agentkit/README.md b/python/coinbase-agentkit/README.md index 39165638d..3ce0a0b00 100644 --- a/python/coinbase-agentkit/README.md +++ b/python/coinbase-agentkit/README.md @@ -1101,6 +1101,200 @@ agentkit = AgentKit(AgentKitConfig( # - Signing messages: wallet_provider.sign_message(message) ``` +## Handling Retries and Timeouts + +When building AI agents that interact with blockchain networks and APIs, it's important to handle transient failures gracefully. AgentKit provides utilities and patterns for implementing retry logic and timeout handling. + +### Retry Strategies + +For operations that may fail due to temporary network issues or rate limiting, implement retry logic with exponential backoff: + +```python +import time +from typing import TypeVar, Callable + +T = TypeVar('T') + +def retry_with_exponential_backoff( + fn: Callable[[], T], + max_retries: int = 3, + base_delay: float = 1.0, + initial_delay: float = 0.0 +) -> T: + """ + Retry a function with exponential backoff. + + Args: + fn: Function to retry + max_retries: Maximum number of retries (default: 3) + base_delay: Base delay in seconds between retries (default: 1.0) + initial_delay: Initial delay before first attempt (default: 0.0) + + Returns: + Result from the function + + Raises: + Exception: The last error if all retries fail + """ + if initial_delay > 0: + time.sleep(initial_delay) + + last_error = None + for attempt in range(max_retries + 1): + try: + return fn() + except Exception as error: + last_error = error + + if attempt == max_retries: + raise last_error + + # Calculate delay with exponential backoff: base_delay * 2^attempt + delay = base_delay * (2 ** attempt) + time.sleep(delay) + + raise last_error +``` + +### Example: Retrying Token Transfers + +```python +from coinbase_agentkit import AgentKit, AgentKitConfig + +agent_kit = AgentKit(AgentKitConfig( + wallet_provider=wallet_provider, + action_providers=[wallet_action_provider()], +)) + +# Retry a transfer operation +def transfer_with_retry(to: str, amount: str) -> str: + """Transfer tokens with automatic retry on transient failures.""" + return retry_with_exponential_backoff( + lambda: agent_kit.run( + action="transfer", + params={ + "to": to, + "amount": amount, + "asset_id": "eth" + } + ), + max_retries=3, + base_delay=2.0 + ) + +try: + result = transfer_with_retry( + "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", + "0.01" + ) + print(f"Transfer successful: {result}") +except Exception as e: + print(f"Transfer failed after retries: {e}") +``` + +### Handling Timeouts + +For HTTP requests and API calls, always set appropriate timeouts: + +```python +import requests + +def fetch_with_timeout(url: str, timeout: int = 30) -> dict: + """ + Fetch data from an API with timeout. + + Args: + url: API endpoint URL + timeout: Timeout in seconds (default: 30) + + Returns: + JSON response data + """ + response = requests.get(url, timeout=timeout) + response.raise_for_status() + return response.json() + +# Example: Fetch token price with timeout +try: + price_data = retry_with_exponential_backoff( + lambda: fetch_with_timeout("https://api.example.com/price/eth", timeout=30), + max_retries=3, + base_delay=1.0 + ) +except requests.Timeout: + print("Request timed out after 30 seconds") +except Exception as e: + print(f"Failed to fetch price: {e}") +``` + +### Transaction Confirmation Polling + +When waiting for blockchain transaction confirmations, implement polling with a timeout: + +```python +import time + +def wait_for_transaction( + wallet_provider, + tx_hash: str, + timeout_seconds: int = 120, + poll_interval: int = 5 +) -> dict: + """ + Wait for a transaction to be confirmed. + + Args: + wallet_provider: Wallet provider instance + tx_hash: Transaction hash to monitor + timeout_seconds: Maximum time to wait (default: 120) + poll_interval: Seconds between checks (default: 5) + + Returns: + Transaction receipt + + Raises: + TimeoutError: If transaction is not confirmed within timeout + """ + start_time = time.time() + + while time.time() - start_time < timeout_seconds: + try: + receipt = wallet_provider.get_transaction_receipt(tx_hash) + if receipt: + return receipt + except Exception as error: + print(f"Error checking transaction: {error}") + + time.sleep(poll_interval) + + raise TimeoutError( + f"Transaction {tx_hash} not confirmed after {timeout_seconds}s" + ) +``` + +### Best Practices + +**When to use retries:** +- ✅ Network connectivity issues +- ✅ Rate limiting (HTTP 429) +- ✅ Temporary service unavailability (HTTP 503) +- ✅ RPC endpoint failures +- ❌ Invalid parameters (HTTP 400) +- ❌ Authentication failures (HTTP 401, 403) +- ❌ Insufficient balance errors + +**Recommended timeout values:** +- **Fast operations** (< 30s): Balance checks, address lookups +- **Medium operations** (30-90s): Token transfers, NFT minting +- **Long operations** (90-300s): Smart contract deployments, complex DeFi interactions + +**Error handling tips:** +- Always log retry attempts for debugging +- Set maximum retry limits to prevent infinite loops +- Use exponential backoff to avoid overwhelming services +- Implement circuit breakers for repeated failures +- Provide fallback behavior when operations fail + ## Contributing See [CONTRIBUTING.md](https://github.com/coinbase/agentkit/blob/main/CONTRIBUTING.md) for more information. diff --git a/typescript/agentkit/README.md b/typescript/agentkit/README.md index f965c6c1b..ac2969d6f 100644 --- a/typescript/agentkit/README.md +++ b/typescript/agentkit/README.md @@ -1615,6 +1615,229 @@ const walletData = await walletProvider.exportWallet(); } ``` +## Handling Retries and Timeouts + +When building AI agents that interact with blockchain networks and APIs, it's important to handle transient failures gracefully. AgentKit provides built-in utilities for implementing retry logic and timeout handling. + +### Retry with Exponential Backoff + +AgentKit includes a `retryWithExponentialBackoff` utility function in `src/utils.ts`: + +```typescript +import { retryWithExponentialBackoff } from '@coinbase/agentkit'; + +// Basic usage with default settings (3 retries, 1000ms base delay) +const result = await retryWithExponentialBackoff(async () => { + return await someApiCall(); +}); + +// Custom configuration +const result = await retryWithExponentialBackoff( + async () => await fetchBalance(address), + 5, // maxRetries: Maximum number of retry attempts + 2000, // baseDelay: Base delay in milliseconds + 500 // initialDelay: Delay before first attempt in milliseconds +); +``` + +The function implements exponential backoff with the formula: `delay = baseDelay × 2^attempt` + +### Example: Retrying Token Transfers + +```typescript +import { AgentKit } from '@coinbase/agentkit'; +import { retryWithExponentialBackoff } from '@coinbase/agentkit'; + +const agentkit = await AgentKit.from({ + cdpApiKeyId: process.env.CDP_API_KEY_ID!, + cdpApiKeySecret: process.env.CDP_API_KEY_SECRET!, +}); + +// Transfer with retry logic +async function transferWithRetry(to: string, amount: string) { + return await retryWithExponentialBackoff( + async () => { + const result = await agentkit.run({ + action: 'transfer', + params: { + to, + amount, + assetId: 'eth' + } + }); + return result; + }, + 3, // Retry up to 3 times + 2000 // 2 second base delay + ); +} + +try { + const result = await transferWithRetry( + '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', + '0.01' + ); + console.log('Transfer successful:', result); +} catch (error) { + console.error('Transfer failed after retries:', error); +} +``` + +### Handling Timeouts + +For HTTP requests and API calls, implement timeouts using AbortController: + +```typescript +async function fetchWithTimeout(url: string, timeoutMs: number = 30000) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeoutMs); + + try { + const response = await fetch(url, { signal: controller.signal }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + + return await response.json(); + } finally { + clearTimeout(timeoutId); + } +} + +// Example: Fetch token price with retry and timeout +async function fetchTokenPrice(symbol: string) { + return await retryWithExponentialBackoff( + async () => { + return await fetchWithTimeout( + `https://api.example.com/price/${symbol}`, + 30000 // 30 second timeout + ); + }, + 3, // Retry up to 3 times + 1000 // 1 second base delay + ); +} + +try { + const price = await fetchTokenPrice('eth'); + console.log('ETH Price:', price); +} catch (error) { + console.error('Failed to fetch price:', error); +} +``` + +### Transaction Confirmation Polling + +When waiting for blockchain transaction confirmations, implement polling with a timeout: + +```typescript +async function waitForTransaction( + agentkit: AgentKit, + txHash: string, + timeoutMs: number = 120000 +) { + const startTime = Date.now(); + const pollInterval = 5000; // 5 seconds + + while (Date.now() - startTime < timeoutMs) { + try { + const receipt = await agentkit.wallet.getTransactionReceipt(txHash); + + if (receipt) { + console.log('Transaction confirmed:', receipt); + return receipt; + } + + // Wait before checking again + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } catch (error) { + console.warn('Error checking transaction:', error); + await new Promise(resolve => setTimeout(resolve, pollInterval)); + } + } + + throw new Error(`Transaction ${txHash} timed out after ${timeoutMs}ms`); +} +``` + +### Custom Retry Logic + +For more complex scenarios, implement custom retry logic with conditional retries: + +```typescript +async function customRetry( + fn: () => Promise, + shouldRetry: (error: Error, attempt: number) => boolean, + maxRetries: number = 3, + baseDelay: number = 1000 +): Promise { + let lastError: Error; + + for (let attempt = 0; attempt <= maxRetries; attempt++) { + try { + return await fn(); + } catch (error) { + lastError = error as Error; + + // Check if we should retry this error + if (attempt === maxRetries || !shouldRetry(lastError, attempt)) { + throw lastError; + } + + const delay = baseDelay * Math.pow(2, attempt); + console.log(`Retrying after ${delay}ms (attempt ${attempt + 1}/${maxRetries})`); + await new Promise(resolve => setTimeout(resolve, delay)); + } + } + + throw lastError!; +} + +// Usage: Only retry on specific errors +await customRetry( + async () => await someApiCall(), + (error, attempt) => { + // Retry on rate limiting or server errors + if (error.message.includes('429') || error.message.includes('503')) { + return true; + } + // Don't retry on auth errors + if (error.message.includes('401') || error.message.includes('403')) { + return false; + } + // Retry on other errors for first 2 attempts + return attempt < 2; + }, + 5, + 2000 +); +``` + +### Best Practices + +**When to use retries:** +- ✅ Network connectivity issues +- ✅ Rate limiting (HTTP 429) +- ✅ Temporary service unavailability (HTTP 503) +- ✅ RPC endpoint failures +- ❌ Invalid parameters (HTTP 400) +- ❌ Authentication failures (HTTP 401, 403) +- ❌ Insufficient balance errors + +**Recommended timeout values:** +- **Fast operations** (< 30s): Balance checks, address lookups +- **Medium operations** (30-90s): Token transfers, NFT minting +- **Long operations** (90-300s): Smart contract deployments, complex DeFi interactions + +**Error handling tips:** +- Always log retry attempts for debugging +- Set maximum retry limits to prevent infinite loops +- Use exponential backoff to avoid overwhelming services +- Implement circuit breakers for repeated failures +- Provide fallback behavior when operations fail +- Check error codes before retrying (don't retry 4xx errors) + ## Contributing See [CONTRIBUTING.md](../../CONTRIBUTING.md) for more information.