EkuboProtocol / EkuboProtocol/wallet
wallet_send_execution_plan reports "send reported a mined transaction but its receipt disappeared" for a transaction that actually landed
- Dominant language
- Rust
- Stars
- 4
- Forks
- 2
- Avg merge
- 13h 29m
- Merged PRs (30d)
- 33
Description
## Summary
`wallet_send_execution_plan` returned an error for a transaction that **succeeded on chain**. The nonce advanced and the transaction is in a block; only the receipt read failed, so the tool reported a mined transaction whose receipt vanished.
## What happened
Base (chain 8453), wallet `mcp-tester` (`0x7e908a418795A1f6edF35fe3695bCA3fAe044E1F`), sending an Ekubo TWAMM `lockAndExecuteVirtualOrders` plan on 2026-08-23 ~21:12 UTC.
The plan simulated cleanly:
```
simulation_id 74381ac7-d600-4135-8b02-987c4575bbfe
success: true, gas_used 36189, to 0xd47f1b1edcfeabb08f6ebd8fc337c27e636c75ba
prepared nonce: 23
```
Sending it returned only:
```
send reported a mined transaction but its receipt disappeared
```
No `request_id`, no transaction hash, no status — so there was nothing to hand to `wallet_get_execution_status` or `wallet_wait_for_execution` to reconcile with.
`wallet_get_status` immediately afterwards showed:
```json
{ "wallet_id": "mcp-tester", "chain_id": "8453", "transaction_count": 24 }
```
Nonce 23 had been consumed, i.e. the transaction was mined successfully. The send was fine; the reporting was not.
## Likely cause
The Base network is configured with two ordered RPC endpoints:
```
base (8453): https://base-mainnet.g.alchemy.com/ , https://mainnet.base.org/
```
A receipt lookup that lands on a different endpoint than the broadcast, against a chain producing 2-second blocks, can legitimately return null for a receipt that exists — the second endpoint is simply a block or two behind, or had not yet indexed it. Treating that null as "the receipt disappeared" turns a normal propagation lag into a hard error.
## Why it matters
This is the worst shape for a send error: it reads as though something went wrong with a transaction that in fact succeeded. An agent or user seeing it may reasonably retry, and for a non-idempotent action a retry is a second real transaction. Here the action was idempotent maintenance so nothing was lost, but the same message on a swap, a transfer, or a stake would invite a duplicate.
It also strands the caller: without a `request_id` in the error payload there is no supported way to reconcile. I only established what happened by reading the nonce out of `wallet_get_status`.
## Suggested fix
1. Do not treat a null receipt as terminal. Re-poll (ideally pinned to the endpoint that accepted the broadcast) before concluding anything, and fall back to `eth_getTransactionByHash` and the account nonce.
2. Whatever the outcome, return the `request_id` and `transaction_hash` in the error payload so the caller can reconcile with `wallet_get_execution_status`. The record was persisted before broadcast, so both are known.
3. If the state is genuinely uncertain, say so as "pending / unconfirmed, do not retry" rather than as a failure, since the signed bytes were already broadcast.
Found while testing the full Ekubo hosted MCP surface with a live 0.1 ETH round trip; every other send in that run (33 transactions across Ethereum and Base) reported correctly.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start at the Rust implementation of wallet_send_execution_plan, then trace the persisted request record through broadcast and receipt lookup. Compare its error path with wallet_get_execution_status and wallet_get_status, focusing on how a null receipt is handled after broadcast. Done means propagation lag is retried or reported as pending/unconfirmed without inviting a retry, while request_id and transaction_hash remain available for reconciliation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- blockchain, rust
- Domain
- backend-api-design, blockchain
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100