Skip to content

EVM: Add settler function to set safeguards with a signature - #84

Draft
alavarello wants to merge 2 commits into
mainfrom
settler/add-policy-by-signature
Draft

EVM: Add settler function to set safeguards with a signature#84
alavarello wants to merge 2 commits into
mainfrom
settler/add-policy-by-signature

Conversation

@alavarello

Copy link
Copy Markdown
Member

What

Adds Settler.setSafeguardWithSignature, which lets anyone submit a safeguard on a user's behalf as long as they carry that user's EIP-712 signature. Until now setSafeguard derived the target user from msg.sender, so the user had to send the transaction themselves.

function setSafeguardWithSignature(address user, bytes memory safeguard, uint256 deadline, bytes memory signature)

It rejects a past deadline, consumes the user's nonce, recovers the signer from the EIP-712 hash of UserSafeguard(address user,bytes safeguard,uint256 nonce,uint256 deadline), and requires that signer to equal user before delegating to the same _setSafeguard internal that setSafeguard uses. Storage, replacement semantics, and the SafeguardSet event are therefore identical between the two entry points.

The signature is bound to a per-user nonce (getUserSafeguardNonce, a new public mapping appended after the existing storage so the upgrade stays layout-safe) plus a deadline. The nonce stops a signature from being replayed to re-apply a stale safeguard after the user has changed it; the deadline bounds how long a signed-but-never-submitted authorization stays usable. The typed struct and its type hash live in contracts/safeguards/Safeguards.sol as UserSafeguard and SafeguardsHelpers, mirroring how Validation/IntentsHelpers are organized in Intents.sol.

Two decisions worth review

No ERC-1271 support. The first implementation used OpenZeppelin's SignatureChecker, which would have accepted signatures from smart-account users. It cost 882 bytes of deployed bytecode and pushed Settler to 24658 bytes, past the 24576-byte EIP-170 limit even with the optimizer at 1000 runs. It now uses ECDSA.recover, which is also how proposal and validation signatures are verified elsewhere in the same contract. Smart-account users aren't locked out: SmartAccountContract.call is callable by its owner, so an owner can still route a call to the plain setSafeguard.

Contract size headroom is nearly gone. Settler is now 24380 bytes, leaving 196 bytes under the EIP-170 limit, down from 800 before this change. The next addition to this contract will likely need library extraction or a comparable size reduction.

Tests

13 new cases in packages/evm/test/Settler.test.ts, following the nested-context convention of the neighbouring setSafeguard block. Every case submits the transaction from an account other than the user, so the msg.sender independence is what's actually exercised.

Covered: setting with no prior safeguard, replacing an existing one, nonce consumption (and that it leaves another user's nonce untouched), plus reverts for a replayed signature, a signature from another account, signatures bound to a different user / safeguard / nonce / deadline, and an expired deadline. The signing helper is signUserSafeguard in test/helpers/safeguards.ts, built the same way as the existing signProposal.

Full EVM suite passes at 460 tests and both linters are clean.

🤖 Generated with Claude Code

alavarello and others added 2 commits August 19, 2026 10:57
Adds setSafeguardWithSignature so a safeguard can be set on behalf of a
user by anyone holding the user's EIP-712 signature, instead of requiring
the user to be msg.sender. The signature is bound to a per-user nonce and
a deadline to prevent replays of stale authorizations.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant