| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 | // SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/extensions/ERC20Snapshot.sol)pragma solidity ^0.8.0;import "../ERC20.sol";import "../../../utils/Arrays.sol";import "../../../utils/Counters.sol";/** * @dev This contract extends an ERC20 token with a snapshot mechanism. When a snapshot is created, the balances and * total supply at the time are recorded for later access. * * This can be used to safely create mechanisms based on token balances such as trustless dividends or weighted voting. * In naive implementations it's possible to perform a "double spend" attack by reusing the same balance from different * accounts. By using snapshots to calculate dividends or voting power, those attacks no longer apply. It can also be * used to create an efficient ERC20 forking mechanism. * * Snapshots are created by the internal {_snapshot} function, which will emit the {Snapshot} event and return a * snapshot id. To get the total supply at the time of a snapshot, call the function {totalSupplyAt} with the snapshot * id. To get the balance of an account at the time of a snapshot, call the {balanceOfAt} function with the snapshot id * and the account address. * * NOTE: Snapshot policy can be customized by overriding the {_getCurrentSnapshotId} method. For example, having it * return `block.number` will trigger the creation of snapshot at the beginning of each new block. When overriding this * function, be careful about the monotonicity of its result. Non-monotonic snapshot ids will break the contract. * * Implementing snapshots for every block using this method will incur significant gas costs. For a gas-efficient * alternative consider {ERC20Votes}. * * ==== Gas Costs * * Snapshots are efficient. Snapshot creation is _O(1)_. Retrieval of balances or total supply from a snapshot is _O(log * n)_ in the number of snapshots that have been created, although _n_ for a specific account will generally be much * smaller since identical balances in subsequent snapshots are stored as a single entry. * * There is a constant overhead for normal ERC20 transfers due to the additional snapshot bookkeeping. This overhead is * only significant for the first transfer that immediately follows a snapshot for a particular account. Subsequent * transfers will have normal cost until the next snapshot, and so on. */abstract contract ERC20Snapshot is ERC20 {    // Inspired by Jordi Baylina's MiniMeToken to record historical balances:    // https://github.com/Giveth/minime/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol    using Arrays for uint256[];    using Counters for Counters.Counter;    // Snapshotted values have arrays of ids and the value corresponding to that id. These could be an array of a    // Snapshot struct, but that would impede usage of functions that work on an array.    struct Snapshots {        uint256[] ids;        uint256[] values;    }    mapping(address => Snapshots) private _accountBalanceSnapshots;    Snapshots private _totalSupplySnapshots;    // Snapshot ids increase monotonically, with the first value being 1. An id of 0 is invalid.    Counters.Counter private _currentSnapshotId;    /**     * @dev Emitted by {_snapshot} when a snapshot identified by `id` is created.     */    event Snapshot(uint256 id);    /**     * @dev Creates a new snapshot and returns its snapshot id.     *     * Emits a {Snapshot} event that contains the same id.     *     * {_snapshot} is `internal` and you have to decide how to expose it externally. Its usage may be restricted to a     * set of accounts, for example using {AccessControl}, or it may be open to the public.     *     * [WARNING]     * ====     * While an open way of calling {_snapshot} is required for certain trust minimization mechanisms such as forking,     * you must consider that it can potentially be used by attackers in two ways.     *     * First, it can be used to increase the cost of retrieval of values from snapshots, although it will grow     * logarithmically thus rendering this attack ineffective in the long term. Second, it can be used to target     * specific accounts and increase the cost of ERC20 transfers for them, in the ways specified in the Gas Costs     * section above.     *     * We haven't measured the actual numbers; if this is something you're interested in please reach out to us.     * ====     */    function _snapshot() internal virtual returns (uint256) {        _currentSnapshotId.increment();        uint256 currentId = _getCurrentSnapshotId();        emit Snapshot(currentId);        return currentId;    }    /**     * @dev Get the current snapshotId     */    function _getCurrentSnapshotId() internal view virtual returns (uint256) {        return _currentSnapshotId.current();    }    /**     * @dev Retrieves the balance of `account` at the time `snapshotId` was created.     */    function balanceOfAt(address account, uint256 snapshotId) public view virtual returns (uint256) {        (bool snapshotted, uint256 value) = _valueAt(snapshotId, _accountBalanceSnapshots[account]);        return snapshotted ? value : balanceOf(account);    }    /**     * @dev Retrieves the total supply at the time `snapshotId` was created.     */    function totalSupplyAt(uint256 snapshotId) public view virtual returns (uint256) {        (bool snapshotted, uint256 value) = _valueAt(snapshotId, _totalSupplySnapshots);        return snapshotted ? value : totalSupply();    }    // Update balance and/or total supply snapshots before the values are modified. This is implemented    // in the _beforeTokenTransfer hook, which is executed for _mint, _burn, and _transfer operations.    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {        super._beforeTokenTransfer(from, to, amount);        if (from == address(0)) {            // mint            _updateAccountSnapshot(to);            _updateTotalSupplySnapshot();        } else if (to == address(0)) {            // burn            _updateAccountSnapshot(from);            _updateTotalSupplySnapshot();        } else {            // transfer            _updateAccountSnapshot(from);            _updateAccountSnapshot(to);        }    }    function _valueAt(uint256 snapshotId, Snapshots storage snapshots) private view returns (bool, uint256) {        require(snapshotId > 0, "ERC20Snapshot: id is 0");        require(snapshotId <= _getCurrentSnapshotId(), "ERC20Snapshot: nonexistent id");        // When a valid snapshot is queried, there are three possibilities:        //  a) The queried value was not modified after the snapshot was taken. Therefore, a snapshot entry was never        //  created for this id, and all stored snapshot ids are smaller than the requested one. The value that corresponds        //  to this id is the current one.        //  b) The queried value was modified after the snapshot was taken. Therefore, there will be an entry with the        //  requested id, and its value is the one to return.        //  c) More snapshots were created after the requested one, and the queried value was later modified. There will be        //  no entry for the requested id: the value that corresponds to it is that of the smallest snapshot id that is        //  larger than the requested one.        //        // In summary, we need to find an element in an array, returning the index of the smallest value that is larger if        // it is not found, unless said value doesn't exist (e.g. when all values are smaller). Arrays.findUpperBound does        // exactly this.        uint256 index = snapshots.ids.findUpperBound(snapshotId);        if (index == snapshots.ids.length) {            return (false, 0);        } else {            return (true, snapshots.values[index]);        }    }    function _updateAccountSnapshot(address account) private {        _updateSnapshot(_accountBalanceSnapshots[account], balanceOf(account));    }    function _updateTotalSupplySnapshot() private {        _updateSnapshot(_totalSupplySnapshots, totalSupply());    }    function _updateSnapshot(Snapshots storage snapshots, uint256 currentValue) private {        uint256 currentId = _getCurrentSnapshotId();        if (_lastSnapshotId(snapshots.ids) < currentId) {            snapshots.ids.push(currentId);            snapshots.values.push(currentValue);        }    }    function _lastSnapshotId(uint256[] storage ids) private view returns (uint256) {        if (ids.length == 0) {            return 0;        } else {            return ids[ids.length - 1];        }    }}
 |