KeeperHub / KeeperHub/keeperhub
Workflows re-send approvals the chain has already granted, and nothing surfaces it
- Dominant language
- TypeScript
- Stars
- 24
- Forks
- 93
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 266
Description
### Before filing
- [x] I searched open and closed issues for this proposal.
- [x] I checked the docs and the current behaviour on `staging`.
- [x] This is one change, not several. (Several means several issues.)
### Reason: what you cannot do today
Inside KeeperHub there is no way to tell that a workflow is paying for an approve it doesn't need.
The shape is common: approve a token, then spend it. If the allowance from the previous run still covers the spend, that approve is an on-chain write that changes nothing and still costs gas. It goes out on every run, for as long as the workflow is scheduled. The builder shows both nodes as normal steps, because as far as the graph is concerned they are.
This came out of building Meld for the Agents Onchain hackathon. To measure it I created 100 workflows across 7 shapes, ran each one against an optimised copy, and read the gas off the receipts: 400 executions, 1,092 transactions on Sepolia, a redundant approve removed in 100 of 100, zero regressions. The hashes are at https://github.com/chrsnikhil/meld/blob/agent/docs/receipts.json
What I do instead today is run that analysis outside the product, in a browser extension and an MCP server, against the public API.
### Reason: what the workaround costs
The workaround works, but it doesn't actually deliver the saving, and that is the part worth weighing.
Meld can only create an optimised copy of a workflow. It cannot edit the original, so the saving is only realised if someone notices the copy, migrates their schedule onto it, and retires the old one. In practice that last step doesn't happen. The analysis is right and the money still gets spent.
The rest of the cost is smaller but real. It needs a browser extension installed alongside the product. It needs an org API key held outside KeeperHub to read workflows and execution history. And because 441 of the 442 action types keep their calldata inside protocol-aware config, the analysis has to read execution logs through the API to learn what a run actually did, which is information the platform already holds and could read directly.
There is no workaround at all for the detection appearing where the user is, which is the builder.
### Scope: what this touches, and what it does not
Touches: workflow analysis, and whatever surface reports the result.
Does not touch: the execution engine, signing or the delegate, pricing or plan limits, the database schema (the analysis can be computed on demand from the graph plus the latest execution), and any existing response shape if it lands as a new route.
On whether this is one change: it is not, and I'd rather say so up front. Detection and rewrite are separable. Surfacing "this approve looks redundant, based on the last run" could ship, deploy and be correct on its own, with no rewrite anywhere. The rewrite could then be reverted without breaking it.
So I'd file this as the detection only, and open the rewrite as a separate issue once there's a view on whether it's wanted. Everything below is scoped to detection.
### Plan: what you propose
Read the workflow graph, classify nodes as read or write against the action registry, and find runs of consecutive on-chain writes on the same chain. Within those, look for an approve immediately preceding a spend to the same spender and token.
For each candidate, read the most recent completed execution to learn the allowance that was actually granted and what was actually spent. That step is what makes it a measurement rather than a guess, and it's why a workflow that has never run should report "unknown" rather than "redundant".
Then report per workflow: which approve looks redundant, which execution the conclusion came from, and where it refuses. Refusals matter as much as detections. It should decline when there is a second approve to the same spender and token pair (approve sets an allowance rather than adding to it, so removing one can change the final state), when anything sits between the approve and the spend, when the writes are on different chains, or when the action type is unknown.
Shape: I'd suggest a new read-only route rather than changing an existing one, so no current caller is affected. Response would carry the candidate, the execution it was learned from, and any refusals with reasons.
I don't have a strong view on whether it should also appear in the builder, and I'd rather you decided that than have me assume it.
### Plan: alternatives you considered
Do nothing. Defensible if the pattern is rarer in your real workflows than in my test set. I should be straight that my 100 workflows were built to contain a redundant approve, so they measure the effect and not how often it occurs. Pointed at real Aave, Compound and Morpho workflows in my org, the same analyser reports no-approve and zero percent. Establishing prevalence needs your data, not mine, and if it turns out to be rare then doing nothing is the right call.
Leave it external, as it is now. Rejected because of the copy problem above. An optimisation that requires a manual migration mostly doesn't get adopted.
Static analysis with no execution history. Rejected. The graph cannot tell you the allowance, and most action types hide their calldata behind config, so it would produce confident guesses. A wrong "this is redundant" is worse than no feature.
Batching adjacent writes instead of eliding one. Rejected for now. It needs the paid Code action, and it is not reliably better: in 26 trials I measured, 2 came out worse, with gas rising from 238,468 to 260,380, because the delegate call carries its own overhead. Elision worked in 100 of 100. If batching is interesting, it belongs in its own issue.
### Scope: compatibility
- [ ] Changes an existing response shape, status code, CLI flag, or default.
- [ ] Adds, removes, or upgrades a dependency.
- [ ] Changes database schema or requires a migration.
- [ ] Touches authentication, permissions, validation, or spend limits.
- [ ] Changes pricing, plan limits, or anything a user is charged.
Contributor guide
Assessment
This issue has not been assessed yet.