Skip to content

Conversation

@Jds-23
Copy link

@Jds-23 Jds-23 commented Sep 3, 2025

No description provided.

… and decoding on storage and loading accordingly
…uteTimeline which will used to recalculate digest to verify and then execute (ithacaxyz#329)
@Jds-23 Jds-23 changed the title Feat/add native timelock to account keys #329 feat: add native timelock to account keys #329 Sep 3, 2025
@legion2002 legion2002 added this to the v1.0.0 milestone Sep 11, 2025
@Jds-23 Jds-23 force-pushed the feat/add-native-timelock-to-account-keys branch from 7b94a60 to c966be3 Compare September 14, 2025 04:46
@Jds-23 Jds-23 marked this pull request as ready for review September 29, 2025 05:01
@Jds-23
Copy link
Author

Jds-23 commented Sep 29, 2025

@legion2002 @gakonst

…ck field, payment spend separation, relayer concepts, and timelocker payment implementation. These files are no longer needed as the project evolves.
Copy link
Collaborator

@legion2002 legion2002 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this PR is on the right track. Requested a few implementation detail changes.
Also can you please rebase with latest main to remove old dependencies like devtools and safe-singleton deployer, and also update to latest foundry nightly linting.

keyHash: keyHash,
readyTimestamp: uint40(block.timestamp + getKey(keyHash).timelock)
});
bytes32 timelockHash = _addTimelock(timelocker, computeDigest(calls, nonce));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach has an intricacy that the relayer payment and the spending limit check happens at the time of adding the timelock.
Not at the time of actually executing it.
I think this could lead to some weird kinds of attacks and inconsistencies. Like the relayer takes the payments, but never executes the action after the timelock.
Or the spending limit changes by the time the timelock is executed, so you can bypass spending limits.

A cleaner way to do this would be, to add a separate prepTimelock function. Where you can pass in just the calls and nonce ( or maybe user can directly pass in the digest ), and it adds the digest to the timelock mapping.

Then this execute checks that for timelocked keys, the timelock should already have passed. If not it reverts.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spend limit checks happens at the time of executing. As the function executeTimelock calls the GuardedExecutor._execute(Call[] calldata calls, bytes32 keyHash) in line 658.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the idea of adding another execution input with executeTimelock since it is not 7821 compliant.

We should keep the same execute() function, but instead create a new function like startTimelock which adds the timelock key to the storage, and then someone can call the normal execute function later to do the actual execution.

In this case we would just check the timelock status in the main execute function

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @legion2002, updated the execution flow. Now, the new prepTimelock function is used to add a timelocked and validateTimelockIsExecutable helps unwrapAndValidateSignature validate the timelock, and execution goes through execute.

The new optimisations will be with:

  1. Handle storage bloat from timelocks, as execute is used to execute the timelock, we are not removing the timelock from storage. Either add a manual cleaning process or somehow trigger a cleanup by detecting in the execute function itself.
  2. Add a way to cancel a pending timelock. We could do it by invalidating the nonce.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work with this! Having the validateTimelock function in unwrapAndValidateSignature makes sense, specially because you don't want a key to transfer the money out of your account using some kind of permit magic.
Having said that, we only allow super admin keys to do isValidSignature so maybe it is a better idea to validate timelock just during execution, and allow the key to make any unwrapAndValidateSignature signatures, without timelock.

I am leaning towards the latter, because this means we can reduce storage bloat, by checking timelock inside the execution and then removing it.

Also for cancelling a pending timelock, we can just allow the key itself, or any admin key to cancel any timelock.

ButI think the PR is at a good place right for now.
We'll figure out what's the right time to get this in, depending on how we'll schedule the next few releases.

Thanks for your contribution! Will let you know when/if we plan to schedule the next release.

…imelockHashes' to 'timelockDigests' and updating associated event parameters.
…ks instead of `_execute` and now `_execute` exeuctes timelocked upon passing.
…amp` with `startTimestamp`. Enhance `prepTimelock` , `_execute`, `unwrapAndValidateSignature` and add a`validateTimelockIsExecutable` functions for improved execution flow.
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.

2 participants