This guide demonstrates advanced techniques for utilizing gas simulation to achieve better fee estimations and guarantees, as well as implementing efficient transaction batching patterns using the cheqd SDK. These techniques are based on the SDK's internal implementation and provide production-ready optimization strategies.
Gas simulation allows you to accurately estimate transaction costs before execution, providing several key benefits:
Core Benefits
Benefit
Description
Use Case
Cost Prediction
Accurate fee estimation before transaction execution
User experience, cost planning
Failure Prevention
Early detection of insufficient gas scenarios
Transaction reliability
Optimal Gas Setting
Avoid over/under-provisioning gas
Cost optimization
Batch Optimization
Efficient grouping of multiple transactions
Throughput maximization
Simulation vs Real Execution
Aspect
Simulation
Real Execution
Network State
Current state snapshot
State at execution time
Gas Consumption
Estimated usage
Actual usage
Side Effects
None (read-only)
Permanent state changes
Cost
Free
Consumes actual fees
Advanced Gas Estimation
Basic Gas Simulation
The SDK provides a simulate() method for accurate gas estimation:
Enhanced Gas Estimation with Safety Margins
Bulk Gas Estimation for Multiple Transactions
Transaction Batching Strategies
The SDK provides an intelligent batchMessages() method that optimally groups transactions based on gas limits:
Core Batching Implementation
Advanced Batching Strategies
1. Priority-Based Batching
2. Smart Batching with Gas Optimization
3. Transaction Type-Aware Batching
Fee Optimization Patterns
Dynamic Fee Calculation with Simulation
Batch Fee Optimization
Production Implementation
Complete Implementation Example
Error Handling and Resilience
Robust Error Handling
Circuit Breaker Pattern
Built-in Transaction Retry Policy
The cheqd SDK includes a sophisticated retry mechanism in the broadcastTx method that automatically handles transient network failures and ensures reliable transaction delivery. Understanding this built-in functionality helps you design more robust applications.
Retry Policy Overview
The CheqdSigningStargateClient.broadcastTx() method implements an enhanced retry policy with the following features:
Feature
Description
Default Value
Max Retries
Maximum number of broadcast attempts
3
Timeout
Maximum wait time for transaction inclusion
60,000 ms (60s)
Poll Interval
Frequency of checking for transaction inclusion
3,000 ms (3s)
Backoff Delay
Brief delay between retry attempts
1,000 ms (1s)
How the Retry Policy Works
Retry Flow:
Initial Broadcast: Attempts to broadcast transaction using broadcastTxSync
Transaction Tracking: Records transaction hash for monitoring
Polling Phase: Continuously checks for transaction inclusion in blocks
Failure Handling: On broadcast failure, implements exponential backoff
Retry Logic: Retries up to maxRetries times with same signed transaction
Double-Spend Prevention: Reuses identical signed transaction bytes across retries
Retry Scenarios
The retry policy handles several common failure scenarios:
Error Handling and Transaction Tracking
The retry policy provides enhanced error information:
Integration with Gas Simulation
The retry policy works seamlessly with gas simulation and fee calculation:
Production Configuration
For production deployments, you can tune the retry parameters based on your requirements:
Monitoring Retry Behavior
You can implement monitoring to track retry patterns:
Key Benefits
The built-in retry policy provides several advantages:
Reliability: Automatic handling of transient network issues
Transaction Safety: Prevents double-spending through transaction reuse
Visibility: Maintains transaction hash tracking even on failures
Configurability: Tunable parameters for different use cases
Integration: Works seamlessly with gas simulation and fee calculation
This retry mechanism significantly improves the reliability of transaction broadcasting without requiring additional application-level retry logic.
Best Practices Summary
Gas Simulation Best Practices
Always Use Safety Margins: Apply 20-50% buffer to simulated gas estimates
Handle Simulation Failures: Implement fallback gas values for production systems
Batch Simulations: Simulate multiple transactions in parallel for efficiency
Monitor Gas Usage: Track actual vs estimated gas consumption
Network-Specific Limits: Respect network gas limits and consensus parameters
Batching Best Practices
Optimal Batch Size: Target 80-90% of max gas limit for efficiency
Transaction Prioritization: Group high-priority transactions together
Type-Aware Batching: Consider transaction complexity when batching
Error Isolation: Design batches to minimize failure propagation
Progress Monitoring: Implement progress tracking for large batch operations
Production Deployment
Comprehensive Testing: Test all scenarios in development environments
Monitoring and Alerting: Track gas usage, failure rates, and cost optimization
Graceful Degradation: Implement fallback strategies for all failure modes
Performance Metrics: Monitor transaction throughput and cost effectiveness
Regular Optimization: Continuously tune parameters based on network conditions
This comprehensive approach to gas simulation and transaction batching will provide optimal performance, cost efficiency, and reliability for production cheqd SDK implementations.
interface CircuitBreakerState {
failures: number;
lastFailure?: Date;
state: 'closed' | 'open' | 'half-open';
}
class SimulationCircuitBreaker {
private state: CircuitBreakerState = {
failures: 0,
state: 'closed'
};
private readonly threshold: number;
private readonly resetTimeout: number;
private readonly fallbackGas: number;
constructor(
threshold: number = 5,
resetTimeout: number = 60000, // 1 minute
fallbackGas: number = 200_000
) {
this.threshold = threshold;
this.resetTimeout = resetTimeout;
this.fallbackGas = fallbackGas;
}
async executeWithCircuitBreaker<T>(
operation: () => Promise<T>,
fallback: () => T
): Promise<T> {
// Check if circuit should reset
if (this.state.state === 'open' && this.shouldReset()) {
this.state.state = 'half-open';
}
// If circuit is open, use fallback immediately
if (this.state.state === 'open') {
console.warn('Circuit breaker is open, using fallback');
return fallback();
}
try {
const result = await operation();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
if (this.state.state === 'open') {
console.warn('Circuit breaker opened, using fallback');
return fallback();
}
throw error;
}
}
private shouldReset(): boolean {
return this.state.lastFailure &&
Date.now() - this.state.lastFailure.getTime() > this.resetTimeout;
}
private onSuccess(): void {
this.state.failures = 0;
this.state.state = 'closed';
}
private onFailure(): void {
this.state.failures++;
this.state.lastFailure = new Date();
if (this.state.failures >= this.threshold) {
this.state.state = 'open';
console.warn(`Circuit breaker opened after ${this.state.failures} failures`);
}
}
getState(): CircuitBreakerState {
return { ...this.state };
}
}
// Built into the SDK - no additional code needed
const result = await signer.broadcastTx(
txBytes,
60_000, // timeoutMs
3_000, // pollIntervalMs
3 // maxRetries
);
// Example scenarios handled automatically:
// 1. Network congestion - retry broadcast
try {
const result = await signer.signAndBroadcast(address, messages, 'auto');
// SDK automatically retries on temporary network issues
} catch (error) {
if (error.name === 'TimeoutError') {
console.log('Transaction hash:', error.txHash); // Available even on timeout
}
}
// 2. Node synchronization issues - poll longer
// The SDK continues polling until timeout or success
// 3. Temporary RPC failures - retry with backoff
// Built-in 1-second backoff between retry attempts
try {
const result = await signer.broadcastTx(txBytes, 60000, 3000, 3);
console.log('Transaction successful:', result.transactionHash);
} catch (error) {
if (error.name === 'TimeoutError') {
// Even on timeout, transaction hash is available
console.log('Transaction may still be processed:', error.txHash);
// You can continue monitoring manually if needed
const finalResult = await signer.getTx(error.txHash);
} else if (error.name === 'BroadcastTxError') {
// Transaction was rejected during CheckTx
console.error('Transaction rejected:', error.log);
}
}
// Complete example with gas simulation and retry policy
async function executeTransactionWithRetries(
signer: CheqdSigningStargateClient,
address: string,
messages: EncodeObject[]
): Promise<DeliverTxResponse> {
// 1. Simulate for accurate gas estimation
const gasEstimate = await signer.simulate(address, messages, '');
const gasWithMargin = Math.round(gasEstimate * 1.3);
// 2. Calculate fees based on simulation
const fee = calculateDidFee(gasWithMargin, signer.gasPrice);
// 3. Sign transaction
const txRaw = await signer.sign(address, messages, fee, '');
const txBytes = TxRaw.encode(txRaw).finish();
// 4. Broadcast with built-in retry policy
return signer.broadcastTx(
txBytes,
90_000, // Extended timeout for complex transactions
2_000, // More frequent polling
5 // More retries for critical operations
);
}
// Conservative configuration for critical transactions
const conservativeResult = await signer.broadcastTx(
txBytes,
120_000, // 2-minute timeout
2_000, // Poll every 2 seconds
5 // 5 retry attempts
);
// Fast configuration for less critical operations
const fastResult = await signer.broadcastTx(
txBytes,
30_000, // 30-second timeout
5_000, // Poll every 5 seconds
2 // 2 retry attempts
);
class TransactionMonitor {
private retryStats = {
attempts: [] as number[],
timeouts: 0,
successes: 0
};
async monitoredBroadcast(
signer: CheqdSigningStargateClient,
txBytes: Uint8Array
): Promise<DeliverTxResponse> {
const startTime = Date.now();
try {
const result = await signer.broadcastTx(txBytes, 60000, 3000, 3);
this.retryStats.successes++;
const duration = Date.now() - startTime;
console.log(`Transaction succeeded in ${duration}ms`);
return result;
} catch (error) {
if (error.name === 'TimeoutError') {
this.retryStats.timeouts++;
console.warn('Transaction timeout, but may still be processed');
}
throw error;
}
}
getStats() {
return { ...this.retryStats };
}
}