Contact handle for follow-up: X/Twitter @hyunh543 · Telegram @hyunh543 · email [email protected]
Environment: Solana Devnet — API base https://devnet.multihopper.com/api/v1, RPC https://api.devnet.solana.com. Test-mode key (mh_test_…, redacted). All findings reproduced without touching mainnet or third-party funds.
Date of testing: 2026-07-02 (UTC).
@solana/web3.js, an instrumented HTTP client that logs every request/response (src/client.ts), and client-side signing helpers that preserve server partial signatures (src/sign.ts)..env, keypair files, logs) excluded. To review it, DM me your GitHub username (@hyunh543 / [email protected]) and I'll add your team as collaborators. Each finding has a standalone repro script.create → prepare → sign/broadcast → confirm-broadcast → monitor flow faithfully, then exercise realistic agent failure modes (retries, interruptions, key reuse, expiry). Every claim below is backed by logs in logs/*.jsonl and, where on-chain, by transaction signatures.| Suggested testing area | Covered | Where |
|---|---|---|
| Agent understanding of create → prepare → sign/broadcast → confirm-broadcast → monitor | ✅ | LLM agent (src/agent.ts) + all scripts |
| Client-side signing, preserving server partial signatures | ✅ | signing-proof.ts (routeInitTxs[0] server presign preserved) |
| Strict broadcast sequencing (keeper → route → orchestrator → session) | ◑ Partial | keeper-funding broadcast tested; Finding “non-uniform blockhashes”. Full route/orchestrator/session ordering not broadcast (needs a devnet SPL-token balance) — noted, not claimed. |
| Two-step confirm-broadcast + missing keeper signature (MH_039) | ◑ Partial | confirm-broadcast flow + empty-body error tested; MH_039 not separately triggered |
| Idempotency-Key behavior, duplicates, conflicting retries, error handling | ✅ | idem.ts (MH_070/071, dedup) + Finding 2 |
| Expiry & resume after interrupted / partial broadcasts | ✅ | Finding 2 (expiry) + Finding 1 (crash-window resume) |
| Status polling, webhooks, recovery paths, failure interpretation | ◑ Partial | polling (GET /transfers/:id) + recovery (rescue/prepare → MH_081) tested; webhook delivery not tested this round |
| Documentation gaps / unsafe assumptions / missing examples | ✅ | Findings 1–3 + supporting doc findings |
(Webhook delivery and the full route→orchestrator→session broadcast ordering are the two areas we did not exercise end-to-end this round; flagged here rather than overclaimed.)
Severity: Critical (direct, repeatable fund loss; refutes a documented safety guarantee)
Affected flow/actions: POST /transfers/:id/prepare (resume), the keeper-funding broadcast step, and the documented two-step confirm-broadcast protection.
POST /transfers → POST /transfers/:id/prepare to obtain preparedTxs.keeperFundingTx.confirm-broadcast (simulates a crash / timeout / process restart in the exact window the docs say is protected).POST /transfers/:id/prepare again with a new Idempotency-Key.preparedTxs.resume.keeperAlreadyFunded === false and a re-emitted keeperFundingTx.Repro scripts: src/doublefund.ts (shows re-emission), src/doublefund2.ts (measures the second debit).