Checkpoints.sol 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/Checkpoints.sol)
  3. // This file was procedurally generated from scripts/generate/templates/Checkpoints.js.
  4. pragma solidity ^0.8.20;
  5. import {Math} from "../math/Math.sol";
  6. /**
  7. * @dev This library defines the `Trace*` struct, for checkpointing values as they change at different points in
  8. * time, and later looking up past values by block number. See {Votes} as an example.
  9. *
  10. * To create a history of checkpoints define a variable type `Checkpoints.Trace*` in your contract, and store a new
  11. * checkpoint for the current transaction block using the {push} function.
  12. */
  13. library Checkpoints {
  14. /**
  15. * @dev A value was attempted to be inserted on a past checkpoint.
  16. */
  17. error CheckpointUnorderedInsertion();
  18. struct Trace224 {
  19. Checkpoint224[] _checkpoints;
  20. }
  21. struct Checkpoint224 {
  22. uint32 _key;
  23. uint224 _value;
  24. }
  25. /**
  26. * @dev Pushes a (`key`, `value`) pair into a Trace224 so that it is stored as the checkpoint.
  27. *
  28. * Returns previous value and new value.
  29. *
  30. * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint32).max` key set will disable the
  31. * library.
  32. */
  33. function push(
  34. Trace224 storage self,
  35. uint32 key,
  36. uint224 value
  37. ) internal returns (uint224 oldValue, uint224 newValue) {
  38. return _insert(self._checkpoints, key, value);
  39. }
  40. /**
  41. * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
  42. * there is none.
  43. */
  44. function lowerLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
  45. uint256 len = self._checkpoints.length;
  46. uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
  47. return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
  48. }
  49. /**
  50. * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
  51. * if there is none.
  52. */
  53. function upperLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
  54. uint256 len = self._checkpoints.length;
  55. uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
  56. return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
  57. }
  58. /**
  59. * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
  60. * if there is none.
  61. *
  62. * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
  63. * keys).
  64. */
  65. function upperLookupRecent(Trace224 storage self, uint32 key) internal view returns (uint224) {
  66. uint256 len = self._checkpoints.length;
  67. uint256 low = 0;
  68. uint256 high = len;
  69. if (len > 5) {
  70. uint256 mid = len - Math.sqrt(len);
  71. if (key < _unsafeAccess(self._checkpoints, mid)._key) {
  72. high = mid;
  73. } else {
  74. low = mid + 1;
  75. }
  76. }
  77. uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
  78. return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
  79. }
  80. /**
  81. * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
  82. */
  83. function latest(Trace224 storage self) internal view returns (uint224) {
  84. uint256 pos = self._checkpoints.length;
  85. return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
  86. }
  87. /**
  88. * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
  89. * in the most recent checkpoint.
  90. */
  91. function latestCheckpoint(Trace224 storage self) internal view returns (bool exists, uint32 _key, uint224 _value) {
  92. uint256 pos = self._checkpoints.length;
  93. if (pos == 0) {
  94. return (false, 0, 0);
  95. } else {
  96. Checkpoint224 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);
  97. return (true, ckpt._key, ckpt._value);
  98. }
  99. }
  100. /**
  101. * @dev Returns the number of checkpoints.
  102. */
  103. function length(Trace224 storage self) internal view returns (uint256) {
  104. return self._checkpoints.length;
  105. }
  106. /**
  107. * @dev Returns checkpoint at given position.
  108. */
  109. function at(Trace224 storage self, uint32 pos) internal view returns (Checkpoint224 memory) {
  110. return self._checkpoints[pos];
  111. }
  112. /**
  113. * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
  114. * or by updating the last one.
  115. */
  116. function _insert(
  117. Checkpoint224[] storage self,
  118. uint32 key,
  119. uint224 value
  120. ) private returns (uint224 oldValue, uint224 newValue) {
  121. uint256 pos = self.length;
  122. if (pos > 0) {
  123. Checkpoint224 storage last = _unsafeAccess(self, pos - 1);
  124. uint32 lastKey = last._key;
  125. uint224 lastValue = last._value;
  126. // Checkpoint keys must be non-decreasing.
  127. if (lastKey > key) {
  128. revert CheckpointUnorderedInsertion();
  129. }
  130. // Update or push new checkpoint
  131. if (lastKey == key) {
  132. last._value = value;
  133. } else {
  134. self.push(Checkpoint224({_key: key, _value: value}));
  135. }
  136. return (lastValue, value);
  137. } else {
  138. self.push(Checkpoint224({_key: key, _value: value}));
  139. return (0, value);
  140. }
  141. }
  142. /**
  143. * @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`
  144. * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
  145. * `high`.
  146. *
  147. * WARNING: `high` should not be greater than the array's length.
  148. */
  149. function _upperBinaryLookup(
  150. Checkpoint224[] storage self,
  151. uint32 key,
  152. uint256 low,
  153. uint256 high
  154. ) private view returns (uint256) {
  155. while (low < high) {
  156. uint256 mid = Math.average(low, high);
  157. if (_unsafeAccess(self, mid)._key > key) {
  158. high = mid;
  159. } else {
  160. low = mid + 1;
  161. }
  162. }
  163. return high;
  164. }
  165. /**
  166. * @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`
  167. * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
  168. * `high`.
  169. *
  170. * WARNING: `high` should not be greater than the array's length.
  171. */
  172. function _lowerBinaryLookup(
  173. Checkpoint224[] storage self,
  174. uint32 key,
  175. uint256 low,
  176. uint256 high
  177. ) private view returns (uint256) {
  178. while (low < high) {
  179. uint256 mid = Math.average(low, high);
  180. if (_unsafeAccess(self, mid)._key < key) {
  181. low = mid + 1;
  182. } else {
  183. high = mid;
  184. }
  185. }
  186. return high;
  187. }
  188. /**
  189. * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
  190. */
  191. function _unsafeAccess(
  192. Checkpoint224[] storage self,
  193. uint256 pos
  194. ) private pure returns (Checkpoint224 storage result) {
  195. assembly {
  196. mstore(0, self.slot)
  197. result.slot := add(keccak256(0, 0x20), pos)
  198. }
  199. }
  200. struct Trace208 {
  201. Checkpoint208[] _checkpoints;
  202. }
  203. struct Checkpoint208 {
  204. uint48 _key;
  205. uint208 _value;
  206. }
  207. /**
  208. * @dev Pushes a (`key`, `value`) pair into a Trace208 so that it is stored as the checkpoint.
  209. *
  210. * Returns previous value and new value.
  211. *
  212. * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
  213. * library.
  214. */
  215. function push(
  216. Trace208 storage self,
  217. uint48 key,
  218. uint208 value
  219. ) internal returns (uint208 oldValue, uint208 newValue) {
  220. return _insert(self._checkpoints, key, value);
  221. }
  222. /**
  223. * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
  224. * there is none.
  225. */
  226. function lowerLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {
  227. uint256 len = self._checkpoints.length;
  228. uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
  229. return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
  230. }
  231. /**
  232. * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
  233. * if there is none.
  234. */
  235. function upperLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {
  236. uint256 len = self._checkpoints.length;
  237. uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
  238. return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
  239. }
  240. /**
  241. * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
  242. * if there is none.
  243. *
  244. * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
  245. * keys).
  246. */
  247. function upperLookupRecent(Trace208 storage self, uint48 key) internal view returns (uint208) {
  248. uint256 len = self._checkpoints.length;
  249. uint256 low = 0;
  250. uint256 high = len;
  251. if (len > 5) {
  252. uint256 mid = len - Math.sqrt(len);
  253. if (key < _unsafeAccess(self._checkpoints, mid)._key) {
  254. high = mid;
  255. } else {
  256. low = mid + 1;
  257. }
  258. }
  259. uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
  260. return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
  261. }
  262. /**
  263. * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
  264. */
  265. function latest(Trace208 storage self) internal view returns (uint208) {
  266. uint256 pos = self._checkpoints.length;
  267. return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
  268. }
  269. /**
  270. * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
  271. * in the most recent checkpoint.
  272. */
  273. function latestCheckpoint(Trace208 storage self) internal view returns (bool exists, uint48 _key, uint208 _value) {
  274. uint256 pos = self._checkpoints.length;
  275. if (pos == 0) {
  276. return (false, 0, 0);
  277. } else {
  278. Checkpoint208 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);
  279. return (true, ckpt._key, ckpt._value);
  280. }
  281. }
  282. /**
  283. * @dev Returns the number of checkpoints.
  284. */
  285. function length(Trace208 storage self) internal view returns (uint256) {
  286. return self._checkpoints.length;
  287. }
  288. /**
  289. * @dev Returns checkpoint at given position.
  290. */
  291. function at(Trace208 storage self, uint32 pos) internal view returns (Checkpoint208 memory) {
  292. return self._checkpoints[pos];
  293. }
  294. /**
  295. * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
  296. * or by updating the last one.
  297. */
  298. function _insert(
  299. Checkpoint208[] storage self,
  300. uint48 key,
  301. uint208 value
  302. ) private returns (uint208 oldValue, uint208 newValue) {
  303. uint256 pos = self.length;
  304. if (pos > 0) {
  305. Checkpoint208 storage last = _unsafeAccess(self, pos - 1);
  306. uint48 lastKey = last._key;
  307. uint208 lastValue = last._value;
  308. // Checkpoint keys must be non-decreasing.
  309. if (lastKey > key) {
  310. revert CheckpointUnorderedInsertion();
  311. }
  312. // Update or push new checkpoint
  313. if (lastKey == key) {
  314. last._value = value;
  315. } else {
  316. self.push(Checkpoint208({_key: key, _value: value}));
  317. }
  318. return (lastValue, value);
  319. } else {
  320. self.push(Checkpoint208({_key: key, _value: value}));
  321. return (0, value);
  322. }
  323. }
  324. /**
  325. * @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`
  326. * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
  327. * `high`.
  328. *
  329. * WARNING: `high` should not be greater than the array's length.
  330. */
  331. function _upperBinaryLookup(
  332. Checkpoint208[] storage self,
  333. uint48 key,
  334. uint256 low,
  335. uint256 high
  336. ) private view returns (uint256) {
  337. while (low < high) {
  338. uint256 mid = Math.average(low, high);
  339. if (_unsafeAccess(self, mid)._key > key) {
  340. high = mid;
  341. } else {
  342. low = mid + 1;
  343. }
  344. }
  345. return high;
  346. }
  347. /**
  348. * @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`
  349. * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
  350. * `high`.
  351. *
  352. * WARNING: `high` should not be greater than the array's length.
  353. */
  354. function _lowerBinaryLookup(
  355. Checkpoint208[] storage self,
  356. uint48 key,
  357. uint256 low,
  358. uint256 high
  359. ) private view returns (uint256) {
  360. while (low < high) {
  361. uint256 mid = Math.average(low, high);
  362. if (_unsafeAccess(self, mid)._key < key) {
  363. low = mid + 1;
  364. } else {
  365. high = mid;
  366. }
  367. }
  368. return high;
  369. }
  370. /**
  371. * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
  372. */
  373. function _unsafeAccess(
  374. Checkpoint208[] storage self,
  375. uint256 pos
  376. ) private pure returns (Checkpoint208 storage result) {
  377. assembly {
  378. mstore(0, self.slot)
  379. result.slot := add(keccak256(0, 0x20), pos)
  380. }
  381. }
  382. struct Trace160 {
  383. Checkpoint160[] _checkpoints;
  384. }
  385. struct Checkpoint160 {
  386. uint96 _key;
  387. uint160 _value;
  388. }
  389. /**
  390. * @dev Pushes a (`key`, `value`) pair into a Trace160 so that it is stored as the checkpoint.
  391. *
  392. * Returns previous value and new value.
  393. *
  394. * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint96).max` key set will disable the
  395. * library.
  396. */
  397. function push(
  398. Trace160 storage self,
  399. uint96 key,
  400. uint160 value
  401. ) internal returns (uint160 oldValue, uint160 newValue) {
  402. return _insert(self._checkpoints, key, value);
  403. }
  404. /**
  405. * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
  406. * there is none.
  407. */
  408. function lowerLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
  409. uint256 len = self._checkpoints.length;
  410. uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
  411. return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
  412. }
  413. /**
  414. * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
  415. * if there is none.
  416. */
  417. function upperLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
  418. uint256 len = self._checkpoints.length;
  419. uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
  420. return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
  421. }
  422. /**
  423. * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
  424. * if there is none.
  425. *
  426. * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
  427. * keys).
  428. */
  429. function upperLookupRecent(Trace160 storage self, uint96 key) internal view returns (uint160) {
  430. uint256 len = self._checkpoints.length;
  431. uint256 low = 0;
  432. uint256 high = len;
  433. if (len > 5) {
  434. uint256 mid = len - Math.sqrt(len);
  435. if (key < _unsafeAccess(self._checkpoints, mid)._key) {
  436. high = mid;
  437. } else {
  438. low = mid + 1;
  439. }
  440. }
  441. uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
  442. return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
  443. }
  444. /**
  445. * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
  446. */
  447. function latest(Trace160 storage self) internal view returns (uint160) {
  448. uint256 pos = self._checkpoints.length;
  449. return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
  450. }
  451. /**
  452. * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
  453. * in the most recent checkpoint.
  454. */
  455. function latestCheckpoint(Trace160 storage self) internal view returns (bool exists, uint96 _key, uint160 _value) {
  456. uint256 pos = self._checkpoints.length;
  457. if (pos == 0) {
  458. return (false, 0, 0);
  459. } else {
  460. Checkpoint160 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);
  461. return (true, ckpt._key, ckpt._value);
  462. }
  463. }
  464. /**
  465. * @dev Returns the number of checkpoints.
  466. */
  467. function length(Trace160 storage self) internal view returns (uint256) {
  468. return self._checkpoints.length;
  469. }
  470. /**
  471. * @dev Returns checkpoint at given position.
  472. */
  473. function at(Trace160 storage self, uint32 pos) internal view returns (Checkpoint160 memory) {
  474. return self._checkpoints[pos];
  475. }
  476. /**
  477. * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
  478. * or by updating the last one.
  479. */
  480. function _insert(
  481. Checkpoint160[] storage self,
  482. uint96 key,
  483. uint160 value
  484. ) private returns (uint160 oldValue, uint160 newValue) {
  485. uint256 pos = self.length;
  486. if (pos > 0) {
  487. Checkpoint160 storage last = _unsafeAccess(self, pos - 1);
  488. uint96 lastKey = last._key;
  489. uint160 lastValue = last._value;
  490. // Checkpoint keys must be non-decreasing.
  491. if (lastKey > key) {
  492. revert CheckpointUnorderedInsertion();
  493. }
  494. // Update or push new checkpoint
  495. if (lastKey == key) {
  496. last._value = value;
  497. } else {
  498. self.push(Checkpoint160({_key: key, _value: value}));
  499. }
  500. return (lastValue, value);
  501. } else {
  502. self.push(Checkpoint160({_key: key, _value: value}));
  503. return (0, value);
  504. }
  505. }
  506. /**
  507. * @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`
  508. * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
  509. * `high`.
  510. *
  511. * WARNING: `high` should not be greater than the array's length.
  512. */
  513. function _upperBinaryLookup(
  514. Checkpoint160[] storage self,
  515. uint96 key,
  516. uint256 low,
  517. uint256 high
  518. ) private view returns (uint256) {
  519. while (low < high) {
  520. uint256 mid = Math.average(low, high);
  521. if (_unsafeAccess(self, mid)._key > key) {
  522. high = mid;
  523. } else {
  524. low = mid + 1;
  525. }
  526. }
  527. return high;
  528. }
  529. /**
  530. * @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`
  531. * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
  532. * `high`.
  533. *
  534. * WARNING: `high` should not be greater than the array's length.
  535. */
  536. function _lowerBinaryLookup(
  537. Checkpoint160[] storage self,
  538. uint96 key,
  539. uint256 low,
  540. uint256 high
  541. ) private view returns (uint256) {
  542. while (low < high) {
  543. uint256 mid = Math.average(low, high);
  544. if (_unsafeAccess(self, mid)._key < key) {
  545. low = mid + 1;
  546. } else {
  547. high = mid;
  548. }
  549. }
  550. return high;
  551. }
  552. /**
  553. * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
  554. */
  555. function _unsafeAccess(
  556. Checkpoint160[] storage self,
  557. uint256 pos
  558. ) private pure returns (Checkpoint160 storage result) {
  559. assembly {
  560. mstore(0, self.slot)
  561. result.slot := add(keccak256(0, 0x20), pos)
  562. }
  563. }
  564. }