Sponsored Content

DEV Community

Christian Pichichero
Christian Pichichero

Posted on

Scoped Permissions vs Custody: Letting Software Act On-Chain Without Holding Your Keys

If you've ever set up a recurring on-chain action β€” a DCA buy, a rebalance, a claim-and-restake β€” you've run into the same fork in the road: either you hand a private key to something (a bot, a script, a service), or you sign every transaction yourself and the automation stops being automatic. Custody is the easy way out of that problem, and it's also the thing that goes wrong most often in this industry. So it's worth being precise about what "non-custodial automation" actually means mechanically, because the phrase gets used loosely.

There are two structurally different ways to let a piece of software act from your wallet without giving it your funds.

Delegated session signers. You generate a separate keypair, authorize it (usually via a signed message or an on-chain transaction) to act on your account, and hand the private half to a service. The service's backend now holds a key that can sign transactions on your behalf. Non-custodial in the narrow sense that your main wallet's key never leaves your device β€” but the scope of what that session key can do is enforced by the vendor's own logic, before it ever reaches the chain. Something like this:

// backend, off-chain, before it decides to sign
const isCallInScope = (call, policy) =>
 call.target === policy.allowedTarget &&
 policy.spent + call.value <= policy.cap;
Enter fullscreen mode Exit fullscreen mode

That's a real check, and a careful team writes it correctly. But notice where it lives: in a server you don't control, checked against a policy object that server maintains. If the backend has a bug, gets compromised, or is simply told by an operator to ignore the cap for one call, nothing on-chain stops it. The chain sees a validly signed transaction from an authorized key and executes it. The account itself has no opinion about whether the call was "in scope" β€” it trusted the key, full stop.

Account-enforced permissions (ERC-7715 style). The newer approach, built for smart accounts under ERC-4337 and formalized in proposals like ERC-7715, moves the scope check into the account's own validation logic via caveat enforcers. When you grant a permission, you're not just handing over a key β€” you're installing a rule inside your account that every action from that permission has to pass, checked by the account contract itself at execution time:

// runs in the account's validation path, on-chain
function enforceCaveat(bytes calldata call, Caveat calldata c) external view {
 require(call.target == c.allowedTarget, "target not allowlisted");
 require(c.spent + call.value <= c.cap, "exceeds cap");
}
Enter fullscreen mode Exit fullscreen mode

The difference sounds small but it isn't. In the 7715 model, even if the delegate's key is fully compromised, the account contract rejects any call that violates the caveat β€” the enforcement is data the account carries, not trust the account extends. Revocation is also a first-class on-chain action against your own account state, not a request you send to someone else's server and hope gets honored before the next scheduled run.

The honest trade-off: 7715 requires your wallet to be a smart account that supports the standard, and tooling for it is still young β€” wallet support is uneven, and the caveat-enforcer ecosystem (which enforcers exist, which are audited, which compose safely with each other) is not mature. The session-signer approach works today with wallets that have none of that infrastructure, and for a lot of use cases it is a reasonable, shippable answer. It's just that when you use it, the meaning of "scoped" is a promise made by a company's backend code, not a rule your account carries. That's a real distinction, not a pedantic one, and it's worth asking any "non-custodial automation" product which side of it they're on.

A few other things are true regardless of which model you pick. Gas sponsorship (someone else pays gas so the user doesn't need a native-token balance sitting around) is orthogonal to custody β€” you can sponsor gas under either architecture. An allowlist of target contracts and an allocation cap reduce blast radius but don't eliminate it: a bug in an allowlisted contract, or a cap set too high, still lets real money move. And revocability is only as good as how quickly it takes effect β€” an on-chain revocation is final the moment it's mined; an off-chain "we'll stop signing now" is final whenever the backend process notices.

Disclosure: I build Tradevo (app.tradevo.co), which runs algorithmic strategies from a wallet the user holds, through a scoped on-chain authorization the user can revoke at any moment β€” architecturally closer to the session-delegation model above, with the scope (allowed contracts, allocation caps) enforced against that authorization rather than left to an unchecked key. I'm not claiming it's the account-enforced version described above; it's worth being clear about which one you're actually getting, from us or anyone else, before you sign anything.

Top comments (0)