Automated management normally means handing over your keys. Here is the permission model that removes that trade-off, and where its real limits are.
A smart contract wallet can grant software a permission that names exact functions with exact constrained arguments. Rebalancing and withdrawing are different functions, so one can be granted and the other withheld. The manager can trade all day and still be mechanically unable to move funds to itself.
The usual objection to automated investing in crypto is straightforward and correct: for software to trade on your behalf, it needs the ability to move your assets, and anything that can move your assets can steal them.
That is true of a normal wallet. A private key is all or nothing. Whoever holds it can do everything the account can do, and there is no way to hand over half of it. This is why most automated crypto products are custodial. It is the path of least resistance.
Smart contract wallets break the all-or-nothing property, and that is the entire trick.
A standard Ethereum account is a keypair. The private key produces signatures; a valid signature authorises any transaction whatsoever. There is no concept of a partial key, no notion of "this key may only call these functions". The account has no code, so there is nowhere to put a rule.
A smart contract wallet is different. It is a program that owns assets and decides for itself what to execute. Because it is a program, it can hold rules. It can be told that one address may do a specific list of things, and that everything else is rejected. The rules live on the blockchain and execute on every transaction, which means they are enforced rather than promised.
The wallet is a Safe, the most widely used smart contract wallet in Ethereum, holding a large share of on-chain assets. Alongside it sits a separate contract, a Zodiac Roles modifier, whose only job is to hold roles and check every transaction against them.
A role is not a description. It is a list of entries, each naming:
Anything not explicitly listed is rejected. The default is no, not yes, which is the correct default for something guarding money.
In Fonte's case the keeper role permits supplying and withdrawing USDC on Aave, swapping between the assets the strategy holds, providing liquidity on the venues the strategy uses, and rebalancing between them. That is enough to run the strategy, which is the point.
The keeper cannot send your funds to an arbitrary address. It has exactly one permission that moves USDC out of the vault to a third party, and that is the fee sweep: USDC.transfer, selector 0xa9059cbb, with the recipient argument constrained to exactly one address, the fee wallet. It can move USDC to that address and nowhere else. The amount is not capped by the contract, which is a real limitation worth naming, and it is bounded instead by the fee that has actually been earned.
Constraining the function is not enough. Constraining the argument is what does the work. "The keeper may call transfer" would let it send your balance anywhere. "The keeper may call transfer where recipient equals this one address" is a guarantee, because the check runs on-chain on every single call and a transaction that violates it does not execute.
Withdrawal is a separate role with its own two entries. Aave's withdraw(asset, amount, to), selector 0x69328dec, with asset pinned to USDC and to pinned to your address. And USDC.transfer(to, amount), with the recipient pinned to your address. Both are single calls, not batched.
Because both destinations are pinned to you, a holder of that role can only ever pull funds to themselves. It cannot rebalance, swap, borrow, approve, or batch calls together. It does one thing.
The honest framing is not that risk disappears. It is that a specific, historically common failure mode is removed.
If the keeper's key is stolen tomorrow, the thief inherits the keeper's permissions and nothing more. They can rebalance your portfolio, which is annoying and could cost you money through bad trades and fees. They cannot send your funds to themselves, because no permission in the role allows a transfer to an address they control. The attack that has drained most automated crypto products is simply not available.
If the company shuts down, your funds sit in your wallet, and your withdrawal role still works, because it is a permission on a contract rather than a feature of a website. Nobody has to be alive for you to get your money.
Bad trades are still possible. Every action the keeper is allowed to take is one it can take badly. Permission scoping constrains where money can go, not whether the strategy is any good.
Value can leak through permitted actions. An attacker who cannot steal directly could still cause losses through repeated pointless rebalancing, paying fees and slippage each time. Scoping bounds the worst case; it does not make it zero.
The permission set is only as good as its narrowest entry. A role with one loose entry is a role with a hole. This is a real engineering discipline, not a checkbox, and it needs revisiting whenever a new capability is added.
The contracts themselves are a dependency. The Safe, the Roles modifier, and every protocol the strategy touches are code that can have flaws. Using large, long-lived, widely audited contracts is a risk reduction, not a risk elimination.
None of this requires trust, which is the nicest property it has. The wallet address, the modifier address and every permission entry are public on Base. You can read the role's entries directly from the modifier contract on a block explorer and confirm which selectors are allowed and which arguments are pinned.
If you want the short version of the check: find the permission that moves funds, and look at whether its destination is constrained. That single detail tells you most of what you need to know about any system making this claim.