A QA engineer that understands crypto and wallets.
Every flow of your dApp tested on forks,
including the odd cases real users run into.
How it works
Finds the flows that move money and writes a test for each: a goal, an expected result, short steps.
Write your own tests or edit the ones it wrote. Say what should happen in plain words, and an LLM checks it on every run, not just a fixed script.
Runs every test with its own wallet on a local fork. Real contracts.
Forks of six chains today
Replays each flow the way users break it: rejected, wrong network, switched account, slow RPC.
Runs again from CI on every commit or release, and every day on your live app. When a test stops passing, you hear about it in Slack, Telegram or email.
In CI
Runs on every change.
The same tests from the CLI or a GitHub Action, with JUnit, JSON and Markdown reports.
$ dappdrill run --unhappy
Running 14 tests on https://harborvault.xyz
✓Deposit hUSDC into the vault41s
✓Withdraw hUSDC38s
✓Withdraw half of the deposit35s
✓Claim the deposit bonus22s
✓Get test hUSDC9s
✓Connect a wallet6s
✗Deposit hUSDC into the vault · Rejected in wallet17s
The Deposit button stays on “Confirming…” after the wallet rejects.
✓Deposit hUSDC into the vault · Slow RPC58s
✗Deposit hUSDC into the vault · Wrong network12s
The deposit was sent without asking to switch networks.
✓Deposit hUSDC into the vault · Switched account24s
✓Deposit hUSDC into the vault · Not enough gas14s
✓Withdraw hUSDC · Rejected in wallet16s
✓Withdraw hUSDC · Slow RPC52s
✗Withdraw hUSDC · Not enough gas13s
Clicking Withdraw does nothing and shows no message.
11 of 14 passed, 3 failed. Videos and screenshots: dappdrill-out/artifacts
.github/workflows/dappdrill.yml
- uses: dappdrill/dappdrill/.github/actions/dappdrill@main with: url: ${{ steps.preview.outputs.url }} unhappy: "true"
The contract
Audited, fuzzed, proven, bountied.
- Audits
- Fuzzing and invariant tests
- Formal verification
- Bug bounties after launch
The screen
Clicked through by hand, the day before release.
- What happens when the user rejects?
- Wrong network? A switched account?
- Two wallets installed?
- A slow RPC?
On live frontends
Most bugs aren’t attacks. They’re a real user doing something unexpected.
Compound / Slow RPC
One click sent two deposits.
two supply() calls, 6 ms apart
Supply1 clicksupply() ×2Compound / Two wallets
The user picked MetaMask. The app talked to Rabby.
EIP-6963 choice ignored
dAppMetaMask picked
Rabby used
pod / Wrong network
Sign-in hangs on “Connecting…” forever.
no switch prompt, no message
Connecting…3:02
Found by DappDrill on live frontends, run on local forks, November 2026. Each finding checked by hand.