Skip to content

feat(transaction): add useTransactionSimulation hook#2639

Open
lau90eth wants to merge 1 commit into
coinbase:mainfrom
lau90eth:feat/transaction-simulation-v2
Open

feat(transaction): add useTransactionSimulation hook#2639
lau90eth wants to merge 1 commit into
coinbase:mainfrom
lau90eth:feat/transaction-simulation-v2

Conversation

@lau90eth

@lau90eth lau90eth commented May 2, 2026

Copy link
Copy Markdown

Summary

Every user on Base has paid gas for a transaction that failed.
"Insufficient liquidity", "execution reverted", "out of gas" —
all discovered after signing and paying.

OnchainKit has zero pre-flight checks before the user confirms.

This PR adds useTransactionSimulation, a hook that simulates
transactions before signing — showing exactly what will happen,
or warning if it will fail.

const { simulation, willFail, warnings, isSimulating } = 
  useTransactionSimulation();

// "✅ Simulation passed — gas: 21,000"
// "🔴 This transaction will fail: insufficient liquidity"

What changed

  • useTransactionSimulation hook — simulates all calls from
    TransactionContext before the user signs. Exposes success/failure
    status, decoded error messages, gas estimate, and warnings.
  • simulateTransaction utility — uses viem eth_call to dry-run
    a transaction against the current chain state. Zero external API
    dependencies — reads directly from chain via the configured RPC.
  • SimulationResult type — standardized shape including success
    flag, gasUsed, returnData, decoded error, and warnings array.
  • Public exports — added to src/transaction/index.ts

Usage

import { useTransactionSimulation } from '@coinbase/onchainkit/transaction';

const {
  simulation,      // SimulationResult | null
  willFail,        // true if simulation detected a revert
  warnings,        // string[] — decoded error messages
  isSimulating,    // loading state
  error,           // APIError | null
} = useTransactionSimulation();

// Gate the TransactionButton
<Transaction calls={calls}>
  {willFail && (
    <div>🔴 {warnings[0]}</div>
  )}
  {simulation?.success && (
    <div>✅ Gas estimate: {simulation.gasUsed.toString()}</div>
  )}
  <TransactionButton disabled={willFail} />
</Transaction>

// Disable simulation (e.g. for gas-only transactions)
const { simulation } = useTransactionSimulation({ enabled: false });

API

type UseTransactionSimulationParams = {
  enabled?: boolean;  // default: true
};

type SimulationResult = {
  success: boolean;
  gasUsed: bigint;
  returnData?: Hex;
  decodedOutput?: unknown;
  errorMessage?: string;
  warnings: string[];
};

type UseTransactionSimulationResult = {
  simulation: SimulationResult | null;
  willFail: boolean;
  warnings: string[];
  isSimulating: boolean;
  error: APIError | null;
};

Notes to reviewers

  • Zero external dependencies — uses viem eth_call directly.
    No Tenderly, no Alchemy simulation API, no API key required.
    Works on any EVM chain configured in OnchainKitProvider.
  • willFail — convenience flag derived from
    simulation?.success === false. Safe to use as a gate for
    TransactionButton disabled prop.
  • warnings — human-readable error messages decoded from
    the revert reason. Suitable for direct UI rendering.
  • enabled: false — opt-out for transactions where simulation
    is not meaningful (e.g. pure ETH transfers with no calldata).
  • Reads calls from useTransactionContext automatically.
  • Re-runs simulation when calls change.
  • Follows the same APIError pattern as existing utilities.

Testing

  • useTransactionSimulation.test.ts — 5 tests
  • simulateTransaction.test.ts — utility unit tests
  • All 389 test files pass (2697 tests)

Test cases covered:

  • enabled: false → returns null immediately, no fetch
  • Empty calls → returns null, no fetch
  • Successful simulation → success: true, gasUsed populated
  • Failed simulation → willFail: true, warnings populated
  • API/RPC error → error state exposed, graceful fallback

Risk

Low. Read-only hook. eth_call does not broadcast a transaction
or modify any state — it is a pure simulation against current
chain state.

Returns null when enabled: false or no calls present.
Does not modify TransactionProvider, TransactionButton,
or any existing transaction logic.

Adds transaction simulation to prevent failed transactions before signing.

- useTransactionSimulation hook: simulates transactions, detects failures
- simulateTransaction utility: uses viem eth_call, zero external dependencies
- Shows: success ✅, failure 🔴 with error message
- Detects: revert, insufficient funds, gas exceeded
- 5 tests covering: disabled, empty state, success, failure, API error

Refs: coinbase#2572
@vercel

vercel Bot commented May 2, 2026

Copy link
Copy Markdown

@lau90eth is attempting to deploy a commit to the Coinbase Team on Vercel.

A member of the Team first needs to authorize it.

@cb-heimdall

Copy link
Copy Markdown

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@lau90eth

lau90eth commented Jun 9, 2026

Copy link
Copy Markdown
Author

Hi @coinbase/onchainkit-maintainers,

Gentle ping on #2639 (useTransactionSimulation).

This hook allows simulating transactions before the user signs them — one of the most requested security features for OnchainKit.

I've also published a companion security suite here for easier testing:
https://github.com/lau90eth/onchainkit-security-suite

Happy to address any feedback or adjust the implementation.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants