Skip to main content

Timelock

The timelock is the heart of Kleidi's security model. It forces a delay between when you schedule a transaction and when it can be executed.

Why Delays Matter

The core idea is simple: time is a security primitive.

If someone forces you to transfer your funds, they need to maintain control over you for the entire delay period. For a 30-day timelock, that means kidnapping you for a month while avoiding detection. It's impractical.

Even for less extreme attacks (stolen keys, social engineering), the delay gives you time to notice something's wrong and cancel it.

How It Works

Every transaction follows a three-phase lifecycle:

1. Scheduled

A signer (or group of signers meeting the threshold) creates and signs a transaction. This schedules it on-chain.

At this point:

  • The transaction is public and visible to everyone
  • It cannot be executed yet
  • It can be canceled by the signers
  • The countdown begins

2. Executable

After the minimum delay passes, the transaction becomes executable.

Now:

  • Anyone can execute it (not just signers)
  • You pay gas to execute
  • The transaction stays executable for the expiration period
  • It can still be canceled

3. Expired (or Executed)

If nobody executes it before the expiration period ends, it expires and becomes invalid.

Or, someone executes it and it moves to "executed" status.

Timeline example with 7-day delay and 14-day expiration:

Day 0: Transaction scheduled
Day 7: Becomes executable
Day 21: Expires (if not executed)

You have from day 7 to day 21 to execute it.

The Salt Parameter

When you schedule a transaction, you include a "salt" value. This is just a random number that makes each transaction unique.

Why does this matter?

Without salt, you couldn't schedule the same transaction twice. Want to send 1 ETH to Alice today and again next month? You need different salts to make them unique transactions.

The salt also prevents replay attacks where someone tries to execute an old transaction again.

Canceling Proposals

Any signer can cancel a pending proposal at any time before it executes.

When you cancel:

  • The proposal is immediately removed from the queue
  • It cannot be un-canceled
  • You'd need to schedule a new transaction (and wait through the full delay again)

Canceling is useful when:

  • You spot an error
  • Plans changed
  • You think the transaction might be unauthorized
  • You're just testing and want to clean up

Who Can Execute?

This is important: anyone can execute a transaction once it's ready.

Usually, the person who proposed it will execute it (since they need it to happen). But technically, any address could execute it and pay the gas.

Why allow this? It makes the system more robust. If signers are unavailable, anyone with a bot could execute pending transactions.

Minimum Delay vs Expiration Period

These are two different settings:

Minimum Delay: How long you must wait before a transaction can be executed.

  • Set during deployment
  • Can be changed (through a timelocked proposal)
  • Range: 1 day to 30 days typically

Expiration Period: How long a transaction stays executable before it expires.

  • Also set during deployment
  • Also changeable through timelock
  • Should be at least as long as the delay

Example config:

  • Minimum delay: 7 days
  • Expiration period: 14 days
  • Result: Schedule on day 0, executable from day 7 to day 21

Max Concurrent Proposals

There's a limit of 100 proposals in the queue at once.

This exists because canceling all proposals (if needed) must fit in one transaction. With thousands of proposals, it could run out of gas.

For most users, 100 is way more than enough. If you hit the limit, either:

  • Execute some proposals
  • Let some expire
  • Clean up expired ones manually

The Proposal Queue

All pending and executable proposals live in an on-chain queue.

You can view this queue:

  • In the Kleidi web app
  • On a block explorer (if you know what to look for)
  • By calling the timelock contract directly

The queue shows:

  • What each proposal does
  • When it was scheduled
  • When it becomes executable
  • When it expires
  • Current status

Technical Implementation

For those interested in the details:

The timelock stores proposals in a mapping keyed by a hash of:

  • Target address
  • Value (ETH amount)
  • Calldata
  • Salt

When you schedule a proposal, it records:

  • Timestamp when scheduled
  • Calculated execution time (timestamp + delay)
  • Calculated expiration time (execution time + expiration period)

Proposals can be in one of these states:

  • Not scheduled (default, hash doesn't exist)
  • Scheduled but not yet executable
  • Executable but not yet expired
  • Expired
  • Executed

Common Questions

Can I speed up a transaction?

No. The delay is fixed. Even if everyone agrees and it's urgent, you cannot skip the delay. That's the whole point.

What if I made a mistake?

Cancel it immediately and schedule a new one with the correct parameters. You'll wait through the full delay again, but at least it will be right.

Can an attacker cancel my legitimate proposals?

Only if they control enough signers to meet your threshold. In that case, they could also schedule malicious proposals, so you have bigger problems.

What happens during the delay?

Nothing. The transaction just waits. It's visible on-chain for anyone to inspect. This transparency is a feature, not a bug.

Do I need to be online to execute?

No. Once a transaction is executable, it stays that way until someone executes it or it expires. You can execute it whenever is convenient within that window.

Can I change the delay period?

Yes, by scheduling a transaction that calls the updateDelay function on the timelock contract. That change itself will require waiting through your current delay.