-
Notifications
You must be signed in to change notification settings - Fork 108
Description
Overview
When applications submit an EVM transaction via the CDP SDK and then wait for confirmation, there are real-world cases where a transaction is never mined (dropped) or is replaced (e.g., sped up/canceled). In these scenarios, a receipt polling helper (or any internal confirmation loop) can hang indefinitely without surfacing a clear terminal state.
This issue proposes adding robust handling for dropped/replaced transactions so SDK consumers can reliably detect and recover.
Problem Description
In production, transactions may:
- remain pending for a long time and eventually get dropped from mempool
- be replaced by a new transaction with the same nonce (speed-up/cancel)
- be mined on a different hash than the one originally returned
If waitForReceipt (or equivalent confirmation logic) only polls for a receipt by the original tx hash, it may never resolve, leaving apps stuck in a “pending” UI state and forcing developers to implement their own timeout/nonce tracking logic.
Expected Behavior
- Confirmation helpers should not hang forever.
- SDK should provide a deterministic outcome:
- receipt returned on success
- explicit error/terminal result when dropped, replaced, or timed out
- Timeouts and polling intervals should be configurable with safe defaults.
Steps to reproduce
- Send a transaction using the SDK.
- Before it is mined, replace it using the same nonce (speed-up/cancel) from the wallet or another tool.
- Call a receipt-waiting helper using the original tx hash.
- Observe that the call may continue polling indefinitely without resolving.
Example code
```ts
import { CdpClient } from "@coinbase/cdp-sdk";
async function run() {
const cdp = new CdpClient({ apiKey: process.env.CDP_API_KEY! });
const txHash = await cdp.evm.sendTransaction({
network: "base-sepolia",
to: "0x0000000000000000000000000000000000000000",
value: "0",
data: "0x",
});
// If tx is replaced/dropped, this may never resolve today
const receipt = await cdp.evm.waitForReceipt({
network: "base-sepolia",
txHash,
// desired options: timeoutMs, pollIntervalMs, onProgress, etc.
});
console.log("Receipt:", receipt);
}
run().catch(console.error);
Proposed Solution
- Add timeoutMs and pollIntervalMs options (if not present), with safe defaults.
- Detect “replaced” behavior by optionally tracking:
- sender address + nonce (if available from send call or via lookup)
- mempool/chain queries to see if a different tx with same nonce was mined
- Return a typed terminal result or throw a typed error (e.g., TxDroppedError, TxReplacedError, TxTimeoutError).
- Document recommended handling patterns for UIs and backend workers.