|
| 1 | +// SPDX-License-Identifier: MIT |
| 2 | +// OpenZeppelin Contracts (last updated v5.0.0) (utils/types/Time.sol) |
| 3 | + |
| 4 | +pragma solidity ^0.8.20; |
| 5 | + |
| 6 | +import {Math} from "../math/Math.sol"; |
| 7 | +import {SafeCast} from "../math/SafeCast.sol"; |
| 8 | + |
| 9 | +/** |
| 10 | + * @dev This library provides helpers for manipulating time-related objects. |
| 11 | + * |
| 12 | + * It uses the following types: |
| 13 | + * - `uint48` for timepoints |
| 14 | + * - `uint32` for durations |
| 15 | + * |
| 16 | + * While the library doesn't provide specific types for timepoints and duration, it does provide: |
| 17 | + * - a `Delay` type to represent duration that can be programmed to change value automatically at a given point |
| 18 | + * - additional helper functions |
| 19 | + */ |
| 20 | +library Time { |
| 21 | + using Time for *; |
| 22 | + |
| 23 | + /** |
| 24 | + * @dev Get the block timestamp as a Timepoint. |
| 25 | + */ |
| 26 | + function timestamp() internal view returns (uint48) { |
| 27 | + return SafeCast.toUint48(block.timestamp); |
| 28 | + } |
| 29 | + |
| 30 | + /** |
| 31 | + * @dev Get the block number as a Timepoint. |
| 32 | + */ |
| 33 | + function blockNumber() internal view returns (uint48) { |
| 34 | + return SafeCast.toUint48(block.number); |
| 35 | + } |
| 36 | + |
| 37 | + // ==================================================== Delay ===================================================== |
| 38 | + /** |
| 39 | + * @dev A `Delay` is a uint32 duration that can be programmed to change value automatically at a given point in the |
| 40 | + * future. The "effect" timepoint describes when the transitions happens from the "old" value to the "new" value. |
| 41 | + * This allows updating the delay applied to some operation while keeping some guarantees. |
| 42 | + * |
| 43 | + * In particular, the {update} function guarantees that if the delay is reduced, the old delay still applies for |
| 44 | + * some time. For example if the delay is currently 7 days to do an upgrade, the admin should not be able to set |
| 45 | + * the delay to 0 and upgrade immediately. If the admin wants to reduce the delay, the old delay (7 days) should |
| 46 | + * still apply for some time. |
| 47 | + * |
| 48 | + * |
| 49 | + * The `Delay` type is 112 bits long, and packs the following: |
| 50 | + * |
| 51 | + * ``` |
| 52 | + * | [uint48]: effect date (timepoint) |
| 53 | + * | | [uint32]: value before (duration) |
| 54 | + * ↓ ↓ ↓ [uint32]: value after (duration) |
| 55 | + * 0xAAAAAAAAAAAABBBBBBBBCCCCCCCC |
| 56 | + * ``` |
| 57 | + * |
| 58 | + * NOTE: The {get} and {withUpdate} functions operate using timestamps. Block number based delays are not currently |
| 59 | + * supported. |
| 60 | + */ |
| 61 | + type Delay is uint112; |
| 62 | + |
| 63 | + /** |
| 64 | + * @dev Wrap a duration into a Delay to add the one-step "update in the future" feature |
| 65 | + */ |
| 66 | + function toDelay(uint32 duration) internal pure returns (Delay) { |
| 67 | + return Delay.wrap(duration); |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * @dev Get the value at a given timepoint plus the pending value and effect timepoint if there is a scheduled |
| 72 | + * change after this timepoint. If the effect timepoint is 0, then the pending value should not be considered. |
| 73 | + */ |
| 74 | + function _getFullAt(Delay self, uint48 timepoint) |
| 75 | + private |
| 76 | + pure |
| 77 | + returns (uint32, uint32, uint48) |
| 78 | + { |
| 79 | + (uint32 valueBefore, uint32 valueAfter, uint48 effect) = self.unpack(); |
| 80 | + return effect <= timepoint ? (valueAfter, 0, 0) : (valueBefore, valueAfter, effect); |
| 81 | + } |
| 82 | + |
| 83 | + /** |
| 84 | + * @dev Get the current value plus the pending value and effect timepoint if there is a scheduled change. If the |
| 85 | + * effect timepoint is 0, then the pending value should not be considered. |
| 86 | + */ |
| 87 | + function getFull(Delay self) internal view returns (uint32, uint32, uint48) { |
| 88 | + return _getFullAt(self, timestamp()); |
| 89 | + } |
| 90 | + |
| 91 | + /** |
| 92 | + * @dev Get the current value. |
| 93 | + */ |
| 94 | + function get(Delay self) internal view returns (uint32) { |
| 95 | + (uint32 delay,,) = self.getFull(); |
| 96 | + return delay; |
| 97 | + } |
| 98 | + |
| 99 | + /** |
| 100 | + * @dev Update a Delay object so that it takes a new duration after a timepoint that is automatically computed to |
| 101 | + * enforce the old delay at the moment of the update. Returns the updated Delay object and the timestamp when the |
| 102 | + * new delay becomes effective. |
| 103 | + */ |
| 104 | + function withUpdate(Delay self, uint32 newValue, uint32 minSetback) |
| 105 | + internal |
| 106 | + view |
| 107 | + returns (Delay updatedDelay, uint48 effect) |
| 108 | + { |
| 109 | + uint32 value = self.get(); |
| 110 | + uint32 setback = uint32(Math.max(minSetback, value > newValue ? value - newValue : 0)); |
| 111 | + effect = timestamp() + setback; |
| 112 | + return (pack(value, newValue, effect), effect); |
| 113 | + } |
| 114 | + |
| 115 | + /** |
| 116 | + * @dev Split a delay into its components: valueBefore, valueAfter and effect (transition timepoint). |
| 117 | + */ |
| 118 | + function unpack(Delay self) |
| 119 | + internal |
| 120 | + pure |
| 121 | + returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) |
| 122 | + { |
| 123 | + uint112 raw = Delay.unwrap(self); |
| 124 | + |
| 125 | + valueAfter = uint32(raw); |
| 126 | + valueBefore = uint32(raw >> 32); |
| 127 | + effect = uint48(raw >> 64); |
| 128 | + |
| 129 | + return (valueBefore, valueAfter, effect); |
| 130 | + } |
| 131 | + |
| 132 | + /** |
| 133 | + * @dev pack the components into a Delay object. |
| 134 | + */ |
| 135 | + function pack(uint32 valueBefore, uint32 valueAfter, uint48 effect) |
| 136 | + internal |
| 137 | + pure |
| 138 | + returns (Delay) |
| 139 | + { |
| 140 | + return |
| 141 | + Delay.wrap((uint112(effect) << 64) | (uint112(valueBefore) << 32) | uint112(valueAfter)); |
| 142 | + } |
| 143 | +} |
0 commit comments