The first thing anyone building on-chain automation runs into is a very specific fork in the road, and most people take the wrong branch without noticing. You want your positions to rebalance, or a stop to fire when a liquidation threshold gets close, or a limit order to fill while you sleep. The obvious way to do that is to write a bot that holds your key, watches the chain, and signs transactions when a condition is met. It works on day one. It also means there is now a server somewhere with a hot private key that can move all of your money, and that server is the entire security model. Everything below is about keeping the automation and getting rid of that server.
The reason this matters is that the failure mode is not subtle. A hot key on a box that holds your funds is a single point of total loss, and it does not have to be hacked in some clever way. A leaked environment variable, a dependency compromised upstream, a misconfigured log that prints the key, a backup in the wrong bucket. Any one of those and it is gone, all of it, in one transaction. So the real question is never whether to automate. It is how to automate in a way where the worst case is bounded to something you can live with.
Keepers do the watching so you do not have to hold a key
The cleanest mental model is to separate two jobs that people usually smush together. There is deciding what should happen and under what condition, and there is being the thing that is online at 3am to actually submit the transaction. Keeper networks exist to take the second job off your hands. You register an upkeep, which is really just a condition check plus an action, and a decentralized set of nodes competes to call it when the condition goes true. Chainlink Automation and Gelato are the ones most people reach for, and the pattern is the same.
What makes this non-custodial is that the keeper never gets your key. Your funds live in a smart contract, usually a vault or a smart account, and the keeper is only allowed to poke a specific function that the contract itself gates. The contract decides what the keeper can and cannot do. A keeper calling rebalance() can only ever rebalance according to the logic you deployed. It cannot call withdraw() and send everything to itself, because you never gave it that power in the code. The keeper is a caller with no privileges beyond the one door you left open for it.
The honest tradeoff is that you have now pushed the trust into the contract, so the contract had better be right. A keeper-triggered function that has a slippage parameter you forgot to bound, or a price input from an oracle that can be nudged, is a way to get drained slowly and legally by whoever notices first. Keepers remove the hot-key risk and hand you a smart-contract-correctness risk instead. That is usually a good trade, but only if you treat the callable function like the attack surface it is.
Session keys let a smart account grant scoped, expiring power
The other big idea, and the one that changed how I think about this, comes out of smart accounts. Instead of your wallet being a single keypair that can do anything forever, a smart account is a contract that can hold multiple keys with different rules attached to each. A session key is a temporary key you mint for an automation, and you get to write the fine print. You can say this key may only interact with this one router contract, may only spend up to some cap, may only trade these two tokens, and expires in seven days.
That last part is the underrated one. A scoped, expiring key means a compromised automation has a blast radius and a clock. If the bot holding the session key gets popped, the attacker inherits exactly the permissions you wrote and nothing more, and even those evaporate on expiry. Compare that to the hot-key setup where a compromise is unbounded in both scope and time. The session-key version turns "they drained my wallet" into "they could have moved at most X into the one contract I whitelisted, for a few days, and then it stopped."
The catch is that this only works if the account you are using actually enforces the scoping on-chain. Some setups do the permission check in a piece of software that sits in front of the key, which is fine right up until someone talks to the key directly and skips the check. If the constraints are not enforced by the account contract itself, they are a suggestion. When you evaluate a smart-account provider for this, the one question that matters is where the permission logic runs. In the contract, good. In a relayer or an SDK, treat the scoping as decorative.
Approval hygiene is the part everyone skips and later regrets
None of the above saves you if your token approvals are sloppy, because approvals are their own standing grant of power that lives entirely outside your automation design. Every time you let a contract move a token on your behalf, you sign an approval, and the lazy default across most of DeFi is to approve an unlimited amount so you never have to sign again. That unlimited approval is a persistent right for that contract to pull that token from your wallet, and it does not expire, and you forget it exists. If that contract is ever compromised, or was malicious in a quiet way from the start, the approval is the door it walks through.
The hygiene is not complicated, just tedious enough that people skip it. A few rules I actually follow:
- Approve the specific amount you need for the action, not the max, whenever the extra signature is bearable. For automation that runs repeatedly, approve a sane ceiling rather than infinity.
- Keep the wallet that holds real balances separate from the one that talks to automations. A dedicated automation wallet with a small working float means the standing approvals are attached to an account that does not hold the bulk of anything.
- Review and revoke approvals on a schedule. Any approval to a contract you no longer use is pure downside with zero benefit.
- Prefer permit-style approvals that carry their own expiry over the old set-and-forget kind, when the token and the protocol support them.
The reason approval hygiene belongs in the same conversation as keepers and session keys is that they solve different halves of the same problem and neither is complete alone. Keepers and session keys shrink what your automation can do if it is compromised. Approval hygiene shrinks what any contract can pull from your wallet if it turns hostile. You want both, because an attacker will happily take whichever one you left open.
How to pick, in threat-model terms
If I line the three approaches up by what happens on the worst day, the picture is clear enough to act on. A hot-key bot: worst case is total, immediate, permanent loss of everything the key controls, from any single leak. A keeper network: worst case is bounded by whatever the callable function is allowed to do, so your exposure equals your contract-correctness, and the key is never at risk. A session key: worst case is bounded by the scope and expiry you wrote, so a compromise costs you at most the whitelisted actions for a limited window. Layer approval hygiene under all of it and you cap what a hostile contract can drain regardless of how the automation is wired.
In practice the setup I keep coming back to is a smart account issuing narrowly scoped session keys for the automations, a keeper network doing the actual watch-and-fire so nothing depends on my laptop being awake, and a separate low-balance wallet for anything that needs standing approvals. It is more moving parts than a bot with a hot key in a config file, and that is the point. Every part is there to make the bad day survivable instead of terminal. When we wire non-custodial execution into Blockcircle across the exchanges we cover, this is the same logic, keep the trigger automated and keep the authority scoped so the keys never have to leave the user's control.
The one habit worth building before any of this is to write down, for each automation, the single sentence that starts with "if this gets compromised, the most it can do is." If you cannot finish that sentence with a bounded answer, you have a hot-key problem wearing a nicer outfit, and it is worth fixing before you fund it.