Time lock
The admin queues a payment for a future time, and it can execute only after a minimum delay and before a grace period ends. Watchers get time to react before it runs.
time-lock.lithe
(* Payments are queued with a delay, so anyone watching has time to react before
they execute. The admin can cancel a queued payment. *)
import "std/abi";
import "std/crypto";
import "std/evm";
error NotAdmin;
error DelayTooShort of uint;
error NotQueued of bytes32;
error TooEarly of record {
now : timestamp;
eta : timestamp;
}
error Stale of bytes32;
event Queued of record {
indexed id : bytes32;
to : address;
value : wei;
eta : timestamp;
}
event Executed of record {
indexed id : bytes32;
}
event Cancelled of record {
indexed id : bytes32;
}
storage is record {
admin : address;
queued : map(bytes32, bool);
}
pure function minDelay() : uint is 2days
pure function gracePeriod() : uint is 14days
external function init() is {
storage.admin := Evm.sender();
}
view function onlyAdmin() raises NotAdmin is {
assert(Evm.sender() = storage.admin, NotAdmin);
}
external pure function paymentId(const to : address; const value : wei; const eta : timestamp) : bytes32 is
Crypto.keccak256(Abi.encode(to, value, eta))
external function queue(const to : address; const value : wei; const eta : timestamp) : bytes32
raises NotAdmin | DelayTooShort
is {
onlyAdmin();
assert(eta >= Evm.blockTimestamp() + minDelay(), DelayTooShort(minDelay()));
const id : bytes32 := Crypto.keccak256(Abi.encode(to, value, eta));
storage.queued[id] := true;
emit Queued { id = id; to = to; value = value; eta = eta; };
} with id
external function cancel(const id : bytes32)
raises NotAdmin | NotQueued
is {
onlyAdmin();
assert(storage.queued[id], NotQueued(id));
storage.queued[id] := false;
emit Cancelled { id = id; };
}
external nonreentrant function execute(const to : address; const value : wei; const eta : timestamp)
raises NotAdmin | NotQueued | TooEarly | Stale
is {
onlyAdmin();
const id : bytes32 := Crypto.keccak256(Abi.encode(to, value, eta));
assert(storage.queued[id], NotQueued(id));
assert(Evm.blockTimestamp() >= eta, TooEarly { now = Evm.blockTimestamp(); eta = eta; });
assert(Evm.blockTimestamp() <= eta + gracePeriod(), Stale(id));
storage.queued[id] := false;
emit Executed { id = id; };
defer Evm.transfer(to, value);
}
external payable function fund() is {
skip
}
