1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123 | contract TimeLock {
event QueueTransaction(
bytes32 indexed txHash,
address indexed target,
uint value,
string signature,
bytes data,
uint eta
);
event ExecuteTransaction(
bytes32 indexed txHash,
address indexed target,
uint value,
string signature,
bytes data,
uint eta
);
event CancelTransaction(
bytes32 indexed txHash,
address indexed target,
uint value,
string signature,
bytes data,
uint eta
);
uint public constant GRACE_PERIOD = 14 days;
uint public constant MINIMUM_DELAY = 2 days;
uint public constant MAXIMUM_DELAY = 30 days;
address public admin;
uint public delay;
mapping(bytes32 => bool) public queuedTransactions;
constructor(uint _delay) {
require(
_delay >= MINIMUM_DELAY && _delay <= MAXIMUM_DELAY,
"Delay not in range"
);
admin = msg.sender;
delay = _delay;
}
function queueTransaction(
address target,
uint value,
string memory signature,
bytes memory data,
uint eta
) public returns (bytes32) {
require(msg.sender == admin, "Not admin");
require(
eta >= block.timestamp + delay,
"Eta too soon"
);
bytes32 txHash = keccak256(
abi.encode(target, value, signature, data, eta)
);
queuedTransactions[txHash] = true;
emit QueueTransaction(txHash, target, value, signature, data, eta);
return txHash;
}
function executeTransaction(
address target,
uint value,
string memory signature,
bytes memory data,
uint eta
) public payable returns (bytes memory) {
require(msg.sender == admin, "Not admin");
bytes32 txHash = keccak256(
abi.encode(target, value, signature, data, eta)
);
require(queuedTransactions[txHash], "Tx not queued");
require(block.timestamp >= eta, "Too early");
require(
block.timestamp <= eta + GRACE_PERIOD,
"Tx stale"
);
queuedTransactions[txHash] = false;
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
callData = abi.encodePacked(
bytes4(keccak256(bytes(signature))),
data
);
}
(bool success, bytes memory returnData) = target.call{value: value}(
callData
);
require(success, "Tx failed");
emit ExecuteTransaction(txHash, target, value, signature, data, eta);
return returnData;
}
function cancelTransaction(
address target,
uint value,
string memory signature,
bytes memory data,
uint eta
) public {
require(msg.sender == admin, "Not admin");
bytes32 txHash = keccak256(
abi.encode(target, value, signature, data, eta)
);
queuedTransactions[txHash] = false;
emit CancelTransaction(txHash, target, value, signature, data, eta);
}
}
|