Timelock with Scheduler

Beyond the beginner friendly Soroban basics? Let’s now look at a larger Smart Contract example.

As usual we’ll get to the essence of the code. We’ll stick to Alice and Bob for familiarity.

What are we trying to achieve?

The TimeLockContract is a Soroban smart contract designed to manage delayed execution of operations in a secure and auditable way. Alice and Bob can use this contract to schedule tasks that execute only after a specified delay, ensuring accountability and preventing premature execution, and nobody wants premature execution right? 😃

Alice

1. initialize():

  • Who can call it? Likely, only the contract owner or a privileged entity (perhaps Alice). Alice? Who the f*** is Alice?
  • Why? Initialization requires setting the MinDelay and ensuring the contract is not already initialized, which usually happens once for the entire contract setup. This is a setup function, so only one entity (like Alice) is typically responsible.

2. schedule():

  • Who can call it? Both Alice and Bob can potentially schedule operations.
  • Why? Anyone can schedule a time-locked operation by providing the target contract address, function name, parameters, and delay. Alice and Bob can both schedule tasks, as long as the delay meets the minimum required.

3. execute():

  • Who can call it? Both Alice and Bob can execute the operation as long as the conditions are met (i.e., the operation is READY and has no unsatisfied predecessor).
  • Why? Execution requires checking whether the operation is ready and if its predecessor has completed. Alice or Bob can execute, but they need to ensure these conditions are satisfied before proceeding.

4. cancel():

  • Who can call it? Both Alice and Bob could potentially cancel operations if they are in the WAITING or READY state.
  • Why? This allows either party to remove scheduled tasks, provided they are not in the EXECUTED state. If an operation is already executed, no one can cancel it.

5. update_min_delay():

  • Who can call it? Likely only Alice (the contract owner or admin).
  • Why? Changing the minimum delay would usually be a privilege of the contract owner (Alice), as it affects all scheduled operations.

6. get_schedule_lock_time():

  • Who can call it? Both Alice and Bob.
  • Why? This is a read-only function that returns the lock time for a specific operation. It does not require special permissions, so both Alice and Bob can query it.

7. get_operation_state():

  • Who can call it? Both Alice and Bob.
  • Why? This function checks the state of an operation, which is helpful to determine if the operation can be executed. It’s typically used for querying, so both parties could call it.

8. Internal Helpers:

  • These functions are used internally by the contract logic and would not be directly called by Alice or Bob, but they are essential for the flow of the schedule, execute, and cancel operations.

Soroban Smart Contract

Full code

Summary:

  • Alice is likely to be the admin or owner who can call initialization and update the minimum delay.
  • Both Alice and Bob can interact with the contract for scheduling operations, executing them (if conditions are met), and canceling operations.

Note, this is taken from : https://soroban-by-example.vercel.app/

+---------------------------+
|       initialize()        |
|---------------------------|
|  Set MinDelay in storage  |
|  Check initialization     |
|  Emit "Initialized" Event |
+---------------------------+
           |
           v
+---------------------------+
|        schedule()         |
|---------------------------|
|  Validate delay           |
|  Generate operation_id    |
|  Add operation to storage |
|  Emit "CallScheduled"     |
+---------------------------+
           |
           v
+---------------------------+
|        execute()          |
|---------------------------|
|  Validate state: READY    |
|  Validate predecessor     |
|  Execute task:            |
|    - Native/External call |
|  Mark operation as DONE   |
|  Emit "CallExecuted"      |
+---------------------------+
           |
           v
+---------------------------+
|       cancel()            |
|---------------------------|
|  Validate state:          |
|    READY or WAITING       |
|  Remove operation         |
|  Emit "OperationCancelled"|
+---------------------------+
           |
           v
+---------------------------+
|    update_min_delay()     |
|---------------------------|
|  Update MinDelay in       |
|  storage and emit event   |
+---------------------------+
           |
           v
+---------------------------+
|   get_schedule_lock_time()|
|---------------------------|
|  Retrieve lock time from  |
|  storage for operation_id |
+---------------------------+
           |
           v
+---------------------------+
|   get_operation_state()   |
|---------------------------|
|  Check operation state:   |
|    UNSET, WAITING, READY, |
|    EXECUTED               |
+---------------------------+
           |
           v
+---------------------------+
|  Internal Helpers:        |
|---------------------------|
|  hash_call()              |
|    - Generate operation_id|
|  add_operation()          |
|    - Add operation to     |
|      persistent storage   |
|  check_execute()          |
|    - Validate state       |
|  exec_external()          |
|    - Call external        |
|  exec_native()            |
|    - Call native function |
+---------------------------+

What is the min delay?

The “min delay” in this contract is a critical component designed to enforce a minimum waiting period before scheduled operations can be executed. Its purpose is to:

1. Introduce a Buffer for Security

  • The min_delay ensures that no operation can be executed immediately after being scheduled. This delay acts as a buffer period, allowing stakeholders or administrators time to review, verify, or cancel potentially malicious or incorrect operations. 🤔

2. Prevent Immediate Execution

  • When scheduling an operation with the schedule function, the delay parameter must be at least the value of min_delay. If it is not, the contract will panic with the InsufficientDelay error.
  • This prevents any operation from bypassing the mandatory waiting period.

3. Allow Configurability

  • The initialize function sets the min_delay at contract deployment, with an upper limit of 30 days (MAX_MIN_DELAY).
  • The delay can be adjusted later using the update_min_delay function so that administrators can respond to changing requirements if need be.

Why Is It Important?

  1. Protection Against Immediate Exploitation:
    • Without a minimum delay, an attacker who gains control of the contract could schedule and execute harmful operations immediately, leaving no time for intervention.🫣
  2. Time for Monitoring:
    • A delay provides time to monitor the blockchain for any unauthorized or suspicious scheduled operations.
  3. Administrative Oversight:
    • It allows administrators or trusted parties to review the upcoming actions before they are executed.
checking a smart contract!

Note how the contract makes good use of Rust enums? DataKey, OperationState, TimeLockError.