ERC20SnapshotUpgradeable.sol 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Snapshot.sol)
  3. pragma solidity ^0.8.0;
  4. import "../ERC20Upgradeable.sol";
  5. import "../../../utils/ArraysUpgradeable.sol";
  6. import "../../../utils/CountersUpgradeable.sol";
  7. import "../../../proxy/utils/Initializable.sol";
  8. /**
  9. * @dev This contract extends an ERC20 token with a snapshot mechanism. When a snapshot is created, the balances and
  10. * total supply at the time are recorded for later access.
  11. *
  12. * This can be used to safely create mechanisms based on token balances such as trustless dividends or weighted voting.
  13. * In naive implementations it's possible to perform a "double spend" attack by reusing the same balance from different
  14. * accounts. By using snapshots to calculate dividends or voting power, those attacks no longer apply. It can also be
  15. * used to create an efficient ERC20 forking mechanism.
  16. *
  17. * Snapshots are created by the internal {_snapshot} function, which will emit the {Snapshot} event and return a
  18. * snapshot id. To get the total supply at the time of a snapshot, call the function {totalSupplyAt} with the snapshot
  19. * id. To get the balance of an account at the time of a snapshot, call the {balanceOfAt} function with the snapshot id
  20. * and the account address.
  21. *
  22. * NOTE: Snapshot policy can be customized by overriding the {_getCurrentSnapshotId} method. For example, having it
  23. * return `block.number` will trigger the creation of snapshot at the begining of each new block. When overridding this
  24. * function, be careful about the monotonicity of its result. Non-monotonic snapshot ids will break the contract.
  25. *
  26. * Implementing snapshots for every block using this method will incur significant gas costs. For a gas-efficient
  27. * alternative consider {ERC20Votes}.
  28. *
  29. * ==== Gas Costs
  30. *
  31. * Snapshots are efficient. Snapshot creation is _O(1)_. Retrieval of balances or total supply from a snapshot is _O(log
  32. * n)_ in the number of snapshots that have been created, although _n_ for a specific account will generally be much
  33. * smaller since identical balances in subsequent snapshots are stored as a single entry.
  34. *
  35. * There is a constant overhead for normal ERC20 transfers due to the additional snapshot bookkeeping. This overhead is
  36. * only significant for the first transfer that immediately follows a snapshot for a particular account. Subsequent
  37. * transfers will have normal cost until the next snapshot, and so on.
  38. */
  39. abstract contract ERC20SnapshotUpgradeable is Initializable, ERC20Upgradeable {
  40. function __ERC20Snapshot_init() internal onlyInitializing {
  41. }
  42. function __ERC20Snapshot_init_unchained() internal onlyInitializing {
  43. }
  44. // Inspired by Jordi Baylina's MiniMeToken to record historical balances:
  45. // https://github.com/Giveth/minimd/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol
  46. using ArraysUpgradeable for uint256[];
  47. using CountersUpgradeable for CountersUpgradeable.Counter;
  48. // Snapshotted values have arrays of ids and the value corresponding to that id. These could be an array of a
  49. // Snapshot struct, but that would impede usage of functions that work on an array.
  50. struct Snapshots {
  51. uint256[] ids;
  52. uint256[] values;
  53. }
  54. mapping(address => Snapshots) private _accountBalanceSnapshots;
  55. Snapshots private _totalSupplySnapshots;
  56. // Snapshot ids increase monotonically, with the first value being 1. An id of 0 is invalid.
  57. CountersUpgradeable.Counter private _currentSnapshotId;
  58. /**
  59. * @dev Emitted by {_snapshot} when a snapshot identified by `id` is created.
  60. */
  61. event Snapshot(uint256 id);
  62. /**
  63. * @dev Creates a new snapshot and returns its snapshot id.
  64. *
  65. * Emits a {Snapshot} event that contains the same id.
  66. *
  67. * {_snapshot} is `internal` and you have to decide how to expose it externally. Its usage may be restricted to a
  68. * set of accounts, for example using {AccessControl}, or it may be open to the public.
  69. *
  70. * [WARNING]
  71. * ====
  72. * While an open way of calling {_snapshot} is required for certain trust minimization mechanisms such as forking,
  73. * you must consider that it can potentially be used by attackers in two ways.
  74. *
  75. * First, it can be used to increase the cost of retrieval of values from snapshots, although it will grow
  76. * logarithmically thus rendering this attack ineffective in the long term. Second, it can be used to target
  77. * specific accounts and increase the cost of ERC20 transfers for them, in the ways specified in the Gas Costs
  78. * section above.
  79. *
  80. * We haven't measured the actual numbers; if this is something you're interested in please reach out to us.
  81. * ====
  82. */
  83. function _snapshot() internal virtual returns (uint256) {
  84. _currentSnapshotId.increment();
  85. uint256 currentId = _getCurrentSnapshotId();
  86. emit Snapshot(currentId);
  87. return currentId;
  88. }
  89. /**
  90. * @dev Get the current snapshotId
  91. */
  92. function _getCurrentSnapshotId() internal view virtual returns (uint256) {
  93. return _currentSnapshotId.current();
  94. }
  95. /**
  96. * @dev Retrieves the balance of `account` at the time `snapshotId` was created.
  97. */
  98. function balanceOfAt(address account, uint256 snapshotId) public view virtual returns (uint256) {
  99. (bool snapshotted, uint256 value) = _valueAt(snapshotId, _accountBalanceSnapshots[account]);
  100. return snapshotted ? value : balanceOf(account);
  101. }
  102. /**
  103. * @dev Retrieves the total supply at the time `snapshotId` was created.
  104. */
  105. function totalSupplyAt(uint256 snapshotId) public view virtual returns (uint256) {
  106. (bool snapshotted, uint256 value) = _valueAt(snapshotId, _totalSupplySnapshots);
  107. return snapshotted ? value : totalSupply();
  108. }
  109. // Update balance and/or total supply snapshots before the values are modified. This is implemented
  110. // in the _beforeTokenTransfer hook, which is executed for _mint, _burn, and _transfer operations.
  111. function _beforeTokenTransfer(
  112. address from,
  113. address to,
  114. uint256 amount
  115. ) internal virtual override {
  116. super._beforeTokenTransfer(from, to, amount);
  117. if (from == address(0)) {
  118. // mint
  119. _updateAccountSnapshot(to);
  120. _updateTotalSupplySnapshot();
  121. } else if (to == address(0)) {
  122. // burn
  123. _updateAccountSnapshot(from);
  124. _updateTotalSupplySnapshot();
  125. } else {
  126. // transfer
  127. _updateAccountSnapshot(from);
  128. _updateAccountSnapshot(to);
  129. }
  130. }
  131. function _valueAt(uint256 snapshotId, Snapshots storage snapshots) private view returns (bool, uint256) {
  132. require(snapshotId > 0, "ERC20Snapshot: id is 0");
  133. require(snapshotId <= _getCurrentSnapshotId(), "ERC20Snapshot: nonexistent id");
  134. // When a valid snapshot is queried, there are three possibilities:
  135. // a) The queried value was not modified after the snapshot was taken. Therefore, a snapshot entry was never
  136. // created for this id, and all stored snapshot ids are smaller than the requested one. The value that corresponds
  137. // to this id is the current one.
  138. // b) The queried value was modified after the snapshot was taken. Therefore, there will be an entry with the
  139. // requested id, and its value is the one to return.
  140. // c) More snapshots were created after the requested one, and the queried value was later modified. There will be
  141. // no entry for the requested id: the value that corresponds to it is that of the smallest snapshot id that is
  142. // larger than the requested one.
  143. //
  144. // In summary, we need to find an element in an array, returning the index of the smallest value that is larger if
  145. // it is not found, unless said value doesn't exist (e.g. when all values are smaller). Arrays.findUpperBound does
  146. // exactly this.
  147. uint256 index = snapshots.ids.findUpperBound(snapshotId);
  148. if (index == snapshots.ids.length) {
  149. return (false, 0);
  150. } else {
  151. return (true, snapshots.values[index]);
  152. }
  153. }
  154. function _updateAccountSnapshot(address account) private {
  155. _updateSnapshot(_accountBalanceSnapshots[account], balanceOf(account));
  156. }
  157. function _updateTotalSupplySnapshot() private {
  158. _updateSnapshot(_totalSupplySnapshots, totalSupply());
  159. }
  160. function _updateSnapshot(Snapshots storage snapshots, uint256 currentValue) private {
  161. uint256 currentId = _getCurrentSnapshotId();
  162. if (_lastSnapshotId(snapshots.ids) < currentId) {
  163. snapshots.ids.push(currentId);
  164. snapshots.values.push(currentValue);
  165. }
  166. }
  167. function _lastSnapshotId(uint256[] storage ids) private view returns (uint256) {
  168. if (ids.length == 0) {
  169. return 0;
  170. } else {
  171. return ids[ids.length - 1];
  172. }
  173. }
  174. /**
  175. * This empty reserved space is put in place to allow future versions to add new
  176. * variables without shifting down storage in the inheritance chain.
  177. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
  178. */
  179. uint256[46] private __gap;
  180. }