This repository has been archived by the owner on Dec 7, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 16
/
Cooldown.sol
79 lines (75 loc) · 3.75 KB
/
Cooldown.sol
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
// SPDX-License-Identifier: CAL
pragma solidity ^0.8.10;
/// @title Cooldown
/// @notice `Cooldown` is a base contract that rate limits functions on
/// the implementing contract per `msg.sender`.
///
/// Each time a function with the `onlyAfterCooldown` modifier is called the
/// `msg.sender` must wait N blocks before calling any modified function.
///
/// This does nothing to prevent sybils who can generate an arbitrary number of
/// `msg.sender` values in parallel to spam a contract.
///
/// `Cooldown` is intended to prevent rapid state cycling to grief a contract,
/// such as rapidly locking and unlocking a large amount of capital in the
/// `SeedERC20` contract.
///
/// Requiring a lock/deposit of significant economic stake that sybils will not
/// have access to AND applying a cooldown IS a sybil mitigation. The economic
/// stake alone is NOT sufficient if gas is cheap as sybils can cycle the same
/// stake between each other. The cooldown alone is NOT sufficient as many
/// sybils can be created, each as a new `msg.sender`.
///
/// @dev Base for anything that enforces a cooldown delay on functions.
/// `Cooldown` requires a minimum time in blocks to elapse between actions that
/// cooldown. The modifier `onlyAfterCooldown` both enforces and triggers the
/// cooldown. There is a single cooldown across all functions per-contract
/// so any function call that requires a cooldown will also trigger it for
/// all other functions.
///
/// Cooldown is NOT an effective sybil resistance alone, as the cooldown is
/// per-address only. It is always possible for many accounts to be created
/// to spam a contract with dust in parallel.
/// Cooldown is useful to stop a single account rapidly cycling contract
/// state in a way that can be disruptive to peers. Cooldown works best when
/// coupled with economic stake associated with each state change so that
/// peers must lock capital during the cooldown. `Cooldown` tracks the first
/// `msg.sender` it sees for a call stack so cooldowns are enforced across
/// reentrant code. Any function that enforces a cooldown also has reentrancy
/// protection.
contract Cooldown {
event CooldownInitialize(address sender, uint256 cooldownDuration);
event CooldownTriggered(address caller, uint256 cooldown);
/// Time in blocks to restrict access to modified functions.
uint256 internal cooldownDuration;
/// Every caller has its own cooldown, the minimum block that the caller
/// call another function sharing the same cooldown state.
mapping(address => uint256) private cooldowns;
address private caller;
/// Initialize the cooldown duration.
/// The cooldown duration is global to the contract.
/// Cooldown duration must be greater than 0.
/// Cooldown duration can only be set once.
/// @param cooldownDuration_ The global cooldown duration.
function initializeCooldown(uint256 cooldownDuration_) internal {
require(cooldownDuration_ > 0, "COOLDOWN_0");
// Reinitialization is a bug.
assert(cooldownDuration == 0);
cooldownDuration = cooldownDuration_;
emit CooldownInitialize(msg.sender, cooldownDuration_);
}
/// Modifies a function to enforce the cooldown for `msg.sender`.
/// Saves the original caller so that cooldowns are enforced across
/// reentrant code.
modifier onlyAfterCooldown() {
address caller_ = caller == address(0) ? caller = msg.sender : caller;
require(cooldowns[caller_] <= block.number, "COOLDOWN");
// Every action that requires a cooldown also triggers a cooldown.
uint256 cooldown_ = block.number + cooldownDuration;
cooldowns[caller_] = cooldown_;
emit CooldownTriggered(caller_, cooldown_);
_;
// Refund as much gas as we can.
delete caller;
}
}