mirror of
https://github.com/th30d4y/OpenLearnX.git
synced 2026-05-27 03:36:32 +00:00
Fix .gitignore: stop tracking ignored files
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../ERC20.sol";
|
||||
import "../../../utils/Context.sol";
|
||||
|
||||
/**
|
||||
* @dev Extension of {ERC20} that allows token holders to destroy both their own
|
||||
* tokens and those that they have an allowance for, in a way that can be
|
||||
* recognized off-chain (via event analysis).
|
||||
*/
|
||||
abstract contract ERC20Burnable is Context, ERC20 {
|
||||
/**
|
||||
* @dev Destroys `amount` tokens from the caller.
|
||||
*
|
||||
* See {ERC20-_burn}.
|
||||
*/
|
||||
function burn(uint256 amount) public virtual {
|
||||
_burn(_msgSender(), amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Destroys `amount` tokens from `account`, deducting from the caller's
|
||||
* allowance.
|
||||
*
|
||||
* See {ERC20-_burn} and {ERC20-allowance}.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must have allowance for ``accounts``'s tokens of at least
|
||||
* `amount`.
|
||||
*/
|
||||
function burnFrom(address account, uint256 amount) public virtual {
|
||||
_spendAllowance(account, _msgSender(), amount);
|
||||
_burn(account, amount);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Capped.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../ERC20.sol";
|
||||
|
||||
/**
|
||||
* @dev Extension of {ERC20} that adds a cap to the supply of tokens.
|
||||
*/
|
||||
abstract contract ERC20Capped is ERC20 {
|
||||
uint256 private immutable _cap;
|
||||
|
||||
/**
|
||||
* @dev Sets the value of the `cap`. This value is immutable, it can only be
|
||||
* set once during construction.
|
||||
*/
|
||||
constructor(uint256 cap_) {
|
||||
require(cap_ > 0, "ERC20Capped: cap is 0");
|
||||
_cap = cap_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the cap on the token's total supply.
|
||||
*/
|
||||
function cap() public view virtual returns (uint256) {
|
||||
return _cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {ERC20-_mint}.
|
||||
*/
|
||||
function _mint(address account, uint256 amount) internal virtual override {
|
||||
require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
|
||||
super._mint(account, amount);
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC20FlashMint.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../../../interfaces/IERC3156FlashBorrower.sol";
|
||||
import "../../../interfaces/IERC3156FlashLender.sol";
|
||||
import "../ERC20.sol";
|
||||
|
||||
/**
|
||||
* @dev Implementation of the ERC3156 Flash loans extension, as defined in
|
||||
* https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
|
||||
*
|
||||
* Adds the {flashLoan} method, which provides flash loan support at the token
|
||||
* level. By default there is no fee, but this can be changed by overriding {flashFee}.
|
||||
*
|
||||
* _Available since v4.1._
|
||||
*/
|
||||
abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
|
||||
bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
|
||||
|
||||
/**
|
||||
* @dev Returns the maximum amount of tokens available for loan.
|
||||
* @param token The address of the token that is requested.
|
||||
* @return The amount of token that can be loaned.
|
||||
*/
|
||||
function maxFlashLoan(address token) public view virtual override returns (uint256) {
|
||||
return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the fee applied when doing flash loans. This function calls
|
||||
* the {_flashFee} function which returns the fee applied when doing flash
|
||||
* loans.
|
||||
* @param token The token to be flash loaned.
|
||||
* @param amount The amount of tokens to be loaned.
|
||||
* @return The fees applied to the corresponding flash loan.
|
||||
*/
|
||||
function flashFee(address token, uint256 amount) public view virtual override returns (uint256) {
|
||||
require(token == address(this), "ERC20FlashMint: wrong token");
|
||||
return _flashFee(token, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the fee applied when doing flash loans. By default this
|
||||
* implementation has 0 fees. This function can be overloaded to make
|
||||
* the flash loan mechanism deflationary.
|
||||
* @param token The token to be flash loaned.
|
||||
* @param amount The amount of tokens to be loaned.
|
||||
* @return The fees applied to the corresponding flash loan.
|
||||
*/
|
||||
function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) {
|
||||
// silence warning about unused variable without the addition of bytecode.
|
||||
token;
|
||||
amount;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the receiver address of the flash fee. By default this
|
||||
* implementation returns the address(0) which means the fee amount will be burnt.
|
||||
* This function can be overloaded to change the fee receiver.
|
||||
* @return The address for which the flash fee will be sent to.
|
||||
*/
|
||||
function _flashFeeReceiver() internal view virtual returns (address) {
|
||||
return address(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Performs a flash loan. New tokens are minted and sent to the
|
||||
* `receiver`, who is required to implement the {IERC3156FlashBorrower}
|
||||
* interface. By the end of the flash loan, the receiver is expected to own
|
||||
* amount + fee tokens and have them approved back to the token contract itself so
|
||||
* they can be burned.
|
||||
* @param receiver The receiver of the flash loan. Should implement the
|
||||
* {IERC3156FlashBorrower-onFlashLoan} interface.
|
||||
* @param token The token to be flash loaned. Only `address(this)` is
|
||||
* supported.
|
||||
* @param amount The amount of tokens to be loaned.
|
||||
* @param data An arbitrary datafield that is passed to the receiver.
|
||||
* @return `true` if the flash loan was successful.
|
||||
*/
|
||||
// This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount
|
||||
// minted at the beginning is always recovered and burned at the end, or else the entire function will revert.
|
||||
// slither-disable-next-line reentrancy-no-eth
|
||||
function flashLoan(
|
||||
IERC3156FlashBorrower receiver,
|
||||
address token,
|
||||
uint256 amount,
|
||||
bytes calldata data
|
||||
) public virtual override returns (bool) {
|
||||
require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan");
|
||||
uint256 fee = flashFee(token, amount);
|
||||
_mint(address(receiver), amount);
|
||||
require(
|
||||
receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE,
|
||||
"ERC20FlashMint: invalid return value"
|
||||
);
|
||||
address flashFeeReceiver = _flashFeeReceiver();
|
||||
_spendAllowance(address(receiver), address(this), amount + fee);
|
||||
if (fee == 0 || flashFeeReceiver == address(0)) {
|
||||
_burn(address(receiver), amount + fee);
|
||||
} else {
|
||||
_burn(address(receiver), amount);
|
||||
_transfer(address(receiver), flashFeeReceiver, fee);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/ERC20Pausable.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../ERC20.sol";
|
||||
import "../../../security/Pausable.sol";
|
||||
|
||||
/**
|
||||
* @dev ERC20 token with pausable token transfers, minting and burning.
|
||||
*
|
||||
* Useful for scenarios such as preventing trades until the end of an evaluation
|
||||
* period, or having an emergency switch for freezing all token transfers in the
|
||||
* event of a large bug.
|
||||
*
|
||||
* IMPORTANT: This contract does not include public pause and unpause functions. In
|
||||
* addition to inheriting this contract, you must define both functions, invoking the
|
||||
* {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
|
||||
* access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
|
||||
* make the contract unpausable.
|
||||
*/
|
||||
abstract contract ERC20Pausable is ERC20, Pausable {
|
||||
/**
|
||||
* @dev See {ERC20-_beforeTokenTransfer}.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the contract must not be paused.
|
||||
*/
|
||||
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
|
||||
super._beforeTokenTransfer(from, to, amount);
|
||||
|
||||
require(!paused(), "ERC20Pausable: token transfer while paused");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/ERC20Permit.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./IERC20Permit.sol";
|
||||
import "../ERC20.sol";
|
||||
import "../../../utils/cryptography/ECDSA.sol";
|
||||
import "../../../utils/cryptography/EIP712.sol";
|
||||
import "../../../utils/Counters.sol";
|
||||
|
||||
/**
|
||||
* @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
|
||||
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
|
||||
*
|
||||
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
|
||||
* presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
|
||||
* need to send a transaction, and thus is not required to hold Ether at all.
|
||||
*
|
||||
* _Available since v3.4._
|
||||
*/
|
||||
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
|
||||
using Counters for Counters.Counter;
|
||||
|
||||
mapping(address => Counters.Counter) private _nonces;
|
||||
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
bytes32 private constant _PERMIT_TYPEHASH =
|
||||
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
|
||||
/**
|
||||
* @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
|
||||
* However, to ensure consistency with the upgradeable transpiler, we will continue
|
||||
* to reserve a slot.
|
||||
* @custom:oz-renamed-from _PERMIT_TYPEHASH
|
||||
*/
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
|
||||
|
||||
/**
|
||||
* @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
|
||||
*
|
||||
* It's a good idea to use the same `name` that is defined as the ERC20 token name.
|
||||
*/
|
||||
constructor(string memory name) EIP712(name, "1") {}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20Permit-permit}.
|
||||
*/
|
||||
function permit(
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 value,
|
||||
uint256 deadline,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) public virtual override {
|
||||
require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
|
||||
|
||||
bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
|
||||
|
||||
bytes32 hash = _hashTypedDataV4(structHash);
|
||||
|
||||
address signer = ECDSA.recover(hash, v, r, s);
|
||||
require(signer == owner, "ERC20Permit: invalid signature");
|
||||
|
||||
_approve(owner, spender, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20Permit-nonces}.
|
||||
*/
|
||||
function nonces(address owner) public view virtual override returns (uint256) {
|
||||
return _nonces[owner].current();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
|
||||
*/
|
||||
// solhint-disable-next-line func-name-mixedcase
|
||||
function DOMAIN_SEPARATOR() external view override returns (bytes32) {
|
||||
return _domainSeparatorV4();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev "Consume a nonce": return the current value and increment.
|
||||
*
|
||||
* _Available since v4.1._
|
||||
*/
|
||||
function _useNonce(address owner) internal virtual returns (uint256 current) {
|
||||
Counters.Counter storage nonce = _nonces[owner];
|
||||
current = nonce.current();
|
||||
nonce.increment();
|
||||
}
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/ERC20Votes.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./ERC20Permit.sol";
|
||||
import "../../../interfaces/IERC5805.sol";
|
||||
import "../../../utils/math/Math.sol";
|
||||
import "../../../utils/math/SafeCast.sol";
|
||||
import "../../../utils/cryptography/ECDSA.sol";
|
||||
|
||||
/**
|
||||
* @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's,
|
||||
* and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1.
|
||||
*
|
||||
* NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module.
|
||||
*
|
||||
* This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either
|
||||
* by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting
|
||||
* power can be queried through the public accessors {getVotes} and {getPastVotes}.
|
||||
*
|
||||
* By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it
|
||||
* requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked.
|
||||
*
|
||||
* _Available since v4.2._
|
||||
*/
|
||||
abstract contract ERC20Votes is ERC20Permit, IERC5805 {
|
||||
struct Checkpoint {
|
||||
uint32 fromBlock;
|
||||
uint224 votes;
|
||||
}
|
||||
|
||||
bytes32 private constant _DELEGATION_TYPEHASH =
|
||||
keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
|
||||
|
||||
mapping(address => address) private _delegates;
|
||||
mapping(address => Checkpoint[]) private _checkpoints;
|
||||
Checkpoint[] private _totalSupplyCheckpoints;
|
||||
|
||||
/**
|
||||
* @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based checkpoints (and voting).
|
||||
*/
|
||||
function clock() public view virtual override returns (uint48) {
|
||||
return SafeCast.toUint48(block.number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Description of the clock
|
||||
*/
|
||||
// solhint-disable-next-line func-name-mixedcase
|
||||
function CLOCK_MODE() public view virtual override returns (string memory) {
|
||||
// Check that the clock was not modified
|
||||
require(clock() == block.number, "ERC20Votes: broken clock mode");
|
||||
return "mode=blocknumber&from=default";
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get the `pos`-th checkpoint for `account`.
|
||||
*/
|
||||
function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) {
|
||||
return _checkpoints[account][pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get number of checkpoints for `account`.
|
||||
*/
|
||||
function numCheckpoints(address account) public view virtual returns (uint32) {
|
||||
return SafeCast.toUint32(_checkpoints[account].length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get the address `account` is currently delegating to.
|
||||
*/
|
||||
function delegates(address account) public view virtual override returns (address) {
|
||||
return _delegates[account];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the current votes balance for `account`
|
||||
*/
|
||||
function getVotes(address account) public view virtual override returns (uint256) {
|
||||
uint256 pos = _checkpoints[account].length;
|
||||
unchecked {
|
||||
return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Retrieve the number of votes for `account` at the end of `timepoint`.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `timepoint` must be in the past
|
||||
*/
|
||||
function getPastVotes(address account, uint256 timepoint) public view virtual override returns (uint256) {
|
||||
require(timepoint < clock(), "ERC20Votes: future lookup");
|
||||
return _checkpointsLookup(_checkpoints[account], timepoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Retrieve the `totalSupply` at the end of `timepoint`. Note, this value is the sum of all balances.
|
||||
* It is NOT the sum of all the delegated votes!
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `timepoint` must be in the past
|
||||
*/
|
||||
function getPastTotalSupply(uint256 timepoint) public view virtual override returns (uint256) {
|
||||
require(timepoint < clock(), "ERC20Votes: future lookup");
|
||||
return _checkpointsLookup(_totalSupplyCheckpoints, timepoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Lookup a value in a list of (sorted) checkpoints.
|
||||
*/
|
||||
function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 timepoint) private view returns (uint256) {
|
||||
// We run a binary search to look for the last (most recent) checkpoint taken before (or at) `timepoint`.
|
||||
//
|
||||
// Initially we check if the block is recent to narrow the search range.
|
||||
// During the loop, the index of the wanted checkpoint remains in the range [low-1, high).
|
||||
// With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant.
|
||||
// - If the middle checkpoint is after `timepoint`, we look in [low, mid)
|
||||
// - If the middle checkpoint is before or equal to `timepoint`, we look in [mid+1, high)
|
||||
// Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not
|
||||
// out of bounds (in which case we're looking too far in the past and the result is 0).
|
||||
// Note that if the latest checkpoint available is exactly for `timepoint`, we end up with an index that is
|
||||
// past the end of the array, so we technically don't find a checkpoint after `timepoint`, but it works out
|
||||
// the same.
|
||||
uint256 length = ckpts.length;
|
||||
|
||||
uint256 low = 0;
|
||||
uint256 high = length;
|
||||
|
||||
if (length > 5) {
|
||||
uint256 mid = length - Math.sqrt(length);
|
||||
if (_unsafeAccess(ckpts, mid).fromBlock > timepoint) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (low < high) {
|
||||
uint256 mid = Math.average(low, high);
|
||||
if (_unsafeAccess(ckpts, mid).fromBlock > timepoint) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
unchecked {
|
||||
return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Delegate votes from the sender to `delegatee`.
|
||||
*/
|
||||
function delegate(address delegatee) public virtual override {
|
||||
_delegate(_msgSender(), delegatee);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Delegates votes from signer to `delegatee`
|
||||
*/
|
||||
function delegateBySig(
|
||||
address delegatee,
|
||||
uint256 nonce,
|
||||
uint256 expiry,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) public virtual override {
|
||||
require(block.timestamp <= expiry, "ERC20Votes: signature expired");
|
||||
address signer = ECDSA.recover(
|
||||
_hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))),
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce");
|
||||
_delegate(signer, delegatee);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1).
|
||||
*/
|
||||
function _maxSupply() internal view virtual returns (uint224) {
|
||||
return type(uint224).max;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Snapshots the totalSupply after it has been increased.
|
||||
*/
|
||||
function _mint(address account, uint256 amount) internal virtual override {
|
||||
super._mint(account, amount);
|
||||
require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes");
|
||||
|
||||
_writeCheckpoint(_totalSupplyCheckpoints, _add, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Snapshots the totalSupply after it has been decreased.
|
||||
*/
|
||||
function _burn(address account, uint256 amount) internal virtual override {
|
||||
super._burn(account, amount);
|
||||
|
||||
_writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Move voting power when tokens are transferred.
|
||||
*
|
||||
* Emits a {IVotes-DelegateVotesChanged} event.
|
||||
*/
|
||||
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual override {
|
||||
super._afterTokenTransfer(from, to, amount);
|
||||
|
||||
_moveVotingPower(delegates(from), delegates(to), amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Change delegation for `delegator` to `delegatee`.
|
||||
*
|
||||
* Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}.
|
||||
*/
|
||||
function _delegate(address delegator, address delegatee) internal virtual {
|
||||
address currentDelegate = delegates(delegator);
|
||||
uint256 delegatorBalance = balanceOf(delegator);
|
||||
_delegates[delegator] = delegatee;
|
||||
|
||||
emit DelegateChanged(delegator, currentDelegate, delegatee);
|
||||
|
||||
_moveVotingPower(currentDelegate, delegatee, delegatorBalance);
|
||||
}
|
||||
|
||||
function _moveVotingPower(address src, address dst, uint256 amount) private {
|
||||
if (src != dst && amount > 0) {
|
||||
if (src != address(0)) {
|
||||
(uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount);
|
||||
emit DelegateVotesChanged(src, oldWeight, newWeight);
|
||||
}
|
||||
|
||||
if (dst != address(0)) {
|
||||
(uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount);
|
||||
emit DelegateVotesChanged(dst, oldWeight, newWeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _writeCheckpoint(
|
||||
Checkpoint[] storage ckpts,
|
||||
function(uint256, uint256) view returns (uint256) op,
|
||||
uint256 delta
|
||||
) private returns (uint256 oldWeight, uint256 newWeight) {
|
||||
uint256 pos = ckpts.length;
|
||||
|
||||
unchecked {
|
||||
Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0) : _unsafeAccess(ckpts, pos - 1);
|
||||
|
||||
oldWeight = oldCkpt.votes;
|
||||
newWeight = op(oldWeight, delta);
|
||||
|
||||
if (pos > 0 && oldCkpt.fromBlock == clock()) {
|
||||
_unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight);
|
||||
} else {
|
||||
ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(clock()), votes: SafeCast.toUint224(newWeight)}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _add(uint256 a, uint256 b) private pure returns (uint256) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function _subtract(uint256 a, uint256 b) private pure returns (uint256) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
|
||||
*/
|
||||
function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) {
|
||||
assembly {
|
||||
mstore(0, ckpts.slot)
|
||||
result.slot := add(keccak256(0, 0x20), pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20VotesComp.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./ERC20Votes.sol";
|
||||
|
||||
/**
|
||||
* @dev Extension of ERC20 to support Compound's voting and delegation. This version exactly matches Compound's
|
||||
* interface, with the drawback of only supporting supply up to (2^96^ - 1).
|
||||
*
|
||||
* NOTE: You should use this contract if you need exact compatibility with COMP (for example in order to use your token
|
||||
* with Governor Alpha or Bravo) and if you are sure the supply cap of 2^96^ is enough for you. Otherwise, use the
|
||||
* {ERC20Votes} variant of this module.
|
||||
*
|
||||
* This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either
|
||||
* by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting
|
||||
* power can be queried through the public accessors {getCurrentVotes} and {getPriorVotes}.
|
||||
*
|
||||
* By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it
|
||||
* requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked.
|
||||
*
|
||||
* _Available since v4.2._
|
||||
*/
|
||||
abstract contract ERC20VotesComp is ERC20Votes {
|
||||
/**
|
||||
* @dev Comp version of the {getVotes} accessor, with `uint96` return type.
|
||||
*/
|
||||
function getCurrentVotes(address account) external view virtual returns (uint96) {
|
||||
return SafeCast.toUint96(getVotes(account));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Comp version of the {getPastVotes} accessor, with `uint96` return type.
|
||||
*/
|
||||
function getPriorVotes(address account, uint256 blockNumber) external view virtual returns (uint96) {
|
||||
return SafeCast.toUint96(getPastVotes(account, blockNumber));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Maximum token supply. Reduced to `type(uint96).max` (2^96^ - 1) to fit COMP interface.
|
||||
*/
|
||||
function _maxSupply() internal view virtual override returns (uint224) {
|
||||
return type(uint96).max;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/ERC20Wrapper.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../ERC20.sol";
|
||||
import "../utils/SafeERC20.sol";
|
||||
|
||||
/**
|
||||
* @dev Extension of the ERC20 token contract to support token wrapping.
|
||||
*
|
||||
* Users can deposit and withdraw "underlying tokens" and receive a matching number of "wrapped tokens". This is useful
|
||||
* in conjunction with other modules. For example, combining this wrapping mechanism with {ERC20Votes} will allow the
|
||||
* wrapping of an existing "basic" ERC20 into a governance token.
|
||||
*
|
||||
* _Available since v4.2._
|
||||
*/
|
||||
abstract contract ERC20Wrapper is ERC20 {
|
||||
IERC20 private immutable _underlying;
|
||||
|
||||
constructor(IERC20 underlyingToken) {
|
||||
require(underlyingToken != this, "ERC20Wrapper: cannot self wrap");
|
||||
_underlying = underlyingToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {ERC20-decimals}.
|
||||
*/
|
||||
function decimals() public view virtual override returns (uint8) {
|
||||
try IERC20Metadata(address(_underlying)).decimals() returns (uint8 value) {
|
||||
return value;
|
||||
} catch {
|
||||
return super.decimals();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the underlying ERC-20 token that is being wrapped.
|
||||
*/
|
||||
function underlying() public view returns (IERC20) {
|
||||
return _underlying;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens.
|
||||
*/
|
||||
function depositFor(address account, uint256 amount) public virtual returns (bool) {
|
||||
address sender = _msgSender();
|
||||
require(sender != address(this), "ERC20Wrapper: wrapper can't deposit");
|
||||
SafeERC20.safeTransferFrom(_underlying, sender, address(this), amount);
|
||||
_mint(account, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens.
|
||||
*/
|
||||
function withdrawTo(address account, uint256 amount) public virtual returns (bool) {
|
||||
_burn(_msgSender(), amount);
|
||||
SafeERC20.safeTransfer(_underlying, account, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal
|
||||
* function that can be exposed with access control if desired.
|
||||
*/
|
||||
function _recover(address account) internal virtual returns (uint256) {
|
||||
uint256 value = _underlying.balanceOf(address(this)) - totalSupply();
|
||||
_mint(account, value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/ERC4626.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../ERC20.sol";
|
||||
import "../utils/SafeERC20.sol";
|
||||
import "../../../interfaces/IERC4626.sol";
|
||||
import "../../../utils/math/Math.sol";
|
||||
|
||||
/**
|
||||
* @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in
|
||||
* https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
|
||||
*
|
||||
* This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for
|
||||
* underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
|
||||
* the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this
|
||||
* contract and not the "assets" token which is an independent contract.
|
||||
*
|
||||
* [CAUTION]
|
||||
* ====
|
||||
* In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning
|
||||
* with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
|
||||
* attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
|
||||
* deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may
|
||||
* similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by
|
||||
* verifying the amount received is as expected, using a wrapper that performs these checks such as
|
||||
* https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
|
||||
*
|
||||
* Since v4.9, this implementation uses virtual assets and shares to mitigate that risk. The `_decimalsOffset()`
|
||||
* corresponds to an offset in the decimal representation between the underlying asset's decimals and the vault
|
||||
* decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which itself
|
||||
* determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default offset
|
||||
* (0) makes it non-profitable, as a result of the value being captured by the virtual shares (out of the attacker's
|
||||
* donation) matching the attacker's expected gains. With a larger offset, the attack becomes orders of magnitude more
|
||||
* expensive than it is profitable. More details about the underlying math can be found
|
||||
* xref:erc4626.adoc#inflation-attack[here].
|
||||
*
|
||||
* The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued
|
||||
* to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets
|
||||
* will cause the first user to exit to experience reduced losses in detriment to the last users that will experience
|
||||
* bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the
|
||||
* `_convertToShares` and `_convertToAssets` functions.
|
||||
*
|
||||
* To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide].
|
||||
* ====
|
||||
*
|
||||
* _Available since v4.7._
|
||||
*/
|
||||
abstract contract ERC4626 is ERC20, IERC4626 {
|
||||
using Math for uint256;
|
||||
|
||||
IERC20 private immutable _asset;
|
||||
uint8 private immutable _underlyingDecimals;
|
||||
|
||||
/**
|
||||
* @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777).
|
||||
*/
|
||||
constructor(IERC20 asset_) {
|
||||
(bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
|
||||
_underlyingDecimals = success ? assetDecimals : 18;
|
||||
_asset = asset_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
|
||||
*/
|
||||
function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) {
|
||||
(bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
|
||||
abi.encodeWithSelector(IERC20Metadata.decimals.selector)
|
||||
);
|
||||
if (success && encodedDecimals.length >= 32) {
|
||||
uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
|
||||
if (returnedDecimals <= type(uint8).max) {
|
||||
return (true, uint8(returnedDecimals));
|
||||
}
|
||||
}
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This
|
||||
* "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the
|
||||
* asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals.
|
||||
*
|
||||
* See {IERC20Metadata-decimals}.
|
||||
*/
|
||||
function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
|
||||
return _underlyingDecimals + _decimalsOffset();
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-asset}. */
|
||||
function asset() public view virtual override returns (address) {
|
||||
return address(_asset);
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-totalAssets}. */
|
||||
function totalAssets() public view virtual override returns (uint256) {
|
||||
return _asset.balanceOf(address(this));
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-convertToShares}. */
|
||||
function convertToShares(uint256 assets) public view virtual override returns (uint256) {
|
||||
return _convertToShares(assets, Math.Rounding.Down);
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-convertToAssets}. */
|
||||
function convertToAssets(uint256 shares) public view virtual override returns (uint256) {
|
||||
return _convertToAssets(shares, Math.Rounding.Down);
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-maxDeposit}. */
|
||||
function maxDeposit(address) public view virtual override returns (uint256) {
|
||||
return type(uint256).max;
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-maxMint}. */
|
||||
function maxMint(address) public view virtual override returns (uint256) {
|
||||
return type(uint256).max;
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-maxWithdraw}. */
|
||||
function maxWithdraw(address owner) public view virtual override returns (uint256) {
|
||||
return _convertToAssets(balanceOf(owner), Math.Rounding.Down);
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-maxRedeem}. */
|
||||
function maxRedeem(address owner) public view virtual override returns (uint256) {
|
||||
return balanceOf(owner);
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-previewDeposit}. */
|
||||
function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
|
||||
return _convertToShares(assets, Math.Rounding.Down);
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-previewMint}. */
|
||||
function previewMint(uint256 shares) public view virtual override returns (uint256) {
|
||||
return _convertToAssets(shares, Math.Rounding.Up);
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-previewWithdraw}. */
|
||||
function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {
|
||||
return _convertToShares(assets, Math.Rounding.Up);
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-previewRedeem}. */
|
||||
function previewRedeem(uint256 shares) public view virtual override returns (uint256) {
|
||||
return _convertToAssets(shares, Math.Rounding.Down);
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-deposit}. */
|
||||
function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {
|
||||
require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max");
|
||||
|
||||
uint256 shares = previewDeposit(assets);
|
||||
_deposit(_msgSender(), receiver, assets, shares);
|
||||
|
||||
return shares;
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-mint}.
|
||||
*
|
||||
* As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero.
|
||||
* In this case, the shares will be minted without requiring any assets to be deposited.
|
||||
*/
|
||||
function mint(uint256 shares, address receiver) public virtual override returns (uint256) {
|
||||
require(shares <= maxMint(receiver), "ERC4626: mint more than max");
|
||||
|
||||
uint256 assets = previewMint(shares);
|
||||
_deposit(_msgSender(), receiver, assets, shares);
|
||||
|
||||
return assets;
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-withdraw}. */
|
||||
function withdraw(uint256 assets, address receiver, address owner) public virtual override returns (uint256) {
|
||||
require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max");
|
||||
|
||||
uint256 shares = previewWithdraw(assets);
|
||||
_withdraw(_msgSender(), receiver, owner, assets, shares);
|
||||
|
||||
return shares;
|
||||
}
|
||||
|
||||
/** @dev See {IERC4626-redeem}. */
|
||||
function redeem(uint256 shares, address receiver, address owner) public virtual override returns (uint256) {
|
||||
require(shares <= maxRedeem(owner), "ERC4626: redeem more than max");
|
||||
|
||||
uint256 assets = previewRedeem(shares);
|
||||
_withdraw(_msgSender(), receiver, owner, assets, shares);
|
||||
|
||||
return assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal conversion function (from assets to shares) with support for rounding direction.
|
||||
*/
|
||||
function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
|
||||
return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal conversion function (from shares to assets) with support for rounding direction.
|
||||
*/
|
||||
function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {
|
||||
return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Deposit/mint common workflow.
|
||||
*/
|
||||
function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual {
|
||||
// If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
|
||||
// `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
|
||||
// calls the vault, which is assumed not malicious.
|
||||
//
|
||||
// Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
|
||||
// assets are transferred and before the shares are minted, which is a valid state.
|
||||
// slither-disable-next-line reentrancy-no-eth
|
||||
SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);
|
||||
_mint(receiver, shares);
|
||||
|
||||
emit Deposit(caller, receiver, assets, shares);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Withdraw/redeem common workflow.
|
||||
*/
|
||||
function _withdraw(
|
||||
address caller,
|
||||
address receiver,
|
||||
address owner,
|
||||
uint256 assets,
|
||||
uint256 shares
|
||||
) internal virtual {
|
||||
if (caller != owner) {
|
||||
_spendAllowance(owner, caller, shares);
|
||||
}
|
||||
|
||||
// If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
|
||||
// `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
|
||||
// calls the vault, which is assumed not malicious.
|
||||
//
|
||||
// Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
|
||||
// shares are burned and after the assets are transferred, which is a valid state.
|
||||
_burn(owner, shares);
|
||||
SafeERC20.safeTransfer(_asset, receiver, assets);
|
||||
|
||||
emit Withdraw(caller, receiver, owner, assets, shares);
|
||||
}
|
||||
|
||||
function _decimalsOffset() internal view virtual returns (uint8) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../IERC20.sol";
|
||||
|
||||
/**
|
||||
* @dev Interface for the optional metadata functions from the ERC20 standard.
|
||||
*
|
||||
* _Available since v4.1._
|
||||
*/
|
||||
interface IERC20Metadata is IERC20 {
|
||||
/**
|
||||
* @dev Returns the name of the token.
|
||||
*/
|
||||
function name() external view returns (string memory);
|
||||
|
||||
/**
|
||||
* @dev Returns the symbol of the token.
|
||||
*/
|
||||
function symbol() external view returns (string memory);
|
||||
|
||||
/**
|
||||
* @dev Returns the decimals places of the token.
|
||||
*/
|
||||
function decimals() external view returns (uint8);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
/**
|
||||
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
|
||||
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
|
||||
*
|
||||
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
|
||||
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
|
||||
* need to send a transaction, and thus is not required to hold Ether at all.
|
||||
*/
|
||||
interface IERC20Permit {
|
||||
/**
|
||||
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
|
||||
* given ``owner``'s signed approval.
|
||||
*
|
||||
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
|
||||
* ordering also apply here.
|
||||
*
|
||||
* Emits an {Approval} event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `spender` cannot be the zero address.
|
||||
* - `deadline` must be a timestamp in the future.
|
||||
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
|
||||
* over the EIP712-formatted function arguments.
|
||||
* - the signature must use ``owner``'s current nonce (see {nonces}).
|
||||
*
|
||||
* For more information on the signature format, see the
|
||||
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
|
||||
* section].
|
||||
*/
|
||||
function permit(
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 value,
|
||||
uint256 deadline,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @dev Returns the current nonce for `owner`. This value must be
|
||||
* included whenever a signature is generated for {permit}.
|
||||
*
|
||||
* Every successful call to {permit} increases ``owner``'s nonce by one. This
|
||||
* prevents a signature from being used multiple times.
|
||||
*/
|
||||
function nonces(address owner) external view returns (uint256);
|
||||
|
||||
/**
|
||||
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
|
||||
*/
|
||||
// solhint-disable-next-line func-name-mixedcase
|
||||
function DOMAIN_SEPARATOR() external view returns (bytes32);
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/draft-ERC20Permit.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
// EIP-2612 is Final as of 2022-11-01. This file is deprecated.
|
||||
|
||||
import "./ERC20Permit.sol";
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/draft-IERC20Permit.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
// EIP-2612 is Final as of 2022-11-01. This file is deprecated.
|
||||
|
||||
import "./IERC20Permit.sol";
|
||||
Reference in New Issue
Block a user